snort-2.9.20/0000755000175000017500000000000014242725721011077 5ustar apoaposnort-2.9.20/compile0000755000175000017500000001632714242047077012467 0ustar apoapo#! /bin/sh # Wrapper for compilers which do not understand '-c -o'. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2018 Free Software Foundation, Inc. # Written by Tom Tromey . # # 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, 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . nl=' ' # We need space, tab and new line, in precisely that order. Quoting is # there to prevent tools from complaining about whitespace usage. IFS=" "" $nl" file_conv= # func_file_conv build_file lazy # Convert a $build file to $host form and store it in $file # Currently only supports Windows hosts. If the determined conversion # type is listed in (the comma separated) LAZY, no conversion will # take place. func_file_conv () { file=$1 case $file in / | /[!/]*) # absolute file, and not a UNC file if test -z "$file_conv"; then # lazily determine how to convert abs files case `uname -s` in MINGW*) file_conv=mingw ;; CYGWIN*) file_conv=cygwin ;; *) file_conv=wine ;; esac fi case $file_conv/,$2, in *,$file_conv,*) ;; mingw/*) file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` ;; cygwin/*) file=`cygpath -m "$file" || echo "$file"` ;; wine/*) file=`winepath -w "$file" || echo "$file"` ;; esac ;; esac } # func_cl_dashL linkdir # Make cl look for libraries in LINKDIR func_cl_dashL () { func_file_conv "$1" if test -z "$lib_path"; then lib_path=$file else lib_path="$lib_path;$file" fi linker_opts="$linker_opts -LIBPATH:$file" } # func_cl_dashl library # Do a library search-path lookup for cl func_cl_dashl () { lib=$1 found=no save_IFS=$IFS IFS=';' for dir in $lib_path $LIB do IFS=$save_IFS if $shared && test -f "$dir/$lib.dll.lib"; then found=yes lib=$dir/$lib.dll.lib break fi if test -f "$dir/$lib.lib"; then found=yes lib=$dir/$lib.lib break fi if test -f "$dir/lib$lib.a"; then found=yes lib=$dir/lib$lib.a break fi done IFS=$save_IFS if test "$found" != yes; then lib=$lib.lib fi } # func_cl_wrapper cl arg... # Adjust compile command to suit cl func_cl_wrapper () { # Assume a capable shell lib_path= shared=: linker_opts= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. eat=1 case $2 in *.o | *.[oO][bB][jJ]) func_file_conv "$2" set x "$@" -Fo"$file" shift ;; *) func_file_conv "$2" set x "$@" -Fe"$file" shift ;; esac ;; -I) eat=1 func_file_conv "$2" mingw set x "$@" -I"$file" shift ;; -I*) func_file_conv "${1#-I}" mingw set x "$@" -I"$file" shift ;; -l) eat=1 func_cl_dashl "$2" set x "$@" "$lib" shift ;; -l*) func_cl_dashl "${1#-l}" set x "$@" "$lib" shift ;; -L) eat=1 func_cl_dashL "$2" ;; -L*) func_cl_dashL "${1#-L}" ;; -static) shared=false ;; -Wl,*) arg=${1#-Wl,} save_ifs="$IFS"; IFS=',' for flag in $arg; do IFS="$save_ifs" linker_opts="$linker_opts $flag" done IFS="$save_ifs" ;; -Xlinker) eat=1 linker_opts="$linker_opts $2" ;; -*) set x "$@" "$1" shift ;; *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) func_file_conv "$1" set x "$@" -Tp"$file" shift ;; *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) func_file_conv "$1" mingw set x "$@" "$file" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -n "$linker_opts"; then linker_opts="-link$linker_opts" fi exec "$@" $linker_opts exit 1 } eat= case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: compile [--help] [--version] PROGRAM [ARGS] Wrapper for compilers which do not understand '-c -o'. Remove '-o dest.o' from ARGS, run PROGRAM with the remaining arguments, and rename the output as expected. If you are trying to build a whole package this is not the right script to run: please start by reading the file 'INSTALL'. Report bugs to . EOF exit $? ;; -v | --v*) echo "compile $scriptversion" exit $? ;; cl | *[/\\]cl | cl.exe | *[/\\]cl.exe | \ icl | *[/\\]icl | icl.exe | *[/\\]icl.exe ) func_cl_wrapper "$@" # Doesn't return... ;; esac ofile= cfile= for arg do if test -n "$eat"; then eat= else case $1 in -o) # configure might choose to run compile as 'compile cc -o foo foo.c'. # So we strip '-o arg' only if arg is an object. eat=1 case $2 in *.o | *.obj) ofile=$2 ;; *) set x "$@" -o "$2" shift ;; esac ;; *.c) cfile=$1 set x "$@" "$1" shift ;; *) set x "$@" "$1" shift ;; esac fi shift done if test -z "$ofile" || test -z "$cfile"; then # If no '-o' option was seen then we might have been invoked from a # pattern rule where we don't need one. That is ok -- this is a # normal compilation that the losing compiler can handle. If no # '.c' file was seen then we are probably linking. That is also # ok. exec "$@" fi # Name of file we expect compiler to create. cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` # Create the lock directory. # Note: use '[/\\:.-]' here to ensure that we don't use the same name # that we are using for the .o file. Also, base the name on the expected # object file name, since that is what matters with a parallel build. lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d while true; do if mkdir "$lockdir" >/dev/null 2>&1; then break fi sleep 1 done # FIXME: race condition here if user kills between mkdir and trap. trap "rmdir '$lockdir'; exit 1" 1 2 15 # Run the compile. "$@" ret=$? if test -f "$cofile"; then test "$cofile" = "$ofile" || mv "$cofile" "$ofile" elif test -f "${cofile}bj"; then test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" fi rmdir "$lockdir" exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: snort-2.9.20/install-sh0000755000175000017500000003601014242725545013107 0ustar apoapo#!/bin/sh # install - install a program, script, or datafile scriptversion=2018-03-11.20; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. tab=' ' nl=' ' IFS=" $tab$nl" # Set DOITPROG to "echo" to test this script. doit=${DOITPROG-} doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) is_target_a_directory=always dst_arg=$2 # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac shift;; -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done # We allow the use of options -d and -T together, by making -d # take the precedence; this is for compatibility with GNU install. if test -n "$dir_arg"; then if test -n "$dst_arg"; then echo "$0: target directory not allowed when installing a directory." >&2 exit 1 fi fi if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg # Protect names problematic for 'test' and other utilities. case $dst_arg in -* | [=\(\)!]) dst_arg=./$dst_arg;; esac done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then if test $# -gt 1 || test "$is_target_a_directory" = always; then if test ! -d "$dst_arg"; then echo "$0: $dst_arg: Is not a directory." >&2 exit 1 fi fi fi if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names problematic for 'test' and other utilities. case $src in -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # If destination is a directory, append the input filename. if test -d "$dst"; then if test "$is_target_a_directory" = never; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dstbase=`basename "$src"` case $dst in */) dst=$dst$dstbase;; *) dst=$dst/$dstbase;; esac dstdir_status=0 else dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi case $dstdir in */) dstdirslash=$dstdir;; *) dstdirslash=$dstdir/;; esac obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) # Note that $RANDOM variable is not portable (e.g. dash); Use it # here however when possible just to lower collision chance. tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null; exit $ret' 0 # Because "mkdir -p" follows existing symlinks and we likely work # directly in world-writeable /tmp, make sure that the '$tmpdir' # directory is successfully created first before we actually test # 'mkdir -p' feature. if (umask $mkdir_umask && $mkdirprog $mkdir_mode "$tmpdir" && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. test_tmpdir="$tmpdir/a" ls_ld_tmpdir=`ls -ld "$test_tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; [-=\(\)!]*) prefix='./';; *) prefix='';; esac oIFS=$IFS IFS=/ set -f set fnord $dstdir shift set +f IFS=$oIFS prefixes= for d do test X"$d" = X && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=${dstdirslash}_inst.$$_ rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: snort-2.9.20/templates/0000755000175000017500000000000014242725721013075 5ustar apoaposnort-2.9.20/templates/sp_template.c0000644000175000017500000001433614241077572015570 0ustar apoapo/* $Id$ */ /* Snort Detection Plugin Source File Template */ /* sp_template * * Purpose: * * Detection engine plugins test an aspect of the current packet and report * their findings. The function may be called many times per packet with * different arguments. These functions are acccessed from the rules file * as standard rule options. When adding a plugin to the system, be sure to * add the "Setup" function to the InitPlugins() function call in * plugbase.c! * * Arguments: * * This is the type of arguements that the detection plugin can take when * referenced as a rule option * * Effect: * * What the plugin does. * * Comments: * * Any comments? * */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "rules.h" #include "treenodes.h" #include "decode.h" #include "plugbase.h" #include "parser.h" #include "snort_debug.h" #include "util.h" #include "plugin_enum.h" /* * don't forget to include the name of this file in plugbase.c! */ /* * setup any data structs here */ typedef struct _TemplateData { /* * your detection option data * structure info goes here */ } TemplateData; /* function prototypes go here */ static void TemplateInit(char *, OptTreeNode *, int); static void TemplateRuleParseFunction(char *, OptTreeNode *, TemplateData *); static int TemplateDetectorFunction(Packet *, struct _OptTreeNode *, OptFpList *); /* * * Function: SetupTemplate() * * Purpose: Generic detection engine plugin template. Registers the * configuration function and links it to a rule keyword. This is * the function that gets called from InitPlugins in plugbase.c. * * Arguments: None. * * Returns: void function * */ void SetupTemplate() { /* map the keyword to an initialization/processing function */ RegisterPlugin("keyword", TemplateInit); DebugMessage(DEBUG_PLUGIN,"Plugin: TemplateName Setup\n"); } /* * * Function: TemplateInit(char *, OptTreeNode *) * * Purpose: Generic rule configuration function. Handles parsing the rule * information and attaching the associated detection function to * the OTN. * * Arguments: data => rule arguments/data * otn => pointer to the current rule option list node * * Returns: void function * */ static void TemplateInit(char *data, OptTreeNode *otn, int protocol) { TemplateData *template_data; OptFpList *ofl; /* * allocate the data structure and attach * it to the rule's data struct list */ template_data = (TemplateData *) SnortAlloc(sizeof(TemplateData)); /* * If this is a transport layer protocol plugin, be sure to * check that the protocol that is passed in matches the * transport layer protocol that you're using for this rule! */ /* * any other initialization of this plugin should be performed here */ /* * this is where the keyword arguments are processed and * placed into the rule option's data structure */ TemplateRuleParseFunction(data, otn, template_data); /* * finally, attach the option's detection function * to the rule's detect function pointer list * * AddOptFuncToList returns a pointer to the node in * the function pointer list where the detector function * is linked into the detection engine, we will grab the * pointer to this node so that we can assign the * config data for this rule option to the functional * node's context pointer */ ofl = AddOptFuncToList(TemplateDetectorFunction, otn); /* * this is where we set the functional node's context pointer * so that the plugin can find the data to test the network * traffic against */ ofl->context = (void *) template_data; } /* * * Function: TemplateRuleParseFunction(char *, OptTreeNode *) * * Purpose: This is the function that is used to process the option keyword's * arguments and attach them to the rule's data structures. * * Arguments: data => argument data * otn => pointer to the current rule's OTN * td => pointer to the configuration storage struct * * Returns: void function * */ static void TemplateRuleParseFunction( char *data, OptTreeNode *otn, TemplateData *td) { /* * manipulate the option arguments here */ /* * see the code in src/detection_plugins for examples of parsing Snort * rule options */ /* * set the final option arguments here */ } /* * * Function: TemplateDetectorFunction(char *, OptTreeNode *, OptFpList *) * * Purpose: Use this function to perform the particular detection routine * that this rule keyword is supposed to encompass. * * Arguments: data => argument data * otn => pointer to the current rule's OTN * fp_list => pointer to the function pointer list current node * * Returns: If the detection test fails, this function *must* return a zero! * On success, it calls the next function in the detection list * */ static int TemplateDetectorFunction( Packet *p, struct _OptTreeNode *otn, OptFpList *fp_list) { TemplateData *td; /* ptr to the detection option's data */ /* * Try to make this function as quick as possible, the faster the * detection plugins are, the less packet loss the program will * experience! Avoid doing things like declaring variables or * anything other than just doing the test and moving on... */ /* * get the current option's context data */ td = (TemplateData *) fp_list->context; /* * your detection function tests go here */ if (the_test_is_successful) { /* call the next function in the function list recursively */ /* THIS CALL *MUST* BE IN THE PLUGIN, OTHERWISE YOU WILL BREAK SNORT'S DETECTION ENGINE!!! */ return fp_list->next->OptTestFunc(p, otn, fp_list->next); } #ifdef DEBUG else { /* * you can put debug comments here or not */ DebugMessage(DEBUG_PLUGIN,"No match\n"); } #endif /* * if the test isn't successful, this function *must* return 0 */ return 0; } snort-2.9.20/templates/spp_template.h0000644000175000017500000000053714241077575015756 0ustar apoapo/* $Id$ */ /* Snort Preprocessor Plugin Header File Template */ /* This file gets included in plugbase.h when it is integrated into the rest * of the program. */ #ifndef __SPP_TEMPLATE_H__ #define __SPP_TEMPLATE_H__ /* * list of function prototypes to export for this preprocessor */ void SetupTemplate(); #endif /* __SPP_TEMPLATE_H__ */ snort-2.9.20/templates/Makefile.in0000644000175000017500000003044514242725550015150 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = templates ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = depcomp = am__maybe_remake_depfiles = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies EXTRA_DIST = sp_template.c spp_template.c sp_template.h spp_template.h all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign templates/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign templates/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/templates/Makefile.am0000444000175000017500000000017314230012554015116 0ustar apoapo## $Id$ AUTOMAKE_OPTIONS=foreign no-dependencies EXTRA_DIST = sp_template.c spp_template.c sp_template.h spp_template.h snort-2.9.20/templates/spp_template.c0000644000175000017500000001271214241077574015746 0ustar apoapo/* $Id$ */ /* Snort Preprocessor Plugin Source File Template */ /* spp_template * * Purpose: * * Preprocessors perform some function *once* for *each* packet. This is * different from detection plugins, which are accessed depending on the * standard rules. When adding a plugin to the system, be sure to * add the "Setup" function to the InitPreprocessors() function call in * plugbase.c! * * Arguments: * * This is the list of arguments that the plugin can take at the * "preprocessor" line in the rules file * * Effect: * * What the preprocessor does. Check out some of the default ones * (e.g. spp_frag2) for a good example of this description. * * Comments: * * Any comments? * */ #include #include #include #include /* * If you're going to issue any alerts from this preproc you * should include generators.h and event_wrapper.h */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "generators.h" #include "event_wrapper.h" #include "util.h" #include "plugbase.h" #include "parser.h" /* * put in other inculdes as necessary */ /* * your preprocessor header file goes here if necessary, don't forget * to include the header file in plugbase.h too! */ #include "spp_template.h" /* * define any needed data structs for things like configuration */ typedef struct _TemplateData { /* Your struct members here */ } TemplateData; /* * If you need to instantiate the preprocessor's * data structure, do it here */ TemplateData SomeData; /* * function prototypes go here */ static void TemplateInit(u_char *); static void ParseTemplateArgs(char *); static void PreprocFunction(Packet *); static void PreprocCleanExitFunction(int, void *); static void PreprocRestartFunction(int, void *); /* * Function: SetupTemplate() * * Purpose: Registers the preprocessor keyword and initialization * function into the preprocessor list. This is the function that * gets called from InitPreprocessors() in plugbase.c. * * Arguments: None. * * Returns: void function * */ void SetupTemplate() { /* * link the preprocessor keyword to the init function in * the preproc list */ RegisterPreprocessor("keyword", TemplateInit); DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template is setup...\n"); } /* * Function: TemplateInit(u_char *) * * Purpose: Calls the argument parsing function, performs final setup on data * structs, links the preproc function into the function list. * * Arguments: args => ptr to argument string * * Returns: void function * */ static void TemplateInit(u_char *args) { DebugMessage(DEBUG_PLUGIN,"Preprocessor: Template Initialized\n"); /* * parse the argument list from the rules file */ ParseTemplateArgs(args); /* * perform any other initialization functions that are required here */ /* * Set the preprocessor function into the function list */ AddFuncToPreprocList(PreprocFunction); AddFuncToCleanExitList(PreprocCleanExitFunction, NULL); AddFuncToRestartList(PreprocRestartFunction, NULL); } /* * Function: ParseTemplateArgs(char *) * * Purpose: Process the preprocessor arguments from the rules file and * initialize the preprocessor's data struct. This function doesn't * have to exist if it makes sense to parse the args in the init * function. * * Arguments: args => argument list * * Returns: void function * */ static void ParseTemplateArgs(char *args) { /* your parsing function goes here, check out the other spp files for examples */ } /* * Function: PreprocFunction(Packet *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ static void PreprocFunction(Packet *p) { /* your preproc function goes here.... */ /* * if you need to issue an alert from your preprocessor, check out * event_wrapper.h, there are some useful helper functions there */ } /* * Function: PreprocCleanExitFunction(int, void *) * * Purpose: This function gets called when Snort is exiting, if there's * any cleanup that needs to be performed (e.g. closing files) * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * functioin when it was registered, may be * needed to properly exit * * Returns: void function */ static void PreprocCleanExitFunction(int signal, void *data) { /* clean exit code goes here */ } /* * Function: PreprocRestartFunction(int, void *) * * Purpose: This function gets called when Snort is restarting on a SIGHUP, * if there's any initialization or cleanup that needs to happen * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * functioin when it was registered, may be * needed to properly exit * * Returns: void function */ static void PreprocRestartFunction(int signal, void *foo) { /* restart code goes here */ } snort-2.9.20/templates/sp_template.h0000644000175000017500000000052514241077573015571 0ustar apoapo/* $Id$ */ /* Snort Detection Plugin Header File Template */ /* * This file gets included in plugbase.h when it is integrated into the rest * of the program. * * Export any functions or data structs you feel necessary. */ #ifndef __SP_TEMPLATE_H__ #define __SP_TEMPLATE_H__ void SetupTemplate(); #endif /* __SP_TEMPLATE_H__ */ snort-2.9.20/aclocal.m40000644000175000017500000136162514242725543012757 0ustar apoapo# generated automatically by aclocal 1.16.1 -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, [m4_warning([this file was generated for autoconf 2.69. You have another version of autoconf. It may work, but is not guaranteed to. If you have problems, you may need to regenerate the build system entirely. To do so, use the procedure documented by the package, typically 'autoreconf'.])]) # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # # Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl # Copyright (C) 2014 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # GNU Libtool 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 of the License, or # (at your option) any later version. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program or library that is built # using GNU Libtool, you may include this file under the same # distribution terms that you use for the rest of that program. # # GNU Libtool 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, see . ]) # serial 58 LT_INIT # LT_PREREQ(VERSION) # ------------------ # Complain and exit if this libtool version is less that VERSION. m4_defun([LT_PREREQ], [m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, [m4_default([$3], [m4_fatal([Libtool version $1 or higher is required], 63)])], [$2])]) # _LT_CHECK_BUILDDIR # ------------------ # Complain if the absolute build directory name contains unusual characters m4_defun([_LT_CHECK_BUILDDIR], [case `pwd` in *\ * | *\ *) AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; esac ]) # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], [AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl AC_BEFORE([$0], [LTDL_INIT])dnl m4_require([_LT_CHECK_BUILDDIR])dnl dnl Autoconf doesn't catch unexpanded LT_ macros by default: m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 dnl unless we require an AC_DEFUNed macro: AC_REQUIRE([LTOPTIONS_VERSION])dnl AC_REQUIRE([LTSUGAR_VERSION])dnl AC_REQUIRE([LTVERSION_VERSION])dnl AC_REQUIRE([LTOBSOLETE_VERSION])dnl m4_require([_LT_PROG_LTMAIN])dnl _LT_SHELL_INIT([SHELL=${CONFIG_SHELL-/bin/sh}]) dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl _LT_SETUP # Only expand once: m4_define([LT_INIT]) ])# LT_INIT # Old names: AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) # _LT_PREPARE_CC_BASENAME # ----------------------- m4_defun([_LT_PREPARE_CC_BASENAME], [ # Calculate cc_basename. Skip known compiler wrappers and cross-prefix. func_cc_basename () { for cc_temp in @S|@*""; do case $cc_temp in compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; \-*) ;; *) break;; esac done func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` } ])# _LT_PREPARE_CC_BASENAME # _LT_CC_BASENAME(CC) # ------------------- # It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, # but that macro is also expanded into generated libtool script, which # arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], [m4_require([_LT_PREPARE_CC_BASENAME])dnl AC_REQUIRE([_LT_DECL_SED])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl func_cc_basename $1 cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set # sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} : ${RM="rm -f"} ])# _LT_FILEUTILS_DEFAULTS # _LT_SETUP # --------- m4_defun([_LT_SETUP], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl _LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl dnl _LT_DECL([], [build_alias], [0], [The build system])dnl _LT_DECL([], [build], [0])dnl _LT_DECL([], [build_os], [0])dnl dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl dnl AC_REQUIRE([AC_PROG_LN_S])dnl test -z "$LN_S" && LN_S="ln -s" _LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl AC_REQUIRE([LT_CMD_MAX_LEN])dnl _LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl _LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PATH_CONVERSION_FUNCTIONS])dnl m4_require([_LT_CMD_RELOAD])dnl m4_require([_LT_CHECK_MAGIC_METHOD])dnl m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi _LT_CHECK_OBJDIR m4_require([_LT_TAG_COMPILER])dnl case $host_os in aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi ;; esac # Global variables: ofile=libtool can_build_shared=yes # All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a with_gnu_ld=$lt_cv_prog_gnu_ld old_CC=$CC old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS test -z "$LD" && LD=ld test -z "$ac_objext" && ac_objext=o _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then _LT_PATH_MAGIC fi ;; esac # Use C for the default configuration in the libtool script LT_SUPPORTED_TAG([CC]) _LT_LANG_C_CONFIG _LT_LANG_DEFAULT_CONFIG _LT_CONFIG_COMMANDS ])# _LT_SETUP # _LT_PREPARE_SED_QUOTE_VARS # -------------------------- # Define a few sed substitution that help us do robust quoting. m4_defun([_LT_PREPARE_SED_QUOTE_VARS], [# Backslashify metacharacters that are still active within # double-quoted strings. sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' # Sed substitution to delay expansion of an escaped single quote. delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' ]) # _LT_PROG_LTMAIN # --------------- # Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, # 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS # in macros and then make a single call at the end using the 'libtool' # label. # _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) # ---------------------------------------- # Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL_INIT], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_INIT], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_INIT]) # _LT_CONFIG_LIBTOOL([COMMANDS]) # ------------------------------ # Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. m4_define([_LT_CONFIG_LIBTOOL], [m4_ifval([$1], [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], [$1 ])])]) # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) # _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) # ----------------------------------------------------- m4_defun([_LT_CONFIG_SAVE_COMMANDS], [_LT_CONFIG_LIBTOOL([$1]) _LT_CONFIG_LIBTOOL_INIT([$2]) ]) # _LT_FORMAT_COMMENT([COMMENT]) # ----------------------------- # Add leading comment marks to the start of each line, and a trailing # full-stop to the whole comment if one is not present already. m4_define([_LT_FORMAT_COMMENT], [m4_ifval([$1], [ m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) )]) # _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) # ------------------------------------------------------------------- # CONFIGNAME is the name given to the value in the libtool script. # VARNAME is the (base) name used in the configure script. # VALUE may be 0, 1 or 2 for a computed quote escaped value based on # VARNAME. Any other value will be used directly. m4_define([_LT_DECL], [lt_if_append_uniq([lt_decl_varnames], [$2], [, ], [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], [m4_ifval([$1], [$1], [$2])]) lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) m4_ifval([$4], [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) lt_dict_add_subkey([lt_decl_dict], [$2], [tagged?], [m4_ifval([$5], [yes], [no])])]) ]) # _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) # -------------------------------------------------------- m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) # lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_tag_varnames], [_lt_decl_filter([tagged?], [yes], $@)]) # _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) # --------------------------------------------------------- m4_define([_lt_decl_filter], [m4_case([$#], [0], [m4_fatal([$0: too few arguments: $#])], [1], [m4_fatal([$0: too few arguments: $#: $1])], [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], [lt_dict_filter([lt_decl_dict], $@)])[]dnl ]) # lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) # -------------------------------------------------- m4_define([lt_decl_quote_varnames], [_lt_decl_filter([value], [1], $@)]) # lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_dquote_varnames], [_lt_decl_filter([value], [2], $@)]) # lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) # --------------------------------------------------- m4_define([lt_decl_varnames_tagged], [m4_assert([$# <= 2])dnl _$0(m4_quote(m4_default([$1], [[, ]])), m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) m4_define([_lt_decl_varnames_tagged], [m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) # lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) # ------------------------------------------------ m4_define([lt_decl_all_varnames], [_$0(m4_quote(m4_default([$1], [[, ]])), m4_if([$2], [], m4_quote(lt_decl_varnames), m4_quote(m4_shift($@))))[]dnl ]) m4_define([_lt_decl_all_varnames], [lt_join($@, lt_decl_varnames_tagged([$1], lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl ]) # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ # Quote a variable value, and forward it to 'config.status' so that its # declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) # _LT_CONFIG_STATUS_DECLARATIONS # ------------------------------ # We delimit libtool config variables with single quotes, so when # we write them to config.status, we have to be sure to quote all # embedded single quotes properly. In configure, this macro expands # each variable declared with _LT_DECL (and _LT_TAGDECL) into: # # ='`$ECHO "$" | $SED "$delay_single_quote_subst"`' m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], [m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAGS # ---------------- # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl available_tags='_LT_TAGS'dnl ]) # _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) # ----------------------------------- # Extract the dictionary values for VARNAME (optionally with TAG) and # expand to a commented shell variable setting: # # # Some comment about what VAR is for. # visible_name=$lt_internal_name m4_define([_LT_LIBTOOL_DECLARE], [_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [description])))[]dnl m4_pushdef([_libtool_name], m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), [0], [_libtool_name=[$]$1], [1], [_libtool_name=$lt_[]$1], [2], [_libtool_name=$lt_[]$1], [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl ]) # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables # suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], [m4_foreach([_lt_var], m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) # _LT_LIBTOOL_TAG_VARS(TAG) # ------------------------- m4_define([_LT_LIBTOOL_TAG_VARS], [m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) # _LT_TAGVAR(VARNAME, [TAGNAME]) # ------------------------------ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # _LT_CONFIG_COMMANDS # ------------------- # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations # into 'config.status', and then the shell code to quote escape them in # for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], dnl If the libtool generation code has been placed in $CONFIG_LT, dnl instead of duplicating it all over again into config.status, dnl then we will have config.status run $CONFIG_LT later, so it dnl needs to know what name is stored there: [AC_CONFIG_COMMANDS([libtool], [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], dnl If the libtool generation code is destined for config.status, dnl expand the accumulated commands and init code now: [AC_CONFIG_COMMANDS([libtool], [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) ])#_LT_CONFIG_COMMANDS # Initialize. m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], [ # The HP-UX ksh and POSIX shell print the target directory to stdout # if CDPATH is set. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH sed_quote_subst='$sed_quote_subst' double_quote_subst='$double_quote_subst' delay_variable_subst='$delay_variable_subst' _LT_CONFIG_STATUS_DECLARATIONS LTCC='$LTCC' LTCFLAGS='$LTCFLAGS' compiler='$compiler_DEFAULT' # A function that is used when there is no print builtin or printf. func_fallback_echo () { eval 'cat <<_LTECHO_EOF \$[]1 _LTECHO_EOF' } # Quote evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done # Double-quote double-evaled strings. for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac done _LT_OUTPUT_LIBTOOL_INIT ]) # _LT_GENERATED_FILE_INIT(FILE, [COMMENT]) # ------------------------------------ # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the # '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). m4_ifdef([AS_INIT_GENERATED], [m4_defun([_LT_GENERATED_FILE_INIT],[AS_INIT_GENERATED($@)])], [m4_defun([_LT_GENERATED_FILE_INIT], [m4_require([AS_PREPARE])]dnl [m4_pushdef([AS_MESSAGE_LOG_FD])]dnl [lt_write_fail=0 cat >$1 <<_ASEOF || lt_write_fail=1 #! $SHELL # Generated by $as_me. $2 SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$1 <<\_ASEOF || lt_write_fail=1 AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT # --------- # This macro allows early generation of the libtool script (before # AC_OUTPUT is called), incase it is used in configure for compilation # tests. AC_DEFUN([LT_OUTPUT], [: ${CONFIG_LT=./config.lt} AC_MSG_NOTICE([creating $CONFIG_LT]) _LT_GENERATED_FILE_INIT(["$CONFIG_LT"], [# Run this file to recreate a libtool stub with the current configuration.]) cat >>"$CONFIG_LT" <<\_LTEOF lt_cl_silent=false exec AS_MESSAGE_LOG_FD>>config.log { echo AS_BOX([Running $as_me.]) } >&AS_MESSAGE_LOG_FD lt_cl_help="\ '$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. Usage: $[0] [[OPTIONS]] -h, --help print this help, then exit -V, --version print version number, then exit -q, --quiet do not print progress messages -d, --debug don't remove temporary files Report bugs to ." lt_cl_version="\ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." while test 0 != $[#] do case $[1] in --version | --v* | -V ) echo "$lt_cl_version"; exit 0 ;; --help | --h* | -h ) echo "$lt_cl_help"; exit 0 ;; --debug | --d* | -d ) debug=: ;; --quiet | --q* | --silent | --s* | -q ) lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] Try '$[0] --help' for more information.]) ;; esac shift done if $lt_cl_silent; then exec AS_MESSAGE_FD>/dev/null fi _LTEOF cat >>"$CONFIG_LT" <<_LTEOF _LT_OUTPUT_LIBTOOL_COMMANDS_INIT _LTEOF cat >>"$CONFIG_LT" <<\_LTEOF AC_MSG_NOTICE([creating $ofile]) _LT_OUTPUT_LIBTOOL_COMMANDS AS_EXIT(0) _LTEOF chmod +x "$CONFIG_LT" # configure is writing to config.log, but config.lt does its own redirection, # appending to config.log, which fails on DOS, as config.log is still kept # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false exec AS_MESSAGE_LOG_FD>>config.log $lt_cl_success || AS_EXIT(1) ])# LT_OUTPUT # _LT_CONFIG(TAG) # --------------- # If TAG is the built-in tag, create an initial libtool script with a # default configuration from the untagged config vars. Otherwise add code # to config.status for appending the configuration named by TAG from the # matching tagged config vars. m4_defun([_LT_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL # Generated automatically by $as_me ($PACKAGE) $VERSION # Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: # NOTE: Changes made to this file will be lost: look at ltmain.sh. # Provide generalized library-building support services. # Written by Gordon Matzigkeit, 1996 _LT_COPYING _LT_LIBTOOL_TAGS # Configured defaults for sys_lib_dlsearch_path munging. : \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG _LT_EOF cat <<'_LT_EOF' >> "$cfgfile" # ### BEGIN FUNCTIONS SHARED WITH CONFIGURE _LT_PREPARE_MUNGE_PATH_LIST _LT_PREPARE_CC_BASENAME # ### END FUNCTIONS SHARED WITH CONFIGURE _LT_EOF case $host_os in aix3*) cat <<\_LT_EOF >> "$cfgfile" # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi _LT_EOF ;; esac _LT_PROG_LTMAIN # We use sed instead of cat because bash on DJGPP gets confused if # if finds mixed CR/LF and LF-only lines. Since sed operates in # text mode, it properly converts lines to CR/LF. This bash problem # is reportedly fixed, but why not run on old versions too? sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" ], [cat <<_LT_EOF >> "$ofile" dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded dnl in a comment (ie after a #). # ### BEGIN LIBTOOL TAG CONFIG: $1 _LT_LIBTOOL_TAG_VARS(_LT_TAG) # ### END LIBTOOL TAG CONFIG: $1 _LT_EOF ])dnl /m4_if ], [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS ])# _LT_CONFIG # LT_SUPPORTED_TAG(TAG) # --------------------- # Trace this macro to discover what tags are supported by the libtool # --tag option, using: # autoconf --trace 'LT_SUPPORTED_TAG:$1' AC_DEFUN([LT_SUPPORTED_TAG], []) # C support is built-in for now m4_define([_LT_LANG_C_enabled], []) m4_define([_LT_TAGS], []) # LT_LANG(LANG) # ------------- # Enable libtool support for the given language if not already enabled. AC_DEFUN([LT_LANG], [AC_BEFORE([$0], [LT_OUTPUT])dnl m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], [Windows Resource], [_LT_LANG(RC)], [m4_ifdef([_LT_LANG_]$1[_CONFIG], [_LT_LANG($1)], [m4_fatal([$0: unsupported language: "$1"])])])dnl ])# LT_LANG # _LT_LANG(LANGNAME) # ------------------ m4_defun([_LT_LANG], [m4_ifdef([_LT_LANG_]$1[_enabled], [], [LT_SUPPORTED_TAG([$1])dnl m4_append([_LT_TAGS], [$1 ])dnl m4_define([_LT_LANG_]$1[_enabled], [])dnl _LT_LANG_$1_CONFIG($1)])dnl ])# _LT_LANG m4_ifndef([AC_PROG_GO], [ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_GO. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # m4_defun([AC_PROG_GO], [AC_LANG_PUSH(Go)dnl AC_ARG_VAR([GOC], [Go compiler command])dnl AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl _AC_ARG_VAR_LDFLAGS()dnl AC_CHECK_TOOL(GOC, gccgo) if test -z "$GOC"; then if test -n "$ac_tool_prefix"; then AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) fi fi if test -z "$GOC"; then AC_CHECK_PROG(GOC, gccgo, gccgo, false) fi ])#m4_defun ])#m4_ifndef # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], [AC_PROVIDE_IFELSE([AC_PROG_CXX], [LT_LANG(CXX)], [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) AC_PROVIDE_IFELSE([AC_PROG_F77], [LT_LANG(F77)], [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) AC_PROVIDE_IFELSE([AC_PROG_FC], [LT_LANG(FC)], [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal dnl pulling things in needlessly. AC_PROVIDE_IFELSE([AC_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], [LT_LANG(GCJ)], [AC_PROVIDE_IFELSE([LT_PROG_GCJ], [LT_LANG(GCJ)], [m4_ifdef([AC_PROG_GCJ], [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([A][M_PROG_GCJ], [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) AC_PROVIDE_IFELSE([AC_PROG_GO], [LT_LANG(GO)], [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) ])# _LT_LANG_DEFAULT_CONFIG # Obsolete macros: AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) AU_DEFUN([AC_LIBTOOL_RC], [LT_LANG(Windows Resource)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_CXX], []) dnl AC_DEFUN([AC_LIBTOOL_F77], []) dnl AC_DEFUN([AC_LIBTOOL_FC], []) dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) dnl AC_DEFUN([AC_LIBTOOL_RC], []) # _LT_TAG_COMPILER # ---------------- m4_defun([_LT_TAG_COMPILER], [AC_REQUIRE([AC_PROG_CC])dnl _LT_DECL([LTCC], [CC], [1], [A C compiler])dnl _LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl _LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl _LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl # If no C compiler was specified, use CC. LTCC=${LTCC-"$CC"} # If no C compiler flags were specified, use CFLAGS. LTCFLAGS=${LTCFLAGS-"$CFLAGS"} # Allow CC to be a program name with arguments. compiler=$CC ])# _LT_TAG_COMPILER # _LT_COMPILER_BOILERPLATE # ------------------------ # Check for compiler boilerplate output or warnings with # the simple compiler test code. m4_defun([_LT_COMPILER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_compile_test_code" >conftest.$ac_ext eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_compiler_boilerplate=`cat conftest.err` $RM conftest* ])# _LT_COMPILER_BOILERPLATE # _LT_LINKER_BOILERPLATE # ---------------------- # Check for linker boilerplate output or warnings with # the simple link test code. m4_defun([_LT_LINKER_BOILERPLATE], [m4_require([_LT_DECL_SED])dnl ac_outfile=conftest.$ac_objext echo "$lt_simple_link_test_code" >conftest.$ac_ext eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err _lt_linker_boilerplate=`cat conftest.err` $RM -r conftest* ])# _LT_LINKER_BOILERPLATE # _LT_REQUIRED_DARWIN_CHECKS # ------------------------- m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in rhapsody* | darwin*) AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) AC_CHECK_TOOL([LIPO], [lipo], [:]) AC_CHECK_TOOL([OTOOL], [otool], [:]) AC_CHECK_TOOL([OTOOL64], [otool64], [:]) _LT_DECL([], [DSYMUTIL], [1], [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) _LT_DECL([], [NMEDIT], [1], [Tool to change global to local symbols on Mac OS X]) _LT_DECL([], [LIPO], [1], [Tool to manipulate fat objects and archives on Mac OS X]) _LT_DECL([], [OTOOL], [1], [ldd/readelf like tool for Mach-O binaries on Mac OS X]) _LT_DECL([], [OTOOL64], [1], [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the # link flags. rm -rf libconftest.dylib* echo "int foo(void){return 1;}" > conftest.c echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? # If there is a non-empty error log, and "single_module" # appears in it, assume the flag caused a linker warning if test -s conftest.err && $GREP single_module conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD # Otherwise, if the output was created with a 0 exit code from # the compiler, it worked. elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -rf libconftest.dylib* rm -f conftest.* fi]) AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no save_LDFLAGS=$LDFLAGS echo "_main" > conftest.sym LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) LDFLAGS=$save_LDFLAGS ]) AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF int main() { return 0;} _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? if test -s conftest.err && $GREP force_load conftest.err; then cat conftest.err >&AS_MESSAGE_LOG_FD elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD fi rm -f conftest.err libconftest.a conftest conftest.c rm -rf conftest.dSYM ]) case $host_os in rhapsody* | darwin1.[[012]]) _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; 10.[[012]][[,.]]*) _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; 10.*) _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi if test yes = "$lt_cv_ld_exported_symbols_list"; then _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= fi ;; esac ]) # _LT_DARWIN_LINKER_FEATURES([TAG]) # --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ m4_require([_LT_REQUIRED_DARWIN_CHECKS]) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported if test yes = "$lt_cv_ld_force_load"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], [ if test yes != "$lt_cv_apple_cc_single_mod"; then _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else _LT_TAGVAR(ld_shlibs, $1)=no fi ]) # _LT_SYS_MODULE_PATH_AIX([TAGNAME]) # ---------------------------------- # Links a minimal program and checks the executable # for the system default hardcoded library path. In most cases, # this is /usr/lib:/lib, but when the MPI compilers are used # the location of the communication and MPI libs are included too. # If we don't find anything, use the default library path according # to the aix ld manual. # Store the results from the different compilers for each TAGNAME. # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], [AC_LINK_IFELSE([AC_LANG_PROGRAM],[ lt_aix_libpath_sed='[ /Import File Strings/,/^$/ { /^0/ { s/^0 *\([^ ]*\) *$/\1/ p } }]' _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` # Check for a 64-bit object if we didn't find anything. if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) fi ])# _LT_SYS_MODULE_PATH_AIX # _LT_SHELL_INIT(ARG) # ------------------- m4_define([_LT_SHELL_INIT], [m4_divert_text([M4SH-INIT], [$1 ])])# _LT_SHELL_INIT # _LT_PROG_ECHO_BACKSLASH # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start # of the generated configure script that will find a shell with a builtin # printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO AC_MSG_CHECKING([how to print strings]) # Test print first, because it will be a builtin if present. if test "X`( print -r -- -n ) 2>/dev/null`" = X-n && \ test "X`print -r -- $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='print -r --' elif test "X`printf %s $ECHO 2>/dev/null`" = "X$ECHO"; then ECHO='printf %s\n' else # Use this function as a fallback that always works. func_fallback_echo () { eval 'cat <<_LTECHO_EOF $[]1 _LTECHO_EOF' } ECHO='func_fallback_echo' fi # func_echo_all arg... # Invoke $ECHO with all args, space-separated. func_echo_all () { $ECHO "$*" } case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; esac m4_ifdef([_AS_DETECT_SUGGESTED], [_AS_DETECT_SUGGESTED([ test -n "${ZSH_VERSION+set}${BASH_VERSION+set}" || ( ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO$ECHO PATH=/empty FPATH=/empty; export PATH FPATH test "X`printf %s $ECHO`" = "X$ECHO" \ || test "X`print -r -- $ECHO`" = "X$ECHO" )])]) _LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) ])# _LT_PROG_ECHO_BACKSLASH # _LT_WITH_SYSROOT # ---------------- AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], [AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], [Search for dependent libraries within DIR (or the compiler's sysroot if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= case $with_sysroot in #( yes) if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( /*) lt_sysroot=`echo "$with_sysroot" | sed -e "$sed_quote_subst"` ;; #( no|'') ;; #( *) AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl [dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) # Find out what ABI is being produced by ac_compile, and set mode # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) HPUX_IA64_MODE=32 ;; *ELF-64*) HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" ;; *N32*) LD="${LD-ld} -melf32bmipn32" ;; *64-bit*) LD="${LD-ld} -melf64bmip" ;; esac else case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -32" ;; *N32*) LD="${LD-ld} -n32" ;; *64-bit*) LD="${LD-ld} -64" ;; esac fi fi rm -rf conftest* ;; mips64*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then emul=elf case `/usr/bin/file conftest.$ac_objext` in *32-bit*) emul="${emul}32" ;; *64-bit*) emul="${emul}64" ;; esac case `/usr/bin/file conftest.$ac_objext` in *MSB*) emul="${emul}btsmip" ;; *LSB*) emul="${emul}ltsmip" ;; esac case `/usr/bin/file conftest.$ac_objext` in *N32*) emul="${emul}n32" ;; esac LD="${LD-ld} -m $emul" fi rm -rf conftest* ;; x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. Note that the listed cases only cover the # situations where additional linker options are needed (such as when # doing 32-bit compilation for a host where ld defaults to 64-bit, or # vice versa); the common cases where no linker options are needed do # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *32-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) case `/usr/bin/file conftest.o` in *x86-64*) LD="${LD-ld} -m elf32_x86_64" ;; *) LD="${LD-ld} -m elf_i386" ;; esac ;; powerpc64le-*linux*) LD="${LD-ld} -m elf32lppclinux" ;; powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) LD="${LD-ld} -m elf_s390" ;; sparc64-*linux*) LD="${LD-ld} -m elf32_sparc" ;; esac ;; *64-bit*) case $host in x86_64-*kfreebsd*-gnu) LD="${LD-ld} -m elf_x86_64_fbsd" ;; x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; powerpcle-*linux*) LD="${LD-ld} -m elf64lppc" ;; powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) LD="${LD-ld} -m elf64_s390" ;; sparc*-*linux*) LD="${LD-ld} -m elf64_sparc" ;; esac ;; esac fi rm -rf conftest* ;; *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf CFLAGS=$SAVE_CFLAGS fi ;; *-*solaris*) # Find out what ABI is being produced by ac_compile, and set linker # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in yes*) case $host in i?86-*-solaris*|x86_64-*-solaris*) LD="${LD-ld} -m elf_x86_64" ;; sparc*-*-solaris*) LD="${LD-ld} -m elf64_sparc" ;; esac # GNU ld 2.21 introduced _sol2 emulations. Use them if available. if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then LD=${LD-ld}_sol2 fi ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" fi ;; esac ;; esac fi rm -rf conftest* ;; esac need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK # _LT_PROG_AR # ----------- m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} : ${AR_FLAGS=cru} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [lt_cv_ar_at_file=no AC_COMPILE_IFELSE([AC_LANG_PROGRAM], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi rm -f conftest.* libconftest.a ]) ]) if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file fi _LT_DECL([], [archiver_list_spec], [1], [How to feed a file listing to the archiver]) ])# _LT_PROG_AR # _LT_CMD_OLD_ARCHIVE # ------------------- m4_defun([_LT_CMD_OLD_ARCHIVE], [_LT_PROG_AR AC_CHECK_TOOL(STRIP, strip, :) test -z "$STRIP" && STRIP=: _LT_DECL([], [STRIP], [1], [A symbol stripping program]) AC_CHECK_TOOL(RANLIB, ranlib, :) test -z "$RANLIB" && RANLIB=: _LT_DECL([], [RANLIB], [1], [Commands used to install an old-style archive]) # Determine commands to create old-style static archives. old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' old_postinstall_cmds='chmod 644 $oldlib' old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in bitrig* | openbsd*) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in darwin*) lock_old_archive_extraction=yes ;; *) lock_old_archive_extraction=no ;; esac _LT_DECL([], [old_postinstall_cmds], [2]) _LT_DECL([], [old_postuninstall_cmds], [2]) _LT_TAGDECL([], [old_archive_cmds], [2], [Commands used to build an old-style archive]) _LT_DECL([], [lock_old_archive_extraction], [0], [Whether to use a lock for old archive extraction]) ])# _LT_CMD_OLD_ARCHIVE # _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------------------- # Check whether the given compiler option works AC_DEFUN([_LT_COMPILER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. # The option is referenced via a variable to avoid confusing sed. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>conftest.err) ac_status=$? cat conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s "$ac_outfile"; then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings other than the usual output. $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' >conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi fi $RM conftest* ]) if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) fi ])# _LT_COMPILER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) # _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, # [ACTION-SUCCESS], [ACTION-FAILURE]) # ---------------------------------------------------- # Check whether the given linker option works AC_DEFUN([_LT_LINKER_OPTION], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then # The linker can only warn and ignore the option if not recognized # So say no if there are warnings if test -s conftest.err; then # Append any errors to the config.log. cat conftest.err 1>&AS_MESSAGE_LOG_FD $ECHO "$_lt_linker_boilerplate" | $SED '/^$/d' > conftest.exp $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 if diff conftest.exp conftest.er2 >/dev/null; then $2=yes fi else $2=yes fi fi $RM -r conftest* LDFLAGS=$save_LDFLAGS ]) if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) fi ])# _LT_LINKER_OPTION # Old name: AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) # LT_CMD_MAX_LEN #--------------- AC_DEFUN([LT_CMD_MAX_LEN], [AC_REQUIRE([AC_CANONICAL_HOST])dnl # find the maximum length of command line arguments AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 teststring=ABCD case $build_os in msdosdjgpp*) # On DJGPP, this test can blow up pretty badly due to problems in libc # (any single argument exceeding 2000 bytes causes a buffer overrun # during glob expansion). Even if it were fixed, the result of this # check would be larger than it should be. lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; gnu*) # Under GNU Hurd, this test is not required because there is # no limit to the length of command line arguments. # Libtool will interpret -1 as no limit whatsoever lt_cv_sys_max_cmd_len=-1; ;; cygwin* | mingw* | cegcc*) # On Win9x/ME, this test blows up -- it succeeds, but takes # about 5 minutes as the teststring grows exponentially. # Worse, since 9x/ME are not pre-emptively multitasking, # you end up with a "frozen" computer, even though with patience # the test eventually succeeds (with a max line length of 256k). # Instead, let's just punt: use the minimum linelength reported by # all of the supported platforms: 8192 (on NT/2K/XP). lt_cv_sys_max_cmd_len=8192; ;; mint*) # On MiNT this can take a long time and run out of memory. lt_cv_sys_max_cmd_len=8192; ;; amigaos*) # On AmigaOS with pdksh, this test takes hours, literally. # So we just punt and use a minimum line length of 8192. lt_cv_sys_max_cmd_len=8192; ;; bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` elif test -x /usr/sbin/sysctl; then lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi # And add a safety zone lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; interix*) # We know the value 262144 and hardcode it with a safety zone (like BSD) lt_cv_sys_max_cmd_len=196608 ;; os2*) # The test takes a long time on OS/2. lt_cv_sys_max_cmd_len=8192 ;; osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not # nice to cause kernel panics so lets avoid the loop below. # First set a reasonable default. lt_cv_sys_max_cmd_len=16384 # if test -x /sbin/sysconfig; then case `/sbin/sysconfig -q proc exec_disable_arg_limit` in *1*) lt_cv_sys_max_cmd_len=-1 ;; esac fi ;; sco3.2v5*) lt_cv_sys_max_cmd_len=102400 ;; sysv5* | sco5v6* | sysv4.2uw2*) kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` if test -n "$kargmax"; then lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` else lt_cv_sys_max_cmd_len=32768 fi ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` if test -n "$lt_cv_sys_max_cmd_len" && \ test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring done # Only check the string length outside the loop. lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` teststring= # Add a significant safety factor because C++ compilers can tack on # massive amounts of additional arguments before passing them to the # linker. It appears as though 1/2 is a usable value. lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` fi ;; esac ]) if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) fi max_cmd_len=$lt_cv_sys_max_cmd_len _LT_DECL([], [max_cmd_len], [0], [What is the maximum length of a command?]) ])# LT_CMD_MAX_LEN # Old name: AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) # _LT_HEADER_DLFCN # ---------------- m4_defun([_LT_HEADER_DLFCN], [AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl ])# _LT_HEADER_DLFCN # _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, # ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 lt_status=$lt_dlunknown cat > conftest.$ac_ext <<_LT_EOF [#line $LINENO "configure" #include "confdefs.h" #if HAVE_DLFCN_H #include #endif #include #ifdef RTLD_GLOBAL # define LT_DLGLOBAL RTLD_GLOBAL #else # ifdef DL_GLOBAL # define LT_DLGLOBAL DL_GLOBAL # else # define LT_DLGLOBAL 0 # endif #endif /* We may have to define LT_DLLAZY_OR_NOW in the command line if we find out it does not work in some platform. */ #ifndef LT_DLLAZY_OR_NOW # ifdef RTLD_LAZY # define LT_DLLAZY_OR_NOW RTLD_LAZY # else # ifdef DL_LAZY # define LT_DLLAZY_OR_NOW DL_LAZY # else # ifdef RTLD_NOW # define LT_DLLAZY_OR_NOW RTLD_NOW # else # ifdef DL_NOW # define LT_DLLAZY_OR_NOW DL_NOW # else # define LT_DLLAZY_OR_NOW 0 # endif # endif # endif # endif #endif /* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ #if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif int fnord () { return 42; } int main () { void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); int status = $lt_dlunknown; if (self) { if (dlsym (self,"fnord")) status = $lt_dlno_uscore; else { if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; else puts (dlerror ()); } /* dlclose (self); */ } else puts (dlerror ()); return status; }] _LT_EOF if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in x$lt_dlno_uscore) $1 ;; x$lt_dlneed_uscore) $2 ;; x$lt_dlunknown|x*) $3 ;; esac else : # compilation failed $3 fi fi rm -fr conftest* ])# _LT_TRY_DLOPEN_SELF # LT_SYS_DLOPEN_SELF # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown else lt_cv_dlopen=no lt_cv_dlopen_libs= case $host_os in beos*) lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; tpf*) # Don't try to run any link tests for TPF. We know it's impossible # because TPF is a cross-compiler, and we know how we open DSOs. lt_cv_dlopen=dlopen lt_cv_dlopen_libs= lt_cv_dlopen_self=no ;; *) AC_CHECK_FUNC([shl_load], [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) ]) ]) ;; esac if test no = "$lt_cv_dlopen"; then enable_dlopen=no else enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) save_CPPFLAGS=$CPPFLAGS test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], lt_cv_dlopen_self, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl _LT_TRY_DLOPEN_SELF( lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) ]) fi CPPFLAGS=$save_CPPFLAGS LDFLAGS=$save_LDFLAGS LIBS=$save_LIBS ;; esac case $lt_cv_dlopen_self in yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; *) enable_dlopen_self=unknown ;; esac case $lt_cv_dlopen_self_static in yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; *) enable_dlopen_self_static=unknown ;; esac fi _LT_DECL([dlopen_support], [enable_dlopen], [0], [Whether dlopen is supported]) _LT_DECL([dlopen_self], [enable_dlopen_self], [0], [Whether dlopen of programs is supported]) _LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], [Whether dlopen of statically linked programs is supported]) ])# LT_SYS_DLOPEN_SELF # Old name: AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) # _LT_COMPILER_C_O([TAGNAME]) # --------------------------- # Check to see if options -c and -o are simultaneously supported by compiler. # This macro does not hard code the compiler like AC_PROG_CC_C_O. m4_defun([_LT_COMPILER_C_O], [m4_require([_LT_DECL_SED])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no $RM -r conftest 2>/dev/null mkdir conftest cd conftest mkdir out echo "$lt_simple_compile_test_code" > conftest.$ac_ext lt_compiler_flag="-o out/conftest2.$ac_objext" # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins # with a dollar sign (not a hyphen), so the echo should work correctly. lt_compile=`echo "$ac_compile" | $SED \ -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ -e 's:$: $lt_compiler_flag:'` (eval echo "\"\$as_me:$LINENO: $lt_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$lt_compile" 2>out/conftest.err) ac_status=$? cat out/conftest.err >&AS_MESSAGE_LOG_FD echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD if (exit $ac_status) && test -s out/conftest2.$ac_objext then # The compiler can only warn and ignore the option if not recognized # So say no if there are warnings $ECHO "$_lt_compiler_boilerplate" | $SED '/^$/d' > out/conftest.exp $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes fi fi chmod u+w . 2>&AS_MESSAGE_LOG_FD $RM conftest* # SGI C++ compiler will create directory out/ii_files/ for # template instantiation test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files $RM out/* && rmdir out cd .. $RM -r conftest $RM conftest* ]) _LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], [Does compiler simultaneously support -c and -o options?]) ])# _LT_COMPILER_C_O # _LT_COMPILER_FILE_LOCKS([TAGNAME]) # ---------------------------------- # Check to see if we can do hard links to lock some files if needed m4_defun([_LT_COMPILER_FILE_LOCKS], [m4_require([_LT_ENABLE_LOCK])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) hard_links=nottested if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes $RM conftest* ln conftest.a conftest.b 2>/dev/null && hard_links=no touch conftest.a ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) if test no = "$hard_links"; then AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else need_locks=no fi _LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) ])# _LT_COMPILER_FILE_LOCKS # _LT_CHECK_OBJDIR # ---------------- m4_defun([_LT_CHECK_OBJDIR], [AC_CACHE_CHECK([for objdir], [lt_cv_objdir], [rm -f .libs 2>/dev/null mkdir .libs 2>/dev/null if test -d .libs; then lt_cv_objdir=.libs else # MS-DOS does not allow filenames that begin with a dot. lt_cv_objdir=_libs fi rmdir .libs 2>/dev/null]) objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR # _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) # -------------------------------------- # Check hardcoding attributes. m4_defun([_LT_LINKER_HARDCODE_LIBPATH], [AC_MSG_CHECKING([how to hardcode library paths into programs]) _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. _LT_TAGVAR(hardcode_action, $1)=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. _LT_TAGVAR(hardcode_action, $1)=unsupported fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no elif test yes = "$shlibpath_overrides_runpath" || test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi _LT_TAGDECL([], [hardcode_action], [0], [How to hardcode a shared library path into an executable]) ])# _LT_LINKER_HARDCODE_LIBPATH # _LT_CMD_STRIPLIB # ---------------- m4_defun([_LT_CMD_STRIPLIB], [m4_require([_LT_DECL_EGREP]) striplib= old_striplib= AC_MSG_CHECKING([whether stripping libraries is possible]) if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" test -z "$striplib" && striplib="$STRIP --strip-unneeded" AC_MSG_RESULT([yes]) else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) fi ;; *) AC_MSG_RESULT([no]) ;; esac fi _LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB # _LT_PREPARE_MUNGE_PATH_LIST # --------------------------- # Make sure func_munge_path_list() is defined correctly. m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], [[# func_munge_path_list VARIABLE PATH # ----------------------------------- # VARIABLE is name of variable containing _space_ separated list of # directories to be munged by the contents of PATH, which is string # having a format: # "DIR[:DIR]:" # string "DIR[ DIR]" will be prepended to VARIABLE # ":DIR[:DIR]" # string "DIR[ DIR]" will be appended to VARIABLE # "DIRP[:DIRP]::[DIRA:]DIRA" # string "DIRP[ DIRP]" will be prepended to VARIABLE and string # "DIRA[ DIRA]" will be appended to VARIABLE # "DIR[:DIR]" # VARIABLE will be replaced by "DIR[ DIR]" func_munge_path_list () { case x@S|@2 in x) ;; *:) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" ;; x:*) eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; *::*) eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" ;; *) eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" ;; esac } ]])# _LT_PREPARE_PATH_LIST # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics m4_defun([_LT_SYS_DYNAMIC_LINKER], [AC_REQUIRE([AC_CANONICAL_HOST])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ if test yes = "$GCC"; then case $host_os in darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in *\;*) # if the path contains ";" then we assume it to be the separator # otherwise default to the standard path separator (i.e. ":") - it is # assumed that no part of a normal pathname contains ";" but that should # okay in the real world where ";" in dirpaths is itself problematic. lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED 's/;/ /g'` ;; *) lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED "s/$PATH_SEPARATOR/ /g"` ;; esac # Ok, now we have the path, separated by spaces, we can step through it # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` # ...but if some path component already ends with the multilib dir we assume # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). case "$lt_multi_os_dir; $lt_search_path_spec " in "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) lt_multi_os_dir= ;; esac for lt_sys_path in $lt_search_path_spec; do if test -d "$lt_sys_path$lt_multi_os_dir"; then lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' BEGIN {RS = " "; FS = "/|\n";} { lt_foo = ""; lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } } } } if (lt_foo != "") { lt_freq[[lt_foo]]++; } if (lt_freq[[lt_foo]] == 1) { print lt_foo; } }'` # AWK program above erroneously prepends '/' to C:/dos/paths # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" fi]) library_names_spec= libname_spec='lib$name' soname_spec= shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= finish_eval= shlibpath_var= shlibpath_overrides_runpath=unknown version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" need_lib_prefix=unknown hardcode_into_libs=no # when you set need_version to no, make sure it does not cause -set_version # flags to be left without arguments need_version=unknown AC_ARG_VAR([LT_SYS_LIBRARY_PATH], [User-defined run-time library search path.]) case $host_os in aix3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with # the line '#! .'. This would cause the generated library to # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac # Using Import Files as archive members, it is possible to support # filename-based versioning of shared library archives on AIX. While # this would work for both with and without runtime linking, it will # prevent static linking of such archives. So we do filename-based # shared library versioning with .so extension only, which is used # when both runtime linking and shared linking is enabled. # Unfortunately, runtime linking may impact performance, so we do # not want this to be the default eventually. Also, we use the # versioned .so libs for executables only if there is the -brtl # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. # To allow for filename-based versioning support, we need to create # libNAME.so.V as an archive file, containing: # *) an Import File, referring to the versioned filename of the # archive as well as the shared archive member, telling the # bitwidth (32 or 64) of that shared object, and providing the # list of exported symbols of that shared object, eventually # decorated with the 'weak' keyword # *) the shared object with the F_LOADONLY flag set, to really avoid # it being seen by the linker. # At run time we better use the real file rather than another symlink, # but for link time we create the symlink libNAME.so -> libNAME.so.V case $with_aix_soname,$aix_use_runtimelinking in # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. aix,yes) # traditional libtool dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; aix,no) # traditional AIX only dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' ;; svr4,*) # full svr4 only dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,yes) # both, prefer svr4 dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' # unpreferred sharedlib libNAME.a needs extra handling postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' # We do not specify a path in Import Files, so LIBPATH fires. shlibpath_overrides_runpath=yes ;; *,no) # both, prefer aix dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" library_names_spec='$libname$release.a $libname.a' soname_spec='$libname$release$shared_ext$major' # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' ;; esac shlibpath_var=LIBPATH fi ;; amigaos*) case $host_cpu in powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) version_type=linux # correct to gnu/linux during the next big refactor need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" # the default ld.so.conf also contains /usr/contrib/lib and # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow # libtool to hard-code these into programs ;; cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no case $GCC,$cc_basename in yes,*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' ;; *,cl*) # Native MSVC libname_spec='$name' soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' library_names_spec='$libname.dll.lib' case $build_os in mingw*) sys_lib_search_path_spec= lt_save_ifs=$IFS IFS=';' for lt_path in $LIB do IFS=$lt_save_ifs # Let DOS variable expansion print the short 8.3 style file name. lt_path=`cd "$lt_path" 2>/dev/null && cmd //C "for %i in (".") do @echo %~si"` sys_lib_search_path_spec="$sys_lib_search_path_spec $lt_path" done IFS=$lt_save_ifs # Convert to MSYS style. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | sed -e 's|\\\\|/|g' -e 's| \\([[a-zA-Z]]\\):| /\\1|g' -e 's|^ ||'` ;; cygwin*) # Convert to unix form, then to dos form, then back to unix form # but this time dos style (no spaces!) so that the unix form looks # like /cygdrive/c/PROGRA~1:/cygdr... sys_lib_search_path_spec=`cygpath --path --unix "$LIB"` sys_lib_search_path_spec=`cygpath --path --dos "$sys_lib_search_path_spec" 2>/dev/null` sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi # FIXME: find the short name or the path components, as spaces are # common. (e.g. "Program Files" -> "PROGRA~1") ;; esac # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' shlibpath_overrides_runpath=yes dynamic_linker='Win32 link.exe' ;; *) # Assume MSVC wrapper library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac # FIXME: first we should search . and the directory the executable is in shlibpath_var=PATH ;; darwin* | rhapsody*) dynamic_linker="$host_os dyld" version_type=darwin need_lib_prefix=no need_version=no library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; dgux*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. if test -x /usr/bin/objformat; then objformat=`/usr/bin/objformat` else case $host_os in freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; esac ;; haiku*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; hpux9* | hpux10* | hpux11*) # Give a soname corresponding to the major version so that dld.sl refuses to # link against other versions. version_type=sunos need_lib_prefix=no need_version=no case $host_cpu in ia64*) shrext_cmds='.so' hardcode_into_libs=yes dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi ;; hppa*64*) shrext_cmds='.sl' hardcode_into_libs=yes dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... postinstall_cmds='chmod 555 $lib' # or fails outright, so override atomically: install_override_mode=555 ;; interix[[3-9]]*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) if test yes = "$lt_cv_prog_gnu_ld"; then version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= ;; *) case $LD in # libtool.m4 will add one of these switches to LD *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= libmagic=32-bit;; *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 libmagic=N32;; *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 libmagic=64-bit;; *) libsuff= shlibsuff= libmagic=never-match;; esac ;; esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; # No shared lib support for Linux oldld, aout, or coff. linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; linux*android*) version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext' soname_spec='$libname$release$shared_ext' finish_cmds= shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes dynamic_linker='Android linker' # Don't embed -rpath directories since the linker doesn't support them. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no # Some binutils ld are patched to set DT_RUNPATH AC_CACHE_VAL([lt_cv_shlibpath_overrides_runpath], [lt_cv_shlibpath_overrides_runpath=no save_LDFLAGS=$LDFLAGS save_libdir=$libdir eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], [lt_cv_shlibpath_overrides_runpath=yes])]) LDFLAGS=$save_LDFLAGS libdir=$save_libdir ]) shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. hardcode_into_libs=yes # Add ABI-specific directories to the system library path. sys_lib_dlsearch_path_spec="/lib64 /usr/lib64 /lib /usr/lib" # Ideally, we could use ldconfig to report *all* directores which are # searched for libraries, however this is still not possible. Aside from not # being certain /sbin/ldconfig is available, command # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, # even though it is searched at run-time. Try to do the best guess by # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="$sys_lib_dlsearch_path_spec $lt_ld_extra" fi # We used to test for /lib/ld.so.1 and disable shared libraries on # powerpc, because MkLinux only supported shared libraries with the # GNU dynamic linker. Since this was broken with cross compilers, # most powerpc-linux boxes support dynamic linking these days and # people can always --disable-shared, the test was removed, and we # assume the GNU/Linux dynamic linker is in use. dynamic_linker='GNU/Linux ld.so' ;; netbsd*) version_type=sunos need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; newsos6) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; *nto* | *qnx*) version_type=qnx need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; openbsd* | bitrig*) version_type=sunos sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then need_version=no else need_version=yes fi library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' version_type=windows shrext_cmds=.dll need_version=no need_lib_prefix=no # OS/2 can only load a DLL with a base name of 8 characters or less. soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; v=$($ECHO $release$versuffix | tr -d .-); n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); $ECHO $n$v`$shared_ext' library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' shlibpath_var=BEGINLIBPATH sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec postinstall_cmds='base_file=`basename \$file`~ dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ chmod a+x \$dldir/$dlname~ if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ dlpath=$dir/\$dldll~ $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no soname_spec='$libname$release$shared_ext$major' library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) dynamic_linker=no ;; solaris*) version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes # ldd complains unless libraries are executable postinstall_cmds='chmod +x $lib' ;; sunos4*) version_type=sunos library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) shlibpath_overrides_runpath=no need_lib_prefix=no runpath_var=LD_RUN_PATH ;; siemens) need_lib_prefix=no ;; motorola) need_lib_prefix=no need_version=no shlibpath_overrides_runpath=no sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' ;; esac ;; sysv4*MP*) if test -d /usr/nec; then version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) version_type=sco need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" ;; esac fi sys_lib_dlsearch_path_spec='/usr/lib' ;; tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) version_type=linux # correct to gnu/linux during the next big refactor library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; *) dynamic_linker=no ;; esac AC_MSG_RESULT([$dynamic_linker]) test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi # remember unaugmented sys_lib_dlsearch_path content for libtool script decls... configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec # ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" # to be used as default LT_SYS_LIBRARY_PATH value in generated libtool configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) _LT_DECL([], [need_lib_prefix], [0], [Do we need the "lib" prefix for modules?]) _LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) _LT_DECL([], [version_type], [0], [Library versioning type]) _LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) _LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) _LT_DECL([], [shlibpath_overrides_runpath], [0], [Is shlibpath searched before the hard-coded library search path?]) _LT_DECL([], [libname_spec], [1], [Format of library name prefix]) _LT_DECL([], [library_names_spec], [1], [[List of archive names. First name is the real one, the rest are links. The last name is the one that the linker finds with -lNAME]]) _LT_DECL([], [soname_spec], [1], [[The coded name of the library, if different from the real name]]) _LT_DECL([], [install_override_mode], [1], [Permission mode override for installation of shared libraries]) _LT_DECL([], [postinstall_cmds], [2], [Command to use after installation of a shared archive]) _LT_DECL([], [postuninstall_cmds], [2], [Command to use after uninstallation of a shared archive]) _LT_DECL([], [finish_cmds], [2], [Commands used to finish a libtool library installation in a directory]) _LT_DECL([], [finish_eval], [1], [[As "finish_cmds", except a single script fragment to be evaled but not shown]]) _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) _LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], [Detected run-time system search path for libraries]) _LT_DECL([], [configure_time_lt_sys_library_path], [2], [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- # find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) lt_save_MAGIC_CMD=$MAGIC_CMD lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$1"; then lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : else cat <<_LT_EOF 1>&2 *** Warning: the command libtool uses to detect shared libraries, *** $file_magic_cmd, produces output that libtool cannot recognize. *** The result is that libtool may fail to recognize shared libraries *** as such. This will affect the creation of libtool libraries that *** depend on shared libraries, but programs linked with such libtool *** libraries will work regardless of this problem. Nevertheless, you *** may want to report the problem to your system manager and/or to *** bug-libtool@gnu.org _LT_EOF fi ;; esac fi break fi done IFS=$lt_save_ifs MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else AC_MSG_RESULT(no) fi _LT_DECL([], [MAGIC_CMD], [0], [Used to examine libraries when file_magic_cmd begins with "file"])dnl ])# _LT_PATH_TOOL_PREFIX # Old name: AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- # find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then if test -n "$ac_tool_prefix"; then _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) else MAGIC_CMD=: fi fi ])# _LT_PATH_MAGIC # LT_PATH_LD # ---------- # find the pathname to the GNU or non-GNU linker AC_DEFUN([LT_PATH_LD], [AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; esac case $ac_prog in # Accept absolute paths. [[\\/]]* | ?:[[\\/]]*) re_direlt='/[[^/]][[^/]]*/\.\./' # Canonicalize the pathname of ld ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. ac_prog=ld ;; *) # If it is relative, then search for the first ld in PATH. with_gnu_ld=unknown ;; esac elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 &1 conftest.i cat conftest.i conftest.i >conftest2.i : ${lt_DD:=$DD} AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], [if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: fi]) rm -f conftest.i conftest2.i conftest.out]) ])# _LT_PATH_DD # _LT_CMD_TRUNCATE # ---------------- # find command to truncate a binary pipe m4_defun([_LT_CMD_TRUNCATE], [m4_require([_LT_PATH_DD]) AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], [printf 0123456789abcdef0123456789abcdef >conftest.i cat conftest.i conftest.i >conftest2.i lt_cv_truncate_bin= if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then cmp -s conftest.i conftest.out \ && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" fi rm -f conftest.i conftest2.i conftest.out test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) _LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], [Command to truncate a binary pipe]) ])# _LT_CMD_TRUNCATE # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_MAGIC_METHOD], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) AC_CACHE_CHECK([how to recognize dependent libraries], lt_cv_deplibs_check_method, [lt_cv_file_magic_cmd='$MAGIC_CMD' lt_cv_file_magic_test_file= lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. # 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path # that responds to the $file_magic_cmd with a given extended regex. # If you have 'file' or equivalent on your system and you're not sure # whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) lt_cv_deplibs_check_method=pass_all ;; beos*) lt_cv_deplibs_check_method=pass_all ;; bsdi[[45]]*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib)' lt_cv_file_magic_cmd='/usr/bin/file -L' lt_cv_file_magic_test_file=/shlib/libc.so ;; cygwin*) # func_win32_libid is a shell function defined in ltmain.sh lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' ;; mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else # Keep this pattern in sync with the one in func_win32_libid. lt_cv_deplibs_check_method='file_magic file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; cegcc*) # use the weaker test based on 'objdump'. See mingw*. lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' lt_cv_file_magic_cmd='$OBJDUMP -f' ;; darwin* | rhapsody*) lt_cv_deplibs_check_method=pass_all ;; freebsd* | dragonfly*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then case $host_cpu in i*86 ) # Not sure whether the presence of OpenBSD here was a mistake. # Let's accept both of them until this is cleared up. lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` ;; esac else lt_cv_deplibs_check_method=pass_all fi ;; haiku*) lt_cv_deplibs_check_method=pass_all ;; hpux10.20* | hpux11*) lt_cv_file_magic_cmd=/usr/bin/file case $host_cpu in ia64*) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so ;; hppa*64*) [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF[ -][0-9][0-9])(-bit)?( [LM]SB)? shared object( file)?[, -]* PA-RISC [0-9]\.[0-9]'] lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl ;; *) lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]]\.[[0-9]]) shared library' lt_cv_file_magic_test_file=/usr/lib/libc.sl ;; esac ;; interix[[3-9]]*) # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' ;; irix5* | irix6* | nonstopux*) case $LD in *-32|*"-32 ") libmagic=32-bit;; *-n32|*"-n32 ") libmagic=N32;; *-64|*"-64 ") libmagic=64-bit;; *) libmagic=never-match;; esac lt_cv_deplibs_check_method=pass_all ;; # This must be glibc/ELF. linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' fi ;; newos6*) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' lt_cv_file_magic_cmd=/usr/bin/file lt_cv_file_magic_test_file=/usr/lib/libnls.so ;; *nto* | *qnx*) lt_cv_deplibs_check_method=pass_all ;; openbsd* | bitrig*) if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' fi ;; osf3* | osf4* | osf5*) lt_cv_deplibs_check_method=pass_all ;; rdos*) lt_cv_deplibs_check_method=pass_all ;; solaris*) lt_cv_deplibs_check_method=pass_all ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) lt_cv_deplibs_check_method=pass_all ;; sysv4 | sysv4.3*) case $host_vendor in motorola) lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` ;; ncr) lt_cv_deplibs_check_method=pass_all ;; sequent) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' ;; sni) lt_cv_file_magic_cmd='/bin/file' lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" lt_cv_file_magic_test_file=/lib/libc.so ;; siemens) lt_cv_deplibs_check_method=pass_all ;; pc) lt_cv_deplibs_check_method=pass_all ;; esac ;; tpf*) lt_cv_deplibs_check_method=pass_all ;; os2*) lt_cv_deplibs_check_method=pass_all ;; esac ]) file_magic_glob= want_nocaseglob=no if test "$build" = "$host"; then case $host_os in mingw* | pw32*) if ( shopt | grep nocaseglob ) >/dev/null 2>&1; then want_nocaseglob=yes else file_magic_glob=`echo aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ | $SED -e "s/\(..\)/s\/[[\1]]\/[[\1]]\/g;/g"` fi ;; esac fi file_magic_cmd=$lt_cv_file_magic_cmd deplibs_check_method=$lt_cv_deplibs_check_method test -z "$deplibs_check_method" && deplibs_check_method=unknown _LT_DECL([], [deplibs_check_method], [1], [Method to check whether dependent libraries are shared objects]) _LT_DECL([], [file_magic_cmd], [1], [Command to use when deplibs_check_method = "file_magic"]) _LT_DECL([], [file_magic_glob], [1], [How to find potential files when deplibs_check_method = "file_magic"]) _LT_DECL([], [want_nocaseglob], [1], [Find potential files using nocaseglob when deplibs_check_method = "file_magic"]) ])# _LT_CHECK_MAGIC_METHOD # LT_PATH_NM # ---------- # find the pathname to a BSD- or MS-compatible name lister AC_DEFUN([LT_PATH_NM], [AC_REQUIRE([AC_PROG_CC])dnl AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. lt_cv_path_NM=$NM else lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. tmp_nm=$ac_dir/$lt_tmp_nm if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty case $build_os in mingw*) lt_bad_file=conftest.nm/nofile ;; *) lt_bad_file=/dev/null ;; esac case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but continue # so that we can try to find one that supports BSD flags ;; esac ;; esac fi done IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) if test no != "$lt_cv_path_NM"; then NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: ;; esac fi AC_SUBST([DUMPBIN]) if test : != "$DUMPBIN"; then NM=$DUMPBIN fi fi test -z "$NM" && NM=nm AC_SUBST([NM]) _LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], [lt_cv_nm_interface="BSD nm" echo "int some_variable = 0;" > conftest.$ac_ext (eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&AS_MESSAGE_LOG_FD) (eval "$ac_compile" 2>conftest.err) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) cat conftest.err >&AS_MESSAGE_LOG_FD (eval echo "\"\$as_me:$LINENO: output\"" >&AS_MESSAGE_LOG_FD) cat conftest.out >&AS_MESSAGE_LOG_FD if $GREP 'External.*some_variable' conftest.out > /dev/null; then lt_cv_nm_interface="MS dumpbin" fi rm -f conftest*]) ])# LT_PATH_NM # Old names: AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_PROG_NM], []) dnl AC_DEFUN([AC_PROG_NM], []) # _LT_CHECK_SHAREDLIB_FROM_LINKLIB # -------------------------------- # how to determine the name of the shared library # associated with a specific link library. # -- PORTME fill in with the dynamic library characteristics m4_defun([_LT_CHECK_SHAREDLIB_FROM_LINKLIB], [m4_require([_LT_DECL_EGREP]) m4_require([_LT_DECL_OBJDUMP]) m4_require([_LT_DECL_DLLTOOL]) AC_CACHE_CHECK([how to associate runtime and link libraries], lt_cv_sharedlib_from_linklib_cmd, [lt_cv_sharedlib_from_linklib_cmd='unknown' case $host_os in cygwin* | mingw* | pw32* | cegcc*) # two different shell functions defined in ltmain.sh; # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib ;; *) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib_fallback ;; esac ;; *) # fallback: assume linklib IS sharedlib lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) sharedlib_from_linklib_cmd=$lt_cv_sharedlib_from_linklib_cmd test -z "$sharedlib_from_linklib_cmd" && sharedlib_from_linklib_cmd=$ECHO _LT_DECL([], [sharedlib_from_linklib_cmd], [1], [Command to associate shared and link libraries]) ])# _LT_CHECK_SHAREDLIB_FROM_LINKLIB # _LT_PATH_MANIFEST_TOOL # ---------------------- # locate the manifest tool m4_defun([_LT_PATH_MANIFEST_TOOL], [AC_CHECK_TOOL(MANIFEST_TOOL, mt, :) test -z "$MANIFEST_TOOL" && MANIFEST_TOOL=mt AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool], [lt_cv_path_mainfest_tool=no echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&AS_MESSAGE_LOG_FD $MANIFEST_TOOL '-?' 2>conftest.err > conftest.out cat conftest.err >&AS_MESSAGE_LOG_FD if $GREP 'Manifest Tool' conftest.out > /dev/null; then lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL # _LT_DLL_DEF_P([FILE]) # --------------------- # True iff FILE is a Windows DLL '.def' file. # Keep in sync with func_dll_def_p in the libtool script AC_DEFUN([_LT_DLL_DEF_P], [dnl test DEF = "`$SED -n dnl -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl -e q dnl Only consider the first "real" line $1`" dnl ])# _LT_DLL_DEF_P # LT_LIB_M # -------- # check for math library AC_DEFUN([LT_LIB_M], [AC_REQUIRE([AC_CANONICAL_HOST])dnl LIBM= case $host in *-*-beos* | *-*-cegcc* | *-*-cygwin* | *-*-haiku* | *-*-pw32* | *-*-darwin*) # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) ])# LT_LIB_M # Old name: AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_CHECK_LIBM], []) # _LT_COMPILER_NO_RTTI([TAGNAME]) # ------------------------------- m4_defun([_LT_COMPILER_NO_RTTI], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; *) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' ;; esac _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], lt_cv_prog_compiler_rtti_exceptions, [-fno-rtti -fno-exceptions], [], [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi _LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], [Compiler flag to turn off builtin functions]) ])# _LT_COMPILER_NO_RTTI # _LT_CMD_GLOBAL_SYMBOLS # ---------------------- m4_defun([_LT_CMD_GLOBAL_SYMBOLS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_PROG_CC])dnl AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([LT_PATH_NM])dnl AC_REQUIRE([LT_PATH_LD])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_TAG_COMPILER])dnl # Check for command to grab the raw symbol name followed by C symbol from nm. AC_MSG_CHECKING([command to parse $NM output from $compiler object]) AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], [ # These are sane defaults that work on at least a few old systems. # [They come from Ultrix. What could be older than Ultrix?!! ;)] # Character class describing NM global symbol codes. symcode='[[BCDEGRST]]' # Regexp to match symbols that can be accessed directly from C. sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' # Define system-specific variables. case $host_os in aix*) symcode='[[BCDT]]' ;; cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; irix* | nonstopux*) symcode='[[BCDEGRST]]' ;; osf*) symcode='[[BCDEGQRST]]' ;; solaris*) symcode='[[BDRT]]' ;; sco3.2v5*) symcode='[[DT]]' ;; sysv4.2uw2*) symcode='[[DT]]' ;; sysv5* | sco5v6* | unixware* | OpenUNIX*) symcode='[[ABDT]]' ;; sysv4) symcode='[[DFNSTU]]' ;; esac # If we're using GNU nm, then use its standard symbol codes. case `$NM -V 2>&1` in *GNU* | *'with BFD'*) symcode='[[ABCDGIRSTW]]' ;; esac if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Gets list of data symbols to import. lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" # Adjust the below global symbol transforms to fixup imported variables. lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" lt_c_name_lib_hook="\ -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" else # Disable hooks by default. lt_cv_sys_global_symbol_to_import= lt_cdecl_hook= lt_c_name_hook= lt_c_name_lib_hook= fi # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. lt_cv_sys_global_symbol_to_cdecl="sed -n"\ $lt_cdecl_hook\ " -e 's/^T .* \(.*\)$/extern int \1();/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ $lt_c_name_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" # Transform an extracted symbol line into symbol name with lib prefix and # symbol address. lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ $lt_c_name_lib_hook\ " -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ " -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ " -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac # Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then # Fake it for dumpbin and say T for any non-static function, # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ " /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ " /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ " /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ " /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ " {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ " {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ " s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ " s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" fi lt_cv_sys_global_symbol_pipe="$lt_cv_sys_global_symbol_pipe | sed '/ __gnu_lto/d'" # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* cat > conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; void nm_test_func(void); void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" else rm -f "$nlist"T fi # Make sure that we snagged all the symbols we need. if $GREP ' nm_test_var$' "$nlist" >/dev/null; then if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else # define LT@&t@_DLSYM_CONST const #endif #ifdef __cplusplus extern "C" { #endif _LT_EOF # Now generate the symbol file. eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' cat <<_LT_EOF >> conftest.$ac_ext /* The mapping between symbol names and symbols. */ LT@&t@_DLSYM_CONST struct { const char *name; void *address; } lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif _LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS CFLAGS=$lt_globsym_save_CFLAGS else echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD fi else echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD fi else echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD cat conftest.$ac_ext >&5 fi rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= fi done ]) if test -z "$lt_cv_sys_global_symbol_pipe"; then lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then AC_MSG_RESULT(failed) else AC_MSG_RESULT(ok) fi # Response file support. if test "$lt_cv_nm_interface" = "MS dumpbin"; then nm_file_list_spec='@' elif $NM --help 2>/dev/null | grep '[[@]]FILE' >/dev/null; then nm_file_list_spec='@' fi _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) _LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) _LT_DECL([nm_interface], [lt_cv_nm_interface], [1], [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS # _LT_COMPILER_PIC([TAGNAME]) # --------------------------- m4_defun([_LT_COMPILER_PIC], [m4_require([_LT_TAG_COMPILER])dnl _LT_TAGVAR(lt_prog_compiler_wl, $1)= _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; *djgpp*) # DJGPP does not support shared libraries at all _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac else case $host_os in aix[[4-9]]*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; chorus*) case $cc_basename in cxch68*) # Green Hills C++ Compiler # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac ;; mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) ;; dgux*) case $cc_basename in ec++*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; ghcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; freebsd* | dragonfly*) # FreeBSD uses GNU C++ ;; hpux9* | hpux10* | hpux11*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac ;; *) ;; esac ;; interix*) # This is c89, which is MS Visual C++ (no shared libs) # Anyone wants to do a port? ;; irix5* | irix6* | nonstopux*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' # CC pic flag -KPIC is the default. ;; *) ;; esac ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; icpc* ) # Intel C++, used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgCC* | pgcpp*) # Portland Group C++ compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; cxx*) # Compaq C++ # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xlc* | xlC* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL 8.0, 9.0 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; esac ;; esac ;; lynxos*) ;; m88k*) ;; mvs*) case $cc_basename in cxx*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' ;; *) ;; esac ;; netbsd*) ;; *qnx* | *nto*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' ;; RCC*) # Rational C++ 2.4.1 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; cxx*) # Digital/Compaq C++ _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # Make sure the PIC flag is empty. It appears that all Alpha # Linux and Compaq Tru64 Unix objects are PIC. _LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; *) ;; esac ;; psos*) ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' ;; *) ;; esac ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; lcc*) # Lucid _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' ;; *) ;; esac ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' ;; *) ;; esac ;; vxworks*) ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ], [ if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but # adding the '-m68020' flag to GCC prevents building anything better, # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac ;; beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). # Although the cygwin gcc ignores -fPIC, still need this for old-style # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' ;; haiku*) # PIC is the default for Haiku. # The "-static" flag exists, but is broken. _LT_TAGVAR(lt_prog_compiler_static, $1)= ;; hpux*) # PIC is the default for 64-bit PA HP-UX, but not for 32-bit # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag # sets the default TLS model and affects inlining. case $host_cpu in hppa*64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac ;; interix[[3-9]]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; msdosdjgpp*) # Just because we use GCC doesn't mean we suddenly get shared libraries # on systems that don't support them. _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no enable_shared=no ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic fi ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" fi ;; esac else # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' fi ;; darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' case $cc_basename in nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) case $host_os in os2*) _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' ;; esac ;; hpux9* | hpux10* | hpux11*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but # not for PA HP-UX. case $host_cpu in hppa*64*|ia64*) # +Z the default ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # PIC (with -KPIC) is the default. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; # Lahey Fortran 8.1. lf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' ;; nagfor*) # NAG Fortran compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; ccc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All Alpha code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; xl* | bgxl* | bgf* | mpixl*) # IBM XL C 8.0/Fortran 10.1, 11.1 on PPC and BlueGene _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; *Sun\ F* | *Sun*Fortran*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; *Intel*\ [[CF]]*Compiler*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; *Portland\ Group*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; esac ;; esac ;; newsos6) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *nto* | *qnx*) # QNX uses GNU C++, but need to define -shared option too, otherwise # it will coredump. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; osf3* | osf4* | osf5*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' # All OSF/1 code is PIC. _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; rdos*) _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; solaris*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' case $cc_basename in f77* | f90* | f95* | sunf77* | sunf90* | sunf95*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; *) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; esac ;; sunos4*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4 | sysv4.2uw2* | sysv4.3*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; unicos*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; uts4*) _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; *) _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no ;; esac fi ]) case $host_os in # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac AC_CACHE_CHECK([for $compiler option to produce PIC], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)], [_LT_TAGVAR(lt_cv_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) _LT_TAGVAR(lt_prog_compiler_pic, $1)=$_LT_TAGVAR(lt_cv_prog_compiler_pic, $1) # # Check to make sure the PIC flag actually works. # if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in "" | " "*) ;; *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; esac], [_LT_TAGVAR(lt_prog_compiler_pic, $1)= _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) fi _LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], [Additional compiler flags for building library objects]) _LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], [How to pass a linker flag through the compiler]) # # Check to make sure the static flag actually works. # wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" _LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), $lt_tmp_static_flag, [], [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) _LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], [Compiler flag to prevent dynamic linking]) ])# _LT_COMPILER_PIC # _LT_LINKER_SHLIBS([TAGNAME]) # ---------------------------- # See if the linker supports building shared libraries. m4_defun([_LT_LINKER_SHLIBS], [AC_REQUIRE([LT_PATH_LD])dnl AC_REQUIRE([LT_PATH_NM])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_TAG_COMPILER])dnl AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) m4_if([$1], [CXX], [ _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in cl*) _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] ;; esac ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' ;; esac ], [ runpath_var= _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_cmds, $1)= _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(old_archive_from_new_cmds, $1)= _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= _LT_TAGVAR(thread_safe_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= # include_expsyms should be a list of space-separated symbols to be *always* # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude # it will be wrapped by ' (' and ')$', so one must not match beginning or # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if # the symbol is explicitly referenced. Since portable code cannot # rely on this symbol name, it's probably fine to never include it in # preloaded symbol tables. # Exclude shared library initialization/finalization symbols. dnl Note also adjust exclude_expsyms for C++ above. extract_expsyms_cmds= case $host_os in cygwin* | mingw* | pw32* | cegcc*) # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. if test yes != "$GCC"; then with_gnu_ld=no fi ;; interix*) # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; openbsd* | bitrig*) with_gnu_ld=no ;; esac _LT_TAGVAR(ld_shlibs, $1)=yes # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility # with the native linker. However, as the warning in the GNU ld # block says, versions before 2.19.5* couldn't really create working # shared libraries, regardless of the interface used. case `$LD -v 2>&1` in *\ \(GNU\ Binutils\)\ 2.19.5*) ;; *\ \(GNU\ Binutils\)\ 2.[[2-9]]*) ;; *\ \(GNU\ Binutils\)\ [[3-9]]*) ;; *) lt_use_gnu_ld_interface=yes ;; esac ;; *) lt_use_gnu_ld_interface=yes ;; esac fi if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... *\ 2.11.*) ;; # other 2.11 versions *) supports_anon_versioning=yes ;; esac # See if GNU ld supports shared libraries. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: the GNU linker, at least up to release 2.19, is reported *** to be unable to reliably create shared libraries on AIX. *** Therefore, libtool is disabling shared libraries support. If you *** really care for shared libraries, you may want to install binutils *** 2.20 or above, or modify your PATH so that a non-GNU linker is found. *** You will then need to restart the configuration process. _LT_EOF fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; cygwin* | mingw* | pw32* | cegcc*) # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 tmp_addflag=' -i_dynamic -nofor_main' ;; ifc* | ifort*) # Intel Fortran compiler tmp_addflag=' -nofor_main' ;; lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; nagfor*) # NAGFOR 5.3 tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in tcc*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; solaris*) if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: The releases 2.8.* of the GNU linker cannot reliably *** create shared libraries on Solaris systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.9.1 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) case `$LD -v 2>&1` in *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 *** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify *** your PATH or compiler configuration so that the native linker is *** used, and then restart. _LT_EOF ;; *) # For security reasons, it is highly recommended that you always # use absolute paths for naming shared libraries, and exclude the # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; sunos4*) _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' wlarc= _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else # PORTME fill in a description of your system's linker (not GNU ld) case $host_os in aix3*) _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported fi ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. # -C means demangle to GNU nm, but means don't demangle to AIX nm. # Without the "-l" option, or with the "-B" option, AIX nm treats # weak defined symbols like other global defined symbols, whereas # GNU nm marks them as "W". # While the 'weak' keyword is ignored in the Export File, we need # it in the Import File for the 'aix-soname' feature, so we have # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # traditional, no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi ;; esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag="$shared_flag "'$wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared libraries. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; amigaos*) case $host_cpu in powerpc) # see comment about AmigaOS4 .so support _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac ;; bsdi[[45]]*) _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic ;; cygwin* | mingw* | pw32* | cegcc*) # When not using gcc, we currently assume that we are using # Microsoft Visual C++. # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. case $cc_basename in cl*) # Native MSVC _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # Assume MSVC wrapper _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' # FIXME: Should let the user specify the lib program. _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; dgux*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little # extra space). freebsd2.2*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; # FreeBSD 3 and greater uses gcc -shared to do shared libraries. freebsd* | dragonfly*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; hpux9*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) if test yes,no = "$GCC,$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes fi ;; hpux11*) if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ # Older versions of the 11.00 compiler do not understand -b yet # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes ;; esac fi ;; irix5* | irix6* | nonstopux*) if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], [save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], [C++], [[int foo (void) { return 0; }]], [Fortran 77], [[ subroutine foo end]], [Fortran], [[ subroutine foo end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) LDFLAGS=$save_LDFLAGS]) if test yes = "$lt_cv_irix_exported_symbol"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; linux*) case $cc_basename in tcc*) # Fabrice Bellard et al's Tiny C Compiler _LT_TAGVAR(ld_shlibs, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out else _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *nto* | *qnx*) ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; osf3*) if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag if test yes = "$GCC"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' if test yes = "$GCC"; then wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) wlarc='$wl' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) if test yes = "$GCC"; then _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes ;; sunos4*) if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4) case $host_vendor in sni) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? ;; siemens) ## LD is ld it makes a PLAMLIB ## CC just makes a GrossModule. _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' _LT_TAGVAR(hardcode_direct, $1)=no ;; motorola) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie ;; esac runpath_var='LD_RUN_PATH' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; sysv4.3*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' ;; sysv4*MP*) if test -d /usr/nec; then _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var=LD_RUN_PATH hardcode_runpath_var=yes _LT_TAGVAR(ld_shlibs, $1)=yes fi ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' if test yes = "$GCC"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; uts4*) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(ld_shlibs, $1)=no ;; esac if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld _LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl _LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl _LT_DECL([], [extract_expsyms_cmds], [2], [The commands to extract the exported symbol list from a shared archive]) # # Do we need to explicitly link libc? # case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; '$CC '*) # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. AC_CACHE_CHECK([whether -lc should be explicitly linked in], [lt_cv_]_LT_TAGVAR(archive_cmds_need_lc, $1), [$RM conftest* echo "$lt_simple_compile_test_code" > conftest.$ac_ext if AC_TRY_EVAL(ac_compile) 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) _LT_TAGVAR(allow_undefined_flag, $1)= if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) then lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=no else lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1)=yes fi _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi $RM conftest* ]) _LT_TAGVAR(archive_cmds_need_lc, $1)=$lt_cv_[]_LT_TAGVAR(archive_cmds_need_lc, $1) ;; esac fi ;; esac _LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], [Whether or not to add -lc for building shared libraries]) _LT_TAGDECL([allow_libtool_libs_with_static_runtimes], [enable_shared_with_static_runtimes], [0], [Whether or not to disallow shared libs when runtime libs are static]) _LT_TAGDECL([], [export_dynamic_flag_spec], [1], [Compiler flag to allow reflexive dlopens]) _LT_TAGDECL([], [whole_archive_flag_spec], [1], [Compiler flag to generate shared objects directly from archives]) _LT_TAGDECL([], [compiler_needs_object], [1], [Whether the compiler copes with passing no objects directly]) _LT_TAGDECL([], [old_archive_from_new_cmds], [2], [Create an old-style archive from a shared archive]) _LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], [Create a temporary old-style archive to link instead of a shared archive]) _LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) _LT_TAGDECL([], [archive_expsym_cmds], [2]) _LT_TAGDECL([], [module_cmds], [2], [Commands used to build a loadable module if different from building a shared archive.]) _LT_TAGDECL([], [module_expsym_cmds], [2]) _LT_TAGDECL([], [with_gnu_ld], [1], [Whether we are building with GNU ld or not]) _LT_TAGDECL([], [allow_undefined_flag], [1], [Flag that allows shared libraries with undefined symbols to be built]) _LT_TAGDECL([], [no_undefined_flag], [1], [Flag that enforces no undefined symbols]) _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_shlibpath_var], [0], [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_automatic], [0], [Set to "yes" if building a shared library automatically hardcodes DIR into the library and all subsequent libraries and executables linked against it]) _LT_TAGDECL([], [inherit_rpath], [0], [Set to yes if linker adds runtime paths of dependent libraries to runtime path list]) _LT_TAGDECL([], [link_all_deplibs], [0], [Whether libtool must link a program against all its dependency libraries]) _LT_TAGDECL([], [always_export_symbols], [0], [Set to "yes" if exported symbols are required]) _LT_TAGDECL([], [export_symbols_cmds], [2], [The commands to list exported symbols]) _LT_TAGDECL([], [exclude_expsyms], [1], [Symbols that should not be listed in the preloaded symbols]) _LT_TAGDECL([], [include_expsyms], [1], [Symbols that must always be exported]) _LT_TAGDECL([], [prelink_cmds], [2], [Commands necessary for linking programs (against libraries) with templates]) _LT_TAGDECL([], [postlink_cmds], [2], [Commands necessary for finishing linking programs]) _LT_TAGDECL([], [file_list_spec], [1], [Specify filename containing input files]) dnl FIXME: Not yet implemented dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], dnl [Compiler flag to generate thread safe objects]) ])# _LT_LINKER_SHLIBS # _LT_LANG_C_CONFIG([TAG]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. ac_ext=c # Object file extension for compiled C test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(){return(0);}' _LT_TAG_COMPILER # Save the default compiler, since it gets overwritten when the other # tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. compiler_DEFAULT=$CC # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP CC=$lt_save_CC ])# _LT_LANG_C_CONFIG # _LT_LANG_CXX_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write # the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl if test -n "$CXX" && ( test no != "$CXX" && ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes fi AC_LANG_PUSH(C++) _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(compiler_needs_object, $1)=no _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for C++ test sources. ac_ext=cpp # Object file extension for compiled C++ test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" # Code to be used in simple link tests lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_LD=$LD lt_save_GCC=$GCC GCC=$GXX lt_save_with_gnu_ld=$with_gnu_ld lt_save_path_LD=$lt_cv_path_LD if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx else $as_unset lt_cv_prog_gnu_ld fi if test -n "${lt_cv_path_LDCXX+set}"; then lt_cv_path_LD=$lt_cv_path_LDCXX else $as_unset lt_cv_path_LD fi test -z "${LDCXX+set}" || LD=$LDCXX CC=${CXX-"c++"} CFLAGS=$CXXFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. if test yes = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi else with_gnu_ld=no wlarc= # A generic and very simple default shared library creation # command for GNU C++ for the case where it uses the native # linker, instead of GNU ld. If possible, this setting should # overridden to take advantage of the native linker features on # the platform it is being used on. _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' fi # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else GXX=no with_gnu_ld=no wlarc= fi # PORTME: fill in a description of your system's C++ link characteristics AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) _LT_TAGVAR(ld_shlibs, $1)=yes case $host_os in aix3*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we # have runtime linking enabled, and use it for executables. # For shared libraries, we enable/disable runtime linking # depending on the kind of the shared library created - # when "with_aix_soname,aix_use_runtimelinking" is: # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables # "aix,yes" lib.so shared, rtl:yes, for executables # lib.a static archive # "both,no" lib.so.V(shr.o) shared, rtl:yes # lib.a(lib.so.V) shared, rtl:no, for executables # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a(lib.so.V) shared, rtl:no # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in *-brtl*) aix_use_runtimelinking=yes break ;; esac done if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then # With aix-soname=svr4, we create the lib.so.V shared archives only, # so we don't have lib.a shared libs to link our executables. # We have to force runtime linking in this case. aix_use_runtimelinking=yes LDFLAGS="$LDFLAGS -Wl,-brtl" fi ;; esac exp_sym_flag='-bexport' no_entry_flag='-bnoentry' fi # When large executables or shared objects are built, AIX ld can # have problems creating the table of contents. If linking a library # or program results in "error TOC overflow" add -mminimal-toc to # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. _LT_TAGVAR(archive_cmds, $1)='' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(file_list_spec, $1)='$wl-f,' case $with_aix_soname,$aix_use_runtimelinking in aix,*) ;; # no import file svr4,* | *,yes) # use import file # The Import File defines what to hardcode. _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no ;; esac if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then # We have reworked collect2 : else # We have old collect2 _LT_TAGVAR(hardcode_direct, $1)=unsupported # It fails to find uninstalled libraries when the uninstalled # path is not listed in the libpath. Setting hardcode_minus_L # to unsupported forces relinking _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)= fi esac shared_flag='-shared' if test yes = "$aix_use_runtimelinking"; then shared_flag=$shared_flag' $wl-G' fi # Need to ensure runtime linking is disabled for the traditional # shared library, or the linker may eventually find shared libraries # /with/ Import File - we do not want to mix them. shared_flag_aix='-shared' shared_flag_svr4='-shared $wl-G' else # not using gcc if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else if test yes = "$aix_use_runtimelinking"; then shared_flag='$wl-G' else shared_flag='$wl-bM:SRE' fi shared_flag_aix='$wl-bM:SRE' shared_flag_svr4='$wl-G' fi fi _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. # The "-G" linker flag allows undefined symbols. _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else if test ia64 = "$host_cpu"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' # -brtl affects multiple linker settings, -berok does not and is overridden later compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' if test svr4 != "$with_aix_soname"; then # This is similar to how AIX traditionally builds its shared # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' fi if test aix != "$with_aix_soname"; then _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' else # used by -dlpreopen to get the symbols _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' fi _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; beos*) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; chorus*) case $cc_basename in *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; cygwin* | mingw* | pw32* | cegcc*) case $GXX,$cc_basename in ,cl* | no,cl*) # Native MSVC # hardcode_libdir_flag_spec is actually meaningless, as there is # no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=yes _LT_TAGVAR(file_list_spec, $1)='@' # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp "$export_symbols" "$output_objdir/$soname.def"; echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; else $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; fi~ $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ lt_tool_outputfile="@TOOL_OUTPUT@"~ case $lt_outputfile in *.exe|*.EXE) ;; *) lt_outputfile=$lt_outputfile.exe lt_tool_outputfile=$lt_tool_outputfile.exe ;; esac~ func_to_tool_file "$lt_outputfile"~ if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; $RM "$lt_outputfile.manifest"; fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' # If the export-symbols file already is a .def file, use it as # is; otherwise, prepend EXPORTS... _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then cp $export_symbols $output_objdir/$soname.def; else echo EXPORTS > $output_objdir/$soname.def; cat $export_symbols >> $output_objdir/$soname.def; fi~ $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; darwin* | rhapsody*) _LT_DARWIN_LINKER_FEATURES($1) ;; os2*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported shrext_cmds=.dll _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ $ECHO EXPORTS >> $output_objdir/$libname.def~ prefix_cmds="$SED"~ if test EXPORTS = "`$SED 1q $export_symbols`"; then prefix_cmds="$prefix_cmds -e 1d"; fi~ prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ emximp -o $lib $output_objdir/$libname.def' _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; dgux*) case $cc_basename in ec++*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; ghcx*) # Green Hills C++ Compiler # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no ;; freebsd-elf*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; freebsd* | dragonfly*) # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF # conventions _LT_TAGVAR(ld_shlibs, $1)=yes ;; haiku*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; hpux10*|hpux11*) if test no = "$with_gnu_ld"; then _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi case $host_cpu in hppa*64*|ia64*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default # location of the library. ;; esac case $cc_basename in CC*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) if test yes = "$GXX"; then if test no = "$with_gnu_ld"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; icpc* | ecpc* ) # Intel C++ with_gnu_ld=yes # version 8.0 and above of icpc choke on multiply defined symbols # if we add $predep_objects and $postdep_objects, however 7.1 and # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ rm -rf $tpldir~ $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ echo "local: *; };" >> $output_objdir/$libname.ver~ $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 # would be better. output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; esac ;; esac ;; lynxos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; m88k*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; mvs*) case $cc_basename in cxx*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; netbsd*) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' wlarc= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no fi # Workaround some broken pre-1.5 toolchains output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' ;; *nto* | *qnx*) _LT_TAGVAR(ld_shlibs, $1)=yes ;; openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; osf3* | osf4* | osf5*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using # the KAI C++ compiler. case $host in osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; esac ;; RCC*) # Rational C++ 2.4.1 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; cxx*) case $host in osf3*) _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ echo "-hidden">> $lib.exp~ $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. # # There doesn't appear to be a way to prevent this compiler from # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac ;; psos*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; sunos4*) case $cc_basename in CC*) # Sun C++ 4.x # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; lcc*) # Lucid # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; solaris*) case $cc_basename in CC* | sunCC*) # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; esac _LT_TAGVAR(link_all_deplibs, $1)=yes output_verbose_link_cmd='func_echo_all' # Archives containing C++ object files must be created using # "CC -xar", where "CC" is the Sun C++ compiler. This is # necessary to make sure instantiated templates are included # in the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' ;; gcx*) # Green Hills C++ Compiler _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker if test yes,no = "$GXX,$with_gnu_ld"; then _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' else # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' fi _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi ;; esac ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; tandem*) case $cc_basename in NCC*) # NonStop-UX NCC 3.20 # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac ;; vxworks*) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; *) # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no ;; esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(GCC, $1)=$GXX _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS LDCXX=$LD LD=$lt_save_LD GCC=$lt_save_GCC with_gnu_ld=$lt_save_with_gnu_ld lt_cv_path_LDCXX=$lt_cv_path_LD lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG # _LT_FUNC_STRIPNAME_CNF # ---------------------- # func_stripname_cnf prefix suffix name # strip PREFIX and SUFFIX off of NAME. # PREFIX and SUFFIX must not contain globbing or regex special # characters, hashes, percent signs, but SUFFIX may contain a leading # dot (in which case that matches only a dot). # # This function is identical to the (non-XSI) version of func_stripname, # except this one can be used by m4 code that may be executed by configure, # rather than the libtool script. m4_defun([_LT_FUNC_STRIPNAME_CNF],[dnl AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { case @S|@2 in .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose # compiler output when linking a shared library. # Parse the compiler output and extract the necessary # objects, libraries and library flags. m4_defun([_LT_SYS_HIDDEN_LIBDEPS], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl AC_REQUIRE([_LT_FUNC_STRIPNAME_CNF])dnl # Dependencies to place before and after the object being linked: _LT_TAGVAR(predep_objects, $1)= _LT_TAGVAR(postdep_objects, $1)= _LT_TAGVAR(predeps, $1)= _LT_TAGVAR(postdeps, $1)= _LT_TAGVAR(compiler_lib_search_path, $1)= dnl we can't use the lt_simple_compile_test_code here, dnl because it contains code intended for an executable, dnl not a library. It's possible we should let each dnl tag define a new lt_????_link_test_code variable, dnl but it's only used here... m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF int a; void foo (void) { a = 0; } _LT_EOF ], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF class Foo { public: Foo (void) { a = 0; } private: int a; }; _LT_EOF ], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer*4 a a=0 return end _LT_EOF ], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF subroutine foo implicit none integer a a=0 return end _LT_EOF ], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF public class foo { private int a; public void bar (void) { a = 0; } }; _LT_EOF ], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF package foo func foo() { } _LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; *\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary dnl objects, libraries and library flags. if AC_TRY_EVAL(ac_compile); then # Parse the compiler output and extract the necessary # objects, libraries and library flags. # Sentinel used to keep track of whether or not we are before # the conftest object file. pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. if test x-L = "$p" || test x-R = "$p"; then prev=$p continue fi # Expand the sysroot to ease extracting the directories later. if test -z "$prev"; then case $p in -L*) func_stripname_cnf '-L' '' "$p"; prev=-L; p=$func_stripname_result ;; -R*) func_stripname_cnf '-R' '' "$p"; prev=-R; p=$func_stripname_result ;; -l*) func_stripname_cnf '-l' '' "$p"; prev=-l; p=$func_stripname_result ;; esac fi case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac if test no = "$pre_test_object_deps_done"; then case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being # linked, so don't bother handling this case. esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then _LT_TAGVAR(postdeps, $1)=$prev$p else _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= ;; *.lto.$objext) ;; # Ignore GCC LTO objects *.$objext) # This assumes that the test object file only shows up # once in the compiler output. if test "$p" = "conftest.$objext"; then pre_test_object_deps_done=yes continue fi if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi fi ;; *) ;; # Ignore the rest. esac done # Clean up. rm -f a.out a.exe else echo "libtool.m4: error: problem compiling $1 test program" fi $RM -f confest.$objext CFLAGS=$_lt_libdeps_save_CFLAGS # PORTME: override above test on systems where it is broken m4_if([$1], [CXX], [case $host_os in interix[[3-9]]*) # Interix 3.5 installs completely hosed .la files for C++, so rather than # hack all around it, let's just trust "g++" to DTRT. _LT_TAGVAR(predep_objects,$1)= _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; esac ]) case " $_LT_TAGVAR(postdeps, $1) " in *" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) _LT_TAGDECL([], [predep_objects], [1], [Dependencies to place before and after the objects being linked to create a shared library]) _LT_TAGDECL([], [postdep_objects], [1]) _LT_TAGDECL([], [predeps], [1]) _LT_TAGDECL([], [postdeps], [1]) _LT_TAGDECL([], [compiler_lib_search_path], [1], [The library search path used internally by the compiler when linking a shared library]) ])# _LT_SYS_HIDDEN_LIBDEPS # _LT_LANG_F77_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for f77 test sources. ac_ext=f # Object file extension for compiled f77 test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} CFLAGS=$FFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) GCC=$G77 if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$G77 _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG # _LT_LANG_FC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(allow_undefined_flag, $1)= _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(archive_expsym_cmds, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no _LT_TAGVAR(inherit_rpath, $1)=no _LT_TAGVAR(module_cmds, $1)= _LT_TAGVAR(module_expsym_cmds, $1)= _LT_TAGVAR(link_all_deplibs, $1)=unknown _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds _LT_TAGVAR(no_undefined_flag, $1)= _LT_TAGVAR(whole_archive_flag_spec, $1)= _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no # Source file extension for fc test sources. ac_ext=${ac_fc_srcext-f} # Object file extension for compiled fc test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # No sense in running all these tests if we already determined that # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t return end " # Code to be used in simple link tests lt_simple_link_test_code="\ program t end " # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} CFLAGS=$FCFLAGS compiler=$CC GCC=$ac_cv_fc_compiler_gnu _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) if test -n "$compiler"; then AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) if test ia64 != "$host_cpu"; then case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in yes,aix,yes) ;; # shared object as lib.so file only yes,svr4,*) ;; # shared object as lib.so archive member only yes,*) enable_static=no ;; # shared object in lib.a archive as well esac fi ;; esac AC_MSG_RESULT([$enable_shared]) AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change ## the running order or otherwise move them around unless you know exactly ## what you are doing... _LT_SYS_HIDDEN_LIBDEPS($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_SYS_DYNAMIC_LINKER($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi # test -n "$compiler" GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG # _LT_LANG_GCJ_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE # Source file extension for Java test sources. ac_ext=java # Object file extension for compiled Java test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="class foo {}" # Code to be used in simple link tests lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG # _LT_LANG_GO_CONFIG([TAG]) # -------------------------- # Ensure that the configuration variables for the GNU Go compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GO_CONFIG], [AC_REQUIRE([LT_PROG_GO])dnl AC_LANG_SAVE # Source file extension for Go test sources. ac_ext=go # Object file extension for compiled Go test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code="package main; func main() { }" # Code to be used in simple link tests lt_simple_link_test_code='package main; func main() { }' # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC=yes CC=${GOC-"gccgo"} CFLAGS=$GOFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # Go did not exist at the time GCC didn't implicitly link libc in. _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds _LT_TAGVAR(reload_flag, $1)=$reload_flag _LT_TAGVAR(reload_cmds, $1)=$reload_cmds if test -n "$compiler"; then _LT_COMPILER_NO_RTTI($1) _LT_COMPILER_PIC($1) _LT_COMPILER_C_O($1) _LT_COMPILER_FILE_LOCKS($1) _LT_LINKER_SHLIBS($1) _LT_LINKER_HARDCODE_LIBPATH($1) _LT_CONFIG($1) fi AC_LANG_RESTORE GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GO_CONFIG # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG # to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE # Source file extension for RC test sources. ac_ext=rc # Object file extension for compiled RC test sources. objext=o _LT_TAGVAR(objext, $1)=$objext # Code to be used in simple compile tests lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER # save warnings/boilerplate of simple test code _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= CC=${RC-"windres"} CFLAGS= compiler=$CC _LT_TAGVAR(compiler, $1)=$CC _LT_CC_BASENAME([$compiler]) _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes if test -n "$compiler"; then : _LT_CONFIG($1) fi GCC=$lt_save_GCC AC_LANG_RESTORE CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_RC_CONFIG # LT_PROG_GCJ # ----------- AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) # Old name: AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) # LT_PROG_GO # ---------- AC_DEFUN([LT_PROG_GO], [AC_CHECK_TOOL(GOC, gccgo,) ]) # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], [AC_CHECK_TOOL(RC, windres,) ]) # Old name: AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_RC], []) # _LT_DECL_EGREP # -------------- # If we don't have a new enough Autoconf to choose the best grep # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_EGREP], [AC_REQUIRE([AC_PROG_EGREP])dnl AC_REQUIRE([AC_PROG_FGREP])dnl test -z "$GREP" && GREP=grep _LT_DECL([], [GREP], [1], [A grep program that handles long lines]) _LT_DECL([], [EGREP], [1], [An ERE matcher]) _LT_DECL([], [FGREP], [1], [A literal string matcher]) dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too AC_SUBST([GREP]) ]) # _LT_DECL_OBJDUMP # -------------- # If we don't have a new enough Autoconf to choose the best objdump # available, choose the one first in the user's PATH. m4_defun([_LT_DECL_OBJDUMP], [AC_CHECK_TOOL(OBJDUMP, objdump, false) test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) AC_SUBST([OBJDUMP]) ]) # _LT_DECL_DLLTOOL # ---------------- # Ensure DLLTOOL variable is set. m4_defun([_LT_DECL_DLLTOOL], [AC_CHECK_TOOL(DLLTOOL, dlltool, false) test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program]) AC_SUBST([DLLTOOL]) ]) # _LT_DECL_SED # ------------ # Check for a fully-functional sed program, that truncates # as few characters as possible. Prefer GNU sed if found. m4_defun([_LT_DECL_SED], [AC_PROG_SED test -z "$SED" && SED=sed Xsed="$SED -e 1s/^X//" _LT_DECL([], [SED], [1], [A sed program that does not truncate output]) _LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], [Sed that helps us avoid accidentally triggering echo(1) options like -n]) ])# _LT_DECL_SED m4_ifndef([AC_PROG_SED], [ # NOTE: This macro has been submitted for inclusion into # # GNU Autoconf as AC_PROG_SED. When it is available in # # a released version of Autoconf we should remove this # # macro and use it instead. # m4_defun([AC_PROG_SED], [AC_MSG_CHECKING([for a sed that does not truncate output]) AC_CACHE_VAL(lt_cv_path_SED, [# Loop through the user's path and test for sed and gsed. # Then use that list of sed's as ones to test for truncation. as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for lt_ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi done done done IFS=$as_save_IFS lt_ac_max=0 lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in # Check for GNU sed and select it if it is found. if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then lt_cv_path_SED=$lt_ac_sed break fi while true; do cat conftest.in conftest.in >conftest.tmp mv conftest.tmp conftest.in cp conftest.in conftest.nl echo >>conftest.nl $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi done done ]) SED=$lt_cv_path_SED AC_SUBST([SED]) AC_MSG_RESULT([$SED]) ])#AC_PROG_SED ])#m4_ifndef # Old name: AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_SED], []) # _LT_CHECK_SHELL_FEATURES # ------------------------ # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], [if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false fi _LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl # test EBCDIC or ASCII case `echo X|tr X '\101'` in A) # ASCII based system # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr lt_SP2NL='tr \040 \012' lt_NL2SP='tr \015\012 \040\040' ;; *) # EBCDIC based system lt_SP2NL='tr \100 \n' lt_NL2SP='tr \r\n \100\100' ;; esac _LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- # Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], [AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_MSG_CHECKING([how to convert $build file names to $host format]) AC_CACHE_VAL(lt_cv_to_host_file_cmd, [case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_w32 ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_cygwin_to_w32 ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_w32 ;; esac ;; *-*-cygwin* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_host_file_cmd=func_convert_file_msys_to_cygwin ;; *-*-cygwin* ) lt_cv_to_host_file_cmd=func_convert_file_noop ;; * ) # otherwise, assume *nix lt_cv_to_host_file_cmd=func_convert_file_nix_to_cygwin ;; esac ;; * ) # unhandled hosts (and "normal" native builds) lt_cv_to_host_file_cmd=func_convert_file_noop ;; esac ]) to_host_file_cmd=$lt_cv_to_host_file_cmd AC_MSG_RESULT([$lt_cv_to_host_file_cmd]) _LT_DECL([to_host_file_cmd], [lt_cv_to_host_file_cmd], [0], [convert $build file names to $host format])dnl AC_MSG_CHECKING([how to convert $build file names to toolchain format]) AC_CACHE_VAL(lt_cv_to_tool_file_cmd, [#assume ordinary cross tools, or native build. lt_cv_to_tool_file_cmd=func_convert_file_noop case $host in *-*-mingw* ) case $build in *-*-mingw* ) # actually msys lt_cv_to_tool_file_cmd=func_convert_file_msys_to_w32 ;; esac ;; esac ]) to_tool_file_cmd=$lt_cv_to_tool_file_cmd AC_MSG_RESULT([$lt_cv_to_tool_file_cmd]) _LT_DECL([to_tool_file_cmd], [lt_cv_to_tool_file_cmd], [0], [convert $build files to toolchain format])dnl ])# _LT_PATH_CONVERSION_FUNCTIONS # Helper functions for option handling. -*- Autoconf -*- # # Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) # _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) # ------------------------------------------ m4_define([_LT_MANGLE_OPTION], [[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) # _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) # --------------------------------------- # Set option OPTION-NAME for macro MACRO-NAME, and if there is a # matching handler defined, dispatch to it. Other OPTION-NAMEs are # saved as a flag. m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), [m4_warning([Unknown $1 option '$2'])])[]dnl ]) # _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) # ------------------------------------------------------------ # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. m4_define([_LT_IF_OPTION], [m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) # _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) # ------------------------------------------------------- # Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME # are set. m4_define([_LT_UNLESS_OPTIONS], [m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), [m4_define([$0_found])])])[]dnl m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 ])[]dnl ]) # _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) # ---------------------------------------- # OPTION-LIST is a space-separated list of Libtool options associated # with MACRO-NAME. If any OPTION has a matching handler declared with # LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about # the unknown option and exit. m4_defun([_LT_SET_OPTIONS], [# Set options m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), [_LT_SET_OPTION([$1], _LT_Option)]) m4_if([$1],[LT_INIT],[ dnl dnl Simply set some default values (i.e off) if boolean options were not dnl specified: _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no ]) _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no ]) dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], [_LT_ENABLE_FAST_INSTALL]) _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS # _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) # ----------------------------------------- m4_define([_LT_MANGLE_DEFUN], [[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) # LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) # ----------------------------------------------- m4_define([LT_OPTION_DEFINE], [m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl ])# LT_OPTION_DEFINE # dlopen # ------ LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes ]) AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) # win32-dll # --------- # Declare package support for building win32 dll's. LT_OPTION_DEFINE([LT_INIT], [win32-dll], [enable_win32_dll=yes case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-cegcc*) AC_CHECK_TOOL(AS, as, false) AC_CHECK_TOOL(DLLTOOL, dlltool, false) AC_CHECK_TOOL(OBJDUMP, objdump, false) ;; esac test -z "$AS" && AS=as _LT_DECL([], [AS], [1], [Assembler program])dnl test -z "$DLLTOOL" && DLLTOOL=dlltool _LT_DECL([], [DLLTOOL], [1], [DLL creation program])dnl test -z "$OBJDUMP" && OBJDUMP=objdump _LT_DECL([], [OBJDUMP], [1], [Object dumper program])dnl ])# win32-dll AU_DEFUN([AC_LIBTOOL_WIN32_DLL], [AC_REQUIRE([AC_CANONICAL_HOST])dnl _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- # implement the --enable-shared flag, and supports the 'shared' and # 'disable-shared' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_shared=yes ;; no) enable_shared=no ;; *) enable_shared=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) _LT_DECL([build_libtool_libs], [enable_shared], [0], [Whether or not to build shared libraries]) ])# _LT_ENABLE_SHARED LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) # Old names: AC_DEFUN([AC_ENABLE_SHARED], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) ]) AC_DEFUN([AC_DISABLE_SHARED], [_LT_SET_OPTION([LT_INIT], [disable-shared]) ]) AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_SHARED], []) dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- # implement the --enable-static flag, and support the 'static' and # 'disable-static' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_static=yes ;; no) enable_static=no ;; *) enable_static=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) _LT_DECL([build_old_libs], [enable_static], [0], [Whether or not to build static libraries]) ])# _LT_ENABLE_STATIC LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) # Old names: AC_DEFUN([AC_ENABLE_STATIC], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) ]) AC_DEFUN([AC_DISABLE_STATIC], [_LT_SET_OPTION([LT_INIT], [disable-static]) ]) AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AM_ENABLE_STATIC], []) dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- # implement the --enable-fast-install flag, and support the 'fast-install' # and 'disable-fast-install' LT_INIT options. # DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], [p=${PACKAGE-default} case $enableval in yes) enable_fast_install=yes ;; no) enable_fast_install=no ;; *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) _LT_DECL([fast_install], [enable_fast_install], [0], [Whether or not to optimize for fast installation])dnl ])# _LT_ENABLE_FAST_INSTALL LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) # Old names: AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) # _LT_WITH_AIX_SONAME([DEFAULT]) # ---------------------------------- # implement the --with-aix-soname flag, and support the `aix-soname=aix' # and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT # is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. m4_define([_LT_WITH_AIX_SONAME], [m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl shared_archive_member_spec= case $host,$enable_shared in power*-*-aix[[5-9]]*,yes) AC_MSG_CHECKING([which variant of shared library versioning to provide]) AC_ARG_WITH([aix-soname], [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], [case $withval in aix|svr4|both) ;; *) AC_MSG_ERROR([Unknown argument to --with-aix-soname]) ;; esac lt_cv_with_aix_soname=$with_aix_soname], [AC_CACHE_VAL([lt_cv_with_aix_soname], [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) with_aix_soname=$lt_cv_with_aix_soname]) AC_MSG_RESULT([$with_aix_soname]) if test aix != "$with_aix_soname"; then # For the AIX way of multilib, we name the shared archive member # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, # the AIX toolchain works better with OBJECT_MODE set (default 32). if test 64 = "${OBJECT_MODE-32}"; then shared_archive_member_spec=shr_64 else shared_archive_member_spec=shr fi fi ;; *) with_aix_soname=aix ;; esac _LT_DECL([], [shared_archive_member_spec], [0], [Shared archive member basename, for filename based shared library versioning on AIX])dnl ])# _LT_WITH_AIX_SONAME LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) # _LT_WITH_PIC([MODE]) # -------------------- # implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. # MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], [lt_p=${PACKAGE-default} case $withval in yes|no) pic_mode=$withval ;; *) pic_mode=default # Look at the argument we got. We use all the common list separators. lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for lt_pkg in $withval; do IFS=$lt_save_ifs if test "X$lt_pkg" = "X$lt_p"; then pic_mode=yes fi done IFS=$lt_save_ifs ;; esac], [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) # Old name: AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) m4_define([_LTDL_MODE], []) LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], [m4_define([_LTDL_MODE], [nonrecursive])]) LT_OPTION_DEFINE([LTDL_INIT], [recursive], [m4_define([_LTDL_MODE], [recursive])]) LT_OPTION_DEFINE([LTDL_INIT], [subproject], [m4_define([_LTDL_MODE], [subproject])]) m4_define([_LTDL_TYPE], []) LT_OPTION_DEFINE([LTDL_INIT], [installable], [m4_define([_LTDL_TYPE], [installable])]) LT_OPTION_DEFINE([LTDL_INIT], [convenience], [m4_define([_LTDL_TYPE], [convenience])]) # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software # Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 6 ltsugar.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) # lt_join(SEP, ARG1, [ARG2...]) # ----------------------------- # Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their # associated separator. # Needed until we can rely on m4_join from Autoconf 2.62, since all earlier # versions in m4sugar had bugs. m4_define([lt_join], [m4_if([$#], [1], [], [$#], [2], [[$2]], [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) m4_define([_lt_join], [m4_if([$#$2], [2], [], [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) # lt_car(LIST) # lt_cdr(LIST) # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support # Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], [$#], 1, [], [m4_dquote(m4_shift($@))])]) m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ # Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different # than defined and empty). # # This macro is needed until we can rely on Autoconf 2.62, since earlier # versions of m4sugar mistakenly expanded SEPARATOR but not STRING. m4_define([lt_append], [m4_define([$1], m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) # lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) # ---------------------------------------------------------- # Produce a SEP delimited list of all paired combinations of elements of # PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list # has the form PREFIXmINFIXSUFFIXn. # Needed until we can rely on m4_combine added in Autoconf 2.62. m4_define([lt_combine], [m4_if(m4_eval([$# > 3]), [1], [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl [[m4_foreach([_Lt_prefix], [$2], [m4_foreach([_Lt_suffix], ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) # lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) # ----------------------------------------------------------------------- # Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited # by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. m4_define([lt_if_append_uniq], [m4_ifdef([$1], [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], [lt_append([$1], [$2], [$3])$4], [$5])], [lt_append([$1], [$2], [$3])$4])]) # lt_dict_add(DICT, KEY, VALUE) # ----------------------------- m4_define([lt_dict_add], [m4_define([$1($2)], [$3])]) # lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) # -------------------------------------------- m4_define([lt_dict_add_subkey], [m4_define([$1($2:$3)], [$4])]) # lt_dict_fetch(DICT, KEY, [SUBKEY]) # ---------------------------------- m4_define([lt_dict_fetch], [m4_ifval([$3], m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) # lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) # ----------------------------------------------------------------- m4_define([lt_if_dict_fetch], [m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], [$5], [$6])]) # lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) # -------------------------------------------------------------- m4_define([lt_dict_filter], [m4_if([$5], [], [], [lt_join(m4_quote(m4_default([$4], [[, ]])), lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) # ltversion.m4 -- version numbers -*- Autoconf -*- # # Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # @configure_input@ # serial 4179 ltversion.m4 # This file is part of GNU Libtool m4_define([LT_PACKAGE_VERSION], [2.4.6]) m4_define([LT_PACKAGE_REVISION], [2.4.6]) AC_DEFUN([LTVERSION_VERSION], [macro_version='2.4.6' macro_revision='2.4.6' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # # Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software # Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. # serial 5 lt~obsolete.m4 # These exist entirely to fool aclocal when bootstrapping libtool. # # In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # # The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN # in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us # using a macro with the same name in our local m4/libtool.m4 it'll # pull the old libtool.m4 in (it doesn't see our shiny new m4_define # and doesn't know about Autoconf macros at all.) # # So we provide this file, which has a silly filename so it's always # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. # We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until # we give up compatibility with versions before 1.7, at which point # we need to keep only those names which we still refer to. # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) m4_ifndef([_LT_REQUIRED_DARWIN_CHECKS], [AC_DEFUN([_LT_REQUIRED_DARWIN_CHECKS])]) m4_ifndef([_LT_AC_PROG_CXXCPP], [AC_DEFUN([_LT_AC_PROG_CXXCPP])]) m4_ifndef([_LT_PREPARE_SED_QUOTE_VARS], [AC_DEFUN([_LT_PREPARE_SED_QUOTE_VARS])]) m4_ifndef([_LT_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_PROG_ECHO_BACKSLASH])]) m4_ifndef([_LT_PROG_F77], [AC_DEFUN([_LT_PROG_F77])]) m4_ifndef([_LT_PROG_FC], [AC_DEFUN([_LT_PROG_FC])]) m4_ifndef([_LT_PROG_CXX], [AC_DEFUN([_LT_PROG_CXX])]) # pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- # serial 11 (pkg-config-0.29.1) dnl Copyright © 2004 Scott James Remnant . dnl Copyright © 2012-2015 Dan Nicholson dnl dnl This program is free software; you can redistribute it and/or modify dnl it under the terms of the GNU General Public License as published by dnl the Free Software Foundation; either version 2 of the License, or dnl (at your option) any later version. dnl dnl This program is distributed in the hope that it will be useful, but dnl WITHOUT ANY WARRANTY; without even the implied warranty of dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dnl General Public License for more details. dnl dnl You should have received a copy of the GNU General Public License dnl along with this program; if not, write to the Free Software dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA dnl 02111-1307, USA. dnl dnl As a special exception to the GNU General Public License, if you dnl distribute this file as part of a program that contains a dnl configuration script generated by Autoconf, you may include it under dnl the same distribution terms that you use for the rest of that dnl program. dnl PKG_PREREQ(MIN-VERSION) dnl ----------------------- dnl Since: 0.29 dnl dnl Verify that the version of the pkg-config macros are at least dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's dnl installed version of pkg-config, this checks the developer's version dnl of pkg.m4 when generating configure. dnl dnl To ensure that this macro is defined, also add: dnl m4_ifndef([PKG_PREREQ], dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) dnl dnl See the "Since" comment for each macro you use to see what version dnl of the macros you require. m4_defun([PKG_PREREQ], [m4_define([PKG_MACROS_VERSION], [0.29.1]) m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) ])dnl PKG_PREREQ dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) dnl ---------------------------------- dnl Since: 0.16 dnl dnl Search for the pkg-config tool and set the PKG_CONFIG variable to dnl first found in the path. Checks that the version of pkg-config found dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is dnl used since that's the first version where most current features of dnl pkg-config existed. AC_DEFUN([PKG_PROG_PKG_CONFIG], [m4_pattern_forbid([^_?PKG_[A-Z_]+$]) m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) fi if test -n "$PKG_CONFIG"; then _pkg_min_version=m4_default([$1], [0.9.0]) AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) PKG_CONFIG="" fi fi[]dnl ])dnl PKG_PROG_PKG_CONFIG dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------------------------------- dnl Since: 0.18 dnl dnl Check to see whether a particular set of modules exists. Similar to dnl PKG_CHECK_MODULES(), but does not set variables or print errors. dnl dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) dnl only at the first occurence in configure.ac, so if the first place dnl it's called might be skipped (such as if it is within an "if", you dnl have to call PKG_CHECK_EXISTS manually AC_DEFUN([PKG_CHECK_EXISTS], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl if test -n "$PKG_CONFIG" && \ AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then m4_default([$2], [:]) m4_ifvaln([$3], [else $3])dnl fi]) dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) dnl --------------------------------------------- dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting dnl pkg_failed based on the result. m4_define([_PKG_CONFIG], [if test -n "$$1"; then pkg_cv_[]$1="$$1" elif test -n "$PKG_CONFIG"; then PKG_CHECK_EXISTS([$3], [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` test "x$?" != "x0" && pkg_failed=yes ], [pkg_failed=yes]) else pkg_failed=untried fi[]dnl ])dnl _PKG_CONFIG dnl _PKG_SHORT_ERRORS_SUPPORTED dnl --------------------------- dnl Internal check to see if pkg-config supports short errors. AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], [AC_REQUIRE([PKG_PROG_PKG_CONFIG]) if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then _pkg_short_errors_supported=yes else _pkg_short_errors_supported=no fi[]dnl ])dnl _PKG_SHORT_ERRORS_SUPPORTED dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl -------------------------------------------------------------- dnl Since: 0.4.0 dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES might not happen, you should be sure to include an dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac AC_DEFUN([PKG_CHECK_MODULES], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl pkg_failed=no AC_MSG_CHECKING([for $1]) _PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) _PKG_CONFIG([$1][_LIBS], [libs], [$2]) m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS and $1[]_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details.]) if test $pkg_failed = yes; then AC_MSG_RESULT([no]) _PKG_SHORT_ERRORS_SUPPORTED if test $_pkg_short_errors_supported = yes; then $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` else $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` fi # Put the nasty error message in config.log where it belongs echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD m4_default([$4], [AC_MSG_ERROR( [Package requirements ($2) were not met: $$1_PKG_ERRORS Consider adjusting the PKG_CONFIG_PATH environment variable if you installed software in a non-standard prefix. _PKG_TEXT])[]dnl ]) elif test $pkg_failed = untried; then AC_MSG_RESULT([no]) m4_default([$4], [AC_MSG_FAILURE( [The pkg-config script could not be found or is too old. Make sure it is in your PATH or set the PKG_CONFIG environment variable to the full path to pkg-config. _PKG_TEXT To get pkg-config, see .])[]dnl ]) else $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS $1[]_LIBS=$pkg_cv_[]$1[]_LIBS AC_MSG_RESULT([yes]) $3 fi[]dnl ])dnl PKG_CHECK_MODULES dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], dnl [ACTION-IF-NOT-FOUND]) dnl --------------------------------------------------------------------- dnl Since: 0.29 dnl dnl Checks for existence of MODULES and gathers its build flags with dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags dnl and VARIABLE-PREFIX_LIBS from --libs. dnl dnl Note that if there is a possibility the first call to dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to dnl include an explicit call to PKG_PROG_PKG_CONFIG in your dnl configure.ac. AC_DEFUN([PKG_CHECK_MODULES_STATIC], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl _save_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" PKG_CHECK_MODULES($@) PKG_CONFIG=$_save_PKG_CONFIG[]dnl ])dnl PKG_CHECK_MODULES_STATIC dnl PKG_INSTALLDIR([DIRECTORY]) dnl ------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable pkgconfigdir as the location where a module dnl should install pkg-config .pc files. By default the directory is dnl $libdir/pkgconfig, but the default can be changed by passing dnl DIRECTORY. The user can override through the --with-pkgconfigdir dnl parameter. AC_DEFUN([PKG_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([pkgconfigdir], [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, [with_pkgconfigdir=]pkg_default) AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_INSTALLDIR dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) dnl -------------------------------- dnl Since: 0.27 dnl dnl Substitutes the variable noarch_pkgconfigdir as the location where a dnl module should install arch-independent pkg-config .pc files. By dnl default the directory is $datadir/pkgconfig, but the default can be dnl changed by passing DIRECTORY. The user can override through the dnl --with-noarch-pkgconfigdir parameter. AC_DEFUN([PKG_NOARCH_INSTALLDIR], [m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) m4_pushdef([pkg_description], [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) AC_ARG_WITH([noarch-pkgconfigdir], [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, [with_noarch_pkgconfigdir=]pkg_default) AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) m4_popdef([pkg_default]) m4_popdef([pkg_description]) ])dnl PKG_NOARCH_INSTALLDIR dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ------------------------------------------- dnl Since: 0.28 dnl dnl Retrieves the value of the pkg-config variable for the given module. AC_DEFUN([PKG_CHECK_VAR], [AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl _PKG_CONFIG([$1], [variable="][$3]["], [$2]) AS_VAR_COPY([$1], [pkg_cv_][$1]) AS_VAR_IF([$1], [""], [$5], [$4])dnl ])dnl PKG_CHECK_VAR dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------ dnl dnl Prepare a "--with-" configure option using the lowercase dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and dnl PKG_CHECK_MODULES in a single macro. AC_DEFUN([PKG_WITH_MODULES], [ m4_pushdef([with_arg], m4_tolower([$1])) m4_pushdef([description], [m4_default([$5], [build with ]with_arg[ support])]) m4_pushdef([def_arg], [m4_default([$6], [auto])]) m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) m4_case(def_arg, [yes],[m4_pushdef([with_without], [--without-]with_arg)], [m4_pushdef([with_without],[--with-]with_arg)]) AC_ARG_WITH(with_arg, AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, [AS_TR_SH([with_]with_arg)=def_arg]) AS_CASE([$AS_TR_SH([with_]with_arg)], [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], [auto],[PKG_CHECK_MODULES([$1],[$2], [m4_n([def_action_if_found]) $3], [m4_n([def_action_if_not_found]) $4])]) m4_popdef([with_arg]) m4_popdef([description]) m4_popdef([def_arg]) ])dnl PKG_WITH_MODULES dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ----------------------------------------------- dnl dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES dnl check._[VARIABLE-PREFIX] is exported as make variable. AC_DEFUN([PKG_HAVE_WITH_MODULES], [ PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) AM_CONDITIONAL([HAVE_][$1], [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) ])dnl PKG_HAVE_WITH_MODULES dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, dnl [DESCRIPTION], [DEFAULT]) dnl ------------------------------------------------------ dnl dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make dnl and preprocessor variable. AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], [ PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) ])dnl PKG_HAVE_DEFINE_WITH_MODULES # Copyright (C) 2002-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_AUTOMAKE_VERSION(VERSION) # ---------------------------- # Automake X.Y traces this macro to ensure aclocal.m4 has been # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version='1.16' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. m4_if([$1], [1.16.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) # _AM_AUTOCONF_VERSION(VERSION) # ----------------------------- # aclocal traces this macro to find the Autoconf version. # This is a private macro too. Using m4_define simplifies # the logic in aclocal, which can simply ignore this definition. m4_define([_AM_AUTOCONF_VERSION], []) # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. # This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], [AM_AUTOMAKE_VERSION([1.16.1])dnl m4_ifndef([AC_AUTOCONF_VERSION], [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl _AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets # $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to # '$srcdir', '$srcdir/..', or '$srcdir/../..'. # # Of course, Automake must honor this variable whenever it calls a # tool from the auxiliary directory. The problem is that $srcdir (and # therefore $ac_aux_dir as well) can be either absolute or relative, # depending on how configure is run. This is pretty annoying, since # it makes $ac_aux_dir quite unusable in subdirectories: in the top # source directory, any form will work fine, but in subdirectories a # relative path needs to be adjusted first. # # $ac_aux_dir/missing # fails when called from a subdirectory if $ac_aux_dir is relative # $top_srcdir/$ac_aux_dir/missing # fails if $ac_aux_dir is absolute, # fails when called from a subdirectory in a VPATH build with # a relative $ac_aux_dir # # The reason of the latter failure is that $top_srcdir and $ac_aux_dir # are both prefixed by $srcdir. In an in-source build this is usually # harmless because $srcdir is '.', but things will broke when you # start a VPATH build or use an absolute $srcdir. # # So we could use something similar to $top_srcdir/$ac_aux_dir/missing, # iff we strip the leading $srcdir from $ac_aux_dir. That would be: # am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` # and then we would define $MISSING as # MISSING="\${SHELL} $am_aux_dir/missing" # This will work as long as MISSING is not called from configure, because # unfortunately $(top_srcdir) has no meaning in configure. # However there are other variables, like CC, which are often used in # configure, and could therefore not use this "fixed" $ac_aux_dir. # # Another solution, used here, is to always expand $ac_aux_dir to an # absolute PATH. The drawback is that using absolute paths prevent a # configured tree to be moved without reconfiguration. AC_DEFUN([AM_AUX_DIR_EXPAND], [AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl # Expand $ac_aux_dir to an absolute path. am_aux_dir=`cd "$ac_aux_dir" && pwd` ]) # AM_CONDITIONAL -*- Autoconf -*- # Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- # Define a conditional. AC_DEFUN([AM_CONDITIONAL], [AC_PREREQ([2.52])dnl m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl AC_SUBST([$1_TRUE])dnl AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' else $1_TRUE='#' $1_FALSE= fi AC_CONFIG_COMMANDS_PRE( [if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then AC_MSG_ERROR([[conditional "$1" was never defined. Usually this means the macro was only invoked conditionally.]]) fi])]) # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, # will think it sees a *use*, and therefore will trigger all it's # C support machinery. Also note that it means that autoscan, seeing # CC etc. in the Makefile, will ask for an AC_PROG_CC use... # _AM_DEPENDENCIES(NAME) # ---------------------- # See how the compiler implements dependency checking. # NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". # We try a few techniques and use that to set a single cache variable. # # We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was # modified to invoke _AM_DEPENDENCIES(CC); we would have a circular # dependency, and given that the user is not expected to run this macro, # just rely on AC_PROG_CC. AC_DEFUN([_AM_DEPENDENCIES], [AC_REQUIRE([AM_SET_DEPDIR])dnl AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl AC_REQUIRE([AM_MAKE_INCLUDE])dnl AC_REQUIRE([AM_DEP_TRACK])dnl m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], [$1], [CXX], [depcc="$CXX" am_compiler_list=], [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], [$1], [UPC], [depcc="$UPC" am_compiler_list=], [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], [depcc="$$1" am_compiler_list=]) AC_CACHE_CHECK([dependency style of $depcc], [am_cv_$1_dependencies_compiler_type], [if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up # making bogus files that we don't know about and never remove. For # instance it was reported that on HP-UX the gcc test will end up # making a dummy file named 'D' -- because '-MD' means "put the output # in D". rm -rf conftest.dir mkdir conftest.dir # Copy depcomp to subdir because otherwise we won't find it if we're # using a relative directory. cp "$am_depcomp" conftest.dir cd conftest.dir # We will build objects and dependencies in a subdirectory because # it helps to detect inapplicable dependency modes. For instance # both Tru64's cc and ICC support -MD to output dependencies as a # side effect of compilation, but ICC will put the dependencies in # the current directory while Tru64 will put them in the object # directory. mkdir sub am_cv_$1_dependencies_compiler_type=none if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi am__universal=false m4_case([$1], [CC], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac], [CXX], [case " $depcc " in #( *\ -arch\ *\ -arch\ *) am__universal=true ;; esac]) for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and # we should not choose a depcomp mode which is confused by this. # # We need to recreate these files for each test, as the compiler may # overwrite some of them when testing with obscure command lines. # This happens at least with the AIX C compiler. : > sub/conftest.c for i in 1 2 3 4 5 6; do echo '#include "conftst'$i'.h"' >> sub/conftest.c # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with # Solaris 10 /bin/sh. echo '/* dummy */' > sub/conftst$i.h done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf # We check with '-c' and '-o' for the sake of the "dashmstdout" # mode. It turns out that the SunPro C++ compiler does not properly # handle '-M -o', and we need to detect this. Also, some Intel # versions had trouble with output in subdirs. am__obj=sub/conftest.${OBJEXT-o} am__minus_obj="-o $am__obj" case $depmode in gcc) # This depmode causes a compiler race in universal mode. test "$am__universal" = false || continue ;; nosideeffect) # After this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested. if test "x$enable_dependency_tracking" = xyes; then continue else break fi ;; msvc7 | msvc7msys | msvisualcpp | msvcmsys) # This compiler won't grok '-c -o', but also, the minuso test has # not run yet. These depmodes are late enough in the game, and # so weak that their functioning should not be impacted. am__obj=conftest.${OBJEXT-o} am__minus_obj= ;; none) break ;; esac if depmode=$depmode \ source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message # that says an option was ignored or not supported. # When given -MP, icc 7.0 and 7.1 complain thusly: # icc: Command line warning: ignoring option '-M'; no argument required # The diagnosis changed in icc 8.0: # icc: Command line remark: option '-MP' not supported if (grep 'ignoring option' conftest.err || grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else am_cv_$1_dependencies_compiler_type=$depmode break fi fi done cd .. rm -rf conftest.dir else am_cv_$1_dependencies_compiler_type=none fi ]) AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) AM_CONDITIONAL([am__fastdep$1], [ test "x$enable_dependency_tracking" != xno \ && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) ]) # AM_SET_DEPDIR # ------------- # Choose a directory name for dependency files. # This macro is AC_REQUIREd in _AM_DEPENDENCIES. AC_DEFUN([AM_SET_DEPDIR], [AC_REQUIRE([AM_SET_LEADING_DOT])dnl AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl ]) # AM_DEP_TRACK # ------------ AC_DEFUN([AM_DEP_TRACK], [AC_ARG_ENABLE([dependency-tracking], [dnl AS_HELP_STRING( [--enable-dependency-tracking], [do not reject slow dependency extractors]) AS_HELP_STRING( [--disable-dependency-tracking], [speeds up one-time build])]) if test "x$enable_dependency_tracking" != xno; then am_depcomp="$ac_aux_dir/depcomp" AMDEPBACKSLASH='\' am__nodep='_no' fi AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) AC_SUBST([AMDEPBACKSLASH])dnl _AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl AC_SUBST([am__nodep])dnl _AM_SUBST_NOTMAKE([am__nodep])dnl ]) # Generate code to set up dependency tracking. -*- Autoconf -*- # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], [{ # Older Autoconf quotes --file arguments for eval, but not when files # are listed without --file. Let's play safe and only enable the eval # if we detect the quoting. # TODO: see whether this extra hack can be removed once we start # requiring Autoconf 2.70 or later. AS_CASE([$CONFIG_FILES], [*\'*], [eval set x "$CONFIG_FILES"], [*], [set x $CONFIG_FILES]) shift # Used to flag and report bootstrapping failures. am_rc=0 for am_mf do # Strip MF so we end up with the name of the file. am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` # Check whether this is an Automake generated Makefile which includes # dependency-tracking related rules and includes. # Grep'ing the whole file directly is not great: AIX grep has a line # limit of 2048, but all sed's we know have understand at least 4000. sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ || continue am_dirpart=`AS_DIRNAME(["$am_mf"])` am_filepart=`AS_BASENAME(["$am_mf"])` AM_RUN_LOG([cd "$am_dirpart" \ && sed -e '/# am--include-marker/d' "$am_filepart" \ | $MAKE -f - am--depfiles]) || am_rc=$? done if test $am_rc -ne 0; then AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments for automatic dependency tracking. Try re-running configure with the '--disable-dependency-tracking' option to at least be able to build the package (albeit without support for automatic dependency tracking).]) fi AS_UNSET([am_dirpart]) AS_UNSET([am_filepart]) AS_UNSET([am_mf]) AS_UNSET([am_rc]) rm -f conftest-deps.mk } ])# _AM_OUTPUT_DEPENDENCY_COMMANDS # AM_OUTPUT_DEPENDENCY_COMMANDS # ----------------------------- # This macro should only be invoked once -- use via AC_REQUIRE. # # This code is only required when automatic dependency tracking is enabled. # This creates each '.Po' and '.Plo' makefile fragment that we'll need in # order to bootstrap the dependency handling code. AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], [AC_CONFIG_COMMANDS([depfiles], [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC]) [_AM_PROG_CC_C_O ]) # AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) # AM_INIT_AUTOMAKE([OPTIONS]) # ----------------------------------------------- # The call with PACKAGE and VERSION arguments is the old style # call (pre autoconf-2.50), which is being phased out. PACKAGE # and VERSION should now be passed to AC_INIT and removed from # the call to AM_INIT_AUTOMAKE. # We support both call styles for the transition. After # the next Automake release, Autoconf can make the AC_INIT # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], [AC_PREREQ([2.65])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl AC_REQUIRE([AC_PROG_INSTALL])dnl if test "`cd $srcdir && pwd`" != "`pwd`"; then # Use -I$(srcdir) only when $(srcdir) != ., so that make's output # is not polluted with repeated "-I." AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl # test to see if srcdir already configured if test -f $srcdir/config.status; then AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) fi fi # test whether we have cygpath if test -z "$CYGPATH_W"; then if (cygpath --version) >/dev/null 2>/dev/null; then CYGPATH_W='cygpath -w' else CYGPATH_W=echo fi fi AC_SUBST([CYGPATH_W]) # Define the identity of the package. dnl Distinguish between old-style and new-style calls. m4_ifval([$2], [AC_DIAGNOSE([obsolete], [$0: two- and three-arguments forms are deprecated.]) m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl AC_SUBST([PACKAGE], [$1])dnl AC_SUBST([VERSION], [$2])], [_AM_SET_OPTIONS([$1])dnl dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. m4_if( m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), [ok:ok],, [m4_fatal([AC_INIT should be called with package and version arguments])])dnl AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl _AM_IF_OPTION([no-define],, [AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl # Some tools Automake needs. AC_REQUIRE([AM_SANITY_CHECK])dnl AC_REQUIRE([AC_ARG_PROGRAM])dnl AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) AM_MISSING_PROG([AUTOCONF], [autoconf]) AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) AM_MISSING_PROG([AUTOHEADER], [autoheader]) AM_MISSING_PROG([MAKEINFO], [makeinfo]) AC_REQUIRE([AM_PROG_INSTALL_SH])dnl AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AC_PROG_MKDIR_P])dnl # For better backward compatibility. To be removed once Automake 1.9.x # dies out for good. For more background, see: # # AC_SUBST([mkdir_p], ['$(MKDIR_P)']) # We need awk for the "check" target (and possibly the TAP driver). The # system "awk" is bad on some platforms. AC_REQUIRE([AC_PROG_AWK])dnl AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], [_AM_DEPENDENCIES([CC])], [m4_define([AC_PROG_CC], m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], [_AM_DEPENDENCIES([CXX])], [m4_define([AC_PROG_CXX], m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], [_AM_DEPENDENCIES([OBJC])], [m4_define([AC_PROG_OBJC], m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], [_AM_DEPENDENCIES([OBJCXX])], [m4_define([AC_PROG_OBJCXX], m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl ]) AC_REQUIRE([AM_SILENT_RULES])dnl dnl The testsuite driver may need to know about EXEEXT, so add the dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. AC_CONFIG_COMMANDS_PRE(dnl [m4_provide_if([_AM_COMPILER_EXEEXT], [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl # POSIX will say in a future version that running "rm -f" with no argument # is OK; and we want to be able to make that assumption in our Makefile # recipes. So use an aggressive probe to check that the usage we want is # actually supported "in the wild" to an acceptable degree. # See automake bug#10828. # To make any issue more visible, cause the running configure to be aborted # by default if the 'rm' program in use doesn't match our expectations; the # user can still override this though. if rm -f && rm -fr && rm -rf; then : OK; else cat >&2 <<'END' Oops! Your 'rm' program seems unable to run without file operands specified on the command line, even when the '-f' option is present. This is contrary to the behaviour of most rm programs out there, and not conforming with the upcoming POSIX standard: Please tell bug-automake@gnu.org about your system, including the value of your $PATH and any error possibly output before this message. This can help us improve future automake versions. END if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then echo 'Configuration will proceed anyway, since you have set the' >&2 echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 echo >&2 else cat >&2 <<'END' Aborting the configuration process, to ensure you take notice of the issue. You can download and install GNU coreutils to get an 'rm' implementation that behaves properly: . If you want to complete the configuration process using your problematic 'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM to "yes", and re-run configure. END AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) fi fi dnl The trailing newline in this macro's definition is deliberate, for dnl backward compatibility and to allow trailing 'dnl'-style comments dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. ]) dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further dnl mangled by Autoconf and run in a shell conditional statement. m4_define([_AC_COMPILER_EXEEXT], m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header # that is generated. The stamp files are numbered to have different names. # Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the # loop where config.status creates the headers, so we can generate # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. _am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_SH # ------------------ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl if test x"${install_sh+set}" != xset; then case $am_aux_dir in *\ * | *\ *) install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; *) install_sh="\${SHELL} $am_aux_dir/install-sh" esac fi AC_SUBST([install_sh])]) # Copyright (C) 2003-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # Check whether the underlying file-system supports filenames # with a leading dot. For instance MS-DOS doesn't. AC_DEFUN([AM_SET_LEADING_DOT], [rm -rf .tst 2>/dev/null mkdir .tst 2>/dev/null if test -d .tst; then am__leading_dot=. else am__leading_dot=_ fi rmdir .tst 2>/dev/null AC_SUBST([am__leading_dot])]) # Check to see how 'make' treats includes. -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MAKE_INCLUDE() # ----------------- # Check whether make has an 'include' directive that can support all # the idioms we need for our automatic dependency tracking code. AC_DEFUN([AM_MAKE_INCLUDE], [AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) cat > confinc.mk << 'END' am__doit: @echo this is the am__doit target >confinc.out .PHONY: am__doit END am__include="#" am__quote= # BSD make does it like this. echo '.include "confinc.mk" # ignored' > confmf.BSD # Other make implementations (GNU, Solaris 10, AIX) do it like this. echo 'include confinc.mk # ignored' > confmf.GNU _am_result=no for s in GNU BSD; do AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) AS_CASE([$?:`cat confinc.out 2>/dev/null`], ['0:this is the am__doit target'], [AS_CASE([$s], [BSD], [am__include='.include' am__quote='"'], [am__include='include' am__quote=''])]) if test "$am__include" != "#"; then _am_result="yes ($s style)" break fi done rm -f confinc.* confmf.* AC_MSG_RESULT([${_am_result}]) AC_SUBST([am__include])]) AC_SUBST([am__quote])]) # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- # Copyright (C) 1997-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ AC_DEFUN([AM_MISSING_PROG], [AC_REQUIRE([AM_MISSING_HAS_RUN]) $1=${$1-"${am_missing_run}$2"} AC_SUBST($1)]) # AM_MISSING_HAS_RUN # ------------------ # Define MISSING if not defined so far and test if it is modern enough. # If it is, set am_missing_run to use it, otherwise, to nothing. AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl if test x"${MISSING+set}" != xset; then case $am_aux_dir in *\ * | *\ *) MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; *) MISSING="\${SHELL} $am_aux_dir/missing" ;; esac fi # Use eval to expand $SHELL if eval "$MISSING --is-lightweight"; then am_missing_run="$MISSING " else am_missing_run= AC_MSG_WARN(['missing' script is too old or missing]) fi ]) # -*- Autoconf -*- # Obsolete and "removed" macros, that must however still report explicit # error messages when used, to smooth transition. # # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. AC_DEFUN([AM_CONFIG_HEADER], [AC_DIAGNOSE([obsolete], ['$0': this macro is obsolete. You should use the 'AC][_CONFIG_HEADERS' macro instead.])dnl AC_CONFIG_HEADERS($@)]) AC_DEFUN([AM_PROG_CC_STDC], [AC_PROG_CC am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc AC_DIAGNOSE([obsolete], ['$0': this macro is obsolete. You should simply use the 'AC][_PROG_CC' macro instead. Also, your code should no longer depend upon 'am_cv_prog_cc_stdc', but upon 'ac_cv_prog_cc_stdc'.])]) AC_DEFUN([AM_C_PROTOTYPES], [AC_FATAL([automatic de-ANSI-fication support has been removed])]) AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES]) # Helper functions for option handling. -*- Autoconf -*- # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_MANGLE_OPTION(NAME) # ----------------------- AC_DEFUN([_AM_MANGLE_OPTION], [[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) # _AM_SET_OPTION(NAME) # -------------------- # Set option NAME. Presently that only means defining a flag for this option. AC_DEFUN([_AM_SET_OPTION], [m4_define(_AM_MANGLE_OPTION([$1]), [1])]) # _AM_SET_OPTIONS(OPTIONS) # ------------------------ # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], [m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- # Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. AC_DEFUN([_AM_IF_OPTION], [m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) # Copyright (C) 1999-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_CC_C_O # --------------- # Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC # to automatically call this. AC_DEFUN([_AM_PROG_CC_C_O], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([compile])dnl AC_LANG_PUSH([C])dnl AC_CACHE_CHECK( [whether $CC understands -c and -o together], [am_cv_prog_cc_c_o], [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) # Make sure it works both with $CC and with simple cc. # Following AC_PROG_CC_C_O, we do the test twice because some # compilers refuse to overwrite an existing .o file with -o, # though they will create one. am_cv_prog_cc_c_o=yes for am_i in 1 2; do if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ && test -f conftest2.$ac_objext; then : OK else am_cv_prog_cc_c_o=no break fi done rm -f core conftest* unset am_i]) if test "$am_cv_prog_cc_c_o" != yes; then # Losing compiler, so override with the script. # FIXME: It is wrong to rewrite CC. # But if we don't then we get into trouble of one sort or another. # A longer-term fix would be to have automake use am__CC in this case, # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" CC="$am_aux_dir/compile $CC" fi AC_LANG_POP([C])]) # For backward compatibility. AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_RUN_LOG(COMMAND) # ------------------- # Run COMMAND, save the exit status in ac_status, and log it. # (This has been adapted from Autoconf's _AC_RUN_LOG macro.) AC_DEFUN([AM_RUN_LOG], [{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ac_status=$? echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD (exit $ac_status); }]) # Check to make sure that the build environment is sane. -*- Autoconf -*- # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SANITY_CHECK # --------------- AC_DEFUN([AM_SANITY_CHECK], [AC_MSG_CHECKING([whether build environment is sane]) # Reject unsafe characters in $srcdir or the absolute working directory # name. Accept space and tab only in the latter. am_lf=' ' case `pwd` in *[[\\\"\#\$\&\'\`$am_lf]]*) AC_MSG_ERROR([unsafe absolute working directory name]);; esac case $srcdir in *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; esac # Do 'set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( am_has_slept=no for am_try in 1 2; do echo "timestamp, slept: $am_has_slept" > conftest.file set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. set X `ls -t "$srcdir/configure" conftest.file` fi if test "$[*]" != "X $srcdir/configure conftest.file" \ && test "$[*]" != "X conftest.file $srcdir/configure"; then # If neither matched, then we have a broken ls. This can happen # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken alias in your environment]) fi if test "$[2]" = conftest.file || test $am_try -eq 2; then break fi # Just in case. sleep 1 am_has_slept=yes done test "$[2]" = conftest.file ) then # Ok. : else AC_MSG_ERROR([newly created file is older than distributed files! Check your system clock]) fi AC_MSG_RESULT([yes]) # If we didn't sleep, we still need to ensure time stamps of config.status and # generated files are strictly newer. am_sleep_pid= if grep 'slept: no' conftest.file >/dev/null 2>&1; then ( sleep 1 ) & am_sleep_pid=$! fi AC_CONFIG_COMMANDS_PRE( [AC_MSG_CHECKING([that generated files are newer than configure]) if test -n "$am_sleep_pid"; then # Hide warnings about reused PIDs. wait $am_sleep_pid 2>/dev/null fi AC_MSG_RESULT([done])]) rm -f conftest.file ]) # Copyright (C) 2009-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_SILENT_RULES([DEFAULT]) # -------------------------- # Enable less verbose build rules; with the default set to DEFAULT # ("yes" being less verbose, "no" or empty being verbose). AC_DEFUN([AM_SILENT_RULES], [AC_ARG_ENABLE([silent-rules], [dnl AS_HELP_STRING( [--enable-silent-rules], [less verbose build output (undo: "make V=1")]) AS_HELP_STRING( [--disable-silent-rules], [verbose build output (undo: "make V=0")])dnl ]) case $enable_silent_rules in @%:@ ((( yes) AM_DEFAULT_VERBOSITY=0;; no) AM_DEFAULT_VERBOSITY=1;; *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; esac dnl dnl A few 'make' implementations (e.g., NonStop OS and NextStep) dnl do not support nested variable expansions. dnl See automake bug#9928 and bug#10237. am_make=${MAKE-make} AC_CACHE_CHECK([whether $am_make supports nested variables], [am_cv_make_support_nested_variables], [if AS_ECHO([['TRUE=$(BAR$(V)) BAR0=false BAR1=true V=1 am__doit: @$(TRUE) .PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then am_cv_make_support_nested_variables=yes else am_cv_make_support_nested_variables=no fi]) if test $am_cv_make_support_nested_variables = yes; then dnl Using '$V' instead of '$(V)' breaks IRIX make. AM_V='$(V)' AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' else AM_V=$AM_DEFAULT_VERBOSITY AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY fi AC_SUBST([AM_V])dnl AM_SUBST_NOTMAKE([AM_V])dnl AC_SUBST([AM_DEFAULT_V])dnl AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl AC_SUBST([AM_DEFAULT_VERBOSITY])dnl AM_BACKSLASH='\' AC_SUBST([AM_BACKSLASH])dnl _AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl ]) # Copyright (C) 2001-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # AM_PROG_INSTALL_STRIP # --------------------- # One issue with vendor 'install' (even GNU) is that you can't # specify the program used to strip binaries. This is especially # annoying in cross-compiling environments, where the build's strip # is unlikely to handle the host's binaries. # Fortunately install-sh will honor a STRIPPROG variable, so we # always use install-sh in "make install-strip", and initialize # STRIPPROG with the value of the STRIP variable (set by the user). AC_DEFUN([AM_PROG_INSTALL_STRIP], [AC_REQUIRE([AM_PROG_INSTALL_SH])dnl # Installed binaries are usually stripped using 'strip' when the user # run "make install-strip". However 'strip' might not be the right # tool to use in cross-compilation environments, therefore Automake # will honor the 'STRIP' environment variable to overrule this program. dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. if test "$cross_compiling" != no; then AC_CHECK_TOOL([STRIP], [strip], :) fi INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) # Copyright (C) 2006-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- # Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) # AM_SUBST_NOTMAKE(VARIABLE) # -------------------------- # Public sister of _AM_SUBST_NOTMAKE. AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004-2018 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # _AM_PROG_TAR(FORMAT) # -------------------- # Check how to create a tarball in format FORMAT. # FORMAT should be one of 'v7', 'ustar', or 'pax'. # # Substitute a variable $(am__tar) that is a command # writing to stdout a FORMAT-tarball containing the directory # $tardir. # tardir=directory && $(am__tar) > result.tar # # Substitute a variable $(am__untar) that extract such # a tarball read from stdin. # $(am__untar) < result.tar # AC_DEFUN([_AM_PROG_TAR], [# Always define AMTAR for backward compatibility. Yes, it's still used # in the wild :-( We should find a proper way to deprecate it ... AC_SUBST([AMTAR], ['$${TAR-tar}']) # We'll loop over all known methods to create a tar archive until one works. _am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' m4_if([$1], [v7], [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], [m4_case([$1], [ustar], [# The POSIX 1988 'ustar' format is defined with fixed-size fields. # There is notably a 21 bits limit for the UID and the GID. In fact, # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 # and bug#13588). am_max_uid=2097151 # 2^21 - 1 am_max_gid=$am_max_uid # The $UID and $GID variables are not portable, so we need to resort # to the POSIX-mandated id(1) utility. Errors in the 'id' calls # below are definitely unexpected, so allow the users to see them # (that is, avoid stderr redirection). am_uid=`id -u || echo unknown` am_gid=`id -g || echo unknown` AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) if test $am_uid -le $am_max_uid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) if test $am_gid -le $am_max_gid; then AC_MSG_RESULT([yes]) else AC_MSG_RESULT([no]) _am_tools=none fi], [pax], [], [m4_fatal([Unknown tar format])]) AC_MSG_CHECKING([how to create a $1 tar archive]) # Go ahead even if we have the value already cached. We do so because we # need to set the values for the 'am__tar' and 'am__untar' variables. _am_tools=${am_cv_prog_tar_$1-$_am_tools} for _am_tool in $_am_tools; do case $_am_tool in gnutar) for _am_tar in tar gnutar gtar; do AM_RUN_LOG([$_am_tar --version]) && break done am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' am__untar="$_am_tar -xf -" ;; plaintar) # Must skip GNU tar: if it does not support --format= it doesn't create # ustar tarball either. (tar --version) >/dev/null 2>&1 && continue am__tar='tar chf - "$$tardir"' am__tar_='tar chf - "$tardir"' am__untar='tar xf -' ;; pax) am__tar='pax -L -x $1 -w "$$tardir"' am__tar_='pax -L -x $1 -w "$tardir"' am__untar='pax -r' ;; cpio) am__tar='find "$$tardir" -print | cpio -o -H $1 -L' am__tar_='find "$tardir" -print | cpio -o -H $1 -L' am__untar='cpio -i -H $1 -d' ;; none) am__tar=false am__tar_=false am__untar=false ;; esac # If the value was cached, stop now. We just wanted to have am__tar # and am__untar set. test -n "${am_cv_prog_tar_$1}" && break # tar/untar a dummy directory, and stop if the command works. rm -rf conftest.dir mkdir conftest.dir echo GrepMe > conftest.dir/file AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) rm -rf conftest.dir if test -s conftest.tar; then AM_RUN_LOG([$am__untar /dev/null 2>&1 && break fi done rm -rf conftest.dir AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) AC_MSG_RESULT([$am_cv_prog_tar_$1])]) AC_SUBST([am__tar]) AC_SUBST([am__untar]) ]) # _AM_PROG_TAR snort-2.9.20/RELEASE.NOTES0000444000175000017500000000100314242720226012754 0ustar apoapo2022-05-13 - Snort 2.9.20 [*] New Additions Added support for dns root queries and underflow. Added support to get extra data from SMTP and HTTP into IPS event. Added support for login success and failure eventing for IMAP and POP3. Added support to handle empty string for SNI/CN/SAN/ORG. [*] Improvements / Fix Fixed a scenario where SSL traffic was not detected correctly. Fixed security zones info in intrusion events. Fixed URL lookup failure. snort-2.9.20/ylwrap0000755000175000017500000001531414242725547012355 0ustar apoapo#! /bin/sh # ylwrap - wrapper for lex/yacc invocations. scriptversion=2018-03-07.03; # UTC # Copyright (C) 1996-2018 Free Software Foundation, Inc. # # Written by Tom Tromey . # # 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, 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # This file is maintained in Automake, please report # bugs to or send patches to # . get_dirname () { case $1 in */*|*\\*) printf '%s\n' "$1" | sed -e 's|\([\\/]\)[^\\/]*$|\1|';; # Otherwise, we want the empty string (not "."). esac } # guard FILE # ---------- # The CPP macro used to guard inclusion of FILE. guard () { printf '%s\n' "$1" \ | sed \ -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g' \ -e 's/__*/_/g' } # quote_for_sed [STRING] # ---------------------- # Return STRING (or stdin) quoted to be used as a sed pattern. quote_for_sed () { case $# in 0) cat;; 1) printf '%s\n' "$1";; esac \ | sed -e 's|[][\\.*]|\\&|g' } case "$1" in '') echo "$0: No files given. Try '$0 --help' for more information." 1>&2 exit 1 ;; --basedir) basedir=$2 shift 2 ;; -h|--h*) cat <<\EOF Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... Wrapper for lex/yacc invocations, renaming files as desired. INPUT is the input file OUTPUT is one file PROG generates DESIRED is the file we actually want instead of OUTPUT PROGRAM is program to run ARGS are passed to PROG Any number of OUTPUT,DESIRED pairs may be used. Report bugs to . EOF exit $? ;; -v|--v*) echo "ylwrap $scriptversion" exit $? ;; esac # The input. input=$1 shift # We'll later need for a correct munging of "#line" directives. input_sub_rx=`get_dirname "$input" | quote_for_sed` case $input in [\\/]* | ?:[\\/]*) # Absolute path; do nothing. ;; *) # Relative path. Make it absolute. input=`pwd`/$input ;; esac input_rx=`get_dirname "$input" | quote_for_sed` # Since DOS filename conventions don't allow two dots, # the DOS version of Bison writes out y_tab.c instead of y.tab.c # and y_tab.h instead of y.tab.h. Test to see if this is the case. y_tab_nodot=false if test -f y_tab.c || test -f y_tab.h; then y_tab_nodot=true fi # The parser itself, the first file, is the destination of the .y.c # rule in the Makefile. parser=$1 # A sed program to s/FROM/TO/g for all the FROM/TO so that, for # instance, we rename #include "y.tab.h" into #include "parse.h" # during the conversion from y.tab.c to parse.c. sed_fix_filenames= # Also rename header guards, as Bison 2.7 for instance uses its header # guard in its implementation file. sed_fix_header_guards= while test $# -ne 0; do if test x"$1" = x"--"; then shift break fi from=$1 # Handle y_tab.c and y_tab.h output by DOS if $y_tab_nodot; then case $from in "y.tab.c") from=y_tab.c;; "y.tab.h") from=y_tab.h;; esac fi shift to=$1 shift sed_fix_filenames="${sed_fix_filenames}s|"`quote_for_sed "$from"`"|$to|g;" sed_fix_header_guards="${sed_fix_header_guards}s|"`guard "$from"`"|"`guard "$to"`"|g;" done # The program to run. prog=$1 shift # Make any relative path in $prog absolute. case $prog in [\\/]* | ?:[\\/]*) ;; *[\\/]*) prog=`pwd`/$prog ;; esac dirname=ylwrap$$ do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret' trap "ret=129; $do_exit" 1 trap "ret=130; $do_exit" 2 trap "ret=141; $do_exit" 13 trap "ret=143; $do_exit" 15 mkdir $dirname || exit 1 cd $dirname case $# in 0) "$prog" "$input" ;; *) "$prog" "$@" "$input" ;; esac ret=$? if test $ret -eq 0; then for from in * do to=`printf '%s\n' "$from" | sed "$sed_fix_filenames"` if test -f "$from"; then # If $2 is an absolute path name, then just use that, # otherwise prepend '../'. case $to in [\\/]* | ?:[\\/]*) target=$to;; *) target=../$to;; esac # Do not overwrite unchanged header files to avoid useless # recompilations. Always update the parser itself: it is the # destination of the .y.c rule in the Makefile. Divert the # output of all other files to a temporary file so we can # compare them to existing versions. if test $from != $parser; then realtarget=$target target=tmp-`printf '%s\n' "$target" | sed 's|.*[\\/]||g'` fi # Munge "#line" or "#" directives. Don't let the resulting # debug information point at an absolute srcdir. Use the real # output file name, not yy.lex.c for instance. Adjust the # include guards too. sed -e "/^#/!b" \ -e "s|$input_rx|$input_sub_rx|" \ -e "$sed_fix_filenames" \ -e "$sed_fix_header_guards" \ "$from" >"$target" || ret=$? # Check whether files must be updated. if test "$from" != "$parser"; then if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then echo "$to is unchanged" rm -f "$target" else echo "updating $to" mv -f "$target" "$realtarget" fi fi else # A missing file is only an error for the parser. This is a # blatant hack to let us support using "yacc -d". If -d is not # specified, don't fail when the header file is "missing". if test "$from" = "$parser"; then ret=1 fi fi done fi # Remove the directory. cd .. rm -rf $dirname exit $ret # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: snort-2.9.20/depcomp0000755000175000017500000005602014242725550012457 0ustar apoapo#! /bin/sh # depcomp - compile a program generating dependencies as side-effects scriptversion=2018-03-07.03; # UTC # Copyright (C) 1999-2018 Free Software Foundation, Inc. # 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, 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, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Alexandre Oliva . case $1 in '') echo "$0: No command. Try '$0 --help' for more information." 1>&2 exit 1; ;; -h | --h*) cat <<\EOF Usage: depcomp [--help] [--version] PROGRAM [ARGS] Run PROGRAMS ARGS to compile a file, generating dependencies as side-effects. Environment variables: depmode Dependency tracking mode. source Source file read by 'PROGRAMS ARGS'. object Object file output by 'PROGRAMS ARGS'. DEPDIR directory where to store dependencies. depfile Dependency file to output. tmpdepfile Temporary file to use when outputting dependencies. libtool Whether libtool is used (yes/no). Report bugs to . EOF exit $? ;; -v | --v*) echo "depcomp $scriptversion" exit $? ;; esac # Get the directory component of the given path, and save it in the # global variables '$dir'. Note that this directory component will # be either empty or ending with a '/' character. This is deliberate. set_dir_from () { case $1 in */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; *) dir=;; esac } # Get the suffix-stripped basename of the given path, and save it the # global variable '$base'. set_base_from () { base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` } # If no dependency file was actually created by the compiler invocation, # we still have to create a dummy depfile, to avoid errors with the # Makefile "include basename.Plo" scheme. make_dummy_depfile () { echo "#dummy" > "$depfile" } # Factor out some common post-processing of the generated depfile. # Requires the auxiliary global variable '$tmpdepfile' to be set. aix_post_process_depfile () { # If the compiler actually managed to produce a dependency file, # post-process it. if test -f "$tmpdepfile"; then # Each line is of the form 'foo.o: dependency.h'. # Do two passes, one to just change these to # $object: dependency.h # and one to simply output # dependency.h: # which is needed to avoid the deleted-header problem. { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" } > "$depfile" rm -f "$tmpdepfile" else make_dummy_depfile fi } # A tabulation character. tab=' ' # A newline character. nl=' ' # Character ranges might be problematic outside the C locale. # These definitions help. upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ lower=abcdefghijklmnopqrstuvwxyz digits=0123456789 alpha=${upper}${lower} if test -z "$depmode" || test -z "$source" || test -z "$object"; then echo "depcomp: Variables source, object and depmode must be set" 1>&2 exit 1 fi # Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. depfile=${depfile-`echo "$object" | sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} rm -f "$tmpdepfile" # Avoid interferences from the environment. gccflag= dashmflag= # Some modes work just like other modes, but use different flags. We # parameterize here, but still list the modes in the big case below, # to make depend.m4 easier to write. Note that we *cannot* use a case # here, because this file can only contain one case statement. if test "$depmode" = hp; then # HP compiler uses -M and no extra arg. gccflag=-M depmode=gcc fi if test "$depmode" = dashXmstdout; then # This is just like dashmstdout with a different argument. dashmflag=-xM depmode=dashmstdout fi cygpath_u="cygpath -u -f -" if test "$depmode" = msvcmsys; then # This is just like msvisualcpp but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvisualcpp fi if test "$depmode" = msvc7msys; then # This is just like msvc7 but w/o cygpath translation. # Just convert the backslash-escaped backslashes to single forward # slashes to satisfy depend.m4 cygpath_u='sed s,\\\\,/,g' depmode=msvc7 fi if test "$depmode" = xlc; then # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. gccflag=-qmakedep=gcc,-MF depmode=gcc fi case "$depmode" in gcc3) ## gcc 3 implements dependency tracking that does exactly what ## we want. Yay! Note: for some reason libtool 1.4 doesn't like ## it if -MD -MP comes after the -MF stuff. Hmm. ## Unfortunately, FreeBSD c89 acceptance of flags depends upon ## the command line argument order; so add the flags where they ## appear in depend2.am. Note that the slowdown incurred here ## affects only configure: in makefiles, %FASTDEP% shortcuts this. for arg do case $arg in -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; *) set fnord "$@" "$arg" ;; esac shift # fnord shift # $arg done "$@" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi mv "$tmpdepfile" "$depfile" ;; gcc) ## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. ## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. ## (see the conditional assignment to $gccflag above). ## There are various ways to get dependency output from gcc. Here's ## why we pick this rather obscure method: ## - Don't want to use -MD because we'd like the dependencies to end ## up in a subdir. Having to rename by hand is ugly. ## (We might end up doing this anyway to support other compilers.) ## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like ## -MM, not -M (despite what the docs say). Also, it might not be ## supported by the other compilers which use the 'gcc' depmode. ## - Using -M directly means running the compiler twice (even worse ## than renaming). if test -z "$gccflag"; then gccflag=-MD, fi "$@" -Wp,"$gccflag$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The second -e expression handles DOS-style file names with drive # letters. sed -e 's/^[^:]*: / /' \ -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" ## This next piece of magic avoids the "deleted header file" problem. ## The problem is that when a header file which appears in a .P file ## is deleted, the dependency causes make to die (because there is ## typically no way to rebuild the header). We avoid this by adding ## dummy dependencies for each header file. Too bad gcc doesn't do ## this for us directly. ## Some versions of gcc put a space before the ':'. On the theory ## that the space means something, we add a space to the output as ## well. hp depmode also adds that space, but also prefixes the VPATH ## to the object. Take care to not repeat it in the output. ## Some versions of the HPUX 10.20 sed can't process this invocation ## correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; sgi) if test "$libtool" = yes; then "$@" "-Wp,-MDupdate,$tmpdepfile" else "$@" -MDupdate "$tmpdepfile" fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files echo "$object : \\" > "$depfile" # Clip off the initial element (the dependent). Don't try to be # clever and replace this with sed code, as IRIX sed won't handle # lines with more than a fixed number of characters (4096 in # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; # the IRIX cc adds comments like '#:fec' to the end of the # dependency line. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ | tr "$nl" ' ' >> "$depfile" echo >> "$depfile" # The second pass generates a dummy entry for each header file. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" ;; xlc) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; aix) # The C for AIX Compiler uses -M and outputs the dependencies # in a .u file. In older versions, this file always lives in the # current directory. Also, the AIX compiler puts '$object:' at the # start of each line; $object doesn't have directory information. # Version 6 uses the directory in both cases. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.u tmpdepfile2=$base.u tmpdepfile3=$dir.libs/$base.u "$@" -Wc,-M else tmpdepfile1=$dir$base.u tmpdepfile2=$dir$base.u tmpdepfile3=$dir$base.u "$@" -M fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done aix_post_process_depfile ;; tcc) # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 # FIXME: That version still under development at the moment of writing. # Make that this statement remains true also for stable, released # versions. # It will wrap lines (doesn't matter whether long or short) with a # trailing '\', as in: # # foo.o : \ # foo.c \ # foo.h \ # # It will put a trailing '\' even on the last line, and will use leading # spaces rather than leading tabs (at least since its commit 0394caf7 # "Emit spaces for -MD"). "$@" -MD -MF "$tmpdepfile" stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. # We have to change lines of the first kind to '$object: \'. sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" # And for each line of the second kind, we have to emit a 'dep.h:' # dummy dependency, to avoid the deleted-header problem. sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" rm -f "$tmpdepfile" ;; ## The order of this option in the case statement is important, since the ## shell code in configure will try each of these formats in the order ## listed in this file. A plain '-MD' option would be understood by many ## compilers, so we must ensure this comes after the gcc and icc options. pgcc) # Portland's C compiler understands '-MD'. # Will always output deps to 'file.d' where file is the root name of the # source file under compilation, even if file resides in a subdirectory. # The object file name does not affect the name of the '.d' file. # pgcc 10.2 will output # foo.o: sub/foo.c sub/foo.h # and will wrap long lines using '\' : # foo.o: sub/foo.c ... \ # sub/foo.h ... \ # ... set_dir_from "$object" # Use the source, not the object, to determine the base name, since # that's sadly what pgcc will do too. set_base_from "$source" tmpdepfile=$base.d # For projects that build the same source file twice into different object # files, the pgcc approach of using the *source* file root name can cause # problems in parallel builds. Use a locking strategy to avoid stomping on # the same $tmpdepfile. lockdir=$base.d-lock trap " echo '$0: caught signal, cleaning up...' >&2 rmdir '$lockdir' exit 1 " 1 2 13 15 numtries=100 i=$numtries while test $i -gt 0; do # mkdir is a portable test-and-set. if mkdir "$lockdir" 2>/dev/null; then # This process acquired the lock. "$@" -MD stat=$? # Release the lock. rmdir "$lockdir" break else # If the lock is being held by a different process, wait # until the winning process is done or we timeout. while test -d "$lockdir" && test $i -gt 0; do sleep 1 i=`expr $i - 1` done fi i=`expr $i - 1` done trap - 1 2 13 15 if test $i -le 0; then echo "$0: failed to acquire lock after $numtries attempts" >&2 echo "$0: check lockdir '$lockdir'" >&2 exit 1 fi if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" # Each line is of the form `foo.o: dependent.h', # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. # Do two passes, one to just change these to # `$object: dependent.h' and one to simply `dependent.h:'. sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this invocation # correctly. Breaking it into two sed invocations is a workaround. sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; hp2) # The "hp" stanza above does not work with aCC (C++) and HP's ia64 # compilers, which have integrated preprocessors. The correct option # to use with these is +Maked; it writes dependencies to a file named # 'foo.d', which lands next to the object file, wherever that # happens to be. # Much of this is similar to the tru64 case; see comments there. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then tmpdepfile1=$dir$base.d tmpdepfile2=$dir.libs/$base.d "$@" -Wc,+Maked else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d "$@" +Maked fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" do test -f "$tmpdepfile" && break done if test -f "$tmpdepfile"; then sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" # Add 'dependent.h:' lines. sed -ne '2,${ s/^ *// s/ \\*$// s/$/:/ p }' "$tmpdepfile" >> "$depfile" else make_dummy_depfile fi rm -f "$tmpdepfile" "$tmpdepfile2" ;; tru64) # The Tru64 compiler uses -MD to generate dependencies as a side # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put # dependencies in 'foo.d' instead, so we check for that too. # Subdirectories are respected. set_dir_from "$object" set_base_from "$object" if test "$libtool" = yes; then # Libtool generates 2 separate objects for the 2 libraries. These # two compilations output dependencies in $dir.libs/$base.o.d and # in $dir$base.o.d. We have to check for both files, because # one of the two compilations can be disabled. We should prefer # $dir$base.o.d over $dir.libs/$base.o.d because the latter is # automatically cleaned when .libs/ is deleted, while ignoring # the former would cause a distcleancheck panic. tmpdepfile1=$dir$base.o.d # libtool 1.5 tmpdepfile2=$dir.libs/$base.o.d # Likewise. tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 "$@" -Wc,-MD else tmpdepfile1=$dir$base.d tmpdepfile2=$dir$base.d tmpdepfile3=$dir$base.d "$@" -MD fi stat=$? if test $stat -ne 0; then rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" exit $stat fi for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" do test -f "$tmpdepfile" && break done # Same post-processing that is required for AIX mode. aix_post_process_depfile ;; msvc7) if test "$libtool" = yes; then showIncludes=-Wc,-showIncludes else showIncludes=-showIncludes fi "$@" $showIncludes > "$tmpdepfile" stat=$? grep -v '^Note: including file: ' "$tmpdepfile" if test $stat -ne 0; then rm -f "$tmpdepfile" exit $stat fi rm -f "$depfile" echo "$object : \\" > "$depfile" # The first sed program below extracts the file names and escapes # backslashes for cygpath. The second sed program outputs the file # name when reading, but also accumulates all include files in the # hold buffer in order to output them again at the end. This only # works with sed implementations that can handle large buffers. sed < "$tmpdepfile" -n ' /^Note: including file: *\(.*\)/ { s//\1/ s/\\/\\\\/g p }' | $cygpath_u | sort -u | sed -n ' s/ /\\ /g s/\(.*\)/'"$tab"'\1 \\/p s/.\(.*\) \\/\1:/ H $ { s/.*/'"$tab"'/ G p }' >> "$depfile" echo >> "$depfile" # make sure the fragment doesn't end with a backslash rm -f "$tmpdepfile" ;; msvc7msys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; #nosideeffect) # This comment above is used by automake to tell side-effect # dependency tracking mechanisms from slower ones. dashmstdout) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout, regardless of -o. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done test -z "$dashmflag" && dashmflag=-M # Require at least two characters before searching for ':' # in the target name. This is to cope with DOS-style filenames: # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. "$@" $dashmflag | sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" rm -f "$depfile" cat < "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process this sed invocation # correctly. Breaking it into two sed invocations is a workaround. tr ' ' "$nl" < "$tmpdepfile" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; dashXmstdout) # This case only exists to satisfy depend.m4. It is never actually # run, as this mode is specially recognized in the preamble. exit 1 ;; makedepend) "$@" || exit $? # Remove any Libtool call if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # X makedepend shift cleared=no eat=no for arg do case $cleared in no) set ""; shift cleared=yes ;; esac if test $eat = yes; then eat=no continue fi case "$arg" in -D*|-I*) set fnord "$@" "$arg"; shift ;; # Strip any option that makedepend may not understand. Remove # the object too, otherwise makedepend will parse it as a source file. -arch) eat=yes ;; -*|$object) ;; *) set fnord "$@" "$arg"; shift ;; esac done obj_suffix=`echo "$object" | sed 's/^.*\././'` touch "$tmpdepfile" ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" rm -f "$depfile" # makedepend may prepend the VPATH from the source file name to the object. # No need to regex-escape $object, excess matching of '.' is harmless. sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" # Some versions of the HPUX 10.20 sed can't process the last invocation # correctly. Breaking it into two sed invocations is a workaround. sed '1,2d' "$tmpdepfile" \ | tr ' ' "$nl" \ | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ | sed -e 's/$/ :/' >> "$depfile" rm -f "$tmpdepfile" "$tmpdepfile".bak ;; cpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi # Remove '-o $object'. IFS=" " for arg do case $arg in -o) shift ;; $object) shift ;; *) set fnord "$@" "$arg" shift # fnord shift # $arg ;; esac done "$@" -E \ | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ | sed '$ s: \\$::' > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" cat < "$tmpdepfile" >> "$depfile" sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" rm -f "$tmpdepfile" ;; msvisualcpp) # Important note: in order to support this mode, a compiler *must* # always write the preprocessed file to stdout. "$@" || exit $? # Remove the call to Libtool. if test "$libtool" = yes; then while test "X$1" != 'X--mode=compile'; do shift done shift fi IFS=" " for arg do case "$arg" in -o) shift ;; $object) shift ;; "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") set fnord "$@" shift shift ;; *) set fnord "$@" "$arg" shift shift ;; esac done "$@" -E 2>/dev/null | sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" rm -f "$depfile" echo "$object : \\" > "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" echo "$tab" >> "$depfile" sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" rm -f "$tmpdepfile" ;; msvcmsys) # This case exists only to let depend.m4 do its work. It works by # looking at the text of this script. This case will never be run, # since it is checked for above. exit 1 ;; none) exec "$@" ;; *) echo "Unknown depmode $depmode" 1>&2 exit 1 ;; esac exit 0 # Local Variables: # mode: shell-script # sh-indentation: 2 # eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: snort-2.9.20/LICENSE0000444000175000017500000005103114230012553012067 0ustar apoapo***************************************************************************** The text that follows is the GNU General Public License, Version 2 (GPL V2) and governs your use, modification and/or distribution of SNORT. Section 9 of the GPL V2 acknowledges that the Free Software Foundation may publish revised and/or new versions of the GPL V2 from time to time. Section 9 further states that a licensee of a program subject to the GPL V2 could be free to use any such revised and/or new versions under two different scenarios: 1. "Failure to Specify." Section 9 of the GPL V2 allows a licensee of a program governed by an unspecified version of the General Public License to choose any version of the General Public License ever published by the Free Software Foundation to govern his or her use of such program. This provision is not applicable to your use of SNORT because we have expressly stated in a number of instances that any third party's use, modification or distribution of SNORT is governed by GPL V2. 2. "Any Later Version." At the end of the terms and condition of the GPL V2 is a section called "How to Apply these Terms to Your New Program," which provides guidance to a developer on how to apply the GPL V2 to a third party's use, modification and/or distribution of his/her program. Among other things, this guidance suggests that the developer attach certain notices to the program. Of particular importance is the following notice: "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." Thus if a developer follows strictly the guidance provided by the Free Software Foundation, Section 9 of the GPL V2 provides the licensee the option to either use, modify or distribute the program under GPL V2 or under any later version published by the Free Software Foundation. SNORT is an open source project that is governed exclusively by the GPL V2 and any third party desiring to use, modify or distribute SNORT must do so by strictly following the terms and conditions of GPL V2. Anyone using, modifying or distributing SNORT does not have the option to chose to use, modify or distribute SNORT under any revised or new version of the GPL, including without limitation, the GNU General Public License Version 3. For ease of reference, the comparable notice that is used with SNORT (contained in the 'README' file) is as follows: "This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 2 as published by the Free Software Foundation. You may not use, modify or distribute this program under any other version of the GNU General Public License." If you have any questions about this statement, please feel free to email snort-info@snort.org. ***************************************************************************** GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy 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 Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. snort-2.9.20/snort.80000444000175000017500000007447514230012553012341 0ustar apoapo.\" Process this file with .\" groff -man -Tascii snort.8 .\" .\" $Id$ .TH SNORT 8 "December 2011" .SH NAME Snort \- open source network intrusion detection system .SH SYNOPSIS .B snort [-bCdDeEfHIMNOpqQsTUvVwWxXy?] [-A .I alert-mode .B ] [-B .I address-conversion-mask .B ] [-c .I rules-file .B ] [-F .I bpf-file .B ] [-g .I group-name .B ] [-G .I id .B ] [-h .I home-net .B ] [-i .I interface .B ] [-k .I checksum-mode .B ] [-K .I logging-mode .B ] [-l .I log-dir .B ] [-L .I bin-log-file .B ] [-m .I umask .B ] [-n .I packet-count .B ] [-P .I snap-length .B ] [-r .I tcpdump-file .B ] [-R .I name .B ] [-S .I variable=value .B ] [-t .I chroot_directory .B ] [-u .I user-name .B ] [-Z .I pathname .B ] [--logid .I id .B ] [--perfmon-file .I pathname .B ] [--pid-path .I pathname .B ] [--snaplen .I snap-length .B ] [--help .B ] [--version .B ] [--dynamic-engine-lib .I file .B ] [--dynamic-engine-lib-dir .I directory .B ] [--dynamic-detection-lib .I file .B ] [--dynamic-detection-lib-dir .I directory .B ] [--dump-dynamic-rules .I directory .B ] [--dynamic-preprocessor-lib .I file .B ] [--dynamic-preprocessor-lib-dir .I directory .B ] [--dynamic-output-lib .I file .B ] [--dynamic-output-lib-dir .I directory .B ] [--alert-before-pass .B ] [--treat-drop-as-alert .B ] [--treat-drop-as-ignore .B ] [--process-all-events .B ] [--enable-inline-test .B ] [--create-pidfile .B ] [--nolock-pidfile .B ] [--no-interface-pidfile .B ] [--disable-attribute-reload-thread .B ] [--pcap-single= .I tcpdump-file .B ] [--pcap-filter= .I filter .B ] [--pcap-list= .I list .B ] [--pcap-dir= .I directory .B ] [--pcap-file= .I file .B ] [--pcap-no-filter .B ] [--pcap-reset .B ] [--pcap-reload .B ] [--pcap-show .B ] [--exit-check .I count .B ] [--conf-error-out .B ] [--enable-mpls-multicast .B ] [--enable-mpls-overlapping-ip .B ] [--max-mpls-labelchain-len .B ] [--mpls-payload-type .B ] [--require-rule-sid .B ] [--daq .I type .B ] [--daq-mode .I mode .B ] [--daq-var .I name=value .B ] [--daq-dir .I dir .B ] [--daq-list .I [dir] .B ] [--dirty-pig .B ] [--cs-dir .I dir .B ] [--ha-peer .B ] [--ha-out .I file .B ] [--ha-in .I file .B ] .I expression .SH DESCRIPTION .B Snort is an open source network intrusion detection system, capable of performing real-time traffic analysis and packet logging on IP networks. It can perform protocol analysis, content searching/matching and can be used to detect a variety of attacks and probes, such as buffer overflows, stealth port scans, CGI attacks, SMB probes, OS fingerprinting attempts, and much more. Snort uses a flexible rules language to describe traffic that it should collect or pass, as well as a detection engine that utilizes a modular plugin architecture. Snort also has a modular real-time alerting capability, incorporating alerting and logging plugins for syslog, a ASCII text files, UNIX sockets or XML. .PP Snort has three primary uses. It can be used as a straight packet sniffer like .BR tcpdump (1), a packet logger (useful for network traffic debugging, etc), or as a full blown network intrusion detection system. .PP Snort logs packets in .BR tcpdump (1) binary format or in Snort's decoded ASCII format to a hierarchy of logging directories that are named based on the IP address of the "foreign" host. .SH OPTIONS .IP "-A alert-mode" Alert using the specified .I alert-mode. Valid alert modes include .B fast, full, none, and .B unsock. .B Fast writes alerts to the default "alert" file in a single-line, syslog style alert message. .B Full writes the alert to the "alert" file with the full decoded header as well as the alert message. .B None turns off alerting. .B Unsock is an experimental mode that sends the alert information out over a UNIX socket to another process that attaches to that socket. .IP -b Log packets in a .BR tcpdump (1) formatted file. All packets are logged in their native binary state to a tcpdump formatted log file named with the snort start timestamp and "snort.log". This option results in much faster operation of the program since it doesn't have to spend time in the packet binary->text converters. Snort can keep up pretty well with 100Mbps networks in '-b' mode. To choose an alternate name for the binary log file, use the '-L' switch. .IP "-B address-conversion-mask" Convert all IP addresses in .I home-net to addresses specified by .I address-conversion-mask. Used to obfuscate IP addresses within binary logs. Specify .I home-net with the '-h' switch. Note this is .B not the same as $HOME_NET. .IP "-c config-file" Use the rules located in file .I config-file. .IP -C Print the character data from the packet payload only (no hex). .IP -d Dump the application layer data when displaying packets in verbose or packet logging mode. .IP -D Run Snort in daemon mode. Alerts are sent to /var/log/snort/alert unless otherwise specified. .IP -e Display/log the link layer packet headers. .IP -E .B *WIN32 ONLY* Log alerts to the Windows Event Log. .IP -f Activate PCAP line buffering .IP "-F bpf-file" Read BPF filters from .I bpf-file. This is handy for people running Snort as a SHADOW replacement or with a love Of super complex BPF filters. See the "expressions" section of this man page for more info on writing BPF filters. .IP "-g group" Change the group/GID Snort runs under to .I group after initialization. This switch allows Snort to drop root privileges after it's initialization phase has completed as a security measure. .IP "-G id" Use id as a base event ID when logging events. .IP "-h home-net" Set the "home network" to .I home-net. The format of this address variable is a network prefix plus a CIDR block, such as 192.168.1.0/24. Once this variable is set, all decoded packet logging will be done relative to the home network address space. This is useful because of the way that Snort formats its ASCII log data. With this value set to the local network, all decoded output will be logged into decode directories with the address of the foreign computer as the directory name, which is very useful during traffic analysis. This option does not change "$HOME_NET" in IDS mode. .IP "-H" Force hash tables to be deterministic instead of using a random number generator for the seed & scale. Useful for testing and generating repeatable results with the same traffic. .IP "-i interface" Sniff packets on .I interface. .IP "-I" Print out the receiving interface name in alerts. .IP "-k checksum-mode" Tune the internal checksum verification functionality with .I alert-mode. Valid checksum modes include .B all, noip, notcp, noudp, noicmp, and .B none. .B All activates checksum verification for all supported protocols. .B Noip turns off IP checksum verification, which is handy if the gateway router is already dropping packets that fail their IP checksum checks. .B Notcp turns off TCP checksum verification, all other checksum modes are .B on. .B noudp turns off UDP checksum verification. .B Noicmp turns off ICMP checksum verification. .B None turns off the entire checksum verification subsystem. .IP "-K logging-mode" Select a packet logging mode. The default is pcap. .I logging-mode. Valid logging modes include .B pcap, ascii, and .B none. .B Pcap logs packets through the pcap library into pcap (tcpdump) format. .B Ascii logs packets in the old "directories and files" format with packet printouts in each file. .B None Turns off packet logging. .IP "-l log-dir" Set the output logging directory to .I log-dir. All plain text alerts and packet logs go into this directory. If this option is not specified, the default logging directory is set to /var/log/snort. .IP "-L binary-log-file" Set the filename of the binary log file to .I binary-log-file. If this switch is not used, the default name is a timestamp for the time that the file is created plus "snort.log". .IP "-m umask" Set the file mode creation mask to .I umask .IP "-M" Log console messages to syslog when not running daemon mode. Using both -D and -M will send all messages to syslog including e.g. SIGUSR1 dump packet stats. This switch has no impact on logging of alerts. .IP "-n packet-count" Process .I packet-count packets and exit. .IP -N Turn off packet logging. The program still generates alerts normally. .IP -O Obfuscate the IP addresses when in ASCII packet dump mode. This switch changes the IP addresses that get printed to the screen/log file to "xxx.xxx.xxx.xxx". If the homenet address switch is set (-h), only addresses on the homenet will be obfuscated while non- homenet IPs will be left visible. Perfect for posting to your favorite security mailing list! .IP -p Turn off promiscuous mode sniffing. .IP "-P snap-length" Set the packet snaplen to .I snap-length. By default, this is set to 1514. .IP "-q" Quiet operation. Don't display banner and initialization information. In daemon mode, banner and initialization information is not logged to syslog. .IP "-Q" Enable inline mode operation. .IP "-r tcpdump-file" Read the tcpdump-formatted file .I tcpdump-file. This will cause Snort to read and process the file fed to it. This is useful if, for instance, you've got a bunch of SHADOW files that you want to process for content, or even if you've got a bunch of reassembled packet fragments which have been written into a tcpdump formatted file. .IP "-R name" Use name as a suffix to the snort pidfile. .IP -s Send alert messages to syslog. On linux boxen, they will appear in /var/log/secure, /var/log/messages on many other platforms. .IP "-S variable=value" Set variable name "variable" to value "value". This is useful for setting the value of a defined variable name in a Snort rules file to a command line specified value. For instance, if you define a HOME_NET variable name inside of a Snort rules file, you can set this value from it's predefined value at the command line. .IP "-t chroot" Changes Snort's root directory to .I chroot after initialization. Please note that all log/alert filenames are relative to the chroot directory if chroot is used. .IP -T Snort will start up in self-test mode, checking all the supplied command line switches and rules files that are handed to it and indicating that everything is ready to proceed. This is a good switch to use if daemon mode is going to be used, it verifies that the Snort configuration that is about to be used is valid and won't fail at run time. Note, Snort looks for either /etc/snort.conf or ./snort.conf. If your config lives elsewhere, use the -c option to specify a valid .I config-file. .IP "-u user" Change the user/UID Snort runs under to .I user after initialization. .IP -U Changes the timestamp in all logs to be in UTC .IP -v Be verbose. Prints packets out to the console. There is one big problem with verbose mode: it's slow. If you are doing IDS work with Snort, .B don't use the '-v' switch, you .B WILL drop packets. .IP -V Show the version number and exit. .IP "-w" Show management frames if running on an 802.11 (wireless) network. .IP "-W" .B *WIN32 ONLY* Enumerate the network interfaces available. .IP "-x" Exit if Snort configuration problems occur such as duplicate gid/sid or flowbits without Stream5. .IP "-X" Dump the raw packet data starting at the link layer. This switch overrides the '-d' switch. .IP "-y" Include the year in alert and log files .IP "-Z pathname" Set the perfmonitor preprocessor path/filename to pathname. .IP -? Show the program usage statement and exit. .IP "--logid id" Same as -G. .IP "--perfmon-file pathname" Same as -Z. .IP "--pid-path directory" Specify the directory for the Snort PID file. .IP "--snaplen snap-length" Same as -P. .IP "--help" Same as -? .IP "--version" Same as -V .IP "--dynamic-engine-lib file" Load a dynamic detection engine shared library specified by file. .IP "--dynamic-engine-lib-dir directory" Load all dynamic detection engine shared libraries specified from directory. .IP "--dynamic-detection-lib file" Load a dynamic detection rules shared library specified by file. .IP "--dynamic-detection-lib-dir directory" Load all dynamic detection rules shared libraries specified from directory. .IP "--dump-dynamic-rules directory" Create stub rule files from all loaded dynamic detection rules libraries. Files will be created in directory. This is required to be done prior to running snort using those detection rules and the generated rules files must be included in snort.conf. .IP "--dynamic-preprocessor-lib file" Load a dynamic preprocessor shared library specified by file. .IP "--dynamic-preprocessor-lib-dir directory" Load all dynamic preprocessor shared libraries specified from directory. .IP "--alert-before-pass" Process alert, drop, sdrop, or reject before pass. Default is pass before alert, drop, etc. .IP "--treat-drop-as-alert" Converts drop, sdrop, and reject rules into alert rules during startup. .IP "--treat-drop-as-ignore" Use drop, sdrop, and reject rules to ignore session traffic when not inline. .IP "--process-all-events" Process all triggered events in group order, per Rule Ordering configuration. Default stops after first group. .IP "--enable-inline-test" Enable Inline-Test Mode Operation. .IP "--pid-path directory" Specify the path for Snort's PID file. .IP "--create-pidfile" Create PID file, even when not in Daemon mode. .IP "--nolock-pidfile" Do not try to lock Snort PID file. .IP "--no-interface-pidfile" Do not include the interface name in Snort PID file .IP "--pcap-single=\fItcpdump-file\fP" Same as -r. Added for completeness. .IP "--pcap-filter=\fIfilter\fP" Shell style filter to apply when getting pcaps from file or directory. This filter will apply to any --pcap-file or --pcap-dir arguments following. Use --pcap-no-filter to delete filter for following --pcap-file or --pcap-dir arguments or specify --pcap-filter again to forget previous filter and to apply to following --pcap-file or --pcap-dir arguments. .IP "--pcap-list=\fI""list""\fP" A space separated list of pcaps to read. .IP "--pcap-dir=\fIdirectory\fP" A directory to recurse to look for pcaps. Sorted in ascii order. .IP "--pcap-file=\fIfile\fP" File that contains a list of pcaps to read. Can specify path to pcap or directory to recurse to get pcaps. .IP "--pcap-no-filter" Reset to use no filter when getting pcaps from file or directory. .IP "--pcap-reset" If reading multiple pcaps, reset snort to post-configuration state before reading next pcap. The default, i.e. without this option, is not to reset state. .IP "--pcap-show" Print a line saying what pcap is currently being read. .IP "--exit-check=\fIcount\fP" Signal termination after callbacks from DAQ_Acquire(), showing the time it takes from signaling until DAQ_Stop() is called. .IP "--conf-error-out" Same as -x. .IP "--require-rule-sid" Require an SID for every rule to be correctly threshold all rules. .IP "--daq " Select packet acquisition module (default is pcap). .IP "--daq-mode " Select the DAQ operating mode. .IP "--daq-var " Specify extra DAQ configuration variable. .IP "--daq-dir " Tell Snort where to find desired DAQ. .IP "--daq-list []" List packet acquisition modules available in dir. .IP "--cs-dir " Tell Snort to use control socket and create the socket in dir. .IP "\fI expression\fP" .RS selects which packets will be dumped. If no \fIexpression\fP is given, all packets on the net will be dumped. Otherwise, only packets for which \fIexpression\fP is `true' will be dumped. .LP The \fIexpression\fP consists of one or more .I primitives. Primitives usually consist of an .I id (name or number) preceded by one or more qualifiers. There are three different kinds of qualifier: .IP \fItype\fP qualifiers say what kind of thing the id name or number refers to. Possible types are .BR host , .B net and .BR port . E.g., `host foo', `net 128.3', `port 20'. If there is no type qualifier, .B host is assumed. .IP \fIdir\fP qualifiers specify a particular transfer direction to and/or from .I id. Possible directions are .BR src , .BR dst , .B "src or dst" and .B "src and" .BR dst . E.g., `src foo', `dst net 128.3', `src or dst port ftp-data'. If there is no dir qualifier, .B "src or dst" is assumed. For `null' link layers (i.e. point to point protocols such as slip) the .B inbound and .B outbound qualifiers can be used to specify a desired direction. .IP \fIproto\fP qualifiers restrict the match to a particular protocol. Possible protos are: .BR ether , .BR fddi , .BR ip , .BR arp , .BR rarp , .BR decnet , .BR lat , .BR sca , .BR moprc , .BR mopdl , .B tcp and .BR udp . E.g., `ether src foo', `arp net 128.3', `tcp port 21'. If there is no proto qualifier, all protocols consistent with the type are assumed. E.g., `src foo' means `(ip or arp or rarp) src foo' (except the latter is not legal syntax), `net bar' means `(ip or arp or rarp) net bar' and `port 53' means `(tcp or udp) port 53'. .LP [`fddi' is actually an alias for `ether'; the parser treats them identically as meaning ``the data link level used on the specified network interface.'' FDDI headers contain Ethernet-like source and destination addresses, and often contain Ethernet-like packet types, so you can filter on these FDDI fields just as with the analogous Ethernet fields. FDDI headers also contain other fields, but you cannot name them explicitly in a filter expression.] .LP In addition to the above, there are some special `primitive' keywords that don't follow the pattern: .BR gateway , .BR broadcast , .BR less , .B greater and arithmetic expressions. All of these are described below. .LP More complex filter expressions are built up by using the words .BR and , .B or and .B not to combine primitives. E.g., `host foo and not port ftp and not port ftp-data'. To save typing, identical qualifier lists can be omitted. E.g., `tcp dst port ftp or ftp-data or domain' is exactly the same as `tcp dst port ftp or tcp dst port ftp-data or tcp dst port domain'. .LP Allowable primitives are: .IP "\fBdst host \fIhost\fR" True if the IP destination field of the packet is \fIhost\fP, which may be either an address or a name. .IP "\fBsrc host \fIhost\fR" True if the IP source field of the packet is \fIhost\fP. .IP "\fBhost \fIhost\fP" True if either the IP source or destination of the packet is \fIhost\fP. Any of the above host expressions can be prepended with the keywords, \fBip\fP, \fBarp\fP, or \fBrarp\fP as in: .in +.5i .nf \fBip host \fIhost\fR .fi .in -.5i which is equivalent to: .in +.5i .nf \fBether proto \fI\\ip\fB and host \fIhost\fR .fi .in -.5i If \fIhost\fR is a name with multiple IP addresses, each address will be checked for a match. .IP "\fBether dst \fIehost\fP" True if the ethernet destination address is \fIehost\fP. \fIEhost\fP may be either a name from /etc/ethers or a number (see .IR ethers (3N) for numeric format). .IP "\fBether src \fIehost\fP" True if the ethernet source address is \fIehost\fP. .IP "\fBether host \fIehost\fP" True if either the ethernet source or destination address is \fIehost\fP. .IP "\fBgateway\fP \fIhost\fP" True if the packet used \fIhost\fP as a gateway. I.e., the ethernet source or destination address was \fIhost\fP but neither the IP source nor the IP destination was \fIhost\fP. \fIHost\fP must be a name and must be found in both /etc/hosts and /etc/ethers. (An equivalent expression is .in +.5i .nf \fBether host \fIehost \fBand not host \fIhost\fR .fi .in -.5i which can be used with either names or numbers for \fIhost / ehost\fP.) .IP "\fBdst net \fInet\fR" True if the IP destination address of the packet has a network number of \fInet\fP. \fINet\fP may be either a name from /etc/networks or a network number (see \fInetworks(4)\fP for details). .IP "\fBsrc net \fInet\fR" True if the IP source address of the packet has a network number of \fInet\fP. .IP "\fBnet \fInet\fR" True if either the IP source or destination address of the packet has a network number of \fInet\fP. .IP "\fBnet \fInet\fR \fBmask \fImask\fR" True if the IP address matches \fInet\fR with the specific netmask. May be qualified with \fBsrc\fR or \fBdst\fR. .IP "\fBnet \fInet\fR/\fIlen\fR" True if the IP address matches \fInet\fR a netmask \fIlen\fR bits wide. May be qualified with \fBsrc\fR or \fBdst\fR. .IP "\fBdst port \fIport\fR" True if the packet is ip/tcp or ip/udp and has a destination port value of \fIport\fP. The \fIport\fP can be a number or a name used in /etc/services (see .IR tcp (4P) and .IR udp (4P)). If a name is used, both the port number and protocol are checked. If a number or ambiguous name is used, only the port number is checked (e.g., \fBdst port 513\fR will print both tcp/login traffic and udp/who traffic, and \fBport domain\fR will print both tcp/domain and udp/domain traffic). .IP "\fBsrc port \fIport\fR" True if the packet has a source port value of \fIport\fP. .IP "\fBport \fIport\fR" True if either the source or destination port of the packet is \fIport\fP. Any of the above port expressions can be prepended with the keywords, \fBtcp\fP or \fBudp\fP, as in: .in +.5i .nf \fBtcp src port \fIport\fR .fi .in -.5i which matches only tcp packets whose source port is \fIport\fP. .IP "\fBless \fIlength\fR" True if the packet has a length less than or equal to \fIlength\fP. This is equivalent to: .in +.5i .nf \fBlen <= \fIlength\fP. .fi .in -.5i .IP "\fBgreater \fIlength\fR" True if the packet has a length greater than or equal to \fIlength\fP. This is equivalent to: .in +.5i .nf \fBlen >= \fIlength\fP. .fi .in -.5i .IP "\fBip proto \fIprotocol\fR" True if the packet is an ip packet (see .IR ip (4P)) of protocol type \fIprotocol\fP. \fIProtocol\fP can be a number or one of the names \fIicmp\fP, \fIigrp\fP, \fIudp\fP, \fInd\fP, or \fItcp\fP. Note that the identifiers \fItcp\fP, \fIudp\fP, and \fIicmp\fP are also keywords and must be escaped via backslash (\\), which is \\\\ in the C-shell. .IP "\fBether broadcast\fR" True if the packet is an ethernet broadcast packet. The \fIether\fP keyword is optional. .IP "\fBip broadcast\fR" True if the packet is an IP broadcast packet. It checks for both the all-zeroes and all-ones broadcast conventions, and looks up the local subnet mask. .IP "\fBether multicast\fR" True if the packet is an ethernet multicast packet. The \fIether\fP keyword is optional. This is shorthand for `\fBether[0] & 1 != 0\fP'. .IP "\fBip multicast\fR" True if the packet is an IP multicast packet. .IP "\fBether proto \fIprotocol\fR" True if the packet is of ether type \fIprotocol\fR. \fIProtocol\fP can be a number or a name like \fIip\fP, \fIarp\fP, or \fIrarp\fP. Note these identifiers are also keywords and must be escaped via backslash (\\). [In the case of FDDI (e.g., `\fBfddi protocol arp\fR'), the protocol identification comes from the 802.2 Logical Link Control (LLC) header, which is usually layered on top of the FDDI header. \fITcpdump\fP assumes, when filtering on the protocol identifier, that all FDDI packets include an LLC header, and that the LLC header is in so-called SNAP format.] .IP "\fBdecnet src \fIhost\fR" True if the DECNET source address is .IR host , which may be an address of the form ``10.123'', or a DECNET host name. [DECNET host name support is only available on Ultrix systems that are configured to run DECNET.] .IP "\fBdecnet dst \fIhost\fR" True if the DECNET destination address is .IR host . .IP "\fBdecnet host \fIhost\fR" True if either the DECNET source or destination address is .IR host . .IP "\fBip\fR, \fBarp\fR, \fBrarp\fR, \fBdecnet\fR" Abbreviations for: .in +.5i .nf \fBether proto \fIp\fR .fi .in -.5i where \fIp\fR is one of the above protocols. .IP "\fBlat\fR, \fBmoprc\fR, \fBmopdl\fR" Abbreviations for: .in +.5i .nf \fBether proto \fIp\fR .fi .in -.5i where \fIp\fR is one of the above protocols. Note that \fISnort\fP does not currently know how to parse these protocols. .IP "\fBtcp\fR, \fBudp\fR, \fBicmp\fR" Abbreviations for: .in +.5i .nf \fBip proto \fIp\fR .fi .in -.5i where \fIp\fR is one of the above protocols. .IP "\fIexpr relop expr\fR" True if the relation holds, where \fIrelop\fR is one of >, <, >=, <=, =, !=, and \fIexpr\fR is an arithmetic expression composed of integer constants (expressed in standard C syntax), the normal binary operators [+, -, *, /, &, |], a length operator, and special packet data accessors. To access data inside the packet, use the following syntax: .in +.5i .nf \fIproto\fB [ \fIexpr\fB : \fIsize\fB ]\fR .fi .in -.5i \fIProto\fR is one of \fBether, fddi, ip, arp, rarp, tcp, udp, \fRor \fBicmp\fR, and indicates the protocol layer for the index operation. The byte offset, relative to the indicated protocol layer, is given by \fIexpr\fR. \fISize\fR is optional and indicates the number of bytes in the field of interest; it can be either one, two, or four, and defaults to one. The length operator, indicated by the keyword \fBlen\fP, gives the length of the packet. For example, `\fBether[0] & 1 != 0\fP' catches all multicast traffic. The expression `\fBip[0] & 0xf != 5\fP' catches all IP packets with options. The expression `\fBip[6:2] & 0x1fff = 0\fP' catches only unfragmented datagrams and frag zero of fragmented datagrams. This check is implicitly applied to the \fBtcp\fP and \fBudp\fP index operations. For instance, \fBtcp[0]\fP always means the first byte of the TCP \fIheader\fP, and never means the first byte of an intervening fragment. .LP Primitives may be combined using: .IP A parenthesized group of primitives and operators (parentheses are special to the Shell and must be escaped). .IP Negation (`\fB!\fP' or `\fBnot\fP'). .IP Concatenation (`\fB&&\fP' or `\fBand\fP'). .IP Alternation (`\fB||\fP' or `\fBor\fP'). .LP Negation has highest precedence. Alternation and concatenation have equal precedence and associate left to right. Note that explicit \fBand\fR tokens, not juxtaposition, are now required for concatenation. .LP If an identifier is given without a keyword, the most recent keyword is assumed. For example, .in +.5i .nf \fBnot host vs and ace\fR .fi .in -.5i is short for .in +.5i .nf \fBnot host vs and host ace\fR .fi .in -.5i which should not be confused with .in +.5i .nf \fBnot ( host vs or ace )\fR .fi .in -.5i .LP Expression arguments can be passed to Snort as either a single argument or as multiple arguments, whichever is more convenient. Generally, if the expression contains Shell metacharacters, it is easier to pass it as a single, quoted argument. Multiple arguments are concatenated with spaces before being parsed. .SH READING PCAPS Instead of having Snort listen on an interface, you can give it a packet capture to read. Snort will read and analyze the packets as if they came off the wire. This can be useful for testing and debugging Snort. \fBRead a single pcap\fR .RS 5 .PD 0 $ snort -r foo.pcap .PP $ snort --pcap-single=foo.pcap .RE 0 \fBRead pcaps from a file\fR .RS 5 $ cat foo.txt .PP foo1.pcap .PP foo2.pcap .PP /home/foo/pcaps $ snort --pcap-file=foo.txt This will read foo1.pcap, foo2.pcap and all files under /home/foo/pcaps. Note that Snort will not try to determine whether the files under that directory are really pcap files or not. .RE 0 \fBRead pcaps from a command line list\fR .RS 5 $ snort --pcap-list="foo1.pcap foo2.pcap foo3.pcap" This will read foo1.pcap, foo2.pcap and foo3.pcap. .RE 0 \fBRead pcaps under a directory\fR .RS 5 $ snort --pcap-dir="/home/foo/pcaps" This will include all of the files under /home/foo/pcaps. .RE 0 \fBUsing filters\fR .RS 5 $ cat foo.txt .PP foo1.pcap .PP foo2.pcap .PP /home/foo/pcaps $ snort --pcap-filter="*.pcap" --pcap-file=foo.txt .PP $ snort --pcap-filter="*.pcap" --pcap-dir=/home/foo/pcaps The above will only include files that match the shell pattern "*.pcap", in other words, any file ending in ".pcap". $ snort --pcap-filter="*.pcap --pcap-file=foo.txt \\ .PP > --pcap-filter="*.cap" --pcap-dir=/home/foo/pcaps In the above, the first filter "*.pcap" will only be applied to the pcaps in the file "foo.txt" (and any directories that are recursed in that file). The addition of the second filter "*.cap" will cause the first filter to be forgotten and then applied to the directory /home/foo/pcaps, so only files ending in ".cap" will be included from that directory. $ snort --pcap-filter="*.pcap --pcap-file=foo.txt \\ .PP > --pcap-no-filter --pcap-dir=/home/foo/pcaps In this example, the first filter will be applied to foo.txt, then no filter will be applied to the files found under /home/foo/pcaps, so all files found under /home/foo/pcaps will be included. $ snort --pcap-filter="*.pcap --pcap-file=foo.txt \\ .PP > --pcap-no-filter --pcap-dir=/home/foo/pcaps \\ .PP > --pcap-filter="*.cap" --pcap-dir=/home/foo/pcaps2 In this example, the first filter will be applied to foo.txt, then no filter will be applied to the files found under /home/foo/pcaps, so all files found under /home/foo/pcaps will be included, then the filter "*.cap" will be applied to files found under /home/foo/pcaps2. .RE 0 \fBResetting state\fR .RS 5 $ snort --pcap-dir=/home/foo/pcaps --pcap-reset The above example will read all of the files under /home/foo/pcaps, but after each pcap is read, Snort will be reset to a post-configuration state, meaning all buffers will be flushed, statistics reset, etc. For each pcap, it will be like Snort is seeing traffic for the first time. .RE 0 \fBPrinting the pcap\fR .RS 5 $ snort --pcap-dir=/home/foo/pcaps --pcap-show The above example will read all of the files under /home/foo/pcaps and will print a line indicating which pcap is currently being read. .RE 0 .PD .SH RULES Snort uses a simple but flexible rules language to describe network packet signatures and associate them with actions. The current rules document can be found at http://www.snort.org/snort-rules. .SH NOTES The following signals have the specified effect when sent to the daemon process using the \fBkill(1)\fR command: .PP .IP SIGHUP Causes the daemon to close all opened files and restart. Please \fBnote\fR that this will only work if the \fBfull\fR pathname is used to invoke snort in daemon mode, otherwise snort will just exit with an error message being sent to \fBsyslogd(8)\fR. .PP .IP SIGUSR1 Causes the program to dump its current packet statistical information to the console or \fBsyslogd(8)\fR if in daemon mode. .PP .IP SIGUSR2 Causes the program to rotate Perfmonitor statistical information to the console or \fBsyslogd(8)\fR if in daemon mode. .PP .IP SIGURG Causes the program to reload attribute table. .PP .IP SIGCHLD Used internally. .PP Please refer to manual for more details. Any other signal might cause the daemon to close all opened files and exit. .SH HISTORY .B Snort has been freely available under the GPL license since 1998. .SH DIAGNOSTICS .B Snort returns a 0 on a successful exit, 1 if it exits on an error. .SH BUGS After consulting the BUGS file included with the source distribution, send bug reports to snort-devel@lists.snort.org .SH AUTHOR Martin Roesch .SH "SEE ALSO" .BR tcpdump (1), .BR pcap (3) snort-2.9.20/m4/0000755000175000017500000000000014242725721011417 5ustar apoaposnort-2.9.20/m4/Makefile.in0000644000175000017500000003034214242725545013472 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = m4 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = depcomp = am__maybe_remake_depfiles = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies EXTRA_DIST = Makefile.am all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign m4/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign m4/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/m4/Makefile.am0000444000175000017500000000011314230012553013431 0ustar apoapo## $Id$ AUTOMAKE_OPTIONS=foreign no-dependencies EXTRA_DIST = Makefile.am snort-2.9.20/verstuff.pl0000555000175000017500000000335214230012554013272 0ustar apoapo#!/usr/bin/perl -w # $Id$ # Author: Chris Green # Purpose: make sure snort versions stay in sync # Created: Tue Jul 22 17:21:42 EDT 2003 use strict; my $version = "Unknown!"; if($#ARGV < 0) { die "bad args found!\n"; } my $prefix = $ARGV[0]; open(CONFIGURE,"$prefix/configure.in") or die "Can't open configure.in!\n"; while() { if($_ =~ m/^AM_INIT_AUTOMAKE\(snort,/) { chomp; $version = $_; $version =~ s/^AM_INIT_AUTOMAKE\(snort,([^\)]+)\)/$1/; last; } } close(CONFIGURE); # print "version is $version!\n"; open(WIN32CONFIG, "$prefix/src/win32/WIN32-Includes/config.h"); open(WIN32CONFIGNEW, ">$prefix/src/win32/WIN32-Includes/config.h.new"); while() { if($_ =~ m/^\#define VERSION "[^"]+"/) { $_ =~ s/^(\#define VERSION ")[^"]+(.*$)/${1}${version}${2}/; } print WIN32CONFIGNEW $_; } system("mv -f ${prefix}/src/win32/WIN32-Includes/config.h.new ${prefix}/src/win32/WIN32-Includes/config.h"); open(MANUAL, "<$prefix/doc/snort_manual.tex"); open(MANUALNEW, ">$prefix/doc/snort_manual.tex.new"); while() { s/(Snort\\texttrademark Users Manual\\\\ ).*?(\})/$1 $version $2/; print MANUALNEW $_; } system("mv -f $prefix/doc/snort_manual.tex.new $prefix/doc/snort_manual.tex"); open(MANUAL, "<$prefix/rpm/snort.spec"); open(MANUALNEW, ">$prefix/rpm/snort.spec.new"); while() { s/^Version: .*$/Version: $version/; print MANUALNEW $_; } system("mv -f $prefix/rpm/snort.spec.new $prefix/rpm/snort.spec"); open (CONF, "<$prefix/etc/snort.conf"); open (CONFNEW,">$prefix/etc/snort.conf.new"); while () { s/Snort .* Ruleset/Snort $version Ruleset/; print CONFNEW $_; } system("mv -f $prefix/etc/snort.conf.new $prefix/etc/snort.conf"); snort-2.9.20/Makefile.in0000644000175000017500000007401614242725545013160 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ $(am__configure_deps) $(am__DIST_COMMON) am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ configure.lineno config.status.lineno mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = snort.pc CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = depcomp = am__maybe_remake_depfiles = SOURCES = DIST_SOURCES = RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } man8dir = $(mandir)/man8 am__installdirs = "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(pkgconfigdir)" NROFF = nroff MANS = $(man_MANS) DATA = $(pkgconfig_DATA) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ cscope distdir distdir-am dist dist-all distcheck am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) \ $(LISP)config.h.in # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags CSCOPE = cscope DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in \ $(srcdir)/snort.pc.in COPYING ChangeLog compile config.guess \ config.sub install-sh ltmain.sh missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) top_distdir = $(distdir) am__remove_distdir = \ if test -d "$(distdir)"; then \ find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ && rm -rf "$(distdir)" \ || { sleep 5 && rm -rf "$(distdir)"; }; \ else :; fi am__post_remove_distdir = $(am__remove_distdir) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" DIST_ARCHIVES = $(distdir).tar.gz GZIP_ENV = --best DIST_TARGETS = dist-gzip distuninstallcheck_listfiles = find . -type f -print am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' distcleancheck_listfiles = find . -type f -print ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies SUBDIRS = src doc etc templates rpm m4 preproc_rules tools EXTRA_DIST = ChangeLog snort.8 LICENSE verstuff.pl RELEASE.NOTES snort.pc.in VERSION man_MANS = snort.8 DISTCLEANFILES = stamp-h.in cflags.out cppflags.out pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = snort.pc all: config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: am--refresh: Makefile @: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ && exit 0; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ echo ' $(SHELL) ./config.status'; \ $(SHELL) ./config.status;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck $(top_srcdir)/configure: $(am__configure_deps) $(am__cd) $(srcdir) && $(AUTOCONF) $(ACLOCAL_M4): $(am__aclocal_m4_deps) $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) $(am__aclocal_m4_deps): config.h: stamp-h1 @test -f $@ || rm -f stamp-h1 @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 cd $(top_builddir) && $(SHELL) ./config.status config.h $(srcdir)/config.h.in: $(am__configure_deps) ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) rm -f stamp-h1 touch $@ distclean-hdr: -rm -f config.h stamp-h1 snort.pc: $(top_builddir)/config.status $(srcdir)/snort.pc.in cd $(top_builddir) && $(SHELL) ./config.status $@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs distclean-libtool: -rm -f libtool config.lt install-man8: $(man_MANS) @$(NORMAL_INSTALL) @list1=''; \ list2='$(man_MANS)'; \ test -n "$(man8dir)" \ && test -n "`echo $$list1$$list2`" \ || exit 0; \ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \ { for i in $$list1; do echo "$$i"; done; \ if test -n "$$list2"; then \ for i in $$list2; do echo "$$i"; done \ | sed -n '/\.8[a-z]*$$/p'; \ fi; \ } | while read p; do \ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; echo "$$p"; \ done | \ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \ sed 'N;N;s,\n, ,g' | { \ list=; while read file base inst; do \ if test "$$base" = "$$inst"; then list="$$list $$file"; else \ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \ fi; \ done; \ for i in $$list; do echo "$$i"; done | $(am__base_list) | \ while read files; do \ test -z "$$files" || { \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \ done; } uninstall-man8: @$(NORMAL_UNINSTALL) @list=''; test -n "$(man8dir)" || exit 0; \ files=`{ for i in $$list; do echo "$$i"; done; \ l2='$(man_MANS)'; for i in $$l2; do echo "$$i"; done | \ sed -n '/\.8[a-z]*$$/p'; \ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir) install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscope: cscope.files test ! -s cscope.files \ || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) clean-cscope: -rm -f cscope.files cscope.files: clean-cscope cscopelist cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -rm -f cscope.out cscope.in.out cscope.po.out cscope.files distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) $(am__remove_distdir) test -d "$(distdir)" || mkdir "$(distdir)" @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook -test -n "$(am__skip_mode_fix)" \ || find "$(distdir)" -type d ! -perm -755 \ -exec chmod u+rwx,go+rx {} \; -o \ ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ || chmod -R a+r "$(distdir)" dist-gzip: distdir tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz $(am__post_remove_distdir) dist-bzip2: distdir tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 $(am__post_remove_distdir) dist-lzip: distdir tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz $(am__post_remove_distdir) dist-xz: distdir tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz $(am__post_remove_distdir) dist-tarZ: distdir @echo WARNING: "Support for distribution archives compressed with" \ "legacy program 'compress' is deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z $(am__post_remove_distdir) dist-shar: distdir @echo WARNING: "Support for shar distribution archives is" \ "deprecated." >&2 @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz $(am__post_remove_distdir) dist-zip: distdir -rm -f $(distdir).zip zip -rq $(distdir).zip $(distdir) $(am__post_remove_distdir) dist dist-all: $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' $(am__post_remove_distdir) # This target untars the dist file and tries a VPATH configuration. Then # it guarantees that the distribution is self-contained by making another # tarfile. distcheck: dist case '$(DIST_ARCHIVES)' in \ *.tar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ *.tar.bz2*) \ bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ *.tar.lz*) \ lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ *.tar.xz*) \ xz -dc $(distdir).tar.xz | $(am__untar) ;;\ *.tar.Z*) \ uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ *.shar.gz*) \ eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ *.zip*) \ unzip $(distdir).zip ;;\ esac chmod -R a-w $(distdir) chmod u+w $(distdir) mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst chmod a-w $(distdir) test -d $(distdir)/_build || exit 0; \ dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ && am__cwd=`pwd` \ && $(am__cd) $(distdir)/_build/sub \ && ../../configure \ $(AM_DISTCHECK_CONFIGURE_FLAGS) \ $(DISTCHECK_CONFIGURE_FLAGS) \ --srcdir=../.. --prefix="$$dc_install_base" \ && $(MAKE) $(AM_MAKEFLAGS) \ && $(MAKE) $(AM_MAKEFLAGS) dvi \ && $(MAKE) $(AM_MAKEFLAGS) check \ && $(MAKE) $(AM_MAKEFLAGS) install \ && $(MAKE) $(AM_MAKEFLAGS) installcheck \ && $(MAKE) $(AM_MAKEFLAGS) uninstall \ && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ distuninstallcheck \ && chmod -R a-w "$$dc_install_base" \ && ({ \ (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ } || { rm -rf "$$dc_destdir"; exit 1; }) \ && rm -rf "$$dc_destdir" \ && $(MAKE) $(AM_MAKEFLAGS) dist \ && rm -rf $(DIST_ARCHIVES) \ && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ && cd "$$am__cwd" \ || exit 1 $(am__post_remove_distdir) @(echo "$(distdir) archives ready for distribution: "; \ list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' distuninstallcheck: @test -n '$(distuninstallcheck_dir)' || { \ echo 'ERROR: trying to run $@ with an empty' \ '$$(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ $(am__cd) '$(distuninstallcheck_dir)' || { \ echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ exit 1; \ }; \ test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left after uninstall:" ; \ if test -n "$(DESTDIR)"; then \ echo " (check DESTDIR support)"; \ fi ; \ $(distuninstallcheck_listfiles) ; \ exit 1; } >&2 distcleancheck: distclean @if test '$(srcdir)' = . ; then \ echo "ERROR: distcleancheck can only run from a VPATH build" ; \ exit 1 ; \ fi @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ || { echo "ERROR: files left in build directory after distclean:" ; \ $(distcleancheck_listfiles) ; \ exit 1; } >&2 check-am: all-am check: check-recursive all-am: Makefile $(MANS) $(DATA) config.h installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(man8dir)" "$(DESTDIR)$(pkgconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -f Makefile distclean-am: clean-am distclean-generic distclean-hdr \ distclean-libtool distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-man install-pkgconfigDATA install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-man8 install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-man uninstall-pkgconfigDATA uninstall-man: uninstall-man8 .MAKE: $(am__recursive_targets) all install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ am--refresh check check-am clean clean-cscope clean-generic \ clean-libtool cscope cscopelist-am ctags ctags-am dist \ dist-all dist-bzip2 dist-gzip dist-hook dist-lzip dist-shar \ dist-tarZ dist-xz dist-zip distcheck distclean \ distclean-generic distclean-hdr distclean-libtool \ distclean-tags distcleancheck distdir distuninstallcheck dvi \ dvi-am html html-am info info-am install install-am \ install-data install-data-am install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-man8 \ install-pdf install-pdf-am install-pkgconfigDATA install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-man uninstall-man8 \ uninstall-pkgconfigDATA .PRECIOUS: Makefile dist-hook: # always ensure that the win32 build is in sync perl $(distdir)/verstuff.pl $(distdir) # work around a horrible doc/Makefile.am rm -rf $(distdir)/doc/signatures/CVS # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/0000755000175000017500000000000014242725721011666 5ustar apoaposnort-2.9.20/src/obfuscation.c0000644000175000017500000013556314241076643014364 0ustar apoapo/****************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ******************************************************************************/ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "obfuscation.h" #include "sf_types.h" #include "snort_debug.h" #include "decode.h" #include "util.h" #include "stream_api.h" #include "snort_bounds.h" #ifdef OBFUSCATION_TEST_STANDALONE # ifndef OBFUSCATION_TEST # define OBFUSCATION_TEST # endif # define TraverseReassembled stream_api->traverse_stream_segments static int TraverseReassembled( Packet *, int (*)(DAQ_PktHdr_t *, uint8_t *, uint8_t *, uint32_t, void *), void * ); #endif /******************************************************************************* * Macros ******************************************************************************/ #define OBFUSCATE_ENTRIES 512 #define OBFUSCATE_MAXLEN_ENTRIES 8 #define OBFUSCATE_SLICE_ENTRIES (OBFUSCATE_ENTRIES - OBFUSCATE_MAXLEN_ENTRIES) /******************************************************************************* * Data structures ******************************************************************************/ typedef struct _ObfuscationEntry { Packet *p; ob_size_t offset; ob_size_t length; ob_char_t ob_char; uint32_t seq; } ObfuscationEntry; typedef struct _ObfuscationStruct { int num_entries; int num_maxlen_entries; int sorted; ObfuscationEntry entries[OBFUSCATE_ENTRIES]; ObfuscationEntry *sort_entries[OBFUSCATE_ENTRIES]; ObfuscationEntry *maxlen_entries[OBFUSCATE_MAXLEN_ENTRIES]; } ObfuscationStruct; typedef struct _ObfuscationCallbackData { const Packet *packet; ObfuscationCallback user_callback; void *user_data; int entry_index; ob_size_t total_offset; } ObfuscationCallbackData; typedef struct _ObfuscationStreamCallbackData { ObfuscationCallbackData *data; uint32_t next_seq; int last_entry_index; } ObfuscationStreamCallbackData; typedef struct _ObfuscatedPayload { uint8_t **payload; ob_size_t *payload_len; ob_size_t payload_size; } ObfuscatedPayload; /******************************************************************************* * Globals ******************************************************************************/ static ObfuscationStruct ob_struct; /******************************************************************************* * Private function prototypes ******************************************************************************/ static inline int NumObfuscateMaxLenEntries(void); static inline int NumObfuscateSliceEntries(void); static inline ObRet ObfuscationEntryOverflow(ob_size_t); static inline int PayloadObfuscationRequired(Packet *); static inline void SetObfuscationEntry(ObfuscationEntry *, Packet *, ob_size_t, ob_size_t, ob_char_t, uint32_t); static inline void SortObfuscationEntries(void); static inline void SetObfuscationCallbackData( ObfuscationCallbackData *, Packet *, ObfuscationCallback, void *); static inline void SetObfuscationStreamCallbackData( ObfuscationStreamCallbackData *, ObfuscationCallbackData *, Packet *, ObfuscationCallback, void *); static ObRet AddObfuscationEntry(Packet *, ob_size_t, ob_size_t, ob_char_t); static int ObfuscationEntrySort(const void *, const void *); static ObRet TraverseObfuscationList(ObfuscationCallbackData *, const DAQ_PktHdr_t *, const uint8_t *, ob_size_t, uint32_t); static int ObfuscateStreamSegmentsCallback(DAQ_PktHdr_t *, uint8_t *, uint8_t *, uint32_t, void *); static ObRet GetObfuscatedPayloadCallback(const DAQ_PktHdr_t *, const uint8_t *, ob_size_t, ob_char_t, void *); static void PrintObfuscationEntry(const ObfuscationEntry *, int); /******************************************************************************* * API prototypes ******************************************************************************/ static void OB_API_ResetObfuscationEntries(void); static ObRet OB_API_AddObfuscationEntry(Packet *, ob_size_t, ob_size_t, ob_char_t); static int OB_API_PayloadObfuscationRequired(Packet *); static ObRet OB_API_ObfuscatePacket(Packet *, ObfuscationCallback, void *); static ObRet OB_API_ObfuscatePacketStreamSegments(Packet *, ObfuscationCallback, void *); static ObRet OB_API_GetObfuscatedPayload(Packet *, uint8_t **, ob_size_t *); static void OB_API_PrintObfuscationEntries(int); /* API accessor */ ObfuscationApi obfuscationApi = { OB_API_ResetObfuscationEntries, // resetObfuscationEntries OB_API_AddObfuscationEntry, // addObfuscationEntry OB_API_PayloadObfuscationRequired, // payloadObfuscationRequired OB_API_ObfuscatePacket, // obfuscatePacket OB_API_ObfuscatePacketStreamSegments, // obfuscatePacketStreamSegments OB_API_GetObfuscatedPayload, // getObfuscatedPayload OB_API_PrintObfuscationEntries // printObfuscationEntries }; ObfuscationApi *obApi = &obfuscationApi; static inline uint32_t get_pkt_seq_num(const Packet* p) { uint32_t pkt_seq = 0; if (p->tcph) pkt_seq = ntohl(p->tcph->th_seq); return pkt_seq; } static inline int32_t get_length_from_seq(uint32_t start, uint32_t end) { uint32_t length = end - start; if (length > UINT16_MAX) return (-1); else return length; } /******************************************************************************* * API Function definitions ******************************************************************************/ // resetObfuscationEntries void OB_API_ResetObfuscationEntries(void) { ob_struct.num_entries = 0; ob_struct.num_maxlen_entries = 0; ob_struct.sorted = 0; } // addObfuscationEntry static ObRet OB_API_AddObfuscationEntry(Packet *p, ob_size_t offset, ob_size_t length, ob_char_t ob_char) { if (p == NULL) return OB_RET_ERROR; p->packet_flags |= PKT_PAYLOAD_OBFUSCATE; return AddObfuscationEntry(p, offset, length, ob_char); } // payloadObfuscationRequired static int OB_API_PayloadObfuscationRequired(Packet *p) { return PayloadObfuscationRequired(p); } // obfuscatePacket static ObRet OB_API_ObfuscatePacket(Packet *p, ObfuscationCallback user_callback, void *user_data) { ObfuscationCallbackData callback_data; if (!PayloadObfuscationRequired(p)) return OB_RET_ERROR; SortObfuscationEntries(); SetObfuscationCallbackData(&callback_data, p, user_callback, user_data); /* Send header information first - isn't obfuscated */ if (user_callback(p->pkth, p->pkt, (ob_size_t)(p->data - p->pkt), 0, user_data) != OB_RET_SUCCESS) { return OB_RET_ERROR; } if (TraverseObfuscationList(&callback_data, NULL, p->data, (ob_size_t)(p->pkth->caplen - (p->data - p->pkt)), get_pkt_seq_num(p)) != OB_RET_SUCCESS) { return OB_RET_ERROR; } return OB_RET_SUCCESS; } // obfuscatePacketStreamSegments static ObRet OB_API_ObfuscatePacketStreamSegments(Packet *p, ObfuscationCallback user_callback, void *user_data) { ObfuscationStreamCallbackData stream_callback_data; ObfuscationCallbackData callback_data; if (!PayloadObfuscationRequired(p)) return OB_RET_ERROR; SortObfuscationEntries(); SetObfuscationStreamCallbackData(&stream_callback_data, &callback_data, p, user_callback, user_data); if (stream_api->traverse_stream_segments(p, ObfuscateStreamSegmentsCallback, (void *)&stream_callback_data) == -1) { return OB_RET_ERROR; } return OB_RET_SUCCESS; } // getObfuscatedPayload static ObRet OB_API_GetObfuscatedPayload(Packet *p, uint8_t **payload, ob_size_t *payload_len) { ObfuscationCallbackData callback_data; ObfuscatedPayload user_data; if (!PayloadObfuscationRequired(p)) return OB_RET_ERROR; if ((payload == NULL) || (payload_len == NULL)) return OB_RET_ERROR; *payload = NULL; *payload_len = 0; user_data.payload = payload; user_data.payload_len = payload_len; user_data.payload_size = 0; SortObfuscationEntries(); SetObfuscationCallbackData(&callback_data, p, GetObfuscatedPayloadCallback, (void *)&user_data); if (TraverseObfuscationList(&callback_data, NULL, p->data, (ob_size_t)(p->pkth->caplen - (p->data - p->pkt)), get_pkt_seq_num(p)) != OB_RET_SUCCESS) { return OB_RET_ERROR; } return OB_RET_SUCCESS; } // printObfuscationEntries static void OB_API_PrintObfuscationEntries(int sorted) { int i; ObfuscationEntry *entry; if (sorted) SortObfuscationEntries(); for (i = 0; i < ob_struct.num_entries; i++) { LogMessage("Entry: %d\n", i); if (sorted) entry = ob_struct.sort_entries[i]; else entry = &ob_struct.entries[i]; PrintObfuscationEntry(entry, 2); } } /******************************************************************************* * Private function definitions ******************************************************************************/ /******************************************************************************* * Function: NumObfuscateMaxLenEntries() * * Gets the number of current OB_LENGTH_MAX entries that have been added. * * Arguments * None * * Returns * The number of current OB_LENGTH_MAX entries. * ******************************************************************************/ static inline int NumObfuscateMaxLenEntries(void) { return ob_struct.num_maxlen_entries; } /******************************************************************************* * Function: NumObfuscateSliceEntries() * * Gets the number of current slice entries that have been added. * * Arguments * None * * Returns * The number of current slice entries. * ******************************************************************************/ static inline int NumObfuscateSliceEntries(void) { return ob_struct.num_entries - ob_struct.num_maxlen_entries; } /******************************************************************************* * Function: ObfuscationEntryOverflow() * * Determines whether or not there is enough space in the static entry array to * add another obfucation entry. * * Arguments * ob_size_t * The length of the entry that should be added. If length is OB_LENGTH_MAX * then the max length array is checked. * * Returns * OB_RET_SUCCESS if the entry can be added * OB_RET_OVERFLOW if there isn't enough space to add another entry * ******************************************************************************/ static inline ObRet ObfuscationEntryOverflow(ob_size_t length) { if (length == OB_LENGTH_MAX) { if (NumObfuscateMaxLenEntries() >= OBFUSCATE_MAXLEN_ENTRIES) return OB_RET_OVERFLOW; } else { if (NumObfuscateSliceEntries() >= OBFUSCATE_SLICE_ENTRIES) return OB_RET_OVERFLOW; } return OB_RET_SUCCESS; } /******************************************************************************* * Function: PayloadObfuscationRequired() * * Determines whether or not the packet requires obfuscation. An obfuscation * flag is added to the packet flags when an obfuscation entry is added that * is associated with the packet. If there isn't any data, then it doesn't * need obfuscation. * * Arguments * Packet *p * The Packet to check * * Returns * 0 if obfuscation is not needed. * 1 if the packet has been flagged for obfuscation. * ******************************************************************************/ static inline int PayloadObfuscationRequired(Packet *p) { if ((p == NULL) || (p->pkth == NULL) || (p->pkt == NULL) || (p->data == NULL) || (p->pkt >= p->data) || ((ob_size_t)(p->data - p->pkt) > p->pkth->caplen)) { return 0; } if (!(p->packet_flags & PKT_PAYLOAD_OBFUSCATE) || (ob_struct.num_entries == 0)) { return 0; } return 1; } /******************************************************************************* * Function: SetObfuscationEntry() * * Initializes an obfuscation entry with the passed in values. * * Arguments * ObfuscationEntry *entry * The obfuscation entry to initialize * Packet *p * The Packet to associate with this entry * ob_size_t offset * The offset into the packet to start obfuscation * ob_size_t length * The amount of data to obfuscate starting from offset * ob_char_t ob_char * The character to use when obfuscating * * Returns * None * ******************************************************************************/ static inline void SetObfuscationEntry(ObfuscationEntry *entry, Packet *p, ob_size_t offset, ob_size_t length, ob_char_t ob_char, uint32_t seq) { if (entry == NULL) return; entry->p = p; entry->offset = offset; entry->length = length; entry->ob_char = ob_char; entry->seq = seq; } /******************************************************************************* * Function: SetObfuscationCallbackData() * * Initializes the callback data for use in TraverseObfuscationList. * * Arguments * ObfuscationCallbackData *callback_data * The callback data struct to initialize * Packet *p * The Packet to associate with this entry * ob_size_t offset * The offset into the packet to start obfuscation * ob_size_t length * The amount of data to obfuscate starting from offset * ob_char_t ob_char * The character to use when obfuscating * * Returns * None * ******************************************************************************/ static inline void SetObfuscationCallbackData( ObfuscationCallbackData *callback_data, Packet *packet, ObfuscationCallback user_callback, void *user_data) { if (callback_data == NULL) return; callback_data->packet = packet; callback_data->user_callback = user_callback; callback_data->user_data = user_data; callback_data->entry_index = 0; callback_data->total_offset = 0; } /******************************************************************************* * Function: SetObfuscationStreamCallbackData() * * Initializes the callback data for use in TraverseObfuscationList. * * Arguments * ObfuscationStreamCallbackData *stream_callback_data * The stream callback data struct to initialize * ObfuscationCallbackData *callback_data * The callback data struct to initialize * Packet *p * The Packet to associate with this entry * ob_size_t offset * The offset into the packet to start obfuscation * ob_size_t length * The amount of data to obfuscate starting from offset * ob_char_t ob_char * The character to use when obfuscating * * Returns * None * ******************************************************************************/ static inline void SetObfuscationStreamCallbackData( ObfuscationStreamCallbackData *stream_callback_data, ObfuscationCallbackData *callback_data, Packet *packet, ObfuscationCallback user_callback, void *user_data) { if ((stream_callback_data == NULL) || (callback_data == NULL)) return; SetObfuscationCallbackData(callback_data, packet, user_callback, user_data); stream_callback_data->data = callback_data; stream_callback_data->next_seq = 0; stream_callback_data->last_entry_index = 0; } /******************************************************************************* * Function: SortObfuscationEntries() * * Uses qsort to sort the entries that have been added. Possibly qsort is not * the most efficient sort here since, in general, the entries will be added * from smallest offset to largest. * * Arguments * None * * Returns * None * ******************************************************************************/ static inline void SortObfuscationEntries(void) { if (!ob_struct.sorted) { qsort((void *)ob_struct.sort_entries, ob_struct.num_entries, sizeof(ObfuscationEntry *), ObfuscationEntrySort); ob_struct.sorted = 1; } } /******************************************************************************* * Function: AddObfuscationEntry() * * Adds an obfuscation entry to the obfuscation list. OB_LENGTH_MAX entries * are first checked to see if there is an entry already associated with * the Packet passed in. If there is, the entry with the lesser of the two * offsets is used. * * Arguments * Packet *p * The Packet to be associated with this entry * ob_size_t offset * The offset into the payload of this packet to start obfuscating * ob_size_t length * The length of the payload starting at offset to obfuscate * ob_char_t * The character to use when obfuscating * * Returns * OB_RET_SUCCESS if the entry was successfully added * OB_RET_OVERFLOW if there is no room left to store the entry * ******************************************************************************/ static ObRet AddObfuscationEntry(Packet *p, ob_size_t offset, ob_size_t length, ob_char_t ob_char) { ObfuscationEntry *entry; int entry_index = ob_struct.num_entries; uint32_t seq = offset + get_pkt_seq_num(p); if (length == OB_LENGTH_MAX) { int i; /* Check to see if there is an OB_LENGTH_MAX entry already associated * with this packet */ for (i = 0; i < ob_struct.num_maxlen_entries; i++) { entry = ob_struct.maxlen_entries[i]; if (entry->p == p) { /* Already have an entry for this packet. Use the entry with * the lesser of the two offsets */ if (offset < entry->offset) { entry->offset = offset; entry->ob_char = ob_char; } return OB_RET_SUCCESS; } } } if (ObfuscationEntryOverflow(length) != OB_RET_SUCCESS) return OB_RET_OVERFLOW; /* Reset sorted since we're adding an entry and the list will need * to be sorted again */ ob_struct.sorted = 0; /* Get the entry at the current index */ entry = &ob_struct.entries[entry_index]; SetObfuscationEntry(entry, p, offset, length, ob_char, seq); ob_struct.sort_entries[entry_index] = entry; ob_struct.num_entries++; if (length == OB_LENGTH_MAX) { ob_struct.maxlen_entries[ob_struct.num_maxlen_entries] = entry; ob_struct.num_maxlen_entries++; } return OB_RET_SUCCESS; } /******************************************************************************* * Function: ObfuscationEntrySort() * * Sorting callback. Sorted by offset, then length if the offsets are the same. * * Arguments * const void *data1 * The compare to argument * const void *data2 * The argument to compare to the first argument * * Returns * -1 if the first ObfuscationEntry is considered less than the second * 1 if the first ObfuscationEntry is considered greater than the second * 0 if both offset and length are equal * ******************************************************************************/ static int ObfuscationEntrySort(const void *data1, const void *data2) { ObfuscationEntry *ob1 = *((ObfuscationEntry **)data1); ObfuscationEntry *ob2 = *((ObfuscationEntry **)data2); if (ob1->offset < ob2->offset) return -1; else if (ob1->offset > ob2->offset) return 1; else if (ob1->length < ob2->length) return -1; else if (ob1->length > ob2->length) return 1; return 0; } /******************************************************************************* * Function: TraverseObfuscationList() * * This is the main function for obfuscating a payload or stream segments. * It walks through a packet and obfuscation entries, calling the user * callback with obfuscated and non-obfuscated instructions. * * Arguments * ObfuscationCallbackData *data * The state tracking data structure. Has the packet being obfuscated, * current obfuscation entry and total number of bytes obfuscated thus * far. * DAQ_PktHdr_t *pkth * The pcap header information associated with the payload being * obfuscated. * uint8_t *pkt * The start of the packet including Ethernet headers, etc. * uint8_t *payload * Pointer to the payload data to be obfuscated * ob_size_t * The size of the payload data * uint32_t * The sequence number of payload data * * Returns * OB_RET_SUCCESS if successfully completed * OB_RET_ERROR if the user callback doesn't return OB_RET_SUCCESS * ******************************************************************************/ static ObRet TraverseObfuscationList(ObfuscationCallbackData *data, const DAQ_PktHdr_t *pkth, const uint8_t *payload_data, ob_size_t payload_size, uint32_t seq_num) { int i; ob_size_t payload_offset = 0; const DAQ_PktHdr_t *pkth_tmp = pkth; uint32_t data_seq = get_pkt_seq_num(data->packet); int32_t length; #ifdef OBFUSCATION_TEST uint8_t print_array[OB_LENGTH_MAX]; ob_size_t total_offset = data->total_offset; ob_size_t start_total_offset = 0; ob_size_t start_payload_offset = 0; #endif if ((payload_data == NULL) || (payload_size == 0)) return OB_RET_ERROR; #ifdef OBFUSCATION_TEST LogMessage("Payload data: %u bytes\n", payload_size); LogMessage("===============================================================" "=================\n"); #endif /* Masks the start of raw packet that is not part of this rebuilt packet */ length = get_length_from_seq(seq_num, data_seq); if ( length > 0) { /* Call the user callback and tell it to obfuscate to 00 */ if (data->user_callback(pkth_tmp, NULL, (ob_size_t)length, 0, data->user_data) != OB_RET_SUCCESS) { return OB_RET_ERROR; } payload_offset += length; /* Only the first payload call sends the pcap_pkthdr */ pkth_tmp = NULL; } /* Start from current saved obfuscation entry index */ for (i = data->entry_index; i < ob_struct.num_entries; i++) { /* Get the entry from the sorted array */ const ObfuscationEntry *entry = ob_struct.sort_entries[i]; int32_t ob_offset = get_length_from_seq (seq_num, entry->seq); int32_t ob_end = get_length_from_seq (seq_num, entry->seq + entry->length); ob_size_t ob_length = entry->length; if (ob_end <= 0 ) continue; else if (ob_offset < 0) { ob_length = ob_end; ob_offset = 0; } /* Make sure it's for the right packet */ if (entry->p != data->packet) { #ifdef OBFUSCATION_TEST LogMessage("flags1: %08x, flags2: %08x\n", entry->p->packet_flags, data->packet->packet_flags); #endif continue; } #ifdef OBFUSCATION_TEST LogMessage(" Total offset: %u\n\n", total_offset); start_total_offset = total_offset; start_payload_offset = payload_offset; #endif /* Note the obfuscation offset is only used at this point to determine * the amount of data that does not need to be obfuscated up to the * offset or the length of what needs to be obfuscated if the offset * is less than what's already been logged */ if (ob_offset > payload_offset) { /* Get the amount of non-obfuscated data - need to log straight * packet data up to obfuscation offset */ ob_size_t length = ob_offset - payload_offset; /* If there is more length than what's left in the packet, * truncate it, do we don't overflow */ if (length > (payload_size - payload_offset)) length = payload_size - payload_offset; /* Call the user callback and tell it not to obfuscate the data * by passing in a non-NULL packet pointer */ if (data->user_callback(pkth_tmp, payload_data + payload_offset, length, 0, data->user_data) != OB_RET_SUCCESS) { return OB_RET_ERROR; } #ifdef OBFUSCATION_TEST SafeMemcpy(print_array + payload_offset, payload_data + payload_offset, length, print_array, print_array + sizeof(print_array)); #endif /* Only the first payload call sends the pcap_pkthdr */ pkth_tmp = NULL; /* Adjust offsets */ payload_offset += length; /* If there is no more packet data, break out of the loop */ if (payload_offset == payload_size) { #ifdef OBFUSCATION_TEST PrintPacketData(print_array + start_payload_offset, length); LogMessage("\n"); #endif break; } } else if (ob_offset < payload_offset) { /* If the entries offset is less than the current total offset, * decrease the length. */ if(ob_length > (payload_offset - ob_offset)) ob_length -= (payload_offset - ob_offset); else continue; } /* Adjust the amount of data to obfuscate if it exceeds the amount of * data left in the packet. Account for overflow */ if (((payload_offset + ob_length) > payload_size) || ((payload_offset + ob_length) <= payload_offset)) { ob_length = payload_size - payload_offset; } /* Call the user callback and tell it to obfuscate the data by passing * in a NULL packet pointer */ if (data->user_callback(pkth_tmp, NULL, ob_length, entry->ob_char, data->user_data) != OB_RET_SUCCESS) { return OB_RET_ERROR; } #ifdef OBFUSCATION_TEST LogMessage(" Entry: %d\n", i); LogMessage(" --------------------------\n"); PrintObfuscationEntry(entry, 4); LogMessage("\n"); SafeMemset(print_array + payload_offset, entry->ob_char, ob_length, print_array, print_array + sizeof(print_array)); if (ob_length < entry->length) { if (ob_offset < start_total_offset) { if (payload_offset + ob_length == payload_size) { LogMessage(" Obfuscating beyond already obfuscated " "(%u bytes) and to end of payload: %u bytes\n\n", (start_total_offset - ob_offset), ob_length); } else { LogMessage(" Obfuscating beyond already obfuscated " "(%u bytes): %u bytes\n\n", (start_total_offset - ob_offset), ob_length); } } else { LogMessage(" Obfuscating to end of payload: " "%u bytes\n\n", ob_length); } } else { LogMessage(" Obfuscating: %u bytes\n\n", ob_length); } PrintPacketData(print_array + start_payload_offset, (payload_offset - start_payload_offset) + ob_length); if (((entry->offset + entry->length) - (total_offset + ob_length)) > 0) { LogMessage("\n Remaining amount to obfuscate: %u bytes\n", (entry->offset + entry->length) - (total_offset + ob_length)); } LogMessage("\n"); #endif /* Only the first payload call sends the pcap_pkthdr */ pkth_tmp = NULL; /* Adjust offsets */ payload_offset += ob_length; /* If there is no more packet data, break out of the loop */ if (payload_offset == payload_size) break; } /* There's more data in the packet left, meaning we ran out of * obfuscation entries */ if (payload_size > payload_offset) { int32_t data_left = get_length_from_seq(seq_num + payload_offset, data->packet->dsize + data_seq); length = payload_size - payload_offset; if (data_left > 0) { if (data_left < length) length = data_left; /* Call the user callback and tell it not to obfuscate the data * by passing in a non-NULL packet pointer */ if (data->user_callback(pkth_tmp, payload_data + payload_offset, length, 0, data->user_data) != OB_RET_SUCCESS) { return OB_RET_ERROR; } #ifdef OBFUSCATION_TEST SafeMemcpy(print_array + payload_offset, payload_data + payload_offset, length, print_array, print_array + sizeof(print_array)); #endif payload_offset += length; } length = payload_size - payload_offset; /* Masks the start of raw packet that is not part of this rebuilt packet */ if (length > 0) { /* Call the user callback and tell it to obfuscate the data with 00*/ if(data->user_callback(pkth_tmp, NULL, length, 0, data->user_data) != OB_RET_SUCCESS) { return OB_RET_ERROR; } #ifdef OBFUSCATION_TEST SafeMemcpy(print_array + payload_offset, payload_data + payload_offset, length, print_array, print_array + sizeof(print_array)); #endif } } #ifdef OBFUSCATION_TEST LogMessage("Obfuscated payload\n"); LogMessage("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" "~~~~~~~~~~\n"); PrintPacketData(print_array, payload_size); LogMessage("\n\n"); #endif /* Save these for next time we come in if necessary. Mainly for * traversing stream segments */ data->entry_index = i; return OB_RET_SUCCESS; } /******************************************************************************* * Function: ObfuscateStreamSegmentsCallback() * * Stream API callback for traverse_stream_segments. * * Arguments * DAQ_PktHdr_t *pkth * The pcap header information associated with the segment packet. * uint8_t *pkt * Pointer to the segment packet data starting at packet headers * uint8_t *payload * Pointer to the segment payload data to be obfuscated * uint32_t * The sequence number of this segment * void *data * The ObfuscationCallBack data * * Returns * Per stream api traverse_reassembled: * 0 if obfuscation was successful. * -1 if we had to bail on the obfuscation due to the user callback * telling us to - this should tell traverse_stream_segments to stop * traversing and not call this anymore. * ******************************************************************************/ static int ObfuscateStreamSegmentsCallback(DAQ_PktHdr_t *pkth, uint8_t *pkt, uint8_t *payload, uint32_t seq_num, void *data) { ObfuscationStreamCallbackData *callback_data = (ObfuscationStreamCallbackData *)data; ob_size_t payload_size = (uint16_t)(pkth->caplen - (payload - pkt)); if ((pkt >= payload) || ((ob_size_t)(payload - pkt) > pkth->caplen)) return -1; if (callback_data->data->user_callback(pkth, pkt, (ob_size_t)(payload - pkt), 0, callback_data->data->user_data) != OB_RET_SUCCESS) { return -1; } /* If we get an overlap set the entry index to where the last packet * started, else set the last entry index to the end of the one for * the last segment */ if (callback_data->next_seq > seq_num) { callback_data->data->entry_index = callback_data->last_entry_index; } else { callback_data->last_entry_index = callback_data->data->entry_index; } if (TraverseObfuscationList(callback_data->data, NULL, payload, payload_size, seq_num) != OB_RET_SUCCESS) { return -1; } /* Update next expected sequence number */ callback_data->next_seq = seq_num + payload_size; return 0; } /******************************************************************************* * Function: GetObfuscationPayloadCallback() * * ObfuscationCallback for returning an allocated obfuscated payload. * * Arguments * DAQ_PktHdr_t *pkth * The pcap header information associated with the payload being * obfuscated. * uint8_t *packet_data * Pointer to the packet data to be obfuscated * ob_char_t ob_char * The obfuscation character * ob_size_t length * The length of the portion of packet payload to use * void *user_data * The ObfuscatedPayload data * * Returns * OB_RET_ERROR if copying obfuscation data is not successful * OB_RET_SUCCESS if successful copying data to payload * ******************************************************************************/ static ObRet GetObfuscatedPayloadCallback(const DAQ_PktHdr_t *pkth, const uint8_t *packet_data, ob_size_t length, ob_char_t ob_char, void *user_data) { ObfuscatedPayload *ob_payload = (ObfuscatedPayload *)user_data; uint8_t *payload; ob_size_t payload_len, payload_size; if (ob_payload == NULL) return OB_RET_ERROR; if ((ob_payload->payload == NULL) || (ob_payload->payload_len == NULL)) return OB_RET_ERROR; payload = *ob_payload->payload; payload_len = *ob_payload->payload_len; payload_size = ob_payload->payload_size; if ((payload_len + length) > payload_size) { /* Allocate extra so we don't have to reallocate every time in */ ob_size_t new_size = payload_len + length + 100; uint8_t *tmp = (uint8_t *)SnortAlloc(new_size); if (payload != NULL) { if (SafeMemcpy(tmp, payload, payload_len, tmp, tmp + new_size) != SAFEMEM_SUCCESS) { free(tmp); free(payload); return OB_RET_ERROR; } free(payload); } payload_size = new_size; ob_payload->payload_size = new_size; *ob_payload->payload = tmp; payload = tmp; } if (packet_data != NULL) { if (SafeMemcpy(payload + payload_len, packet_data, length, payload, payload + payload_size) != SAFEMEM_SUCCESS) { free(payload); return OB_RET_ERROR; } } else { if (SafeMemset(payload + payload_len, (uint8_t)ob_char, length, payload, payload + payload_size) != SAFEMEM_SUCCESS) { free(payload); return OB_RET_ERROR; } } *ob_payload->payload_len += length; return OB_RET_SUCCESS; } /******************************************************************************* * Function: PrintObfuscationEntry() * * Prints an obfuscation entry offsetted with optional leading whitespace. * * Arguments * const ObfuscationEntry *entry * The entry to print * int leading_whitespace * The amount of whitespace to use before printing a line. * Returns * None * ******************************************************************************/ static void PrintObfuscationEntry(const ObfuscationEntry *entry, int leading_space) { if (entry == NULL) return; LogMessage("%*sPacket: %p\n", leading_space, "", (void*)entry->p); LogMessage("%*sOffset: %u\n", leading_space, "", entry->offset); LogMessage("%*sLength: %u\n", leading_space, "", entry->length); if (isgraph((int)entry->ob_char)) LogMessage("%*sOb char: \'%c\'\n", leading_space, "", entry->ob_char); else LogMessage("%*sOb char: 0x%02x\n", leading_space, "", entry->ob_char); } /****************************************************************************** * Testing ******************************************************************************/ #ifdef OBFUSCATION_TEST_STANDALONE #include #include #include #include #include #include #define PAYLOAD_ALLOC_SIZE 1024 /* Used for standalone testing */ typedef struct _Segment { DAQ_PktHdr_t *pkth; uint8_t *data; uint16_t size; struct _Segment *next; } Segment; /* Used for standalone testing */ typedef struct _ObPacket { struct Packet p; Segment *seglist; } ObPacket; static uint8_t *ob_payload = NULL; static void ObTestAlloc(void **, int, int); static void CreateObEntries(Packet *, ob_char_t, ob_size_t, ob_size_t, int, int); static ObRet ObCallback(DAQ_PktHdr_t *, uint8_t *, ob_char_t, ob_size_t, void *); static uint8_t * GetPayloadFromFile(char *, ob_size_t *); static int TraverseReassembled(Packet *p, int (*callback)(DAQ_PktHdr_t *, uint8_t *, void *), void *user_data) { ObfuscationCallbackData *callback_data = (ObfuscationCallbackData *)user_data; int segments = 0; Segment *seg; ObPacket *op = (ObPacket *)p; for (seg = op->seglist; seg != NULL; seg = seg->next) { if (callback(seg->pkth, seg->data, user_data) != 0) return segments; segments++; } return segments; } static void ObTestAlloc(void **ptr, int ptr_size, int this_size) { if (ptr == NULL) return; if (*ptr == NULL) { *ptr = calloc(1, this_size); if (*ptr == NULL) { fprintf(stderr, "Failed to allocate memory for payload.\n"); exit(1); } } else { if (this_size > ptr_size) { *ptr = realloc(*ptr, this_size); if (*ptr == NULL) { fprintf(stderr, "Failed to allocate memory for payload.\n"); exit(1); } } } } static void CreateObEntries(Packet *p, ob_char_t ob_char, ob_size_t ob_offset, ob_size_t ob_length, int reverse, int add_maxlen) { typedef struct _ob_tmp_struct { ob_size_t offset; ob_size_t length; } ob_tmp_struct_t; ob_size_t offset; ob_tmp_struct_t *tmp_struct = NULL; int num_tmps = 0; int i; if (p == NULL) return; for (offset = (rand() % ob_offset) + 1; offset < (p->dsize - ob_offset); offset += (rand() % ob_offset) + 1) { ob_size_t length = rand() % ob_length + 1; ObTestAlloc((void **)&tmp_struct, sizeof(ob_tmp_struct_t) * num_tmps, sizeof(ob_tmp_struct_t) * (num_tmps + 1)); tmp_struct[num_tmps].offset = offset; tmp_struct[num_tmps].length = length; num_tmps++; if (add_maxlen && (offset > p->dsize/2)) obApi->addObfuscationEntry(p, offset, OB_LENGTH_MAX, ob_char); if ((offset + length) >= p->dsize) break; } if (reverse) { for (i = num_tmps - 1; i >= 0; i--) { obApi->addObfuscationEntry(p, tmp_struct[i].offset, tmp_struct[i].length, ob_char); } } else { for (i = 0; i < num_tmps; i++) { obApi->addObfuscationEntry(p, tmp_struct[i].offset, tmp_struct[i].length, ob_char); } } } static ObRet ObCallback(DAQ_PktHdr_t *pkth, uint8_t *packet_data, ob_char_t ob_char, ob_size_t length, void *user_data) { ob_size_t *offset = (ob_size_t *)user_data; if (packet_data != NULL) memcpy(ob_payload + *offset, packet_data, length); else memset(ob_payload + *offset, ob_char, length); *offset += length; return OB_RET_SUCCESS; } static uint8_t * GetPayloadFromFile(char *payload_file, ob_size_t *payload_bytes) { uint8_t *payload = NULL; FILE *fp; ob_size_t bytes; if (payload_bytes == NULL) return NULL; *payload_bytes = 0; fp = fopen(payload_file, "r"); if (fp == NULL) { fprintf(stderr, "Could not open payload file \"%s\": %s\n", payload_file, strerror(errno)); exit(1); } ObTestAlloc((void **)&payload, 0, PAYLOAD_ALLOC_SIZE); while ((bytes = fread(payload + *payload_bytes, sizeof(char), PAYLOAD_ALLOC_SIZE, fp)) == PAYLOAD_ALLOC_SIZE) { ObTestAlloc((void **)&payload, *payload_bytes + bytes, *payload_bytes + bytes + bytes); *payload_bytes += bytes; } fclose(fp); *payload_bytes += bytes; if (*payload_bytes > OB_LENGTH_MAX) *payload_bytes = OB_LENGTH_MAX; return payload; } static uint8_t * GetStaticPayload(ob_char_t ob_char, ob_size_t *payload_bytes) { uint8_t *payload = NULL; ob_size_t alloc_size = 1000; ob_char_t char1 = 0x00; ob_char_t char2 = 0x01; ob_char_t c = char1; if (c == ob_char) c = char2; ObTestAlloc((void **)&payload, 0, alloc_size); memset(payload, c, alloc_size); *payload_bytes = alloc_size; return payload; } static void SegmentPayload(Packet *p) { ob_size_t length; ob_size_t i; Segment *last; ObPacket *op = (ObPacket *)p; for (i = 0; i < p->dsize; i += length) { Segment *seg = NULL; length = rand() % 20 + 1; if (i + length > p->dsize) length = p->dsize - i; ObTestAlloc((void **)&seg, 0, sizeof(Segment)); ObTestAlloc((void **)&seg->data, 0, length); ObTestAlloc((void **)&seg->pkth, 0, sizeof(DAQ_PktHdr_t)); memcpy(seg->data, p->data + i, length); seg->size = length; seg->pkth->caplen = length; if (op->seglist == NULL) { op->seglist = seg; last = seg; } else { last->next = seg; last = seg; } if ((i + length) == p->dsize) break; } } void PrintUsage(char *prog) { fprintf(stderr, "Usage: %s [options]\n", prog); fprintf(stderr, " -a (add max length entry)\n"); fprintf(stderr, " -c \n"); fprintf(stderr, " -l \n"); fprintf(stderr, " -o \n"); fprintf(stderr, " -p \n"); fprintf(stderr, " -s (use segmentation)\n"); fprintf(stderr, " -r (reverse entries before sorting)\n"); } int main(int argc, char *argv[]) { char c; char *payload_file = NULL; ob_char_t ob_char = 'X'; int segment = 0; int reverse = 0; int add_maxlen = 0; ob_size_t ob_offset = 50; ob_size_t ob_length = 16; uint8_t *payload = NULL; ob_size_t payload_bytes = 0; ob_size_t offset = 0; DAQ_PktHdr_t pkth, *pkthtmp; Packet packet; while ((c = getopt(argc, argv, "ac:l:o:p:rsh")) != -1) { switch (c) { case 'a': add_maxlen = 1; break; case 'c': ob_char = (ob_char_t)strtol(optarg, NULL, 0); break; case 'l': { int value; if (!isdigit(optarg[0])) { PrintUsage(argv[0]); fprintf(stderr, "Obfuscation max length must be a " "positive integer.\n"); exit(1); } value = atoi(optarg); if (value > UINT16_MAX) { PrintUsage(argv[0]); fprintf(stderr, "Obfuscation max length must be " "less than 65535.\n"); exit(1); } ob_length = (ob_size_t)value; } break; case 'o': { int value; if (!isdigit(optarg[0])) { PrintUsage(argv[0]); fprintf(stderr, "Obfuscation offset must be a " "positive integer.\n"); exit(1); } value = atoi(optarg); if (value > UINT16_MAX) { PrintUsage(argv[0]); fprintf(stderr, "Obfuscation max offset must " "be less than 65535.\n"); exit(1); } ob_offset = (ob_size_t)value; } break; case 'p': payload_file = strdup(optarg); if (payload_file == NULL) { PrintUsage(argv[0]); fprintf(stderr, "Failed to copy payload file.\n"); exit(1); } break; case 'r': reverse = 1; break; case 's': segment = 1; break; case 'h': PrintUsage(argv[0]); exit(0); default: PrintUsage(argv[0]); fprintf(stderr, "Invalid option. Use -h for usage.\n"); exit(1); } } srand(time(NULL)); if (payload_file != NULL) { payload = GetPayloadFromFile(payload_file, &payload_bytes); if (payload == NULL) { fprintf(stderr, "Failed to get data from \"%s\"\n", payload_file); exit(1); } } else { payload = GetStaticPayload(ob_char, &payload_bytes); } ObTestAlloc((void **)&ob_payload, 0, payload_bytes); obApi->resetObfuscationEntries(); memset(&packet, 0, sizeof(packet)); pkthtmp = (DAQ_PktHdr_t *)&packet.pkth; pkthtmp = &pkth; pkthtmp->caplen = payload_bytes; pkthtmp->ts.tv_sec = 0; pkthtmp->ts.tv_usec = 0; packet.packet_flags |= PKT_PAYLOAD_OBFUSCATE; packet.data = payload; packet.dsize = payload_bytes; CreateObEntries(&packet, ob_char, ob_offset, ob_length, reverse, add_maxlen); //obApi->printObfuscationEntries(); if (segment) { SegmentPayload(&packet); if (obApi->payloadObfuscationRequired(&packet)) obApi->obfuscateStreamSegments(&packet, ObCallback, &offset); } else { if (obApi->payloadObfuscationRequired(&packet)) obApi->obfuscatePayload(&packet, ObCallback, &offset); } free(payload); free(ob_payload); if (payload_file != NULL) free(payload_file); return 0; } #endif /* OBFUSCATION_TEST_STANDALONE */ snort-2.9.20/src/snprintf.c0000644000175000017500000003225214241077411013674 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* ** This file contains various routines which we shamelessly steal from other ** opensource products :-P (I love code reuseability idea) ** fygrave@tigerteam.net */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HAVE_SNPRINTF #include "snprintf.h" /* snprintf() and all supporting routines were taken from sendmail, hence the * copyleft message */ /* * Copyright (c) 1998 Sendmail, Inc. All rights reserved. * Copyright (c) 1997 Eric P. Allman. All rights reserved. * Copyright (c) 1988, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * */ /* ** SNPRINTF, VSNPRINT -- counted versions of printf ** ** These versions have been grabbed off the net. They have been ** cleaned up to compile properly and support for .precision and ** %lx has been added. */ /************************************************************** * Original: * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 * A bombproof version of doprnt (sm_dopr) included. * Sigh. This sort of thing is always nasty do deal with. Note that * the version here does not include floating point... * * snprintf() is used instead of sprintf() as it does limit checks * for string length. This covers a nasty loophole. * * The other functions are there to prevent NULL pointers from * causing nast effects. **************************************************************/ void sm_dopr(); char *DoprEnd; int SnprfOverflow; #ifndef HAVE_SNPRINTF /* VARARGS3 */ int #ifdef __STDC__ snprintf(char *str, size_t count, const char *fmt, ...) #else snprintf(str, count, fmt, va_alist) char *str; size_t count; const char *fmt; va_dcl #endif { int len; VA_LOCAL_DECL VA_START(fmt); len = vsnprintf(str, count, fmt, ap); VA_END; return len; } #ifndef luna2 #ifndef HAVE_VSNPRINTF int vsnprintf(str, count, fmt, args) char *str; size_t count; const char *fmt; va_list args; { str[0] = 0; DoprEnd = str + count - 1; SnprfOverflow = 0; sm_dopr( str, fmt, args ); if(count > 0) DoprEnd[0] = 0; if(SnprfOverflow && tTd(57, 2)) printf("\nvsnprintf overflow, len = %ld, str = %s", (long) count, shortenstring(str, MAXSHORTSTR)); return strlen((const char *)str); } #endif /* !HAVE_VSNPRINTF */ #endif /* !luna2 */ #endif /* !HASSNPRINTF */ /* * sm_dopr(): poor man's version of doprintf */ void fmtstr __P((char *value, int ljust, int len, int zpad, int maxwidth)); void fmtnum __P((long value, int base, int dosign, int ljust, int len, int zpad)); void dostr __P(( char * , int )); char *output; void dopr_outch __P(( int c )); int SyslogErrno; void sm_dopr( buffer, format, args ) char *buffer; const char *format; va_list args; { int ch; long value; int longflag = 0; int pointflag = 0; int maxwidth = 0; char *strvalue; int ljust; int len; int zpad; #if !HAVE_STRERROR && !defined(ERRLIST_PREDEFINED) extern char *sys_errlist[]; extern int sys_nerr; #endif output = buffer; while((ch = *format++) != '\0') { switch(ch) { case '%': ljust = len = zpad = maxwidth = 0; longflag = pointflag = 0; nextch: ch = *format++; switch(ch) { case 0: dostr( "**end of format**" , 0); return; case '-': ljust = 1; goto nextch; case '0': /* set zero padding if len not set */ if(len==0 && !pointflag) zpad = '0'; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if(pointflag) maxwidth = maxwidth*10 + ch - '0'; else len = len*10 + ch - '0'; goto nextch; case '*': if(pointflag) maxwidth = va_arg( args, int ); else len = va_arg( args, int ); goto nextch; case '.': pointflag = 1; goto nextch; case 'l': longflag = 1; goto nextch; case 'u': case 'U': /*fmtnum(value,base,dosign,ljust,len,zpad) */ if(longflag) { value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 10,0, ljust, len, zpad ); break; case 'o': case 'O': /*fmtnum(value,base,dosign,ljust,len,zpad) */ if(longflag) { value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 8,0, ljust, len, zpad ); break; case 'd': case 'D': if(longflag) { value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 10,1, ljust, len, zpad ); break; case 'x': if(longflag) { value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value, 16,0, ljust, len, zpad ); break; case 'X': if(longflag) { value = va_arg( args, long ); } else { value = va_arg( args, int ); } fmtnum( value,-16,0, ljust, len, zpad ); break; case 's': strvalue = va_arg( args, char *); if(maxwidth > 0 || !pointflag) { if(pointflag && len > maxwidth) len = maxwidth; /* Adjust padding */ fmtstr( strvalue,ljust,len,zpad, maxwidth); } break; case 'c': ch = va_arg( args, int ); dopr_outch( ch ); break; case 'm': #if HAVE_STRERROR dostr(strerror(SyslogErrno), 0); #else if(SyslogErrno < 0 || SyslogErrno >= sys_nerr) { dostr("Error ", 0); fmtnum(SyslogErrno, 10, 0, 0, 0, 0); } else dostr((char *)sys_errlist[SyslogErrno], 0); #endif break; case '%': dopr_outch( ch ); continue; default: dostr( "???????" , 0); } break; default: dopr_outch( ch ); break; } } *output = 0; } void fmtstr( value, ljust, len, zpad, maxwidth ) char *value; int ljust, len, zpad, maxwidth; { int padlen, strlen; /* amount to pad */ if(value == 0) { value = ""; } for(strlen = 0; value[strlen]; ++ strlen); /* strlen */ if(strlen > maxwidth && maxwidth) strlen = maxwidth; padlen = len - strlen; if(padlen < 0) padlen = 0; if(ljust) padlen = -padlen; while(padlen > 0) { dopr_outch( ' ' ); --padlen; } dostr( value, maxwidth ); while(padlen < 0) { dopr_outch( ' ' ); ++padlen; } } void fmtnum( value, base, dosign, ljust, len, zpad ) long value; int base, dosign, ljust, len, zpad; { int signvalue = 0; unsigned long uvalue; char convert[20]; int place = 0; int padlen = 0; /* amount to pad */ int caps = 0; /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", value, base, dosign, ljust, len, zpad )); */ uvalue = value; if(dosign) { if(value < 0) { signvalue = '-'; uvalue = -value; } } if(base < 0) { caps = 1; base = -base; } do { convert[place++] = (caps? "0123456789ABCDEF":"0123456789abcdef") [uvalue % (unsigned)base ]; uvalue = (uvalue / (unsigned)base ); }while(uvalue); convert[place] = 0; padlen = len - place; if(padlen < 0) padlen = 0; if(ljust) padlen = -padlen; /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", convert,place,signvalue,padlen)); */ if(zpad && padlen > 0) { if(signvalue) { dopr_outch( signvalue ); --padlen; signvalue = 0; } while(padlen > 0) { dopr_outch( zpad ); --padlen; } } while(padlen > 0) { dopr_outch( ' ' ); --padlen; } if(signvalue) dopr_outch( signvalue ); while(place > 0) dopr_outch( convert[--place] ); while(padlen < 0) { dopr_outch( ' ' ); ++padlen; } } void dostr( str , cut) char *str; int cut; { if(cut) { while(*str && cut-- > 0) dopr_outch(*str++); } else { while(*str) dopr_outch(*str++); } } void dopr_outch( c ) int c; { #if 0 if(iscntrl(c) && c != '\n' && c != '\t') { c = '@' + (c & 0x1F); if(DoprEnd == 0 || output < DoprEnd) *output++ = '^'; } #endif if(DoprEnd == 0 || output < DoprEnd) *output++ = c; else SnprfOverflow++; } /* ** QUAD_TO_STRING -- Convert a quad type to a string. ** ** Convert a quad type to a string. This must be done ** separately as %lld/%qd are not supported by snprint() ** and adding support would slow down systems which only ** emulate the data type. ** ** Parameters: ** value -- number to convert to a string. ** ** Returns: ** pointer to a string. */ char * quad_to_string(value) QUAD_T value; { char *fmtstr; static char buf[64]; /* ** Use sprintf() instead of snprintf() since snprintf() ** does not support %qu or %llu. The buffer is large enough ** to hold the string so there is no danger of buffer ** overflow. */ #if NEED_PERCENTQ fmtstr = "%qu"; #elif SIZEOF_UNSIGNED_LONG_INT == 8 fmtstr = "%lu"; #else fmtstr = "%llu"; #endif sprintf(buf, fmtstr, value); return buf; } /* ** SHORTENSTRING -- return short version of a string ** ** If the string is already short, just return it. If it is too ** long, return the head and tail of the string. ** ** Parameters: ** s -- the string to shorten. ** m -- the max length of the string. ** ** Returns: ** Either s or a short version of s. */ char * shortenstring(s, m) register const char *s; int m; { int l; static char buf[MAXSHORTSTR + 1]; l = strlen(s); if(l < m) return(char *) s; if(m > MAXSHORTSTR) m = MAXSHORTSTR; else if(m < 10) { if(m < 5) { strncpy(buf, s, m); buf[m] = '\0'; return buf; } strncpy(buf, s, m - 3); strcpy(buf + m - 3, "..."); return buf; } m = (m - 3) / 2; strncpy(buf, s, m); strcpy(buf + m, "..."); strcpy(buf + m + 3, s + l - m); return buf; } #endif snort-2.9.20/src/obfuscation.h0000644000175000017500000002203114241076644014353 0ustar apoapo/****************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ******************************************************************************/ #ifndef __OBFUSCATION_H__ #define __OBFUSCATION_H__ #include #include "decode.h" /******************************************************************************* * Macros ******************************************************************************/ /* This should be defined to be greater than or equal to the maximum * amount of data expected to be obfuscated */ #define OB_LENGTH_MAX UINT16_MAX /******************************************************************************* * Types ******************************************************************************/ typedef uint8_t ob_char_t; typedef uint16_t ob_size_t; typedef enum _ObRet { OB_RET_SUCCESS, OB_RET_ERROR, OB_RET_OVERFLOW } ObRet; /******************************************************************************* * Callback to use for obfuscating payload or stream segments - see API below. * * The first chunk of a payload or stream segment whether needing obfuscation * or not will pass a valid pcap_pkthdr struct. Subsequent calls will pass NULL * for this structure. This is useful, especially for the stream segment API * call to know when a new segment begins. Any new "payload" will have a valid * pcap_pkthdr struct. * * If the slice sent in has a non-NULL packet data pointer, the data should *NOT* * be obfuscated. * * If the chunk sent in has a NULL packet data pointer, then that chunk of data * should be obfuscated with the obfuscation character. * * The length passed in is the amount of data that should be copied from the * packet data pointer or the amount of data that should be written with the * obfuscation character. * * Arguments * DAQ_PktHdr_t *pkth * The pcap header that contains the packet caplen and timestamps * uint8_t *packet_data * A pointer to the current offset into the packet data. NULL if * obfuscation of the payload slice is required. * ob_char_t ob_char * The obfuscation character to use if packet_data is NULL. * ob_size_t length * The amount of data to be logged or obfuscated. * void *user_data * The user data passed in to the API functions obfuscatePayload() or * obfuscateStreamSegments below. * * Returns * OB_RET_SUCCESS if all is good * OB_RET_ERROR if the rest of the obfuscation should not be done * ******************************************************************************/ typedef ObRet (*ObfuscationCallback) ( const DAQ_PktHdr_t *pkth, const uint8_t *packet_data, ob_size_t length, ob_char_t ob_char, void *user_data ); /******************************************************************************* * Obfuscation API ******************************************************************************/ typedef struct _ObfuscationApi { /* * Resets/clears any entries that have been added * Should be done per packet aquisition * * Arguments * None * * Returns * None */ void (*resetObfuscationEntries)(void); /* * Adds an obfuscation entry to the queue * * Arguments * Packet *p * The Packet struct that has the payload data that should be obfuscated * ob_size_t offset * The offset from the beginning of the payload to start obfuscation * ob_size_t length * The amount of data to obfuscate * ob_char_t ob_char * The character to use when obfuscating * * There are two types of entries that can be added. A slice entry that * has an offset and length less than OB_LENGTH_MAX and an entry with * length OB_LENGTH_MAX that implies obfuscating from offset to the end * of the packet data. * * NOTE -- * There is a fixed size of slice entries and OB_LENGTH_MAX entries. * If OB_RET_OVERFLOW is returned when attempting to add a slice entry, * a second call can be made to add an OB_LENGTH_MAX entry. Only one * OB_LENGTH_MAX entry can be associated with each Packet. If there is * already an OB_LENGTH_MAX entry for the packet, the lower of the two * offsets will be used. Although you should check for OB_RET_OVERFLOW * when attempting to add an OB_LENGTH_MAX entry, the fixed size should * be more than enough space to store an entry for each possible packet * that could be in the system at the time. * * Returns * OB_RET_SUCCESS on sucess * OB_RET_ERROR on error * OB_RET_OVERFLOW if there is no space left to add an entry */ ObRet (*addObfuscationEntry)(Packet *p, ob_size_t offset, ob_size_t length, ob_char_t ob_char); /* * Determines if there are any obfuscation entries associated with * the given Packet * * Arguments * Packet * * The Packet to check * * Returns * 1 if the packet requires obfuscation * 0 if it doesn't */ int (*payloadObfuscationRequired)(Packet *p); /* * Obfuscate the payload associated with the Packet. Mainly for use by the * output system to print or log an obfuscated payload. The callback will * be called for both payload segments that need obfuscation and those that * don't. See comment on ObfuscationCallback above. * * Arguments * Packet * * The Packet whose payload should be obfuscated * ObfuscationCallback * The function that will be called for each obfuscated and * non-obfuscated segment in the payload * void * * User data that will be passed to the callback * * Returns * OB_RET_SUCCESS on sucess * OB_RET_ERROR on error */ ObRet (*obfuscatePacket)(Packet *p, ObfuscationCallback callback, void *user_data); /* * Obfuscate the stream segments associated with the Packet. Mainly for use * by the output system to print or log the stream segments associated with * a Packet that have been marked as needing obfuscation. The callback will * be called for both stream segments that need obfuscation and those that * don't. It will be called for all stream segments. See comment on * ObfuscationCallback above. * * Arguments * Packet * * The Packet whose stream segments should be obfuscated * ObfuscationCallback * The function that will be called for each obfuscated and * non-obfuscated part of the stream segments. * void * * User data that will be passed to the callback * * Returns * OB_RET_SUCCESS on sucess * OB_RET_ERROR on error */ ObRet (*obfuscatePacketStreamSegments)(Packet *p, ObfuscationCallback callback, void *user_data); /* * Obfuscates the Packet payload and returns payload and payload length * in parameters * * NOTE * *payload will be set to NULL, so don't pass in an already * allocated pointer. * *payload_len will be zeroed. * * The payload returned is dynamically allocated and MUST be free'd. * * Arguments * Packet * * The Packet whose payload should be obfuscated * uint8_t **payload * A pointer to a payload pointer so it can be allocated, returned * and accessed. * ob_size_t *payload_len * A pointer to an ob_size_t so the length can be returned. * * Returns * OB_RET_ERROR if the payload could not be obfuscated * the pointers to payload and payload_len will not be valid * OB_RET_SUCCESS if the payload was obfuscated * the pointers to payload and payload_len will be valid */ ObRet (*getObfuscatedPayload)(Packet *p, uint8_t **payload, ob_size_t *payload_len); /* * Prints the current obfuscation entries. * * Arguments * int sorted * Print the sorted entries and sort if necessary. * * Returns * None */ void (*printObfuscationEntries)(int sorted); } ObfuscationApi; /* For access when including header */ extern ObfuscationApi *obApi; #endif /* __OBFUSCATION_H__ */ snort-2.9.20/src/treenodes.h0000644000175000017500000001227014241077435014032 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* We moved the OptTreeNode and RuleTreeNode here to make them easier to include in dynamic preprocessors. */ #ifndef TREENODES_H #define TREENODES_H #include "rules.h" #include "plugin_enum.h" #include "rule_option_types.h" struct _OptTreeNode; /* forward declaration of OTN data struct */ struct _RuleTreeNode; /* forward declaration of RTN data struct */ /* same as the rule header FP list */ typedef struct _OptFpList { /* context data for this test */ void *context; int (*OptTestFunc)(void *option_data, Packet *p); struct _OptFpList *next; unsigned char isRelative; option_type_t type; } OptFpList; typedef struct _OptTreeNode { /* plugin/detection functions go here */ OptFpList *opt_func; RspFpList *rsp_func; /* response functions */ OutputFuncNode *outputFuncs; /* per sid enabled output functions */ /* the ds_list is absolutely essential for the plugin system to work, it allows the plugin authors to associate "dynamic" data structures with the rule system, letting them link anything they can come up with to the rules list */ void *ds_list[PLUGIN_MAX]; /* list of plugin data struct pointers */ int chain_node_number; int evalIndex; /* where this rule sits in the evaluation sets */ int proto; /* protocol, added for integrity checks during rule parsing */ int session_flag; /* record session data */ char *logto; /* log file in which to write packets which match this rule*/ /* metadata about signature */ SigInfo sigInfo; uint8_t stateless; /* this rule can fire regardless of session state */ uint8_t established; /* this rule can only fire if it is established */ uint8_t unestablished; Event event_data; void* detection_filter; /* if present, evaluated last, after header checks */ TagData *tag; /* stuff for dynamic rules activation/deactivation */ int active_flag; int activation_counter; int countdown; int activates; int activated_by; struct _OptTreeNode *OTN_activation_ptr; struct _RuleTreeNode *RTN_activation_ptr; struct _OptTreeNode *next; struct _OptTreeNode *nextSoid; /* ptr to list of RTNs (head part) */ struct _RuleTreeNode **proto_nodes; /**number of proto_nodes. */ unsigned short proto_node_num; uint8_t failedCheckBits; char generated; uint16_t longestPatternLen; int rule_state; /* Enabled or Disabled */ #ifdef PERF_PROFILING uint64_t ticks; uint64_t ticks_match; uint64_t ticks_no_match; uint64_t checks; uint64_t matches; uint64_t alerts; uint8_t noalerts; #endif int pcre_flag; /* PPM */ uint64_t ppm_suspend_time; /* PPM */ uint64_t ppm_disable_cnt; /*PPM */ uint32_t num_detection_opts; /**unique index generated in ruleIndexMap. */ int ruleIndex; /* List of preprocessor registered fast pattern contents */ void *preproc_fp_list; } OptTreeNode; /* function pointer list for rule head nodes */ typedef struct _RuleFpList { /* context data for this test */ void *context; /* rule check function pointer */ int (*RuleHeadFunc)(Packet *, struct _RuleTreeNode *, struct _RuleFpList *, int); /* pointer to the next rule function node */ struct _RuleFpList *next; } RuleFpList; typedef struct _RuleTreeNode { RuleFpList *rule_func; /* match functions.. (Bidirectional etc.. ) */ int head_node_number; RuleType type; IpAddrSet *sip; IpAddrSet *dip; int proto; PortObject * src_portobject; PortObject * dst_portobject; uint32_t flags; /* control flags */ #if 0 struct _RuleTreeNode *right; /* ptr to the next RTN in the list */ /** list of rule options to associate with this rule node */ OptTreeNode *down; #endif /**points to global parent RTN list (Drop/Alert) which contains this * RTN. */ struct _ListHead *listhead; /**reference count from otn. Multiple OTNs can reference this RTN with the same * policy. */ unsigned int otnRefCount; } RuleTreeNode; #endif /* TREENODES_H */ snort-2.9.20/src/idle_processing_funcs.h0000644000175000017500000000245114241076626016412 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _IDLE_PROCESSING_FUNCS_H #define _IDLE_PROCESSING_FUNCS_H #include "idle_processing.h" int IdleProcessingRegisterHandler(IdleProcessingHandler); void IdleProcessingExecute(void); void IdleProcessingCleanUp(void); #endif /* _IDLE_PROCESSING_FUNCS_H */ snort-2.9.20/src/pcrm.c0000644000175000017500000012670414241076723013005 0ustar apoapo/* ** $Id$ ** ** pcrm.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Marc Norton ** Dan Roelker ** ** NOTES ** 5.15.02 - Initial version of pcrm.c distributed. - Norton/Roelker ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* ** ** Packet Classificationa and Rule Manager ** ** ** A Fast Packet Classification method for Rule and Pattern Matching in SNORT ** -------------------------------------------------------------------------- ** ** A simple method for grouping rules into lists and looking them up quickly ** in realtime. ** ** There is a natural problem when aggregating rules into pattern groups for ** performing multi-pattern matching not seen with single pattern Boyer-Moore ** strategies. The problem is how to group the rules efficiently when ** considering that there are multiple parameters which govern what rules to ** apply to each packet or connection. The paramters, sip, dip, sport, dport, ** and flags form an enormous address space of possible packets that ** must be tested in realtime against a subset of rule patterns. Methods to ** group patterns precisely based on all of these parameters can quickly ** become complicated by both algorithmic implications and implementation ** details. The procedure described herein is quick and simple. ** ** The methodology presented here to solve this problem is based on the ** premise that we can use the source and destination ports to isolate ** pattern groups for pattern matching, and rely on an event validation ** procedure to authenticate other parameters such as sip, dip and flags after ** a pattern match is made. An instrinsic assumption here is that most sip ** and dip values will be acceptable and that the big gain in performance ** is due to the fact that by isolating traffic based on services (ports) ** we gain the most benefit. Additionally, and just as important, is the ** requirement that we can perform a multi-pattern recognition-inspection phase ** on a large set of patterns many times quicker than we can apply a single ** pattern test against many single patterns. ** ** The current implementation assumes that for each rule the src and dst ports ** each have one of 2 possible values. Either a specific port number or the ** ANYPORT designation. This does allow us to handle port ranges and NOT port ** rules as well. ** ** We make the following assumptions about classifying packets based on ports: ** ** 1) There are Unique ports which represent special services. For example, ** ports 21,25,80,110,etc. ** ** 2) Patterns can be grouped into Unique Pattern groups, and a Generic ** Pattern Group ** a) Unique pattern groups exist for source ports 21,25,80,110,etc. ** b) Unique pattern groups exist for destination ports 21,25,80,etc. ** c) A Generic pattern group exists for rules applied to every ** combination of source and destination ports. ** ** We make the following assumptions about packet traffic: ** ** 1) Well behaved traffic has one Unique port and one ephemeral port for ** most packets and sometimes legitimately, as in the case of DNS, has ** two unique ports that are the same. But we always determine that ** packets with two different but Unique ports is bogus, and should ** generate an alert. For example, if you have traffic going from ** port 80 to port 20. ** ** 2) In fact, state could tell us which side of this connection is a ** service and which side is a client. Than we could handle this packet ** more precisely, but this is a rare situation and is still bogus. We ** can choose not to do pattern inspections on these packets or to do ** complete inspections. ** ** Rules are placed into each group as follows: ** ** 1) Src Port == Unique Service, Dst Port == ANY -> Unique Src Port Table ** Src Port == Unique Service, Dst Port == ** Unique -> Unique Src & Dst Port Tables ** 2) Dst Port == Unqiue Service, Src Port == ANY -> Unique Dst Port Table ** Dst Port == Unqiue Service, Src Port == ** Unique -> Unique Dst & Src Port Tables ** 3) Dst Port == ANY, Src Port == ANY -> Generic Rule Set, ** And add to all Unique Src/Dst Rule Sets that have entries ** 4) !Dst or !Src Port is the same as ANY Dst or ANY Src port respectively ** 5) DstA:DstB is treated as an ANY port group, same for SrcA:SrcB ** ** Initialization ** -------------- ** For each rule check the dst-port, if it's specific, then add it to the ** dst table. If the dst-port is Any port, then do not add it to the dst ** port table. Repeat this for the src-port. ** ** If the rule has Any for both ports then it's added generic rule list. ** ** Also, fill in the Unique-Conflicts array, this indicates if it's OK to have ** the same Unique service port for both destination and source. This will ** force an alert if it's not ok. We optionally pattern match against this ** anyway. ** ** Processing Rules ** ----------------- ** When packets arrive: ** ** Categorize the Port Uniqueness: ** ** a)Check the DstPort[DstPort] for possible rules, ** if no entry,then no rules exist for this packet with this destination. ** ** b)Check the SrcPort[SrcPort] for possible rules, ** if no entry,then no rules exist for this packet with this source. ** ** Process the Uniqueness: ** ** If a AND !b has rules or !a AND b has rules then ** match against those rules ** ** If a AND b have rules then ** if( sourcePort != DstPort ) ** Alert on this traffic and optionally match both rule sets ** else if( SourcePort == DstPort ) ** Check the Unique-Conflicts array for allowable conflicts ** if( NOT allowed ) ** Alert on this traffic, optionally match the rules ** else ** match both sets of rules against this traffic ** ** If( !a AND ! b ) then ** Pattern Match against the Generic Rules ( these apply to all packets) ** ** ** example.c ** --------- ** ** PORT_RULE_MAP * prm; ** PORT_GROUP *src, *dst, *generic; ** ** RULE * prule; //user defined rule structure for user rules ** ** prm = prmNewMap(); ** ** for( each rule ) ** { ** prule = ....get a rule pointer ** ** prmAddRule( prm, prule->dport, prule->sport, prule ); ** } ** ** prmCompileGroups( prm ); ** ** while( sniff-packets ) ** { ** .... ** ** stat = prmFindRuleGroup( prm, dport, sport, &src, &dst, &generic ); ** switch( stat ) ** { ** case 0: // No rules at all ** break; ** case 1: // Dst Rules ** // pass 'dst->pgPatData', 'dst->pgPatDataUri' to the pattern engine ** break; ** case 2: // Src Rules ** // pass 'src->pgPatData', 'src->pgPatDataUri' to the pattern engine ** break; ** case 3: // Src/Dst Rules - Both ports represent Unique service ports ** // pass 'src->pgPatData' ,'src->pgPatDataUri' to the pattern engine ** // pass 'dst->pgPatData' 'src->pgPatDataUri' to the pattern engine ** break; ** case 4: // Generic Rules Only ** // pass 'generic->pgPatData' to the pattern engine ** // pass 'generic->pgPatDataUri' to the pattern engine ** break; ** } ** } ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "pcrm.h" #include "util.h" #include "fpcreate.h" #include "snort.h" /* ** ** NAME ** prmNewMap:: ** ** DESCRIPTION ** Allocate new PORT_RULE_MAP and return pointer. ** ** FORMAL INPUTS ** None ** ** FORMAL OUTPUT ** PORT_RULE_MAP * - NULL if failed, ptr otherwise. ** */ PORT_RULE_MAP * prmNewMap(void) { PORT_RULE_MAP * p; p = (PORT_RULE_MAP *)calloc(1, sizeof(PORT_RULE_MAP) ); return p; } /* ** ** NAME ** prmNewByteMap:: ** ** DESCRIPTION ** Allocate new BYTE_RULE_MAP and return pointer. ** ** FORMAL INPUTS ** None ** ** FORMAL OUTPUT ** BYTE_RULE_MAP * - NULL if failed, ptr otherwise. ** */ BYTE_RULE_MAP * prmNewByteMap(void) { BYTE_RULE_MAP * p; p = (BYTE_RULE_MAP *)calloc(1, sizeof(BYTE_RULE_MAP) ); return p; } /* ** ** NAME ** prmxFreeGroup:: ** ** DESCRIPTION ** Frees a PORT_GROUP of it's RuleNodes. ** ** FORMAL INPUTS ** PORT_GROUP * - port group to free ** ** FORMAL OUTPUT ** None ** */ static void prmxFreeGroup(PORT_GROUP *pg) { RULE_NODE * rn, *rx; rn = pg->pgHead; while( rn ) { rx = rn->rnNext; free( rn ); rn = rx; } pg->pgHead = NULL; rn = pg->pgHeadNC; while( rn ) { rx = rn->rnNext; free( rn ); rn = rx; } pg->pgHeadNC = NULL; rn = pg->pgUriHead; while( rn ) { rx = rn->rnNext; free( rn ); rn = rx; } pg->pgUriHead = NULL; } /* ** ** NAME ** prmFreeMap ** ** DESCRIPTION ** Frees the memory utilized by a PORT_RULE_MAP. ** ** FORMAL INPUTS ** PORT_RULE_MAP * - PORT_RULE_MAP to free ** ** FORMAL OUTPUT ** None ** */ void prmFreeMap( PORT_RULE_MAP * p ) { int i; if( p ) { for(i=0;iprmSrcPort[i]) { prmxFreeGroup( p->prmSrcPort[i] ); free(p->prmSrcPort[i]); } } for(i=0;iprmDstPort[i]) { prmxFreeGroup( p->prmDstPort[i] ); free(p->prmDstPort[i]); } } if(p->prmGeneric) { prmxFreeGroup( p->prmGeneric ); free(p->prmGeneric); } free( p ); } } /* ** ** NAME ** prmFreeByteMap ** ** DESCRIPTION ** Frees the memory utilized by a BYTE_RULE_MAP. ** ** FORMAL INPUTS ** BYTE_RULE_MAP * - BYTE_RULE_MAP to free ** ** FORMAL OUTPUT ** None ** */ void prmFreeByteMap( BYTE_RULE_MAP * p ) { int i; if( p ) { for(i=0;i<256;i++) { prmxFreeGroup( &p->prmByteGroup[i] ); } prmxFreeGroup( &p->prmGeneric ); free( p ); } } /* ** ** NAME ** prmxAddPortRule:: ** ** DESCRIPTION ** Adds a RULE_NODE to a PORT_GROUP. This particular ** function is specific in that it adds "content" rules. ** A "content" rule is a snort rule that has a content ** flag. ** ** Each RULE_NODE in a PORT_GROUP is given a RULE_NODE ** ID. This allows us to track particulars as to what ** rules have been alerted upon, and allows other neat ** things like correlating events on different streams. ** The RULE_NODE IDs may not be consecutive, because ** we can add RULE_NODES into "content", "uri", and ** "no content" lists. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to add the rule to. ** RULE_PTR - void ptr to the user information ** ** FORMAL OUTPUT ** int - 0 is successful, 1 is failure ** */ //static int prmxAddPortRule( PORT_GROUP *p, RULE_PTR rd ) { if( !p->pgHead ) { p->pgHead = (RULE_NODE*) calloc( 1,sizeof(RULE_NODE) ); if( !p->pgHead )return 1; p->pgHead->rnNext = 0; p->pgHead->rnRuleData = rd; p->pgTail = p->pgHead; } else { p->pgTail->rnNext = (RULE_NODE*)calloc( 1,sizeof(RULE_NODE) ); if(!p->pgTail->rnNext)return 1; p->pgTail = p->pgTail->rnNext; p->pgTail->rnNext = 0; p->pgTail->rnRuleData = rd; } /* ** Set RULE_NODE ID to unique identifier */ p->pgTail->iRuleNodeID = p->pgCount; /* ** Update the total Rule Node Count for this PORT_GROUP */ p->pgCount++; p->pgContentCount++; return 0; } /* ** ** NAME ** prmxAddPortRuleUri:: ** ** DESCRIPTION ** Adds a RULE_NODE to a PORT_GROUP. This particular ** function is specific in that it adds "uri" rules. ** A "uri" rule is a snort rule that has a uri ** flag. ** ** Each RULE_NODE in a PORT_GROUP is given a RULE_NODE ** ID. This allows us to track particulars as to what ** rules have been alerted upon, and allows other neat ** things like correlating events on different streams. ** The RULE_NODE IDs may not be consecutive, because ** we can add RULE_NODES into "content", "uri", and ** "no content" lists. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to add the rule to. ** RULE_PTR - void ptr to the user information ** ** FORMAL OUTPUT ** int - 0 is successful, 1 is failure ** */ //static int prmxAddPortRuleUri( PORT_GROUP *p, RULE_PTR rd ) { if( !p->pgUriHead ) { p->pgUriHead = (RULE_NODE*) calloc(1, sizeof(RULE_NODE) ); if( !p->pgUriHead ) return 1; p->pgUriTail = p->pgUriHead; p->pgUriHead->rnNext = 0; p->pgUriHead->rnRuleData = rd; } else { p->pgUriTail->rnNext = (RULE_NODE*)calloc(1, sizeof(RULE_NODE) ); if( !p->pgUriTail->rnNext) return 1; p->pgUriTail = p->pgUriTail->rnNext; p->pgUriTail->rnNext = 0; p->pgUriTail->rnRuleData = rd; } /* ** Set RULE_NODE ID to unique identifier */ p->pgUriTail->iRuleNodeID = p->pgCount; /* ** Update the total Rule Node Count for this PORT_GROUP */ p->pgCount++; p->pgUriContentCount++; return 0; } /* ** ** NAME ** prmxAddPortRuleNC:: ** ** DESCRIPTION ** Adds a RULE_NODE to a PORT_GROUP. This particular ** function is specific in that it adds "no content" rules. ** A "no content" rule is a snort rule that has no "content" ** or "uri" flag, and hence does not need to be pattern ** matched. ** ** Each RULE_NODE in a PORT_GROUP is given a RULE_NODE ** ID. This allows us to track particulars as to what ** rules have been alerted upon, and allows other neat ** things like correlating events on different streams. ** The RULE_NODE IDs may not be consecutive, because ** we can add RULE_NODES into "content", "uri", and ** "no content" lists. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to add the rule to. ** RULE_PTR - void ptr to the user information ** ** FORMAL OUTPUT ** int - 0 is successful, 1 is failure ** */ //static int prmxAddPortRuleNC( PORT_GROUP *p, RULE_PTR rd ) { if( !p->pgHeadNC ) { p->pgHeadNC = (RULE_NODE*) calloc( 1,sizeof(RULE_NODE) ); if( !p->pgHeadNC )return 1; p->pgTailNC = p->pgHeadNC; p->pgHeadNC->rnNext = 0; p->pgHeadNC->rnRuleData = rd; } else { p->pgTailNC->rnNext = (RULE_NODE*)calloc( 1,sizeof(RULE_NODE) ); if(!p->pgTailNC->rnNext)return 1; p->pgTailNC = p->pgTailNC->rnNext; p->pgTailNC->rnNext = 0; p->pgTailNC->rnRuleData = rd; } /* ** Set RULE_NODE ID to unique identifier */ p->pgTailNC->iRuleNodeID = p->pgCount; /* ** Update the Total Rule Node Count for this PORT_GROUP */ p->pgCount++; p->pgNoContentCount++; return 0; } /* ** ** NAME ** prmAddNotNode:: ** ** DESCRIPTION ** NOT SUPPORTED YET. Build a list of pur NOT nodes i.e. content !"this" ** content:!"that". ** */ void prmAddNotNode( PORT_GROUP * pg, int id ) { NOT_RULE_NODE * p = calloc(1,sizeof( NOT_RULE_NODE)); if( !p ) return ; p->iPos = id; if( !pg->pgNotRuleList ) { pg->pgNotRuleList = p; p->next = 0; } else { p->next = pg->pgNotRuleList; pg->pgNotRuleList = p; } } /* ** ** NAME ** prmGetFirstRule:: ** ** DESCRIPTION ** This function returns the first rule user data in ** the "content" list of a PORT_GROUP. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to retrieve data from. ** ** FORMAL OUTPUT ** RULE_PTR - the ptr to the user data. ** */ RULE_PTR prmGetFirstRule( PORT_GROUP * pg ) { pg->pgCur = pg->pgHead; if( !pg->pgCur ) return 0; return pg->pgCur->rnRuleData; } /* ** ** NAME ** prmGetNextRule:: ** ** DESCRIPTION ** Gets the next "content" rule. This function allows easy ** walking of the "content" rule list. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to retrieve data from. ** ** FORMAL OUTPUT ** RULE_PTR - ptr to the user data ** */ RULE_PTR prmGetNextRule( PORT_GROUP * pg ) { if( pg->pgCur ) pg->pgCur = pg->pgCur->rnNext; if( !pg->pgCur ) return 0; return pg->pgCur->rnRuleData; } /* ** ** NAME ** prmGetFirstRuleUri:: ** ** DESCRIPTION ** This function returns the first rule user data in ** the "uri" list of a PORT_GROUP. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to retrieve data from. ** ** FORMAL OUTPUT ** RULE_PTR - the ptr to the user data. ** */ RULE_PTR prmGetFirstRuleUri( PORT_GROUP * pg ) { pg->pgUriCur = pg->pgUriHead; if( !pg->pgUriCur ) return 0; return pg->pgUriCur->rnRuleData; } /* ** ** NAME ** prmGetNextRuleUri:: ** ** DESCRIPTION ** Gets the next "uri" rule. This function allows easy ** walking of the "uri" rule list. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to retrieve data from. ** ** FORMAL OUTPUT ** RULE_PTR - ptr to the user data ** */ RULE_PTR prmGetNextRuleUri( PORT_GROUP * pg ) { if( pg->pgUriCur ) pg->pgUriCur = pg->pgUriCur->rnNext; if( !pg->pgUriCur ) return 0; return pg->pgUriCur->rnRuleData; } /* ** ** NAME ** prmGetFirstRuleNC:: ** ** DESCRIPTION ** This function returns the first rule user data in ** the "no content" list of a PORT_GROUP. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to retrieve data from. ** ** FORMAL OUTPUT ** RULE_PTR - the ptr to the user data. ** */ RULE_PTR prmGetFirstRuleNC( PORT_GROUP * pg ) { pg->pgCurNC = pg->pgHeadNC; if( !pg->pgCurNC ) return 0; return pg->pgCurNC->rnRuleData; } /* ** ** NAME ** prmGetNextRuleNC:: ** ** DESCRIPTION ** Gets the next "no content" rule. This function allows easy ** walking of the "no content" rule list. ** ** FORMAL INPUTS ** PORT_GROUP * - PORT_GROUP to retrieve data from. ** ** FORMAL OUTPUT ** RULE_PTR - ptr to the user data ** */ RULE_PTR prmGetNextRuleNC( PORT_GROUP * pg ) { if( pg->pgCurNC ) pg->pgCurNC = pg->pgCurNC->rnNext; if( !pg->pgCurNC ) return 0; return pg->pgCurNC->rnRuleData; } /* ** ** NAME ** prmAddRule:: ** ** DESCRIPTION ** This function adds a rule to a PORT_RULE_MAP. Depending on the ** values of the sport and dport, the rule gets added in different ** groups (src,dst,generic). The values for dport and sport ** can be: 0 -> 64K or -1 for generic (meaning that the rule applies ** to all values. ** ** Warning: Consider this carefully. ** Some rules use 6000:6005 -> any for a port designation, we could ** add each rule to it's own group, in this case Src=6000 to 6005. ** But we opt to add them as ANY rules for now, to reduce groups. ** ** IMPORTANT: ** This function adds a rule to the "content" list of rules. ** ** FORMAL INPUTS ** PORT_RULE_MAP * - PORT_RULE_MAP to add rule to. ** int - the dst port value. ** int - the src port value. ** RULE_PTR - the ptr to the user data for the rule. ** ** FORMAL OUTPUT ** int - 0 is successful, 1 is failure. ** */ int prmAddRule( PORT_RULE_MAP * p, int dport, int sport, RULE_PTR rd ) { if( dport != ANYPORT && dport < MAX_PORTS ) /* dst=21,25,80,110,139 */ { p->prmNumDstRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmDstPort[dport] == NULL) { p->prmDstPort[dport] = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmDstPort[dport] == NULL) { return 1; } } if(p->prmDstPort[dport]->pgCount==0) p->prmNumDstGroups++; prmxAddPortRule( p->prmDstPort[ dport ], rd ); } if( sport != ANYPORT && sport < MAX_PORTS) /* src=ANY, SRC=80,21,25,etc. */ { p->prmNumSrcRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmSrcPort[sport] == NULL) { p->prmSrcPort[sport] = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmSrcPort[sport] == NULL) { return 1; } } if(p->prmSrcPort[sport]->pgCount==0) p->prmNumSrcGroups++; prmxAddPortRule( p->prmSrcPort[ sport ], rd ); } if( sport == ANYPORT && dport == ANYPORT) /* dst=ANY, src=ANY */ { p->prmNumGenericRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmGeneric == NULL) { p->prmGeneric = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmGeneric == NULL) { return 1; } } prmxAddPortRule( p->prmGeneric, rd ); } return 0; } int prmAddByteRule( BYTE_RULE_MAP * p, int dport, RULE_PTR rd ) { if( dport != ANYPORT && dport < 256 ) /* dst=21,25,80,110,139 */ { p->prmNumRules++; if( p->prmByteGroup[dport].pgCount==0 ) p->prmNumGroups++; prmxAddPortRule( &(p->prmByteGroup[ dport ]), rd ); } else if( dport == ANYPORT ) /* dst=ANY, src=ANY */ { p->prmNumGenericRules++; prmxAddPortRule( &(p->prmGeneric), rd ); } return 0; } /* ** ** NAME ** prmAddRuleUri:: ** ** DESCRIPTION ** This function adds a rule to a PORT_RULE_MAP. Depending on the ** values of the sport and dport, the rule gets added in different ** groups (src,dst,generic). The values for dport and sport ** can be: 0 -> 64K or -1 for generic (meaning that the rule applies ** to all values. ** ** Warning: Consider this carefully. ** Some rules use 6000:6005 -> any for a port designation, we could ** add each rule to it's own group, in this case Src=6000 to 6005. ** But we opt to add them as ANY rules for now, to reduce groups. ** ** IMPORTANT: ** This function adds a rule to the "uri" list of rules. ** ** FORMAL INPUTS ** PORT_RULE_MAP * - PORT_RULE_MAP to add rule to. ** int - the dst port value. ** int - the src port value. ** RULE_PTR - the ptr to the user data for the rule. ** ** FORMAL OUTPUT ** int - 0 is successful, 1 is failure. ** */ int prmAddRuleUri( PORT_RULE_MAP * p, int dport, int sport, RULE_PTR rd ) { if( dport != ANYPORT && dport < MAX_PORTS ) /* dst=21,25,80,110,139 */ { p->prmNumDstRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmDstPort[dport] == NULL) { p->prmDstPort[dport] = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmDstPort[dport] == NULL) { return 1; } } if(p->prmDstPort[dport]->pgCount==0) p->prmNumDstGroups++; prmxAddPortRuleUri( p->prmDstPort[ dport ], rd ); } if( sport != ANYPORT && sport < MAX_PORTS) /* src=ANY, SRC=80,21,25,etc. */ { p->prmNumSrcRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmSrcPort[sport] == NULL) { p->prmSrcPort[sport] = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmSrcPort[sport] == NULL) { return 1; } } if(p->prmSrcPort[sport]->pgCount==0) p->prmNumSrcGroups++; prmxAddPortRuleUri( p->prmSrcPort[ sport ], rd ); } if( sport == ANYPORT && dport == ANYPORT) /* dst=ANY, src=ANY */ { p->prmNumGenericRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmGeneric == NULL) { p->prmGeneric = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmGeneric == NULL) { return 1; } } prmxAddPortRuleUri( p->prmGeneric, rd ); } return 0; } /* ** ** NAME ** prmAddRuleNC:: ** ** DESCRIPTION ** This function adds a rule to a PORT_RULE_MAP. Depending on the ** values of the sport and dport, the rule gets added in different ** groups (src,dst,generic). The values for dport and sport ** can be: 0 -> 64K or -1 for generic (meaning that the rule applies ** to all values. ** ** Warning: Consider this carefully. ** Some rules use 6000:6005 -> any for a port designation, we could ** add each rule to it's own group, in this case Src=6000 to 6005. ** But we opt to add them as ANY rules for now, to reduce groups. ** ** IMPORTANT: ** This function adds a rule to the "no content" list of rules. ** ** FORMAL INPUTS ** PORT_RULE_MAP * - PORT_RULE_MAP to add rule to. ** int - the dst port value. ** int - the src port value. ** RULE_PTR - the ptr to the user data for the rule. ** ** FORMAL OUTPUT ** int - 0 is successful, 1 is failure. ** */ int prmAddRuleNC( PORT_RULE_MAP * p, int dport, int sport, RULE_PTR rd ) { if( dport != ANYPORT && dport < MAX_PORTS ) /* dst=21,25,80,110,139 */ { p->prmNumDstRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmDstPort[dport] == NULL) { p->prmDstPort[dport] = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmDstPort[dport] == NULL) { return 1; } } if(p->prmDstPort[dport]->pgCount==0) p->prmNumDstGroups++; prmxAddPortRuleNC( p->prmDstPort[ dport ], rd ); } if( sport != ANYPORT && sport < MAX_PORTS) /* src=ANY, SRC=80,21,25,etc. */ { p->prmNumSrcRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmSrcPort[sport] == NULL) { p->prmSrcPort[sport] = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmSrcPort[sport] == NULL) { return 1; } } if(p->prmSrcPort[sport]->pgCount==0) p->prmNumSrcGroups++; prmxAddPortRuleNC( p->prmSrcPort[ sport ], rd ); } if( sport == ANYPORT && dport == ANYPORT) /* dst=ANY, src=ANY */ { p->prmNumGenericRules++; /* ** Check to see if this PORT_GROUP has been initialized */ if(p->prmGeneric == NULL) { p->prmGeneric = (PORT_GROUP *)calloc(1, sizeof(PORT_GROUP)); if(p->prmGeneric == NULL) { return 1; } } prmxAddPortRuleNC( p->prmGeneric, rd ); } return 0; } int prmAddByteRuleNC( BYTE_RULE_MAP * p, int dport, RULE_PTR rd ) { if( dport != ANYPORT && dport < 256 ) /* dst=21,25,80,110,139 */ { p->prmNumRules++; if(p->prmByteGroup[dport].pgCount==0) p->prmNumGroups++; prmxAddPortRuleNC( &(p->prmByteGroup[ dport ]), rd ); } else if( dport == ANYPORT) /* dst=ANY, src=ANY */ { p->prmNumGenericRules++; prmxAddPortRuleNC( &(p->prmGeneric), rd ); } return 0; } /* ** ** NAME ** prmFindRuleGroup:: ** ** DESCRIPTION ** Given a PORT_RULE_MAP, this function selects the PORT_GROUP or ** PORT_GROUPs necessary to fully match a given dport, sport pair. ** The selection logic looks at both the dport and sport and ** determines if one or both are unique. If one is unique, then ** the appropriate PORT_GROUP ptr is set. If both are unique, then ** both th src and dst PORT_GROUP ptrs are set. If neither of the ** ports are unique, then the gen PORT_GROUP ptr is set. ** ** FORMAL INPUTS ** PORT_RULE_MAP * - the PORT_RULE_MAP to pick PORT_GROUPs from. ** int - the dst port value (0->64K or -1 for generic) ** int - the src port value (0->64K or -1 for generic) ** PORT_GROUP ** - the src PORT_GROUP ptr to set. ** PORT_GROUP ** - the dst PORT_GROUP ptr to set. ** PORT_GROUP ** - the generic PORT_GROUP ptr to set. ** ** FORMAL OUTPUT ** int - 0: Don't evaluate ** 1: There are port groups to evaluate ** ** NOTES ** Currently, if there is a "unique conflict", we return both the src ** and dst PORT_GROUPs. This conflict forces us to do two searches, one ** for the src and one for the dst. So we are taking twice the time to ** inspect a packet then usual. Obviously, this is not good. There ** are several options that we have to deal with unique conflicts, but ** have not implemented any currently. The optimum solution will be to ** incorporate streaming and protocol analysis to a session so we know ** what to match against. ** */ int prmFindRuleGroup( PORT_RULE_MAP *p, int dport, int sport, PORT_GROUP **src, PORT_GROUP **dst, #ifdef TARGET_BASED PORT_GROUP **ns_src, PORT_GROUP **ns_dst, #endif PORT_GROUP **gen ) { if (!p || !src || !dst || !gen) return 0; *src = NULL; *dst = NULL; *gen = NULL; #ifdef TARGET_BASED if ( !ns_src || !ns_dst ) return 0; *ns_src = NULL; *ns_dst = NULL; #endif if (dport != ANYPORT && dport < MAX_PORTS) { *dst = p->prmDstPort[dport]; #ifdef TARGET_BASED *ns_dst = p->prmNoServiceDstPort[dport]; #endif } if (sport != ANYPORT && sport < MAX_PORTS) { *src = p->prmSrcPort[sport]; #ifdef TARGET_BASED *ns_src = p->prmNoServiceSrcPort[sport]; #endif } /* If no Src/Dst rules - use the generic set, if any exist */ if (p->prmGeneric != NULL && p->prmGeneric->pgCount > 0) { if (fpDetectSplitAnyAny(snort_conf->fast_pattern_config) || (!*src && !*dst)) { *gen = p->prmGeneric; } } if (*src == NULL && *dst == NULL && *gen == NULL) { #ifdef TARGET_BASED if (*ns_src == NULL && *ns_dst == NULL) #endif { return 0; } } return 1; } #ifdef TARGET_BASED int prmFindNoServiceRuleGroup ( PORT_RULE_MAP *p, int dport, int sport, PORT_GROUP ** src, PORT_GROUP ** dst, PORT_GROUP ** gen ) { if (!p || !src || !dst) return 0; *src = NULL; *dst = NULL; *gen = NULL; if (dport != ANYPORT && dport < MAX_PORTS) *dst = p->prmNoServiceDstPort[dport]; if (sport != ANYPORT && sport < MAX_PORTS) *src = p->prmNoServiceSrcPort[sport]; if (p->prmGeneric != NULL && p->prmGeneric->pgCount > 0) { if (fpDetectSplitAnyAny(snort_conf->fast_pattern_config) || (!*src && !*dst)) { *gen = p->prmGeneric; } } if (*src || *dst || *gen) return 1; return 0; } #endif int prmFindGenericRuleGroup(PORT_RULE_MAP *p, PORT_GROUP ** gen) { if (gen == NULL) { return 0; } *gen = NULL; if ((p->prmGeneric != NULL) && (p->prmGeneric->pgCount > 0)) { if (fpDetectSplitAnyAny(snort_conf->fast_pattern_config)) { *gen = p->prmGeneric; return 1; } } return 0; } /* * */ int prmFindByteRuleGroup( BYTE_RULE_MAP * p, int dport, PORT_GROUP **dst , PORT_GROUP ** gen) { int stat= 0; if( (dport != ANYPORT && dport < 256 ) && p->prmByteGroup[dport].pgCount ) { *dst = &p->prmByteGroup[dport]; stat = 1; }else{ *dst=0; } /* If no Src/Dst rules - use the generic set, if any exist */ if( !stat && (p->prmGeneric.pgCount > 0) ) { *gen = &p->prmGeneric; stat = 4; }else{ *gen = 0; } return stat; } /* ** Access each Rule group by index (0-MAX_PORTS) */ PORT_GROUP * prmFindDstRuleGroup( PORT_RULE_MAP * p, int port ) { if( port < 0 || port >= MAX_PORTS ) return 0; if( p->prmDstPort[port]) return p->prmDstPort[port]; return 0; } /* ** Access each Rule group by index (0-MAX_PORTS) */ PORT_GROUP * prmFindSrcRuleGroup( PORT_RULE_MAP * p, int port ) { if( port < 0 || port >= MAX_PORTS ) return 0; if( p->prmSrcPort[port]) return p->prmSrcPort[port]; return 0; } /* ** Assign the pattern matching data to this group */ int prmSetGroupPatData( PORT_GROUP * pg, void * data ) { pg->pgPms[PM_TYPE__CONTENT] = data; return 0; } /* ** Get the patttern matching data for this group */ void * prmGetGroupPatData( PORT_GROUP * pg ) { return pg->pgPms[PM_TYPE__CONTENT]; } /* ** ** NAME ** prmCompileGroups:: ** ** DESCRIPTION ** Add Generic rules to each Unique rule group, this could be ** optimized a bit, right now we will process generic rules ** twice when packets have 2 unique ports, but this will not ** occur often. ** ** The generic rules are added to the Unique rule groups, so that ** the setwise methodology can be taking advantage of. ** ** FORMAL INPUTS ** PORT_RULE_MAP * - the PORT_RULE_MAP to compile generice rules. ** ** FORMAL OUTPUT ** int - 0 is successful; ** */ int prmCompileGroups( PORT_RULE_MAP * p ) { PORT_GROUP *pgGen, *pgSrc, *pgDst; RULE_PTR *prule; int i; /* ** Add Generic to Src and Dst groups */ pgGen = p->prmGeneric; if(!pgGen) return 0; for(i=0;iprmSrcPort[i]) { pgSrc = p->prmSrcPort[i]; prule = prmGetFirstRule( pgGen ); while( prule ) { prmxAddPortRule( pgSrc, prule ); prule = prmGetNextRule( pgGen ); } prule = prmGetFirstRuleUri( pgGen ); while( prule ) { prmxAddPortRuleUri( pgSrc, prule ); prule = prmGetNextRuleUri( pgGen ); } prule = prmGetFirstRuleNC( pgGen ); while( prule ) { prmxAddPortRuleNC( pgSrc, prule ); prule = prmGetNextRuleNC( pgGen ); } } if(p->prmDstPort[i]) { pgDst = p->prmDstPort[i]; prule = prmGetFirstRule( pgGen ); while( prule ) { prmxAddPortRule( pgDst, prule ); prule = prmGetNextRule( pgGen ); } prule = prmGetFirstRuleUri( pgGen ); while( prule ) { prmxAddPortRuleUri( pgDst, prule ); prule = prmGetNextRuleUri( pgGen ); } prule = prmGetFirstRuleNC( pgGen ); while( prule ) { prmxAddPortRuleNC( pgDst, prule ); prule = prmGetNextRuleNC( pgGen ); } } #ifdef TARGET_BASED if(p->prmNoServiceDstPort[i]) { pgDst = p->prmNoServiceDstPort [i]; for (prule = prmGetFirstRule (pgGen); prule; prule = prmGetNextRule (pgGen)) prmxAddPortRule (pgDst, prule); for (prule = prmGetFirstRuleUri (pgGen); prule; prule = prmGetNextRuleUri (pgGen)) prmxAddPortRuleUri (pgDst, prule); for (prule = prmGetFirstRuleNC (pgGen); prule; prule = prmGetNextRuleNC (pgGen)) prmxAddPortRuleNC (pgDst, prule); } if(p->prmNoServiceSrcPort[i]) { pgSrc = p->prmNoServiceSrcPort [i]; for (prule = prmGetFirstRule (pgGen); prule; prule = prmGetNextRule (pgGen)) prmxAddPortRule (pgSrc, prule); for (prule = prmGetFirstRuleUri (pgGen); prule; prule = prmGetNextRuleUri (pgGen)) prmxAddPortRuleUri (pgSrc, prule); for (prule = prmGetFirstRuleNC (pgGen); prule; prule = prmGetNextRuleNC (pgGen)) prmxAddPortRuleNC (pgSrc, prule); } #endif // TARGET_BASED } return 0; } /* * * */ int prmCompileByteGroups( BYTE_RULE_MAP * p ) { PORT_GROUP *pgGen, *pgByte; RULE_PTR *prule; int i; /* ** Add Generic to Unique groups */ pgGen = &p->prmGeneric; if( !pgGen->pgCount ) return 0; for(i=0;i<256;i++) { if(p->prmByteGroup[i].pgCount) { pgByte = &p->prmByteGroup[i]; prule = prmGetFirstRule( pgGen ); while( prule ) { prmxAddPortRule( pgByte, prule ); prule = prmGetNextRule( pgGen ); } prule = prmGetFirstRuleNC( pgGen ); while( prule ) { prmxAddPortRuleNC( pgByte, prule ); prule = prmGetNextRuleNC( pgGen ); } } } return 0; } /* ** ** NAME ** prmShowStats:: ** ** DESCRIPTION ** This function shows some basic stats on the fast packet ** classification. It show the the number of PORT_GROUPS ** for a PORT_RULE_MAP, and the break down of the different ** rule types (content, uri, no content). ** ** FORMAL INPUTS ** PORT_RULE_MAP * - the PORT_RULE_MAP to show stats on. ** ** FORMAL OUTPUT ** int - 0 is successful. ** */ int prmShowStats( PORT_RULE_MAP * p ) { int i; PORT_GROUP * pg; LogMessage("Packet Classification Rule Manager Stats ----\n"); LogMessage("NumDstGroups : %d\n",p->prmNumDstGroups); LogMessage("NumSrcGroups : %d\n",p->prmNumSrcGroups); LogMessage("\n"); LogMessage("NumDstRules : %d\n",p->prmNumDstRules); LogMessage("NumSrcRules : %d\n",p->prmNumSrcRules); LogMessage("NumGenericRules: %d\n",p->prmNumGenericRules); LogMessage("\n"); LogMessage("%d Dst Groups In Use, %d Unique Rules, includes generic\n",p->prmNumDstGroups,p->prmNumDstRules); for(i=0;ipgUriContentCount,pg->pgContentCount,pg->pgNoContentCount); if( pg->avgLen ) { LogMessage("MinLen=%d MaxLen=%d AvgLen=%d",pg->minLen,pg->maxLen,pg->avgLen); if(pg->c1)LogMessage(" [1]=%d",pg->c1); if(pg->c2)LogMessage(" [2]=%d",pg->c2); if(pg->c3)LogMessage(" [3]=%d",pg->c3); if(pg->c4)LogMessage(" [4]=%d",pg->c4); LogMessage("\n"); } } } LogMessage("%d Src Groups In Use, %d Unique Rules, includes generic\n",p->prmNumSrcGroups,p->prmNumSrcRules); for(i=0;ipgUriContentCount,pg->pgContentCount,pg->pgNoContentCount); if( pg->avgLen ) { LogMessage("MinLen=%d MaxLen=%d AvgLen=%d",pg->minLen,pg->maxLen,pg->avgLen); if(pg->c1)LogMessage(" [1]=%d",pg->c1); if(pg->c2)LogMessage(" [2]=%d",pg->c2); if(pg->c3)LogMessage(" [3]=%d",pg->c3); if(pg->c4)LogMessage(" [4]=%d",pg->c4); LogMessage("\n"); } } } pg = p->prmGeneric; if(pg){ LogMessage(" Generic Rules : %d uricontent, %d content, %d nocontent \n", pg->pgUriContentCount,pg->pgContentCount,pg->pgNoContentCount); if( pg->avgLen ) { LogMessage("MinLen=%d MaxLen=%d AvgLen=%d",pg->minLen,pg->maxLen,pg->avgLen); if(pg->c1)LogMessage(" [1]=%d",pg->c1); if(pg->c2)LogMessage(" [2]=%d",pg->c2); if(pg->c3)LogMessage(" [3]=%d",pg->c3); if(pg->c4)LogMessage(" [4]=%d",pg->c4); LogMessage("\n"); } } return 0; } /* ** ** NAME ** prmShowEventStats:: ** ** DESCRIPTION ** This function is used at the close of the Fast Packet ** inspection. It tells how many non-qualified and qualified ** hits occurred for each PORT_GROUP. A non-qualified hit ** is defined by an initial match against a packet, but upon ** further inspection a hit was not validated. Non-qualified ** hits occur because we can match on the most unique aspect ** of a packet, this is the content. Snort has other flags ** then content though, so once we hit a content match we must ** verify these additional flags. Sometimes these flags do ** not pass the validation. A qualified hit is an event that ** has been fully qualified, and has been put in the event ** cache for event selection. Qualified hits are not a subset ** of non-qualified hits. Ideally, non-qualified hits should ** be zero. The reason for these stats is that it allows ** users to trouble shoot PORT_GROUPs. A poorly written rule ** may cause many non-qualified events, and these stats ** allow the user to track this down. ** ** FORMAL INPUTS ** PORT_RULE_MAP * - the PORT_RULE_MAP to show stats on. ** ** FORMAL OUTPUT ** int - 0 is successful. ** */ int prmShowEventStats( PORT_RULE_MAP * p ) { int i; PORT_GROUP * pg; int NQEvents = 0; int QEvents = 0; LogMessage("Packet Classification Rule Manager Stats ----\n"); LogMessage("NumDstGroups : %d\n",p->prmNumDstGroups); LogMessage("NumSrcGroups : %d\n",p->prmNumSrcGroups); LogMessage("\n"); LogMessage("NumDstRules : %d\n",p->prmNumDstRules); LogMessage("NumSrcRules : %d\n",p->prmNumSrcRules); LogMessage("NumGenericRules: %d\n",p->prmNumGenericRules); LogMessage("\n"); LogMessage("%d Dst Groups In Use, %d Unique Rules, includes generic\n",p->prmNumDstGroups,p->prmNumDstRules); for(i=0;ipgNQEvents; QEvents += pg->pgQEvents; if( pg->pgNQEvents + pg->pgQEvents ) { LogMessage(" Dst Port %5d : %d group entries \n",i, pg->pgCount); LogMessage(" NQ Events : %d\n", pg->pgNQEvents); LogMessage(" Q Events : %d\n", pg->pgQEvents); } } } LogMessage("%d Src Groups In Use, %d Unique Rules, includes generic\n",p->prmNumSrcGroups,p->prmNumSrcRules); for(i=0;ipgNQEvents; QEvents += pg->pgQEvents; if( pg->pgNQEvents + pg->pgQEvents ) { LogMessage(" Src Port %5d : %d group entries \n",i, pg->pgCount); LogMessage(" NQ Events : %d\n", pg->pgNQEvents); LogMessage(" Q Events : %d\n", pg->pgQEvents); } } } pg = p->prmGeneric; if(pg) { NQEvents += pg->pgNQEvents; QEvents += pg->pgQEvents; if( pg->pgNQEvents + pg->pgQEvents ) { LogMessage(" Generic Rules : %d group entries\n", pg->pgCount); LogMessage(" NQ Events : %d\n", pg->pgNQEvents); LogMessage(" Q Events : %d\n", pg->pgQEvents); } } LogMessage("Total NQ Events : %d\n", NQEvents); LogMessage("Total Q Events : %d\n", QEvents); return 0; } snort-2.9.20/src/cpuclock.h0000644000175000017500000000625714241074712013650 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef CPU_CLOCK_TICKS_H #define CPU_CLOCK_TICKS_H /* Assembly to find clock ticks. */ #ifdef WIN32 #include /* INTEL WINDOWS */ __inline void __cputicks_msc(uint64_t *val) { __int64 t; __asm { rdtsc; mov dword PTR [t],eax; mov dword PTR [t+4],edx; } *val = (uint64_t)t; } #define get_clockticks(val) __cputicks_msc(&val) /* #define get_clockticks(val) \ QueryPerformanceCounter((PLARGE_INTEGER)&val) */ #else #include /* INTEL LINUX/BSD/.. */ #if (defined(__i386) || defined(__amd64) || defined(__x86_64__)) #define get_clockticks(val) \ { \ uint32_t a, d; \ __asm__ __volatile__ ("rdtsc" : "=a" (a), "=d" (d)); \ val = ((uint64_t)a) | (((uint64_t)d) << 32); \ } #else #if (defined(__ia64) && defined(__GNUC__) ) #define get_clockticks(val) \ { \ __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(val)); \ } #else #if (defined(__ia64) && defined(__hpux)) #include #define get_clockticks(val) \ { \ val = _Asm_mov_from_ar (_AREG_ITC); \ } #else /* POWER PC */ #if (defined(__GNUC__) && (defined(__powerpc__) || (defined(__ppc__)))) #define get_clockticks(val) \ { \ uint32_t tbu0, tbu1, tbl; \ do \ { \ __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0)); \ __asm__ __volatile__ ("mftb %0" : "=r"(tbl)); \ __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1)); \ } while (tbu0 != tbu1); \ val = ((uint64_t)tbl) | (((uint64_t)tbu0) << 32); \ } #else /* SPARC */ #ifdef SPARCV9 #ifdef _LP64 #define get_clockticks(val) \ { \ __asm__ __volatile__("rd %%tick, %0" : "=r"(val)); \ } #else #define get_clockticks(val) \ { \ uint32_t a, b; \ __asm__ __volatile__("rd %%tick, %0\n" \ "srlx %0, 32, %1" \ : "=r"(a), "=r"(b)); \ val = ((uint64_t)a) | (((uint64_t)b) << 32); \ } #endif /* _LP64 */ #else #define get_clockticks(val) #endif /* SPARC */ #endif /* POWERPC || PPC */ #endif /* IA64 && HPUX */ #endif /* IA64 && GNUC */ #endif /* I386 || AMD64 || X86_64 */ #endif /* WIN32 */ static inline double get_ticks_per_usec (void) { uint64_t start = 0, end = 0; get_clockticks(start); #ifdef WIN32 Sleep(1000); #else sleep(1); #endif get_clockticks(end); return (double)(end-start)/1e6; } #endif /* CPU_CLOCK_TICKS_H */ snort-2.9.20/src/snort.h0000644000175000017500000015273014241077406013213 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** Copyright (C) 1998-2005 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* $Id$ */ #ifndef __SNORT_H__ #define __SNORT_H__ /* I N C L U D E S **********************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "sf_types.h" #include "spo_plugbase.h" #include "decode.h" #include "perf.h" #include "sfdaq.h" #include "sf_types.h" #include "sfutil/sflsq.h" #include "sfutil/sfActionQueue.h" #include "profiler.h" #include "rules.h" #include "treenodes.h" #include "sfutil/sf_ipvar.h" #include "sfutil/sfghash.h" #include "sfutil/sfrim.h" #include "sfutil/sfportobject.h" #include "sfutil/asn1.h" #include "sfutil/sf_sechash.h" #include "signature.h" #include "event_queue.h" #include "sfthreshold.h" #include "fpcreate.h" #include "plugbase.h" #include "fpdetect.h" #include "ppm.h" #include "sfutil/sfrf.h" #include "sfutil/sfPolicy.h" #include "pkt_tracer.h" #include "detection_filter.h" #include "generators.h" #include "preprocids.h" #include #include "sf_dynamic_meta.h" #if defined(INLINE_FAILOPEN) || \ defined(TARGET_BASED) || defined(SNORT_RELOAD) # include #endif /* D E F I N E S ************************************************************/ /* Mark this as a modern version of snort */ #define SNORT_20 /* * The original Ethernet IEEE 802.3 standard defined the minimum Ethernet * frame size as 64 bytes. The snaplen is L2 MRU for snort and hence following * standard, the MIN_SNAPLEN should be 64. */ #define MIN_SNAPLEN 64 #define MAX_SNAPLEN UINT16_MAX #define MAX_IFS 1 #define TIMEBUF_SIZE 26 #define MAX_PIDFILE_SUFFIX 11 /* uniqueness extension to PID file, see '-R' */ #define ASSURE_ALL 0 /* all TCP alerts fire regardless of stream state */ #define ASSURE_EST 1 /* only established TCP sessions fire alerts */ /* This macro helps to simplify the differences between Win32 and non-Win32 code when printing out the name of the interface */ #ifndef WIN32 # define PRINT_INTERFACE(i) (i ? i : "NULL") #else # define PRINT_INTERFACE(i) print_interface(i) #endif #define RF_ANY_SIP 0x01 #define RF_ANY_DIP 0x02 #define RF_ANY_SP 0x04 #define RF_ANY_DP 0x10 #define RF_ANY_FLAGS 0x20 #ifndef WIN32 # define DEFAULT_LOG_DIR "/var/log/snort" # define DEFAULT_DAEMON_ALERT_FILE "alert" #else # define DEFAULT_LOG_DIR "log" # define DEFAULT_DAEMON_ALERT_FILE "log/alert.ids" #endif /* WIN32 */ /* you can redefine the user ID which is allowed to * initialize interfaces using pcap and read from them */ #ifndef SNIFFUSER # define SNIFFUSER 0 #endif #ifdef ACCESSPERMS # define FILEACCESSBITS ACCESSPERMS #else # ifdef S_IAMB # define FILEACCESSBITS S_IAMB # else # define FILEACCESSBITS 0x1FF # endif #endif #define DO_IP_CHECKSUMS 0x00000001 #define DO_TCP_CHECKSUMS 0x00000002 #define DO_UDP_CHECKSUMS 0x00000004 #define DO_ICMP_CHECKSUMS 0x00000008 #define LOG_UNIFIED 0x00000001 #define LOG_TCPDUMP 0x00000002 #define LOG_UNIFIED2 0x0000004 #ifndef SIGNAL_SNORT_RELOAD #define SIGNAL_SNORT_RELOAD SIGHUP #endif #ifndef SIGNAL_SNORT_DUMP_STATS #define SIGNAL_SNORT_DUMP_STATS SIGUSR1 #endif #ifndef SIGNAL_SNORT_ROTATE_STATS #define SIGNAL_SNORT_ROTATE_STATS SIGUSR2 #endif // this one should not be changed by user #define SIGNAL_SNORT_CHILD_READY SIGCHLD #ifdef TARGET_BASED #ifndef SIGNAL_SNORT_READ_ATTR_TBL # define SIGNAL_SNORT_READ_ATTR_TBL SIGURG #endif #endif #define MODE_PACKET_DUMP 1 #define MODE_PACKET_LOG 2 #define MODE_IDS 3 #define MODE_TEST 4 #define MODE_RULE_DUMP 5 #define MODE_VERSION 6 #define LOG_ASCII 1 #define LOG_PCAP 2 #define LOG_NONE 3 #define ALERT_FULL 1 #define ALERT_FAST 2 #define ALERT_NONE 3 #define ALERT_UNSOCK 4 #define ALERT_STDOUT 5 #define ALERT_CMG 6 #define ALERT_SYSLOG 8 #define ALERT_TEST 9 #define ALERT_UNIFIED 10 #ifdef MPLS # define MPLS_PAYLOADTYPE_IPV4 1 # define MPLS_PAYLOADTYPE_ETHERNET 2 # define MPLS_PAYLOADTYPE_IPV6 3 # define MPLS_PAYLOADTYPE_ERROR -1 # define DEFAULT_MPLS_PAYLOADTYPE MPLS_PAYLOADTYPE_IPV4 # define DEFAULT_LABELCHAIN_LENGTH -1 #endif /* This feature allows us to change the state of a rule, * independent of it appearing in a rules file. */ #define RULE_STATE_DISABLED 0 #define RULE_STATE_ENABLED 1 #define MAX_DYNAMIC_ENGINES 16 #define MAX_DYNAMIC_DETECTION_LIBS 16 #define MAX_DYNAMIC_PREPROC_LIBS 16 #ifdef TARGET_BASED # define ATTRIBUTE_TABLE_RELOAD_FLAG 0x01 # define ATTRIBUTE_TABLE_AVAILABLE_FLAG 0x02 # define ATTRIBUTE_TABLE_RELOADING_FLAG 0x04 # define ATTRIBUTE_TABLE_TAKEN_FLAG 0x08 # define ATTRIBUTE_TABLE_PARSE_FAILED_FLAG 0x10 # define DEFAULT_MAX_ATTRIBUTE_HOSTS 10000 # define DEFAULT_MAX_ATTRIBUTE_SERVICES_PER_HOST 100 # define DEFAULT_MAX_METADATA_SERVICES 8 # define MAX_MAX_ATTRIBUTE_HOSTS (512 * 1024) # define MIN_MAX_ATTRIBUTE_HOSTS 32 # define MAX_MAX_ATTRIBUTE_SERVICES_PER_HOST 65535 # define MIN_MAX_ATTRIBUTE_SERVICES_PER_HOST 1 # define MAX_MAX_METADATA_SERVICES 256 # define MIN_MAX_METADATA_SERVICES 1 #if defined(FEAT_OPEN_APPID) # define MAX_MAX_METADATA_APPID 256 # define MIN_MAX_METADATA_APPID 1 # define DEFAULT_MAX_METADATA_APPID 8 #endif /* defined(FEAT_OPEN_APPID) */ #endif # define DEFAULT_MAX_IP6_EXTENSIONS 8 struct _SnortConfig; typedef int (*InitDetectionLibFunc)(struct _SnortConfig *); /* D A T A S T R U C T U R E S *********************************************/ typedef struct _VarEntry { char *name; char *value; unsigned char flags; IpAddrSet *addrset; uint32_t id; struct _VarEntry *prev; struct _VarEntry *next; } VarEntry; /* GetoptLong Option numbers ********************/ typedef enum _GetOptLongIds { PID_PATH = 1, DYNAMIC_LIBRARY_DIRECTORY, DYNAMIC_LIBRARY_FILE, DYNAMIC_PREPROC_DIRECTORY, DYNAMIC_PREPROC_FILE, DYNAMIC_ENGINE_FILE, DYNAMIC_ENGINE_DIRECTORY, DUMP_DYNAMIC_RULES, DYNAMIC_OUTPUT_DIRECTORY, DYNAMIC_OUTPUT_FILE, CREATE_PID_FILE, TREAT_DROP_AS_ALERT, TREAT_DROP_AS_IGNORE, PROCESS_ALL_EVENTS, ALERT_BEFORE_PASS, NOLOCK_PID_FILE, NO_IFACE_PID_FILE, #ifdef INLINE_FAILOPEN DISABLE_INLINE_FAILOPEN, #endif NO_LOGGING_TIMESTAMPS, PCAP_LOOP, PCAP_SINGLE, PCAP_FILE_LIST, PCAP_LIST, PCAP_DIR, PCAP_FILTER, PCAP_NO_FILTER, PCAP_RELOAD, PCAP_RESET, PCAP_SHOW, #define EXIT_CHECK // allow for rollback for now #ifdef EXIT_CHECK ARG_EXIT_CHECK, #endif #ifdef TARGET_BASED DISABLE_ATTRIBUTE_RELOAD, #endif DETECTION_SEARCH_METHOD, CONF_ERROR_OUT, #ifdef MPLS ENABLE_MPLS_MULTICAST, ENABLE_OVERLAPPING_IP, MAX_MPLS_LABELCHAIN_LEN, MPLS_PAYLOAD_TYPE, #endif REQUIRE_RULE_SID, ARG_DAQ_TYPE, ARG_DAQ_MODE, ARG_DAQ_VAR, ARG_DAQ_DIR, ARG_DAQ_LIST, ARG_DIRTY_PIG, ENABLE_INLINE_TEST, ARG_CS_DIR, ARG_HA_PEER, ARG_HA_OUT, ARG_HA_IN, ARG_HA_PDTS_IN, SUPPRESS_CONFIG_LOG, #ifdef DUMP_BUFFER BUFFER_DUMP, BUFFER_DUMP_ALERT, #endif GET_OPT_LONG_IDS_MAX } GetOptLongIds; typedef struct _PreprocConfig { char *keyword; char *opts; char *file_name; int file_line; /* We have to configure internal and dynamic preprocessors separately, * mainly because of the stream_api which is set in stream5 and needs to * be set before calling the dynamic preprocessor initialization * functions which set _dpd and call the setup function. streamAPI is set * in the _dpd so stream5 needs to be configured first */ int configured; struct _PreprocConfig *next; } PreprocConfig; typedef struct _OutputConfig { char *keyword; char *opts; char *file_name; int file_line; ListHead *rule_list; struct _OutputConfig *next; } OutputConfig; #ifdef SIDE_CHANNEL typedef struct _SideChannelModuleConfig { char *keyword; char *opts; char *file_name; int file_line; struct _SideChannelModuleConfig *next; } SideChannelModuleConfig; typedef struct _SideChannelConfig { bool enabled; char *opts; SideChannelModuleConfig *module_configs; } SideChannelConfig; #endif typedef enum _DynamicType { DYNAMIC_TYPE__ENGINE, DYNAMIC_TYPE__DETECTION, DYNAMIC_TYPE__PREPROCESSOR, DYNAMIC_TYPE__SIDE_CHANNEL, DYNAMIC_TYPE__MAX } DynamicType; typedef enum _PathType { PATH_TYPE__FILE, PATH_TYPE__DIRECTORY } PathType; typedef struct _DynamicLibPath { PathType ptype; char *path; time_t last_mod_time; } DynamicLibPath; #define MAX_DYNAMIC_LIBS 16 typedef struct _DynamicLibInfo { DynamicType type; unsigned int count; DynamicLibPath *lib_paths[MAX_DYNAMIC_LIBS]; } DynamicLibInfo; typedef enum _RunMode { /* -V */ RUN_MODE__VERSION = 1, /* --dump-dynamic-rules */ RUN_MODE__RULE_DUMP, /* neither of the above and snort.conf presence (-c or implicit) */ RUN_MODE__IDS, /* snort.conf presence and -T */ RUN_MODE__TEST, /* neither -V or --dump-dynamic-rules and no snort.conf, but logging * enabled on command line - NONE type logging seems to count here */ RUN_MODE__PACKET_LOG, RUN_MODE__PACKET_DUMP } RunMode; typedef enum _RunModeFlag { /* -V */ RUN_MODE_FLAG__VERSION = 0x00000001, /* --dump-dynamic-rules */ RUN_MODE_FLAG__RULE_DUMP = 0x00000002, /* neither of the above and snort.conf presence (-c or implicit) */ RUN_MODE_FLAG__IDS = 0x00000004, /* snort.conf presence and -T */ RUN_MODE_FLAG__TEST = 0x00000008, /* neither -V or --dump-dynamic-rules and no snort.conf, but logging * enabled on command line - NONE type logging seems to count here */ RUN_MODE_FLAG__PACKET_LOG = 0x00000010, RUN_MODE_FLAG__PACKET_DUMP = 0x00000020 } RunModeFlag; typedef enum _RunFlag { RUN_FLAG__READ = 0x00000001, /* -r --pcap-dir, etc. */ RUN_FLAG__DAEMON = 0x00000002, /* -D */ RUN_FLAG__DAEMON_RESTART = 0x00000004, /* --restart */ RUN_FLAG__NO_PROMISCUOUS = 0x00000008, /* -p */ RUN_FLAG__INLINE = 0x00000010, /* -Q */ RUN_FLAG__STATIC_HASH = 0x00000020, /* -H */ RUN_FLAG__CREATE_PID_FILE = 0x00000040, /* --pid-path and --create-pidfile */ RUN_FLAG__NO_LOCK_PID_FILE = 0x00000080, /* --nolock-pidfile */ RUN_FLAG__TREAT_DROP_AS_ALERT = 0x00000100, /* --treat-drop-as-alert */ RUN_FLAG__ALERT_BEFORE_PASS = 0x00000200, /* --alert-before-pass */ RUN_FLAG__CONF_ERROR_OUT = 0x00000400, /* -x and --conf-error-out */ #ifdef MPLS RUN_FLAG__MPLS_MULTICAST = 0x00000800, /* --enable_mpls_multicast */ RUN_FLAG__MPLS_OVERLAPPING_IP = 0x00001000, /* --enable_mpls_overlapping_ip */ #endif /* --process-all-events * this is transferred to the snort event queue var */ RUN_FLAG__PROCESS_ALL_EVENTS = 0x00002000, #ifdef TARGET_BASED /* --disable-attribute-reload-thread */ RUN_FLAG__DISABLE_ATTRIBUTE_RELOAD_THREAD = 0x00004000, #endif RUN_FLAG__STATEFUL = 0x00008000, /* set if stream5 configured */ RUN_FLAG__INLINE_TEST = 0x00010000, /* --enable-inline-test*/ // UNUSED = 0x00020000, #ifdef INLINE_FAILOPEN RUN_FLAG__DISABLE_FAILOPEN = 0x00040000, /* --disable-inline-init-failopen */ #endif #ifdef MIMICK_IPV6 RUN_FLAG__MIMICK_IP6 = 0x00100000, /* -6 */ #endif RUN_FLAG__PCAP_RESET = 0x00200000, RUN_FLAG__PCAP_SHOW = 0x00400000, RUN_FLAG__REQUIRE_RULE_SID = 0x00800000, RUN_FLAG__NO_PCRE = 0x01000000, RUN_FLAG__ASSURE_EST = 0x02000000 /* config stateful */ #if defined(WIN32) && defined(ENABLE_WIN32_SERVICE) ,RUN_FLAG__TERMINATE_SERVICE = 0x04000000, RUN_FLAG__PAUSE_SERVICE = 0x08000000 #endif ,RUN_FLAG__TREAT_DROP_AS_IGNORE= 0x10000000, /* --treat-drop-as-ignore */ #if defined(SNORT_RELOAD) && !defined(WIN32) RUN_FLAG__PCAP_RELOAD = 0x20000000, /* --pcap-reload */ #endif RUN_FLAG__NO_IFACE_PID_FILE = 0x40000000 /* --no-interface-pidfile */ } RunFlag; typedef enum _OutputFlag { OUTPUT_FLAG__LINE_BUFFER = 0x00000001, /* -f */ OUTPUT_FLAG__VERBOSE_DUMP = 0x00000002, /* -X */ OUTPUT_FLAG__CHAR_DATA = 0x00000004, /* -C */ OUTPUT_FLAG__APP_DATA = 0x00000008, /* -d */ OUTPUT_FLAG__SHOW_DATA_LINK = 0x00000010, /* -e */ #ifndef NO_NON_ETHER_DECODER OUTPUT_FLAG__SHOW_WIFI_MGMT = 0x00000020, /* -w */ #endif OUTPUT_FLAG__USE_UTC = 0x00000040, /* -U */ OUTPUT_FLAG__INCLUDE_YEAR = 0x00000080, /* -y */ /* Note using this alters the packet - can't be used inline */ OUTPUT_FLAG__OBFUSCATE = 0x00000100, /* -B */ OUTPUT_FLAG__ALERT_IFACE = 0x00000200, /* -I */ OUTPUT_FLAG__NO_TIMESTAMP = 0x00000400, /* --nostamps */ OUTPUT_FLAG__ALERT_PKT_CNT = 0x00000800, /* -A packet-count */ /* XXX XXX pv.outputVidInAlerts */ OUTPUT_FLAG__ALERT_VLAN = 0x00001000 /* config include_vlan_in_alerts */ } OutputFlag; typedef enum _LoggingFlag { LOGGING_FLAG__VERBOSE = 0x00000001, /* -v */ LOGGING_FLAG__QUIET = 0x00000002, /* -q */ LOGGING_FLAG__SYSLOG = 0x00000004 /* -M */ #ifdef WIN32 ,LOGGING_FLAG__SYSLOG_REMOTE = 0x00000008 /* -s and -E */ #endif } LoggingFlag; typedef enum _InternalLogLevel { INTERNAL_LOG_LEVEL__SUPPRESS_ALL, INTERNAL_LOG_LEVEL__ERROR, INTERNAL_LOG_LEVEL__WARNING, INTERNAL_LOG_LEVEL__MESSAGE } InternalLogLevel; /* -k * config checksum_mode * config checksum_drop_mode */ typedef enum _ChecksumFlag { CHECKSUM_FLAG__IP = 0x00000001, CHECKSUM_FLAG__TCP = 0x00000002, CHECKSUM_FLAG__UDP = 0x00000004, CHECKSUM_FLAG__ICMP = 0x00000008, CHECKSUM_FLAG__ALL = 0x7fffffff } ChecksumFlag; typedef enum { /* config autogenerate_preprocessor_decoder_rules */ POLICY_FLAG__AUTO_OTN = 0x00000001 } PolicyFlag; typedef enum _PolicyModeFlag { POLICY_MODE__PASSIVE, POLICY_MODE__INLINE, POLICY_MODE__INLINE_TEST } PolicyMode; typedef enum _DecodeEventFlag { DECODE_EVENT_FLAG__DEFAULT = 0x00000001, DECODE_EVENT_FLAG__OVERSIZED = 0x00000002, DECODE_EVENT_FLAG__TCP_EXP_OPT = 0x00000004, DECODE_EVENT_FLAG__TCP_OBS_OPT = 0x00000008, DECODE_EVENT_FLAG__TCP_TTCP_OPT = 0x00000010, DECODE_EVENT_FLAG__TCP_OPT_ANOMALY = 0x00000020, DECODE_EVENT_FLAG__IP_OPT_ANOMALY = 0x00000040, DECODE_EVENT_FLAG__IPV6_BAD_FRAG = 0x00000080, DECODE_EVENT_FLAG__IPV6_BSD_ICMP_FRAG = 0x00000100 } DecodeEventFlag; typedef enum { TUNNEL_GTP = 0x01, TUNNEL_TEREDO = 0x02, TUNNEL_6IN4 = 0x04, TUNNEL_4IN6 = 0x08, TUNNEL_4IN4 = 0x10, TUNNEL_6IN6 = 0x20, TUNNEL_GRE = 0x40, TUNNEL_MPLS = 0x80 } TunnelFlags; typedef struct _VarNode { char *name; char *value; char *line; struct _VarNode *next; } VarNode; #ifdef TARGET_BASED typedef struct _TargetBasedConfig { char *args; char *file_name; int file_line; } TargetBasedConfig; #endif typedef struct _SnortPolicy { #ifdef TARGET_BASED TargetBasedConfig target_based_config; #endif PreprocConfig *preproc_configs; VarEntry *var_table; uint32_t var_id; vartable_t *ip_vartable; /* The portobjects in these are attached to rtns and used during runtime */ PortVarTable *portVarTable; /* named entries, uses a hash table */ PortTable *nonamePortVarTable; /* un-named entries */ PreprocEnableMask pp_enabled[MAX_PORTS]; PreprocEvalFuncNode *preproc_eval_funcs; PreprocEvalFuncNode *unused_preproc_eval_funcs; PreprocMetaEvalFuncNode *preproc_meta_eval_funcs; int preproc_proto_mask; int num_preprocs; int num_meta_preprocs; int ips_policy_mode; int nap_policy_mode; uint32_t policy_flags; /* mask of preprocessors that have registered runtime process functions */ PreprocEnableMask preproc_bit_mask; PreprocEnableMask preproc_meta_bit_mask; int num_detects; //int detect_bit_mask; int detect_proto_mask; DetectionEvalFuncNode *detect_eval_funcs; /** Identifier assigned by user to correlate unified2 events to actual * policy. User or DC should assign each policy a unique number. Snort * will not verify uniqueness. */ unsigned short configPolicyId; char *policy_version; uint8_t min_ttl; /* config min_ttl */ #ifdef NORMALIZER uint8_t new_ttl; /* config new_ttl */ #endif //checksum_mode and checksum_drop are now policy specific int checksum_flags; /* -k */ int checksum_flags_modified; int checksum_flags_saved; int checksum_drop_flags; int checksum_drop_flags_modified; //disable_decode_alerts and disable_decode_drop int decoder_alert_flags; int decoder_drop_flags; int decoder_alert_flags_saved; int decoder_drop_flags_saved; bool ssl_policy_enabled; } SnortPolicy; typedef struct _DynamicDetectionPlugin { void *handle; DynamicPluginMeta metaData; InitDetectionLibFunc initFunc; struct _DynamicDetectionPlugin *next; struct _DynamicDetectionPlugin *prev; } DynamicDetectionPlugin; #ifdef INTEL_SOFT_CPM struct _IntelPmHandles; #endif struct _MandatoryEarlySessionCreator; #ifdef SNORT_RELOAD struct _ReloadAdjustEntry; #endif struct _fileConfig; struct _DynamicRuleNode; typedef struct _IpsPortFilter { tSfPolicyId parserPolicyId; uint16_t port_filter[MAX_PORTS + 1]; } IpsPortFilter; typedef struct _SnortConfig { RunMode run_mode; int run_mode_flags; int run_flags; int output_flags; int logging_flags; int log_tcpdump; int no_log; int no_alert; int dirty_pig; //used for processing command line arguments, checksum configuration //in conf files is maintained at policy level int checksum_flags; /* -k */ int checksum_flags_modified; int checksum_drop_flags; int checksum_drop_flags_modified; uint32_t event_log_id; /* -G */ int pkt_snaplen; uint64_t pkt_cnt; /* -n */ #ifdef REG_TEST uint64_t pkt_skip; #endif char *dynamic_rules_path; /* --dump-dynamic-rules */ /* --dynamic-engine-lib * --dynamic-engine-lib-dir * --dynamic-detection-lib * --dynamic-detection-lib-dir * --dynamic-preprocessor-lib * --dynamic-preprocessor-lib-dir * * See below for struct type */ DynamicLibInfo *dyn_engines; DynamicLibInfo *dyn_rules; DynamicLibInfo *dyn_preprocs; #ifdef SIDE_CHANNEL DynamicLibInfo *dyn_side_channels; #endif char pid_path[STD_BUF]; /* --pid-path or config pidpath */ #ifdef EXIT_CHECK uint64_t exit_check; /* --exit-check */ #endif /* -h and -B */ sfcidr_t homenet; sfcidr_t obfuscation_net; /* config disable_decode_alerts * config enable_decode_oversized_alerts * config enable_decode_oversized_drops * config enable_decode_drops * config disable_decode_drops * config disable_tcpopt_experimental_alerts * config enable_tcpopt_experimental_drops * config disable_tcpopt_experimental_drops * config disable_tcpopt_obsolete_alerts * config enable_tcpopt_obsolete_drops * config disable_tcpopt_obsolete_drops * config disable_ttcp_alerts, config disable_tcpopt_ttcp_alerts * config enable_ttcp_drops, config enable_tcpopt_ttcp_drops * config disable_ttcp_drops * config disable_tcpopt_alerts * config enable_tcpopt_drops * config disable_tcpopt_drops * config disable_ipopt_alerts * config enable_ipopt_drops * config disable_ipopt_drops * config ipv6_frag: * bsd_icmp_frag_alert * bad_ipv6_frag_alert * frag_timeout - not in DecoderFlags * max_frag_sessions - not in DecoderFlags * drop_bad_ipv6_frag */ uint32_t ipv6_frag_timeout; uint32_t ipv6_max_frag_sessions; uint16_t flowbit_size; char pid_filename[STD_BUF]; /* used with pid_path */ char pidfile_suffix[MAX_PIDFILE_SUFFIX + 1]; /* -R */ char *log_dir; /* -l or config log_dir */ char *orig_log_dir; /* set in case of chroot */ char *interface; /* -i or config interface */ char *bpf_file; /* -F or config bpf_file */ char *pcap_log_file; /* -L */ char *chroot_dir; /* -t or config chroot */ char *alert_file; char *perf_file; /* -Z */ char *bpf_filter; /* last command line arguments */ char* daq_type; /* --daq or config daq */ char* daq_mode; /* --daq-mode or config daq_mode */ void* daq_vars; /* --daq-var or config daq_var */ void* daq_dirs; /* --daq-dir or config daq_dir */ char* event_trace_file; uint16_t event_trace_max; int thiszone; #ifdef WIN32 char syslog_server[STD_BUF]; int syslog_server_port; # ifdef ENABLE_WIN32_SERVICE int terminate_service_flag; int pause_service_flag; # endif #endif uint8_t ignore_ports[UINT16_MAX + 1]; /* config ignore_ports */ long int tagged_packet_limit; /* config tagged_packet_limit */ long int pcre_match_limit; /* config pcre_match_limit */ long int pcre_match_limit_recursion; /* config pcre_match_limit_recursion */ int *pcre_ovector; int pcre_ovector_size; #ifdef PERF_PROFILING ProfileConfig profile_rules; /* config profile_rules */ ProfileConfig profile_preprocs; /* config profile_preprocs */ #endif int user_id; int group_id; mode_t file_mask; #ifdef MPLS uint8_t mpls_payload_type; /* --mpls_payload_type */ long int mpls_stack_depth; /* --max_mpls_labelchain_len */ #endif int default_rule_state; /* config default_rule_state */ char* react_page; /* config react */ #ifdef ACTIVE_RESPONSE uint8_t respond_attempts; /* config respond */ char* respond_device; uint8_t *eth_dst; /* config destination MAC address */ #endif #ifdef TARGET_BASED uint32_t max_attribute_hosts; /* config max_attribute_hosts */ uint32_t max_attribute_services_per_host; /* config max_attribute_services_per_host */ uint32_t max_metadata_services; /* config max_metadata_services */ #endif #if defined(FEAT_OPEN_APPID) uint32_t max_metadata_appid; #endif /* defined(FEAT_OPEN_APPID) */ OutputConfig *output_configs; OutputConfig *rule_type_output_configs; SFGHASH *config_table; /* table of config keywords and arguments */ int asn1_mem; int active_dynamic_nodes; RuleState *rule_state_list; ClassType *classifications; ReferenceSystemNode *references; SFGHASH *so_rule_otn_map; SFGHASH *otn_map; SFGHASH *preproc_rule_options; FastPatternConfig *fast_pattern_config; EventQueueConfig *event_queue_config; PreprocPostConfigFuncNode *preproc_post_config_funcs; PreprocCheckConfigFuncNode *preproc_config_check_funcs; /* XXX XXX policy specific? */ ThresholdConfig *threshold_config; RateFilterConfig *rate_filter_config; DetectionFilterConfig *detection_filter_config; SF_EVENTQ *event_queue[NUM_EVENT_QUEUES]; SF_LIST **ip_proto_only_lists; uint8_t ip_proto_array[NUM_IP_PROTOS]; int num_rule_types; RuleListNode *rule_lists; int evalOrder[RULE_TYPE__MAX + 1]; ListHead Alert; /* Alert Block Header */ ListHead Log; /* Log Block Header */ ListHead Pass; /* Pass Block Header */ ListHead Activation; /* Activation Block Header */ ListHead Dynamic; /* Dynamic Block Header */ ListHead Drop; ListHead SDrop; ListHead Reject; PostConfigFuncNode *plugin_post_config_funcs; OTNX_MATCH_DATA *omd; /* Pattern matcher queue statistics */ unsigned int max_inq; uint64_t tot_inq_flush; uint64_t tot_inq_inserts; uint64_t tot_inq_uinserts; /* Protected Content secure hash type default */ Secure_Hash_Type Default_Protected_Content_Hash_Type; /* master port list table */ rule_port_tables_t *port_tables; #ifdef PPM_MGR ppm_cfg_t ppm_cfg; #endif /* The port-rule-maps map the src-dst ports to rules for * udp and tcp, for Ip we map the dst port as the protocol, * and for Icmp we map the dst port to the Icmp type. This * allows us to use the decode packet information to in O(1) * select a group of rules to apply to the packet. These * rules may have uricontent, content, or they may be no content * rules, or any combination. We process the uricontent 1st, * then the content, and then the no content rules for udp/tcp * and icmp, than we process the ip rules. */ PORT_RULE_MAP *prmIpRTNX; PORT_RULE_MAP *prmTcpRTNX; PORT_RULE_MAP *prmUdpRTNX; PORT_RULE_MAP *prmIcmpRTNX; #ifdef TARGET_BASED srmm_table_t *srmmTable; /* srvc rule map master table */ srmm_table_t *spgmmTable; /* srvc port_group map master table */ sopg_table_t *sopgTable; /* service-oridnal to port_group table */ #endif SFXHASH *detection_option_hash_table; SFXHASH *detection_option_tree_hash_table; SFXHASH *rtn_hash_table; tSfPolicyConfig *policy_config; SnortPolicy **targeted_policies; IpsPortFilter **udp_ips_port_filter_list; unsigned int num_policies_allocated; char *base_version; uint8_t enable_teredo; /* config enable_deep_teredo_inspection */ uint8_t enable_gtp; /* config enable_gtp */ char *gtp_ports; uint8_t enable_esp; uint8_t vlan_agnostic; /* config vlan_agnostic */ uint8_t addressspace_agnostic; /* config addressspace_agnostic */ uint8_t log_ipv6_extra; /* config log_ipv6_extra_data */ uint8_t tunnel_mask; uint32_t so_rule_memcap; uint32_t paf_max; /* config paf_max */ char *cs_dir; bool ha_peer; char *ha_out; char *ha_in; char *ha_pdts_in; char *output_dir; struct _fileConfig *file_config; int disable_all_policies; PreprocEnableMask reenabled_preprocessor_bits; /* flags for preprocessors to check, if all policies are disabled */ #ifdef SIDE_CHANNEL SideChannelConfig side_channel_config; #endif #ifdef SNORT_RELOAD int reloadPolicyFlag; PreprocessorSwapData *preprocSwapData; void *streamReloadConfig; #endif tSfPolicyId parserPolicyId; #ifdef INTEL_SOFT_CPM struct _IntelPmHandles *ipm_handles; #endif /* Used when a user defines a new rule type (ruletype keyword) * It points to the new rule type's ListHead and is used for accessing the * rule type's AlertList and LogList. * The output plugins used for the rule type need to be attached to the new * rule type's list head's AlertList or LogList. It's set before calling * the output plugin's initialization routine, because in that routine, * AddFuncToOutputList is called (plugbase.c) and there, the output function * is attached to the new rule type's appropriate list. * NOTE: This variable MUST NOT be used during runtime */ ListHead *head_tmp; uint8_t max_ip6_extensions; int internal_log_level; int suppress_config_log; uint8_t disable_replace_opt; struct _MandatoryEarlySessionCreator* mandatoryESCreators; bool normalizer_set; #ifdef DUMP_BUFFER char *buffer_dump_file; #endif #ifdef SNORT_RELOAD struct _ReloadAdjustEntry* raSessionEntry; struct _ReloadAdjustEntry* volatile raEntry; struct _ReloadAdjustEntry* raCurrentEntry; time_t raLastLog; #endif DynamicDetectionPlugin *loadedDetectionPlugins; struct _DynamicRuleNode *dynamic_rules; char *memdump_file; } SnortConfig; /* struct to collect packet statistics */ typedef struct _PacketCount { uint64_t total_from_daq; uint64_t total_processed; uint64_t s5tcp1; uint64_t s5tcp2; uint64_t ipv6opts; uint64_t eth; uint64_t ethdisc; uint64_t ipv6disc; uint64_t ip6ext; uint64_t other; uint64_t tcp; uint64_t udp; uint64_t icmp; uint64_t arp; #ifndef NO_NON_ETHER_DECODER uint64_t eapol; #endif uint64_t vlan; uint64_t nested_vlan; uint64_t ipv6; uint64_t ipv6_up; uint64_t ipv6_upfail; uint64_t frag6; uint64_t icmp6; uint64_t tdisc; uint64_t udisc; uint64_t tcp6; uint64_t udp6; uint64_t teredo; uint64_t ipdisc; uint64_t icmpdisc; uint64_t embdip; uint64_t ip; uint64_t ipx; uint64_t ethloopback; uint64_t invalid_checksums; uint64_t bad_ttl; #ifdef GRE uint64_t ip4ip4; uint64_t ip4ip6; uint64_t ip6ip4; uint64_t ip6ip6; uint64_t gre; uint64_t gre_ip; uint64_t gre_eth; uint64_t gre_arp; uint64_t gre_ipv6; uint64_t gre_ipv6ext; uint64_t gre_ipx; uint64_t gre_loopback; uint64_t gre_vlan; uint64_t gre_ppp; #endif uint64_t discards; uint64_t alert_pkts; uint64_t total_alert_pkts; uint64_t log_pkts; uint64_t pass_pkts; uint64_t match_limit; uint64_t queue_limit; uint64_t log_limit; uint64_t event_limit; uint64_t alert_limit; uint64_t frags; /* number of frags that have come in */ uint64_t frag_trackers; /* number of tracking structures generated */ uint64_t rebuilt_frags; /* number of packets rebuilt */ uint64_t frag_incomp; /* number of frags cleared due to memory issues */ uint64_t frag_timeout; /* number of frags cleared due to timeout */ uint64_t rebuild_element; /* frags that were element of rebuilt pkt */ uint64_t frag_mem_faults; /* number of times the memory cap was hit */ uint64_t tcp_stream_pkts; /* number of packets tcp reassembly touches */ uint64_t rebuilt_tcp; /* number of phoney tcp packets generated */ uint64_t tcp_streams; /* number of tcp streams created */ uint64_t rebuilt_segs; /* number of tcp segments used in rebuilt pkts */ uint64_t queued_segs; /* number of tcp segments stored for rebuilt pkts */ uint64_t str_mem_faults; /* number of times the stream memory cap was hit */ #ifdef TARGET_BASED uint64_t attribute_table_reloads; /* number of times attribute table was reloaded. */ #endif #ifndef NO_NON_ETHER_DECODER #ifdef DLT_IEEE802_11 /* wireless statistics */ uint64_t wifi_mgmt; uint64_t wifi_data; uint64_t wifi_control; uint64_t assoc_req; uint64_t assoc_resp; uint64_t reassoc_req; uint64_t reassoc_resp; uint64_t probe_req; uint64_t probe_resp; uint64_t beacon; uint64_t atim; uint64_t dissassoc; uint64_t auth; uint64_t deauth; uint64_t ps_poll; uint64_t rts; uint64_t cts; uint64_t ack; uint64_t cf_end; uint64_t cf_end_cf_ack; uint64_t data; uint64_t data_cf_ack; uint64_t data_cf_poll; uint64_t data_cf_ack_cf_poll; uint64_t cf_ack; uint64_t cf_poll; uint64_t cf_ack_cf_poll; #endif #endif // NO_NON_ETHER_DECODER #ifdef MPLS uint64_t mpls; #endif uint64_t internal_blacklist; uint64_t internal_whitelist; uint64_t syn_rate_limit_events; uint64_t syn_rate_limit_drops; } PacketCount; typedef struct _PcapReadObject { int type; char *arg; char *filter; } PcapReadObject; #if defined(DAQ_CAPA_CST_TIMEOUT) bool Daq_Capa_Timeout; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) bool Daq_Capa_Vrf; #endif /* ptr to the packet processor */ typedef void (*grinder_t)(Packet *, const DAQ_PktHdr_t*, const uint8_t *); /* E X T E R N S ************************************************************/ extern const struct timespec thread_sleep; extern volatile bool snort_initializing; extern volatile int snort_exiting; #ifdef SNORT_RELOAD typedef uint32_t snort_reload_t; extern volatile snort_reload_t reload_signal; extern volatile int detection_lib_changed; extern snort_reload_t reload_total; #endif #if defined(SNORT_RELOAD) && !defined(WIN32) extern volatile int snort_reload_thread_created; extern pid_t snort_reload_thread_pid; #endif extern SnortConfig *snort_conf; extern SnortConfig *snort_cmd_line_conf; extern int internal_log_level; #include "sfutil/sfPolicyData.h" /* Specifically for logging the IPv6 fragmented ICMP BSD vulnerability */ extern Packet *BsdPseudoPacket; extern PacketCount pc; /* packet count information */ extern char **protocol_names; extern grinder_t grinder; #ifdef SIDE_CHANNEL extern pthread_mutex_t snort_process_lock; #endif extern pthread_mutex_t dynamic_rules_lock; extern OutputFuncNode *AlertList; extern OutputFuncNode *LogList; extern tSfActionQueueId decoderActionQ; #if defined(SNORT_RELOAD) && !defined(WIN32) extern volatile int snort_reload; #endif #ifdef SNORT_RELOAD extern PostConfigFuncNode *plugin_reload_funcs; #endif extern PeriodicCheckFuncNode *periodic_check_funcs; #if defined(DAQ_VERSION) && DAQ_VERSION > 9 void print_pktverdict (Packet *, uint64_t ); void print_flow(Packet *, char *, uint32_t, uint64_t, uint64_t ); #endif /* P R O T O T Y P E S ******************************************************/ int SnortMain(int argc, char *argv[]); DAQ_Verdict ProcessPacket(Packet*, const DAQ_PktHdr_t*, const uint8_t*, void*); Packet *NewGrinderPkt(Packet *p, DAQ_PktHdr_t* phdr, uint8_t *pkt); void DeleteGrinderPkt(Packet *); void SetupMetadataCallback(void); int InMainThread(void); bool SnortIsInitializing(void); void SigCantHupHandler(int signal); void print_packet_count(void); int SignalCheck(void); void Restart(void); void FreeVarList(VarNode *); SnortConfig * SnortConfNew(void); void SnortConfFree(SnortConfig *); void CleanupPreprocessors(SnortConfig *); void CleanupPlugins(SnortConfig *); void CleanExit(int); SnortConfig * MergeSnortConfs(SnortConfig *, SnortConfig *); void SnortShutdownThreads(int); typedef void (*sighandler_t)(int); int SnortAddSignal(int sig, sighandler_t handler, int); #ifdef TARGET_BASED void SigNoAttributeTableHandler(int); #endif /* * If any of the following API are modified or new ones are * introduced, we have to make sure if they are called in * reload path. If yes, they have to use new snort config. */ static inline int ScTestMode(void) { return snort_conf->run_mode == RUN_MODE__TEST; } static inline int ScRuleDumpMode(void) { return snort_conf->run_mode == RUN_MODE__RULE_DUMP; } static inline int ScVersionMode(void) { return snort_conf->run_mode == RUN_MODE__VERSION; } static inline int ScIdsMode(void) { return snort_conf->run_mode == RUN_MODE__IDS; } static inline int ScPacketLogMode(void) { return snort_conf->run_mode == RUN_MODE__PACKET_LOG; } static inline int ScPacketDumpMode(void) { return snort_conf->run_mode == RUN_MODE__PACKET_DUMP; } static inline int ScDaemonMode(void) { return snort_conf->run_flags & RUN_FLAG__DAEMON; } static inline int ScDaemonRestart(void) { return snort_conf->run_flags & RUN_FLAG__DAEMON_RESTART; } static inline int ScReadMode(void) { return snort_conf->run_flags & RUN_FLAG__READ; } static inline int ScLogSyslog(void) { return snort_conf->logging_flags & LOGGING_FLAG__SYSLOG; } #ifdef WIN32 static inline int ScLogSyslogRemote(void) { return snort_conf->logging_flags & LOGGING_FLAG__SYSLOG_REMOTE; } #endif static inline int ScLogVerbose(void) { return snort_conf->logging_flags & LOGGING_FLAG__VERBOSE; } static inline int ScLogQuiet(void) { return snort_conf->logging_flags & LOGGING_FLAG__QUIET; } static inline int ScCheckInternalLogLevel(int level) { return internal_log_level >= level; } static inline void ScSetInternalLogLevel(int level) { if (!ScLogQuiet()) internal_log_level = level; } static inline void ScRestoreInternalLogLevel(void) { internal_log_level = snort_conf->internal_log_level; } static inline int ScDecoderAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__DEFAULT; } static inline int ScDecoderDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__DEFAULT; } static inline int ScDecoderOversizedAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__OVERSIZED; } static inline int ScDecoderOversizedDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__OVERSIZED; } static inline int ScDecoderIpv6BadFragAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__IPV6_BAD_FRAG; } static inline int ScDecoderIpv6BadFragDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__IPV6_BAD_FRAG; } static inline int ScDecoderIpv6BsdIcmpFragAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__IPV6_BSD_ICMP_FRAG; } static inline int ScDecoderIpv6BsdIcmpFragDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__IPV6_BSD_ICMP_FRAG; } static inline int ScDecoderTcpOptAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__TCP_OPT_ANOMALY; } static inline int ScDecoderTcpOptDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__TCP_OPT_ANOMALY; } static inline int ScDecoderTcpOptExpAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__TCP_EXP_OPT; } static inline int ScDecoderTcpOptExpDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__TCP_EXP_OPT; } static inline int ScDecoderTcpOptObsAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__TCP_OBS_OPT; } static inline int ScDecoderTcpOptObsDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__TCP_OBS_OPT; } static inline int ScDecoderTcpOptTTcpAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__TCP_TTCP_OPT; } static inline int ScDecoderTcpOptTTcpDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__TCP_TTCP_OPT; } static inline int ScDecoderIpOptAlerts(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_alert_flags & DECODE_EVENT_FLAG__IP_OPT_ANOMALY; } static inline int ScDecoderIpOptDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->decoder_drop_flags & DECODE_EVENT_FLAG__IP_OPT_ANOMALY; } static inline int ScIpChecksums(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_flags & CHECKSUM_FLAG__IP; } static inline int ScIpChecksumDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__IP; } static inline int ScUdpChecksums(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_flags & CHECKSUM_FLAG__UDP; } static inline int ScUdpChecksumDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__UDP; } static inline int ScTcpChecksums(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_flags & CHECKSUM_FLAG__TCP; } static inline int ScTcpChecksumDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__TCP; } static inline int ScIcmpChecksums(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_flags & CHECKSUM_FLAG__ICMP; } static inline int ScIcmpChecksumDrops(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->checksum_drop_flags & CHECKSUM_FLAG__ICMP; } static inline int ScIgnoreTcpPort(uint16_t port) { return snort_conf->ignore_ports[port] & PROTO_BIT__TCP; } static inline int ScIgnoreUdpPort(uint16_t port) { return snort_conf->ignore_ports[port] & PROTO_BIT__UDP; } #ifdef MPLS static inline long int ScMplsStackDepth(void) { return snort_conf->mpls_stack_depth; } #ifdef MPLS_RFC4023_SUPPORT static inline long int ScMplsPayloadCheck(uint8_t ih1, long int iRet) { // IPv4 if (((ih1 & 0xF0) >> 4) == 4) return MPLS_PAYLOADTYPE_IPV4; // IPv6 else if (((ih1 & 0xF0) >> 4) == 6) return MPLS_PAYLOADTYPE_IPV6; else return iRet; } #endif static inline long int ScMplsPayloadType(void) { return snort_conf->mpls_payload_type; } static inline int ScMplsOverlappingIp(void) { return snort_conf->run_flags & RUN_FLAG__MPLS_OVERLAPPING_IP; } static inline int ScMplsMulticast(void) { return snort_conf->run_flags & RUN_FLAG__MPLS_MULTICAST; } #endif static inline uint32_t ScIpv6FragTimeout(void) { return snort_conf->ipv6_frag_timeout; } static inline uint32_t ScIpv6MaxFragSessions(void) { return snort_conf->ipv6_max_frag_sessions; } static inline uint8_t ScMinTTL(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->min_ttl; } #ifdef NORMALIZER static inline uint8_t ScNewTTL(void) { return snort_conf->targeted_policies[getNapRuntimePolicy()]->new_ttl; } #endif static inline uint32_t ScPafMax (void) { return snort_conf->paf_max; } static inline bool ScPafEnabled (void) { return ( ScPafMax() > 0 ); } static inline uint32_t ScEventLogId(void) { return snort_conf->event_log_id; } static inline int ScConfErrorOut(void) { return snort_conf->run_flags & RUN_FLAG__CONF_ERROR_OUT; } static inline int ScAssureEstablished(void) { return snort_conf->run_flags & RUN_FLAG__ASSURE_EST; } /* Set if stream5 is configured */ static inline int ScStateful(void) { return snort_conf->run_flags & RUN_FLAG__STATEFUL; } static inline long int ScPcreMatchLimit(void) { return snort_conf->pcre_match_limit; } static inline long int ScPcreMatchLimitRecursion(void) { return snort_conf->pcre_match_limit_recursion; } #ifdef PERF_PROFILING static inline int ScProfilePreprocs(void) { return snort_conf->profile_preprocs.num; } static inline int ScProfileRules(void) { return snort_conf->profile_rules.num; } #endif static inline int ScStaticHash(void) { return snort_conf->run_flags & RUN_FLAG__STATIC_HASH; } static inline int ScAutoGenPreprocDecoderOtns(void) { return (((snort_conf->targeted_policies[getNapRuntimePolicy()])->policy_flags) & POLICY_FLAG__AUTO_OTN ); } static inline int ScProcessAllEvents(void) { return snort_conf->event_queue_config->process_all_events; } static inline int ScNapPassiveMode(void) { return (((snort_conf->targeted_policies[getNapRuntimePolicy()])->nap_policy_mode) == POLICY_MODE__PASSIVE ); } static inline int ScIpsPassiveMode(void) { return (((snort_conf->targeted_policies[getIpsRuntimePolicy()])->ips_policy_mode) == POLICY_MODE__PASSIVE ); } static inline int ScAdapterPassiveMode(void) { return !(snort_conf->run_flags & (RUN_FLAG__INLINE | RUN_FLAG__INLINE_TEST)); } static inline int ScNapInlineMode(void) { return (((snort_conf->targeted_policies[getNapRuntimePolicy()])->nap_policy_mode) == POLICY_MODE__INLINE ); } static inline int ScIpsInlineMode(void) { return (((snort_conf->targeted_policies[getIpsRuntimePolicy()])->ips_policy_mode) == POLICY_MODE__INLINE ); } static inline int ScAdapterInlineMode(void) { return snort_conf->run_flags & RUN_FLAG__INLINE; } static inline int ScNapInlineTestMode(void) { return (((snort_conf->targeted_policies[getNapRuntimePolicy()])->nap_policy_mode) == POLICY_MODE__INLINE_TEST ); } static inline int ScIpsInlineTestMode(void) { return (((snort_conf->targeted_policies[getIpsRuntimePolicy()])->ips_policy_mode) == POLICY_MODE__INLINE_TEST ); } static inline int ScAdapterInlineTestMode(void) { return snort_conf->run_flags & RUN_FLAG__INLINE_TEST; } static inline int ScOutputIncludeYear(void) { return snort_conf->output_flags & OUTPUT_FLAG__INCLUDE_YEAR; } static inline int ScOutputUseUtc(void) { return snort_conf->output_flags & OUTPUT_FLAG__USE_UTC; } static inline int ScOutputDataLink(void) { return snort_conf->output_flags & OUTPUT_FLAG__SHOW_DATA_LINK; } static inline int ScVerboseByteDump(void) { return snort_conf->output_flags & OUTPUT_FLAG__VERBOSE_DUMP; } static inline int ScAlertPacketCount(void) { return snort_conf->output_flags & OUTPUT_FLAG__ALERT_PKT_CNT; } static inline int ScObfuscate(void) { return snort_conf->output_flags & OUTPUT_FLAG__OBFUSCATE; } static inline int ScOutputAppData(void) { return snort_conf->output_flags & OUTPUT_FLAG__APP_DATA; } static inline int ScOutputCharData(void) { return snort_conf->output_flags & OUTPUT_FLAG__CHAR_DATA; } static inline int ScAlertInterface(void) { return snort_conf->output_flags & OUTPUT_FLAG__ALERT_IFACE; } static inline int ScNoOutputTimestamp(void) { return snort_conf->output_flags & OUTPUT_FLAG__NO_TIMESTAMP; } static inline int ScLineBufferedLogging(void) { return snort_conf->output_flags & OUTPUT_FLAG__LINE_BUFFER; } static inline int ScDefaultRuleState(void) { return snort_conf->default_rule_state; } static inline int ScRequireRuleSid(void) { return snort_conf->run_flags & RUN_FLAG__REQUIRE_RULE_SID; } #ifdef INLINE_FAILOPEN static inline int ScDisableInlineFailopen(void) { return snort_conf->run_flags & RUN_FLAG__DISABLE_FAILOPEN; } #endif static inline int ScNoLockPidFile(void) { return snort_conf->run_flags & RUN_FLAG__NO_LOCK_PID_FILE; } static inline long int ScTaggedPacketLimit(void) { return snort_conf->tagged_packet_limit; } static inline int ScCreatePidFile(void) { return snort_conf->run_flags & RUN_FLAG__CREATE_PID_FILE; } static inline int ScNoInterfacePidFile(void) { return snort_conf->run_flags & RUN_FLAG__NO_IFACE_PID_FILE; } static inline int ScPcapShow(void) { return snort_conf->run_flags & RUN_FLAG__PCAP_SHOW; } static inline int ScPcapReset(void) { return snort_conf->run_flags & RUN_FLAG__PCAP_RESET; } #ifndef NO_NON_ETHER_DECODER static inline int ScOutputWifiMgmt(void) { return snort_conf->output_flags & OUTPUT_FLAG__SHOW_WIFI_MGMT; } #endif #ifdef TARGET_BASED static inline uint32_t ScMaxAttrHosts(SnortConfig *sc) { return sc->max_attribute_hosts; } static inline uint32_t ScMaxAttrServicesPerHost(void) { return snort_conf->max_attribute_services_per_host; } static inline int ScDisableAttrReload(SnortConfig *sc) { return sc->run_flags & RUN_FLAG__DISABLE_ATTRIBUTE_RELOAD_THREAD; } #endif static inline int ScTreatDropAsAlert(void) { return snort_conf->run_flags & RUN_FLAG__TREAT_DROP_AS_ALERT; } static inline int ScTreatDropAsIgnore(void) { return snort_conf->run_flags & RUN_FLAG__TREAT_DROP_AS_IGNORE; } static inline int ScAlertBeforePass(void) { return snort_conf->run_flags & RUN_FLAG__ALERT_BEFORE_PASS; } static inline int ScNoPcre(void) { return snort_conf->run_flags & RUN_FLAG__NO_PCRE; } static inline int ScGetEvalIndex(RuleType type) { return snort_conf->evalOrder[type]; } static inline int ScNoLog(void) { return snort_conf->no_log; } static inline int ScNoAlert(void) { return snort_conf->no_alert; } #if defined(WIN32) && defined(ENABLE_WIN32_SERVICE) static inline int ScTerminateService(void) { return snort_conf->run_flags & RUN_FLAG__TERMINATE_SERVICE; } static inline int ScPauseService(void) { return snort_conf->run_flags & RUN_FLAG__PAUSE_SERVICE; } #endif static inline int ScUid(void) { return snort_conf->user_id; } static inline int ScGid(void) { return snort_conf->group_id; } static inline char * ScPcapLogFile(void) { return snort_conf->pcap_log_file; } #ifdef SIDE_CHANNEL static inline int ScSideChannelEnabled(void) { return snort_conf->side_channel_config.enabled; } #endif // use of macro avoids depending on generators.h #define EventIsInternal(gid) (gid == GENERATOR_INTERNAL) static inline void EnableInternalEvent(RateFilterConfig *config, uint32_t sid) { if (config == NULL) return; config->internal_event_mask |= (1 << sid); } static inline int InternalEventIsEnabled(RateFilterConfig *config, uint32_t sid) { if (config == NULL) return 0; return (config->internal_event_mask & (1 << sid)); } static inline int ScIsPreprocEnabled(uint32_t preproc_id, tSfPolicyId policy_id) { SnortPolicy *policy; if (policy_id >= snort_conf->num_policies_allocated) return 0; policy = snort_conf->targeted_policies[policy_id]; if (policy == NULL) return 0; if (policy->preproc_bit_mask & (UINT64_C(1) << preproc_id)) return 1; return 0; } static inline int ScDeepTeredoInspection(void) { return snort_conf->enable_teredo; } static inline int ScGTPDecoding(void) { return snort_conf->enable_gtp; } static inline int ScIsGTPPort(uint16_t port) { return snort_conf->gtp_ports[port]; } static inline int ScESPDecoding(void) { return snort_conf->enable_esp; } static inline int ScVlanAgnostic(void) { return snort_conf->vlan_agnostic; } static inline int ScAddressSpaceAgnostic(void) { return snort_conf->addressspace_agnostic; } static inline int ScLogIPv6Extra(void) { return snort_conf->log_ipv6_extra; } static inline uint32_t ScSoRuleMemcap(void) { return snort_conf->so_rule_memcap; } static inline bool ScTunnelBypassEnabled (uint8_t proto) { return !(snort_conf->tunnel_mask & proto); } static inline uint8_t ScMaxIP6Extensions(void) { return snort_conf->max_ip6_extensions; } static inline int ScSuppressConfigLog(void) { return snort_conf->suppress_config_log; } static inline int ScDisableReplaceOpt(void) { return snort_conf->disable_replace_opt; } static inline int ScIpsInlineModeNewConf (SnortConfig * sc) { return (((sc->targeted_policies[getParserPolicy(sc)])->ips_policy_mode) == POLICY_MODE__INLINE ); } static inline int ScAdapterInlineModeNewConf (SnortConfig * sc) { return sc->run_flags & RUN_FLAG__INLINE; } static inline int ScTreatDropAsIgnoreNewConf (SnortConfig * sc) { return sc->run_flags & RUN_FLAG__TREAT_DROP_AS_IGNORE; } static inline int ScIpsInlineTestModeNewConf (SnortConfig * sc) { return (((sc->targeted_policies[getParserPolicy(sc)])->ips_policy_mode) == POLICY_MODE__INLINE_TEST ); } static inline int ScAdapterInlineTestModeNewConf (SnortConfig * sc) { return sc->run_flags & RUN_FLAG__INLINE_TEST; } static inline int ScTestModeNewConf (SnortConfig * sc) { return sc->run_mode == RUN_MODE__TEST; } static inline int ScConfErrorOutNewConf (SnortConfig * sc) { return sc->run_flags & RUN_FLAG__CONF_ERROR_OUT; } static inline int ScDefaultRuleStateNewConf (SnortConfig * sc) { return sc->default_rule_state; } static inline int ScRequireRuleSidNewConf (SnortConfig * sc) { return sc->run_flags & RUN_FLAG__REQUIRE_RULE_SID; } static inline int ScTreatDropAsAlertNewConf (SnortConfig * sc) { return sc->run_flags & RUN_FLAG__TREAT_DROP_AS_ALERT; } static inline int ScSuppressConfigLogNewConf (SnortConfig* sc) { return sc->suppress_config_log; } static inline int ScLogQuietNewConf (SnortConfig* sc) { return sc->logging_flags & LOGGING_FLAG__QUIET; } static inline void ScSetInternalLogLevelNewConf (SnortConfig* sc, int level) { if (!ScLogQuietNewConf(sc)) internal_log_level = level; } static inline void ScRestoreInternalLogLevelNewConf (SnortConfig* sc) { internal_log_level = sc->internal_log_level; } static inline long int ScPcreMatchLimitNewConf (SnortConfig *sc) { return sc->pcre_match_limit; } static inline long int ScPcreMatchLimitRecursionNewConf (SnortConfig *sc) { return sc->pcre_match_limit_recursion; } static inline int ScNoOutputTimestampNewConf (SnortConfig *sc) { return sc->output_flags & OUTPUT_FLAG__NO_TIMESTAMP; } static inline int ScNapPassiveModeNewConf (SnortConfig* sc) { return (((sc->targeted_policies[getParserPolicy(sc)])->nap_policy_mode) == POLICY_MODE__PASSIVE ); } static inline int ScNapInlineTestModeNewConf (SnortConfig* sc) { return (((sc->targeted_policies[getParserPolicy(sc)])->nap_policy_mode) == POLICY_MODE__INLINE_TEST ); } static inline uint32_t ScPafMaxNewConf (SnortConfig *sc) { return sc->paf_max; } static inline bool ScPafEnabledNewConf (SnortConfig *sc) { return ( ScPafMaxNewConf(sc) > 0 ); } #if defined(DAQ_CAPA_CST_TIMEOUT) static inline uint64_t GetTimeout( Packet *p, uint64_t *timeout) { DAQ_QueryFlow_t query; int rval; query.type = DAQ_QUERYFLOW_TYPE_TIMEOUT_VAL; query.length = sizeof(uint64_t); query.value = timeout; rval = DAQ_QueryFlow( p->pkth, &query); if(rval != DAQ_SUCCESS) *timeout = 1; return 1; } #endif /* query flow */ #endif /* __SNORT_H__ */ snort-2.9.20/src/encode.c0000644000175000017500000014535114241076510013272 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file encode.c // @author Russ Combs #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifdef HAVE_DUMBNET_H #include #else #include #endif #include "assert.h" #include "encode.h" #include "sfdaq.h" #include "sf_iph.h" #include "snort.h" #include "session_api.h" #include "stream_api.h" #include "checksum.h" #ifdef MPLS #include "preprocessors/Session/session_common.h" #endif #define GET_IP_HDR_LEN(h) (((h)->ip_verhl & 0x0f) << 2) #define GET_TCP_HDR_LEN(h) (((h)->th_offx2 & 0xf0) >> 2) #define SET_TCP_HDR_LEN(h, n) (h)->th_offx2 = ((n << 2) & 0xF0) #define MIN_TTL 64 #define MAX_TTL 255 #define ICMP_UNREACH_DATA 8 // (per RFC 792) #define IP_ID_COUNT 8192 static uint8_t* dst_mac = NULL; Packet* encode_pkt = NULL; uint64_t total_rebuilt_pkts = 0; //------------------------------------------------------------------------- // encoders operate layer by layer: // * base+off is start of packet // * base+end is start of current layer // * base+size-1 is last byte of packet (in) / buffer (out) typedef blob_t Buffer; typedef enum { ENC_OK, ENC_BAD_PROTO, ENC_BAD_OPT, ENC_OVERFLOW } ENC_STATUS; typedef struct { EncodeType type; EncodeFlags flags; uint8_t layer; const Packet* p; uint16_t ip_len; uint8_t* ip_hdr; const uint8_t* payLoad; uint32_t payLen; uint8_t proto; } EncState; #define FORWARD(e) (e->flags & ENC_FLAG_FWD) #define REVERSE(f) (!(f & ENC_FLAG_FWD)) // PKT_MAX is sized to ensure that any reassembled packet // can accommodate a full datagram at innermost layer #define PKT_MAX (ETHERNET_HEADER_LEN + VLAN_HEADER_LEN + ETHERNET_MTU + IP_MAXPACKET) // all layer encoders look like this: typedef ENC_STATUS (*Encoder)(EncState*, Buffer* in, Buffer* out); typedef ENC_STATUS (*Updater)(Packet*, Layer*, uint32_t* len); typedef void (*Formatter)(EncodeFlags, const Packet* p, Packet* c, Layer*); // TBD implement other encoder functions typedef struct { Encoder fencode; Updater fupdate; Formatter fformat; } EncoderFunctions; // forward declaration; definition at end of file static EncoderFunctions encoders[PROTO_MAX]; static void IpId_Init(); static void IpId_Term(); static const uint8_t* Encode_Packet( EncState* enc, const Packet* p, uint32_t* len); static ENC_STATUS UN6_ICMP6_Encode(EncState*, Buffer*, Buffer*); static ENC_STATUS UN6_UDP_Encode(EncState*, Buffer*, Buffer*); //------------------------------------------------------------------------- static inline PROTO_ID NextEncoder (EncState* enc) { if ( enc->layer < enc->p->next_layer ) { PROTO_ID next = enc->p->layers[enc->layer++].proto; if ( next < PROTO_MAX ) { if ( encoders[next].fencode ) return next; } } return PROTO_MAX; } //------------------------------------------------------------------------- // basic setup stuff //------------------------------------------------------------------------- void Encode_Init (void) { IpId_Init(); } void Encode_Term (void) { IpId_Term(); } //------------------------------------------------------------------------- // encoders: // - raw pkt data only, no need for Packet stuff except to facilitate // encoding // - don't include original options // - inner layer differs from original (eg tcp data segment becomes rst) // - must ensure proper ttl/hop limit for reverse direction // - sparc twiddle must be factored in packet start for transmission // // iterate over decoded layers and encode the response packet. actually // make nested calls. on the way in we setup invariant stuff and as we // unwind the stack we finish up encoding in a more normal fashion (now // the outer layer knows the length of the inner layer, etc.). // // when multiple responses are sent, both forwards and backwards directions, // or multiple ICMP types (unreachable port, host, net), it may be possible // to reuse the 1st encoding and just tweak it. optimization for later // consideration. // pci is copied from in to out // * addresses / ports are swapped if !fwd // * options, etc. are stripped // * checksums etc. are set // * if next layer is udp, it is set to icmp unreachable w/udp // * if next layer is tcp, it becomes a tcp rst or tcp fin w/opt data //------------------------------------------------------------------------- const uint8_t* Encode_Reject( EncodeType type, EncodeFlags flags, const Packet* p, uint32_t* len) { EncState enc; enc.type = type; enc.flags = flags; enc.payLoad = NULL; enc.payLen = 0; enc.ip_hdr = NULL; enc.ip_len = 0; enc.proto = 0; if ( encode_pkt ) p = encode_pkt; return Encode_Packet(&enc, p, len); } const uint8_t* Encode_Response( EncodeType type, EncodeFlags flags, const Packet* p, uint32_t* len, const uint8_t* payLoad, uint32_t payLen ) { EncState enc; enc.type = type; enc.flags = flags; enc.payLoad = payLoad; enc.payLen = payLen; enc.ip_hdr = NULL; enc.ip_len = 0; enc.proto = 0; if ( encode_pkt ) p = encode_pkt; return Encode_Packet(&enc, p, len); } //------------------------------------------------------------------------- // formatters: // - these packets undergo detection // - need to set Packet stuff except for frag3 which calls grinder // - include original options except for frag3 inner ip // - inner layer header is very similar but payload differs // - original ttl is always used //------------------------------------------------------------------------- #ifdef HAVE_DAQ_ADDRESS_SPACE_ID int Encode_Format_With_DAQ_Info ( EncodeFlags f, const Packet* p, Packet* c, PseudoPacketType type, const DAQ_PktHdr_t* phdr, uint32_t opaque) #elif defined(HAVE_DAQ_ACQUIRE_WITH_META) int Encode_Format_With_DAQ_Info ( EncodeFlags f, const Packet* p, Packet* c, PseudoPacketType type, uint32_t opaque) #else int Encode_Format (EncodeFlags f, const Packet* p, Packet* c, PseudoPacketType type) #endif { DAQ_PktHdr_t* pkth = (DAQ_PktHdr_t*)c->pkth; uint8_t* pkt = (uint8_t*)c->pkt; int i, next_layer = p->next_layer; Layer* lyr; size_t len; if ( next_layer < 1 ) return -1; memset(c, 0, PKT_ZERO_LEN); c->raw_ip6h = NULL; c->pkth = pkth; c->pkt = pkt; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID pkth->ingress_index = phdr->ingress_index; pkth->ingress_group = phdr->ingress_group; pkth->egress_index = phdr->egress_index; pkth->egress_group = phdr->egress_group; pkth->flags = phdr->flags & (~DAQ_PKT_FLAG_HW_TCP_CS_GOOD); #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) pkth->address_space_id_src = phdr->address_space_id_src; pkth->address_space_id_dst = phdr->address_space_id_dst; #else pkth->address_space_id = phdr->address_space_id; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) && defined(DAQ_VERSION) && DAQ_VERSION > 10 pkth->carrier_id = phdr->carrier_id; #endif pkth->opaque = opaque; if( type == PSEUDO_PKT_TCP || type == PSEUDO_PKT_IP ) pkth->priv_ptr = phdr->priv_ptr; #if defined(DAQ_VERSION) && DAQ_VERSION > 8 pkth->proto = phdr->proto; #endif #ifdef HAVE_DAQ_FLOW_ID pkth->flow_id = phdr->flow_id; #endif #elif defined(HAVE_DAQ_ACQUIRE_WITH_META) pkth->opaque = opaque; #endif #ifdef HAVE_DAQ_REAL_ADDRESSES if (pkth->flags & DAQ_PKT_FLAG_REAL_ADDRESSES) { pkth->n_real_sPort = phdr->n_real_sPort; pkth->n_real_dPort = phdr->n_real_dPort; pkth->real_sIP = phdr->real_sIP; pkth->real_dIP = phdr->real_dIP; } #endif if ( f & ENC_FLAG_NET ) { for ( i = next_layer-1; i >= 0; i-- ) if ( p->layers[i].proto == PROTO_IP4 || p->layers[i].proto == PROTO_IP6 ) break; if ( i < next_layer ) next_layer = i + 1; } // copy raw packet data to clone lyr = (Layer*)p->layers + next_layer - 1; len = lyr->start - p->pkt + lyr->length; memcpy((void*)c->pkt, p->pkt, len); // set up layers for ( i = 0; i < next_layer; i++ ) { const uint8_t* b = c->pkt + (p->layers[i].start - p->pkt); lyr = c->layers + i; lyr->proto = p->layers[i].proto; lyr->length = p->layers[i].length; lyr->start = (uint8_t*)b; if ( lyr->proto < PROTO_MAX ) encoders[lyr->proto].fformat(f, p, c, lyr); #ifdef DEBUG else FatalError("Encode_New() => unsupported proto = %d\n", lyr->proto); #endif } c->next_layer = next_layer; // setup payload info c->data = lyr->start + lyr->length; len = c->data - c->pkt; assert(len < PKT_MAX - IP_MAXPACKET); c->max_dsize = IP_MAXPACKET - len; c->proto_bits = p->proto_bits; c->packet_flags |= PKT_PSEUDO; c->pseudo_type = type; UpdateRebuiltPktCount(); switch ( type ) { case PSEUDO_PKT_SMB_SEG: case PSEUDO_PKT_DCE_SEG: case PSEUDO_PKT_DCE_FRAG: case PSEUDO_PKT_SMB_TRANS: c->packet_flags |= PKT_REASSEMBLED_OLD; break; default: break; } // setup pkt capture header pkth->caplen = pkth->pktlen = len; pkth->ts = p->pkth->ts; // cooked packet gets same policy as raw c->configPolicyId = p->configPolicyId; if ( !c->max_dsize ) return -1; return 0; } //------------------------------------------------------------------------- // formatters: // - these packets undergo detection // - need to set Packet stuff except for frag3 which calls grinder // - include original options except for frag3 inner ip // - inner layer header is very similar but payload differs // - original ttl is always used //------------------------------------------------------------------------- #ifdef HAVE_DAQ_ADDRESS_SPACE_ID int Encode_Format (EncodeFlags f, const Packet* p, Packet* c, PseudoPacketType type) { return Encode_Format_With_DAQ_Info(f, p, c, type, p->pkth, p->pkth->opaque); } #elif defined(HAVE_DAQ_ACQUIRE_WITH_META) int Encode_Format (EncodeFlags f, const Packet* p, Packet* c, PseudoPacketType type) { return Encode_Format_With_DAQ_Info(f, p, c, type, p->pkth->opaque); } #endif //------------------------------------------------------------------------- // updaters: these functions set length and checksum fields, only needed // when a packet is modified. some packets only have replacements so only // the checksums need to be updated. we always set the length rather than // checking each time if needed. //------------------------------------------------------------------------- void Encode_Update (Packet* p) { int i; uint32_t len = 0; DAQ_PktHdr_t* pkth = (DAQ_PktHdr_t*)p->pkth; p->actual_ip_len = 0; for ( i = p->next_layer - 1; i >= 0; i-- ) { Layer* lyr = p->layers + i; encoders[lyr->proto].fupdate(p, lyr, &len); } // see IP6_Update() for an explanation of this ... if ( !(p->packet_flags & PKT_MODIFIED) #ifdef NORMALIZER || (p->packet_flags & PKT_RESIZED) #endif ) pkth->caplen = pkth->pktlen = len; p->packet_flags &= ~PKT_LOGGED; } //------------------------------------------------------------------------- // internal packet support //------------------------------------------------------------------------- Packet* Encode_New () { Packet* p = SnortAlloc(sizeof(*p)); uint8_t* b = SnortAlloc(sizeof(*p->pkth) + PKT_MAX + SPARC_TWIDDLE); IP6Option* ip6_extensions = SnortAlloc(sizeof(IP6Option) * ScMaxIP6Extensions()); if ( !p || !b || !ip6_extensions ) FatalError("Encode_New() => Failed to allocate packet\n"); p->pkth = (void*)b; b += sizeof(*p->pkth); b += SPARC_TWIDDLE; p->pkt = b; p->ip6_extensions = ip6_extensions; return p; } void Encode_Delete (Packet* p) { free((void*)p->pkth); // cast away const! free(p->ip6_extensions); free(p); } /* Set the destination MAC address*/ void Encode_SetDstMAC(uint8_t *mac) { dst_mac = mac; } //------------------------------------------------------------------------- // private implementation stuff //------------------------------------------------------------------------- static uint8_t s_pkt[PKT_MAX]; static const uint8_t* Encode_Packet( EncState* enc, const Packet* p, uint32_t* len) { Buffer ibuf, obuf; ENC_STATUS status = ENC_BAD_PROTO; PROTO_ID next; ibuf.base = (uint8_t*)p->pkt; ibuf.off = ibuf.end = 0; ibuf.size = p->pkth->caplen; obuf.base = s_pkt; obuf.off = obuf.end = 0; obuf.size = sizeof(s_pkt); enc->layer = 0; enc->p = p; next = NextEncoder(enc); if ( next < PROTO_MAX ) { Encoder e = encoders[next].fencode; status = (*e)(enc, &ibuf, &obuf); } if ( status != ENC_OK || enc->layer != p->next_layer ) { *len = 0; return NULL; } *len = (uint32_t)obuf.end; return obuf.base + obuf.off; } //------------------------------------------------------------------------- // ip id considerations: // // we use dnet's rand services to generate a vector of random 16-bit values and // iterate over the vector as IDs are assigned. when we wrap to the beginning, // the vector is randomly reordered. //------------------------------------------------------------------------- static rand_t* s_rand = NULL; static uint16_t s_id_index = 0; static uint16_t s_id_pool[IP_ID_COUNT]; static void IpId_Init (void) { if ( s_rand ) rand_close(s_rand); s_rand = rand_open(); if ( !s_rand ) FatalError("encode::IpId_Init: rand_open() failed.\n"); rand_get(s_rand, s_id_pool, sizeof(s_id_pool)); } static void IpId_Term (void) { if ( s_rand ) rand_close(s_rand); s_rand = NULL; } static inline uint16_t IpId_Next () { #ifdef REG_TEST uint16_t id = htons(s_id_index + 1); #else uint16_t id = s_id_pool[s_id_index]; #endif s_id_index = (s_id_index + 1) % IP_ID_COUNT; if ( !s_id_index ) rand_shuffle(s_rand, s_id_pool, sizeof(s_id_pool), 1); return id; } //------------------------------------------------------------------------- // ttl considerations: // // we try to use the TTL captured for the session by the stream preprocessor // when the session started. if that is not available, we use the current // TTL for forward packets and use (maximum - current) TTL for reverse // packets. // // the reason we don't just force ttl to 255 (max) is to make it look a // little more authentic. // // for reference, flexresp used a const rand >= 64 in both directions (the // number was determined at startup and never changed); flexresp2 used the // next higher multiple of 64 in both directions; and react used a const // 64 in both directions. // // note that the ip6 hop limit field is entirely equivalent to the ip4 TTL. // hop limit is in fact a more accurrate name for the actual usage of this // field. //------------------------------------------------------------------------- static inline uint8_t GetTTL (const EncState* enc) { char dir; uint8_t ttl; int outer = !enc->ip_hdr; if ( !enc->p->ssnptr ) return 0; if ( enc->p->packet_flags & PKT_FROM_CLIENT ) dir = FORWARD(enc) ? SSN_DIR_FROM_CLIENT : SSN_DIR_FROM_SERVER; else dir = FORWARD(enc) ? SSN_DIR_FROM_SERVER : SSN_DIR_FROM_CLIENT; // outermost ip is considered to be outer here, // even if it is the only ip layer ... ttl = session_api->get_session_ttl(enc->p->ssnptr, dir, outer); // so if we don't get outer, we use inner if ( 0 == ttl && outer ) ttl = session_api->get_session_ttl(enc->p->ssnptr, dir, 0); return ttl; } static inline uint8_t FwdTTL (const EncState* enc, uint8_t ttl) { uint8_t new_ttl = GetTTL(enc); if ( !new_ttl ) new_ttl = ttl; return new_ttl; } static inline uint8_t RevTTL (const EncState* enc, uint8_t ttl) { uint8_t new_ttl = GetTTL(enc); if ( !new_ttl ) new_ttl = ( MAX_TTL - ttl ); if ( new_ttl < MIN_TTL ) new_ttl = MIN_TTL; return new_ttl; } //------------------------------------------------------------------------- // the if in UPDATE_BOUND can be defined out after testing because: // 1. the packet was already decoded in decode.c so is structurally sound; and // 2. encode takes at most the same space as decode. #define UPDATE_BOUND(buf, n) \ buf->end += n; \ if ( buf->end > buf->size ) \ return ENC_OVERFLOW //------------------------------------------------------------------------- // BUFLEN // Get the buffer length for a given protocol #define BUFF_DIFF(buf, ho) ((uint8_t*)(buf->base+buf->end)-(uint8_t*)ho) //------------------------------------------------------------------------- // ethernet //------------------------------------------------------------------------- static ENC_STATUS Eth_Encode (EncState* enc, Buffer* in, Buffer* out) { // not raw ip -> encode layer 2 int raw = ( enc->flags & ENC_FLAG_RAW ); EtherHdr* hi = (EtherHdr*)enc->p->layers[enc->layer-1].start; PROTO_ID next = NextEncoder(enc); // if not raw ip AND out buf is empty if ( !raw && (out->off == out->end) ) { // for alignment out->off = out->end = SPARC_TWIDDLE; } // if not raw ip OR out buf is not empty if ( !raw || (out->off != out->end) ) { // we get here for outer-most layer when not raw ip // we also get here for any encapsulated ethernet layer. EtherHdr* ho = (EtherHdr*)(out->base + out->end); UPDATE_BOUND(out, sizeof(*ho)); ho->ether_type = hi->ether_type; if ( FORWARD(enc) ) { memcpy(ho->ether_src, hi->ether_src, sizeof(ho->ether_src)); /*If user configured remote MAC address, use it*/ if (NULL != dst_mac) memcpy(ho->ether_dst, dst_mac, sizeof(ho->ether_dst)); else memcpy(ho->ether_dst, hi->ether_dst, sizeof(ho->ether_dst)); } else { memcpy(ho->ether_src, hi->ether_dst, sizeof(ho->ether_src)); /*If user configured remote MAC address, use it*/ if (NULL != dst_mac) memcpy(ho->ether_dst, dst_mac, sizeof(ho->ether_dst)); else memcpy(ho->ether_dst, hi->ether_src, sizeof(ho->ether_dst)); } } if ( next < PROTO_MAX ) return encoders[next].fencode(enc, in, out); return ENC_OK; } static ENC_STATUS Eth_Update (Packet* p, Layer* lyr, uint32_t* len) { *len += lyr->length; return ENC_OK; } static void Eth_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { EtherHdr* ch = (EtherHdr*)lyr->start; c->eh = ch; if ( REVERSE(f) ) { int i = lyr - c->layers; EtherHdr* ph = (EtherHdr*)p->layers[i].start; memcpy(ch->ether_dst, ph->ether_src, sizeof(ch->ether_dst)); memcpy(ch->ether_src, ph->ether_dst, sizeof(ch->ether_src)); } } //------------------------------------------------------------------------- // FabricPath //------------------------------------------------------------------------- static ENC_STATUS FPath_Encode (EncState* enc, Buffer* in, Buffer* out) { // not raw ip -> encode layer 2 int raw = ( enc->flags & ENC_FLAG_RAW ); FPathHdr* hi = (FPathHdr*)enc->p->layers[enc->layer-1].start; PROTO_ID next = NextEncoder(enc); // if not raw ip AND out buf is empty if ( !raw && (out->off == out->end) ) { // for alignment out->off = out->end = 0; } // if not raw ip OR out buf is not empty if ( !raw || (out->off != out->end) ) { // we get here for outer-most layer when not raw ip // we also get here for any encapsulated ethernet layer. FPathHdr* ho = (FPathHdr*)(out->base + out->end); UPDATE_BOUND(out, sizeof(*ho)); ho->fpath_type = hi->fpath_type; if ( FORWARD(enc) ) { memcpy(ho->fpath_src, hi->fpath_src, sizeof(ho->fpath_src)); memcpy(ho->fpath_dst, hi->fpath_dst, sizeof(ho->fpath_dst)); } else { memcpy(ho->fpath_src, hi->fpath_dst, sizeof(ho->fpath_src)); memcpy(ho->fpath_dst, hi->fpath_src, sizeof(ho->fpath_dst)); } } if ( next < PROTO_MAX ) return encoders[next].fencode(enc, in, out); return ENC_OK; } static ENC_STATUS FPath_Update (Packet* p, Layer* lyr, uint32_t* len) { *len += lyr->length; return ENC_OK; } static void FPath_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { FPathHdr* ch = (FPathHdr*)lyr->start; if ( REVERSE(f) ) { int i = lyr - c->layers; FPathHdr* ph = (FPathHdr*)p->layers[i].start; memcpy(ch->fpath_dst, ph->fpath_src, sizeof(ch->fpath_dst)); memcpy(ch->fpath_src, ph->fpath_dst, sizeof(ch->fpath_src)); } } //------------------------------------------------------------------------- // VLAN //------------------------------------------------------------------------- static void VLAN_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { #ifdef HAVE_DAQ_REAL_ADDRESSES if (!(p->pkth->flags & DAQ_PKT_FLAG_IGNORE_VLAN)) #endif c->vh = (VlanTagHdr*)lyr->start; } //------------------------------------------------------------------------- // GRE //------------------------------------------------------------------------- #ifdef GRE static void GRE_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { c->greh = (GREHdr*)lyr->start; } #endif //------------------------------------------------------------------------- // IP4 //------------------------------------------------------------------------- static ENC_STATUS IP4_Encode (EncState* enc, Buffer* in, Buffer* out) { int len; uint32_t start = out->end; IPHdr* hi = (IPHdr*)enc->p->layers[enc->layer-1].start; IPHdr* ho = (IPHdr*)(out->base + out->end); PROTO_ID next = NextEncoder(enc); UPDATE_BOUND(out, sizeof(*ho)); /* IPv4 encoded header is hardcoded 20 bytes */ ho->ip_verhl = 0x45; ho->ip_off = 0; ho->ip_id = IpId_Next(); ho->ip_tos = hi->ip_tos; ho->ip_proto = hi->ip_proto; if ( FORWARD(enc) ) { ho->ip_src.s_addr = hi->ip_src.s_addr; ho->ip_dst.s_addr = hi->ip_dst.s_addr; ho->ip_ttl = FwdTTL(enc, hi->ip_ttl); } else { ho->ip_src.s_addr = hi->ip_dst.s_addr; ho->ip_dst.s_addr = hi->ip_src.s_addr; ho->ip_ttl = RevTTL(enc, hi->ip_ttl); } enc->ip_hdr = (uint8_t*)hi; enc->ip_len = IP_HLEN(hi) << 2; if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if ( ENC_OK != err ) return err; } if ( enc->proto ) { ho->ip_proto = enc->proto; enc->proto = 0; } len = out->end - start; ho->ip_len = htons((uint16_t)len); ho->ip_csum = 0; /* IPv4 encoded header is hardcoded 20 bytes, we save some * cycles and use the literal header size for checksum */ ho->ip_csum = in_chksum_ip((const uint16_t *)ho, sizeof *ho); return ENC_OK; } static ENC_STATUS IP4_Update (Packet* p, Layer* lyr, uint32_t* len) { IPHdr* h = (IPHdr*)(lyr->start); int i = lyr - p->layers; *len += GET_IP_HDR_LEN(h); if ( i + 1 == p->next_layer ) { *len += p->dsize; } h->ip_len = htons((uint16_t)*len); if ( !PacketWasCooked(p) || (p->packet_flags & PKT_REBUILT_FRAG) ) { h->ip_csum = 0; h->ip_csum = in_chksum_ip((const uint16_t *)h, GET_IP_HDR_LEN(h)); } return ENC_OK; } static void IP4_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { // TBD handle nested ip layers IPHdr* ch = (IPHdr*)lyr->start; c->iph = ch; if ( REVERSE(f) ) { int i = lyr - c->layers; IPHdr* ph = (IPHdr*)p->layers[i].start; ch->ip_src.s_addr = ph->ip_dst.s_addr; ch->ip_dst.s_addr = ph->ip_src.s_addr; } if ( f & ENC_FLAG_DEF ) { int i = lyr - c->layers; if ( i + 1 == p->next_layer ) { lyr->length = sizeof(*ch); ch->ip_len = htons(lyr->length); SET_IP_HLEN(ch, lyr->length >> 2); } } sfiph_build(c, c->iph, AF_INET); } //------------------------------------------------------------------------- // ICMP // UNR encoder creates ICMP unreachable //------------------------------------------------------------------------- static inline int IcmpCode (EncodeType et) { switch ( et ) { case ENC_UNR_NET: return ICMP_UNREACH_NET; case ENC_UNR_HOST: return ICMP_UNREACH_HOST; case ENC_UNR_PORT: return ICMP_UNREACH_PORT; case ENC_UNR_FW: return ICMP_UNREACH_FILTER_PROHIB; default: break; } return ICMP_UNREACH_PORT; } typedef struct { uint8_t type; uint8_t code; uint16_t cksum; uint32_t unused; } IcmpHdr; static ENC_STATUS UN4_Encode (EncState* enc, Buffer* in, Buffer* out) { uint8_t* p; uint8_t* hi = enc->p->layers[enc->layer-1].start; IcmpHdr* ho = (IcmpHdr*)(out->base + out->end); #ifdef DEBUG if ( enc->type < ENC_UNR_NET ) return ENC_BAD_OPT; #endif enc->proto = IPPROTO_ICMP; UPDATE_BOUND(out, sizeof(*ho)); ho->type = ICMP_UNREACH; ho->code = IcmpCode(enc->type); ho->cksum = 0; ho->unused = 0; // copy original ip header p = out->base + out->end; UPDATE_BOUND(out, enc->ip_len); memcpy(p, enc->ip_hdr, enc->ip_len); // copy first 8 octets of original ip data (ie udp header) p = out->base + out->end; UPDATE_BOUND(out, ICMP_UNREACH_DATA); memcpy(p, hi, ICMP_UNREACH_DATA); ho->cksum = in_chksum_icmp((uint16_t *)ho, BUFF_DIFF(out, ho)); return ENC_OK; } static ENC_STATUS ICMP4_Update (Packet* p, Layer* lyr, uint32_t* len) { IcmpHdr* h = (IcmpHdr*)(lyr->start); *len += lyr->length + p->dsize; if ( !PacketWasCooked(p) || (p->packet_flags & PKT_REBUILT_FRAG) ) { h->cksum = 0; h->cksum = in_chksum_icmp((uint16_t *)h, *len); } return ENC_OK; } static void ICMP4_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { // TBD handle nested icmp4 layers c->icmph = (ICMPHdr*)lyr->start; } //------------------------------------------------------------------------- // UDP //------------------------------------------------------------------------- static ENC_STATUS UDP_Encode (EncState* enc, Buffer* in, Buffer* out) { PROTO_ID next = PROTO_MAX; if ( enc->layer < enc->p->next_layer ) { next = enc->p->layers[enc->layer].proto; } if ( enc->type == ENC_UDP || ((PROTO_GTP == next) && (encoders[next].fencode)) ) { int len; ENC_STATUS err; uint32_t start = out->end; UDPHdr* hi = (UDPHdr*)enc->p->layers[enc->layer-1].start; UDPHdr* ho = (UDPHdr*)(out->base + out->end); UPDATE_BOUND(out, sizeof(*ho)); if ( FORWARD(enc) ) { ho->uh_sport = hi->uh_sport; ho->uh_dport = hi->uh_dport; } else { ho->uh_sport = hi->uh_dport; ho->uh_dport = hi->uh_sport; } if ( enc->type != ENC_UDP) { next = NextEncoder(enc); if ( next < PROTO_MAX ) { err = encoders[next].fencode(enc, in, out); if (ENC_OK != err ) return err; } len = out->end - start; } else { uint8_t* pdu = out->base + out->end; UPDATE_BOUND(out, enc->payLen); memcpy(pdu, enc->payLoad, enc->payLen); len = enc->payLen + sizeof(UDPHdr); } ho->uh_len = htons((uint16_t)len); ho->uh_chk = 0; if (IP_VER((IPHdr *)enc->ip_hdr) == 4) { pseudoheader ps; ps.sip = ((IPHdr *)enc->ip_hdr)->ip_src.s_addr; ps.dip = ((IPHdr *)enc->ip_hdr)->ip_dst.s_addr; ps.zero = 0; ps.protocol = IPPROTO_UDP; ps.len = ho->uh_len; ho->uh_chk = in_chksum_udp(&ps, (uint16_t *)ho, len); } else { pseudoheader6 ps6; memcpy(ps6.sip, ((IP6RawHdr *)enc->ip_hdr)->ip6_src.s6_addr, sizeof(ps6.sip)); memcpy(ps6.dip, ((IP6RawHdr *)enc->ip_hdr)->ip6_dst.s6_addr, sizeof(ps6.dip)); ps6.zero = 0; ps6.protocol = IPPROTO_UDP; ps6.len = ho->uh_len; ho->uh_chk = in_chksum_udp6(&ps6, (uint16_t *)ho, len); } return ENC_OK; } if ( IP_VER((IPHdr*)enc->ip_hdr) == 4 ) return UN4_Encode(enc, in, out); return UN6_UDP_Encode(enc, in, out); } static ENC_STATUS UDP_Update (Packet* p, Layer* lyr, uint32_t* len) { UDPHdr* h = (UDPHdr*)(lyr->start); *len += sizeof(*h) + p->dsize; h->uh_len = htons((uint16_t)*len); if ( !PacketWasCooked(p) || (p->packet_flags & PKT_REBUILT_FRAG) ) { h->uh_chk = 0; if (IS_IP4(p)) { pseudoheader ps; ps.sip = ((IPHdr *)(lyr-1)->start)->ip_src.s_addr; ps.dip = ((IPHdr *)(lyr-1)->start)->ip_dst.s_addr; ps.zero = 0; ps.protocol = IPPROTO_UDP; ps.len = htons((uint16_t)*len); h->uh_chk = in_chksum_udp(&ps, (uint16_t *)h, *len); } else { pseudoheader6 ps6; for (lyr--; lyr->proto != PROTO_IP6 && lyr != p->layers; lyr--); if (lyr->proto == PROTO_IP6) { memcpy(ps6.sip, ((IP6RawHdr *)lyr->start)->ip6_src.s6_addr, sizeof(ps6.sip)); memcpy(ps6.dip, ((IP6RawHdr *)lyr->start)->ip6_dst.s6_addr, sizeof(ps6.dip)); ps6.zero = 0; ps6.protocol = IPPROTO_UDP; ps6.len = htons((uint16_t)*len); h->uh_chk = in_chksum_udp6(&ps6, (uint16_t *)h, *len); } } } return ENC_OK; } static void UDP_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { UDPHdr* ch = (UDPHdr*)lyr->start; c->udph = ch; if ( REVERSE(f) ) { int i = lyr - c->layers; UDPHdr* ph = (UDPHdr*)p->layers[i].start; ch->uh_sport = ph->uh_dport; ch->uh_dport = ph->uh_sport; } c->sp = ntohs(ch->uh_sport); c->dp = ntohs(ch->uh_dport); } //------------------------------------------------------------------------- // TCP // encoder creates TCP RST // should always try to use acceptable ack since we send RSTs in a // stateless fashion ... from rfc 793: // // In all states except SYN-SENT, all reset (RST) segments are validated // by checking their SEQ-fields. A reset is valid if its sequence number // is in the window. In the SYN-SENT state (a RST received in response // to an initial SYN), the RST is acceptable if the ACK field // acknowledges the SYN. //------------------------------------------------------------------------- static ENC_STATUS TCP_Encode (EncState* enc, Buffer* in, Buffer* out) { int len, ctl; TCPHdr* hi = (TCPHdr*)enc->p->layers[enc->layer-1].start; TCPHdr* ho = (TCPHdr*)(out->base + out->end); UPDATE_BOUND(out, sizeof(*ho)); len = GET_TCP_HDR_LEN(hi) - sizeof(*hi); UPDATE_BOUND(in, len); ctl = (hi->th_flags & TH_SYN) ? 1 : 0; if ( FORWARD(enc) ) { ho->th_sport = hi->th_sport; ho->th_dport = hi->th_dport; // th_seq depends on whether the data passes or drops if ( DAQ_GetInterfaceMode(enc->p->pkth) != DAQ_MODE_INLINE ) ho->th_seq = htonl(ntohl(hi->th_seq) + enc->p->dsize + ctl); else ho->th_seq = hi->th_seq; ho->th_ack = hi->th_ack; } else { ho->th_sport = hi->th_dport; ho->th_dport = hi->th_sport; ho->th_seq = hi->th_ack; ho->th_ack = htonl(ntohl(hi->th_seq) + enc->p->dsize + ctl); } if ( enc->flags & ENC_FLAG_SEQ ) { uint32_t seq = ntohl(ho->th_seq); seq += (enc->flags & ENC_FLAG_VAL); ho->th_seq = htonl(seq); } ho->th_offx2 = 0; SET_TCP_OFFSET(ho, (TCP_HDR_LEN >> 2)); ho->th_win = ho->th_urp = 0; if ( enc->type == ENC_TCP_FIN || enc->type == ENC_TCP_PUSH ) { if ( enc->payLoad && enc->payLen > 0 ) { uint8_t* pdu = out->base + out->end; UPDATE_BOUND(out, enc->payLen); memcpy(pdu, enc->payLoad, enc->payLen); } ho->th_flags = TH_ACK; if ( enc->type == ENC_TCP_PUSH ) { ho->th_flags |= TH_PUSH; ho->th_win = htons(65535); } else { ho->th_flags |= TH_FIN; } } else { ho->th_flags = TH_RST | TH_ACK; } // in case of ip6 extension headers, this gets next correct enc->proto = IPPROTO_TCP; ho->th_sum = 0; if (IP_VER((IPHdr *)enc->ip_hdr) == 4) { pseudoheader ps; int len = BUFF_DIFF(out, ho); ps.sip = ((IPHdr *)(enc->ip_hdr))->ip_src.s_addr; ps.dip = ((IPHdr *)(enc->ip_hdr))->ip_dst.s_addr; ps.zero = 0; ps.protocol = IPPROTO_TCP; ps.len = htons((uint16_t)len); ho->th_sum = in_chksum_tcp(&ps, (uint16_t *)ho, len); } else { pseudoheader6 ps6; int len = BUFF_DIFF(out, ho); memcpy(ps6.sip, ((IP6RawHdr *)enc->ip_hdr)->ip6_src.s6_addr, sizeof(ps6.sip)); memcpy(ps6.dip, ((IP6RawHdr *)enc->ip_hdr)->ip6_dst.s6_addr, sizeof(ps6.dip)); ps6.zero = 0; ps6.protocol = IPPROTO_TCP; ps6.len = htons((uint16_t)len); ho->th_sum = in_chksum_tcp6(&ps6, (uint16_t *)ho, len); } return ENC_OK; } static ENC_STATUS TCP_Update (Packet* p, Layer* lyr, uint32_t* len) { TCPHdr* h = (TCPHdr*)(lyr->start); *len += GET_TCP_HDR_LEN(h) + p->dsize; if ( !PacketWasCooked(p) || (p->packet_flags & PKT_REBUILT_FRAG) ) { h->th_sum = 0; if (IS_IP4(p)) { pseudoheader ps; ps.sip = ((IPHdr *)(lyr-1)->start)->ip_src.s_addr; ps.dip = ((IPHdr *)(lyr-1)->start)->ip_dst.s_addr; ps.zero = 0; ps.protocol = IPPROTO_TCP; ps.len = htons((uint16_t)*len); h->th_sum = in_chksum_tcp(&ps, (uint16_t *)h, *len); } else { pseudoheader6 ps6; for (lyr--; lyr->proto != PROTO_IP6 && lyr != p->layers; lyr--); if (lyr->proto == PROTO_IP6) { memcpy(ps6.sip, ((IP6RawHdr *)lyr->start)->ip6_src.s6_addr, sizeof(ps6.sip)); memcpy(ps6.dip, ((IP6RawHdr *)lyr->start)->ip6_dst.s6_addr, sizeof(ps6.dip)); ps6.zero = 0; ps6.protocol = IPPROTO_TCP; ps6.len = htons((uint16_t)*len); h->th_sum = in_chksum_tcp6(&ps6, (uint16_t *)h, *len); } } } return ENC_OK; } static void TCP_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { TCPHdr* ch = (TCPHdr*)lyr->start; c->tcph = ch; if ( REVERSE(f) ) { int i = lyr - c->layers; TCPHdr* ph = (TCPHdr*)p->layers[i].start; ch->th_sport = ph->th_dport; ch->th_dport = ph->th_sport; } c->sp = ntohs(ch->th_sport); c->dp = ntohs(ch->th_dport); } //------------------------------------------------------------------------- // IP6 encoder //------------------------------------------------------------------------- static ENC_STATUS IP6_Encode (EncState* enc, Buffer* in, Buffer* out) { int len; uint32_t start = out->end; IP6RawHdr* hi = (IP6RawHdr*)enc->p->layers[enc->layer-1].start; IP6RawHdr* ho = (IP6RawHdr*)(out->base + out->end); PROTO_ID next = NextEncoder(enc); UPDATE_BOUND(out, sizeof(*ho)); ho->ip6flow = htonl(ntohl(hi->ip6flow) & 0xFFF00000); ho->ip6nxt = hi->ip6nxt; if ( FORWARD(enc) ) { memcpy(ho->ip6_src.s6_addr, hi->ip6_src.s6_addr, sizeof(ho->ip6_src.s6_addr)); memcpy(ho->ip6_dst.s6_addr, hi->ip6_dst.s6_addr, sizeof(ho->ip6_dst.s6_addr)); ho->ip6hops = FwdTTL(enc, hi->ip6hops); } else { memcpy(ho->ip6_src.s6_addr, hi->ip6_dst.s6_addr, sizeof(ho->ip6_src.s6_addr)); memcpy(ho->ip6_dst.s6_addr, hi->ip6_src.s6_addr, sizeof(ho->ip6_dst.s6_addr)); ho->ip6hops = RevTTL(enc, hi->ip6hops); } enc->ip_hdr = (uint8_t*)hi; enc->ip_len = sizeof(*hi); if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if ( ENC_OK != err ) return err; } if ( enc->proto ) { ho->ip6nxt = enc->proto; enc->proto = 0; } len = out->end - start; ho->ip6plen = htons((uint16_t)(len - sizeof(*ho))); return ENC_OK; } static ENC_STATUS IP6_Update (Packet* p, Layer* lyr, uint32_t* len) { IP6RawHdr* h = (IP6RawHdr*)(lyr->start); int i = lyr - p->layers; // if we didn't trim payload or format this packet, // we may not know the actual lengths because not all // extension headers are decoded and we stop at frag6. // in such case we do not modify the packet length. if ( (p->packet_flags & PKT_MODIFIED) #ifdef NORMALIZER && !(p->packet_flags & PKT_RESIZED) #endif ) { *len = ntohs(h->ip6plen) + sizeof(*h); } else { if ( i + 1 == p->next_layer ) *len += lyr->length + p->dsize; // w/o all extension headers, can't use just the // fixed ip6 header length so we compute header delta else *len += lyr[1].start - lyr->start; // len includes header, remove for payload h->ip6plen = htons((uint16_t)(*len - sizeof(*h))); } return ENC_OK; } static void IP6_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { IP6RawHdr* ch = (IP6RawHdr*)lyr->start; if ( REVERSE(f) ) { int i = lyr - c->layers; IP6RawHdr* ph = (IP6RawHdr*)p->layers[i].start; memcpy(ch->ip6_src.s6_addr, ph->ip6_dst.s6_addr, sizeof(ch->ip6_src.s6_addr)); memcpy(ch->ip6_dst.s6_addr, ph->ip6_src.s6_addr, sizeof(ch->ip6_dst.s6_addr)); } if ( f & ENC_FLAG_DEF ) { int i = lyr - c->layers; if ( i + 1 == p->next_layer ) { uint8_t* b = (uint8_t*)p->ip6_extensions[p->ip6_frag_index].data; if ( b ) lyr->length = b - p->layers[i].start; } } sfiph_build(c, ch, AF_INET6); // set outer to inner so this will always wind pointing to inner c->raw_ip6h = ch; } //------------------------------------------------------------------------- // IP6 options functions //------------------------------------------------------------------------- static ENC_STATUS Opt6_Encode (EncState* enc, Buffer* in, Buffer* out) { // we don't encode ext headers PROTO_ID next = NextEncoder(enc); if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if ( ENC_OK != err ) return err; } return ENC_OK; } static ENC_STATUS Opt6_Update (Packet* p, Layer* lyr, uint32_t* len) { int i = lyr - p->layers; *len += lyr->length; if ( i + 1 == p->next_layer ) *len += p->dsize; return ENC_OK; } //------------------------------------------------------------------------- // ICMP6 functions //------------------------------------------------------------------------- static inline int IcmpV6Code (EncodeType et) { switch ( et ) { case ENC_UNR_NET: return ICMP6_UNREACH_NET; case ENC_UNR_HOST: return ICMP6_UNREACH_HOST; case ENC_UNR_PORT: return ICMP6_UNREACH_PORT; case ENC_UNR_FW: return ICMP6_UNREACH_FILTER_PROHIB; default: break; } return ICMP6_UNREACH_PORT; } static inline ENC_STATUS UN6_Encode_Icmpv6Hdr( EncState *enc, IcmpHdr *ho, Buffer *out ) { enc->proto = IPPROTO_ICMPV6; UPDATE_BOUND(out, sizeof(*ho)); ho->type = 1; // dest unreachable ho->code = IcmpV6Code( enc->type ); ho->cksum = 0; ho->unused = 0; return ENC_OK; } static inline ENC_STATUS UN6_Encode_OrigIcmpV6( EncState *enc, Buffer *out ) { uint8_t* p; // copy original ip header p = out->base + out->end; UPDATE_BOUND(out, enc->ip_len); // TBD should be able to elminate enc->ip_hdr by using layer-2 memcpy(p, enc->ip_hdr, enc->ip_len); return ENC_OK; } static inline ENC_STATUS UN6_Encode_OrigUdp( EncState *enc, Buffer *out ) { uint8_t* p; uint8_t* hi = enc->p->layers[enc->layer-1].start; // copy original ip header p = out->base + out->end; UPDATE_BOUND(out, enc->ip_len); // TBD should be able to elminate enc->ip_hdr by using layer-2 memcpy(p, enc->ip_hdr, enc->ip_len); ((IP6RawHdr*)p)->ip6nxt = IPPROTO_UDP; // copy first 8 octets of original ip data (ie udp header) // TBD: copy up to minimum MTU worth of data p = out->base + out->end; UPDATE_BOUND(out, ICMP_UNREACH_DATA); memcpy(p, hi, ICMP_UNREACH_DATA); return ENC_OK; } static inline void UN6_Encode_Compute_Chksum( EncState *enc, IcmpHdr *ho, Buffer *out ) { pseudoheader6 ps6; int len; len = BUFF_DIFF(out, ho); memcpy(ps6.sip, ((IP6RawHdr *)enc->ip_hdr)->ip6_src.s6_addr, sizeof(ps6.sip)); memcpy(ps6.dip, ((IP6RawHdr *)enc->ip_hdr)->ip6_dst.s6_addr, sizeof(ps6.dip)); ps6.zero = 0; ps6.protocol = IPPROTO_ICMPV6; ps6.len = htons((uint16_t)(len)); ho->cksum = in_chksum_icmp6(&ps6, (uint16_t *)ho, len); } static ENC_STATUS UN6_ICMP6_Encode (EncState* enc, Buffer* in, Buffer* out) { IcmpHdr* ho = (IcmpHdr*)(out->base + out->end); #ifdef DEBUG if ( enc->type < ENC_UNR_NET ) return ENC_BAD_OPT; #endif UN6_Encode_Icmpv6Hdr( enc, ho, out); UN6_Encode_OrigIcmpV6( enc, out ); UN6_Encode_Compute_Chksum( enc, ho, out ); return ENC_OK; } static ENC_STATUS UN6_UDP_Encode(EncState* enc, Buffer* in, Buffer* out) { IcmpHdr* ho = (IcmpHdr*)(out->base + out->end); #ifdef DEBUG if ( enc->type < ENC_UNR_NET ) return ENC_BAD_OPT; #endif UN6_Encode_Icmpv6Hdr( enc, ho, out); UN6_Encode_OrigUdp( enc, out ); UN6_Encode_Compute_Chksum( enc, ho, out ); return ENC_OK; } static ENC_STATUS ICMP6_Update (Packet* p, Layer* lyr, uint32_t* len) { IcmpHdr* h = (IcmpHdr*)(lyr->start); *len += lyr->length + p->dsize; if ( !PacketWasCooked(p) || (p->packet_flags & PKT_REBUILT_FRAG) ) { pseudoheader6 ps6; for (lyr--; lyr->proto != PROTO_IP6 && lyr != p->layers; lyr--); if (lyr->proto == PROTO_IP6) { h->cksum = 0; memcpy(ps6.sip, ((IP6RawHdr *)lyr->start)->ip6_src.s6_addr, sizeof(ps6.sip)); memcpy(ps6.dip, ((IP6RawHdr *)lyr->start)->ip6_dst.s6_addr, sizeof(ps6.dip)); ps6.zero = 0; ps6.protocol = IPPROTO_ICMPV6; ps6.len = htons((uint16_t)*len); h->cksum = in_chksum_icmp6(&ps6, (uint16_t *)h, *len); } } return ENC_OK; } static void ICMP6_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { // TBD handle nested icmp6 layers c->icmp6h = (ICMP6Hdr*)lyr->start; } //------------------------------------------------------------------------- // GTP functions //------------------------------------------------------------------------- static ENC_STATUS update_GTP_length(GTPHdr* h, int gtp_total_len ) { /*The first 3 bits are version number*/ uint8_t version = (h->flag & 0xE0) >> 5; switch (version) { case 0: /*GTP v0*/ h->length = htons((uint16_t)(gtp_total_len - GTP_V0_HEADER_LEN)); break; case 1: /*GTP v1*/ h->length = htons((uint16_t)(gtp_total_len - GTP_MIN_LEN)); break; default: return ENC_BAD_PROTO; } return ENC_OK; } static ENC_STATUS GTP_Encode (EncState* enc, Buffer* in, Buffer* out) { int n = enc->p->layers[enc->layer-1].length; int len; GTPHdr* hi = (GTPHdr*) (enc->p->layers[enc->layer-1].start); GTPHdr* ho = (GTPHdr*)(out->base + out->end); uint32_t start = out->end; PROTO_ID next = NextEncoder(enc); UPDATE_BOUND(out, n); memcpy(ho, hi, n); if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if (ENC_OK != err ) return err; } len = out->end - start; return( update_GTP_length(ho,len)); } static ENC_STATUS GTP_Update (Packet* p, Layer* lyr, uint32_t* len) { GTPHdr* h = (GTPHdr*)(lyr->start); *len += lyr->length; return( update_GTP_length(h,*len)); } //------------------------------------------------------------------------- // PPPoE functions //------------------------------------------------------------------------- static ENC_STATUS PPPoE_Encode (EncState* enc, Buffer* in, Buffer* out) { int n = enc->p->layers[enc->layer-1].length; int len; PPPoEHdr* hi = (PPPoEHdr*)(enc->p->layers[enc->layer-1].start); PPPoEHdr* ho = (PPPoEHdr*)(out->base + out->end); uint32_t start; PROTO_ID next = NextEncoder(enc); UPDATE_BOUND(out, n); memcpy(ho, hi, n); start = out->end; if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if (ENC_OK != err ) return err; } len = out->end - start; ho->length = htons((uint16_t)len); return ENC_OK; } //------------------------------------------------------------------------- // MPLS functions //------------------------------------------------------------------------- static ENC_STATUS MPLS_Encode (EncState* enc, Buffer* in, Buffer* out) { uint16_t n; uint8_t* hi = NULL; uint8_t* ho; PROTO_ID next; SessionControlBlock* scb = (SessionControlBlock*)(enc->p->ssnptr); if ( FORWARD(enc) ) { if( scb != NULL && scb->clientMplsHeader != NULL ) { n = scb->clientMplsHeader->length; hi = scb->clientMplsHeader->start; } } else { if( scb != NULL && scb->serverMplsHeader != NULL ) { n = scb->serverMplsHeader->length; hi = scb->serverMplsHeader->start; } } if(!(hi) ) { n = enc->p->layers[enc->layer-1].length; hi = enc->p->layers[enc->layer-1].start; } ho = (uint8_t*)(out->base + out->end); next = NextEncoder(enc); UPDATE_BOUND(out, n); memcpy(ho, hi, n); if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if (ENC_OK != err ) return err; } return ENC_OK; } //------------------------------------------------------------------------- // XXX (generic) functions //------------------------------------------------------------------------- static ENC_STATUS XXX_Encode (EncState* enc, Buffer* in, Buffer* out) { int n = enc->p->layers[enc->layer-1].length; uint8_t* hi = enc->p->layers[enc->layer-1].start; uint8_t* ho = (uint8_t*)(out->base + out->end); PROTO_ID next = NextEncoder(enc); UPDATE_BOUND(out, n); memcpy(ho, hi, n); if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if (ENC_OK != err ) return err; } return ENC_OK; } // for general cases, may need to move dsize out of top, tcp, and // udp and put in Encode_Update() (then this can be eliminated and // xxx called instead). (another thought is to add data as a "layer"). #if 0 static ENC_STATUS Top_Update (Packet* p, Layer* lyr, uint32_t* len) { *len += lyr->length + p->dsize; return ENC_OK; } #endif static ENC_STATUS XXX_Update (Packet* p, Layer* lyr, uint32_t* len) { *len += lyr->length; return ENC_OK; } static ENC_STATUS GRE_Update (Packet* p, Layer* lyr, uint32_t* len) { GREHdr* h = (GREHdr*)(lyr->start); *len += lyr->length; if(GRE_CHKSUM(h)) { if(lyr->length >= 6) { uint16_t *csum = (uint16_t *) (lyr->start + 4); *csum = 0; *csum = ones_compl_checksum((const char *)h, *len); } } return ENC_OK; } static void XXX_Format (EncodeFlags f, const Packet* p, Packet* c, Layer* lyr) { // nop } static ENC_STATUS GRE_Encode (EncState* enc, Buffer* in, Buffer* out) { int n = enc->p->layers[enc->layer-1].length; GREHdr* hi = (GREHdr *)enc->p->layers[enc->layer-1].start; GREHdr* ho = (GREHdr *)(out->base + out->end); PROTO_ID next = NextEncoder(enc); UPDATE_BOUND(out, n); memcpy(ho, hi, n); if ( next < PROTO_MAX ) { ENC_STATUS err = encoders[next].fencode(enc, in, out); if (ENC_OK != err ) return err; } if(GRE_CHKSUM(ho)) { if(n >= 6) { int len = BUFF_DIFF(out, ho); uint16_t *csum = (uint16_t *) ((uint8_t *)ho + 4); *csum = 0; *csum = ones_compl_checksum((const char *)ho, len); } } return ENC_OK; } //------------------------------------------------------------------------- // function table: // these must be in the same order PROTO_IDs are defined! // all entries must have a function //------------------------------------------------------------------------- static EncoderFunctions encoders[PROTO_MAX] = { { Eth_Encode, Eth_Update, Eth_Format }, { FPath_Encode, FPath_Update, FPath_Format }, // FabricPath { XXX_Encode, XXX_Update, XXX_Format }, // Cisco Metadata { IP4_Encode, IP4_Update, IP4_Format }, { UN4_Encode, ICMP4_Update, ICMP4_Format }, { XXX_Encode, XXX_Update, XXX_Format, }, // ICMP_IP4 { UDP_Encode, UDP_Update, UDP_Format }, { TCP_Encode, TCP_Update, TCP_Format }, { IP6_Encode, IP6_Update, IP6_Format }, { Opt6_Encode, Opt6_Update, XXX_Format }, // IP6 Hop Opts { Opt6_Encode, Opt6_Update, XXX_Format }, // IP6 Dst Opts { UN6_ICMP6_Encode, ICMP6_Update, ICMP6_Format }, { XXX_Encode, XXX_Update, XXX_Format, }, // ICMP_IP6 { XXX_Encode, XXX_Update, VLAN_Format }, #ifdef GRE { GRE_Encode, GRE_Update, GRE_Format }, { XXX_Encode, XXX_Update, XXX_Format }, // ERSPAN #endif { PPPoE_Encode,XXX_Update, XXX_Format }, { XXX_Encode, XXX_Update, XXX_Format }, // PPP Encap #ifdef MPLS { MPLS_Encode, XXX_Update, XXX_Format }, // MPLS #endif { XXX_Encode, XXX_Update, XXX_Format, }, // ARP { GTP_Encode, GTP_Update, XXX_Format, }, // GTP { XXX_Encode, XXX_Update, XXX_Format, } // Auth Header }; snort-2.9.20/src/dynamic-preprocessors/0000755000175000017500000000000014242725717016226 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/file/0000755000175000017500000000000014242725717017145 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/file/file_event_log.c0000644000175000017500000002517014241075731022270 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 4.11.2013 - Initial Source Code. ** Log file events */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "event.h" #include "sf_snort_packet.h" #include "snort_debug.h" #include "sf_textlog.h" #include "output_api.h" #include "output_lib.h" #include "sf_dynamic_common.h" #include "file_sha.h" #include "file_event_log.h" #include "sf_dynamic_preprocessor.h" #define OUTPUT_MOD_VERSION 1 #define OUTPUT_NAME "filelog" #define OUTPUT_TYPE (DYNAMIC_OUTPUT_TYPE_FLAG__ALERT) /* full buf was chosen to allow printing max size packets * in hex/ascii mode: * each byte => 2 nibbles + space + ascii + overhead */ #define FULL_BUF (4*65535) #define FAST_BUF (4*K_BYTES) /* * not defined for backwards compatibility * (default is produced by OpenAlertFile() #define DEFAULT_FILE "alert.fast" */ #define DEFAULT_LIMIT (128*M_BYTES) typedef struct { TextLog* log; uint8_t packet_flag; } FileLogData; static void file_log_init(struct _SnortConfig *, char* args); static void file_log_term(int, void* arg); static int file_log_parse (void** config, char* args, const char* default_output_file); static void file_log_exec(void* packet, char* msg, void* arg, void* eventInfo); OUTPUT_SO_PUBLIC Output_Module_t OUTPUT_MODULE_DATA = { .api_major_version = OUTPUT_API_MAJOR_VERSION, .api_minor_version = OUTPUT_API_MINOR_VERSION, .module_version = OUTPUT_MOD_VERSION, .name = OUTPUT_NAME, .type = OUTPUT_TYPE, .default_file = "file", .load = file_log_init, .parse_args = file_log_parse, .postconfig = NULL, .alert_output = file_log_exec, .log_output = NULL, .rotate = NULL, .shutdown = file_log_term, .next = NULL }; /*********************Output plugin for file log*******************************/ static char* getFullName (char* dir, const char* filespec) { char *filename = NULL; char buffer[STD_BUF + 1]; if(filespec == NULL) { _dod.fatalMsg("no argument in this file option, " "remove extra ':' at the end of the alert option\n"); } buffer[STD_BUF] = '\0'; if(filespec[0] == '/') { /* absolute filespecs are saved as is */ filename = strdup(filespec); } else { /* relative filespec is considered relative to the log directory */ /* or /var/log if the log directory has not been set */ /* Make sure this function isn't called before log dir is set */ if (dir != NULL) { strncpy(buffer, dir, sizeof(buffer) - 1); } else { strncpy(buffer, "/var/log/snort", sizeof(buffer) - 1); } strncat(buffer, "/", STD_BUF - strlen(buffer)); strncat(buffer, filespec, STD_BUF - strlen(buffer)); buffer[sizeof(buffer) - 1] = '\0'; filename = strdup(buffer); } return filename; } static void file_log_init (struct _SnortConfig *sc, char* args) { init_output_module(sc, &OUTPUT_MODULE_DATA, args); _dod.logMsg("%s: version %d\n", __FUNCTION__, OUTPUT_MOD_VERSION); } static void file_log_term (int unused, void* arg) { FileLogData *data = (FileLogData *)arg; DEBUG_WRAP(_dod.debugMsg(DEBUG_LOG, "%s\n", "FileLog_Term");); /*free memory from FileLogData */ if ( data->log ) _dod.textLog_Term(data->log); free(data); /*free memory from FileLogData */ } static void file_log_exec(void* packet, char* msg, void* arg, void* eventInfo) { FileLogData *data = (FileLogData *)arg; SFSnortPacket *p = (SFSnortPacket*)packet; Event *event = (Event *)eventInfo; uint8_t filename[STD_BUF]; uint8_t *file_name; uint32_t file_name_len = 0; if ( !event || ((event->sig_generator != GENERATOR_FILE_TYPE) && (event->sig_generator != GENERATOR_FILE_SIGNATURE))) return; _dod.logTimeStamp(data->log, p); /*Get the file name captured*/ if(_dpd.fileAPI->get_file_name(p->stream_session, &file_name, &file_name_len)) { FileCharEncoding encoding = SNORT_CHAR_ENCODING_ASCII; if(file_name_len > 0) encoding = _dpd.fileAPI->get_character_encoding(file_name, file_name_len); if(SNORT_CHAR_ENCODING_ASCII == encoding) { if (file_name_len >= sizeof(filename)) file_name_len = sizeof(filename) - 1; memcpy(filename, file_name, file_name_len); filename[file_name_len] = '\0'; _dod.textLog_Puts(data->log, " [**] "); _dod.textLog_Print(data->log, " [File: %s, size: %d bytes]", filename, _dpd.fileAPI->get_file_size(p->stream_session)); } else if(SNORT_CHAR_ENCODING_UTF_16LE == encoding) { if(file_name_len > (sizeof(filename) - 2)) file_name_len = sizeof(filename) - 2;//Last 2 bytes reserved for NULL termination memcpy(filename, file_name, file_name_len); filename[file_name_len] = '\0'; filename[file_name_len + 1] = '\0'; _dod.textLog_Puts(data->log, " [**] [File: "); _dod.textLog_Flush(data->log); _dod.textLog_PrintUnicode(data->log, filename + UTF_16_LE_BOM_LEN, file_name_len, true); _dod.textLog_Print(data->log, ", size: %d bytes]", _dpd.fileAPI->get_file_size(p->stream_session)); } else { _dod.textLog_Puts(data->log, " [**] "); _dod.textLog_Print(data->log, " [File: %s, size: %d bytes]", "<>", _dpd.fileAPI->get_file_size(p->stream_session)); } } if (event->sig_generator == GENERATOR_FILE_SIGNATURE) { char sha256[SHA256_HASH_STR_SIZE + 1]; sha_to_str((char *)_dpd.fileAPI->get_sig_sha256(p->stream_session), sha256, SHA256_HASH_STR_SIZE + 1); sha256[SHA256_HASH_STR_SIZE] = '\0'; _dod.textLog_Print(data->log, " [signature: %s]", sha256); } if( p != NULL && _dod.active_PacketWasDropped() ) { _dod.textLog_Puts(data->log, " [Drop]"); } else if( p != NULL && _dod.active_PacketWouldBeDropped() ) { _dod.textLog_Puts(data->log, " [WDrop]"); } _dod.textLog_Puts(data->log, " [**] "); _dod.textLog_Print(data->log, "[%lu:%lu:%lu] ", (unsigned long) event->sig_generator, (unsigned long) event->sig_id, (unsigned long) event->sig_rev); if (_dod.ScAlertInterface()) { _dod.textLog_Print(data->log, " <%s> ", _dod.getDAQinterface()); } /*Ignore event message for file signature events*/ if ((msg != NULL) && (event->sig_generator == GENERATOR_FILE_TYPE)) { _dod.textLog_Puts(data->log, msg); } _dod.textLog_Puts(data->log, " [**] "); /* print the packet header to the alert file */ if ((p != NULL) && IPH_IS_VALID(p)) { _dod.textLog_Print(data->log, "{%s} ", _dod.getProtocolName(p)); _dod.logIpAddrs(data->log, p); } _dod.textLog_NewLine(data->log); _dod.textLog_Flush(data->log); } static int file_log_parse (void** config, char* args, const char* default_output_file) { char *tok; FileLogData *data; char* filename = NULL; unsigned long limit = DEFAULT_LIMIT; unsigned int bufSize = FAST_BUF; int i = 0; DEBUG_WRAP(_dod.debugMsg(DEBUG_LOG, "FileLog_Parse: %s\n", args);); data = (FileLogData *)calloc(1, sizeof(*data)); if ( !data ) { _dod.fatalMsg("file_log: unable to allocate memory!\n"); return OUTPUT_ERROR; } if ( !args ) args = ""; tok = strtok( args, " \t"); while ( tok ) { char *end; switch (i) { case 0: if ( !strcasecmp(tok, "stdout") ) { filename = strdup(tok); if (!filename) _dod.fatalMsg("file_log error in %s(%i): %s\n", *(_dod.config_file), *(_dod.config_line), tok); } else { char *dir = _dod.getLogDirectory(); filename = getFullName(dir, tok); } break; case 1: if ( !strcasecmp("packet", tok) ) { data->packet_flag = 1; bufSize = FULL_BUF; break; } /* in this case, only 2 options allowed */ else i++; /* fall thru so "packet" is optional ... */ case 2: limit = strtol(tok, &end, 10); if ( tok == end ) _dod.fatalMsg("file_log error in %s(%i): %s\n", *(_dod.config_file), *(_dod.config_line), tok); if ( end && toupper(*end) == 'G' ) limit <<= 30; /* GB */ else if ( end && toupper(*end) == 'M' ) limit <<= 20; /* MB */ else if ( end && toupper(*end) == 'K' ) limit <<= 10; /* KB */ break; case 3: _dod.fatalMsg("file_log: error in %s(%i): %s\n", *(_dod.config_file), *(_dod.config_line), tok); break; } tok = strtok( NULL, " " ); i++; } #ifdef DEFAULT_FILE if ( !filename ) filename = getFullName(_dod.getLogDirectory(), DEFAULT_FILE); #endif DEBUG_WRAP(_dod.debugMsg( DEBUG_INIT, "file_log: '%s' %d %ld\n", filename ? filename:"alert", data->packet_flag, limit );); if ((filename == NULL) && (_dod.getAlertFile() != NULL)) filename = _dod.SnortStrdup(_dod.getAlertFile()); data->log = _dod.textLog_Init(filename, bufSize, limit); if (filename != NULL) free(filename); *config = data; return OUTPUT_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/file/sf_file.dsp0000444000175000017500000001136714230012554021253 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_file" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_file - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_file.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_file.mak" CFG="sf_file - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_file - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_file - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_file - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "SF_SNORT_PREPROC_DLL" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_file - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "SF_SNORT_PREPROC_DLL" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_file - Win32 Release" # Name "sf_file - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\spp_file.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\spp_file.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/file/include/0000755000175000017500000000000014242725717020570 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/file/include/circular_buffer.c0000644000175000017500000001206214242050235024054 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** ** Circular buffer is thread safe for one writer and one reader thread ** ** This implementaton is inspired by one slot open approach. ** See http://en.wikipedia.org/wiki/Circular_buffer ** ** 5.25.13 - Initial Source Code. Hui Cao */ #include "sf_types.h" #include "circular_buffer.h" #include #include /* Circular buffer object */ struct _CircularBuffer{ uint64_t size; /* maximum number of elements */ uint64_t start; /* index of oldest element, reader update only */ uint64_t end; /* index to write new element, writer update only*/ uint64_t under_run; uint64_t over_run; ElemType *elems; /* vector of elements */ uint64_t total_write; uint64_t total_read; }; /* This approach adds one bit to end and start pointers */ CircularBuffer * cbuffer_init(uint64_t size) { CircularBuffer* cb = calloc(1, sizeof(*cb)); if ( !cb ) return NULL; cb->size = size + 1; cb->elems = (ElemType *)calloc(cb->size, sizeof(ElemType)); if (!cb->elems) { free(cb); return NULL; } return cb; } void cbuffer_free(CircularBuffer *cb) { if(cb && cb->elems) { free(cb->elems); cb->elems = NULL; } free(cb); } /* We use mirror flag to detection full or empty efficiently*/ int cbuffer_is_full(CircularBuffer *cb) { uint64_t next = cb->end + 1; if ( next == cb->size ) next = 0; return (next == cb->start); } /* We use mirror flag to detection full or empty efficiently*/ int cbuffer_is_empty(CircularBuffer *cb) { return (cb->end == cb->start); } /* Returns number of elements in use*/ uint64_t cbuffer_used(CircularBuffer *cb) { /* cb->end < cb->start means passing the end of buffer */ if (cb->end < cb->start) { return (cb->size + cb->end - cb->start); } else { return (cb->end - cb->start); } } /* Returns number of free elements*/ uint64_t cbuffer_available(CircularBuffer *cb) { return (cbuffer_size(cb) - cbuffer_used(cb)); } /* Returns total number of elements*/ uint64_t cbuffer_size(CircularBuffer *cb) { return (cb->size - 1); } /* * Add one element to the buffer, * * Args: * CircularBuffer *: buffer * ElemType elem: the element to be added * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_write(CircularBuffer *cb, const ElemType elem) { uint64_t w = cb->end; if ( cbuffer_is_full (cb)) /* full, return error */ { cb->over_run++; return CB_FAIL; } cb->elems[w++] = elem; if ( w == cb->size ) w = 0; cb->end = w; cb->total_write++; return CB_SUCCESS; } /* * Read one element from the buffer and remove it from buffer, * * Args: * CircularBuffer *: buffer * ElemType *elem: the element pointer to be stored * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_read(CircularBuffer *cb, ElemType *elem) { uint64_t r = cb->start; if (cbuffer_is_empty(cb)) /* Empty, return error */ { cb->under_run++; return CB_FAIL; } *elem = cb->elems[r++]; if ( r == cb->size ) r = 0; cb->start = r; cb->total_read++; return CB_SUCCESS; } /* * Read one element from the buffer and no change on buffer * * Args: * CircularBuffer *: buffer * ElemType *elem: the element pointer to be stored * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_peek(CircularBuffer *cb, ElemType *elem) { if (cbuffer_is_empty(cb)) /* Empty, return error */ return CB_FAIL; *elem = cb->elems[cb->start]; return CB_SUCCESS; } /* Returns total number of reads*/ uint64_t cbuffer_num_reads(CircularBuffer *cb) { return (cb->total_read); } /* Returns total number of writes*/ uint64_t cbuffer_num_writes(CircularBuffer *cb) { return (cb->total_write); } /* Returns total number of writer overruns*/ uint64_t cbuffer_num_over_runs(CircularBuffer *cb) { return (cb->over_run); } /* Returns total number of reader overruns*/ uint64_t cbuffer_num_under_runs(CircularBuffer *cb) { return (cb->under_run); } snort-2.9.20/src/dynamic-preprocessors/file/include/output_lib.c0000644000175000017500000000543014242050234023105 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Date: 01-27-2012 ** Author: Hui Cao */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "output_api.h" #include "output_lib.h" extern char *file_name; extern int file_line; DynamicOutputData _dod; OUTPUT_SO_PUBLIC int initOutputPlugins(void *dod) { DynamicOutputData* outputData = (DynamicOutputData*)dod; /*Check the version*/ if (!dod) return OUTPUT_ERROR; if (outputData->majorVersion != OUTPUT_DATA_MAJOR_VERSION) { printf("ERROR major version %d != %d\n", outputData->majorVersion, OUTPUT_DATA_MAJOR_VERSION); return OUTPUT_ERROR; } if (outputData->minorVersion < OUTPUT_DATA_MINOR_VERSION) { printf("ERROR minor version %d < %d\n", outputData->minorVersion, OUTPUT_DATA_MINOR_VERSION); return OUTPUT_ERROR; } if (outputData->size < sizeof(DynamicOutputData)) { printf("ERROR size %d != %u\n", outputData->size, (unsigned)sizeof(*outputData)); return OUTPUT_ERROR; } _dod = *((DynamicOutputData*)dod); return OUTPUT_SUCCESS; } void init_output_module(struct _SnortConfig *sc, Output_Module_t *om, char *args) { void *config; if (!om) return; printf("Initializing module %s \n", om->name); om->parse_args(&config, args, om->default_file); if (om->alert_output) { _dod.addOutputModule(sc, om->alert_output, DYNAMIC_OUTPUT_TYPE_FLAG__ALERT, config); } if (om->log_output) { _dod.addOutputModule(sc, om->log_output, DYNAMIC_OUTPUT_TYPE_FLAG__LOG, config); } #ifdef SNORT_RELOAD if (om->rotate) { _dod.addReload(om->rotate, config); } #endif if (om->postconfig) { _dod.addPostconfig(sc, om->postconfig, config); } if (om->shutdown) { _dod.addCleanExit(om->shutdown, config); } } snort-2.9.20/src/dynamic-preprocessors/file/file_event_log.h0000644000175000017500000000213714241075732022274 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifndef _FILE_EVENT_LOG_H_ #define _FILE_EVENT_LOG_H_ #include "file_api.h" #endif snort-2.9.20/src/dynamic-preprocessors/file/Makefile.in0000644000175000017500000005717414242725546021230 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/dynamic-preprocessors/file ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_file_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am_libsf_file_preproc_la_OBJECTS = spp_file.lo file_agent.lo \ file_event_log.lo file_inspect_config.lo file_sha.lo \ output_lib.lo circular_buffer.lo @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_file_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_file_preproc_la_OBJECTS = $(am_libsf_file_preproc_la_OBJECTS) \ $(nodist_libsf_file_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_file_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_file_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_file_preproc_la_SOURCES) \ $(nodist_libsf_file_preproc_la_SOURCES) DIST_SOURCES = $(libsf_file_preproc_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs -I./include INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_file_preproc.la libsf_file_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_file_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_file_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c libsf_file_preproc_la_SOURCES = \ spp_file.c \ spp_file.h \ file_agent.c \ file_agent.h \ file_event_log.c \ file_event_log.h \ file_inspect_config.c \ file_inspect_config.h \ file_sha.c \ file_sha.h \ ./include/output_lib.c \ ./include/circular_buffer.c BUILT_SOURCES = \ include/output_lib.c \ include/output_api.h \ include/output_lib.h \ include/output_common.h \ include/sf_textlog.h \ include/circular_buffer.c \ include/circular_buffer.h EXTRA_DIST = \ sf_file.dsp copy_headers = \ mkdir -p include; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/file/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/file/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_file_preproc.la: $(libsf_file_preproc_la_OBJECTS) $(libsf_file_preproc_la_DEPENDENCIES) $(EXTRA_libsf_file_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_file_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_file_preproc_la_OBJECTS) $(libsf_file_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< output_lib.lo: ./include/output_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o output_lib.lo `test -f './include/output_lib.c' || echo '$(srcdir)/'`./include/output_lib.c circular_buffer.lo: ./include/circular_buffer.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o circular_buffer.lo `test -f './include/circular_buffer.c' || echo '$(srcdir)/'`./include/circular_buffer.c sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: all check install install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool clean-local cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am \ install-dynamicpreprocessorLTLIBRARIES install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile include/output_lib.c: ${top_srcdir}/src/dynamic-output/libs/output_lib.c @src_header=$?; dst_header=$@; $(copy_headers) include/output_api.h: ${top_srcdir}/src/dynamic-output/plugins/output_api.h @src_header=$?; dst_header=$@; $(copy_headers) include/output_lib.h: ${top_srcdir}/src/dynamic-output/plugins/output_lib.h @src_header=$?; dst_header=$@; $(copy_headers) include/output_common.h: ${top_srcdir}/src/dynamic-output/plugins/output_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_textlog.h: ${top_srcdir}/src/sfutil/sf_textlog.h @src_header=$?; dst_header=$@; $(copy_headers) include/circular_buffer.h: ${top_srcdir}/src/file-process/circular_buffer.h @src_header=$?; dst_header=$@; $(copy_headers) include/circular_buffer.c: ${top_srcdir}/src/file-process/circular_buffer.c @src_header=$?; dst_header=$@; $(copy_headers) clean-local: rm -rf include all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/file/spp_file.h0000644000175000017500000000430514241075741021113 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Definitions, structs, function prototype(s) for * the file preprocessor. * Author: Hui Cao */ #ifndef SPP_FILE_H #define SPP_FILE_H #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" typedef struct _File_Stats { uint64_t file_types_total; /* Total number of file type callbacks */ uint64_t file_signatures_total; /* Total number of file signature callbacks */ uint64_t files_to_disk_total; /* Files needed to be saved on disk */ uint64_t file_duplicates_total; /* Files already on disk */ uint64_t files_saved; /* Files saved on disk */ uint64_t file_reserve_failures; uint64_t file_capture_min; uint64_t file_capture_max; uint64_t file_capture_memcap; uint64_t file_read_failures; uint64_t file_agent_memcap_failures; uint64_t file_data_to_disk; /*file data stored */ uint64_t files_to_host_total; /*files sent */ uint64_t file_data_to_host; /*file data sent */ uint64_t file_transfer_failures; /*file transfer failures */ } File_Stats; /* Prototypes for public interface, initialzie file inspect functions */ extern void SetupFileInspect(void); extern File_Stats file_inspect_stats; extern tSfPolicyUserContextId file_config; #define FILE_FATAL_ERROR DynamicPreprocessorFatalMessage #endif /* SPP_FILE_H */ snort-2.9.20/src/dynamic-preprocessors/file/file_sha.h0000644000175000017500000000603714241075737021075 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Simple SHA hash table * Author: Hui Cao */ #ifndef SPP_FILE_SHA_H #define SPP_FILE_SHA_H #define SHA256_HASH_STR_SIZE 64 #define SHA256_HASH_SIZE 32 /* * ERROR DEFINES */ #define SHAHASH_NOMEM -2 #define SHAHASH_ERR -1 #define SHAHASH_OK 0 #define SHAHASH_INTABLE 1 /* * HASH NODE */ typedef struct _ShaHashNode { struct _ShaHashNode *next; void *sha; /* Pointer to the sha */ void *data; /* Pointer to the users data, this is never copied! */ } ShaHashNode; typedef struct _ShaHash { ShaHashNode **entries; /* array of node ptr's */ int nrows; /* # rows int the hash table */ unsigned count; /* total # nodes in table */ char sha_size; /* bytes in sha_size */ } ShaHash; /* Create SHA table * * Args: * char sha_bytes: number of bytes in sha * */ ShaHash *sha_table_new( char sha_bytes); /* * Add a key + data pair * --------------------- * * key + data should both be non-zero, although data can be zero * * t - hash table * sha - sha value * data - users data pointer * * returns SHAHASH_NOMEM: alloc error * SHAHASH_INTABLE : key already in table * SHAHASH_OK: added a node for this key + data pair * */ int sha_table_add( ShaHash *t, void *sha, void *data ); /* * Find a Node based on the sha, return users data. */ void * sha_table_find( ShaHash *t, void *sha); /* * Delete the hash Table * * free key's, free node's, and free the users data, if they * supply a free function */ void sha_table_delete( ShaHash *h ); /* * Convert a printable str to sha * * Args: * char *sha256: point to sha to be saved * char *sha_str: point to the string that will be parsed * int max_size: maximum number of bytes to be read from sha_str */ int str_to_sha (char *sha_str, char *sha256, int max_size); /* * Convert a SHA256 to a printable str * * Args: * char *sha256: point to sha in binary * char *sha_str: point to the string that will be saved * int max_size: maximum number of bytes to be saved to sha_str */ int sha_to_str (char *sha256, char *sha_str, int max_size); #endif /* SPP_FILE_SHA_H */ snort-2.9.20/src/dynamic-preprocessors/file/file_sha.c0000644000175000017500000001554514241075736021073 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Simple SHA hash table * * We use first two bytes of SHA as index * Author: Hui Cao */ #include "sf_types.h" #include "file_sha.h" #include "memory_stats.h" #include "preprocids.h" #include "sf_dynamic_preprocessor.h" #include #include #include typedef uint16_t ShaKeyType; /*16 bits as index, we will have 65536 rows*/ #define SHA_TABLE_ROWS (1<<(sizeof(ShaKeyType)*8)) char conv_to_hex[UINT8_MAX + 1]; /* Create SHA table * * Args: * int sha_bits: number of bytes in sha */ ShaHash *sha_table_new(char sha_bytes) { ShaHash *table = _dpd.snortAlloc(1, sizeof(ShaHash), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); if (!table) return NULL; table->nrows = SHA_TABLE_ROWS; table->entries = _dpd.snortAlloc(table->nrows, sizeof(*(table->entries)), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); if (!table->entries) { _dpd.snortFree(table, sizeof(ShaHash), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); return NULL; } table->sha_size = sha_bytes; table->count = 0; return table; } static int sha_cmp (void *sha1, void *sha2, char sha_bytes) { return memcmp(sha1, sha2, sha_bytes); } static ShaHashNode *sha_table_node_by_sha(ShaHash *table, void *sha) { int index; ShaHashNode *node; index = *(ShaKeyType *)sha; if (!table || !table->entries) return NULL; node = (ShaHashNode *)table->entries[index]; while(node && sha_cmp(node->sha, sha, table->sha_size)) { node = node->next; } if (node) { return node; } else { return NULL; } } /* * Find a Node based on the key, return users data. */ void * sha_table_find(ShaHash *table, void *sha) { ShaHashNode *node; node = sha_table_node_by_sha(table, sha); if (node) { return node->data; } else { return NULL; } } /* * Add a key + data pair * --------------------- * * key + data should both be non-zero, although data can be zero * * t - hash table * sha - sha value * data - users data pointer * * returns SHAHASH_NOMEM: alloc error * SHAHASH_INTABLE : key already in table * SHAHASH_OK: added a node for this key + data pair */ int sha_table_add( ShaHash *table, void *sha, void *data ) { ShaHashNode *oldNode; ShaHashNode *newNode; int index; /*check whether existing*/ oldNode = sha_table_node_by_sha(table, sha); if (oldNode) return SHAHASH_INTABLE; /* Add to the head of entry*/ index = *(ShaKeyType *)sha; oldNode = (ShaHashNode *)table->entries[index]; newNode = _dpd.snortAlloc(1, sizeof (*newNode), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); if (!newNode) return SHAHASH_NOMEM; newNode->next = oldNode; newNode->sha = sha; newNode->data = data; table->entries[index] = newNode; table->count++; return SHAHASH_OK; } /* * Delete the hash Table * * free key's, free node's, and free the users data, if they * supply a free function */ void sha_table_delete( ShaHash *table ) { int i; if (!table || !table->entries) return; for (i = 0; i < table->nrows; i++) { ShaHashNode *node; ShaHashNode *old; node = (ShaHashNode *)table->entries[i]; while(node) { old = node; node = node->next; _dpd.snortFree(old->sha, SHA256_HASH_SIZE, PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); _dpd.snortFree(old, sizeof(ShaHashNode), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); } } _dpd.snortFree(table->entries, sizeof(ShaHashNode) * table->count, PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); _dpd.snortFree(table, sizeof(ShaHash), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); } static void init_char_to_hex_array(void) { size_t i; char conv1[] = "0123456789ABCDEF"; char conv2[] = "abcdef"; /*initializate to unset*/ for (i = 0; i < sizeof (conv_to_hex); i++) { conv_to_hex[i] = -1; } /* number and upper case letter*/ for (i = 0; i < sizeof (conv1); i++) { conv_to_hex[(int)conv1[i]] = i; } /* lower case number */ for (i = 0; i < sizeof (conv2); i++) { conv_to_hex[(int)conv2[i]] = i + 10; } } /* * Convert a printable str to sha */ int str_to_sha (char *sha_str, char *sha256, int max_size) { char *index = sha_str; char *end = sha_str + max_size; int i; char *dest = sha256; static bool initialized = false; if (!initialized) { init_char_to_hex_array(); initialized = true; } /*trim spaces in the head of signature*/ while ((index < end) && isspace((int)*index)) { index++; } /* make sure length is correct */ if (index + SHA256_HASH_STR_SIZE > end) return -1; /* convert it to hex */ for (i = 0; i < SHA256_HASH_SIZE; i++) { char value; if (conv_to_hex[(int)*index] < 0) return -1; value = conv_to_hex[(int)*(index++)]; if (conv_to_hex[(int)*index] < 0) return -1; *(dest++) = conv_to_hex[(int)*(index++)] + (value << 4); } /*Check end of string*/ while ((index < end) && isspace((int)*index)) { index++; } if (index != end) return -1; return 0; } /* * Create file name based on SHA */ int sha_to_str (char *sha256, char *sha_str, int max_size) { char conv[] = "0123456789ABCDEF"; const char *index; const char *end; char *ridx; int len; if (max_size < 1) return 0; index = sha256; if (max_size < SHA256_HASH_SIZE) len = max_size - 1; else len = SHA256_HASH_SIZE; ridx = sha_str; end = index + len; while(index < end) { *ridx++ = conv[((*index & 0xFF)>>4)]; *ridx++ = conv[((*index & 0xFF)&0x0F)]; index++; } *ridx = '\0'; return len; } snort-2.9.20/src/dynamic-preprocessors/file/Makefile.am0000444000175000017500000000435714230012554021171 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs -I./include dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_file_preproc.la libsf_file_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_file_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_file_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c endif libsf_file_preproc_la_SOURCES = \ spp_file.c \ spp_file.h \ file_agent.c \ file_agent.h \ file_event_log.c \ file_event_log.h \ file_inspect_config.c \ file_inspect_config.h \ file_sha.c \ file_sha.h \ ./include/output_lib.c \ ./include/circular_buffer.c BUILT_SOURCES = \ include/output_lib.c \ include/output_api.h \ include/output_lib.h \ include/output_common.h \ include/sf_textlog.h \ include/circular_buffer.c \ include/circular_buffer.h EXTRA_DIST = \ sf_file.dsp copy_headers = \ mkdir -p include; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi include/output_lib.c: ${top_srcdir}/src/dynamic-output/libs/output_lib.c @src_header=$?; dst_header=$@; $(copy_headers) include/output_api.h: ${top_srcdir}/src/dynamic-output/plugins/output_api.h @src_header=$?; dst_header=$@; $(copy_headers) include/output_lib.h: ${top_srcdir}/src/dynamic-output/plugins/output_lib.h @src_header=$?; dst_header=$@; $(copy_headers) include/output_common.h: ${top_srcdir}/src/dynamic-output/plugins/output_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_textlog.h: ${top_srcdir}/src/sfutil/sf_textlog.h @src_header=$?; dst_header=$@; $(copy_headers) include/circular_buffer.h: ${top_srcdir}/src/file-process/circular_buffer.h @src_header=$?; dst_header=$@; $(copy_headers) include/circular_buffer.c: ${top_srcdir}/src/file-process/circular_buffer.c @src_header=$?; dst_header=$@; $(copy_headers) clean-local: rm -rf include all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/file/file_inspect_config.h0000644000175000017500000000423614241075735023311 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Definitions, structs, function prototype(s) for * the file preprocessor. * Author: Hui Cao */ #ifndef SPP_FILE_INSPECT_CONFIG_H #define SPP_FILE_INSPECT_CONFIG_H #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "file_sha.h" #define FILE_CAPTURE_QUEUE_SIZE_DEFAULT 3000 /*files*/ #define FILE_CAPTURE_DISK_SIZE_DEFAULT 300 /*MB*/ typedef struct _FileSigInfo { File_Verdict verdict; } FileSigInfo; /* * Global File preprocessor configuration. * */ typedef struct _fileInspectConfig { bool file_type_enabled; bool file_signature_enabled; bool file_capture_enabled; uint32_t file_capture_queue_size; char *capture_dir; int ref_count; char *hostname; int portno; ShaHash *sig_table; #if defined(DEBUG_MSGS) || defined (REG_TEST) int verdict_delay; /* used for debug, mimic delay to get verdicts */ #endif uint32_t capture_disk_size; /* In megabytes*/ } FileInspectConf; void file_config_parse(FileInspectConf*, const u_char* ); /* Return values * 0: equal * -1: no the same */ int file_config_compare(FileInspectConf* , FileInspectConf* ); /* Release resource of file configruation*/ void file_config_free(FileInspectConf*); #endif /* SPP_FILE_INSPECT_CONFIG_H */ snort-2.9.20/src/dynamic-preprocessors/file/file_inspect_config.c0000644000175000017500000003760014241075733023303 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include "sf_types.h" #include "file_inspect_config.h" #include "file_agent.h" #include "spp_file.h" #include /* * File preprocessor configurations * Author: Hui Cao * */ #define FILE_CONF_SECTION_SEPERATORS ",;" #define FILE_CONF_VALUE_SEPERATORS " " #define FILE_SEPARATORS " \t\r\n" #define FILE_INSPECT_TYPE "type_id" #define FILE_INSPECT_SIG "signature" #define FILE_INSPECT_CAPTURE_MEMORY "capture_memory" #define FILE_INSPECT_CAPTURE_DISK "capture_disk" #define FILE_INSPECT_CAPTURE_NETWORK "capture_network" #define FILE_INSPECT_CAPTURE_QUEUE_SIZE "capture_queue_size" #define FILE_INSPECT_BLACKLIST "blacklist" #define FILE_INSPECT_GREYLIST "greylist" #if defined(DEBUG_MSGS) || defined (REG_TEST) #define FILE_INSPECT_VERDICT_DELAY "verdict_delay" #endif #define MAX_SIG_LINE_LENGTH 8192 static FileSigInfo blackList = {FILE_VERDICT_BLOCK}; static FileSigInfo greyList = {FILE_VERDICT_LOG}; /* * Function: UpdatePathToFile * * Update the path to a file, if using relative path. * The relative path is based on config file directory. * * Arguments: * full_path_filename: file name string * max_size: ? * char *filename: ? * * Returns: * 1 successful * 0 fail * */ static int UpdatePathToFile(char *full_path_filename, unsigned int max_size, char *filename) { char *snort_conf_dir = *(_dpd.snort_conf_dir); if (!snort_conf_dir || !(*snort_conf_dir) || !filename) { DynamicPreprocessorFatalMessage(" %s(%d) => can't create path.\n", *(_dpd.config_file), *(_dpd.config_line)); return 0; } /*filename is too long*/ if ( max_size < strlen(filename) ) { DynamicPreprocessorFatalMessage(" %s(%d) => the file name length %u " "is longer than allowed %u.\n", *(_dpd.config_file), *(_dpd.config_line), strlen(filename), max_size); return 0; } /* If an absolute path is specified, then use that.*/ #ifndef WIN32 if(filename[0] == '/') { snprintf(full_path_filename, max_size, "%s", filename); } else { /* Set up the file name directory.*/ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '/') { snprintf(full_path_filename,max_size, "%s%s", snort_conf_dir, filename); } else { snprintf(full_path_filename, max_size, "%s/%s", snort_conf_dir, filename); } } #else if(strlen(filename)>3 && filename[1]==':' && filename[2]=='\\') { snprintf(full_path_filename, max_size, "%s", filename); } else { /* Set up the file name directory */ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '\\' || snort_conf_dir[strlen(snort_conf_dir) - 1] == '/' ) { snprintf(full_path_filename,max_size, "%s%s", snort_conf_dir, filename); } else { snprintf(full_path_filename, max_size, "%s\\%s", snort_conf_dir, filename); } } #endif return 1; } /* * Load file list signature file * * Arguments: * filename: file name string * FileSigInfo *: The file signature information. * FileInspectConf *: The configuration to be update. * * Returns: * None */ static void file_config_signature(char *filename, FileSigInfo *sig_info, FileInspectConf *config) { FILE *fp = NULL; char linebuf[MAX_SIG_LINE_LENGTH]; char full_path_filename[PATH_MAX+1]; int line_number = 0; /* check table first, create one if not exist*/ if (config->sig_table == NULL) { config->sig_table = sha_table_new(SHA256_HASH_SIZE); } if (config->sig_table == NULL) { FILE_FATAL_ERROR("%s(%d) Could not create file signature hash.\n", *(_dpd.config_file), *(_dpd.config_line)); } /* parse the file line by line, each signature one entry*/ _dpd.logMsg("File inspect: processing file %s\n", filename); UpdatePathToFile(full_path_filename, PATH_MAX, filename); if((fp = fopen(full_path_filename, "r")) == NULL) { char errBuf[STD_BUF]; #ifdef WIN32 snprintf(errBuf, STD_BUF, "%s", strerror(errno)); #else strerror_r(errno, errBuf, STD_BUF); #endif errBuf[STD_BUF-1] = '\0'; FILE_FATAL_ERROR("%s(%d) => Unable to open signature file %s, " "Error: %s\n", *(_dpd.config_file), *(_dpd.config_line), filename, errBuf); return; } while( fgets(linebuf, MAX_SIG_LINE_LENGTH, fp) ) { char *cmt = NULL; char *sha256; FileSigInfo *old_info; DEBUG_WRAP(DebugMessage(DEBUG_FILE, "File signatures: %s\n",linebuf );); line_number++; /* Remove comments */ if( (cmt = strchr(linebuf, '#')) ) *cmt = '\0'; /* Remove newline as well, prevent double newline in logging.*/ if( (cmt = strchr(linebuf, '\n')) ) *cmt = '\0'; if (!strlen(linebuf)) continue; sha256 = _dpd.snortAlloc(1, SHA256_HASH_SIZE, PP_FILE_INSPECT, PP_MEM_CATEGORY_CONFIG); if (!sha256) { FILE_FATAL_ERROR("%s(%d) => No memory for file: %s (%d), \n" "signature: %s\n", *(_dpd.config_file), *(_dpd.config_line), filename, line_number, linebuf); } if (str_to_sha(linebuf, sha256, strlen(linebuf)) < 0) { FILE_FATAL_ERROR("%s(%d) => signature format at file: %s (%d), \n" "signature: %s\n", *(_dpd.config_file), *(_dpd.config_line), filename, line_number, linebuf); } old_info = (FileSigInfo *)sha_table_find(config->sig_table, sha256); if (old_info) { _dpd.snortFree(sha256, SHA256_HASH_SIZE, PP_FILE_INSPECT, PP_MEM_CATEGORY_CONFIG); _dpd.errMsg("%s(%d) => signature redefined at file: %s (%d), \n" "signature: %s\n", *(_dpd.config_file), *(_dpd.config_line), filename, line_number, linebuf); } else { sha_table_add(config->sig_table, sha256, sig_info); } } fclose(fp); } /* Display the configuration for the File preprocessor. * * PARAMETERS: * * FileInspectConf *config: pointer to configuration * * RETURNS: Nothing. */ static void DisplayFileConfig(FileInspectConf *config) { if (config == NULL) return; _dpd.logMsg("File config: \n"); _dpd.logMsg(" file type: %s\n", config->file_type_enabled ? "ENABLED":"DISABLED (Default)"); _dpd.logMsg(" file signature: %s\n", config->file_signature_enabled ? "ENABLED":"DISABLED (Default)"); _dpd.logMsg(" file capture: %s\n", config->file_capture_enabled ? "ENABLED":"DISABLED (Default)"); if (config->file_capture_enabled) { _dpd.logMsg(" file capture directory: %s\n", config->capture_dir ? config->capture_dir:"not saved, memory only"); _dpd.logMsg(" file capture disk size: %u %s\n", config->capture_disk_size, config->capture_disk_size == FILE_CAPTURE_DISK_SIZE_DEFAULT? "(Default) megabytes":"megabytes"); } _dpd.logMsg(" file sent to host: %s, port number: %d\n", config->hostname ? config->hostname:"DISABLED (Default)", config->portno); _dpd.logMsg("\n"); } /* Parses and processes the configuration arguments * supplied in the File preprocessor rule. * * PARAMETERS: * FileInspectConf *config: pointer to configuration * argp: Pointer to string containing the config arguments. * * RETURNS: Nothing. */ void file_config_parse(FileInspectConf *config, const u_char* argp) { char* cur_sectionp = NULL; char* next_sectionp = NULL; char* argcpyp = NULL; if (config == NULL) return; config->capture_disk_size = FILE_CAPTURE_DISK_SIZE_DEFAULT; /* Sanity check(s) */ if (!argp) { DisplayFileConfig(config); return; } argcpyp = strdup((char*) argp); if (!argcpyp) { FILE_FATAL_ERROR("Could not allocate memory to " "parse File options.\n"); return; } cur_sectionp = strtok_r(argcpyp, FILE_CONF_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Arguments token: %s\n", cur_sectionp );); while (cur_sectionp) { char* cur_config; unsigned long value; char* cur_tokenp = strtok(cur_sectionp, FILE_CONF_VALUE_SEPERATORS); if (!cur_tokenp) { cur_sectionp = strtok_r(next_sectionp, FILE_CONF_SECTION_SEPERATORS, &next_sectionp); continue; } cur_config = cur_tokenp; if (!strcasecmp(cur_tokenp, FILE_INSPECT_TYPE)) { config->file_type_enabled = true; } else if (!strcasecmp(cur_tokenp, FILE_INSPECT_SIG)) { config->file_signature_enabled = true; } else if (!strcasecmp(cur_tokenp, FILE_INSPECT_BLACKLIST)) { cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); if(cur_tokenp == NULL) { FILE_FATAL_ERROR("%s(%d) => Please specify list file!\n", *(_dpd.config_file), *(_dpd.config_line)); } file_config_signature(cur_tokenp, &blackList, config); } else if (!strcasecmp(cur_tokenp, FILE_INSPECT_GREYLIST)) { cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); if(cur_tokenp == NULL) { FILE_FATAL_ERROR("%s(%d) => Please specify list file!\n", *(_dpd.config_file), *(_dpd.config_line)); } file_config_signature(cur_tokenp, &greyList, config); } else if (!strcasecmp(cur_tokenp, FILE_INSPECT_CAPTURE_MEMORY)) { config->file_capture_enabled = true; } else if (!strcasecmp(cur_tokenp, FILE_INSPECT_CAPTURE_DISK)) { config->file_capture_enabled = true; cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); if(cur_tokenp == NULL) { FILE_FATAL_ERROR("%s(%d) => Please specify directory!\n", *(_dpd.config_file), *(_dpd.config_line)); } if (strlen(cur_tokenp) > FILE_NAME_LEN) { FILE_FATAL_ERROR("%s(%d) => Directory string is too long!\n", *(_dpd.config_file), *(_dpd.config_line)); return; } if (!(config->capture_dir = strdup(cur_tokenp))) { FILE_FATAL_ERROR("Could not allocate memory to parse " "file options.\n"); return; } cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); if (cur_tokenp) { _dpd.checkValueInRange(cur_tokenp, FILE_INSPECT_CAPTURE_DISK, 0, 65536, &value); config->capture_disk_size = (int)value; } } else if (!strcasecmp(cur_tokenp, FILE_INSPECT_CAPTURE_NETWORK)) { config->file_capture_enabled = true; cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); if(cur_tokenp == NULL) { FILE_FATAL_ERROR("%s(%d) => Please specify hostname!\n", *(_dpd.config_file), *(_dpd.config_line)); } if (!(config->hostname = strdup(cur_tokenp))) { FILE_FATAL_ERROR("Could not allocate memory to parse " "file options.\n"); return; } cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); _dpd.checkValueInRange(cur_tokenp, FILE_INSPECT_CAPTURE_NETWORK, 0, 65536, &value); config->portno = (int)value; } else if (!strcasecmp(cur_tokenp, FILE_INSPECT_CAPTURE_QUEUE_SIZE)) { cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); _dpd.checkValueInRange(cur_tokenp, FILE_INSPECT_CAPTURE_QUEUE_SIZE, 0, UINT32_MAX, &value); config->file_capture_queue_size = (uint32_t) value; } #if defined(DEBUG_MSGS) || defined (REG_TEST) else if (!strcasecmp(cur_tokenp, FILE_INSPECT_VERDICT_DELAY)) { cur_tokenp = strtok(NULL, FILE_CONF_VALUE_SEPERATORS); _dpd.checkValueInRange(cur_tokenp, FILE_INSPECT_VERDICT_DELAY, 0, UINT32_MAX, &value); config->verdict_delay = (uint32_t) value; } #endif else { FILE_FATAL_ERROR(" %s(%d) => Invalid argument: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); return; } /*Check whether too many parameters*/ if (NULL != strtok(NULL, FILE_CONF_VALUE_SEPERATORS)) { FILE_FATAL_ERROR("%s(%d) => Too many arguments: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_config); } cur_sectionp = strtok_r(next_sectionp, FILE_CONF_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Arguments token: %s\n", cur_sectionp );); } DisplayFileConfig(config); free(argcpyp); } /*Return values * 0: equal * -1: no the same */ static inline int _cmp_config_str(char *str1, char *str2) { if (!str1 && !str2) return 0; if (!str1 || !str2) return -1; return (strcmp(str1, str2)); } /*Return values * 0: equal * -1: no the same */ int file_config_compare(FileInspectConf* conf1 , FileInspectConf* conf2) { if (_cmp_config_str(conf1->capture_dir, conf2->capture_dir) ||(conf1->file_capture_enabled != conf2->file_capture_enabled) ||(conf1->file_capture_queue_size != conf2->file_capture_queue_size) ||(conf1->capture_disk_size != conf2->capture_disk_size) ||(conf1->file_signature_enabled != conf2->file_signature_enabled) ||(conf1->file_type_enabled != conf2->file_type_enabled) || _cmp_config_str(conf1->hostname, conf2->hostname) ||(conf1->portno != conf2->portno)) { return -1; } return 0; } void file_config_free(FileInspectConf* config) { if (config->capture_dir) { free(config->capture_dir); config->capture_dir = NULL; } if (config->hostname) { free(config->hostname); config->hostname = NULL; } if (config->sig_table != NULL) { sha_table_delete(config->sig_table); config->sig_table = NULL; } } snort-2.9.20/src/dynamic-preprocessors/file/spp_file.c0000644000175000017500000003574114241075740021115 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * File preprocessor * Author: Hui Cao * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #include "spp_file.h" #include "sf_preproc_info.h" #include #include #include #ifndef WIN32 #include #include #endif #include #include #include #include "file_agent.h" #include "file_inspect_config.h" const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_FILE"; #define FILE_PREPROC_NAME "file_inspect" #define SetupFileInspect DYNAMIC_PREPROC_SETUP /* * Function prototype(s) */ static void FileInit( struct _SnortConfig*, char* ); static void print_file_stats(int exiting); static void FileFreeConfig(tSfPolicyUserContextId config); static int FileCheckConfig(struct _SnortConfig *); static void FileCleanExit(int, void *); static void FileUpdateConfig(FileInspectConf *, tSfPolicyUserContextId); int FileInspectPrintMemStats(FILE *fd, char* buffer, PreprocMemInfo *meminfo); /** File configuration per Policy */ tSfPolicyUserContextId file_config = NULL; #ifdef SNORT_RELOAD static void FileReload(struct _SnortConfig *, char *, void **); static int FileReloadVerify(struct _SnortConfig *, void *); static void * FileReloadSwap(struct _SnortConfig *, void *); static void FileReloadSwapFree(void *); #endif File_Stats file_inspect_stats; /* Called at preprocessor setup time. Links preprocessor keyword * to corresponding preprocessor initialization function. * * PARAMETERS: None. * * RETURNS: Nothing. * */ void SetupFileInspect(void) { /* Link preprocessor keyword to initialization function * in the preprocessor list. */ #ifndef SNORT_RELOAD _dpd.registerPreproc( "file_inspect", FileInit ); #else _dpd.registerPreproc("file_inspect", FileInit, FileReload, FileReloadVerify, FileReloadSwap, FileReloadSwapFree); #endif } /* Initializes the File preprocessor module and registers * it in the preprocessor list. * * PARAMETERS: * * argp: Pointer to argument string to process for config * data. * * RETURNS: Nothing. */ static void FileInit(struct _SnortConfig *sc, char *argp) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); FileInspectConf *pPolicyConfig = NULL; if (file_config == NULL) { /*create a context*/ file_config = sfPolicyConfigCreate(); if (file_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for File config.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupFile(): The Stream preprocessor must be enabled.\n"); } _dpd.addPreprocConfCheck(sc, FileCheckConfig); _dpd.registerPreprocStats(FILE_PREPROC_NAME, print_file_stats); _dpd.registerMemoryStatsFunc(PP_FILE_INSPECT, FileInspectPrintMemStats); _dpd.addPreprocExit(FileCleanExit, NULL, PRIORITY_LAST, PP_FILE_INSPECT); } sfPolicyUserPolicySet (file_config, policy_id); pPolicyConfig = (FileInspectConf *)sfPolicyUserDataGetCurrent(file_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("File preprocessor can only be " "configured once.\n"); } pPolicyConfig = (FileInspectConf *)_dpd.snortAlloc(1, sizeof(FileInspectConf), PP_FILE_INSPECT, PP_MEM_CATEGORY_CONFIG); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "File preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(file_config, pPolicyConfig); file_config_parse(pPolicyConfig, (u_char *)argp); FileUpdateConfig(pPolicyConfig, file_config); file_agent_init(sc, pPolicyConfig); _dpd.addPostConfigFunc(sc, file_agent_thread_init, pPolicyConfig); } static void FileUpdateConfig(FileInspectConf *pPolicyConfig, tSfPolicyUserContextId context) { FileInspectConf *defaultConfig = (FileInspectConf *)sfPolicyUserDataGetDefault(context); if (pPolicyConfig == defaultConfig) { if (!pPolicyConfig->file_capture_queue_size) pPolicyConfig->file_capture_queue_size = FILE_CAPTURE_QUEUE_SIZE_DEFAULT; if (!pPolicyConfig->capture_disk_size) pPolicyConfig->capture_disk_size = FILE_CAPTURE_DISK_SIZE_DEFAULT; } else if (defaultConfig == NULL) { if (pPolicyConfig->file_capture_queue_size) { DynamicPreprocessorFatalMessage("%s(%d) => File inspect: " "file capture queue size must be configured " "in the default config.\n", *(_dpd.config_file), *(_dpd.config_line)); } } else { pPolicyConfig->file_capture_queue_size = defaultConfig->file_capture_queue_size; } } static int FileFreeConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { FileInspectConf *pPolicyConfig = (FileInspectConf *)pData; //do any housekeeping before freeing FileInspectConf file_config_free(pPolicyConfig); sfPolicyUserDataClear (config, policyId); _dpd.snortFree(pPolicyConfig, sizeof(FileInspectConf), PP_FILE_INSPECT, PP_MEM_CATEGORY_CONFIG); return 0; } static void FileFreeConfig(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, FileFreeConfigPolicy); sfPolicyConfigDelete(config); } static int FileCheckPolicyConfig(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData) { _dpd.setParserPolicy(sc, policyId); if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { DynamicPreprocessorFatalMessage("FileCheckPolicyConfig(): The Stream preprocessor must be enabled.\n"); } return 0; } static int FileCheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate (sc, file_config, FileCheckPolicyConfig))) return rval; return 0; } static void FileCleanExit(int signal, void *data) { if (file_config != NULL) { file_agent_close(); FileFreeConfig(file_config); file_config = NULL; } } #ifdef SNORT_RELOAD static void FileReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId file_swap_config = (tSfPolicyUserContextId)*new_config; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); FileInspectConf * pPolicyConfig = NULL; if (file_swap_config == NULL) { //create a context file_swap_config = sfPolicyConfigCreate(); if (file_swap_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for File config.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupFile(): The Stream preprocessor must be enabled.\n"); } *new_config = (void *)file_swap_config; } sfPolicyUserPolicySet (file_swap_config, policy_id); pPolicyConfig = (FileInspectConf *)sfPolicyUserDataGetCurrent(file_swap_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("File preprocessor can only be " "configured once.\n"); } pPolicyConfig = (FileInspectConf *)_dpd.snortAlloc(1, sizeof(FileInspectConf), PP_FILE_INSPECT, PP_MEM_CATEGORY_CONFIG); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "File preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(file_swap_config, pPolicyConfig); file_config_parse(pPolicyConfig, (u_char *)args); FileUpdateConfig(pPolicyConfig, file_config); file_agent_init(sc, pPolicyConfig); } static int FileReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId file_swap_config = (tSfPolicyUserContextId)swap_config; FileInspectConf * pPolicyConfig = NULL; FileInspectConf * pCurrentConfig = NULL; if (file_swap_config == NULL) return 0; pPolicyConfig = (FileInspectConf *)sfPolicyUserDataGet(file_swap_config, _dpd.getDefaultPolicy()); if (!pPolicyConfig) return 0; if (file_config != NULL) { pCurrentConfig = (FileInspectConf *)sfPolicyUserDataGet(file_config, _dpd.getDefaultPolicy()); } if (!pCurrentConfig) return 0; if (file_config_compare(pCurrentConfig, pPolicyConfig)) { _dpd.errMsg("File inspect reload: Changing file settings requires a restart.\n"); return -1; } if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("SetupFile(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } static int FileFreeUnusedConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { FileInspectConf *pPolicyConfig = (FileInspectConf *)pData; //do any housekeeping before freeing FileInspectConf if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); _dpd.snortFree(pPolicyConfig, sizeof(FileInspectConf), PP_FILE_INSPECT, PP_MEM_CATEGORY_CONFIG); } return 0; } static void * FileReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId file_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = file_config; if (file_swap_config == NULL) return NULL; file_config = file_swap_config; file_swap_config = NULL; sfPolicyUserDataFreeIterate (old_config, FileFreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_config; } return NULL; } static void FileReloadSwapFree(void *data) { if (data == NULL) return; FileFreeConfig((tSfPolicyUserContextId)data); } #endif static void print_file_stats(int exiting) { _dpd.logMsg("File Preprocessor Statistics\n"); _dpd.logMsg(" Total file type callbacks: "FMTu64("-10")" \n", file_inspect_stats.file_types_total); _dpd.logMsg(" Total file signature callbacks: "FMTu64("-10")" \n", file_inspect_stats.file_signatures_total); _dpd.logMsg(" Total files would saved to disk: "FMTu64("-10")" \n", file_inspect_stats.files_to_disk_total); _dpd.logMsg(" Total files saved to disk: "FMTu64("-10")" \n", file_inspect_stats.files_saved); _dpd.logMsg(" Total file data saved to disk: "FMTu64("-10")"bytes\n", file_inspect_stats.file_data_to_disk); _dpd.logMsg(" Total files duplicated: "FMTu64("-10")" \n", file_inspect_stats.file_duplicates_total); _dpd.logMsg(" Total files reserving failed: "FMTu64("-10")" \n", file_inspect_stats.file_reserve_failures); _dpd.logMsg(" Total file capture min: "FMTu64("-10")" \n", file_inspect_stats.file_capture_min); _dpd.logMsg(" Total file capture max: "FMTu64("-10")" \n", file_inspect_stats.file_capture_max); _dpd.logMsg(" Total file capture memcap: "FMTu64("-10")" \n", file_inspect_stats.file_capture_memcap); _dpd.logMsg(" Total files reading failed: "FMTu64("-10")" \n", file_inspect_stats.file_read_failures); _dpd.logMsg(" Total file agent memcap failures: "FMTu64("-10")" \n", file_inspect_stats.file_agent_memcap_failures); _dpd.logMsg(" Total files sent: "FMTu64("-10")" \n", file_inspect_stats.files_to_host_total); _dpd.logMsg(" Total file data sent: "FMTu64("-10")" \n", file_inspect_stats.file_data_to_host); _dpd.logMsg(" Total file transfer failures: "FMTu64("-10")" \n", file_inspect_stats.file_transfer_failures); } int FileInspectPrintMemStats(FILE *fd, char* buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; size_t total_heap_memory = meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory; if (fd) { len = fprintf(fd, ",%lu,%u,%u" ",%lu,%u,%u" ",%lu" , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , total_heap_memory); return len; } if (buffer) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics for File Inspect at: %s\n" "Total file data saved to disk: "FMTu64("-10")"bytes\n" "Total file capture max: "FMTu64("-10")" \n" "Total file capture memcap: "FMTu64("-10")" \n" ,ctime(&curr_time) ,file_inspect_stats.file_data_to_disk ,file_inspect_stats.file_capture_max ,file_inspect_stats.file_capture_memcap); } else { _dpd.logMsg("\n"); _dpd.logMsg("Memory Statistics for File Inspect at: %s\n", ctime(&curr_time)); _dpd.logMsg("Total file data saved to disk: "FMTu64("-10")" bytes\n", file_inspect_stats.file_data_to_disk); _dpd.logMsg("Total file capture max: "FMTu64("-10")" \n", file_inspect_stats.file_capture_max); _dpd.logMsg("Total file capture memcap: "FMTu64("-10")" \n", file_inspect_stats.file_capture_memcap); } return len; } snort-2.9.20/src/dynamic-preprocessors/file/file_agent.c0000644000175000017500000004560114241075727021412 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 4.11.2013 - Initial Source Code. Hcao ** ** File agent uses a separate thread to store files and also sends out ** to network. It uses file APIs and provides callbacks. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include "sf_types.h" #include "spp_file.h" #include "file_agent.h" #include "mempool.h" #include "sf_dynamic_preprocessor.h" #include "circular_buffer.h" #include "file_sha.h" #include "sfPolicy.h" int sockfd = 0; /*Use circular buffer to synchronize writer/reader threads*/ static CircularBuffer* file_list; static volatile bool stop_file_capturing = false; static volatile bool capture_thread_running = false; static bool file_type_enabled = false; static bool file_signature_enabled = false; static bool file_capture_enabled = false; pthread_t capture_thread_tid; static pid_t capture_thread_pid; uint64_t capture_disk_avaiable; /* bytes available */ static pthread_cond_t file_available_cond = PTHREAD_COND_INITIALIZER; static pthread_mutex_t file_list_mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct _FILE_MESSAGE_HEADER { /* All values must be in network byte order */ uint16_t version; uint16_t type; uint32_t length; /* Does not include the header */ char filename[FILE_NAME_LEN]; } FileMessageHeader; #define FILE_HEADER_VERSION 0x0001 #define FILE_HEADER_DATA 0x0009 static int file_agent_save_file (FileInfo *, char *); static int file_agent_send_file (FileInfo *); static FileInfo* file_agent_get_file(void); static FileInfo *file_agent_finish_file(void); static File_Verdict file_agent_type_callback(void*, void*, uint32_t, bool,uint32_t); static File_Verdict file_agent_signature_callback(void*, void*, uint8_t*, uint64_t, FileState *, bool, uint32_t, bool); static int file_agent_queue_file(void*, void *); static int file_agent_init_socket(char *hostname, int portno); /* Initialize sockets for file transfer to other host * * Args: * char *hostname: host name or IP address of receiver * int portno: port number of receiver */ int file_agent_init_socket(char *hostname, int portno) { struct sockaddr_in serv_addr; struct hostent *server; sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { FILE_FATAL_ERROR("File inspect: ERROR creating socket!\n"); return -1; } /*get the address info by either host name or IP address*/ server = gethostbyname(hostname); if (server == NULL) { _dpd.errMsg("File inspect: ERROR, no such host\n"); close(sockfd); return -1; } memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; memcpy((char *)&serv_addr.sin_addr.s_addr, (char *)server->h_addr, server->h_length); serv_addr.sin_port = htons(portno); if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) { _dpd.errMsg("File inspect: ERROR connecting host %s: %d!\n", hostname, portno); close(sockfd); return -1; } _dpd.logMsg("File inspect: Connection established on host: %s, port: %d\n", hostname, portno); return 0; } /*Send file data to other host*/ static void file_agent_send_data(int socket_fd, const uint8_t *resp, uint32_t len) { ssize_t numsent; unsigned total_len = len; unsigned total = 0; do { numsent = write(socket_fd, (*(uint8_t **)&resp) + total, total_len - total); if (!numsent) return; else if (numsent > 0) total += numsent; else if (errno != EINTR && errno != EAGAIN) { file_inspect_stats.file_transfer_failures++; return; } } while (total < total_len); } /* Process all the files in the file queue*/ static inline void file_agent_process_files(CircularBuffer *file_list, char *capture_dir, char *hostname) { while (!cbuffer_is_empty(file_list)) { FileInfo *file; file = file_agent_get_file(); if (file && file->sha256) { /* Save to disk */ if (capture_dir) file_agent_save_file(file, capture_dir); /* Send to other host */ if (hostname) file_agent_send_file(file); /* Default, memory only */ } file = file_agent_finish_file(); if (file) { _dpd.fileAPI->release_file(file->file_mem); _dpd.snortFree(file, sizeof(FileInfo), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); } } } /* This is the main thread for file capture, * either store to disk or send to network based on setting */ static void* FileCaptureThread(void *arg) { FileInspectConf* conf = (FileInspectConf*) arg; char *capture_dir = NULL; char *hostname = NULL; #if defined(LINUX) && defined(SYS_gettid) capture_thread_pid = syscall(SYS_gettid); #else capture_thread_pid = getpid(); #endif capture_thread_running = true; capture_disk_avaiable = conf->capture_disk_size<<20; if (conf->capture_dir) capture_dir = strdup(conf->capture_dir); if (conf->hostname) hostname = strdup(conf->hostname); while(1) { file_agent_process_files(file_list, capture_dir, hostname); if (stop_file_capturing) break; pthread_mutex_lock(&file_list_mutex); if (cbuffer_is_empty(file_list)) pthread_cond_wait(&file_available_cond, &file_list_mutex); pthread_mutex_unlock(&file_list_mutex); } if (conf->capture_dir) free(capture_dir); if (conf->hostname) free(hostname); capture_thread_running = false; return NULL; } void file_agent_init(struct _SnortConfig *sc, void *config) { FileInspectConf* conf = (FileInspectConf *)config; /*Need to check configuration to decide whether to enable them*/ if (conf->file_type_enabled) { _dpd.fileAPI->enable_file_type(sc, file_agent_type_callback); file_type_enabled = true; } if (conf->file_signature_enabled) { _dpd.fileAPI->enable_file_signature(sc, file_agent_signature_callback); file_signature_enabled = true; } if (conf->file_capture_enabled) { _dpd.fileAPI->enable_file_capture(sc, file_agent_signature_callback); file_capture_enabled = true; } if (!sockfd && conf->hostname) { file_agent_init_socket(conf->hostname, conf->portno); } } /* Add another thread for file capture to disk or network * When settings are changed, snort must be restarted to get it applied */ void file_agent_thread_init(struct _SnortConfig *sc, void *config) { int rval; const struct timespec thread_sleep = { 0, 100 }; sigset_t mask; FileInspectConf* conf = (FileInspectConf *)config; /* Spin off the file capture handler thread. */ sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGQUIT); sigaddset(&mask, SIGPIPE); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGHUP); sigaddset(&mask, SIGUSR1); sigaddset(&mask, SIGUSR2); sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGURG); sigaddset(&mask, SIGVTALRM); pthread_sigmask(SIG_SETMASK, &mask, NULL); file_list = cbuffer_init(conf->file_capture_queue_size); if(!file_list) { FILE_FATAL_ERROR("File capture: Unable to create file capture queue!"); } if ((rval = pthread_create(&capture_thread_tid, NULL, &FileCaptureThread, conf)) != 0) { sigemptyset(&mask); pthread_sigmask(SIG_SETMASK, &mask, NULL); FILE_FATAL_ERROR("File capture: Unable to create a " "processing thread: %s", strerror(rval)); } while (!capture_thread_running) nanosleep(&thread_sleep, NULL); sigemptyset(&mask); pthread_sigmask(SIG_SETMASK, &mask, NULL); _dpd.logMsg("File capture thread started tid=%p (pid=%u)\n", (void *) capture_thread_tid, capture_thread_pid); } /* * Files are queued in a list * Add one file to the list */ static int file_agent_queue_file(void* ssnptr, void *file_mem) { FileInfo *finfo; char *sha256; if (cbuffer_is_full(file_list)) { return -1; } finfo = _dpd.snortAlloc(1, sizeof(FileInfo), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); if (!finfo) { return -1; } sha256 = (char *) _dpd.fileAPI->get_sig_sha256(ssnptr); if (!sha256) { _dpd.snortFree(finfo, sizeof(FileInfo), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); return -1; } memcpy(finfo->sha256, sha256, SHA256_HASH_SIZE); finfo->file_mem = file_mem; finfo->file_size = _dpd.fileAPI->get_file_capture_size(file_mem); pthread_mutex_lock(&file_list_mutex); if (cbuffer_write(file_list, finfo)) { pthread_mutex_unlock(&file_list_mutex); _dpd.snortFree(finfo, sizeof(FileInfo), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); return -1; } pthread_cond_signal(&file_available_cond); pthread_mutex_unlock(&file_list_mutex); return 0; } /* * Files are queued in a list * Get one file from the list */ static FileInfo* file_agent_get_file(void) { ElemType file; if(cbuffer_peek(file_list, &file)) { return NULL; } return (FileInfo*) file; } /* * Files are queued in a list * Remove one file from the list * The file in head is removed */ static FileInfo* file_agent_finish_file(void) { ElemType file; if(cbuffer_read(file_list, &file)) { return NULL; } return (FileInfo*) file; } /* * writing file to the disk. * * In the case of interrupt errors, the write is retried, but only for a * finite number of times. * * Arguments * uint8_t *: The buffer containing the data to write * size_t: The length of the data to write * FILE *fh: File handler * * Returns: None * */ static void file_agent_write(uint8_t *buf, size_t buf_len, FILE *fh) { int max_retries = 3; size_t bytes_written = 0; int err; /* Nothing to write or nothing to write to */ if ((buf == NULL) || (fh == NULL)) return; /* writing several times */ do { size_t bytes_left = buf_len - bytes_written; bytes_written += fwrite(buf + bytes_written, 1, bytes_left, fh); err = ferror(fh); if (err && (err != EINTR) && (err != EAGAIN)) { break; } max_retries--; } while ((max_retries > 0) && (bytes_written < buf_len)); if (bytes_written < buf_len) { _dpd.errMsg("File inspect: disk writing error - %s!\n", strerror(err)); } } /* Store files on local disk */ static int file_agent_save_file(FileInfo *file, char *capture_dir) { FILE *fh; struct stat buffer; char filename[FILE_NAME_LEN + 1]; int filename_len; char *findex = filename; uint8_t *buff; int size; void *file_mem; filename_len = snprintf(filename, FILE_NAME_LEN, "%s", capture_dir); if (filename_len >= FILE_NAME_LEN ) { _dpd.snortFree(file, sizeof(FileInfo), PP_FILE_INSPECT, PP_MEM_CATEGORY_SESSION); return -1; } file_inspect_stats.files_to_disk_total++; findex += filename_len; filename_len = sha_to_str(file->sha256, findex, FILE_NAME_LEN - filename_len); /*File exists*/ if(stat (filename, &buffer) == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "File exist: %s\n", filename);); file_inspect_stats.file_duplicates_total++; return -1; } if (!capture_disk_avaiable) { return -1; } else if (capture_disk_avaiable < file->file_size) { capture_disk_avaiable = 0; _dpd.errMsg("File inspect: exceeding allocated disk size, " "can't store file!\n"); return -1; } else { capture_disk_avaiable -= file->file_size; } fh = fopen(filename, "w"); if (!fh ) { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Can't create file: %s\n", filename);); return -1; } file_mem = file->file_mem; /*Check the file buffer*/ while (file_mem) { file_mem = _dpd.fileAPI->read_file(file_mem, &buff, &size); /*Get file from file buffer*/ if (!buff || !size ) { file_inspect_stats.file_read_failures++; _dpd.logMsg("File inspect: can't read file!\n"); return -1; } file_agent_write(buff, size, fh); } fclose(fh); file_inspect_stats.files_saved++; file_inspect_stats.file_data_to_disk += file->file_size; return 0; } /* Send file data to other host*/ static int file_agent_send_file(FileInfo *file) { /*Save the file*/ FileMessageHeader fheader; void *file_mem; uint8_t *buff; int size; if (!sockfd) { return 0; } /*Send the file name*/ fheader.version = htons(FILE_HEADER_VERSION); fheader.type = htons(FILE_HEADER_DATA); fheader.length = htonl(file->file_size); memset(fheader.filename, 0, sizeof(fheader.filename)); sha_to_str(file->sha256, fheader.filename, sizeof (fheader.filename)); file_agent_send_data (sockfd, (uint8_t *)&fheader, sizeof(fheader)); DEBUG_WRAP(DebugMessage(DEBUG_FILE, "sent file: %s, with size: %d\n", fheader.filename, file->file_size);); file_mem = file->file_mem; /*Check the file buffer*/ while (file_mem) { file_mem = _dpd.fileAPI->read_file(file_mem, &buff, &size); /*Get file from file buffer*/ if (!buff || !size ) { file_inspect_stats.file_read_failures++; _dpd.logMsg("File inspect: can't read file!\n"); return -1; } file_agent_send_data(sockfd, buff, size); } file_inspect_stats.files_to_host_total++; file_inspect_stats.file_data_to_host += file->file_size; return 0; } /* Close file agent * 1) stop capture thread: waiting all files queued to be captured * 2) free file queue * 3) close socket */ void file_agent_close(void) { int rval; stop_file_capturing = true; pthread_mutex_lock(&file_list_mutex); pthread_cond_signal(&file_available_cond); pthread_mutex_unlock(&file_list_mutex); if ((rval = pthread_join(capture_thread_tid, NULL)) != 0) { FILE_FATAL_ERROR("Thread termination returned an error: %s\n", strerror(rval)); } while(capture_thread_running) sleep(1); cbuffer_free(file_list); if (sockfd) { close(sockfd); sockfd = 0; } } /* * File type callback when file type is identified * * For file capture or file signature, FILE_VERDICT_PENDING must be returned */ static File_Verdict file_agent_type_callback(void* p, void* ssnptr, uint32_t file_type_id, bool upload, uint32_t file_id) { file_inspect_stats.file_types_total++; if (file_signature_enabled || file_capture_enabled) return FILE_VERDICT_UNKNOWN; else return FILE_VERDICT_LOG; } static inline int file_agent_capture_error(FileCaptureState capture_state) { if (capture_state != FILE_CAPTURE_SUCCESS) { file_inspect_stats.file_reserve_failures++; _dpd.logMsg("File inspect: can't reserve file!\n"); switch(capture_state) { case FILE_CAPTURE_MIN: file_inspect_stats.file_capture_min++; break; case FILE_CAPTURE_MAX: file_inspect_stats.file_capture_max++; break; case FILE_CAPTURE_MEMCAP: file_inspect_stats.file_capture_memcap++; break; default: break; } return 1; } return 0; } /* * File signature callback when file transfer is completed * or capture/singature is aborted */ static File_Verdict file_agent_signature_callback (void* p, void* ssnptr, uint8_t* file_sig, uint64_t file_size, FileState *state, bool upload, uint32_t file_id, bool is_partial) { FileCaptureInfo *file_mem = NULL; FileCaptureState capture_state; File_Verdict verdict = FILE_VERDICT_UNKNOWN; FileInspectConf *conf = sfPolicyUserDataGetDefault(file_config); uint64_t capture_file_size; SFSnortPacket *pkt = (SFSnortPacket*)p; file_inspect_stats.file_signatures_total++; if (conf && file_sig) { FileSigInfo *file_verdict; file_verdict = (FileSigInfo *)sha_table_find(conf->sig_table, file_sig); if (file_verdict) { #if defined(DEBUG_MSGS) || defined (REG_TEST) static int verdict_delay = 0; if ((verdict_delay++) < conf->verdict_delay) { verdict = FILE_VERDICT_PENDING; } else #endif verdict = file_verdict->verdict; } } if (!file_capture_enabled) return verdict; /* Check whether there is any error during processing file*/ if (state->capture_state != FILE_CAPTURE_SUCCESS) { if (state->sig_state != FILE_SIG_PROCESSING) file_agent_capture_error(state->capture_state); return verdict; } /* Reserve buffer for file capture */ capture_state = _dpd.fileAPI->reserve_file(ssnptr, &file_mem); /*Check whether there is any error for the last piece of file*/ if (file_agent_capture_error(capture_state)) { return verdict; } /* Check file size */ capture_file_size = _dpd.fileAPI->get_file_capture_size(file_mem); if (file_size != capture_file_size) { _dpd.logMsg("File inspect: file size error %d != %d\n", file_size, capture_file_size); } /*Save the file to our file queue*/ if ((!is_partial) && (file_agent_queue_file(pkt->stream_session, file_mem) < 0)) { file_inspect_stats.file_agent_memcap_failures++; _dpd.logMsg("File inspect: can't queue file!\n"); return verdict; } return verdict; } snort-2.9.20/src/dynamic-preprocessors/file/file_agent.h0000644000175000017500000000333214241075730021404 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifndef _FILE_AGENT_H_ #define _FILE_AGENT_H_ #include "file_api.h" #include "file_inspect_config.h" #include "file_sha.h" #define FILE_NAME_LEN 200 typedef struct _FileInfo { char sha256[SHA256_HASH_SIZE]; size_t file_size; void *file_mem; } FileInfo; /* Initialize file processing. * This should be called when this preprocessor initialized (snort starts) */ void file_agent_init(struct _SnortConfig *sc, void *); /* Initialize file processing thread. * This should be called after initialization of daemon */ void file_agent_thread_init(struct _SnortConfig *sc, void *); /* Close file processing. * This should be called when snort exit */ void file_agent_close(void); #endif snort-2.9.20/src/dynamic-preprocessors/dynamic_preprocessors.vcxproj0000444000175000017500000002336614230012554024251 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MakeFileProj {062B5218-2264-461D-B182-860D8A8F2E7D} 10.0.17763.0 Application v141 Application v141 Utility v141 false Utility v141 false Utility v141 false Utility v141 false .\Debug\ .\Debug\ .\Release\ .\Release\ .\Release\ .\Release\ .\Debug\dynamic_preprocessors.tlb .\Debug\dynamic_preprocessors.tlb .\Release\dynamic_preprocessors.tlb .\Release\dynamic_preprocessors.tlb {30a90482-54e5-49e1-8fd3-4ab70f049d07} false {682d5f1b-d537-43f5-8d3e-90fd4926019e} false {a3ca91ad-6a00-4a8e-978d-17bc7dbc6117} false {2304868a-4dc4-46bf-98e8-00c8a41a7511} false {70a5ab00-59ea-490c-9dbb-4fc3249f44c4} false {4cc650af-8110-4ed8-92a4-cf5a34be99d6} false {442239f3-eb5e-4499-9078-ca1ee69d0055} false {584e267e-2e58-4388-b56c-3bf198680af3} false {27ac2703-100a-4534-9ec4-9dba758152fc} false {f0f86116-a76f-4a8f-b416-9baa2da4d16e} false {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false {e8b8a0a4-51a2-4d42-87c9-8aa65bea6940} false {1b63b2e7-af61-4248-83c5-1b1f0d237d2d} false {967a437f-57a3-4c85-a273-c70c75df16cc} false {6ba82e34-a6fb-4900-90fe-d88bf7aeb001} false snort-2.9.20/src/dynamic-preprocessors/dcerpc2/0000755000175000017500000000000014242725716017547 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_http.c0000644000175000017500000002004414241075637021566 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides session handling of an RPC over HTTP transport. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_http.h" #include "snort_dce2.h" #include "dce2_co.h" #include "dce2_memory.h" #include "dce2_stats.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #ifdef DUMP_BUFFER #include "dcerpc2_buffer_dump.h" #endif /******************************************************************** * Private function prototypes ********************************************************************/ static DCE2_HttpSsnData * DCE2_HttpSsnInit(void); static void DCE2_HttpProcess(DCE2_HttpSsnData *); /******************************************************************** * Function: DCE2_HttpSsnInit() * * Creates and initializes an rpc over http session data structure. * * Arguments: None * * Returns: * DCE2_HttpSsnData * * Valid pointer to an rpc over http session data structure. * NULL if unable to allocate memory. * ********************************************************************/ static DCE2_HttpSsnData * DCE2_HttpSsnInit(void) { DCE2_HttpSsnData *hsd = DCE2_Alloc(sizeof(DCE2_HttpSsnData), DCE2_MEM_TYPE__HTTP_SSN); if (hsd == NULL) return NULL; hsd->state = DCE2_HTTP_STATE__NONE; DCE2_CoInitTracker(&hsd->co_tracker); DCE2_ResetRopts(&hsd->sd.ropts); return hsd; } /******************************************************************** * Function: DCE2_HttpProxySsnInit() * * Wrapper around main session data initialization. Adds * statistical info for a proxy specific rpc over http session. * * Arguments: None * * Returns: * DCE2_HttpSsnData * * Valid pointer to an rpc over http session data structure. * NULL if unable to allocate memory. * ********************************************************************/ DCE2_HttpSsnData * DCE2_HttpProxySsnInit(void) { DCE2_HttpSsnData *hsd = DCE2_HttpSsnInit(); if (hsd == NULL) return NULL; dce2_stats.http_proxy_sessions++; return hsd; } /******************************************************************** * Function: DCE2_HttpServerSsnInit() * * Wrapper around main session data initialization. Adds * statistical info for a server specific rpc over http session. * * Arguments: None * * Returns: * DCE2_HttpSsnData * * Valid pointer to an rpc over http session data structure. * NULL if unable to allocate memory. * ********************************************************************/ DCE2_HttpSsnData * DCE2_HttpServerSsnInit(void) { DCE2_HttpSsnData *hsd = DCE2_HttpSsnInit(); if (hsd == NULL) return NULL; dce2_stats.http_server_sessions++; return hsd; } /******************************************************************** * Function: DCE2_HttpProcessProxy() * * Wrapper arount main processing point for an RPC over HTTP * session. Checks and sets session setup state for a proxy. * * Arguments: * DCE2_HttpSsnData * * Pointer to an RPC over HTTP session data structure. * * Returns: None * ********************************************************************/ void DCE2_HttpProcessProxy(DCE2_HttpSsnData *hsd) { const SFSnortPacket *p = hsd->sd.wire_pkt; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Processing RPC over HTTP proxy packet.\n")); dce2_stats.http_proxy_pkts++; if (hsd->state == DCE2_HTTP_STATE__NONE) { if (DCE2_SsnFromClient(p)) hsd->state = DCE2_HTTP_STATE__INIT_CLIENT; } DCE2_HttpProcess(hsd); } /******************************************************************** * Function: DCE2_HttpProcessServer() * * Wrapper arount main processing point for an RPC over HTTP * session. Checks and sets session setup state for a server. * * Arguments: * DCE2_HttpSsnData * * Pointer to an RPC over HTTP session data structure. * * Returns: None * ********************************************************************/ void DCE2_HttpProcessServer(DCE2_HttpSsnData *hsd) { const SFSnortPacket *p = hsd->sd.wire_pkt; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Processing RPC over HTTP server packet.\n")); dce2_stats.http_server_pkts++; if (hsd->state == DCE2_HTTP_STATE__NONE) { if (DCE2_SsnFromServer(p)) hsd->state = DCE2_HTTP_STATE__INIT_SERVER; } DCE2_HttpProcess(hsd); } /******************************************************************** * Function: DCE2_HttpProcess() * * Main processing point for an RPC over HTTP session. * * Arguments: * DCE2_HttpSsnData * * Pointer to an RPC over HTTP session data structure. * * Returns: None * ********************************************************************/ static void DCE2_HttpProcess(DCE2_HttpSsnData *hsd) { const SFSnortPacket *p = hsd->sd.wire_pkt; const uint8_t *data_ptr = p->payload; uint16_t data_len = p->payload_size; switch (hsd->state) { case DCE2_HTTP_STATE__INIT_CLIENT: #ifdef DUMP_BUFFER dumpBuffer(DCERPC_HTTP_STATE_CLIENT_DUMP,data_ptr,data_len); #endif hsd->state = DCE2_HTTP_STATE__INIT_SERVER; break; case DCE2_HTTP_STATE__INIT_SERVER: #ifdef DUMP_BUFFER dumpBuffer(DCERPC_HTTP_STATE_SERVER_DUMP,data_ptr,data_len); #endif /* Don't really need to look at server response, since if the client * RPC_CONNECT request was bad, the TCP session is terminated by * the server */ hsd->state = DCE2_HTTP_STATE__RPC_DATA; break; case DCE2_HTTP_STATE__RPC_DATA: #ifdef DUMP_BUFFER dumpBuffer(DCERPC_HTTP_STATE_RPC_DATA_DUMP,data_ptr,data_len); #endif DCE2_CoProcess(&hsd->sd, &hsd->co_tracker, data_ptr, data_len); break; default: break; } } /******************************************************************** * Function: DCE2_HttpDataFree() * * Frees dynamically allocated data within the RPC over HTTP * session data structure. * * Arguments: * DCE2_HttpSsnData * * Pointer to an RPC over HTTP session data structure. * * Returns: None * ********************************************************************/ void DCE2_HttpDataFree(DCE2_HttpSsnData *hsd) { if (hsd == NULL) return; DCE2_CoCleanTracker(&hsd->co_tracker); } /******************************************************************** * Function: DCE2_HttpSsnFree() * * Frees the session data structure and any dynamically allocated * data within it. * * Arguments: * void * * Pointer to an RPC over HTTP session data structure. * * Returns: None * ********************************************************************/ void DCE2_HttpSsnFree(void *ssn) { DCE2_HttpSsnData *hsd = (DCE2_HttpSsnData *)ssn; if (hsd == NULL) return; DCE2_HttpDataFree(hsd); DCE2_Free((void *)hsd, sizeof(DCE2_HttpSsnData), DCE2_MEM_TYPE__HTTP_SSN); } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_list.h0000644000175000017500000002406614241075647021600 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides list, queue and stack data structures and methods for use * with the preprocessor. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _DCE2_LIST_H_ #define _DCE2_LIST_H_ #include "dce2_memory.h" #include "dce2_utils.h" #include "sf_types.h" #include "snort_debug.h" /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_ListType { DCE2_LIST_TYPE__NORMAL = 0, /* Don't do anything special */ DCE2_LIST_TYPE__SORTED, /* Sort list by key */ DCE2_LIST_TYPE__SPLAYED /* Move most recently accessed node to head */ } DCE2_ListType; typedef enum _DCE2_ListFlags { DCE2_LIST_FLAG__NO_FLAG = 0x00, /* No flags */ DCE2_LIST_FLAG__NO_DUPS = 0x01, /* No duplicate keys in list */ DCE2_LIST_FLAG__INS_TAIL = 0x02 /* Insert at tail - default is to insert at head */ } DCE2_ListFlags; /******************************************************************** * Callbacks ********************************************************************/ typedef void (*DCE2_ListDataFree)(void *); typedef void (*DCE2_ListKeyFree)(void *); typedef int (*DCE2_ListKeyCompare)(const void *, const void *); /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_ListNode { void *key; void *data; struct _DCE2_ListNode *prev; struct _DCE2_ListNode *next; } DCE2_ListNode; typedef struct _DCE2_List { DCE2_ListType type; DCE2_MemType mtype; uint32_t num_nodes; int flags; DCE2_ListKeyCompare compare; DCE2_ListDataFree data_free; DCE2_ListKeyFree key_free; struct _DCE2_ListNode *head; struct _DCE2_ListNode *tail; struct _DCE2_ListNode *current; struct _DCE2_ListNode *next; struct _DCE2_ListNode *prev; } DCE2_List; typedef struct _DCE2_QueueNode { void *data; struct _DCE2_QueueNode *prev; struct _DCE2_QueueNode *next; } DCE2_QueueNode; typedef DCE2_ListDataFree DCE2_QueueDataFree; typedef struct _DCE2_Queue { uint32_t num_nodes; DCE2_MemType mtype; DCE2_QueueDataFree data_free; struct _DCE2_QueueNode *current; struct _DCE2_QueueNode *head; struct _DCE2_QueueNode *tail; struct _DCE2_QueueNode *next; struct _DCE2_QueueNode *prev; } DCE2_Queue; typedef struct _DCE2_StackNode { void *data; struct _DCE2_StackNode *prev; struct _DCE2_StackNode *next; } DCE2_StackNode; typedef DCE2_ListDataFree DCE2_StackDataFree; typedef struct _DCE2_Stack { uint32_t num_nodes; DCE2_MemType mtype; DCE2_StackDataFree data_free; struct _DCE2_StackNode *current; struct _DCE2_StackNode *head; struct _DCE2_StackNode *tail; } DCE2_Stack; /* Circular queue */ typedef DCE2_ListDataFree DCE2_CQueueDataFree; typedef struct _DCE2_CQueue { uint32_t num_nodes; DCE2_MemType mtype; DCE2_CQueueDataFree data_free; int size; int cur_idx; void **queue; int head_idx; int tail_idx; } DCE2_CQueue; typedef DCE2_ListDataFree DCE2_CStackDataFree; typedef struct _DCE2_CStack { uint32_t num_nodes; DCE2_MemType mtype; DCE2_CStackDataFree data_free; int size; void **stack; int tail_idx; int cur_idx; } DCE2_CStack; /******************************************************************** * Public function prototypes ********************************************************************/ DCE2_List * DCE2_ListNew(DCE2_ListType, DCE2_ListKeyCompare, DCE2_ListDataFree, DCE2_ListKeyFree, int, DCE2_MemType); void * DCE2_ListFind(DCE2_List *, void *); DCE2_Ret DCE2_ListFindKey(DCE2_List *, void *); DCE2_Ret DCE2_ListInsert(DCE2_List *, void *, void *); DCE2_Ret DCE2_ListRemove(DCE2_List *, void *); void * DCE2_ListFirst(DCE2_List *); void * DCE2_ListNext(DCE2_List *); void * DCE2_ListLast(DCE2_List *); void * DCE2_ListPrev(DCE2_List *); void DCE2_ListRemoveCurrent(DCE2_List *); static inline int DCE2_ListIsEmpty(DCE2_List *); void DCE2_ListEmpty(DCE2_List *); void DCE2_ListDestroy(DCE2_List *); DCE2_Queue * DCE2_QueueNew(DCE2_QueueDataFree, DCE2_MemType); DCE2_Ret DCE2_QueueEnqueue(DCE2_Queue *, void *); void * DCE2_QueueDequeue(DCE2_Queue *); void * DCE2_QueueFirst(DCE2_Queue *); void * DCE2_QueueNext(DCE2_Queue *); void * DCE2_QueueLast(DCE2_Queue *); void * DCE2_QueuePrev(DCE2_Queue *); void DCE2_QueueRemoveCurrent(DCE2_Queue *); static inline int DCE2_QueueIsEmpty(DCE2_Queue *); void DCE2_QueueEmpty(DCE2_Queue *); void DCE2_QueueDestroy(DCE2_Queue *); DCE2_Stack * DCE2_StackNew(DCE2_StackDataFree, DCE2_MemType); DCE2_Ret DCE2_StackPush(DCE2_Stack *, void *); void * DCE2_StackPop(DCE2_Stack *); void * DCE2_StackFirst(DCE2_Stack *); void * DCE2_StackNext(DCE2_Stack *); void * DCE2_StackLast(DCE2_Stack *); void * DCE2_StackPrev(DCE2_Stack *); static inline int DCE2_StackIsEmpty(DCE2_Stack *); void DCE2_StackEmpty(DCE2_Stack *); void DCE2_StackDestroy(DCE2_Stack *); DCE2_CQueue * DCE2_CQueueNew(int, DCE2_CQueueDataFree, DCE2_MemType); DCE2_Ret DCE2_CQueueEnqueue(DCE2_CQueue *, void *); void * DCE2_CQueueDequeue(DCE2_CQueue *); void * DCE2_CQueueFirst(DCE2_CQueue *); void * DCE2_CQueueNext(DCE2_CQueue *); static inline int DCE2_CQueueIsEmpty(DCE2_CQueue *); void DCE2_CQueueEmpty(DCE2_CQueue *); void DCE2_CQueueDestroy(DCE2_CQueue *); DCE2_CStack * DCE2_CStackNew(int, DCE2_CStackDataFree, DCE2_MemType); DCE2_Ret DCE2_CStackPush(DCE2_CStack *, void *); void * DCE2_CStackPop(DCE2_CStack *); void * DCE2_CStackTop(DCE2_CStack *); void * DCE2_CStackFirst(DCE2_CStack *); void * DCE2_CStackNext(DCE2_CStack *); static inline int DCE2_CStackIsEmpty(DCE2_CStack *); void DCE2_CStackEmpty(DCE2_CStack *); void DCE2_CStackDestroy(DCE2_CStack *); /******************************************************************** * Function: DCE2_ListIsEmpty() * * Determines whether or not the list has any items in it * currently. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: * int * 1 if the list has zero nodes in it or the list object * passed in is NULL. * 0 if the list has one or more nodes in it. * ********************************************************************/ static inline int DCE2_ListIsEmpty(DCE2_List *list) { if (list == NULL) return 1; if (list->num_nodes == 0) return 1; return 0; } /******************************************************************** * Function: DCE2_QueueIsEmpty() * * Determines whether or not the queue has any items in it * currently. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: * int * 1 if the queue has zero nodes in it or the queue object * passed in is NULL. * 0 if the queue has one or more nodes in it. * ********************************************************************/ static inline int DCE2_QueueIsEmpty(DCE2_Queue *queue) { if (queue == NULL) return 1; if (queue->num_nodes == 0) return 1; return 0; } /******************************************************************** * Function: DCE2_StackIsEmpty() * * Determines whether or not the stack has any items in it * currently. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: * int * 1 if the stack has zero nodes in it or the stack object * passed in is NULL. * 0 if the stack has one or more nodes in it. * ********************************************************************/ static inline int DCE2_StackIsEmpty(DCE2_Stack *stack) { if (stack == NULL) return 1; if (stack->num_nodes == 0) return 1; return 0; } /******************************************************************** * Function: DCE2_CQueueIsEmpty() * * Determines whether or not the queue has any items in it * currently. * * Arguments: * DCE2_CQueue * * A pointer to the queue object. * * Returns: * int * 1 if the queue has zero nodes in it or the queue object * passed in is NULL. * 0 if the queue has one or more nodes in it. * ********************************************************************/ static inline int DCE2_CQueueIsEmpty(DCE2_CQueue *cqueue) { if (cqueue == NULL) return 1; if (cqueue->num_nodes == 0) return 1; return 0; } /******************************************************************** * Function: DCE2_CStackIsEmpty() * * Determines whether or not the stack has any items in it * currently. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * * Returns: * int * 1 if the stack has zero nodes in it or the stack object * passed in is NULL. * 0 if the stack has one or more nodes in it. * ********************************************************************/ static inline int DCE2_CStackIsEmpty(DCE2_CStack *cstack) { if (cstack == NULL) return 1; if (cstack->num_nodes == 0) return 1; return 0; } #endif /* _DCE2_LIST_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/spp_dce2.c0000644000175000017500000020704014241075703021406 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include "sf_types.h" #include "spp_dce2.h" #include "sf_preproc_info.h" #include "dce2_memory.h" #include "dce2_list.h" #include "dce2_utils.h" #include "dce2_config.h" #include "dce2_roptions.h" #include "dce2_stats.h" #include "dce2_event.h" #include "dce2_paf.h" #include "dce2_smb.h" #include "dce2_smb2.h" #include "snort_dce2.h" #include "preprocids.h" #include "profiler.h" #include "sfrt.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #ifdef SNORT_RELOAD #include "appdata_adjuster.h" #include "dce2_session.h" #ifdef REG_TEST #include "reg_test.h" #endif #endif #ifdef DCE2_LOG_EXTRA_DATA #include "Unified2_common.h" #endif #ifdef DUMP_BUFFER #include "dcerpc2_buffer_dump.h" #endif /******************************************************************** * Global variables ********************************************************************/ #ifdef PERF_PROFILING PreprocStats dce2_pstat_main; PreprocStats dce2_pstat_session; PreprocStats dce2_pstat_new_session; PreprocStats dce2_pstat_session_state; PreprocStats dce2_pstat_detect; PreprocStats dce2_pstat_log; PreprocStats dce2_pstat_smb_seg; PreprocStats dce2_pstat_smb_req; PreprocStats dce2_pstat_smb_uid; PreprocStats dce2_pstat_smb_tid; PreprocStats dce2_pstat_smb_fid; PreprocStats dce2_pstat_smb_file; PreprocStats dce2_pstat_smb_file_detect; PreprocStats dce2_pstat_smb_file_api; PreprocStats dce2_pstat_smb_fingerprint; PreprocStats dce2_pstat_smb_negotiate; PreprocStats dce2_pstat_co_seg; PreprocStats dce2_pstat_co_frag; PreprocStats dce2_pstat_co_reass; PreprocStats dce2_pstat_co_ctx; PreprocStats dce2_pstat_cl_acts; PreprocStats dce2_pstat_cl_frag; PreprocStats dce2_pstat_cl_reass; #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 0; const int BUILD_VERSION = 3; const char *PREPROC_NAME = "SF_DCERPC2"; #define DCE2_RegisterPreprocessor DYNAMIC_PREPROC_SETUP /******************************************************************** * Macros ********************************************************************/ #ifdef PERF_PROFILING #define DCE2_PSTAT__MAIN "DceRpcMain" #define DCE2_PSTAT__SESSION "DceRpcSession" #define DCE2_PSTAT__NEW_SESSION "DceRpcNewSession" #define DCE2_PSTAT__SSN_STATE "DceRpcSessionState" #define DCE2_PSTAT__DETECT "DceRpcDetect" #define DCE2_PSTAT__LOG "DceRpcLog" #define DCE2_PSTAT__SMB_SEG "DceRpcSmbSeg" #define DCE2_PSTAT__SMB_REQ "DceRpcSmbReq" #define DCE2_PSTAT__SMB_UID "DceRpcSmbUid" #define DCE2_PSTAT__SMB_TID "DceRpcSmbTid" #define DCE2_PSTAT__SMB_FID "DceRpcSmbFid" #define DCE2_PSTAT__SMB_FILE "DceRpcSmbFile" #define DCE2_PSTAT__SMB_FILE_DETECT "DceRpcSmbFileDetect" #define DCE2_PSTAT__SMB_FILE_API "DceRpcSmbFileAPI" #define DCE2_PSTAT__SMB_FP "DceRpcSmbFingerprint" #define DCE2_PSTAT__SMB_NEG "DceRpcSmbNegotiate" #define DCE2_PSTAT__CO_SEG "DceRpcCoSeg" #define DCE2_PSTAT__CO_FRAG "DceRpcCoFrag" #define DCE2_PSTAT__CO_REASS "DceRpcCoReass" #define DCE2_PSTAT__CO_CTX "DceRpcCoCtx" #define DCE2_PSTAT__CL_ACTS "DceRpcClActs" #define DCE2_PSTAT__CL_FRAG "DceRpcClFrag" #define DCE2_PSTAT__CL_REASS "DceRpcClReass" #endif /* PERF_PROFILING */ /******************************************************************** * Private function prototypes ********************************************************************/ static void DCE2_InitGlobal(struct _SnortConfig *, char *); static void DCE2_InitServer(struct _SnortConfig *, char *); static int DCE2_CheckConfig(struct _SnortConfig *); static void DCE2_Main(void *, void *); static void DCE2_PrintStats(int); static void DCE2_Reset(int, void *); static void DCE2_ResetStats(int, void *); static void DCE2_CleanExit(int, void *); #ifdef DCE2_LOG_EXTRA_DATA static int DCE2_LogSmbFileName(void *, uint8_t **, uint32_t *, uint32_t *); #endif #ifdef SNORT_RELOAD static void DCE2_ReloadGlobal(struct _SnortConfig *, char *, void **); static void DCE2_ReloadServer(struct _SnortConfig *, char *, void **); static int DCE2_ReloadVerify(struct _SnortConfig *, void *); static bool DCE2_ReloadAdjust(bool, tSfPolicyId, void *); static void * DCE2_ReloadSwap(struct _SnortConfig *, void *); static void DCE2_ReloadSwapFree(void *); #endif static void DCE2_AddPortsToPaf(struct _SnortConfig *, DCE2_Config *, tSfPolicyId); static void DCE2_ScAddPortsToPaf(struct _SnortConfig *, void *); static uint32_t max(uint32_t a, uint32_t b); static uint32_t DCE2_GetReloadSafeMemcap(); static bool dce2_file_cache_is_enabled = false; static bool dce2_file_cache_was_enabled = false; #ifdef SNORT_RELOAD static bool dce2_ada_was_enabled = false; static bool dce2_ada_is_enabled = false; #endif int dce_print_mem_stats(FILE *, char *, PreprocMemInfo *); /******************************************************************** * Function: DCE2_RegisterPreprocessor() * * Purpose: Registers the DCE/RPC preprocessor with Snort * * Arguments: None * * Returns: None * ********************************************************************/ void DCE2_RegisterPreprocessor(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc(DCE2_GNAME, DCE2_InitGlobal); _dpd.registerPreproc(DCE2_SNAME, DCE2_InitServer); #else _dpd.registerPreproc(DCE2_GNAME, DCE2_InitGlobal, DCE2_ReloadGlobal, DCE2_ReloadVerify, DCE2_ReloadSwap, DCE2_ReloadSwapFree); _dpd.registerPreproc(DCE2_SNAME, DCE2_InitServer, DCE2_ReloadServer, NULL, NULL, NULL); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getDCERPC2Buffers, DCERPC2_BUFFER_DUMP_FUNC); #endif _dpd.registerMemoryStatsFunc(PP_DCE2, dce_print_mem_stats); } /********************************************************************* * Function: DCE2_InitGlobal() * * Purpose: Initializes the global DCE/RPC preprocessor config. * * Arguments: snort.conf argument line for the DCE/RPC preprocessor. * * Returns: None * *********************************************************************/ static void DCE2_InitGlobal(struct _SnortConfig *sc, char *args) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); DCE2_Config *pDefaultPolicyConfig = NULL; DCE2_Config *pCurrentPolicyConfig = NULL; if ((_dpd.streamAPI == NULL) || (_dpd.streamAPI->version != STREAM_API_VERSION5)) { DCE2_Die("%s(%d) \"%s\" configuration: " "Stream must be enabled with TCP and UDP tracking.", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } if (dce2_config == NULL) { dce2_config = sfPolicyConfigCreate(); dce2_file_cache_is_enabled = false; dce2_file_cache_was_enabled = false; #ifdef SNORT_RELOAD dce2_ada_was_enabled = false; dce2_ada_is_enabled = false; #endif if (dce2_config == NULL) { DCE2_Die("%s(%d) \"%s\" configuration: Could not allocate memory " "configuration.\n", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } DCE2_MemInit(); DCE2_StatsInit(); DCE2_EventsInit(); smb_file_name[0] = '\0'; /* Initialize reassembly packet */ DCE2_InitRpkts(); #ifdef ACTIVE_RESPONSE DCE2_SmbInitDeletePdu(); #endif DCE2_SmbInitGlobals(); _dpd.addPreprocConfCheck(sc, DCE2_CheckConfig); _dpd.registerPreprocStats(DCE2_GNAME, DCE2_PrintStats); _dpd.addPreprocReset(DCE2_Reset, NULL, PRIORITY_LAST, PP_DCE2); _dpd.addPreprocResetStats(DCE2_ResetStats, NULL, PRIORITY_LAST, PP_DCE2); _dpd.addPreprocExit(DCE2_CleanExit, NULL, PRIORITY_LAST, PP_DCE2); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc(DCE2_PSTAT__MAIN, &dce2_pstat_main, 0, _dpd.totalPerfStats, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SESSION, &dce2_pstat_session, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__NEW_SESSION, &dce2_pstat_new_session, 2, &dce2_pstat_session, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SSN_STATE, &dce2_pstat_session_state, 2, &dce2_pstat_session, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__LOG, &dce2_pstat_log, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__DETECT, &dce2_pstat_detect, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_SEG, &dce2_pstat_smb_seg, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_REQ, &dce2_pstat_smb_req, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_UID, &dce2_pstat_smb_uid, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_TID, &dce2_pstat_smb_tid, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_FID, &dce2_pstat_smb_fid, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_FILE, &dce2_pstat_smb_file, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_FILE_DETECT, &dce2_pstat_smb_file_detect, 2, &dce2_pstat_smb_file, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_FILE_API, &dce2_pstat_smb_file_api, 2, &dce2_pstat_smb_file, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_FP, &dce2_pstat_smb_fingerprint, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__SMB_NEG, &dce2_pstat_smb_negotiate, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__CO_SEG, &dce2_pstat_co_seg, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__CO_FRAG, &dce2_pstat_co_frag, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__CO_REASS, &dce2_pstat_co_reass, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__CO_CTX, &dce2_pstat_co_ctx, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__CL_ACTS, &dce2_pstat_cl_acts, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__CL_FRAG, &dce2_pstat_cl_frag, 1, &dce2_pstat_main, NULL); _dpd.addPreprocProfileFunc(DCE2_PSTAT__CL_REASS, &dce2_pstat_cl_reass, 1, &dce2_pstat_main, NULL); #endif #ifdef TARGET_BASED dce2_proto_ids.dcerpc = _dpd.findProtocolReference(DCE2_PROTO_REF_STR__DCERPC); if (dce2_proto_ids.dcerpc == SFTARGET_UNKNOWN_PROTOCOL) dce2_proto_ids.dcerpc = _dpd.addProtocolReference(DCE2_PROTO_REF_STR__DCERPC); /* smb and netbios-ssn refer to the same thing */ dce2_proto_ids.nbss = _dpd.findProtocolReference(DCE2_PROTO_REF_STR__NBSS); if (dce2_proto_ids.nbss == SFTARGET_UNKNOWN_PROTOCOL) dce2_proto_ids.nbss = _dpd.addProtocolReference(DCE2_PROTO_REF_STR__NBSS); // register with session to handle service _dpd.sessionAPI->register_service_handler( PP_DCE2, dce2_proto_ids.dcerpc ); _dpd.sessionAPI->register_service_handler( PP_DCE2, dce2_proto_ids.nbss ); #endif } sfPolicyUserPolicySet(dce2_config, policy_id); pDefaultPolicyConfig = (DCE2_Config *)sfPolicyUserDataGetDefault(dce2_config); pCurrentPolicyConfig = (DCE2_Config *)sfPolicyUserDataGetCurrent(dce2_config); if ((policy_id != 0) && (pDefaultPolicyConfig == NULL)) { DCE2_Die("%s(%d) \"%s\" configuration: Must configure default policy " "if other policies are to be configured.\n", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } /* Can only do one global configuration */ if (pCurrentPolicyConfig != NULL) { DCE2_Die("%s(%d) \"%s\" configuration: Only one global configuration can be specified.", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } DCE2_RegRuleOptions(sc); pCurrentPolicyConfig = (DCE2_Config *)DCE2_Alloc(sizeof(DCE2_Config), DCE2_MEM_TYPE__CONFIG); sfPolicyUserDataSetCurrent(dce2_config, pCurrentPolicyConfig); /* Parse configuration args */ DCE2_GlobalConfigure(pCurrentPolicyConfig, args); if (policy_id != 0) pCurrentPolicyConfig->gconfig->memcap = pDefaultPolicyConfig->gconfig->memcap; if ( pCurrentPolicyConfig->gconfig->disabled ) return; /* Register callbacks */ _dpd.addPreproc(sc, DCE2_Main, PRIORITY_APPLICATION, PP_DCE2, PROTO_BIT__TCP | PROTO_BIT__UDP); #ifdef TARGET_BASED _dpd.streamAPI->set_service_filter_status (sc, dce2_proto_ids.dcerpc, PORT_MONITOR_SESSION, policy_id, 1); _dpd.streamAPI->set_service_filter_status (sc, dce2_proto_ids.nbss, PORT_MONITOR_SESSION, policy_id, 1); #endif #ifdef SNORT_RELOAD if (!ada) { size_t memcap = DCE2_GetReloadSafeMemcap(dce2_config); ada = ada_init(DCE2_MemInUse, PP_DCE2, memcap); if (!ada) _dpd.fatalMsg("Failed to initialize DCE ADA session cache.\n"); } dce2_ada_is_enabled = true; #endif } /********************************************************************* * Function: DCE2_InitServer() * * Purpose: Initializes a DCE/RPC server configuration * * Arguments: snort.conf argument line for the DCE/RPC preprocessor. * * Returns: None * *********************************************************************/ static void DCE2_InitServer(struct _SnortConfig *sc, char *args) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); DCE2_Config *pPolicyConfig = NULL; if (dce2_config != NULL) { sfPolicyUserPolicySet (dce2_config, policy_id); pPolicyConfig = (DCE2_Config *)sfPolicyUserDataGetCurrent(dce2_config); } if ((dce2_config == NULL) || (pPolicyConfig == NULL) || (pPolicyConfig->gconfig == NULL)) { DCE2_Die("%s(%d) \"%s\" configuration: \"%s\" must be configured " "before \"%s\".", *_dpd.config_file, *_dpd.config_line, DCE2_SNAME, DCE2_GNAME, DCE2_SNAME); } /* Parse configuration args */ DCE2_ServerConfigure(sc, pPolicyConfig, args); // enable preproc for ports of interest... // TBD-EDM - verify... DCE2_RegisterPortsWithSession( sc, pPolicyConfig->dconfig ); } static int DCE2_CheckConfigPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { int rval; DCE2_Config *pPolicyConfig = (DCE2_Config *)pData; DCE2_ServerConfig *dconfig; if ( pPolicyConfig->gconfig->disabled ) return 0; _dpd.setParserPolicy(sc, policyId); // config_file/config_line are not set here if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { DCE2_Log(DCE2_LOG_TYPE__WARN, "Stream must be enabled with TCP and UDP tracking."); return -1; } dconfig = pPolicyConfig->dconfig; if (dconfig == NULL) { if ((rval = DCE2_CreateDefaultServerConfig(sc, pPolicyConfig, policyId))) return rval; } #ifdef TARGET_BASED if (!_dpd.isAdaptiveConfiguredForSnortConfig(sc)) #endif { if ((rval = DCE2_ScCheckTransports(pPolicyConfig))) return rval; } DCE2_AddPortsToPaf(sc, pPolicyConfig, policyId); #ifdef TARGET_BASED DCE2_PafRegisterService(sc, dce2_proto_ids.nbss, policyId, DCE2_TRANS_TYPE__SMB); DCE2_PafRegisterService(sc, dce2_proto_ids.dcerpc, policyId, DCE2_TRANS_TYPE__TCP); #endif #ifdef DCE2_LOG_EXTRA_DATA pPolicyConfig->xtra_logging_smb_file_name_id = _dpd.streamAPI->reg_xtra_data_cb(DCE2_LogSmbFileName); #endif /* Register routing table memory */ if (pPolicyConfig->sconfigs != NULL) DCE2_RegMem(sfrt_usage(pPolicyConfig->sconfigs), DCE2_MEM_TYPE__RT); if (!pPolicyConfig->gconfig->legacy_mode) { DCE2_Smb2Init(pPolicyConfig->gconfig->memcap); dce2_file_cache_is_enabled = true; } return 0; } /********************************************************************* * Function: DCE2_CheckConfig() * * Purpose: Verifies the DCE/RPC preprocessor configuration * * Arguments: None * * Returns: None * *********************************************************************/ static int DCE2_CheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate (sc, dce2_config, DCE2_CheckConfigPolicy))) { return rval; } return 0; } /********************************************************************* * Function: DCE2_Main() * * Purpose: Main entry point for DCE/RPC processing. * * Arguments: * void * - pointer to packet structure * void * - pointer to context * * Returns: None * *********************************************************************/ static void DCE2_Main(void *pkt, void *context) { SFSnortPacket *p = (SFSnortPacket *)pkt; PROFILE_VARS; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ALL, "%s\n", DCE2_DEBUG__START_MSG)); sfPolicyUserPolicySet (dce2_config, _dpd.getNapRuntimePolicy()); #ifdef DUMP_BUFFER dumpBufferInit(); #endif #ifdef DEBUG_MSGS if (DCE2_SsnFromServer(p)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Packet from Server.\n")); } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Packet from Client.\n")); } #endif // preconditions - what we registered for assert((IsUDP(p) || IsTCP(p)) && p->payload && p->payload_size); /* No inspection to do */ if ( !_dpd.sessionAPI->is_session_verified( p->stream_session ) ) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Session not established - not inspecting.\n")); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ALL, "%s\n", DCE2_DEBUG__END_MSG)); return; } PREPROC_PROFILE_START(dce2_pstat_main); if (DCE2_Process(p) == DCE2_RET__INSPECTED) DCE2_DisableDetect(p); PREPROC_PROFILE_END(dce2_pstat_main); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ALL, "%s\n", DCE2_DEBUG__END_MSG)); } #ifdef DCE2_LOG_EXTRA_DATA /****************************************************************** * Function: DCE2_LogSmbFileName * * Purpose: Callback for unified2 logging of extra data, in this * case the SMB file name. * * Arguments: * void * - stream session pointer * uint8_t ** - pointer to buffer for extra data * uint32_t * - pointer to length of extra data * uint32_t * - pointer to type of extra data * * Returns: * int - 1 for success * 0 for failure * ******************************************************************/ static int DCE2_LogSmbFileName(void *ssn_ptr, uint8_t **buf, uint32_t *len, uint32_t *type) { if ((_dpd.streamAPI->get_application_data(ssn_ptr, PP_DCE2) == NULL) || (smb_file_name_len == 0)) return 0; *buf = (uint8_t *)smb_file_name; *len = smb_file_name_len; *type = EVENT_INFO_SMB_FILENAME; return 1; } #endif /****************************************************************** * Function: DCE2_PrintStats() * * Purpose: Print statistics being kept by the preprocessor. * * Arguments: * int - whether Snort is exiting or not * * Returns: None * ******************************************************************/ static void DCE2_PrintStats(int exiting) { int smb_com; int sub_com; _dpd.logMsg("dcerpc2 Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions: "STDu64"\n", dce2_stats.sessions); _dpd.logMsg(" Active sessions: "STDu64"\n", dce2_stats.sessions_active); if (dce2_stats.sessions > 0) { if (dce2_stats.sessions_autodetected > 0) _dpd.logMsg(" Total sessions autodetected: "STDu64"\n", dce2_stats.sessions_autodetected); if (dce2_stats.sessions_aborted > 0) _dpd.logMsg(" Total sessions aborted: "STDu64"\n", dce2_stats.sessions_aborted); if (dce2_stats.bad_autodetects > 0) _dpd.logMsg(" Bad autodetects: "STDu64"\n", dce2_stats.bad_autodetects); if (dce2_stats.events > 0) _dpd.logMsg(" Preprocessor events: "STDu64"\n", dce2_stats.events); #ifdef DEBUG { unsigned int port; int first = 1; for (port = 0; port < (sizeof(dce2_stats.autoports) / sizeof(dce2_stats.autoports[0])); port++) { DCE2_TransType ttype; for (ttype = DCE2_TRANS_TYPE__NONE; ttype < DCE2_TRANS_TYPE__MAX; ttype++) { if ((dce2_stats.autoports[port][ttype] > 0) && (dce2_trans_strs[ttype] != NULL)) { if (first) { _dpd.logMsg("\n"); _dpd.logMsg(" Autodetected ports:\n"); _dpd.logMsg(" %7s%15s%15s\n", "Port", "Transport", "Total"); first = 0; } _dpd.logMsg(" %7u%15s"FMTu64("15")"\n", port, dce2_trans_strs[ttype], dce2_stats.autoports[port][ttype]); } } } } #endif _dpd.logMsg("\n"); _dpd.logMsg(" Transports\n"); if (dce2_stats.smb_sessions > 0) { _dpd.logMsg(" SMB\n"); _dpd.logMsg(" Total sessions: "STDu64"\n", dce2_stats.smb_sessions); _dpd.logMsg(" Packet stats\n"); _dpd.logMsg(" Packets: "STDu64"\n", dce2_stats.smb_pkts); if (dce2_stats.smb_ignored_bytes > 0) _dpd.logMsg(" Ignored bytes: "STDu64"\n", dce2_stats.smb_ignored_bytes); if (dce2_stats.smb_files_processed > 0) _dpd.logMsg(" Files processed: "STDu64"\n", dce2_stats.smb_files_processed); if (dce2_stats.smb_cli_seg_reassembled > 0) _dpd.logMsg(" Client TCP reassembled: "STDu64"\n", dce2_stats.smb_cli_seg_reassembled); if (dce2_stats.smb_srv_seg_reassembled > 0) _dpd.logMsg(" Server TCP reassembled: "STDu64"\n", dce2_stats.smb_srv_seg_reassembled); _dpd.logMsg(" Maximum outstanding requests: "STDu64"\n", dce2_stats.smb_max_outstanding_requests); // SMB command stats _dpd.logMsg(" SMB command requests/responses processed\n"); for (smb_com = 0; smb_com < SMB_MAX_NUM_COMS; smb_com++) { SmbAndXCom andx = smb_chain_map[smb_com]; // Print out the stats for command requests if ((dce2_stats.smb_com_stats[SMB_TYPE__REQUEST][smb_com] != 0) || (dce2_stats.smb_com_stats[SMB_TYPE__RESPONSE][smb_com] != 0)) { _dpd.logMsg(" %s (0x%02X) : "STDu64"/"STDu64"\n", smb_com_strings[smb_com], smb_com, dce2_stats.smb_com_stats[SMB_TYPE__REQUEST][smb_com], dce2_stats.smb_com_stats[SMB_TYPE__RESPONSE][smb_com]); switch (smb_com) { case SMB_COM_TRANSACTION: for (sub_com = 0; sub_com < TRANS_SUBCOM_MAX+1; sub_com++) { if ((dce2_stats.smb_trans_subcom_stats[SMB_TYPE__REQUEST][sub_com] != 0) || (dce2_stats.smb_trans_subcom_stats[SMB_TYPE__RESPONSE][sub_com] != 0)) { _dpd.logMsg(" %s (0x%04X) : "STDu64"/"STDu64"\n", (sub_com < TRANS_SUBCOM_MAX) ? smb_transaction_sub_command_strings[sub_com] : "Unknown", sub_com, dce2_stats.smb_trans_subcom_stats[SMB_TYPE__REQUEST][sub_com], dce2_stats.smb_trans_subcom_stats[SMB_TYPE__RESPONSE][sub_com]); } } break; case SMB_COM_TRANSACTION2: for (sub_com = 0; sub_com < TRANS2_SUBCOM_MAX+1; sub_com++) { if ((dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__REQUEST][sub_com] != 0) || (dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__RESPONSE][sub_com] != 0)) { _dpd.logMsg(" %s (0x%04X) : "STDu64"/"STDu64"\n", (sub_com < TRANS2_SUBCOM_MAX) ? smb_transaction2_sub_command_strings[sub_com] : "Unknown", sub_com, dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__REQUEST][sub_com], dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__RESPONSE][sub_com]); } } break; case SMB_COM_NT_TRANSACT: for (sub_com = 0; sub_com < NT_TRANSACT_SUBCOM_MAX+1; sub_com++) { if ((dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__REQUEST][sub_com] != 0) || (dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__RESPONSE][sub_com] != 0)) { _dpd.logMsg(" %s (0x%04X) : "STDu64"/"STDu64"\n", (sub_com < NT_TRANSACT_SUBCOM_MAX) ? smb_nt_transact_sub_command_strings[sub_com] : "Unknown", sub_com, dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__REQUEST][sub_com], dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__RESPONSE][sub_com]); } } break; default: break; } } // Print out chaining stats for AndX command requests if (andx != SMB_ANDX_COM__NONE) { int chained_com; for (chained_com = 0; chained_com < SMB_MAX_NUM_COMS; chained_com++) { if ((dce2_stats.smb_chained_stats[SMB_TYPE__REQUEST][andx][chained_com] != 0) || (dce2_stats.smb_chained_stats[SMB_TYPE__RESPONSE][andx][chained_com] != 0)) { _dpd.logMsg(" => %s (0x%02X) : "STDu64"/"STDu64"\n", smb_com_strings[chained_com], chained_com, dce2_stats.smb_chained_stats[SMB_TYPE__REQUEST][andx][chained_com], dce2_stats.smb_chained_stats[SMB_TYPE__RESPONSE][andx][chained_com]); } } } } _dpd.logMsg(" Memory stats (bytes)\n"); _dpd.logMsg(" Current total: %u\n", dce2_memory.smb_total); _dpd.logMsg(" Maximum total: %u\n", dce2_memory.smb_total_max); _dpd.logMsg(" Current session data: %u\n", dce2_memory.smb_ssn); _dpd.logMsg(" Maximum session data: %u\n", dce2_memory.smb_ssn_max); _dpd.logMsg(" Current segmentation buffering: %u\n", dce2_memory.smb_seg); _dpd.logMsg(" Maximum segmentation buffering: %u\n", dce2_memory.smb_seg_max); _dpd.logMsg(" Current uid tracking: %u\n", dce2_memory.smb_uid); _dpd.logMsg(" Maximum uid tracking: %u\n", dce2_memory.smb_uid_max); _dpd.logMsg(" Current tid tracking: %u\n", dce2_memory.smb_tid); _dpd.logMsg(" Maximum tid tracking: %u\n", dce2_memory.smb_tid_max); _dpd.logMsg(" Current fid tracking: %u\n", dce2_memory.smb_fid); _dpd.logMsg(" Maximum fid tracking: %u\n", dce2_memory.smb_fid_max); _dpd.logMsg(" Current file tracking: %u\n", dce2_memory.smb_file); _dpd.logMsg(" Maximum file tracking: %u\n", dce2_memory.smb_file_max); _dpd.logMsg(" Current request tracking: %u\n", dce2_memory.smb_req); _dpd.logMsg(" Maximum request tracking: %u\n", dce2_memory.smb_req_max); /* SMB2 stats */ if (!exiting) { DCE2_Smb2UpdateStats(); } _dpd.logMsg(" SMB2\n"); _dpd.logMsg(" Smb2 prunes: "STDu64"\n", dce2_stats.smb2_prunes); _dpd.logMsg(" Memory used for smb2 processing: "STDu64"\n", dce2_stats.smb2_memory_in_use); _dpd.logMsg(" Maximum memory used for smb2 processing: "STDu64"\n", dce2_stats.smb2_memory_in_use_max); _dpd.logMsg(" SMB2 command requests/responses processed\n"); _dpd.logMsg(" smb2 create : "STDu64"\n", dce2_stats.smb2_create); _dpd.logMsg(" smb2 write : "STDu64"\n", dce2_stats.smb2_write); _dpd.logMsg(" smb2 read : "STDu64"\n", dce2_stats.smb2_read); _dpd.logMsg(" smb2 set info : "STDu64"\n", dce2_stats.smb2_set_info); _dpd.logMsg(" smb2 tree connect : "STDu64"\n", dce2_stats.smb2_tree_connect); _dpd.logMsg(" smb2 tree disconnect: "STDu64"\n", dce2_stats.smb2_tree_disconnect); _dpd.logMsg(" smb2 close : "STDu64"\n", dce2_stats.smb2_close); } if (dce2_stats.tcp_sessions > 0) { _dpd.logMsg(" TCP\n"); _dpd.logMsg(" Total sessions: "STDu64"\n", dce2_stats.tcp_sessions); _dpd.logMsg(" Packet stats\n"); _dpd.logMsg(" Packets: "STDu64"\n", dce2_stats.tcp_pkts); _dpd.logMsg(" Memory stats (bytes)\n"); _dpd.logMsg(" Current total: %u\n", dce2_memory.tcp_total); _dpd.logMsg(" Maximum total: %u\n", dce2_memory.tcp_total_max); _dpd.logMsg(" Current session data: %u\n", dce2_memory.tcp_ssn); _dpd.logMsg(" Maximum session data: %u\n", dce2_memory.tcp_ssn_max); } if (dce2_stats.udp_sessions > 0) { _dpd.logMsg(" UDP\n"); _dpd.logMsg(" Total sessions: "STDu64"\n", dce2_stats.udp_sessions); _dpd.logMsg(" Packet stats\n"); _dpd.logMsg(" Packets: "STDu64"\n", dce2_stats.udp_pkts); _dpd.logMsg(" Memory stats (bytes)\n"); _dpd.logMsg(" Current total: %u\n", dce2_memory.udp_total); _dpd.logMsg(" Maximum total: %u\n", dce2_memory.udp_total_max); _dpd.logMsg(" Current session data: %u\n", dce2_memory.udp_ssn); _dpd.logMsg(" Maximum session data: %u\n", dce2_memory.udp_ssn_max); } if ((dce2_stats.http_server_sessions > 0) || (dce2_stats.http_proxy_sessions > 0)) { _dpd.logMsg(" RPC over HTTP\n"); if (dce2_stats.http_server_sessions > 0) _dpd.logMsg(" Total server sessions: "STDu64"\n", dce2_stats.http_server_sessions); if (dce2_stats.http_proxy_sessions > 0) _dpd.logMsg(" Total proxy sessions: "STDu64"\n", dce2_stats.http_proxy_sessions); _dpd.logMsg(" Packet stats\n"); if (dce2_stats.http_server_sessions > 0) _dpd.logMsg(" Server packets: "STDu64"\n", dce2_stats.http_server_pkts); if (dce2_stats.http_proxy_sessions > 0) _dpd.logMsg(" Proxy packets: "STDu64"\n", dce2_stats.http_proxy_pkts); _dpd.logMsg(" Memory stats (bytes)\n"); _dpd.logMsg(" Current total: %u\n", dce2_memory.http_total); _dpd.logMsg(" Maximum total: %u\n", dce2_memory.http_total_max); _dpd.logMsg(" Current session data: %u\n", dce2_memory.http_ssn); _dpd.logMsg(" Maximum session data: %u\n", dce2_memory.http_ssn_max); } if ((dce2_stats.co_pdus > 0) || (dce2_stats.cl_pkts > 0)) { _dpd.logMsg("\n"); _dpd.logMsg(" DCE/RPC\n"); if (dce2_stats.co_pdus > 0) { _dpd.logMsg(" Connection oriented\n"); _dpd.logMsg(" Packet stats\n"); _dpd.logMsg(" PDUs: "STDu64"\n", dce2_stats.co_pdus); if ((dce2_stats.co_bind > 0) || (dce2_stats.co_bind_ack > 0)) { _dpd.logMsg(" Bind: "STDu64"\n", dce2_stats.co_bind); _dpd.logMsg(" Bind Ack: "STDu64"\n", dce2_stats.co_bind_ack); } if ((dce2_stats.co_alter_ctx > 0) || (dce2_stats.co_alter_ctx_resp > 0)) { _dpd.logMsg(" Alter context: "STDu64"\n", dce2_stats.co_alter_ctx); _dpd.logMsg(" Alter context response: "STDu64"\n", dce2_stats.co_alter_ctx_resp); } if (dce2_stats.co_bind_nack > 0) _dpd.logMsg(" Bind Nack: "STDu64"\n", dce2_stats.co_bind_nack); if ((dce2_stats.co_request > 0) || (dce2_stats.co_response > 0)) { _dpd.logMsg(" Request: "STDu64"\n", dce2_stats.co_request); _dpd.logMsg(" Response: "STDu64"\n", dce2_stats.co_response); } if (dce2_stats.co_fault > 0) _dpd.logMsg(" Fault: "STDu64"\n", dce2_stats.co_fault); if (dce2_stats.co_reject > 0) _dpd.logMsg(" Reject: "STDu64"\n", dce2_stats.co_reject); if (dce2_stats.co_auth3 > 0) _dpd.logMsg(" Auth3: "STDu64"\n", dce2_stats.co_auth3); if (dce2_stats.co_shutdown > 0) _dpd.logMsg(" Shutdown: "STDu64"\n", dce2_stats.co_shutdown); if (dce2_stats.co_cancel > 0) _dpd.logMsg(" Cancel: "STDu64"\n", dce2_stats.co_cancel); if (dce2_stats.co_orphaned > 0) _dpd.logMsg(" Orphaned: "STDu64"\n", dce2_stats.co_orphaned); if (dce2_stats.co_ms_pdu > 0) _dpd.logMsg(" Microsoft Request To Send RPC over HTTP: "STDu64"\n", dce2_stats.co_ms_pdu); if (dce2_stats.co_other_req > 0) _dpd.logMsg(" Other request type: "STDu64"\n", dce2_stats.co_other_req); if (dce2_stats.co_other_resp > 0) _dpd.logMsg(" Other response type: "STDu64"\n", dce2_stats.co_other_resp); _dpd.logMsg(" Request fragments: "STDu64"\n", dce2_stats.co_req_fragments); if (dce2_stats.co_req_fragments > 0) { _dpd.logMsg(" Min fragment size: "STDu64"\n", dce2_stats.co_cli_min_frag_size); _dpd.logMsg(" Max fragment size: "STDu64"\n", dce2_stats.co_cli_max_frag_size); _dpd.logMsg(" Frag reassembled: "STDu64"\n", dce2_stats.co_cli_frag_reassembled); } _dpd.logMsg(" Response fragments: "STDu64"\n", dce2_stats.co_resp_fragments); if (dce2_stats.co_resp_fragments > 0) { _dpd.logMsg(" Min fragment size: "STDu64"\n", dce2_stats.co_srv_min_frag_size); _dpd.logMsg(" Max fragment size: "STDu64"\n", dce2_stats.co_srv_max_frag_size); _dpd.logMsg(" Frag reassembled: "STDu64"\n", dce2_stats.co_srv_frag_reassembled); } _dpd.logMsg(" Client PDU segmented reassembled: "STDu64"\n", dce2_stats.co_cli_seg_reassembled); _dpd.logMsg(" Server PDU segmented reassembled: "STDu64"\n", dce2_stats.co_srv_seg_reassembled); _dpd.logMsg(" Memory stats (bytes)\n"); _dpd.logMsg(" Current segmentation buffering: %u\n", dce2_memory.co_seg); _dpd.logMsg(" Maximum segmentation buffering: %u\n", dce2_memory.co_seg_max); _dpd.logMsg(" Current fragment tracker: %u\n", dce2_memory.co_frag); _dpd.logMsg(" Maximum fragment tracker: %u\n", dce2_memory.co_frag_max); _dpd.logMsg(" Current context tracking: %u\n", dce2_memory.co_ctx); _dpd.logMsg(" Maximum context tracking: %u\n", dce2_memory.co_ctx_max); } if (dce2_stats.cl_pkts > 0) { _dpd.logMsg(" Connectionless\n"); _dpd.logMsg(" Packet stats\n"); _dpd.logMsg(" Packets: "STDu64"\n", dce2_stats.cl_pkts); if ((dce2_stats.cl_request > 0) || (dce2_stats.cl_response > 0)) { _dpd.logMsg(" Request: "STDu64"\n", dce2_stats.cl_request); _dpd.logMsg(" Response: "STDu64"\n", dce2_stats.cl_response); } if (dce2_stats.cl_ack > 0) _dpd.logMsg(" Ack: "STDu64"\n", dce2_stats.cl_ack); if (dce2_stats.cl_cancel > 0) _dpd.logMsg(" Cancel: "STDu64"\n", dce2_stats.cl_cancel); if (dce2_stats.cl_cli_fack > 0) _dpd.logMsg(" Client Fack: "STDu64"\n", dce2_stats.cl_cli_fack); if (dce2_stats.cl_ping > 0) _dpd.logMsg(" Ping: "STDu64"\n", dce2_stats.cl_ping); if (dce2_stats.cl_reject > 0) _dpd.logMsg(" Reject: "STDu64"\n", dce2_stats.cl_reject); if (dce2_stats.cl_cancel_ack > 0) _dpd.logMsg(" Cancel Ack: "STDu64"\n", dce2_stats.cl_cancel_ack); if (dce2_stats.cl_srv_fack > 0) _dpd.logMsg(" Server Fack: "STDu64"\n", dce2_stats.cl_srv_fack); if (dce2_stats.cl_fault > 0) _dpd.logMsg(" Fault: "STDu64"\n", dce2_stats.cl_fault); if (dce2_stats.cl_nocall > 0) _dpd.logMsg(" NoCall: "STDu64"\n", dce2_stats.cl_nocall); if (dce2_stats.cl_working > 0) _dpd.logMsg(" Working: "STDu64"\n", dce2_stats.cl_working); if (dce2_stats.cl_other_req > 0) _dpd.logMsg(" Other request type: "STDu64"\n", dce2_stats.cl_other_req); if (dce2_stats.cl_other_resp > 0) _dpd.logMsg(" Other response type: "STDu64"\n", dce2_stats.cl_other_resp); _dpd.logMsg(" Fragments: "STDu64"\n", dce2_stats.cl_fragments); _dpd.logMsg(" Max fragment size: "STDu64"\n", dce2_stats.cl_max_frag_size); _dpd.logMsg(" Reassembled: "STDu64"\n", dce2_stats.cl_frag_reassembled); if (dce2_stats.cl_max_seqnum > 0) _dpd.logMsg(" Max seq num: "STDu64"\n", dce2_stats.cl_max_seqnum); _dpd.logMsg(" Memory stats (bytes)\n"); _dpd.logMsg(" Current activity tracker: %u\n", dce2_memory.cl_act); _dpd.logMsg(" Maximum activity tracker: %u\n", dce2_memory.cl_act_max); _dpd.logMsg(" Current fragment tracker: %u\n", dce2_memory.cl_frag); _dpd.logMsg(" Maximum fragment tracker: %u\n", dce2_memory.cl_frag_max); } } } /* Have to free it here because CleanExit is called before stats functions * (so anything flushed by stream can go through and count towards stats) */ if (exiting) DCE2_StatsFree(); _dpd.logMsg("\n"); _dpd.logMsg(" Memory stats (bytes)\n"); _dpd.logMsg(" Current total: %u\n", dce2_memory.total); _dpd.logMsg(" Maximum total: %u\n", dce2_memory.total_max); _dpd.logMsg(" Current runtime total: %u\n", dce2_memory.rtotal); _dpd.logMsg(" Maximum runtime total: %u\n", dce2_memory.rtotal_max); _dpd.logMsg(" Current config total: %u\n", dce2_memory.config); _dpd.logMsg(" Maximum config total: %u\n", dce2_memory.config_max); _dpd.logMsg(" Current rule options total: %u\n", dce2_memory.roptions); _dpd.logMsg(" Maximum rule options total: %u\n", dce2_memory.roptions_max); _dpd.logMsg(" Current routing table total: %u\n", dce2_memory.rt); _dpd.logMsg(" Maximum routing table total: %u\n", dce2_memory.rt_max); _dpd.logMsg(" Current initialization total: %u\n", dce2_memory.init); _dpd.logMsg(" Maximum initialization total: %u\n", dce2_memory.init_max); } uint32_t dce_total_memcap(void) { DCE2_Config *pDefaultPolicyConfig = NULL; if (dce2_config) { pDefaultPolicyConfig = (DCE2_Config *)sfPolicyUserDataGetDefault(dce2_config); return pDefaultPolicyConfig->gconfig->memcap; } return 0; } uint32_t dce_free_total_memcap(void) { if (dce2_config) { return dce_total_memcap() - dce2_memory.total; } return 0; } int dce_print_mem_stats(FILE *fd, char* buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; size_t total_heap_memory = meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory + meminfo[PP_MEM_CATEGORY_MISC].used_memory; if (fd) { len = fprintf(fd, ","STDu64","STDu64","STDu64"" ",%u,%u,%u,%u" ","STDu64",%u,%u,%u,%u" ","STDu64",%u,%u,%u,%u" ","STDu64","STDu64",%u,%u,%u,%u" ",%lu,%u,%u" ",%lu,%u,%u" ",%lu,%u,%u,%lu" , dce2_stats.sessions , dce2_stats.sessions_active , dce2_stats.smb_sessions , dce2_memory.smb_total , dce2_memory.smb_total_max , dce2_memory.smb_ssn , dce2_memory.smb_ssn_max , dce2_stats.tcp_sessions , dce2_memory.tcp_total , dce2_memory.tcp_total_max , dce2_memory.tcp_ssn , dce2_memory.tcp_ssn_max , dce2_stats.udp_sessions , dce2_memory.udp_total , dce2_memory.udp_total_max , dce2_memory.udp_ssn , dce2_memory.udp_ssn_max , dce2_stats.http_server_sessions , dce2_stats.http_proxy_sessions , dce2_memory.http_total , dce2_memory.http_total_max , dce2_memory.http_ssn , dce2_memory.http_ssn_max , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_MISC].used_memory , meminfo[PP_MEM_CATEGORY_MISC].num_of_alloc , meminfo[PP_MEM_CATEGORY_MISC].num_of_free , total_heap_memory); return len; } if (buffer) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics for DCE at: %s\n" "dcerpc2 Preprocessor Statistics:\n" " Total sessions : "STDu64"\n" " Active sessions : "STDu64"\n" " Total SMB sessions : "STDu64"\n" " Total TCP sessions : "STDu64"\n" " Total UDP sessions : "STDu64"\n" " Total HTTP server sessions : "STDu64"\n" " Total HTTP proxy sessions : "STDu64"\n" "\nTotal Memory stats :\n" " Current memory : %u\n" " Maximum memory : %u\n" " Total memcap : %u\n" " Free memory : %u\n" "\nSMB Memory stats :\n" " Current memory : %u\n" " Maximum memory : %u\n" " Current session data : %u\n" " Maximum session data : %u\n" " Current segmentation buffering : %u\n" " Maximum segmentation buffering : %u\n" "\nTCP Memory stats :\n" " Current memory : %u\n" " Maximum memory : %u\n" " Current session data : %u\n" " Maximum session data : %u\n" "\nUDP Memory stats :\n" " Current memory : %u\n" " Maximum memory : %u\n" " Current session data : %u\n" " Maximum session data : %u\n" "\nHTTP Memory stats :\n" " Current memory : %u\n" " Maximum memory : %u\n" " Current session data : %u\n" " Maximum session data : %u\n" , ctime(&curr_time) , dce2_stats.sessions , dce2_stats.sessions_active , dce2_stats.smb_sessions , dce2_stats.tcp_sessions , dce2_stats.udp_sessions , dce2_stats.http_server_sessions , dce2_stats.http_proxy_sessions , dce2_memory.total , dce2_memory.total_max , dce_total_memcap() , dce_free_total_memcap() , dce2_memory.smb_total , dce2_memory.smb_total_max , dce2_memory.smb_ssn , dce2_memory.smb_ssn_max , dce2_memory.smb_seg , dce2_memory.smb_seg_max , dce2_memory.tcp_total , dce2_memory.tcp_total_max , dce2_memory.tcp_ssn , dce2_memory.tcp_ssn_max , dce2_memory.udp_total , dce2_memory.udp_total_max , dce2_memory.udp_ssn , dce2_memory.udp_ssn_max , dce2_memory.http_total , dce2_memory.http_total_max , dce2_memory.http_ssn , dce2_memory.http_ssn_max); } else { _dpd.logMsg("\n"); _dpd.logMsg("Memory Statistics of DCE at: %s\n",ctime(&curr_time)); _dpd.logMsg("dcerpc2 Preprocessor Statistics:\n"); _dpd.logMsg(" Total sessions : "STDu64"\n", dce2_stats.sessions); _dpd.logMsg(" Active sessions : "STDu64"\n", dce2_stats.sessions_active); _dpd.logMsg(" Total SMB sessions : "STDu64"\n", dce2_stats.smb_sessions); _dpd.logMsg(" Total TCP sessions : "STDu64"\n", dce2_stats.tcp_sessions); _dpd.logMsg(" Total UDP sessions : "STDu64"\n", dce2_stats.udp_sessions); _dpd.logMsg(" Total HTTP server sessions : "STDu64"\n", dce2_stats.http_server_sessions); _dpd.logMsg(" Total HTTP proxy sessions : "STDu64"\n", dce2_stats.http_proxy_sessions); _dpd.logMsg("Total Memory stats :\n"); _dpd.logMsg(" Current total : %u\n", dce2_memory.total); _dpd.logMsg(" Maximum total : %u\n", dce2_memory.total_max); _dpd.logMsg(" Total memcap : %u\n", dce_total_memcap()); _dpd.logMsg(" Free total : %u\n", dce_free_total_memcap()); _dpd.logMsg("SMB Memory stats :\n"); _dpd.logMsg(" Current total : %u\n", dce2_memory.smb_total); _dpd.logMsg(" Maximum total : %u\n", dce2_memory.smb_total_max); _dpd.logMsg(" Current session data : %u\n", dce2_memory.smb_ssn); _dpd.logMsg(" Maximum session data : %u\n", dce2_memory.smb_ssn_max); _dpd.logMsg(" Current segmentation buffer : %u\n", dce2_memory.smb_seg); _dpd.logMsg(" Maximum segmentation buffer : %u\n", dce2_memory.smb_seg_max); _dpd.logMsg("TCP Memory stats :\n"); _dpd.logMsg(" Current total : %u\n", dce2_memory.tcp_total); _dpd.logMsg(" Maximum total : %u\n", dce2_memory.tcp_total_max); _dpd.logMsg(" Current session data : %u\n", dce2_memory.tcp_ssn); _dpd.logMsg(" Maximum session data : %u\n", dce2_memory.tcp_ssn_max); _dpd.logMsg("UDP Memory stats :\n"); _dpd.logMsg(" Current total : %u\n", dce2_memory.udp_total); _dpd.logMsg(" Maximum total : %u\n", dce2_memory.udp_total_max); _dpd.logMsg(" Current session data : %u\n", dce2_memory.udp_ssn); _dpd.logMsg(" Maximum session data : %u\n", dce2_memory.udp_ssn_max); _dpd.logMsg("HTTP Memory stats :\n"); _dpd.logMsg(" Current total : %u\n", dce2_memory.http_total); _dpd.logMsg(" Maximum total : %u\n", dce2_memory.http_total_max); _dpd.logMsg(" Current session data : %u\n", dce2_memory.http_ssn); _dpd.logMsg(" Maximum session data : %u\n", dce2_memory.http_ssn_max); } return len; } /****************************************************************** * Function: DCE2_Reset() * * Purpose: Reset the preprocessor to a post configuration state. * * Arguments: * int - signal that caused the reset * void * - pointer to data * * Returns: None * ******************************************************************/ static void DCE2_Reset(int signal, void *data) { if (!DCE2_CStackIsEmpty(dce2_pkt_stack)) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Packet stack is not empty when it should be.", __FILE__, __LINE__); DCE2_CStackEmpty(dce2_pkt_stack); } } /****************************************************************** * Function: DCE2_ResetStats() * * Purpose: Reset any statistics being kept by the preprocessor. * * Arguments: * int - signal that caused function to be called * void * - pointer to data * * Returns: None * ******************************************************************/ static void DCE2_ResetStats(int signal, void *data) { DCE2_StatsInit(); } /****************************************************************** * Function: DCE2_CleanExit() * * Purpose: Do any cleanup necessary when Snort exits. * * Arguments: * int - signal that caused Snort to exit * void * - pointer to data * * Returns: None * ******************************************************************/ static void DCE2_CleanExit(int signal, void *data) { DCE2_FreeConfigs(dce2_config); dce2_config = NULL; #ifdef SNORT_RELOAD ada_delete( ada ); ada = NULL; #endif DCE2_FreeGlobals(); DCE2_Smb2Close(); } #ifdef SNORT_RELOAD /********************************************************************* * Function: DCE2_ReloadGlobal() * * Purpose: Creates a new global DCE/RPC preprocessor config. * * Arguments: snort.conf argument line for the DCE/RPC preprocessor. * * Returns: None * *********************************************************************/ static void DCE2_ReloadGlobal(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId dce2_swap_config = (tSfPolicyUserContextId)*new_config; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); DCE2_Config *pDefaultPolicyConfig = NULL; DCE2_Config *pCurrentPolicyConfig = NULL; if ((_dpd.streamAPI == NULL) || (_dpd.streamAPI->version != STREAM_API_VERSION5)) { DCE2_Die("%s(%d) \"%s\" configuration: " "Stream must be enabled with TCP and UDP tracking.", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } if (dce2_swap_config == NULL) { //create a context dce2_swap_config = sfPolicyConfigCreate(); dce2_file_cache_was_enabled = !DCE2_IsFileCache(NULL); dce2_file_cache_is_enabled = false; dce2_ada_is_enabled = false; dce2_ada_was_enabled = ada != NULL; if (dce2_swap_config == NULL) { DCE2_Die("%s(%d) \"%s\" configuration: Could not allocate memory " "configuration.\n", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } *new_config = (void *)dce2_swap_config; } sfPolicyUserPolicySet(dce2_swap_config, policy_id); pDefaultPolicyConfig = (DCE2_Config *)sfPolicyUserDataGetDefault(dce2_swap_config); pCurrentPolicyConfig = (DCE2_Config *)sfPolicyUserDataGetCurrent(dce2_swap_config); if ((policy_id != 0) && (pDefaultPolicyConfig == NULL)) { DCE2_Die("%s(%d) \"%s\" configuration: Must configure default policy " "if other policies are to be configured.\n", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } /* Can only do one global configuration */ if (pCurrentPolicyConfig != NULL) { DCE2_Die("%s(%d) \"%s\" configuration: Only one global configuration can be specified.", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); } DCE2_RegRuleOptions(sc); pCurrentPolicyConfig = (DCE2_Config *)DCE2_Alloc(sizeof(DCE2_Config), DCE2_MEM_TYPE__CONFIG); sfPolicyUserDataSetCurrent(dce2_swap_config, pCurrentPolicyConfig); /* Parse configuration args */ DCE2_GlobalConfigure(pCurrentPolicyConfig, args); if ( pCurrentPolicyConfig->gconfig->disabled ) return; _dpd.addPreproc(sc, DCE2_Main, PRIORITY_APPLICATION, PP_DCE2, PROTO_BIT__TCP | PROTO_BIT__UDP); #ifdef TARGET_BASED _dpd.streamAPI->set_service_filter_status (sc, dce2_proto_ids.dcerpc, PORT_MONITOR_SESSION, policy_id, 1); _dpd.streamAPI->set_service_filter_status (sc, dce2_proto_ids.nbss, PORT_MONITOR_SESSION, policy_id, 1); #endif if (policy_id != 0) pCurrentPolicyConfig->gconfig->memcap = pDefaultPolicyConfig->gconfig->memcap; if (!ada) { size_t memcap = DCE2_GetReloadSafeMemcap(dce2_swap_config); ada = ada_init(DCE2_MemInUse, PP_DCE2, memcap); if (!ada) _dpd.fatalMsg("Failed to initialize DCE ADA session cache.\n"); } dce2_ada_is_enabled = true; } /********************************************************************* * Function: DCE2_ReloadServer() * * Purpose: Creates a new DCE/RPC server configuration * * Arguments: snort.conf argument line for the DCE/RPC preprocessor. * * Returns: None * *********************************************************************/ static void DCE2_ReloadServer(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId dce2_swap_config; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); DCE2_Config *pPolicyConfig = NULL; dce2_swap_config = (tSfPolicyUserContextId)_dpd.getRelatedReloadData(sc, DCE2_GNAME); if (dce2_swap_config != NULL) { sfPolicyUserPolicySet (dce2_swap_config, policy_id); pPolicyConfig = (DCE2_Config *)sfPolicyUserDataGetCurrent(dce2_swap_config); } if ((dce2_swap_config == NULL) || (pPolicyConfig == NULL) || (pPolicyConfig->gconfig == NULL)) { DCE2_Die("%s(%d) \"%s\" configuration: \"%s\" must be configured " "before \"%s\".", *_dpd.config_file, *_dpd.config_line, DCE2_SNAME, DCE2_GNAME, DCE2_SNAME); } /* Parse configuration args */ DCE2_ServerConfigure(sc, pPolicyConfig, args); } static int DCE2_ReloadVerifyPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { int rval; DCE2_Config *swap_config = (DCE2_Config *)pData; DCE2_Config *current_config = (DCE2_Config *)sfPolicyUserDataGet(dce2_config, policyId); DCE2_ServerConfig *dconfig; //do any housekeeping before freeing DCE2_Config if ( swap_config == NULL || swap_config->gconfig->disabled ) return 0; if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { DCE2_Log(DCE2_LOG_TYPE__WARN, "%s(%d) \"%s\" configuration: " "Stream must be enabled with TCP and UDP tracking.", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME); return -1; } dconfig = swap_config->dconfig; if (dconfig == NULL) { if ((rval = DCE2_CreateDefaultServerConfig(sc, swap_config, policyId))) return rval; } #ifdef TARGET_BASED if (!_dpd.isAdaptiveConfiguredForSnortConfig(sc)) #endif { if ((rval = DCE2_ScCheckTransports(swap_config))) return rval; } DCE2_AddPortsToPaf(sc, swap_config, policyId); #ifdef TARGET_BASED DCE2_PafRegisterService(sc, dce2_proto_ids.nbss, policyId, DCE2_TRANS_TYPE__SMB); DCE2_PafRegisterService(sc, dce2_proto_ids.dcerpc, policyId, DCE2_TRANS_TYPE__TCP); #endif #ifdef DCE2_LOG_EXTRA_DATA swap_config->xtra_logging_smb_file_name_id = _dpd.streamAPI->reg_xtra_data_cb(DCE2_LogSmbFileName); #endif /* Register routing table memory */ if (swap_config->sconfigs != NULL) DCE2_RegMem(sfrt_usage(swap_config->sconfigs), DCE2_MEM_TYPE__RT); if (!swap_config->gconfig->legacy_mode) { DCE2_Smb2Init(swap_config->gconfig->memcap); dce2_file_cache_is_enabled = true; } if (current_config == NULL) return 0; return 0; } /********************************************************************* * Function: DCE2_ReloadVerify() * WARNING This runs in a thread that is Asynchronous with the * packet loop thread * * Purpose: Verifies a reloaded DCE/RPC preprocessor configuration * * Arguments: None * * Returns: * int * -1 if changed configuration value requires a restart * 0 if configuration is ok * *********************************************************************/ static int DCE2_ReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId dce2_swap_config = (tSfPolicyUserContextId)swap_config; if ((dce2_swap_config == NULL) || (dce2_config == NULL)) return 0; if (sfPolicyUserDataIterate(sc, dce2_swap_config, DCE2_ReloadVerifyPolicy) != 0) { return -1; } tSfPolicyId policy_id = _dpd.getParserPolicy(sc); /* Look in DCE2_InitGlobal and DCE2_ReloadGlobal to find that DefaultPolicy can't be NULL * You'll also find that all memcaps are synched to DefaultPolicy as the memcap is global * to all policies * Here we are setting up the work that needs to be done to adjust to the new configuration */ uint32_t current_memcap = DCE2_GetReloadSafeMemcap(dce2_config); uint32_t new_memcap = DCE2_GetReloadSafeMemcap(dce2_swap_config); if (dce2_ada_was_enabled && !dce2_ada_is_enabled) { ada_set_new_cap(ada, 0); _dpd.reloadAdjustRegister(sc, "dce2-mem-reloader", policy_id, DCE2_ReloadAdjust, NULL, NULL); } else if (new_memcap != current_memcap || (dce2_file_cache_was_enabled && !dce2_file_cache_is_enabled)) { ada_set_new_cap(ada, (size_t)(new_memcap)); _dpd.reloadAdjustRegister(sc, "dce2-mem-reloader", policy_id, DCE2_ReloadAdjust, NULL, NULL); } return 0; } /********************************************************************* * Function: DCE2_ReloadAdjust * * Purpose: After reloading with a different configuration that * that requres work to adapt, this function will perform the * work in small bursts. * * Arguments: idle - if snort is idling (low packets) do more work * raPolicyId - identifies which policy the config is for * userData - data needed by this function * * Returns: * bool * return false if it needs to work more * return true if there is no work left * *********************************************************************/ static bool DCE2_ReloadAdjust(bool idle, tSfPolicyId raPolicyId, void *userData) { /* delete files until the file cache memcaps are met * delete file cache if not needed * delete dce session data from sessions until dce global memcap is met * delete ada cache if not needed * return true when completed successfully, else false */ int maxWork = idle ? 512 : 32; bool memcaps_satisfied = DCE2_Smb2AdjustFileCache(maxWork, dce2_file_cache_is_enabled) && ada_reload_adjust_func(idle, raPolicyId, (void *) ada); if (memcaps_satisfied && dce2_ada_was_enabled && !dce2_ada_is_enabled) { ada_delete(ada); ada = NULL; } #ifdef REG_TEST if (memcaps_satisfied && REG_TEST_FLAG_RELOAD & getRegTestFlags()) printf("dcerpc2-reload reload-adjust %d %d \n", ada != NULL, !DCE2_IsFileCache(NULL)); #endif return memcaps_satisfied; } static int DCE2_ReloadSwapPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { DCE2_Config *pPolicyConfig = (DCE2_Config *)pData; //do any housekeeping before freeing config if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); DCE2_FreeConfig(pPolicyConfig); } return 0; } /********************************************************************* * Function: DCE2_ReloadSwap() * * Purpose: Swaps a new config for the old one. * * Arguments: None * * Returns: None * *********************************************************************/ static void * DCE2_ReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId dce2_swap_config = (tSfPolicyUserContextId) swap_config; tSfPolicyUserContextId old_config = dce2_config; if (dce2_swap_config == NULL) return NULL; /* Set file cache's new memcaps so it doesn't misbehave * Ensure that the correct amount of memory is registered * See DCE2_Smb2Init for inspiration */ uint32_t current_memcap = 0; if (dce2_file_cache_was_enabled) current_memcap = DCE2_GetReloadSafeMemcap(dce2_config); uint32_t swap_memcap = 0; if (dce2_file_cache_is_enabled) swap_memcap = DCE2_GetReloadSafeMemcap(dce2_swap_config); DCE2_SetSmbMemcap((uint64_t) swap_memcap >> 1); if (dce2_file_cache_was_enabled) { DCE2_UnRegMem(current_memcap >> 1, DCE2_MEM_TYPE__SMB_SSN); if (dce2_file_cache_is_enabled) DCE2_RegMem(swap_memcap >> 1, DCE2_MEM_TYPE__SMB_SSN); } dce2_config = dce2_swap_config; sfPolicyUserDataFreeIterate (old_config, DCE2_ReloadSwapPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) return (void *)old_config; return NULL; } static void DCE2_ReloadSwapFree(void *data) { if (data == NULL) return; DCE2_FreeConfigs((tSfPolicyUserContextId)data); } #endif // Used for iterate function below since we can't pass it static tSfPolicyId dce2_paf_tmp_policy_id = 0; /********************************************************************* * Function: DCE2_AddPortsToPaf() * * Add detect and autodetect ports to stream5 paf * * Arguments: * DCE2_Config * * Pointer to configuration structure. * * Returns: None * *********************************************************************/ static void DCE2_AddPortsToPaf(struct _SnortConfig *sc, DCE2_Config *config, tSfPolicyId policy_id) { if (config == NULL) return; dce2_paf_tmp_policy_id = policy_id; DCE2_ScAddPortsToPaf(sc, config->dconfig); if (config->sconfigs != NULL) sfrt_iterate_with_snort_config(sc, config->sconfigs, DCE2_ScAddPortsToPaf); dce2_paf_tmp_policy_id = 0; } static void DCE2_ScAddPortsToPaf(struct _SnortConfig *snortConf, void *data) { DCE2_ServerConfig *sc = (DCE2_ServerConfig *)data; unsigned int port; tSfPolicyId policy_id = dce2_paf_tmp_policy_id; if (data == NULL) return; for (port = 0; port < DCE2_PORTS__MAX; port++) { if (DCE2_IsPortSet(sc->smb_ports, (uint16_t)port)) { DCE2_PafRegisterPort(snortConf, (uint16_t)port, policy_id, DCE2_TRANS_TYPE__SMB); } if (DCE2_IsPortSet(sc->auto_smb_ports, (uint16_t)port)) { DCE2_PafRegisterPort(snortConf, (uint16_t)port, policy_id, DCE2_TRANS_TYPE__SMB); } if (DCE2_IsPortSet(sc->tcp_ports, (uint16_t)port)) { DCE2_PafRegisterPort(snortConf, (uint16_t)port, policy_id, DCE2_TRANS_TYPE__TCP); } if (DCE2_IsPortSet(sc->auto_tcp_ports, (uint16_t)port)) { DCE2_PafRegisterPort(snortConf, (uint16_t)port, policy_id, DCE2_TRANS_TYPE__TCP); } #if 0 if (DCE2_IsPortSet(sc->http_proxy_ports, (uint16_t)port)) { /* TODO Implement PAF registration and callback. */ } if (DCE2_IsPortSet(sc->http_server_ports, (uint16_t)port)) { /* TODO Implement PAF registration and callback. */ } #endif } } static uint32_t max(uint32_t a, uint32_t b) { if (a >= b) return a; return b; } /********************************************************************* * Function: DCE2_GetReloadSafeMemcap * * Purpose: Provide a safe memcap that can be used durring * packet processing. * This is based on how the memcaps are set in InitGlobal and * ReloadGlobal * * The memcap in the config for the policyId currently running is * what is used as a memcap, according to DCE2_Process calling * DCE2_GcMemcap. * * Based on InitGlobal and ReloadGlobal all memcaps are equal to * the memcap in the default policy except for policy_id 0 * * So to be safe we'll just return the max of the two. * * This function also helps to prevent segmentation faults caused * by accessing null pointers. * * Arguments: pConfig - mapping between policyIDs and configs * * Returns: A safe memcap * *********************************************************************/ static uint32_t DCE2_GetReloadSafeMemcap(tSfPolicyUserContextId pConfig) { DCE2_Config *pDefaultPolicyConfig = sfPolicyUserDataGetDefault(pConfig); DCE2_Config *pPolicyConfig = sfPolicyUserDataGet(pConfig, 0); uint32_t defaultMem = pDefaultPolicyConfig == NULL ? 0 : pDefaultPolicyConfig->gconfig->memcap; uint32_t policyMem = pPolicyConfig == NULL ? 0 : pPolicyConfig->gconfig->memcap; return max(defaultMem,policyMem); } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_utils.h0000644000175000017500000003465714241075674021774 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_UTILS_H_ #define _DCE2_UTILS_H_ #include "dce2_debug.h" #include "dce2_memory.h" #include "dcerpc.h" #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "snort_debug.h" #include "snort_bounds.h" /******************************************************************** * Macros ********************************************************************/ #define DCE2_SENTINEL -1 /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_Ret { DCE2_RET__SUCCESS = 0, DCE2_RET__ERROR, DCE2_RET__MEMCAP, DCE2_RET__NOT_INSPECTED, DCE2_RET__INSPECTED, DCE2_RET__REASSEMBLE, DCE2_RET__SEG, DCE2_RET__FULL, DCE2_RET__FRAG, DCE2_RET__ALERTED, DCE2_RET__IGNORE, DCE2_RET__DUPLICATE } DCE2_Ret; typedef enum _DCE2_TransType { DCE2_TRANS_TYPE__NONE = 0, DCE2_TRANS_TYPE__SMB, DCE2_TRANS_TYPE__TCP, DCE2_TRANS_TYPE__UDP, DCE2_TRANS_TYPE__HTTP_PROXY, DCE2_TRANS_TYPE__HTTP_SERVER, DCE2_TRANS_TYPE__MAX } DCE2_TransType; typedef enum _DCE2_BufferMinAddFlag { DCE2_BUFFER_MIN_ADD_FLAG__USE, DCE2_BUFFER_MIN_ADD_FLAG__IGNORE } DCE2_BufferMinAddFlag; typedef enum _DCE2_BufType { DCE2_BUF_TYPE__NULL, DCE2_BUF_TYPE__SEG, DCE2_BUF_TYPE__FRAG } DCE2_BufType; typedef enum _DCE2_LogType { DCE2_LOG_TYPE__LOG, DCE2_LOG_TYPE__WARN, DCE2_LOG_TYPE__ERROR } DCE2_LogType; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_Buffer { uint8_t *data; uint32_t len; uint32_t size; DCE2_MemType mtype; uint32_t min_add_size; uint32_t offset; } DCE2_Buffer; /******************************************************************** * Inline function prototypes ********************************************************************/ #define DCE2_MOVE(data_ptr, data_len, amount) \ { data_len -= (amount); data_ptr = (uint8_t *)data_ptr + (amount); } static inline int DCE2_BufferIsEmpty(DCE2_Buffer *); static inline void DCE2_BufferEmpty(DCE2_Buffer *); static inline uint32_t DCE2_BufferSize(DCE2_Buffer *); static inline uint32_t DCE2_BufferLength(DCE2_Buffer *); static inline void DCE2_BufferSetLength(DCE2_Buffer *, uint32_t); static inline uint8_t * DCE2_BufferData(DCE2_Buffer *); static inline uint32_t DCE2_BufferMinAllocSize(DCE2_Buffer *); static inline void DCE2_BufferSetMinAllocSize(DCE2_Buffer *, uint32_t); static inline char * DCE2_PruneWhiteSpace(char *); static inline int DCE2_IsEmptyStr(char *); static inline DCE2_Ret DCE2_Memcpy(void *, const void *, uint32_t, const void *, const void *); static inline DCE2_Ret DCE2_Memmove(void *, const void *, uint32_t, const void *, const void *); static inline int DCE2_UuidCompare(const void *, const void *); static inline void DCE2_CopyUuid(Uuid *, const Uuid *, const DceRpcBoFlag); /******************************************************************** * Public function prototypes ********************************************************************/ DCE2_Buffer * DCE2_BufferNew(uint32_t, uint32_t, DCE2_MemType); DCE2_Ret DCE2_BufferAddData(DCE2_Buffer *, const uint8_t *, uint32_t, uint32_t, DCE2_BufferMinAddFlag); DCE2_Ret DCE2_BufferMoveData(DCE2_Buffer *, uint32_t, const uint8_t *, uint32_t); void DCE2_BufferDestroy(DCE2_Buffer *); DCE2_Ret DCE2_HandleSegmentation(DCE2_Buffer *, const uint8_t *, uint16_t, uint32_t, uint16_t *); NORETURN void DCE2_Die(const char *, ...); void DCE2_Log(DCE2_LogType, const char *, ...); const char * DCE2_UuidToStr(const Uuid *, DceRpcBoFlag); void DCE2_PrintPktData(const uint8_t *, const uint16_t); /********************************************************************* * Function: DCE2_BufferIsEmpty() * * Determines whether or not a buffer should be considered empty. * Buffer is considered empty if it is NULL, it's data is NULL * or the length of the data is zero. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * * Returns: * 1 if considered empty * 0 if not considered empty * *********************************************************************/ static inline int DCE2_BufferIsEmpty(DCE2_Buffer *buf) { if (buf == NULL) return 1; if ((buf->data == NULL) || (buf->len == 0)) return 1; return 0; } /********************************************************************* * Function: DCE2_BufferEmpty() * * Sets the buffer's data length to zero. Essentially says that * any copied data is not important anymore. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * * Returns: None * *********************************************************************/ static inline void DCE2_BufferEmpty(DCE2_Buffer *buf) { if (buf == NULL) return; buf->len = 0; } /********************************************************************* * Function: DCE2_BufferSize() * * Returns the size of the data currently allocated for storing data. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * * Returns: * uint32_t * The size of the allocated data or zero if buffer * object is NULL. * *********************************************************************/ static inline uint32_t DCE2_BufferSize(DCE2_Buffer *buf) { if (buf == NULL) return 0; return buf->size; } /********************************************************************* * Function: DCE2_BufferLength() * * Returns the length of the data copied into the buffer. This will * always be less than or equal to the size of the data allocated. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * * Returns: * uint32_t * The length of the data copied into the buffer or zero * if buffer object is NULL. * *********************************************************************/ static inline uint32_t DCE2_BufferLength(DCE2_Buffer *buf) { if (buf == NULL) return 0; return buf->len; } /********************************************************************* * Function: DCE2_BufferOffset() * * Returns the offset of the data copied into the buffer. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * * Returns: * uint32_t * The length of the data copied into the buffer or zero * if buffer object is NULL. * *********************************************************************/ static inline uint32_t DCE2_BufferOffset(DCE2_Buffer *buf) { if (buf == NULL) return 0; return buf->offset; } /********************************************************************* * Function: DCE2_BufferSetLength() * * Sets the length of the buffer up to the buffer size. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * uint32_t len * The length to set the amount of data in the buffer to * * Returns: None * *********************************************************************/ static inline void DCE2_BufferSetLength(DCE2_Buffer *buf, uint32_t len) { if (buf == NULL) return; if (len > buf->size) buf->len = buf->size; else buf->len = len; } /********************************************************************* * Function: DCE2_BufferData() * * Returns a pointer to the allocated data. Note that this could * be NULL if not data has been allocated yet. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * * Returns: * uint8_t * * Pointer to the buffer data or NULL if none allocated or * buffer object is NULL. * *********************************************************************/ static inline uint8_t * DCE2_BufferData(DCE2_Buffer *buf) { if (buf == NULL) return NULL; return buf->data; } /********************************************************************* * Function: DCE2_BufferMinAllocSize() * * Returns the minimum allocation size for the buffer. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * * Returns: * uint32_t * The minimum allocation size. * *********************************************************************/ static inline uint32_t DCE2_BufferMinAllocSize(DCE2_Buffer *buf) { if (buf == NULL) return 0; return buf->min_add_size; } /********************************************************************* * Function: DCE2_BufferSetMinAllocSize() * * Sets the minimum allocation size for the buffer. * * Arguments: * DCE2_Buffer * * Pointer to buffer object. * uint32_t * Size to set the minimum allocation size to. * * Returns: None * *********************************************************************/ static inline void DCE2_BufferSetMinAllocSize(DCE2_Buffer *buf, uint32_t size) { if (buf == NULL) return; buf->min_add_size = size; } /******************************************************************** * Function: DCE2_PruneWhiteSpace() * * Prunes whitespace surrounding string. * String must be 0 terminated. * * Arguments: * char * * NULL terminated string to prune. * int * length of string * * Returns: * char * - Pointer to the pruned string. Note that the pointer * still points within the original string. * * Side effects: Spaces at the end of the string passed in as an * argument are replaced by NULL bytes. * ********************************************************************/ static inline char * DCE2_PruneWhiteSpace(char *str) { char *end; if (str == NULL) return NULL; /* Put end a char before NULL byte */ end = str + (strlen(str) - 1); while (isspace((int)*str)) str++; while ((end > str) && isspace((int)*end)) { *end = '\0'; end--; } return str; } /******************************************************************** * Function: DCE2_IsEmptyStr() * * Checks if string is NULL, empty or just spaces. * String must be 0 terminated. * * Arguments: None * char * - string to check * * Returns: * 1 if string is NULL, empty or just spaces * 0 otherwise * ********************************************************************/ static inline int DCE2_IsEmptyStr(char *str) { char *end; if (str == NULL) return 1; end = str + strlen(str); while ((str < end) && isspace((int)*str)) str++; if (str == end) return 1; return 0; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * DCE2_RET__ERROR - memcpy failed * DCE2_RET__SUCCESS - memcpy succeeded * ********************************************************************/ static inline DCE2_Ret DCE2_Memcpy(void *dst, const void *src, uint32_t len, const void *dst_start, const void *dst_end) { if (SafeMemcpy(dst, src, (size_t)len, dst_start, dst_end) != SAFEMEM_SUCCESS) return DCE2_RET__ERROR; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * DCE2_RET__ERROR - memmove failed * DCE2_RET__SUCCESS - memmove succeeded * ********************************************************************/ static inline DCE2_Ret DCE2_Memmove(void *dst, const void *src, uint32_t len, const void *dst_start, const void *dst_end) { if (SafeMemmove(dst, src, (size_t)len, dst_start, dst_end) != SAFEMEM_SUCCESS) return DCE2_RET__ERROR; return DCE2_RET__SUCCESS; } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ static inline int DCE2_UuidCompare(const void *data1, const void *data2) { const Uuid *uuid1 = (Uuid *)data1; const Uuid *uuid2 = (Uuid *)data2; if ((uuid1 == NULL) || (uuid2 == NULL)) return -1; if ((uuid1->time_low == uuid2->time_low) && (uuid1->time_mid == uuid2->time_mid) && (uuid1->time_high_and_version == uuid2->time_high_and_version) && (uuid1->clock_seq_and_reserved == uuid2->clock_seq_and_reserved) && (uuid1->clock_seq_low == uuid2->clock_seq_low) && (memcmp(uuid1->node, uuid2->node, sizeof(uuid1->node)) == 0)) { return 0; } /* Just return something other than 0 */ return -1; } /********************************************************************* * Function: DCE2_CopyUuid() * * Copies a src uuid to a dst uuid based on the byte * order specified. * * Arguments: * Uuid * * Pointer to uuid to copy to. * Uuid * * Pointer to uuid to copy from. * const int * The byte order to use. * * Returns: None * *********************************************************************/ static inline void DCE2_CopyUuid(Uuid *dst_uuid, const Uuid *pkt_uuid, const DceRpcBoFlag byte_order) { dst_uuid->time_low = DceRpcNtohl(&pkt_uuid->time_low, byte_order); dst_uuid->time_mid = DceRpcNtohs(&pkt_uuid->time_mid, byte_order); dst_uuid->time_high_and_version = DceRpcNtohs(&pkt_uuid->time_high_and_version, byte_order); dst_uuid->clock_seq_and_reserved = pkt_uuid->clock_seq_and_reserved; dst_uuid->clock_seq_low = pkt_uuid->clock_seq_low; memcpy(dst_uuid->node, pkt_uuid->node, sizeof(dst_uuid->node)); } #endif /* _DCE2_UTILS_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_config.c0000644000175000017500000052201314241075631022051 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Parses and processes configuration set in snort.conf. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_config.h" #include "dce2_utils.h" #include "dce2_list.h" #include "dce2_memory.h" #include "dce2_event.h" #include "dce2_session.h" #include "sf_dynamic_preprocessor.h" #include "sf_types.h" #include "sfrt.h" #include "sf_ip.h" #include #include #include #include #include #ifndef WIN32 #include #include #endif /* WIN32 */ #include #include /******************************************************************** * Global variables ********************************************************************/ tSfPolicyUserContextId dce2_config = NULL; DCE2_Config *dce2_eval_config = NULL; /* Default ports */ static const uint16_t DCE2_PORTS_SMB__DEFAULT[] = {139, 445}; static const uint16_t DCE2_PORTS_TCP__DEFAULT[] = {135}; static const uint16_t DCE2_PORTS_UDP__DEFAULT[] = {135}; //static const uint16_t DCE2_PORTS_HTTP_PROXY__DEFAULT[] = {80}; static const uint16_t DCE2_PORTS_HTTP_SERVER__DEFAULT[] = {593}; static char dce2_config_error[1024]; /******************************************************************** * Macros ********************************************************************/ #define DCE2_GOPT__MEMCAP "memcap" #define DCE2_GOPT__DISABLE_DEFRAG "disable_defrag" #define DCE2_GOPT__MAX_FRAG_LEN "max_frag_len" #define DCE2_GOPT__REASSEMBLE_THRESHOLD "reassemble_threshold" #define DCE2_GOPT__DISABLED "disabled" #define DCE2_GOPT__LEGACY_MODE "smb_legacy_mode" #define DCE2_GOPT__EVENTS "events" #define DCE2_GARG__EVENTS_NONE "none" #define DCE2_GARG__EVENTS_MEMCAP "memcap" #define DCE2_GARG__EVENTS_SMB "smb" #define DCE2_GARG__EVENTS_CO "co" /* Connection-oriented DCE/RPC */ #define DCE2_GARG__EVENTS_CL "cl" /* Connectionless DCE/RPC */ #define DCE2_GARG__EVENTS_ALL "all" #define DCE2_GOPT__SMB_FINGERPRINT "smb_fingerprint_policy" #define DCE2_GARG__SMBFP_CLIENT "client" #define DCE2_GARG__SMBFP_SERVER "server" #define DCE2_GARG__SMBFP_BOTH "both" #define DCE2_GARG__SMBFP_NONE "none" #define DCE2_SOPT__DEFAULT "default" #define DCE2_SOPT__NET "net" #define DCE2_SOPT__POLICY "policy" #define DCE2_SARG__POLICY_WIN2000 "Win2000" #define DCE2_SARG__POLICY_WINXP "WinXP" #define DCE2_SARG__POLICY_WINVISTA "WinVista" #define DCE2_SARG__POLICY_WIN2003 "Win2003" #define DCE2_SARG__POLICY_WIN2008 "Win2008" #define DCE2_SARG__POLICY_WIN7 "Win7" #define DCE2_SARG__POLICY_SAMBA "Samba" #define DCE2_SARG__POLICY_SAMBA_3_0_37 "Samba-3.0.37" /* Samba version 3.0.37 and previous */ #define DCE2_SARG__POLICY_SAMBA_3_0_22 "Samba-3.0.22" /* Samba version 3.0.22 and previous */ #define DCE2_SARG__POLICY_SAMBA_3_0_20 "Samba-3.0.20" /* Samba version 3.0.20 and previous */ #define DCE2_SOPT__DETECT "detect" #define DCE2_SOPT__AUTODETECT "autodetect" #define DCE2_SARG__DETECT_NONE "none" #define DCE2_SARG__DETECT_SMB "smb" #define DCE2_SARG__DETECT_TCP "tcp" #define DCE2_SARG__DETECT_UDP "udp" #define DCE2_SARG__DETECT_HTTP_PROXY "rpc-over-http-proxy" #define DCE2_SARG__DETECT_HTTP_SERVER "rpc-over-http-server" #define DCE2_SOPT__NO_AUTODETECT_HTTP_PROXY_PORTS "no_autodetect_http_proxy_ports" #define DCE2_SOPT__SMB_INVALID_SHARES "smb_invalid_shares" #define DCE2_SOPT__SMB_MAX_CHAIN "smb_max_chain" #define DCE2_SMB_MAX_CHAIN__DEFAULT 3 #define DCE2_SMB_MAX_CHAIN__MAX 255 /* uint8_t is used to store value */ #define DCE2_SOPT__SMB_FILE_INSPECTION "smb_file_inspection" #define DCE2_SARG__SMB_FILE_INSPECTION_ON "on" #define DCE2_SARG__SMB_FILE_INSPECTION_OFF "off" #define DCE2_SARG__SMB_FILE_INSPECTION_ONLY "only" #define DCE2_SARG__SMB_FILE_INSPECTION_DEPTH "file-depth" #define DCE2_SMB_FILE_DEPTH_DEFAULT 16384 #define DCE2_SOPT__VALID_SMB_VERSIONS "valid_smb_versions" #define DCE2_SARG__VALID_SMB_VERSIONS_V1 "v1" #define DCE2_SARG__VALID_SMB_VERSIONS_V2 "v2" #define DCE2_SARG__VALID_SMB_VERSIONS_ALL "all" #define DCE2_SOPT__SMB2_MAX_COMPOUND "smb2_max_compound" #define DCE2_SMB2_MAX_COMPOUND__DEFAULT 3 #define DCE2_SMB2_MAX_COMPOUND__MAX 255 /* uint8_t is used to store value */ /*** Don't increase max memcap number or it will overflow ***/ #define DCE2_MEMCAP__MIN 1024 /* 1 MB min */ #define DCE2_MEMCAP__MAX ((4 * 1024 * 1024) - 1) /* ~ 4 GB max */ #define DCE2_MAX_FRAG__MAX 65535 #define DCE2_MAX_FRAG__MIN 1514 #define DCE2_AUTO_PORTS__START 1025 /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_GcState { DCE2_GC_STATE__OPT_START, DCE2_GC_STATE__OPT, DCE2_GC_STATE__OPT_END, DCE2_GC_STATE__END } DCE2_GcState; typedef enum _DCE2_GcOptFlag { DCE2_GC_OPT_FLAG__NULL = 0x0000, DCE2_GC_OPT_FLAG__MEMCAP = 0x0001, DCE2_GC_OPT_FLAG__DISABLE_SMB_DESEG = 0x0002, DCE2_GC_OPT_FLAG__DISABLE_DEFRAG = 0x0004, DCE2_GC_OPT_FLAG__MAX_FRAG_LEN = 0x0008, DCE2_GC_OPT_FLAG__EVENTS = 0x0010, DCE2_GC_OPT_FLAG__REASSEMBLE_THRESHOLD = 0x0020, DCE2_GC_OPT_FLAG__DISABLED = 0x0040, DCE2_GC_OPT_FLAG__SMB_FINGERPRINT = 0x0080, DCE2_GC_OPT_FLAG__LEGACY_MODE = 0x0100 } DCE2_GcOptFlag; typedef enum _DCE2_ScState { DCE2_SC_STATE__ROPT_START, /* Required option */ DCE2_SC_STATE__ROPT, DCE2_SC_STATE__OPT_START, DCE2_SC_STATE__OPT, DCE2_SC_STATE__OPT_END, DCE2_SC_STATE__END } DCE2_ScState; typedef enum _DCE2_ScOptFlag { DCE2_SC_OPT_FLAG__NULL = 0x0000, DCE2_SC_OPT_FLAG__DEFAULT = 0x0001, DCE2_SC_OPT_FLAG__NET = 0x0002, DCE2_SC_OPT_FLAG__POLICY = 0x0004, DCE2_SC_OPT_FLAG__DETECT = 0x0008, DCE2_SC_OPT_FLAG__AUTODETECT = 0x0010, DCE2_SC_OPT_FLAG__NO_AUTODETECT_HTTP_PROXY_PORTS = 0x0020, DCE2_SC_OPT_FLAG__SMB_INVALID_SHARES = 0x0040, DCE2_SC_OPT_FLAG__SMB_MAX_CHAIN = 0x0080, DCE2_SC_OPT_FLAG__VALID_SMB_VERSIONS = 0x0100, DCE2_SC_OPT_FLAG__SMB2_MAX_COMPOUND = 0x0200, DCE2_SC_OPT_FLAG__SMB_FILE_INSPECTION = 0x0400 } DCE2_ScOptFlag; typedef enum _DCE2_DetectListState { DCE2_DETECT_LIST_STATE__START, DCE2_DETECT_LIST_STATE__TYPE_START, DCE2_DETECT_LIST_STATE__TYPE, DCE2_DETECT_LIST_STATE__TYPE_END, DCE2_DETECT_LIST_STATE__PORTS_START, DCE2_DETECT_LIST_STATE__PORTS, DCE2_DETECT_LIST_STATE__PORTS_END, DCE2_DETECT_LIST_STATE__END } DCE2_DetectListState; typedef enum _DCE2_SmbFileListState { DCE2_SMB_FILE_LIST_STATE__START, DCE2_SMB_FILE_LIST_STATE__ENABLEMENT_START, DCE2_SMB_FILE_LIST_STATE__ENABLEMENT, DCE2_SMB_FILE_LIST_STATE__ENABLEMENT_END, DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_START, DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH, DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE_START, DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE, DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE_END, DCE2_SMB_FILE_LIST_STATE__END } DCE2_SmbFileListState; /******************************************************************** * Structures ********************************************************************/ /* Just used for printing detect and autodetect configurations */ typedef struct _DCE2_PrintPortsStruct { const uint8_t *port_array; const char *trans_str; } DCE2_PrintPortsStruct; /******************************************************************** * Private function prototypes ********************************************************************/ static void DCE2_GcInitConfig(DCE2_GlobalConfig *gc); static DCE2_Ret DCE2_GcParseConfig(DCE2_GlobalConfig *, char *); static inline DCE2_GcOptFlag DCE2_GcParseOption(char *, char *, int *); static DCE2_Ret DCE2_GcParseMemcap(DCE2_GlobalConfig *, char **, char *); static DCE2_Ret DCE2_GcParseMaxFrag(DCE2_GlobalConfig *, char **, char *); static DCE2_Ret DCE2_GcParseEvents(DCE2_GlobalConfig *, char **, char *); static inline void DCE2_GcSetEvent(DCE2_GlobalConfig *, DCE2_EventFlag); static inline void DCE2_GcClearAllEvents(DCE2_GlobalConfig *); static inline DCE2_EventFlag DCE2_GcParseEvent(char *, char *, int *); static DCE2_Ret DCE2_GcParseReassembleThreshold(DCE2_GlobalConfig *, char **, char *); static DCE2_Ret DCE2_GcParseSmbFingerprintPolicy(DCE2_GlobalConfig *, char **, char *); static void DCE2_GcPrintConfig(const DCE2_GlobalConfig *); static void DCE2_GcError(const char *, ...); static DCE2_Ret DCE2_ScInitConfig(DCE2_ServerConfig *); static DCE2_Ret DCE2_ScInitPortArray(DCE2_ServerConfig *, DCE2_DetectFlag, int); static DCE2_Ret DCE2_ScParseConfig(DCE2_Config *, DCE2_ServerConfig *, char *, DCE2_Queue *); static inline DCE2_ScOptFlag DCE2_ScParseOption(char *, char *, int *); static DCE2_Ret DCE2_ScParsePolicy(DCE2_ServerConfig *, char **, char *); static DCE2_Ret DCE2_ScParseDetect(DCE2_ServerConfig *, char **, char *, int); static inline DCE2_DetectFlag DCE2_ScParseDetectType(char *, char *, int *); static inline void DCE2_ScResetPortsArrays(DCE2_ServerConfig *, int); static DCE2_Ret DCE2_ScParseSmbShares(DCE2_ServerConfig *, char **, char *); static DCE2_Ret DCE2_ScParseSmbMaxChain(DCE2_ServerConfig *, char **, char *); static DCE2_Ret DCE2_ScParseSmb2MaxCompound(DCE2_ServerConfig *, char **, char *); static DCE2_Ret DCE2_ScParseValidSmbVersions(DCE2_ServerConfig *, char **, char *); static DCE2_Ret DCE2_ScParseSmbFileInspection(DCE2_ServerConfig *, char **, char *); static inline DCE2_ValidSmbVersionFlag DCE2_ScParseValidSmbVersion(char *, char *, int *); static inline void DCE2_ScSetValidSmbVersion(DCE2_ServerConfig *, DCE2_ValidSmbVersionFlag); static inline void DCE2_ScClearAllValidSmbVersionFlags(DCE2_ServerConfig *); static DCE2_Ret DCE2_ScAddToRoutingTable(DCE2_Config *, DCE2_ServerConfig *, DCE2_Queue *); static int DCE2_ScSmbShareCompare(const void *, const void *); static void DCE2_ScSmbShareFree(void *); static void DCE2_ScPrintConfig(const DCE2_ServerConfig *, DCE2_Queue *); static void DCE2_ScPrintPorts(const DCE2_ServerConfig *, int); static void DCE2_ScIpListDataFree(void *); static int DCE2_ScCheckTransport(void *); static DCE2_Ret DCE2_ScCheckPortOverlap(const DCE2_ServerConfig *); static void DCE2_AddPortsToStreamFilter(struct _SnortConfig *, DCE2_ServerConfig *, tSfPolicyId); static void DCE2_ScError(const char *, ...); static void DCE2_ServerConfigCleanup(void *); void DCE2_RegisterPortsWithSession( struct _SnortConfig *sc, DCE2_ServerConfig *policy ); /******************************************************************** * Function: DCE2_GlobalConfigure() * * Parses the DCE/RPC global configuration and stores values in a * global configuration structure. * * Arguments: * char * * snort.conf argument line for the dcerpc2 preprocessor. * * Returns: None * ********************************************************************/ void DCE2_GlobalConfigure(DCE2_Config *config, char *args) { if (config == NULL) return; dce2_config_error[0] = '\0'; config->gconfig = (DCE2_GlobalConfig *) DCE2_Alloc(sizeof(DCE2_GlobalConfig), DCE2_MEM_TYPE__CONFIG); /* Allocate memory for global config structure */ if (config->gconfig == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for global configuration.", __FILE__, __LINE__); } /* Initialize the global config structure */ DCE2_GcInitConfig(config->gconfig); /* If no arguments, just use default configuration */ if (DCE2_IsEmptyStr(args)) { DCE2_GcPrintConfig(config->gconfig); return; } if (DCE2_GcParseConfig(config->gconfig, args) != DCE2_RET__SUCCESS) DCE2_Die("%s", dce2_config_error); DCE2_GcPrintConfig(config->gconfig); } /******************************************************************** * Function: DCE2_GcInitConfig * * Initializes global configuration to defaults. * * Arguments: * DCE2_GlobalConfig * * Pointer to global config structure. * * Returns: None * ********************************************************************/ static void DCE2_GcInitConfig(DCE2_GlobalConfig *gc) { if (gc == NULL) return; /* Convert default memcap to 100MB */ gc->memcap = DCE2_MEMCAP__DEFAULT * 1024; /* Enable fragmentation reassembly */ gc->dce_defrag = DCE2_CS__ENABLED; /* Set default max fragment size */ gc->max_frag_len = DCE2_SENTINEL; } /******************************************************************** * Function: DCE2_GcParseConfig() * * Main parsing of global configuration. Parses options and * passes off to individual option handling. * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * char * * Pointer to the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if parsing completed without error. * DCE2_RET__ERROR if an error occurred during parsing. * ********************************************************************/ static DCE2_Ret DCE2_GcParseConfig(DCE2_GlobalConfig *gc, char *args) { DCE2_GcState state = DCE2_GC_STATE__OPT_START; char *ptr, *end; char *opt_start = args; char last_char = 0; int option_mask = 0; ptr = args; end = ptr + strlen(args) + 1; /* Include NULL byte for state */ while (ptr < end) { char c = *ptr; switch (state) { case DCE2_GC_STATE__OPT_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { opt_start = ptr; /* Save pointer to first char of option */ state = DCE2_GC_STATE__OPT; } else if (!DCE2_IsSpaceChar(c)) { DCE2_GcError("Invalid syntax: \"%s\"", ptr); return DCE2_RET__ERROR; } break; case DCE2_GC_STATE__OPT: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { DCE2_GcOptFlag opt_flag; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_GcError("Invalid option: \"%.*s\"", ptr - opt_start, opt_start); return DCE2_RET__ERROR; } opt_flag = DCE2_GcParseOption(opt_start, ptr, &option_mask); switch (opt_flag) { case DCE2_GC_OPT_FLAG__MEMCAP: if (DCE2_GcParseMemcap(gc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_GC_OPT_FLAG__DISABLE_DEFRAG: gc->dce_defrag = DCE2_CS__DISABLED; break; case DCE2_GC_OPT_FLAG__MAX_FRAG_LEN: if (DCE2_GcParseMaxFrag(gc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_GC_OPT_FLAG__EVENTS: if (DCE2_GcParseEvents(gc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_GC_OPT_FLAG__REASSEMBLE_THRESHOLD: if (DCE2_GcParseReassembleThreshold(gc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_GC_OPT_FLAG__DISABLED: gc->disabled = 1; break; case DCE2_GC_OPT_FLAG__SMB_FINGERPRINT: if (DCE2_GcParseSmbFingerprintPolicy(gc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_GC_OPT_FLAG__LEGACY_MODE: gc->legacy_mode = true; break; default: return DCE2_RET__ERROR; } state = DCE2_GC_STATE__OPT_END; continue; } break; case DCE2_GC_STATE__OPT_END: if (DCE2_IsConfigEndChar(c)) { return DCE2_RET__SUCCESS; } else if (DCE2_IsOptEndChar(c)) { state = DCE2_GC_STATE__OPT_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_GcError("Invalid syntax: \"%s\"", ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid option state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } last_char = c; ptr++; } return DCE2_RET__ERROR; } /******************************************************************** * Function: DCE2_GcParseOption() * * Parses the option and returns an option flag. Checks to make * sure option is only configured once. * * Arguments: * char * * Pointer to the first character of the option name. * char * * Pointer to the byte after the last character of * the option name. * int * Pointer to the current option mask. Contains bits set * for each option that has already been configured. Mask * is checked and updated for new option. * * Returns: * DCE2_GcOptFlag * Flag for the global option that is being configured. * NULL flag if not a valid option or option has already * been configured. * ********************************************************************/ static inline DCE2_GcOptFlag DCE2_GcParseOption(char *opt_start, char *opt_end, int *opt_mask) { DCE2_GcOptFlag opt_flag = DCE2_GC_OPT_FLAG__NULL; size_t opt_len = opt_end - opt_start; if (opt_len == strlen(DCE2_GOPT__MEMCAP) && strncasecmp(DCE2_GOPT__MEMCAP, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__MEMCAP; } else if (opt_len == strlen(DCE2_GOPT__DISABLE_DEFRAG) && strncasecmp(DCE2_GOPT__DISABLE_DEFRAG, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__DISABLE_DEFRAG; } else if (opt_len == strlen(DCE2_GOPT__MAX_FRAG_LEN) && strncasecmp(DCE2_GOPT__MAX_FRAG_LEN, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__MAX_FRAG_LEN; } else if (opt_len == strlen(DCE2_GOPT__EVENTS) && strncasecmp(DCE2_GOPT__EVENTS, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__EVENTS; } else if (opt_len == strlen(DCE2_GOPT__REASSEMBLE_THRESHOLD) && strncasecmp(DCE2_GOPT__REASSEMBLE_THRESHOLD, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__REASSEMBLE_THRESHOLD; } else if (opt_len == strlen(DCE2_GOPT__DISABLED) && strncasecmp(DCE2_GOPT__DISABLED, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__DISABLED; } else if (opt_len == strlen(DCE2_GOPT__SMB_FINGERPRINT) && strncasecmp(DCE2_GOPT__SMB_FINGERPRINT, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__SMB_FINGERPRINT; } else if (opt_len == strlen(DCE2_GOPT__LEGACY_MODE) && strncasecmp(DCE2_GOPT__LEGACY_MODE, opt_start, opt_len) == 0) { opt_flag = DCE2_GC_OPT_FLAG__LEGACY_MODE; } else { DCE2_GcError("Invalid option: \"%.*s\"", opt_len, opt_start); return DCE2_GC_OPT_FLAG__NULL; } if (DCE2_CheckAndSetMask(opt_flag, opt_mask) != DCE2_RET__SUCCESS) { DCE2_GcError("Can only configure option once: \"%.*s\"", opt_len, opt_start); return DCE2_GC_OPT_FLAG__NULL; } return opt_flag; } /******************************************************************** * Function: DCE2_GcParseMemcap() * * Parses the argument to the memcap option and adds to global * configuration if successfully parsed. * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing memcap. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * value for the memcap. * DCE2_RET__ERROR if an error occured in parsing the memcap. * ********************************************************************/ static DCE2_Ret DCE2_GcParseMemcap(DCE2_GlobalConfig *gc, char **ptr, char *end) { uint32_t memcap; if (DCE2_ParseValue(ptr, end, &memcap, DCE2_INT_TYPE__UINT32) != DCE2_RET__SUCCESS) { DCE2_GcError("Error parsing \"%s\". Value must be between %d and %d KB.", DCE2_GOPT__MEMCAP, DCE2_MEMCAP__MIN, DCE2_MEMCAP__MAX); return DCE2_RET__ERROR; } if ((memcap < DCE2_MEMCAP__MIN) || (memcap > DCE2_MEMCAP__MAX)) { DCE2_GcError("Invalid \"%s\". Value must be between %d and %d KB", DCE2_GOPT__MEMCAP, DCE2_MEMCAP__MIN, DCE2_MEMCAP__MAX); return DCE2_RET__ERROR; } /* Memcap is configured as kilobytes - convert to bytes */ gc->memcap = memcap * 1024; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_GcParseMaxFrag() * * Parses the argument to the max frag option and adds to global * configuration if successfully parsed. * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing max frag. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * value for the max frag option. * DCE2_RET__ERROR if an error occured in parsing the max frag * option. * ********************************************************************/ static DCE2_Ret DCE2_GcParseMaxFrag(DCE2_GlobalConfig *gc, char **ptr, char *end) { uint16_t max_frag_len; if (DCE2_ParseValue(ptr, end, &max_frag_len, DCE2_INT_TYPE__UINT16) != DCE2_RET__SUCCESS) { DCE2_GcError("Error parsing \"%s\". Value must be between %d and %d", DCE2_GOPT__MAX_FRAG_LEN, DCE2_MAX_FRAG__MIN, DCE2_MAX_FRAG__MAX); return DCE2_RET__ERROR; } if (max_frag_len < DCE2_MAX_FRAG__MIN) { DCE2_GcError("Invalid \"%s\". Value must be between %d and %d", DCE2_GOPT__MAX_FRAG_LEN, DCE2_MAX_FRAG__MIN, DCE2_MAX_FRAG__MAX); return DCE2_RET__ERROR; } /* Cast since we initialize max frag len in global structure to a sentinel */ gc->max_frag_len = (int)max_frag_len; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_GcParseEvents() * * Parses the event types for the events option and adds to * global configuration. * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing memcap. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * events. * DCE2_RET__ERROR if an error occured in parsing the events. * ********************************************************************/ static DCE2_Ret DCE2_GcParseEvents(DCE2_GlobalConfig *gc, char **ptr, char *end) { DCE2_WordListState state = DCE2_WORD_LIST_STATE__START; char *event_start = *ptr; char last_char = 0; int one_event = 0; int event_mask = 0; DCE2_GcClearAllEvents(gc); while (*ptr < end) { char c = **ptr; if (state == DCE2_WORD_LIST_STATE__END) break; switch (state) { case DCE2_WORD_LIST_STATE__START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { /* Only one event */ event_start = *ptr; one_event = 1; state = DCE2_WORD_LIST_STATE__WORD; } else if (DCE2_IsListStartChar(c)) { state = DCE2_WORD_LIST_STATE__WORD_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_GcError("Invalid \"%s\" syntax: \"%s\"", DCE2_GOPT__EVENTS, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { event_start = *ptr; state = DCE2_WORD_LIST_STATE__WORD; } else if (!DCE2_IsSpaceChar(c)) { DCE2_GcError("Invalid \"%s\" syntax: \"%s\"", DCE2_GOPT__EVENTS, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { DCE2_EventFlag eflag; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_GcError("Invalid \"%s\" argument: \"%.*s\"", DCE2_GOPT__EVENTS, *ptr - event_start, event_start); return DCE2_RET__ERROR; } eflag = DCE2_GcParseEvent(event_start, *ptr, &event_mask); switch (eflag) { case DCE2_EVENT_FLAG__NULL: return DCE2_RET__ERROR; case DCE2_EVENT_FLAG__NONE: if (!one_event) { DCE2_GcError("Event type \"%s\" cannot be " "configured in a list", DCE2_GARG__EVENTS_NONE); return DCE2_RET__ERROR; } /* Not really necessary since we're returning early, * but leave it here since this would be the action */ DCE2_GcClearAllEvents(gc); break; case DCE2_EVENT_FLAG__ALL: if (!one_event) { DCE2_GcError("Event type \"%s\" cannot be " "configured in a list", DCE2_GARG__EVENTS_ALL); return DCE2_RET__ERROR; } DCE2_GcSetEvent(gc, eflag); break; default: DCE2_GcSetEvent(gc, eflag); break; } if (one_event) return DCE2_RET__SUCCESS; state = DCE2_WORD_LIST_STATE__WORD_END; continue; } break; case DCE2_WORD_LIST_STATE__WORD_END: if (DCE2_IsListEndChar(c)) { state = DCE2_WORD_LIST_STATE__END; } else if (DCE2_IsListSepChar(c)) { state = DCE2_WORD_LIST_STATE__WORD_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_GcError("Invalid \"%s\" syntax: \"%s\"", DCE2_GOPT__EVENTS, *ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid events state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } last_char = c; (*ptr)++; } if (state != DCE2_WORD_LIST_STATE__END) { DCE2_GcError("Invalid \"%s\" syntax: \"%s\"", DCE2_GOPT__EVENTS, *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_GcParseEvent() * * Parses event type and returns flag indication the type of event. * Checks and sets a bit in a mask to prevent multiple * configurations of the same event type. * * Arguments: * char * * Pointer to the first character of the event type name. * char * * Pointer to the byte after the last character of * the event type name. * int * Pointer to the current event type mask. Contains bits set * for each event type that has already been configured. Mask * is checked and updated for new event type. * * Returns: * DCE2_EventFlag * Flag indicating the type of event. * DCE2_EVENT_FLAG__NULL if no event type or multiple * configuration of event type. * ********************************************************************/ static inline DCE2_EventFlag DCE2_GcParseEvent(char *start, char *end, int *emask) { DCE2_EventFlag eflag = DCE2_EVENT_FLAG__NULL; size_t event_len = end - start; if (event_len == strlen(DCE2_GARG__EVENTS_NONE) && strncasecmp(DCE2_GARG__EVENTS_NONE, start, event_len) == 0) { eflag = DCE2_EVENT_FLAG__NONE; } else if (event_len == strlen(DCE2_GARG__EVENTS_MEMCAP) && strncasecmp(DCE2_GARG__EVENTS_MEMCAP, start, event_len) == 0) { eflag = DCE2_EVENT_FLAG__MEMCAP; } else if (event_len == strlen(DCE2_GARG__EVENTS_SMB) && strncasecmp(DCE2_GARG__EVENTS_SMB, start, event_len) == 0) { eflag = DCE2_EVENT_FLAG__SMB; } else if (event_len == strlen(DCE2_GARG__EVENTS_CO) && strncasecmp(DCE2_GARG__EVENTS_CO, start, event_len) == 0) { eflag = DCE2_EVENT_FLAG__CO; } else if (event_len == strlen(DCE2_GARG__EVENTS_CL) && strncasecmp(DCE2_GARG__EVENTS_CL, start, event_len) == 0) { eflag = DCE2_EVENT_FLAG__CL; } else if (event_len == strlen(DCE2_GARG__EVENTS_ALL) && strncasecmp(DCE2_GARG__EVENTS_ALL, start, event_len) == 0) { eflag = DCE2_EVENT_FLAG__ALL; } else { DCE2_GcError("Invalid \"%s\" argument: \"%.*s\"", DCE2_GOPT__EVENTS, event_len, start); return DCE2_EVENT_FLAG__NULL; } if (DCE2_CheckAndSetMask((int)eflag, emask) != DCE2_RET__SUCCESS) { DCE2_GcError("Event type \"%.*s\" cannot be specified more than once", event_len, start); return DCE2_EVENT_FLAG__NULL; } return eflag; } /********************************************************************* * Function: DCE2_GcSetEvent() * * Sets the event types the user wants to see during processing in * the global configuration event mask. * * Arguments: * DCE2_GlobalConfig * * Pointer to global config structure. * DCE2_EventFlag * The event type flag to set. * * Returns: None * *********************************************************************/ static inline void DCE2_GcSetEvent(DCE2_GlobalConfig *gc, DCE2_EventFlag eflag) { gc->event_mask |= eflag; } /********************************************************************* * Function: DCE2_GcClearAllEvents() * * Clears all of the bits in the global configuration event mask. * * Arguments: * DCE2_GlobalConfig * * Pointer to global config structure. * * Returns: None * *********************************************************************/ static inline void DCE2_GcClearAllEvents(DCE2_GlobalConfig *gc) { gc->event_mask = DCE2_EVENT_FLAG__NULL; } /******************************************************************** * Function: DCE2_GcParseReassembleThreshold() * * Parses the argument to the reassemble threshold option and adds * to global configuration if successfully parsed. * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the reassemble threshold. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * value for the reassemble threshold. * DCE2_RET__ERROR if an error occured in parsing the * reassemble threshold. * ********************************************************************/ static DCE2_Ret DCE2_GcParseReassembleThreshold(DCE2_GlobalConfig *gc, char **ptr, char *end) { uint16_t reassemble_threshold; if (DCE2_ParseValue(ptr, end, &reassemble_threshold, DCE2_INT_TYPE__UINT16) != DCE2_RET__SUCCESS) { DCE2_GcError("Error parsing \"%s\". Value must be between 0 and %u inclusive", DCE2_GOPT__REASSEMBLE_THRESHOLD, UINT16_MAX); return DCE2_RET__ERROR; } gc->reassemble_threshold = reassemble_threshold; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_GcParseSmbFingerPrintPolicy() * * Parses the smb_fingerprint_policy option * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the reassemble threshold. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse. * DCE2_RET__ERROR if an error occured in parsing. * ********************************************************************/ static DCE2_Ret DCE2_GcParseSmbFingerprintPolicy(DCE2_GlobalConfig *gc, char **ptr, char *end) { DCE2_WordListState state = DCE2_WORD_LIST_STATE__START; char *fp_start = *ptr; char last_char = 0; while (*ptr < end) { char c = **ptr; if (state == DCE2_WORD_LIST_STATE__END) break; switch (state) { case DCE2_WORD_LIST_STATE__START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { fp_start = *ptr; state = DCE2_WORD_LIST_STATE__WORD; } else if (!DCE2_IsSpaceChar(c)) { DCE2_GcError("Invalid \"%s\" syntax: \"%s\"", DCE2_GOPT__SMB_FINGERPRINT, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { size_t len = *ptr - fp_start; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_GcError("Invalid \"%s\" argument: \"%*.s\"", DCE2_GOPT__SMB_FINGERPRINT, *ptr - fp_start, fp_start); return DCE2_RET__ERROR; } if (len == strlen(DCE2_GARG__SMBFP_CLIENT) && strncasecmp(DCE2_GARG__SMBFP_CLIENT, fp_start, len) == 0) { gc->smb_fingerprint_policy = DCE2_SMB_FINGERPRINT__CLIENT; } else if (len == strlen(DCE2_GARG__SMBFP_SERVER) && strncasecmp(DCE2_GARG__SMBFP_SERVER, fp_start, len) == 0) { gc->smb_fingerprint_policy = DCE2_SMB_FINGERPRINT__SERVER; } else if (len == strlen(DCE2_GARG__SMBFP_BOTH) && strncasecmp(DCE2_GARG__SMBFP_BOTH, fp_start, len) == 0) { gc->smb_fingerprint_policy = DCE2_SMB_FINGERPRINT__SERVER; gc->smb_fingerprint_policy |= DCE2_SMB_FINGERPRINT__CLIENT; } else if (len == strlen(DCE2_GARG__SMBFP_NONE) && strncasecmp(DCE2_GARG__SMBFP_NONE, fp_start, len) == 0) { gc->smb_fingerprint_policy = DCE2_SMB_FINGERPRINT__NONE; } else { DCE2_GcError("Invalid \"%s\" argument: \"%.*s\"", DCE2_GOPT__SMB_FINGERPRINT, *ptr - fp_start, fp_start); return DCE2_RET__ERROR; } state = DCE2_WORD_LIST_STATE__END; continue; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid smb fingerprint state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } last_char = c; (*ptr)++; } if (state != DCE2_WORD_LIST_STATE__END) { DCE2_GcError("Invalid \"%s\" syntax: \"%s\"", DCE2_GOPT__SMB_FINGERPRINT, *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScInitConfig * * Initializes a server configuration to defaults. * * Arguments: * DCE2_ServerConfig * * Pointer to server configuration structure to initialize. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if initialization fails * DCE2_RET__SUCCESS if initialization succeeds * ********************************************************************/ static DCE2_Ret DCE2_ScInitConfig(DCE2_ServerConfig *sc) { if (sc == NULL) return DCE2_RET__ERROR; /* Set defaults */ sc->policy = DCE2_POLICY__WINXP; sc->smb_max_chain = DCE2_SMB_MAX_CHAIN__DEFAULT; sc->smb2_max_compound = DCE2_SMB2_MAX_COMPOUND__DEFAULT; sc->valid_smb_versions_mask = DCE2_VALID_SMB_VERSION_FLAG__ALL; sc->autodetect_http_proxy_ports = DCE2_CS__ENABLED; sc->smb_file_inspection = DCE2_SMB_FILE_INSPECTION__OFF; sc->smb_file_depth = DCE2_SMB_FILE_DEPTH_DEFAULT; /* Add default detect ports */ if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__SMB, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__TCP, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__UDP, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__HTTP_PROXY, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__HTTP_SERVER, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; /* Add default autodetect ports */ if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__SMB, 1) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__TCP, 1) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__UDP, 1) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__HTTP_PROXY, 1) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_ScInitPortArray(sc, DCE2_DETECT_FLAG__HTTP_SERVER, 1) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScInitPortArray * * Initializes a detect or autodetect port array to default * values. * * Arguments: * DCE2_ServerConfig * * Pointer to server configuration structure to initialize. * DCE2_DetectFlag * The transport for which to set defaults * int * Non-zero to set autodetect ports * Zero to set detect ports * * Returns: * DCE2_Ret * DCE2_RET__ERROR if and invalid DetectFlag is passed in * DCE2_RET__SUCCESS if ports arrays are successfully * initialized * ********************************************************************/ static DCE2_Ret DCE2_ScInitPortArray(DCE2_ServerConfig *sc, DCE2_DetectFlag dflag, int autodetect) { if (!autodetect) { unsigned int array_len; unsigned int i; switch (dflag) { case DCE2_DETECT_FLAG__SMB: DCE2_ClearPorts(sc->smb_ports); array_len = sizeof(DCE2_PORTS_SMB__DEFAULT) / sizeof(DCE2_PORTS_SMB__DEFAULT[0]); for (i = 0; i < array_len; i++) DCE2_SetPort(sc->smb_ports, DCE2_PORTS_SMB__DEFAULT[i]); break; case DCE2_DETECT_FLAG__TCP: DCE2_ClearPorts(sc->tcp_ports); array_len = sizeof(DCE2_PORTS_TCP__DEFAULT) / sizeof(DCE2_PORTS_TCP__DEFAULT[0]); for (i = 0; i < array_len; i++) DCE2_SetPort(sc->tcp_ports, DCE2_PORTS_TCP__DEFAULT[i]); break; case DCE2_DETECT_FLAG__UDP: DCE2_ClearPorts(sc->udp_ports); array_len = sizeof(DCE2_PORTS_UDP__DEFAULT) / sizeof(DCE2_PORTS_UDP__DEFAULT[0]); for (i = 0; i < array_len; i++) DCE2_SetPort(sc->udp_ports, DCE2_PORTS_UDP__DEFAULT[i]); break; case DCE2_DETECT_FLAG__HTTP_PROXY: DCE2_ClearPorts(sc->http_proxy_ports); /* Since there currently aren't any VRT dcerpc rules using * port 80, don't include it by default */ #if 0 array_len = sizeof(DCE2_PORTS_HTTP_PROXY__DEFAULT) / sizeof(DCE2_PORTS_HTTP_PROXY__DEFAULT[0]); for (i = 0; i < array_len; i++) DCE2_SetPort(sc->http_proxy_ports, DCE2_PORTS_HTTP_PROXY__DEFAULT[i]); #endif break; case DCE2_DETECT_FLAG__HTTP_SERVER: DCE2_ClearPorts(sc->http_server_ports); array_len = sizeof(DCE2_PORTS_HTTP_SERVER__DEFAULT) / sizeof(DCE2_PORTS_HTTP_SERVER__DEFAULT[0]); for (i = 0; i < array_len; i++) DCE2_SetPort(sc->http_server_ports, DCE2_PORTS_HTTP_SERVER__DEFAULT[i]); break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, dflag); return DCE2_RET__ERROR; } } else { uint8_t *port_array = NULL; unsigned int port; switch (dflag) { case DCE2_DETECT_FLAG__SMB: DCE2_ClearPorts(sc->auto_smb_ports); return DCE2_RET__SUCCESS; case DCE2_DETECT_FLAG__TCP: DCE2_ClearPorts(sc->auto_tcp_ports); port_array = sc->auto_tcp_ports; break; case DCE2_DETECT_FLAG__UDP: DCE2_ClearPorts(sc->auto_udp_ports); port_array = sc->auto_udp_ports; break; case DCE2_DETECT_FLAG__HTTP_PROXY: DCE2_ClearPorts(sc->auto_http_proxy_ports); return DCE2_RET__SUCCESS; case DCE2_DETECT_FLAG__HTTP_SERVER: DCE2_ClearPorts(sc->auto_http_server_ports); port_array = sc->auto_http_server_ports; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, dflag); return DCE2_RET__ERROR; } /* By default, only autodetect on ports 1025 and above, * and not on SMB or RPC over HTTP proxy */ for (port = DCE2_AUTO_PORTS__START; port < DCE2_PORTS__MAX; port++) DCE2_SetPort(port_array, (uint16_t)port); } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_CreateDefaultServerConfig() * * Creates a default server configuration for non-matching specific * server configurations. * * Arguments: None * * Returns: -1 on error * ********************************************************************/ int DCE2_CreateDefaultServerConfig(struct _SnortConfig *sc, DCE2_Config *config, tSfPolicyId policy_id) { if (config == NULL) return 0; config->dconfig = (DCE2_ServerConfig *) DCE2_Alloc(sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); if (config->dconfig == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for default server " "configuration.", __FILE__, __LINE__); } if (DCE2_ScInitConfig(config->dconfig) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__WARN, "%s(%d) Failed to initialize default server " "configuration.", __FILE__, __LINE__); return -1; } DCE2_AddPortsToStreamFilter(sc, config->dconfig, policy_id); return 0; } /******************************************************************** * Function: DCE2_ServerConfigure() * * Parses the DCE/RPC server configuration and stores values in * server configuration. * * Arguments: * char * * snort.conf argument line for the dcerpc2 preprocessor. * * Returns: None * ********************************************************************/ void DCE2_ServerConfigure(struct _SnortConfig *snortConf, DCE2_Config *config, char *args) { DCE2_ServerConfig *sc; DCE2_Queue *ip_queue; DCE2_Ret status; tSfPolicyId policy_id = _dpd.getParserPolicy(snortConf); if (config == NULL) return; dce2_config_error[0] = '\0'; /* Must have arguments */ if (DCE2_IsEmptyStr(args)) { DCE2_Die("%s(%d) \"%s\" configuration: No arguments to server " "configuration. Must have a \"%s\" or \"%s\" argument.", *_dpd.config_file, *_dpd.config_line, DCE2_SNAME, DCE2_SOPT__DEFAULT, DCE2_SOPT__NET); } /* Alloc server config */ sc = (DCE2_ServerConfig *)DCE2_Alloc(sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); if (sc == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for server " "configuration.", __FILE__, __LINE__); } if (DCE2_ScInitConfig(sc) != DCE2_RET__SUCCESS) { DCE2_ListDestroy(sc->smb_invalid_shares); DCE2_Free((void *)sc, sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); DCE2_Die("%s(%d) \"%s\" configuration: Failed to initialize server configuration.", __FILE__, __LINE__, DCE2_SNAME); } /* The ip queue stores the IPs from a specific server configuration * for adding to the routing tables */ ip_queue = DCE2_QueueNew(DCE2_ScIpListDataFree, DCE2_MEM_TYPE__CONFIG); if (ip_queue == NULL) { DCE2_ListDestroy(sc->smb_invalid_shares); DCE2_Free((void *)sc, sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); DCE2_Die("%s(%d) Failed to allocate memory for IP queue.", __FILE__, __LINE__); } status = DCE2_ScParseConfig(config, sc, args, ip_queue); if (status != DCE2_RET__SUCCESS) { if (sc != config->dconfig) { DCE2_ListDestroy(sc->smb_invalid_shares); DCE2_Free((void *)sc, sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); } DCE2_QueueDestroy(ip_queue); DCE2_Die("%s", dce2_config_error); } /* Check for overlapping detect ports */ if (DCE2_ScCheckPortOverlap(sc) != DCE2_RET__SUCCESS) { if (sc != config->dconfig) { DCE2_ListDestroy(sc->smb_invalid_shares); DCE2_Free((void *)sc, sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); } DCE2_QueueDestroy(ip_queue); DCE2_Die("%s", dce2_config_error); } DCE2_AddPortsToStreamFilter(snortConf, sc, policy_id); DCE2_RegisterPortsWithSession(snortConf, sc); if ((sc != config->dconfig) && (DCE2_ScAddToRoutingTable(config, sc, ip_queue) != DCE2_RET__SUCCESS)) { DCE2_ListDestroy(sc->smb_invalid_shares); DCE2_Free((void *)sc, sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); DCE2_QueueDestroy(ip_queue); DCE2_Die("%s", dce2_config_error); } DCE2_ScPrintConfig(sc, ip_queue); DCE2_QueueDestroy(ip_queue); } /******************************************************************** * Function: DCE2_ScParseConfig() * * Main parsing of a server configuration. Parses options and * passes off to individual option handling. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * char * * Pointer to the configuration line. * DCE2_Queue * * Pointer to a queue for storing IPs. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if parsing completed without error. * DCE2_RET__ERROR if an error occurred during parsing. * ********************************************************************/ static DCE2_Ret DCE2_ScParseConfig(DCE2_Config *config, DCE2_ServerConfig *sc, char *args, DCE2_Queue *ip_queue) { DCE2_ScState state = DCE2_SC_STATE__ROPT_START; char *ptr, *end; char *opt_start = args; char last_char = 0; int option_mask = 0; ptr = args; end = ptr + strlen(args) + 1; /* Include NULL byte for state */ while (ptr < end) { char c = *ptr; switch (state) { case DCE2_SC_STATE__ROPT_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { opt_start = ptr; state = DCE2_SC_STATE__ROPT; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid syntax: \"%s\"", ptr); return DCE2_RET__ERROR; } break; case DCE2_SC_STATE__ROPT: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { DCE2_ScOptFlag opt_flag; DCE2_Ret status; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid option: \"%.*s\"", ptr - opt_start, opt_start); return DCE2_RET__ERROR; } opt_flag = DCE2_ScParseOption(opt_start, ptr, &option_mask); switch (opt_flag) { case DCE2_SC_OPT_FLAG__DEFAULT: if (config->dconfig != NULL) { DCE2_ScError("Can only configure \"%s\" " "configuration once", DCE2_SOPT__DEFAULT); return DCE2_RET__ERROR; } config->dconfig = sc; break; case DCE2_SC_OPT_FLAG__NET: if (config->dconfig == NULL) { DCE2_ScError("Must configure \"%s\" before any " "\"%s\" configurations", DCE2_SOPT__DEFAULT, DCE2_SOPT__NET); return DCE2_RET__ERROR; } status = DCE2_ParseIpList(&ptr, end, ip_queue); if (status != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; default: DCE2_ScError("Invalid first option to server configuration. " "First option must be \"%s\" or \"%s\"", DCE2_SOPT__DEFAULT, DCE2_SOPT__NET); return DCE2_RET__ERROR; } state = DCE2_SC_STATE__OPT_END; continue; } break; case DCE2_SC_STATE__OPT_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { opt_start = ptr; state = DCE2_SC_STATE__OPT; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid syntax: \"%s\"", ptr); return DCE2_RET__ERROR; } break; case DCE2_SC_STATE__OPT: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { DCE2_ScOptFlag opt_flag; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid option: \"%.*s\"", ptr - opt_start, opt_start); return DCE2_RET__ERROR; } opt_flag = DCE2_ScParseOption(opt_start, ptr, &option_mask); switch (opt_flag) { case DCE2_SC_OPT_FLAG__POLICY: if (DCE2_ScParsePolicy(sc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_SC_OPT_FLAG__DETECT: if (DCE2_ScParseDetect(sc, &ptr, end, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_SC_OPT_FLAG__AUTODETECT: if (DCE2_ScParseDetect(sc, &ptr, end, 1) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_SC_OPT_FLAG__NO_AUTODETECT_HTTP_PROXY_PORTS: sc->autodetect_http_proxy_ports = DCE2_CS__DISABLED; break; case DCE2_SC_OPT_FLAG__SMB_INVALID_SHARES: sc->smb_invalid_shares = DCE2_ListNew(DCE2_LIST_TYPE__NORMAL, DCE2_ScSmbShareCompare, DCE2_ScSmbShareFree, DCE2_ScSmbShareFree, DCE2_LIST_FLAG__NO_DUPS | DCE2_LIST_FLAG__INS_TAIL, DCE2_MEM_TYPE__CONFIG); if (sc->smb_invalid_shares == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to allocate memory " "for invalid share list.", __FILE__, __LINE__); return DCE2_RET__ERROR; } if (DCE2_ScParseSmbShares(sc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_SC_OPT_FLAG__SMB_MAX_CHAIN: if (DCE2_ScParseSmbMaxChain(sc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_SC_OPT_FLAG__SMB2_MAX_COMPOUND: if (DCE2_ScParseSmb2MaxCompound(sc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_SC_OPT_FLAG__VALID_SMB_VERSIONS: if (DCE2_ScParseValidSmbVersions(sc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case DCE2_SC_OPT_FLAG__SMB_FILE_INSPECTION: if (DCE2_ScParseSmbFileInspection(sc, &ptr, end) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; #ifdef ACTIVE_RESPONSE if ((sc->smb_file_inspection == DCE2_SMB_FILE_INSPECTION__ONLY) || (sc->smb_file_inspection == DCE2_SMB_FILE_INSPECTION__ON)) _dpd.activeSetEnabled(1); #endif break; case DCE2_SC_OPT_FLAG__DEFAULT: case DCE2_SC_OPT_FLAG__NET: DCE2_ScError("\"%s\" or \"%s\" must be the first " "option to configuration", DCE2_SOPT__DEFAULT, DCE2_SOPT__NET); return DCE2_RET__ERROR; default: return DCE2_RET__ERROR; } state = DCE2_SC_STATE__OPT_END; continue; } break; case DCE2_SC_STATE__OPT_END: if (DCE2_IsConfigEndChar(c)) { return DCE2_RET__SUCCESS; } else if (DCE2_IsOptEndChar(c)) { state = DCE2_SC_STATE__OPT_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid syntax: \"%s\"", ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid option state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } last_char = c; ptr++; } return DCE2_RET__ERROR; } /******************************************************************** * Function: DCE2_ScParseOption() * * Parses the option and returns an option flag. Checks to make * sure option is only configured once. * * Arguments: * char * * Pointer to the first character of the option name. * char * * Pointer to the byte after the last character of * the option name. * int * Pointer to the current option mask. Contains bits set * for each option that has already been configured. Mask * is checked and updated for new option. * * Returns: * DCE2_ScOptFlag * Flag for the server option that is being configured. * NULL flag if not a valid option or option has already * been configured. * ********************************************************************/ static inline DCE2_ScOptFlag DCE2_ScParseOption(char *opt_start, char *opt_end, int *opt_mask) { DCE2_ScOptFlag opt_flag = DCE2_SC_OPT_FLAG__NULL; size_t opt_len = opt_end - opt_start; if (opt_len == strlen(DCE2_SOPT__DEFAULT) && strncasecmp(DCE2_SOPT__DEFAULT, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__DEFAULT; } else if (opt_len == strlen(DCE2_SOPT__NET) && strncasecmp(DCE2_SOPT__NET, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__NET; } else if (opt_len == strlen(DCE2_SOPT__POLICY) && strncasecmp(DCE2_SOPT__POLICY, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__POLICY; } else if (opt_len == strlen(DCE2_SOPT__DETECT) && strncasecmp(DCE2_SOPT__DETECT, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__DETECT; } else if (opt_len == strlen(DCE2_SOPT__AUTODETECT) && strncasecmp(DCE2_SOPT__AUTODETECT, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__AUTODETECT; } else if (opt_len == strlen(DCE2_SOPT__NO_AUTODETECT_HTTP_PROXY_PORTS) && strncasecmp(DCE2_SOPT__NO_AUTODETECT_HTTP_PROXY_PORTS, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__NO_AUTODETECT_HTTP_PROXY_PORTS; } else if (opt_len == strlen(DCE2_SOPT__SMB_INVALID_SHARES) && strncasecmp(DCE2_SOPT__SMB_INVALID_SHARES, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__SMB_INVALID_SHARES; } else if (opt_len == strlen(DCE2_SOPT__SMB_MAX_CHAIN) && strncasecmp(DCE2_SOPT__SMB_MAX_CHAIN, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__SMB_MAX_CHAIN; } else if (opt_len == strlen(DCE2_SOPT__SMB2_MAX_COMPOUND) && strncasecmp(DCE2_SOPT__SMB2_MAX_COMPOUND, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__SMB2_MAX_COMPOUND; } else if (opt_len == strlen(DCE2_SOPT__SMB_FILE_INSPECTION) && strncasecmp(DCE2_SOPT__SMB_FILE_INSPECTION, opt_start, opt_len) == 0) { opt_flag = DCE2_SC_OPT_FLAG__SMB_FILE_INSPECTION; } else { DCE2_ScError("Invalid option: \"%.*s\"", opt_len, opt_start); return DCE2_SC_OPT_FLAG__NULL; } if (DCE2_CheckAndSetMask(opt_flag, opt_mask) != DCE2_RET__SUCCESS) { DCE2_ScError("Can only configure option once: \"%.*s\"", opt_len, opt_start); return DCE2_SC_OPT_FLAG__NULL; } return opt_flag; } /******************************************************************** * Function: DCE2_ScParsePolicy() * * Parses the argument given to the policy option. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the policy argument. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * value for the policy. * DCE2_RET__ERROR if an error occured in parsing the policy. * ********************************************************************/ static DCE2_Ret DCE2_ScParsePolicy(DCE2_ServerConfig *sc, char **ptr, char *end) { DCE2_WordListState state = DCE2_WORD_LIST_STATE__START; char *policy_start = *ptr; char last_char = 0; while (*ptr < end) { char c = **ptr; if (state == DCE2_WORD_LIST_STATE__END) break; switch (state) { case DCE2_WORD_LIST_STATE__START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { policy_start = *ptr; state = DCE2_WORD_LIST_STATE__WORD; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__POLICY, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { size_t policy_len = *ptr - policy_start; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", DCE2_SOPT__POLICY, *ptr - policy_start, policy_start); return DCE2_RET__ERROR; } if (policy_len == strlen(DCE2_SARG__POLICY_WIN2000) && strncasecmp(DCE2_SARG__POLICY_WIN2000, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__WIN2000; } else if (policy_len == strlen(DCE2_SARG__POLICY_WINXP) && strncasecmp(DCE2_SARG__POLICY_WINXP, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__WINXP; } else if (policy_len == strlen(DCE2_SARG__POLICY_WINVISTA) && strncasecmp(DCE2_SARG__POLICY_WINVISTA, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__WINVISTA; } else if (policy_len == strlen(DCE2_SARG__POLICY_WIN2003) && strncasecmp(DCE2_SARG__POLICY_WIN2003, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__WIN2003; } else if (policy_len == strlen(DCE2_SARG__POLICY_WIN2008) && strncasecmp(DCE2_SARG__POLICY_WIN2008, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__WIN2008; } else if (policy_len == strlen(DCE2_SARG__POLICY_WIN7) && strncasecmp(DCE2_SARG__POLICY_WIN7, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__WIN7; } else if (policy_len == strlen(DCE2_SARG__POLICY_SAMBA) && strncasecmp(DCE2_SARG__POLICY_SAMBA, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__SAMBA; } else if (policy_len == strlen(DCE2_SARG__POLICY_SAMBA_3_0_37) && strncasecmp(DCE2_SARG__POLICY_SAMBA_3_0_37, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__SAMBA_3_0_37; } else if (policy_len == strlen(DCE2_SARG__POLICY_SAMBA_3_0_22) && strncasecmp(DCE2_SARG__POLICY_SAMBA_3_0_22, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__SAMBA_3_0_22; } else if (policy_len == strlen(DCE2_SARG__POLICY_SAMBA_3_0_20) && strncasecmp(DCE2_SARG__POLICY_SAMBA_3_0_20, policy_start, policy_len) == 0) { sc->policy = DCE2_POLICY__SAMBA_3_0_20; } else { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", DCE2_SOPT__POLICY, policy_len, policy_start); return DCE2_RET__ERROR; } state = DCE2_WORD_LIST_STATE__END; continue; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } last_char = c; (*ptr)++; } if (state != DCE2_WORD_LIST_STATE__END) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__POLICY, *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScParseDetect() * * Parses the arguments to the detect or autodetect options. These * options take the same arguments. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the policy argument. * char * * Pointer to the end of the configuration line. * int * Non-zero if we're configuring for the autodetect option. * Zero if we're configuring for the detect option. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * arguments for the detect or autodetect options. * DCE2_RET__ERROR if an error occured in parsing the detect * or autodetect arguments. * ********************************************************************/ static DCE2_Ret DCE2_ScParseDetect(DCE2_ServerConfig *sc, char **ptr, char *end, int autodetect) { DCE2_DetectListState state = DCE2_DETECT_LIST_STATE__START; char *type_start = *ptr; uint8_t *port_array = NULL; int dmask = DCE2_DETECT_FLAG__NULL; int one_type = 0; char last_char = 0; DCE2_DetectFlag dflag = DCE2_DETECT_FLAG__NULL; char *option_str = autodetect ? DCE2_SOPT__AUTODETECT : DCE2_SOPT__DETECT; DCE2_ScResetPortsArrays(sc, autodetect); while (*ptr < end) { char c = **ptr; if (state == DCE2_DETECT_LIST_STATE__END) break; switch (state) { case DCE2_DETECT_LIST_STATE__START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { type_start = *ptr; one_type = 1; state = DCE2_DETECT_LIST_STATE__TYPE; } else if (DCE2_IsListStartChar(c)) { state = DCE2_DETECT_LIST_STATE__TYPE_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", option_str, *ptr); return DCE2_RET__ERROR; } break; case DCE2_DETECT_LIST_STATE__TYPE_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { type_start = *ptr; state = DCE2_DETECT_LIST_STATE__TYPE; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", option_str, *ptr); return DCE2_RET__ERROR; } break; case DCE2_DETECT_LIST_STATE__TYPE: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", option_str, *ptr - type_start, type_start); return DCE2_RET__ERROR; } dflag = DCE2_ScParseDetectType(type_start, *ptr, &dmask); switch (dflag) { case DCE2_DETECT_FLAG__NONE: /* This can only be used by itself */ if (dmask != DCE2_DETECT_FLAG__NONE) { DCE2_ScError("Can only use \"%s\" type \"%s\" " "by itself", option_str, DCE2_SARG__DETECT_NONE); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; case DCE2_DETECT_FLAG__SMB: if (autodetect) port_array = sc->auto_smb_ports; else port_array = sc->smb_ports; break; case DCE2_DETECT_FLAG__TCP: if (autodetect) port_array = sc->auto_tcp_ports; else port_array = sc->tcp_ports; break; case DCE2_DETECT_FLAG__UDP: if (autodetect) port_array = sc->auto_udp_ports; else port_array = sc->udp_ports; break; case DCE2_DETECT_FLAG__HTTP_PROXY: if (autodetect) port_array = sc->auto_http_proxy_ports; else port_array = sc->http_proxy_ports; break; case DCE2_DETECT_FLAG__HTTP_SERVER: if (autodetect) port_array = sc->auto_http_server_ports; else port_array = sc->http_server_ports; break; default: return DCE2_RET__ERROR; } state = DCE2_DETECT_LIST_STATE__TYPE_END; continue; } break; case DCE2_DETECT_LIST_STATE__TYPE_END: if (DCE2_IsSpaceChar(c)) { /* Guess that a port list will be next */ state = DCE2_DETECT_LIST_STATE__PORTS_START; } else if (one_type) { return DCE2_ScInitPortArray(sc, dflag, autodetect); } else if (DCE2_IsListSepChar(c)) { if (DCE2_ScInitPortArray(sc, dflag, autodetect) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; state = DCE2_DETECT_LIST_STATE__TYPE_START; } else if (DCE2_IsListEndChar(c)) { if (DCE2_ScInitPortArray(sc, dflag, autodetect) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; state = DCE2_DETECT_LIST_STATE__END; } else { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", option_str, *ptr); return DCE2_RET__ERROR; } break; case DCE2_DETECT_LIST_STATE__PORTS_START: if (DCE2_IsPortChar(c) || DCE2_IsPortRangeChar(c) || DCE2_IsListStartChar(c)) { state = DCE2_DETECT_LIST_STATE__PORTS; continue; } else if (one_type) { if (!DCE2_IsSpaceChar(c)) { return DCE2_ScInitPortArray(sc, dflag, autodetect); } } else if (DCE2_IsListSepChar(c)) { if (DCE2_ScInitPortArray(sc, dflag, autodetect) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; state = DCE2_DETECT_LIST_STATE__TYPE_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", option_str, *ptr); return DCE2_RET__ERROR; } break; case DCE2_DETECT_LIST_STATE__PORTS: if (DCE2_ParsePortList(ptr, end, port_array) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; state = DCE2_DETECT_LIST_STATE__PORTS_END; continue; case DCE2_DETECT_LIST_STATE__PORTS_END: if (one_type) { return DCE2_RET__SUCCESS; } else if (DCE2_IsListEndChar(c)) { state = DCE2_DETECT_LIST_STATE__END; } else if (DCE2_IsListSepChar(c)) { state = DCE2_DETECT_LIST_STATE__TYPE_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", option_str, *ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid %s state: %d", __FILE__, __LINE__, option_str, state); return DCE2_RET__ERROR; } last_char = c; (*ptr)++; } if (state != DCE2_DETECT_LIST_STATE__END) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", option_str, *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScResetPortArrays() * * Clears all of the port bits in the specified port array masks * for the passed in server configuration. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * int * Non-zero if we should clear the autodetect port bits. * Zero if we should clear the detect port bits. * * Returns: None * ********************************************************************/ static inline void DCE2_ScResetPortsArrays(DCE2_ServerConfig *sc, int autodetect) { if (!autodetect) { DCE2_ClearPorts(sc->smb_ports); DCE2_ClearPorts(sc->tcp_ports); DCE2_ClearPorts(sc->udp_ports); DCE2_ClearPorts(sc->http_proxy_ports); DCE2_ClearPorts(sc->http_server_ports); } else { DCE2_ClearPorts(sc->auto_smb_ports); DCE2_ClearPorts(sc->auto_tcp_ports); DCE2_ClearPorts(sc->auto_udp_ports); DCE2_ClearPorts(sc->auto_http_proxy_ports); DCE2_ClearPorts(sc->auto_http_server_ports); } } /******************************************************************** * Function: DCE2_ScParseDetectType() * * Parses the detect type or transport argument to the detect or * autodetect options. * * Arguments: * char * * Pointer to the first character of the detect type name. * char * * Pointer to the byte after the last character of * the detect type name. * int * Pointer to the current detect type mask. Contains bits * set for each detect type that has already been configured. * Mask is checked and updated for new option. * * Returns: * DCE2_DetectFlag * Flag for the detect type or transport. * NULL flag if not a valid detect type or detect type has * already been configured. * ********************************************************************/ static inline DCE2_DetectFlag DCE2_ScParseDetectType(char *start, char *end, int *dmask) { DCE2_DetectFlag dflag = DCE2_DETECT_FLAG__NULL; size_t dtype_len = end - start; if (dtype_len == strlen(DCE2_SARG__DETECT_SMB) && strncasecmp(DCE2_SARG__DETECT_SMB, start, dtype_len) == 0) { dflag = DCE2_DETECT_FLAG__SMB; } else if (dtype_len == strlen(DCE2_SARG__DETECT_TCP) && strncasecmp(DCE2_SARG__DETECT_TCP, start, dtype_len) == 0) { dflag = DCE2_DETECT_FLAG__TCP; } else if (dtype_len == strlen(DCE2_SARG__DETECT_UDP) && strncasecmp(DCE2_SARG__DETECT_UDP, start, dtype_len) == 0) { dflag = DCE2_DETECT_FLAG__UDP; } else if (dtype_len == strlen(DCE2_SARG__DETECT_HTTP_PROXY) && strncasecmp(DCE2_SARG__DETECT_HTTP_PROXY, start, dtype_len) == 0) { dflag = DCE2_DETECT_FLAG__HTTP_PROXY; } else if (dtype_len == strlen(DCE2_SARG__DETECT_HTTP_SERVER) && strncasecmp(DCE2_SARG__DETECT_HTTP_SERVER, start, dtype_len) == 0) { dflag = DCE2_DETECT_FLAG__HTTP_SERVER; } else if (dtype_len == strlen(DCE2_SARG__DETECT_NONE) && strncasecmp(DCE2_SARG__DETECT_NONE, start, dtype_len) == 0) { dflag = DCE2_DETECT_FLAG__NONE; } else { DCE2_ScError("Invalid detect/autodetect type: \"%.*s\"", dtype_len, start); return DCE2_DETECT_FLAG__NULL; } if (DCE2_CheckAndSetMask(dflag, dmask) != DCE2_RET__SUCCESS) { DCE2_ScError("Can only configure option once: \"%.*s\"", dtype_len, start); return DCE2_DETECT_FLAG__NULL; } return dflag; } /******************************************************************** * Function: DCE2_ScParseSmbShares() * * Parses shares given to the smb shares option. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the smb shares arguments. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * arguments to the smb shares option * DCE2_RET__ERROR if an error occured in parsing the smb * shares option. * ********************************************************************/ static DCE2_Ret DCE2_ScParseSmbShares(DCE2_ServerConfig *sc, char **ptr, char *end) { DCE2_WordListState state = DCE2_WORD_LIST_STATE__START; char *share_start = *ptr; int one_share = 0; int quote = 0; while (*ptr < end) { char c = **ptr; if (state == DCE2_WORD_LIST_STATE__END) break; switch (state) { case DCE2_WORD_LIST_STATE__START: if (DCE2_IsQuoteChar(c)) { quote ^= 1; one_share = 1; state = DCE2_WORD_LIST_STATE__QUOTE; } else if (DCE2_IsListStartChar(c)) { state = DCE2_WORD_LIST_STATE__WORD_START; } else if (DCE2_IsGraphChar(c)) { /* Only one share */ share_start = *ptr; one_share = 1; state = DCE2_WORD_LIST_STATE__WORD; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__SMB_INVALID_SHARES, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__QUOTE: if (DCE2_IsGraphChar(c)) { share_start = *ptr; state = DCE2_WORD_LIST_STATE__WORD; } else { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__SMB_INVALID_SHARES, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD_START: if (DCE2_IsQuoteChar(c)) { quote ^= 1; state = DCE2_WORD_LIST_STATE__QUOTE; } else if (DCE2_IsGraphChar(c)) { share_start = *ptr; state = DCE2_WORD_LIST_STATE__WORD; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__SMB_INVALID_SHARES, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD: if (!DCE2_IsGraphChar(c)) { DCE2_SmbShare *smb_share; DCE2_SmbShare *smb_share_key; int share_len = *ptr - share_start; int i, j; DCE2_Ret status; smb_share = (DCE2_SmbShare *)DCE2_Alloc(sizeof(DCE2_SmbShare), DCE2_MEM_TYPE__CONFIG); smb_share_key = (DCE2_SmbShare *)DCE2_Alloc(sizeof(DCE2_SmbShare), DCE2_MEM_TYPE__CONFIG); if ((smb_share == NULL) || (smb_share_key == NULL)) { DCE2_ScSmbShareFree((void *)smb_share); DCE2_ScSmbShareFree((void *)smb_share_key); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to allocate memory for smb share.", __FILE__, __LINE__); return DCE2_RET__ERROR; } smb_share->unicode_str_len = (share_len * 2) + 2; smb_share->unicode_str = (char *)DCE2_Alloc(smb_share->unicode_str_len, DCE2_MEM_TYPE__CONFIG); smb_share->ascii_str_len = share_len + 1; smb_share->ascii_str = (char *)DCE2_Alloc(smb_share->ascii_str_len, DCE2_MEM_TYPE__CONFIG); if ((smb_share->unicode_str == NULL) || (smb_share->ascii_str == NULL)) { DCE2_ScSmbShareFree((void *)smb_share); DCE2_ScSmbShareFree((void *)smb_share_key); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to allocate memory for smb share.", __FILE__, __LINE__); return DCE2_RET__ERROR; } for (i = 0, j = 0; i < share_len; i++, j += 2) { smb_share->unicode_str[j] = (char)toupper((int)share_start[i]); smb_share->ascii_str[i] = (char)toupper((int)share_start[i]); } /* Just use ascii share as the key */ smb_share_key->ascii_str_len = smb_share->ascii_str_len; smb_share_key->ascii_str = (char *)DCE2_Alloc(smb_share_key->ascii_str_len, DCE2_MEM_TYPE__CONFIG); if (smb_share_key->ascii_str == NULL) { DCE2_ScSmbShareFree((void *)smb_share); DCE2_ScSmbShareFree((void *)smb_share_key); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to allocate memory for smb share.", __FILE__, __LINE__); return DCE2_RET__ERROR; } memcpy(smb_share_key->ascii_str, smb_share->ascii_str, smb_share_key->ascii_str_len); status = DCE2_ListInsert(sc->smb_invalid_shares, (void *)smb_share_key, (void *)smb_share); if (status == DCE2_RET__DUPLICATE) { /* Just free this share and move on */ DCE2_ScSmbShareFree((void *)smb_share); DCE2_ScSmbShareFree((void *)smb_share_key); } else if (status != DCE2_RET__SUCCESS) { DCE2_ScSmbShareFree((void *)smb_share); DCE2_ScSmbShareFree((void *)smb_share_key); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to insert invalid share into list.", __FILE__, __LINE__); return DCE2_RET__ERROR; } if (one_share) { if (quote && !DCE2_IsQuoteChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: Unterminated quoted string", DCE2_SOPT__SMB_INVALID_SHARES); return DCE2_RET__ERROR; } state = DCE2_WORD_LIST_STATE__END; break; } state = DCE2_WORD_LIST_STATE__WORD_END; continue; } break; case DCE2_WORD_LIST_STATE__WORD_END: if (quote) { if (!DCE2_IsQuoteChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: Unterminated quoted string", DCE2_SOPT__SMB_INVALID_SHARES); return DCE2_RET__ERROR; } quote ^= 1; } else if (DCE2_IsListEndChar(c)) { state = DCE2_WORD_LIST_STATE__END; } else if (DCE2_IsListSepChar(c)) { state = DCE2_WORD_LIST_STATE__WORD_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__SMB_INVALID_SHARES, *ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid smb shares state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } (*ptr)++; } if (state != DCE2_WORD_LIST_STATE__END) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__SMB_INVALID_SHARES, *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScParseSmbMaxChain() * * Parses the argument to the smb max chain option. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the smb max chain argument. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * argument to the smb max chain option. * DCE2_RET__ERROR if an error occured in parsing the smb max * chain argument. * ********************************************************************/ static DCE2_Ret DCE2_ScParseSmbMaxChain(DCE2_ServerConfig *sc, char **ptr, char *end) { DCE2_Ret status; uint8_t chain_len; status = DCE2_ParseValue(ptr, end, &chain_len, DCE2_INT_TYPE__UINT8); if (status != DCE2_RET__SUCCESS) { DCE2_ScError("Error parsing \"%s\". Value must be between 0 and %u inclusive", DCE2_SOPT__SMB_MAX_CHAIN, UINT8_MAX); return DCE2_RET__ERROR; } sc->smb_max_chain = chain_len; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScParseSmb2MaxCompound() * * Parses the argument to the smb2 max compound option. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the smb max chain argument. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * argument to the smb2 max compound option. * DCE2_RET__ERROR if an error occured in parsing the smb2 max * compound argument. * ********************************************************************/ static DCE2_Ret DCE2_ScParseSmb2MaxCompound(DCE2_ServerConfig *sc, char **ptr, char *end) { DCE2_Ret status; uint8_t compound_len; status = DCE2_ParseValue(ptr, end, &compound_len, DCE2_INT_TYPE__UINT8); if (status != DCE2_RET__SUCCESS) { DCE2_ScError("Error parsing \"%s\". Value must be between 0 and %u inclusive", DCE2_SOPT__SMB2_MAX_COMPOUND, UINT8_MAX); return DCE2_RET__ERROR; } sc->smb2_max_compound = compound_len; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScParseValidSmbVersions() * * Parses the version types for the valid smb versions option and * adds to server configuration. * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * valid smb versions * DCE2_RET__ERROR if an error occured in parsing. * ********************************************************************/ static DCE2_Ret DCE2_ScParseValidSmbVersions(DCE2_ServerConfig *sc, char **ptr, char *end) { DCE2_WordListState state = DCE2_WORD_LIST_STATE__START; char *version_start = *ptr; char last_char = 0; int one_version = 0; int version_mask = 0; DCE2_ScClearAllValidSmbVersionFlags(sc); while (*ptr < end) { char c = **ptr; if (state == DCE2_WORD_LIST_STATE__END) break; switch (state) { case DCE2_WORD_LIST_STATE__START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { /* Only one valid smb version */ version_start = *ptr; one_version = 1; state = DCE2_WORD_LIST_STATE__WORD; } else if (DCE2_IsListStartChar(c)) { state = DCE2_WORD_LIST_STATE__WORD_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__VALID_SMB_VERSIONS, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { version_start = *ptr; state = DCE2_WORD_LIST_STATE__WORD; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__VALID_SMB_VERSIONS, *ptr); return DCE2_RET__ERROR; } break; case DCE2_WORD_LIST_STATE__WORD: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { DCE2_ValidSmbVersionFlag vflag; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", DCE2_SOPT__VALID_SMB_VERSIONS, *ptr - version_start, version_start); return DCE2_RET__ERROR; } vflag = DCE2_ScParseValidSmbVersion(version_start, *ptr, &version_mask); switch (vflag) { case DCE2_VALID_SMB_VERSION_FLAG__NULL: return DCE2_RET__ERROR; case DCE2_VALID_SMB_VERSION_FLAG__ALL: if (!one_version) { DCE2_ScError("Valid smb version \"%s\" cannot be " "configured in a list", DCE2_SARG__VALID_SMB_VERSIONS_ALL); return DCE2_RET__ERROR; } DCE2_ScSetValidSmbVersion(sc, vflag); break; default: DCE2_ScSetValidSmbVersion(sc, vflag); break; } if (one_version) return DCE2_RET__SUCCESS; state = DCE2_WORD_LIST_STATE__WORD_END; continue; } break; case DCE2_WORD_LIST_STATE__WORD_END: if (DCE2_IsListEndChar(c)) { state = DCE2_WORD_LIST_STATE__END; } else if (DCE2_IsListSepChar(c)) { state = DCE2_WORD_LIST_STATE__WORD_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__VALID_SMB_VERSIONS, *ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid valid " "smb versions state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } last_char = c; (*ptr)++; } if (state != DCE2_WORD_LIST_STATE__END) { DCE2_ScError("Invalid \"%s\" syntax: \"%s\"", DCE2_SOPT__VALID_SMB_VERSIONS, *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScParseValidSmbVersion() * * Parses smb version and returns flag indication the smb version. * Checks and sets a bit in a mask to prevent multiple * configurations of the same event type. * * Arguments: * char * * Pointer to the first character of the smb version name. * char * * Pointer to the byte after the last character of * the smb version name. * int * Pointer to the current valid smb versions mask. Contains * bits set for each smb version that has already been * configured. Mask is checked and updated for new version. * * Returns: * DCE2_ValidSmbVersionFlag * Flag indicating the smb version. * DCE2_VALID_SMB_VERSION_FLAG__NULL if no version or multiple * configuration of smb version. * ********************************************************************/ static inline DCE2_ValidSmbVersionFlag DCE2_ScParseValidSmbVersion( char *start, char *end, int *vmask) { DCE2_ValidSmbVersionFlag vflag = DCE2_VALID_SMB_VERSION_FLAG__NULL; size_t version_len = end - start; if (version_len == strlen(DCE2_SARG__VALID_SMB_VERSIONS_V1) && strncasecmp(DCE2_SARG__VALID_SMB_VERSIONS_V1, start, version_len) == 0) { vflag = DCE2_VALID_SMB_VERSION_FLAG__V1; } else if (version_len == strlen(DCE2_SARG__VALID_SMB_VERSIONS_V2) && strncasecmp(DCE2_SARG__VALID_SMB_VERSIONS_V2, start, version_len) == 0) { vflag = DCE2_VALID_SMB_VERSION_FLAG__V2; } else if (version_len == strlen(DCE2_SARG__VALID_SMB_VERSIONS_ALL) && strncasecmp(DCE2_SARG__VALID_SMB_VERSIONS_ALL, start, version_len) == 0) { vflag = DCE2_VALID_SMB_VERSION_FLAG__ALL; } else { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", DCE2_SOPT__VALID_SMB_VERSIONS, version_len, start); return DCE2_VALID_SMB_VERSION_FLAG__NULL; } if (DCE2_CheckAndSetMask((int)vflag, vmask) != DCE2_RET__SUCCESS) { DCE2_ScError("Valid smb version \"%.*s\" cannot be specified more than once", version_len, start); return DCE2_VALID_SMB_VERSION_FLAG__NULL; } return vflag; } /********************************************************************* * Function: DCE2_ScSetValidSmbVersion() * * Sets the valid smb version the user will allow during processing * in the server configuration valid smb versions mask. * * Arguments: * DCE2_ServerConfig * * Pointer to server config structure. * DCE2_ValidSmbVersionFlag * The smb version flag to set. * * Returns: None * *********************************************************************/ static inline void DCE2_ScSetValidSmbVersion(DCE2_ServerConfig *sc, DCE2_ValidSmbVersionFlag vflag) { sc->valid_smb_versions_mask |= vflag; } /********************************************************************* * Function: DCE2_ScClearAllValidSmbVersionFlags() * * Clears all of the bits in the server configuration smb * valid versions mask. * * Arguments: * DCE2_ServerConfig * * Pointer to server config structure. * * Returns: None * *********************************************************************/ static inline void DCE2_ScClearAllValidSmbVersionFlags(DCE2_ServerConfig *sc) { sc->valid_smb_versions_mask = DCE2_VALID_SMB_VERSION_FLAG__NULL; } /******************************************************************** * Function: DCE2_ScParseSmbFileInspection() * * Parses the arguments to the smb_file_inspection option. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the policy argument. * char * * Pointer to the end of the configuration line. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse. * DCE2_RET__ERROR if an error occured in parsing. * ********************************************************************/ static DCE2_Ret DCE2_ScParseSmbFileInspection(DCE2_ServerConfig *sc, char **ptr, char *end) { DCE2_SmbFileListState state = DCE2_SMB_FILE_LIST_STATE__START; const char *option = DCE2_SOPT__SMB_FILE_INSPECTION; char *option_start = *ptr; char *optr; int no_list = 0; char last_char = 0; while (*ptr < end) { char c = **ptr; if (state == DCE2_SMB_FILE_LIST_STATE__END) break; switch (state) { case DCE2_SMB_FILE_LIST_STATE__START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { optr = *ptr; no_list = 1; state = DCE2_SMB_FILE_LIST_STATE__ENABLEMENT; } else if (DCE2_IsListStartChar(c)) { state = DCE2_SMB_FILE_LIST_STATE__ENABLEMENT_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%.*s\"", option, *ptr - option_start, option_start); return DCE2_RET__ERROR; } break; case DCE2_SMB_FILE_LIST_STATE__ENABLEMENT_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { optr = *ptr; state = DCE2_SMB_FILE_LIST_STATE__ENABLEMENT; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%.*s\"", option, *ptr - option_start, option_start); return DCE2_RET__ERROR; } break; case DCE2_SMB_FILE_LIST_STATE__ENABLEMENT: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { int olen = *ptr - optr; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", option, *ptr - optr, optr); return DCE2_RET__ERROR; } if ((olen == strlen(DCE2_SARG__SMB_FILE_INSPECTION_ON)) && (strncasecmp(DCE2_SARG__SMB_FILE_INSPECTION_ON, optr, olen) == 0)) { sc->smb_file_inspection = DCE2_SMB_FILE_INSPECTION__ON; } else if ((olen == strlen(DCE2_SARG__SMB_FILE_INSPECTION_OFF)) && (strncasecmp(DCE2_SARG__SMB_FILE_INSPECTION_OFF, optr, olen) == 0)) { sc->smb_file_inspection = DCE2_SMB_FILE_INSPECTION__OFF; } else if ((olen == strlen(DCE2_SARG__SMB_FILE_INSPECTION_ONLY)) && (strncasecmp(DCE2_SARG__SMB_FILE_INSPECTION_ONLY, optr, olen) == 0)) { sc->smb_file_inspection = DCE2_SMB_FILE_INSPECTION__ONLY; } else { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", option, *ptr - optr, optr); return DCE2_RET__ERROR; } state = DCE2_SMB_FILE_LIST_STATE__ENABLEMENT_END; continue; } break; case DCE2_SMB_FILE_LIST_STATE__ENABLEMENT_END: if (no_list) { return DCE2_RET__SUCCESS; } else if (DCE2_IsListSepChar(c)) { state = DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_START; } else if (DCE2_IsListEndChar(c)) { state = DCE2_SMB_FILE_LIST_STATE__END; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%.*s\"", option, *ptr - option_start, option_start); return DCE2_RET__ERROR; } break; case DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__START)) { optr = *ptr; state = DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%.*s\"", option, *ptr - option_start, option_start); return DCE2_RET__ERROR; } break; case DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { int olen = *ptr - optr; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", option, *ptr - optr, optr); return DCE2_RET__ERROR; } if ((olen == strlen(DCE2_SARG__SMB_FILE_INSPECTION_DEPTH)) && (strncasecmp(DCE2_SARG__SMB_FILE_INSPECTION_DEPTH, optr, olen) == 0)) { state = DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE_START; } else { DCE2_ScError("Invalid \"%s\" argument: \"%.*s\"", option, *ptr - optr, optr); return DCE2_RET__ERROR; } continue; } break; case DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE_START: if (DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { optr = *ptr; state = DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%.*s\"", option, *ptr - option_start, option_start); return DCE2_RET__ERROR; } break; case DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE: if (!DCE2_IsWordChar(c, DCE2_WORD_CHAR_POSITION__MIDDLE)) { char *start_value = optr; if (!DCE2_IsWordChar(last_char, DCE2_WORD_CHAR_POSITION__END)) { DCE2_ScError("Invalid argument to \"%s\": \"%.*s\". Value " "must be between -1 and "STDi64".\n", DCE2_SARG__SMB_FILE_INSPECTION_DEPTH, optr - start_value, start_value, INT64_MAX); return DCE2_RET__ERROR; } if (DCE2_ParseValue(&optr, *ptr, &sc->smb_file_depth, DCE2_INT_TYPE__INT64) != DCE2_RET__SUCCESS) { DCE2_ScError("Invalid argument to \"%s\": \"%.*s\". Value " "must be between -1 and "STDi64".\n", DCE2_SARG__SMB_FILE_INSPECTION_DEPTH, optr - start_value, start_value, INT64_MAX); return DCE2_RET__ERROR; } if ((sc->smb_file_depth < 0) && (sc->smb_file_depth != -1)) { DCE2_ScError("Invalid argument to \"%s\": "STDi64". Value " "must be between -1 and "STDi64".\n", DCE2_SARG__SMB_FILE_INSPECTION_DEPTH, sc->smb_file_depth, INT64_MAX); return DCE2_RET__ERROR; } state = DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE_END; continue; } break; case DCE2_SMB_FILE_LIST_STATE__FILE_DEPTH_VALUE_END: if (DCE2_IsListEndChar(c)) { state = DCE2_SMB_FILE_LIST_STATE__END; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid \"%s\" syntax: \"%.*s\"", option, *ptr - option_start, option_start); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid %s state: %d", __FILE__, __LINE__, option, state); return DCE2_RET__ERROR; } last_char = c; (*ptr)++; } if (state != DCE2_SMB_FILE_LIST_STATE__END) { DCE2_ScError("Invalid \"%s\" syntax: \"%.*s\"", option, *ptr - option_start, option_start); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ScAddToRoutingTable() * * Adds the server configuration to the appropriate routing table * (IPv4 or IPv6) based on the ip addresses and nets (as sfcidr_t) * from the passed in queue. A pointer to the server configuration * is saved in the routing table for each ip set. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * DCE2_Queue * * Queue containing the IPs and nets to add to the routing * tables for this server configuration. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully add all * of the IPs and nets to the routing tables for this * server configuration. * DCE2_RET__ERROR if an error occured in trying to add all * of the IPs and nets to the routing tables for this * server configuration. * ********************************************************************/ static DCE2_Ret DCE2_ScAddToRoutingTable(DCE2_Config *config, DCE2_ServerConfig *sc, DCE2_Queue *ip_queue) { sfcidr_t *ip; if ((config == NULL) || (sc == NULL) || (ip_queue == NULL)) return DCE2_RET__ERROR; for (ip = (sfcidr_t *)DCE2_QueueFirst(ip_queue); ip != NULL; ip = (sfcidr_t *)DCE2_QueueNext(ip_queue)) { int rt_status; if (config->sconfigs == NULL) { config->sconfigs = sfrt_new(DIR_16_4x4_16x5_4x4, IPv6, 100, 20); if (config->sconfigs == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d): Failed to create server configuration " "routing table.", __FILE__, __LINE__); return DCE2_RET__ERROR; } } else { DCE2_ServerConfig *conf; conf = (DCE2_ServerConfig *)sfrt_search(&ip->addr, config->sconfigs); if (conf != NULL) { DCE2_ScError("\"%s\": Cannot have the same net in different " "server configurations", DCE2_SOPT__NET); return DCE2_RET__ERROR; } } rt_status = sfrt_insert(ip, (unsigned char)ip->bits, (void *)sc, RT_FAVOR_SPECIFIC, config->sconfigs); if (rt_status != RT_SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to insert net into routing table.", __FILE__, __LINE__); return DCE2_RET__ERROR; } /* This is a count of the number of pointers or references to this * server configuration in the routing tables. */ sc->ref_count++; } return DCE2_RET__SUCCESS; } /********************************************************************* * Function: DCE2_ScIpListDataFree() * * Callback given to the queue for storing sfcidr_t structures. * * Arguments: * void * * The sfcidr_t structure to free. * * Returns: None * *********************************************************************/ static void DCE2_ScIpListDataFree(void *data) { if (data == NULL) return; DCE2_Free(data, sizeof(sfcidr_t), DCE2_MEM_TYPE__CONFIG); } /******************************************************************** * Function: DCE2_ScGetConfig() * * Convenience function for run-time retrieving of a server * configuration. Does a lookup in the appropriate routing table * based on the IPs in the packet structure. * * Arguments: * const SFSnortPacket * * Pointer to the packet structure flowing through the system. * * Returns: * DCE2_ServerConfig * * A pointer to a valid server configuration if a routing * table lookup succeeded. * A pointer to the default server configuration if an entry * in the routing table could not be found. * ********************************************************************/ const DCE2_ServerConfig * DCE2_ScGetConfig(const SFSnortPacket *p) { const DCE2_ServerConfig *sc = NULL; sfaddr_t* ip; if (dce2_eval_config == NULL) return NULL; if (DCE2_SsnFromClient(p)) ip = GET_DST_IP(((SFSnortPacket *)p)); else ip = GET_SRC_IP(((SFSnortPacket *)p)); if (dce2_eval_config->sconfigs != NULL) sc = sfrt_lookup(ip, dce2_eval_config->sconfigs); if (sc == NULL) return dce2_eval_config->dconfig; return sc; } /********************************************************************* * Function: DCE2_ScSmbShareCompare() * * Callback for the list used to hold the invalid smb shares for * doing a comparison. Don't want any duplicates. * * Arguments: * const void * * The first share name to compare. * const void * * The second share name to compare. * * Returns: * int * 0 if the shares are equal. * -1 if the shares are not equal. * *********************************************************************/ static int DCE2_ScSmbShareCompare(const void *a, const void *b) { DCE2_SmbShare *ashare = (DCE2_SmbShare *)a; DCE2_SmbShare *bshare = (DCE2_SmbShare *)b; if ((ashare == NULL) || (bshare == NULL)) return -1; /* Just check the ascii string */ if (ashare->ascii_str_len != bshare->ascii_str_len) return -1; if (memcmp(ashare->ascii_str, bshare->ascii_str, ashare->ascii_str_len) == 0) return 0; /* Only care about equality for dups */ return -1; } /********************************************************************* * Function: DCE2_ScSmbShareFree() * * Callback to the list used to hold the invalid smb shares for * freeing the shares. * * Arguments: * void * * Pointer to the share structure. * * Returns: None * *********************************************************************/ static void DCE2_ScSmbShareFree(void *data) { DCE2_SmbShare *smb_share = (DCE2_SmbShare *)data; if (smb_share == NULL) return; DCE2_Free((void *)smb_share->unicode_str, smb_share->unicode_str_len, DCE2_MEM_TYPE__CONFIG); DCE2_Free((void *)smb_share->ascii_str, smb_share->ascii_str_len, DCE2_MEM_TYPE__CONFIG); DCE2_Free((void *)smb_share, sizeof(DCE2_SmbShare), DCE2_MEM_TYPE__CONFIG); } /******************************************************************** * Function: DCE2_GcPrintConfig() * * Prints the DCE/RPC global configuration. * * Arguments: * DCE2_GlobalConfig * * Pointer to the global configuration structure. * * Returns: None * ********************************************************************/ static void DCE2_GcPrintConfig(const DCE2_GlobalConfig *gc) { char events[1000]; if (gc == NULL) return; _dpd.logMsg("DCE/RPC 2 Preprocessor Configuration\n"); _dpd.logMsg(" Global Configuration\n"); if(gc->disabled) { _dpd.logMsg(" DCE/RPC 2 Preprocessor: INACTIVE\n"); } _dpd.logMsg(" DCE/RPC Defragmentation: %s\n", gc->dce_defrag == DCE2_CS__ENABLED ? "Enabled" : "Disabled"); if ((gc->dce_defrag == DCE2_CS__ENABLED) && (gc->max_frag_len != DCE2_SENTINEL)) _dpd.logMsg(" Max DCE/RPC Frag Size: %u bytes\n", gc->max_frag_len); _dpd.logMsg(" Memcap: %u KB\n", gc->memcap / 1024); if (gc->reassemble_threshold != 0) _dpd.logMsg(" Reassemble threshold: %u bytes\n", gc->reassemble_threshold); snprintf(events, sizeof(events), " Events: "); events[sizeof(events) - 1] = '\0'; if (gc->event_mask == DCE2_EVENT_FLAG__NULL) { strncat(events, DCE2_GARG__EVENTS_NONE, (sizeof(events) - 1) - strlen(events)); } else { if (gc->event_mask & DCE2_EVENT_FLAG__MEMCAP) { strncat(events, DCE2_GARG__EVENTS_MEMCAP, (sizeof(events) - 1) - strlen(events)); strncat(events, " ", (sizeof(events) - 1) - strlen(events)); } if (gc->event_mask & DCE2_EVENT_FLAG__SMB) { strncat(events, DCE2_GARG__EVENTS_SMB, (sizeof(events) - 1) - strlen(events)); strncat(events, " ", (sizeof(events) - 1) - strlen(events)); } if (gc->event_mask & DCE2_EVENT_FLAG__CO) { strncat(events, DCE2_GARG__EVENTS_CO, (sizeof(events) - 1) - strlen(events)); strncat(events, " ", (sizeof(events) - 1) - strlen(events)); } if (gc->event_mask & DCE2_EVENT_FLAG__CL) { strncat(events, DCE2_GARG__EVENTS_CL, (sizeof(events) - 1) - strlen(events)); strncat(events, " ", (sizeof(events) - 1) - strlen(events)); } } strncat(events, "\n", (sizeof(events) - 1) - strlen(events)); _dpd.logMsg(events); // Just use the events buffer snprintf(events, sizeof(events), " SMB Fingerprint policy: "); if (gc->smb_fingerprint_policy == DCE2_SMB_FINGERPRINT__NONE) strncat(events, "Disabled\n", (sizeof(events) - 1) - strlen(events)); else if (gc->smb_fingerprint_policy == (DCE2_SMB_FINGERPRINT__CLIENT|DCE2_SMB_FINGERPRINT__SERVER)) strncat(events, "Client and Server\n", (sizeof(events) - 1) - strlen(events)); else if (gc->smb_fingerprint_policy & DCE2_SMB_FINGERPRINT__CLIENT) strncat(events, "Client\n", (sizeof(events) - 1) - strlen(events)); else if (gc->smb_fingerprint_policy & DCE2_SMB_FINGERPRINT__SERVER) strncat(events, "Server\n", (sizeof(events) - 1) - strlen(events)); _dpd.logMsg(events); } /******************************************************************** * Function: DCE2_ScPrintConfig() * * Prints a DCE/RPC server configuration. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * DCE2_Queue * * Queue that holds the nets for printing. * * Returns: None * ********************************************************************/ static void DCE2_ScPrintConfig(const DCE2_ServerConfig *sc, DCE2_Queue *net_queue) { char *policy = NULL; unsigned int i; if (sc == NULL) return; if (!DCE2_QueueIsEmpty(net_queue)) { char nets[80]; _dpd.logMsg(" Server Configuration\n"); snprintf(nets, sizeof(nets), " Net: "); nets[sizeof(nets) - 1] = '\0'; while (!DCE2_QueueIsEmpty(net_queue)) { char *ip_addr; uint8_t prefix; sfcidr_t *ip; char tmp_net[INET6_ADDRSTRLEN + 5]; /* Enough for IPv4 plus netmask or full IPv6 plus prefix */ ip = (sfcidr_t *)DCE2_QueueDequeue(net_queue); ip_addr = sfip_to_str(&ip->addr); prefix = (uint8_t)ip->bits; DCE2_Free((void *)ip, sizeof(sfcidr_t), DCE2_MEM_TYPE__CONFIG); snprintf(tmp_net, sizeof(tmp_net), "%s/%u ", ip_addr, prefix); tmp_net[sizeof(tmp_net) - 1] = '\0'; if ((strlen(nets) + strlen(tmp_net)) >= sizeof(nets)) { _dpd.logMsg("%s\n", nets); snprintf(nets, sizeof(nets), " %s", tmp_net); nets[sizeof(nets) - 1] = '\0'; } else { strncat(nets, tmp_net, (sizeof(nets) - 1) - strlen(nets)); } } _dpd.logMsg("%s\n", nets); } else { _dpd.logMsg(" Server Default Configuration\n"); } switch (sc->policy) { case DCE2_POLICY__WIN2000: policy = DCE2_SARG__POLICY_WIN2000; break; case DCE2_POLICY__WINXP: policy = DCE2_SARG__POLICY_WINXP; break; case DCE2_POLICY__WINVISTA: policy = DCE2_SARG__POLICY_WINVISTA; break; case DCE2_POLICY__WIN2003: policy = DCE2_SARG__POLICY_WIN2003; break; case DCE2_POLICY__WIN2008: policy = DCE2_SARG__POLICY_WIN2008; break; case DCE2_POLICY__WIN7: policy = DCE2_SARG__POLICY_WIN7; break; case DCE2_POLICY__SAMBA: policy = DCE2_SARG__POLICY_SAMBA; break; case DCE2_POLICY__SAMBA_3_0_37: policy = DCE2_SARG__POLICY_SAMBA_3_0_37; break; case DCE2_POLICY__SAMBA_3_0_22: policy = DCE2_SARG__POLICY_SAMBA_3_0_22; break; case DCE2_POLICY__SAMBA_3_0_20: policy = DCE2_SARG__POLICY_SAMBA_3_0_20; break; default: DCE2_QueueDestroy(net_queue); DCE2_Die("%s(%d) Invalid policy: %d", __FILE__, __LINE__, sc->policy); } _dpd.logMsg(" Policy: %s\n", policy); DCE2_ScPrintPorts(sc, 0); for (i = 0; i < DCE2_PORTS__MAX; i++) { if (DCE2_IsPortSet(sc->http_proxy_ports, (uint16_t)i)) { _dpd.logMsg(" Autodetect on RPC over HTTP proxy detect ports: %s\n", sc->autodetect_http_proxy_ports == DCE2_CS__ENABLED ? "Yes" : "No"); break; } } DCE2_ScPrintPorts(sc, 1); for (i = 0; i < DCE2_PORTS__MAX; i++) { if (DCE2_IsPortSet(sc->smb_ports, (uint16_t)i) || DCE2_IsPortSet(sc->auto_smb_ports, (uint16_t)i)) { break; } } if ((i != DCE2_PORTS__MAX) && (sc->smb_invalid_shares != NULL)) { char share_str[80]; DCE2_SmbShare *share; snprintf(share_str, sizeof(share_str), " Invalid SMB shares: "); share_str[sizeof(share_str) - 1] = '\0'; for (share = DCE2_ListFirst(sc->smb_invalid_shares); share != NULL; share = DCE2_ListNext(sc->smb_invalid_shares)) { char *tmp_share; unsigned int tmp_share_len; /* Ascii string will be NULL terminated. Also alloc enough for space. * Note that if share is longer than the size of the buffer it will be * put into, it will be truncated */ tmp_share_len = strlen(share->ascii_str) + 2; tmp_share = (char *)DCE2_Alloc(tmp_share_len, DCE2_MEM_TYPE__CONFIG); if (tmp_share == NULL) { DCE2_QueueDestroy(net_queue); DCE2_Die("%s(%d) Failed to allocate memory for printing " "configuration.", __FILE__, __LINE__); } snprintf(tmp_share, tmp_share_len, "%s ", share->ascii_str); tmp_share[tmp_share_len - 1] = '\0'; if ((strlen(share_str) + strlen(tmp_share)) >= sizeof(share_str)) { _dpd.logMsg("%s\n", share_str); snprintf(share_str, sizeof(share_str), " %s", tmp_share); share_str[sizeof(share_str) - 1] = '\0'; } else { strncat(share_str, tmp_share, (sizeof(share_str) - 1) - strlen(share_str)); } DCE2_Free((void *)tmp_share, tmp_share_len, DCE2_MEM_TYPE__CONFIG); } _dpd.logMsg("%s\n", share_str); } if (i != DCE2_PORTS__MAX) { if (sc->smb_max_chain == 0) _dpd.logMsg(" Maximum SMB command chaining: Unlimitied\n"); else if (sc->smb_max_chain == 1) _dpd.logMsg(" Maximum SMB command chaining: No chaining allowed\n"); else _dpd.logMsg(" Maximum SMB command chaining: %u commands\n", sc->smb_max_chain); if (!DCE2_ScSmbFileInspection(sc)) { _dpd.logMsg(" SMB file inspection: Disabled\n"); } else { int64_t file_depth = DCE2_ScSmbFileDepth(sc); if (DCE2_ScSmbFileInspectionOnly(sc)) _dpd.logMsg(" SMB file inspection: Only\n"); else _dpd.logMsg(" SMB file inspection: Enabled\n"); if (file_depth == -1) _dpd.logMsg(" File depth: Disabled\n"); else if (file_depth == 0) _dpd.logMsg(" File depth: Unlimited\n"); else _dpd.logMsg(" File depth: "STDi64"\n", file_depth); } } } /********************************************************************* * Function: DCE2_ScPrintPorts() * * Used for gathering the bits set in a detect or autodetect port * array mask and displaying in a readable form. * * Arguments: * const uint8_t * * The port array mask to get the set bits from. * char * * An array to print the readable string to. * int * The size of the array to print the readable string to. * * Returns: None * *********************************************************************/ static void DCE2_ScPrintPorts(const DCE2_ServerConfig *sc, int autodetect) { unsigned int pps_idx; DCE2_PrintPortsStruct pps[5]; pps[0].trans_str = "SMB"; pps[1].trans_str = "TCP"; pps[2].trans_str = "UDP"; pps[3].trans_str = "RPC over HTTP server"; pps[4].trans_str = "RPC over HTTP proxy"; if (!autodetect) { pps[0].port_array = sc->smb_ports; pps[1].port_array = sc->tcp_ports; pps[2].port_array = sc->udp_ports; pps[3].port_array = sc->http_server_ports; pps[4].port_array = sc->http_proxy_ports; if (_dpd.isPafEnabled()) _dpd.logMsg(" Detect ports (PAF)\n"); else _dpd.logMsg(" Detect ports\n"); } else { pps[0].port_array = sc->auto_smb_ports; pps[1].port_array = sc->auto_tcp_ports; pps[2].port_array = sc->auto_udp_ports; pps[3].port_array = sc->auto_http_server_ports; pps[4].port_array = sc->auto_http_proxy_ports; if (_dpd.isPafEnabled()) _dpd.logMsg(" Autodetect ports (PAF)\n"); else _dpd.logMsg(" Autodetect ports\n"); } for (pps_idx = 0; pps_idx < sizeof(pps) / sizeof(DCE2_PrintPortsStruct); pps_idx++) { int port_start = 1; unsigned int start_port = 0, end_port = 0; unsigned int i; char ports[80]; int got_port = 0; const uint8_t *port_mask_array; snprintf(ports, sizeof(ports), " %s: ", pps[pps_idx].trans_str); ports[sizeof(ports) - 1] = '\0'; port_mask_array = pps[pps_idx].port_array; for (i = 0; i < DCE2_PORTS__MAX; i++) { if (port_start) { if (DCE2_IsPortSet(port_mask_array, (uint16_t)i)) { start_port = i; end_port = i; port_start = 0; got_port = 1; } } if (!port_start) { if (!DCE2_IsPortSet(port_mask_array, (uint16_t)i) || (i == (DCE2_PORTS__MAX - 1))) { char tmp_port[15]; /* big enough to hold a full port range */ if (i == (DCE2_PORTS__MAX - 1) && DCE2_IsPortSet(port_mask_array, (uint16_t)i)) end_port = i; /* Only print range if more than 2 ports */ if ((start_port + 1) < end_port) { snprintf(tmp_port, sizeof(tmp_port), "%u-%u ", start_port, end_port); tmp_port[sizeof(tmp_port) - 1] = '\0'; } else if (start_port < end_port) { snprintf(tmp_port, sizeof(tmp_port), "%u %u ", start_port, end_port); tmp_port[sizeof(tmp_port) - 1] = '\0'; } else { snprintf(tmp_port, sizeof(tmp_port), "%u ", start_port); tmp_port[sizeof(tmp_port) - 1] = '\0'; } if ((strlen(ports) + strlen(tmp_port)) >= sizeof(ports)) { _dpd.logMsg("%s\n", ports); snprintf(ports, sizeof(ports), " %s", tmp_port); ports[sizeof(ports) - 1] = '\0'; } else { strncat(ports, tmp_port, (sizeof(ports) - 1) - strlen(ports)); } port_start = 1; } else { end_port = i; } } } if (got_port) { _dpd.logMsg("%s\n", ports); } else { strncat(ports, "None", (sizeof(ports) - 1) - strlen(ports)); _dpd.logMsg("%s\n", ports); } } } /********************************************************************* * Function: DCE2_ScCheckTransport() * * Makes sure at least one transport for detect or autodetect. If * not there is no sense in running this policy since no detection * will ever be done. * * Arguments: * void * * Pointer to a server configuration structure. * * Returns: -1 on error * *********************************************************************/ static int DCE2_ScCheckTransport(void *data) { unsigned int i; DCE2_ServerConfig *sc = (DCE2_ServerConfig *)data; uint32_t *smb_ports = (uint32_t *)sc->smb_ports; uint32_t *tcp_ports = (uint32_t *)sc->tcp_ports; uint32_t *udp_ports = (uint32_t *)sc->udp_ports; uint32_t *http_proxy_ports = (uint32_t *)sc->http_proxy_ports; uint32_t *http_server_ports = (uint32_t *)sc->http_server_ports; uint32_t *auto_smb_ports = (uint32_t *)sc->auto_smb_ports; uint32_t *auto_tcp_ports = (uint32_t *)sc->auto_tcp_ports; uint32_t *auto_udp_ports = (uint32_t *)sc->auto_udp_ports; uint32_t *auto_http_proxy_ports = (uint32_t *)sc->auto_http_proxy_ports; uint32_t *auto_http_server_ports = (uint32_t *)sc->auto_http_server_ports; if (data == NULL) return 0; for (i = 0; i < DCE2_PORTS__MAX_INDEX >> 2; i++) { if (smb_ports[i] || tcp_ports[i] || udp_ports[i] || http_proxy_ports[i] || http_server_ports[i] || auto_smb_ports[i] || auto_tcp_ports[i] || auto_udp_ports[i] || auto_http_proxy_ports[i] || auto_http_server_ports[i]) { return 0; } } DCE2_Log(DCE2_LOG_TYPE__WARN, "%s: Must have at least one detect or autodetect transport " "enabled for a server configuration if target-based/attribute-" "table/adaptive-profiles is not enabled. However, if specific " "server configurations are configured, the default server " "configuration does not need to have any detect/autodetect " "transports configured.", DCE2_SNAME); return -1; } /********************************************************************* * Function: DCE2_ScCheckTransports() * * Makes sure at least one transport for detect or autodetect. If * not there is no sense in running this policy since no detection * will ever be done. * * Arguments: None * * Returns: -1 on error * *********************************************************************/ int DCE2_ScCheckTransports(DCE2_Config *config) { if (config == NULL) return 0; if (config->sconfigs == NULL) return DCE2_ScCheckTransport(config->dconfig); return sfrt_iterate2(config->sconfigs, DCE2_ScCheckTransport); } /********************************************************************* * Function: DCE2_ScCheckPortOverlap() * * Makes sure there are no overlapping detect ports configured. * It's okay for overlap between TCP and UDP. * Transports SMB, TCP, RPC over HTTP proxy and RPC over HTTP server * cannot define ports that overlap. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if no overlapping ports. * DCE2_RET__ERROR if overlapping ports. * *********************************************************************/ static DCE2_Ret DCE2_ScCheckPortOverlap(const DCE2_ServerConfig *sc) { unsigned int i; uint32_t *smb_ports = (uint32_t *)sc->smb_ports; uint32_t *tcp_ports = (uint32_t *)sc->tcp_ports; uint32_t *http_proxy_ports = (uint32_t *)sc->http_proxy_ports; uint32_t *http_server_ports = (uint32_t *)sc->http_server_ports; /* All port array masks should be the same size */ for (i = 0; i < sizeof(sc->smb_ports) >> 2; i++) { /* Take 4 bytes at a time and bitwise and them. Should * be 0 if there are no overlapping ports. */ uint32_t overlap = smb_ports[i] & tcp_ports[i]; uint32_t cached; if (overlap) { DCE2_ScError("Cannot have overlapping detect ports in " "smb, tcp, rpc-over-http-proxy or rpc-over-http-server. " "Overlapping port detected in tcp ports"); return DCE2_RET__ERROR; } cached = smb_ports[i] | tcp_ports[i]; overlap = http_proxy_ports[i] & cached; if (overlap) { DCE2_ScError("Cannot have overlapping detect ports in " "smb, tcp, rpc-over-http-proxy or rpc-over-http-server. " "Overlapping port detected in rpc-over-http-proxy ports"); return DCE2_RET__ERROR; } cached |= http_proxy_ports[i]; overlap = http_server_ports[i] & cached; if (overlap) { DCE2_ScError("Cannot have overlapping detect ports in " "smb, tcp, rpc-over-http-proxy or rpc-over-http-server. " "Overlapping port detected in rpc-over-http-server ports"); return DCE2_RET__ERROR; } } return DCE2_RET__SUCCESS; } /********************************************************************* * Function: DCE2_RegisterPortsWithSession() * * Add all detect ports to session dispatch table so the DCERPC2 * preprocessor is dispatched for all interested ports * * Arguments: * _SnortConfig * * Pointer to Snort Configuration structure. * DCE2_ServerConfig * * Pointer to a server configuration structure. * * Returns: None * *********************************************************************/ void DCE2_RegisterPortsWithSession( struct _SnortConfig *sc, DCE2_ServerConfig *policy ) { int port; uint8_t ports[DCE2_PORTS__MAX_INDEX]; // create bitmap of all ports set across all protocols for( port = 0; port < DCE2_PORTS__MAX_INDEX; port++ ) ports[ port ] = policy->smb_ports[ port ] | policy->tcp_ports[ port ] | policy->udp_ports[ port ] | policy->http_proxy_ports[ port ] | policy->http_server_ports[ port ] | policy->auto_smb_ports[ port ] | policy->auto_tcp_ports[ port ] | policy->auto_udp_ports[ port ] | policy->auto_http_proxy_ports[ port ] | policy->auto_http_server_ports[ port ]; // for every port enabled for any protocol, register for dispatch for (port = 0; port < DCE2_PORTS__MAX; port++) if (DCE2_IsPortSet(ports, (uint16_t)port)) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_DCE2, PROTO_BIT__TCP | PROTO_BIT__UDP, port ); } /********************************************************************* * Function: DCE2_AddPortsToStreamFilter() * * Add all detect ports to stream5 filter so stream sessions are * created. Don't do autodetect ports and rely on rules to set * any off ports. This is mainly necessary for SMB ports where * we are looking for SMB vulnerabilities in the preprocessor. * * Arguments: * DCE2_ServerConfig * * Pointer to a server configuration structure. * * Returns: None * *********************************************************************/ static void DCE2_AddPortsToStreamFilter(struct _SnortConfig *snortConf, DCE2_ServerConfig *sc, tSfPolicyId policy_id) { unsigned int port; for (port = 0; port < DCE2_PORTS__MAX; port++) { if (DCE2_IsPortSet(sc->smb_ports, (uint16_t)port)) { _dpd.streamAPI->set_port_filter_status (snortConf, IPPROTO_TCP, (uint16_t)port, PORT_MONITOR_SESSION, policy_id, 1); } if (DCE2_IsPortSet(sc->tcp_ports, (uint16_t)port)) { _dpd.streamAPI->set_port_filter_status (snortConf, IPPROTO_TCP, (uint16_t)port, PORT_MONITOR_SESSION, policy_id, 1); } if (DCE2_IsPortSet(sc->udp_ports, (uint16_t)port)) { _dpd.streamAPI->set_port_filter_status (snortConf, IPPROTO_UDP, (uint16_t)port, PORT_MONITOR_SESSION, policy_id, 1); } if (DCE2_IsPortSet(sc->http_proxy_ports, (uint16_t)port)) { _dpd.streamAPI->set_port_filter_status (snortConf, IPPROTO_TCP, (uint16_t)port, PORT_MONITOR_SESSION, policy_id, 1); } if (DCE2_IsPortSet(sc->http_server_ports, (uint16_t)port)) { _dpd.streamAPI->set_port_filter_status (snortConf, IPPROTO_TCP, (uint16_t)port, PORT_MONITOR_SESSION, policy_id, 1); } } } /******************************************************************** * Function: DCE2_ParseIpList() * * Parses an IP list, creates sfcidr_t for each IP address/net and * adds to a queue. * * Arguments: * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the IP list. * char * * Pointer to the end of the string. * DCE2_Queue * * Queue to store the sfcidr_t structures. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * IP list. * DCE2_RET__ERROR if an error occured in parsing the IP list. * ********************************************************************/ DCE2_Ret DCE2_ParseIpList(char **ptr, char *end, DCE2_Queue *ip_queue) { DCE2_IpListState state = DCE2_IP_LIST_STATE__START; sfcidr_t ip; while (*ptr < end) { char c = **ptr; if (state == DCE2_IP_LIST_STATE__END) break; switch (state) { case DCE2_IP_LIST_STATE__START: if (DCE2_IsIpChar(c)) { DCE2_Ret status = DCE2_ParseIp(ptr, end, &ip); sfcidr_t *ip_copy; if (status != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; ip_copy = (sfcidr_t *)DCE2_Alloc(sizeof(sfcidr_t), DCE2_MEM_TYPE__CONFIG); if (ip_copy == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to allocate memory for IP structure.", __FILE__, __LINE__); return DCE2_RET__ERROR; } memcpy((void *)ip_copy, (void *)&ip, sizeof(sfcidr_t)); status = DCE2_QueueEnqueue(ip_queue, ip_copy); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)ip_copy, sizeof(sfcidr_t), DCE2_MEM_TYPE__CONFIG); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to queue an IP structure.", __FILE__, __LINE__); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } else if (DCE2_IsListStartChar(c)) { state = DCE2_IP_LIST_STATE__IP_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid IP list: \"%s\"", *ptr); return DCE2_RET__ERROR; } break; case DCE2_IP_LIST_STATE__IP_START: if (DCE2_IsIpChar(c)) { DCE2_Ret status = DCE2_ParseIp(ptr, end, &ip); sfcidr_t *ip_copy; if (status != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; ip_copy = (sfcidr_t *)DCE2_Alloc(sizeof(sfcidr_t), DCE2_MEM_TYPE__CONFIG); if (ip_copy == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to allocate memory for IP structure.", __FILE__, __LINE__); return DCE2_RET__ERROR; } memcpy((void *)ip_copy, (void *)&ip, sizeof(sfcidr_t)); status = DCE2_QueueEnqueue(ip_queue, ip_copy); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)ip_copy, sizeof(sfcidr_t), DCE2_MEM_TYPE__CONFIG); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to queue an IP structure.", __FILE__, __LINE__); return DCE2_RET__ERROR; } state = DCE2_IP_LIST_STATE__IP_END; continue; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid IP list: \"%s\"", *ptr); return DCE2_RET__ERROR; } break; case DCE2_IP_LIST_STATE__IP_END: if (DCE2_IsListEndChar(c)) { state = DCE2_IP_LIST_STATE__END; } else if (DCE2_IsListSepChar(c)) { state = DCE2_IP_LIST_STATE__IP_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid IP list: \"%s\"", *ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid IP list state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } (*ptr)++; } if (state != DCE2_IP_LIST_STATE__END) { DCE2_ScError("Invalid IP list: \"%s\"", *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ParseIP() * * Parses an IP address or net. Gobbles up ':', '.', '/' and hex * digits. Not very smart, but we'll let sfip_pton take care of it. * * Arguments: * char ** * Pointer to the pointer to the current position in the string * being parsed. This is updated to the current position * after parsing the IP. * char * * Pointer to the end of the string. * sfcidr_t * * Pointer to an sfcidr_t structure that should be filled in * based on the IP or net parsed. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * IP address or net. * DCE2_RET__ERROR if an error occured in parsing the IP * address or net. * ********************************************************************/ DCE2_Ret DCE2_ParseIp(char **ptr, char *end, sfcidr_t *ip) { DCE2_IpState state = DCE2_IP_STATE__START; char *ip_start = NULL; char ip_addr[INET6_ADDRSTRLEN + 5]; /* Enough for IPv4 plus netmask or full IPv6 plus prefix */ memset(ip_addr, 0, sizeof(ip_addr)); while (*ptr < end) { char c = **ptr; switch (state) { case DCE2_IP_STATE__START: if (DCE2_IsIpChar(c)) { ip_start = *ptr; state = DCE2_IP_STATE__IP; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid IP address: \"%s\"", *ptr); return DCE2_RET__ERROR; } break; case DCE2_IP_STATE__IP: if (!DCE2_IsIpChar(c)) { int copy_len = *ptr - ip_start; DCE2_Ret status = DCE2_Memcpy(ip_addr, ip_start, copy_len, ip_addr, ip_addr + sizeof(ip_addr) - 1); if (status != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to copy IP address.", __FILE__, __LINE__); return DCE2_RET__ERROR; } /* No prefix - done with ip */ if (sfip_pton(ip_addr, ip) != SFIP_SUCCESS) { DCE2_ScError("Invalid IP address: \"%.*s\"", copy_len, ip_start); return DCE2_RET__ERROR; } /* Don't allow a zero bit mask */ if ((sfaddr_family(&ip->addr) == AF_INET && ip->bits == 96) || ip->bits == 0) { DCE2_ScError("Invalid IP address with zero bit " "prefix: \"%.*s\"", copy_len, ip_start); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } break; } (*ptr)++; } return DCE2_RET__ERROR; } /******************************************************************** * Function: DCE2_ParsePortList() * * Parses a port list and adds bits associated with the ports * parsed to a bit array. * * Arguments: * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the IP list. * char * * Pointer to the end of the string. * uint8_t * * Pointer to the port array mask to set bits for the ports * parsed. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * port list. * DCE2_RET__ERROR if an error occured in parsing the port list. * ********************************************************************/ DCE2_Ret DCE2_ParsePortList(char **ptr, char *end, uint8_t *port_array) { char *lo_start = NULL; char *hi_start = NULL; DCE2_PortListState state = DCE2_PORT_LIST_STATE__START; uint16_t lo_port = 0, hi_port = 0; int one_port = 0; while (*ptr < end) { char c = **ptr; if (state == DCE2_PORT_LIST_STATE__END) break; switch (state) { case DCE2_PORT_LIST_STATE__START: if (DCE2_IsListStartChar(c)) { state = DCE2_PORT_LIST_STATE__PORT_START; } else if (DCE2_IsPortChar(c)) { one_port = 1; lo_start = *ptr; state = DCE2_PORT_LIST_STATE__PORT_LO; } else if (DCE2_IsPortRangeChar(c)) { one_port = 1; lo_port = 0; state = DCE2_PORT_LIST_STATE__PORT_RANGE; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid port list: \"%s\"", *ptr); return DCE2_RET__ERROR; } break; case DCE2_PORT_LIST_STATE__PORT_START: lo_start = hi_start = NULL; if (DCE2_IsPortChar(c)) { lo_start = *ptr; state = DCE2_PORT_LIST_STATE__PORT_LO; } else if (DCE2_IsPortRangeChar(c)) { lo_port = 0; state = DCE2_PORT_LIST_STATE__PORT_RANGE; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid port list: \"%s\"", *ptr); return DCE2_RET__ERROR; } break; case DCE2_PORT_LIST_STATE__PORT_LO: if (!DCE2_IsPortChar(c)) { DCE2_Ret status = DCE2_GetValue(lo_start, *ptr, &lo_port, 0, DCE2_INT_TYPE__UINT16, 10); if (status != DCE2_RET__SUCCESS) { DCE2_ScError("Invalid port: \"%.*s\"", *ptr - lo_start, lo_start); return DCE2_RET__ERROR; } if (DCE2_IsPortRangeChar(c)) { state = DCE2_PORT_LIST_STATE__PORT_RANGE; } else { DCE2_SetPort(port_array, lo_port); if (one_port) return DCE2_RET__SUCCESS; state = DCE2_PORT_LIST_STATE__PORT_END; continue; } } break; case DCE2_PORT_LIST_STATE__PORT_RANGE: if (DCE2_IsPortChar(c)) { hi_start = *ptr; state = DCE2_PORT_LIST_STATE__PORT_HI; } else { DCE2_SetPortRange(port_array, lo_port, UINT16_MAX); if (one_port) return DCE2_RET__SUCCESS; state = DCE2_PORT_LIST_STATE__PORT_END; continue; } break; case DCE2_PORT_LIST_STATE__PORT_HI: if (!DCE2_IsPortChar(c)) { DCE2_Ret status = DCE2_GetValue(hi_start, *ptr, &hi_port, 0, DCE2_INT_TYPE__UINT16, 10); if (status != DCE2_RET__SUCCESS) { DCE2_ScError("Invalid port: \"%.*s\"", *ptr - hi_start, hi_start); return DCE2_RET__ERROR; } DCE2_SetPortRange(port_array, lo_port, hi_port); if (one_port) return DCE2_RET__SUCCESS; state = DCE2_PORT_LIST_STATE__PORT_END; continue; } break; case DCE2_PORT_LIST_STATE__PORT_END: if (DCE2_IsListEndChar(c)) { state = DCE2_PORT_LIST_STATE__END; } else if (DCE2_IsListSepChar(c)) { state = DCE2_PORT_LIST_STATE__PORT_START; } else if (!DCE2_IsSpaceChar(c)) { DCE2_ScError("Invalid port list: \"%s\"", *ptr); return DCE2_RET__ERROR; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid port list state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } (*ptr)++; } if (state != DCE2_PORT_LIST_STATE__END) { DCE2_ScError("Invalid port list: \"%s\"", *ptr); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ParseValue() * * Parses what should be an integer value and stores in memory * passed in as an argument. This function will parse positive * and negative values and decimal, octal or hexidecimal. The * positive and negative modifiers can only be used with * decimal values. * * Arguments: * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the IP list. * char * * Pointer to the end of the string. * void * * Pointer to the place where the value should be stored if * parsed successfully. * DCE2_IntType * The type of integer that the value pointer points to and * that should be parsed. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * integer value. * DCE2_RET__ERROR if an error occured in parsing the integer * value. * ********************************************************************/ DCE2_Ret DCE2_ParseValue(char **ptr, char *end, void *value, DCE2_IntType int_type) { char *value_start = *ptr; DCE2_ValueState state = DCE2_VALUE_STATE__START; int negate = 0; while (*ptr < end) { char c = **ptr; switch (state) { case DCE2_VALUE_STATE__START: if (c == DCE2_CFG_TOK__HEX_OCT_START) { /* Just in case it's just a 0 */ value_start = *ptr; state = DCE2_VALUE_STATE__HEX_OR_OCT; } else if (isdigit((int)c)) { value_start = *ptr; state = DCE2_VALUE_STATE__DECIMAL; } else if (c == DCE2_CFG_TOK__MINUS) { if ((int_type == DCE2_INT_TYPE__UINT8) || (int_type == DCE2_INT_TYPE__UINT16) || (int_type == DCE2_INT_TYPE__UINT32) || (int_type == DCE2_INT_TYPE__UINT64)) { return DCE2_RET__ERROR; } negate = 1; state = DCE2_VALUE_STATE__MODIFIER; } else if (c == DCE2_CFG_TOK__PLUS) { negate = 0; state = DCE2_VALUE_STATE__MODIFIER; } else if (!isspace((int)c)) /* Allow for leading space */ { return DCE2_RET__ERROR; } break; case DCE2_VALUE_STATE__MODIFIER: if (isdigit((int)c)) { value_start = *ptr; state = DCE2_VALUE_STATE__DECIMAL; } else { return DCE2_RET__ERROR; } break; case DCE2_VALUE_STATE__HEX_OR_OCT: if (tolower((int)c) == tolower((int)DCE2_CFG_TOK__HEX_SEP)) { state = DCE2_VALUE_STATE__HEX_START; } else if (isdigit((int)c)) { value_start = *ptr; state = DCE2_VALUE_STATE__OCTAL; } else { /* It's just a zero */ return DCE2_GetValue(value_start, *ptr, value, negate, int_type, 10); } break; case DCE2_VALUE_STATE__DECIMAL: if (!isdigit((int)c)) { return DCE2_GetValue(value_start, *ptr, value, negate, int_type, 10); } break; case DCE2_VALUE_STATE__HEX_START: if (isxdigit((int)c)) { value_start = *ptr; state = DCE2_VALUE_STATE__HEX; } else { return DCE2_RET__ERROR; } break; case DCE2_VALUE_STATE__HEX: if (!isxdigit((int)c)) { return DCE2_GetValue(value_start, *ptr, value, negate, int_type, 16); } break; case DCE2_VALUE_STATE__OCTAL: if (!isdigit((int)c)) { return DCE2_GetValue(value_start, *ptr, value, negate, int_type, 8); } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid value state: %d", __FILE__, __LINE__, state); return DCE2_RET__ERROR; } (*ptr)++; } // In case we hit the end before getting a non-type character. switch (state) { case DCE2_VALUE_STATE__HEX_OR_OCT: return DCE2_GetValue(value_start, end, value, negate, int_type, 8); case DCE2_VALUE_STATE__DECIMAL: return DCE2_GetValue(value_start, end, value, negate, int_type, 10); case DCE2_VALUE_STATE__HEX: return DCE2_GetValue(value_start, end, value, negate, int_type, 16); case DCE2_VALUE_STATE__OCTAL: return DCE2_GetValue(value_start, end, value, negate, int_type, 8); default: break; } /* If we break out of the loop before finishing, didn't * get a valid value */ return DCE2_RET__ERROR; } /******************************************************************** * Function: DCE2_GetValue() * * Parses integer values up to 64 bit unsigned. Stores the value * parsed in memory passed in as an argument. * * Arguments: * char * * Pointer to the first character in the string to parse. * char * * Pointer to the byte after the last character of * the string to parse. * void * * Pointer to the memory where the parsed integer should * be stored on successful parsing. * int * Non-zero if the parsed value should be negated. * Zero if the parsed value should not be negated. * DCE2_IntType * The type of integer we want to parse and the integer type * that the pointer that the parsed value will be put in is. * uint8_t * The base that the parsed value should be converted to. * Only 8, 10 and 16 are supported. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if we were able to successfully parse the * integer to the type specified. * DCE2_RET__ERROR if an error occured in parsing. * ********************************************************************/ DCE2_Ret DCE2_GetValue(char *start, char *end, void *int_value, int negate, DCE2_IntType int_type, uint8_t base) { uint64_t value = 0; uint64_t place = 1; uint64_t max_value = 0; if ((end == NULL) || (start == NULL) || (int_value == NULL)) return DCE2_RET__ERROR; if (start >= end) return DCE2_RET__ERROR; for (end = end - 1; end >= start; end--) { uint64_t add_value; char c = *end; if ((base == 16) && !isxdigit((int)c)) return DCE2_RET__ERROR; else if ((base != 16) && !isdigit((int)c)) return DCE2_RET__ERROR; if (isdigit((int)c)) add_value = (uint64_t)(c - '0') * place; else add_value = (uint64_t)((toupper((int)c) - 'A') + 10) * place; if ((UINT64_MAX - value) < add_value) return DCE2_RET__ERROR; value += add_value; place *= base; } switch (int_type) { case DCE2_INT_TYPE__INT8: max_value = ((UINT8_MAX - 1) / 2); if (negate) max_value++; break; case DCE2_INT_TYPE__UINT8: max_value = UINT8_MAX; break; case DCE2_INT_TYPE__INT16: max_value = ((UINT16_MAX - 1) / 2); if (negate) max_value++; break; case DCE2_INT_TYPE__UINT16: max_value = UINT16_MAX; break; case DCE2_INT_TYPE__INT32: max_value = ((UINT32_MAX - 1) / 2); if (negate) max_value++; break; case DCE2_INT_TYPE__UINT32: max_value = UINT32_MAX; break; case DCE2_INT_TYPE__INT64: max_value = ((UINT64_MAX - 1) / 2); if (negate) max_value++; break; case DCE2_INT_TYPE__UINT64: max_value = UINT64_MAX; break; } if (value > max_value) return DCE2_RET__ERROR; if (negate) value *= -1; switch (int_type) { case DCE2_INT_TYPE__INT8: *(int8_t *)int_value = (int8_t)value; break; case DCE2_INT_TYPE__UINT8: *(uint8_t *)int_value = (uint8_t)value; break; case DCE2_INT_TYPE__INT16: *(int16_t *)int_value = (int16_t)value; break; case DCE2_INT_TYPE__UINT16: *(uint16_t *)int_value = (uint16_t)value; break; case DCE2_INT_TYPE__INT32: *(int32_t *)int_value = (int32_t)value; break; case DCE2_INT_TYPE__UINT32: *(uint32_t *)int_value = (uint32_t)value; break; case DCE2_INT_TYPE__INT64: *(int64_t *)int_value = (int64_t)value; break; case DCE2_INT_TYPE__UINT64: *(uint64_t *)int_value = (uint64_t)value; break; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_GcError() * * Formats errors related to global configuration and puts in * global error buffer. * * Arguments: * const char * * The format string * ... * The arguments to format string * * Returns: None * ********************************************************************/ static void DCE2_GcError(const char *format, ...) { char buf[1024]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); buf[sizeof(buf) - 1] = '\0'; snprintf(dce2_config_error, sizeof(dce2_config_error), "%s(%d): \"%s\" configuration: %s. Please consult documentation.", *_dpd.config_file, *_dpd.config_line, DCE2_GNAME, buf); dce2_config_error[sizeof(dce2_config_error) - 1] = '\0'; } /******************************************************************** * Function: DCE2_ScError() * * Formats errors related to server configuration and puts in * global error buffer. * * Arguments: * const char * * The format string * ... * The arguments to format string * * Returns: None * ********************************************************************/ static void DCE2_ScError(const char *format, ...) { char buf[1024]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); buf[sizeof(buf) - 1] = '\0'; snprintf(dce2_config_error, sizeof(dce2_config_error), "%s(%d): \"%s\" configuration: %s. Please consult documentation.", *_dpd.config_file, *_dpd.config_line, DCE2_SNAME, buf); dce2_config_error[sizeof(dce2_config_error) - 1] = '\0'; } /******************************************************************** * Function: DCE2_FreeConfig * * Frees a dcerpc configuration * * Arguments: * DCE2_Config * * The configuration to free. * * Returns: None * ********************************************************************/ void DCE2_FreeConfig(DCE2_Config *config) { if (config == NULL) return; if (config->gconfig != NULL) DCE2_Free((void *)config->gconfig, sizeof(DCE2_GlobalConfig), DCE2_MEM_TYPE__CONFIG); if (config->dconfig != NULL) { if (config->dconfig->smb_invalid_shares != NULL) DCE2_ListDestroy(config->dconfig->smb_invalid_shares); DCE2_Free((void *)config->dconfig, sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); } /* Free routing tables and server configurations */ if (config->sconfigs != NULL) { /* UnRegister routing table memory */ DCE2_UnRegMem(sfrt_usage(config->sconfigs), DCE2_MEM_TYPE__RT); sfrt_cleanup(config->sconfigs, DCE2_ServerConfigCleanup); sfrt_free(config->sconfigs); } DCE2_Free((void *)config, sizeof(DCE2_Config), DCE2_MEM_TYPE__CONFIG); } /******************************************************************** * Function: DCE2_FreeConfigs * * Frees a dcerpc configuration * * Arguments: * DCE2_Config * * The configuration to free. * * Returns: None * ********************************************************************/ static int DCE2_FreeConfigsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { DCE2_Config *pPolicyConfig = (DCE2_Config *)pData; //do any housekeeping before freeing DCE22_Config sfPolicyUserDataClear (config, policyId); DCE2_FreeConfig(pPolicyConfig); return 0; } void DCE2_FreeConfigs(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, DCE2_FreeConfigsPolicy); sfPolicyConfigDelete(config); } /****************************************************************** * Function: DCE2_ServerConfigCleanup() * * Free server configurations in routing table. Each server * configuration keeps a reference count of the number of pointers * in the routing table that are pointed to it. The server * configuration is only freed when this count reaches zero. * * Arguments: * void * * Pointer to server configuration. * * Returns: None * ******************************************************************/ static void DCE2_ServerConfigCleanup(void *data) { DCE2_ServerConfig *sc = (DCE2_ServerConfig *)data; if (sc != NULL) { sc->ref_count--; if (sc->ref_count == 0) { DCE2_ListDestroy(sc->smb_invalid_shares); DCE2_Free((void *)sc, sizeof(DCE2_ServerConfig), DCE2_MEM_TYPE__CONFIG); } } } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_udp.h0000644000175000017500000000661014241075672021406 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_UDP_H_ #define _DCE2_UDP_H_ #include "dce2_cl.h" #include "dce2_session.h" #include "dce2_list.h" #include "dce2_utils.h" #include "dcerpc.h" #include "sf_snort_packet.h" #include "sf_types.h" #include "snort_debug.h" /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_UdpSsnData { DCE2_SsnData sd; DCE2_ClTracker cl_tracker; } DCE2_UdpSsnData; /******************************************************************** * Inline function prototypes ********************************************************************/ static inline DCE2_TransType DCE2_UdpAutodetect(const SFSnortPacket *); /******************************************************************** * Public function prototypes ********************************************************************/ DCE2_UdpSsnData * DCE2_UdpSsnInit(void); void DCE2_UdpProcess(DCE2_UdpSsnData *); void DCE2_UdpDataFree(DCE2_UdpSsnData *); void DCE2_UdpSsnFree(void *); /********************************************************************* * Function: DCE2_UdpAutodetect() * * Purpose: Tries to determine if a packet is likely to be DCE/RPC * over UDP. * * Arguments: * const uint8_t * - pointer to packet data. * uint16_t - packet data length. * * Returns: * DCE2_TranType * *********************************************************************/ static inline DCE2_TransType DCE2_UdpAutodetect(const SFSnortPacket *p) { if (p->payload_size >= sizeof(DceRpcClHdr)) { DceRpcClHdr *cl_hdr = (DceRpcClHdr *)p->payload; if ((DceRpcClRpcVers(cl_hdr) == DCERPC_PROTO_MAJOR_VERS__4) && ((DceRpcClPduType(cl_hdr) == DCERPC_PDU_TYPE__REQUEST) || (DceRpcClPduType(cl_hdr) == DCERPC_PDU_TYPE__RESPONSE) || (DceRpcClPduType(cl_hdr) == DCERPC_PDU_TYPE__FAULT) || (DceRpcClPduType(cl_hdr) == DCERPC_PDU_TYPE__REJECT) || (DceRpcClPduType(cl_hdr) == DCERPC_PDU_TYPE__FACK)) && ((DceRpcClLen(cl_hdr) != 0) && (DceRpcClLen(cl_hdr) + sizeof(DceRpcClHdr)) <= p->payload_size)) { return DCE2_TRANS_TYPE__UDP; } } return DCE2_TRANS_TYPE__NONE; } #endif /* _DCE2_UDP_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_roptions.c0000644000175000017500000026412614241075655022477 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_roptions.h" #include "dce2_memory.h" #include "dcerpc.h" #include "dce2_utils.h" #include "dce2_session.h" #include "sf_types.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "sf_dynamic_engine.h" #include "sf_snort_plugin_api.h" #include "sfhashfcn.h" #include "profiler.h" /******************************************************************** * Macros ********************************************************************/ #define DCE2_RTOKEN__OPT_SEP "," /* Rule option option separator */ #define DCE2_RTOKEN__ARG_SEP " \t" /* Rule option argument separator */ #define DCE2_RTOKEN__IFACE_SEP "-" /* Rule option interface separator */ #define DCE2_ROPT__IFACE "dce_iface" #define DCE2_ROPT__OPNUM "dce_opnum" #define DCE2_ROPT__STUB_DATA "dce_stub_data" #define DCE2_ROPT__BYTE_TEST "byte_test" /* Override keyword */ #define DCE2_ROPT__BYTE_JUMP "byte_jump" /* Override keyword */ #define DCE2_ROPT__BYTE_EXTRACT "byte_extract" /* Override keyword */ #define DCE2_RARG__LT '<' #define DCE2_RARG__EQ '=' #define DCE2_RARG__GT '>' #define DCE2_RARG__NE '!' #define DCE2_RARG__AND '&' #define DCE2_RARG__XOR '^' #define DCE2_RARG__ANY_FRAG "any_frag" #define DCE2_RARG__RELATIVE "relative" #define DCE2_RARG__MULTIPLIER "multiplier" #define DCE2_RARG__ALIGN "align" #define DCE2_RARG__POST_OFFSET "post_offset" #define DCE2_RARG__DCE_OVERRIDE "dce" #define DCE2_RARG__DCE_BYTEORDER "dce" #define DCE2_IFACE__MIN_ARGS 1 #define DCE2_IFACE__MAX_ARGS 3 #define DCE2_IFACE__LEN 36 /* counting the dashes */ #define DCE2_IFACE__TIME_LOW_LEN 8 #define DCE2_IFACE__TIME_MID_LEN 4 #define DCE2_IFACE__TIME_HIGH_LEN 4 #define DCE2_IFACE__CLOCK_SEQ_LEN 4 #define DCE2_IFACE__NODE_LEN 12 #define DCE2_BTEST__MIN_ARGS 4 #define DCE2_BTEST__MAX_ARGS 6 #define DCE2_BJUMP__MIN_ARGS 2 #define DCE2_BJUMP__MAX_ARGS 7 #define DCE2_OPNUM__MAX (UINT16_MAX + 1) #define DCE2_OPNUM__MAX_INDEX (DCE2_OPNUM__MAX / 8) /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_IfOp { DCE2_IF_OP__NONE = 0, DCE2_IF_OP__LT, DCE2_IF_OP__EQ, DCE2_IF_OP__GT, DCE2_IF_OP__NE } DCE2_IfOp; typedef enum _DCE2_BtOp { DCE2_BT_OP__NONE = 0, DCE2_BT_OP__LT, DCE2_BT_OP__EQ, DCE2_BT_OP__GT, DCE2_BT_OP__AND, DCE2_BT_OP__XOR } DCE2_BtOp; typedef enum _DCE2_OpnumType { DCE2_OPNUM_TYPE__SINGLE, DCE2_OPNUM_TYPE__MULTIPLE } DCE2_OpnumType; typedef enum _DCE2_OpnumListState { DCE2_OPNUM_LIST_STATE__START, DCE2_OPNUM_LIST_STATE__OPNUM_START, DCE2_OPNUM_LIST_STATE__OPNUM_LO, DCE2_OPNUM_LIST_STATE__OPNUM_RANGE, DCE2_OPNUM_LIST_STATE__OPNUM_HI, DCE2_OPNUM_LIST_STATE__OPNUM_END, DCE2_OPNUM_LIST_STATE__END } DCE2_OpnumListState; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_IfaceData { Uuid iface; uint32_t iface_vers; int iface_vers_maj; int iface_vers_min; DCE2_IfOp operator; int any_frag; } DCE2_IfaceData; typedef struct _DCE_OpnumData { DCE2_OpnumType type; } DCE2_OpnumData; typedef struct _DCE_OpnumSingle { DCE2_OpnumData odata; uint16_t opnum; } DCE2_OpnumSingle; typedef struct _DCE2_OpnumMultiple { DCE2_OpnumData odata; uint8_t *mask; uint16_t mask_size; uint16_t opnum_lo; uint16_t opnum_hi; } DCE2_OpnumMultiple; typedef struct _DCE2_ByteTestData { int num_bytes; uint32_t value; int invert; DCE2_BtOp operator; int32_t offset; /* can be negative */ int relative; } DCE2_ByteTestData; typedef struct _DCE2_ByteJumpData { int num_bytes; int32_t offset; /* can be negative */ int relative; int multiplier; int align; int32_t post_offset; /* can be negative */ } DCE2_ByteJumpData; /******************************************************************** * Private function prototypes ********************************************************************/ static int DCE2_IfaceInit(struct _SnortConfig *, char *, char *, void **); static int DCE2_OpnumInit(struct _SnortConfig *, char *, char *, void **); static void DCE2_ParseOpnumList(char **, char *, uint8_t *); static inline void DCE2_OpnumSet(uint8_t *, const uint16_t); static inline void DCE2_OpnumSetRange(uint8_t *, uint16_t, uint16_t); static inline int DCE2_OpnumIsSet(const uint8_t *, const uint16_t, const uint16_t, const uint16_t); static int DCE2_StubDataInit(struct _SnortConfig *, char *, char *, void **); static int DCE2_ByteTestInit(struct _SnortConfig *, char *, char *, void **); static int DCE2_ByteJumpInit(struct _SnortConfig *, char *, char *, void **); static void DCE2_ParseIface(char *, DCE2_IfaceData *); static int DCE2_IfaceEval(void *, const uint8_t **, void *); static int DCE2_OpnumEval(void *, const uint8_t **, void *); static int DCE2_StubDataEval(void *, const uint8_t **, void *); static int DCE2_ByteTestEval(void *, const uint8_t **, void *); static int DCE2_ByteJumpEval(void *, const uint8_t **, void *); static void DCE2_IfaceCleanup(void *); static void DCE2_OpnumCleanup(void *); static void DCE2_ByteTestCleanup(void *); static void DCE2_ByteJumpCleanup(void *); static uint32_t DCE2_IfaceHash(void *); static uint32_t DCE2_OpnumHash(void *); static uint32_t DCE2_ByteTestHash(void *); static uint32_t DCE2_ByteJumpHash(void *); static int DCE2_IfaceKeyCompare(void *, void *); static int DCE2_OpnumKeyCompare(void *, void *); static int DCE2_ByteTestKeyCompare(void *, void *); static int DCE2_ByteJumpKeyCompare(void *, void *); static inline int DCE2_RoptDoEval(SFSnortPacket *); static NORETURN void DCE2_RoptError(const char *, ...); static inline void * DCE2_AllocFp(uint32_t); static int DCE2_IfaceAddFastPatterns(void *, int, int, FPContentInfo **); /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_RegRuleOptions(struct _SnortConfig *sc) { _dpd.preprocOptRegister(sc, DCE2_ROPT__IFACE, DCE2_IfaceInit, DCE2_IfaceEval, DCE2_IfaceCleanup, DCE2_IfaceHash, DCE2_IfaceKeyCompare, NULL, DCE2_IfaceAddFastPatterns); _dpd.preprocOptRegister(sc, DCE2_ROPT__OPNUM, DCE2_OpnumInit, DCE2_OpnumEval, DCE2_OpnumCleanup, DCE2_OpnumHash, DCE2_OpnumKeyCompare, NULL, NULL); _dpd.preprocOptRegister(sc, DCE2_ROPT__STUB_DATA, DCE2_StubDataInit, DCE2_StubDataEval, NULL, NULL, NULL, NULL, NULL); _dpd.preprocOptOverrideKeyword(sc, DCE2_ROPT__BYTE_TEST, DCE2_RARG__DCE_OVERRIDE, DCE2_ByteTestInit, DCE2_ByteTestEval, DCE2_ByteTestCleanup, DCE2_ByteTestHash, DCE2_ByteTestKeyCompare, NULL, NULL); _dpd.preprocOptOverrideKeyword(sc, DCE2_ROPT__BYTE_JUMP, DCE2_RARG__DCE_OVERRIDE, DCE2_ByteJumpInit, DCE2_ByteJumpEval, DCE2_ByteJumpCleanup, DCE2_ByteJumpHash, DCE2_ByteJumpKeyCompare, NULL, NULL); _dpd.preprocOptByteOrderKeyword(DCE2_RARG__DCE_BYTEORDER, DCE2_GetByteOrder); } /******************************************************************** * Function: DCE2_IfaceInit() * * Parses dce_iface rule option. * * XXX Connectionless uses a 32bit version, connection-oriented * a 16bit major version and 16bit minor version. Not likely to * need to support versions greater than 65535, but may need to * support minor version. * * Arguments: * char * * Name of rule option. * char * * Arguments for rule option. * data ** * Variable for saving rule option structure. * * Returns: * 1 if successful * 0 if name is not dce_iface * Fatal errors if invalid arguments. * ********************************************************************/ static int DCE2_IfaceInit(struct _SnortConfig *sc, char *name, char *args, void **data) { char *token, *saveptr = NULL; int iface_vers = 0, any_frag = 0; int tok_num = 0; DCE2_IfaceData *iface_data; if (strcasecmp(name, DCE2_ROPT__IFACE) != 0) return 0; iface_data = (DCE2_IfaceData *)DCE2_Alloc(sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); if (iface_data == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for iface data structure.", __FILE__, __LINE__); } iface_data->operator = DCE2_IF_OP__NONE; /* Must have arguments */ if (DCE2_IsEmptyStr(args)) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option requires arguments.", DCE2_ROPT__IFACE); } /* Get argument */ token = strtok_r(args, DCE2_RTOKEN__OPT_SEP, &saveptr); if (token == NULL) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_Die("%s(%d) strtok_r() returned NULL when string argument " "was not NULL.", __FILE__, __LINE__); } do { tok_num++; token = DCE2_PruneWhiteSpace(token); if (tok_num == 1) /* Iface uuid */ { DCE2_ParseIface(token, iface_data); } else if ((tok_num > DCE2_IFACE__MIN_ARGS) && (tok_num <= DCE2_IFACE__MAX_ARGS)) { int try_any_frag = 0; /* Need at least two bytes */ if (strlen(token) < 2) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s.", DCE2_ROPT__IFACE, token); } switch (*token) { case DCE2_RARG__LT: iface_data->operator = DCE2_IF_OP__LT; break; case DCE2_RARG__EQ: iface_data->operator = DCE2_IF_OP__EQ; break; case DCE2_RARG__GT: iface_data->operator = DCE2_IF_OP__GT; break; case DCE2_RARG__NE: iface_data->operator = DCE2_IF_OP__NE; break; default: try_any_frag = 1; } if (!try_any_frag) { char *endptr; if (iface_vers) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Cannot configure interface " "version more than once.", DCE2_ROPT__IFACE); } token++; iface_data->iface_vers = _dpd.SnortStrtoul(token, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s.", DCE2_ROPT__IFACE, token); } switch (iface_data->operator) { case DCE2_IF_OP__LT: if (iface_data->iface_vers == 0) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Interface version " "cannot be less than zero.", DCE2_ROPT__IFACE); } else if (iface_data->iface_vers > (UINT16_MAX + 1)) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Interface version " "cannot be greater than %u.", DCE2_ROPT__IFACE, UINT16_MAX); } break; case DCE2_IF_OP__EQ: case DCE2_IF_OP__NE: if (iface_data->iface_vers > UINT16_MAX) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Interface version " "cannot be greater than %u.", DCE2_ROPT__IFACE, UINT16_MAX); } break; case DCE2_IF_OP__GT: if (iface_data->iface_vers >= UINT16_MAX) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Interface version " "cannot be greater than %u.", DCE2_ROPT__IFACE, UINT16_MAX); } break; default: /* Shouldn't get here */ DCE2_Die("%s(%d) Invalid operator: %d", __FILE__, __LINE__, iface_data->operator); break; } if (iface_data->iface_vers <= UINT16_MAX) iface_data->iface_vers_maj = (int)iface_data->iface_vers; else iface_data->iface_vers_maj = DCE2_SENTINEL; iface_vers = 1; } else { if (any_frag) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Cannot configure " "\"%s\" more than once.", DCE2_ROPT__IFACE, DCE2_RARG__ANY_FRAG); } if (strcasecmp(token, DCE2_RARG__ANY_FRAG) == 0) { iface_data->any_frag = 1; } else { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s.", DCE2_ROPT__IFACE, token); } any_frag = 1; } } else { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Too many arguments.", DCE2_ROPT__IFACE); } } while ((token = strtok_r(NULL, DCE2_RTOKEN__OPT_SEP, &saveptr)) != NULL); *data = (void *)iface_data; return 1; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_ParseIface(char *token, DCE2_IfaceData *iface_data) { char *iface, *ifaceptr = NULL; char *if_hex, *if_hexptr = NULL; int num_pieces = 0; /* Has to be a uuid in string format, e.g 4b324fc8-1670-01d3-1278-5a47bf6ee188 * Check the length */ if (strlen(token) != DCE2_IFACE__LEN) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } /* Detach token */ iface = strtok_r(token, DCE2_RTOKEN__ARG_SEP, &ifaceptr); if (iface == NULL) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_Die("%s(%d) strtok_r() returned NULL when string argument " "was not NULL.", __FILE__, __LINE__); } /* Cut into pieces separated by '-' */ if_hex = strtok_r(iface, DCE2_RTOKEN__IFACE_SEP, &if_hexptr); if (if_hex == NULL) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_Die("%s(%d) strtok_r() returned NULL when string argument " "was not NULL.", __FILE__, __LINE__); } do { char *endptr; switch (num_pieces) { case 0: { unsigned long int time_low; if (strlen(if_hex) != DCE2_IFACE__TIME_LOW_LEN) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } time_low = _dpd.SnortStrtoul(if_hex, &endptr, 16); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } iface_data->iface.time_low = (uint32_t)time_low; } break; case 1: { unsigned long int time_mid; if (strlen(if_hex) != DCE2_IFACE__TIME_MID_LEN) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } time_mid = _dpd.SnortStrtoul(if_hex, &endptr, 16); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } /* Length check ensures 16 bit value */ iface_data->iface.time_mid = (uint16_t)time_mid; } break; case 2: { unsigned long int time_high; if (strlen(if_hex) != DCE2_IFACE__TIME_HIGH_LEN) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } time_high = _dpd.SnortStrtoul(if_hex, &endptr, 16); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } /* Length check ensures 16 bit value */ iface_data->iface.time_high_and_version = (uint16_t)time_high; } break; case 3: { unsigned long int clock_seq_and_reserved, clock_seq_low; if (strlen(if_hex) != DCE2_IFACE__CLOCK_SEQ_LEN) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } /* Work backwards */ clock_seq_low = _dpd.SnortStrtoul(&if_hex[2], &endptr, 16); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } iface_data->iface.clock_seq_low = (uint8_t)clock_seq_low; /* Set third byte to null so we can _dpd.SnortStrtoul the first part */ if_hex[2] = '\x00'; clock_seq_and_reserved = _dpd.SnortStrtoul(if_hex, &endptr, 16); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } iface_data->iface.clock_seq_and_reserved = (uint8_t)clock_seq_and_reserved; } break; case 4: { int i, j; if (strlen(if_hex) != DCE2_IFACE__NODE_LEN) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } /* Walk back a byte at a time - 2 hex digits */ for (i = DCE2_IFACE__NODE_LEN - 2, j = sizeof(iface_data->iface.node) - 1; (i >= 0) && (j >= 0); i -= 2, j--) { /* Only giving _dpd.SnortStrtoul 1 byte */ iface_data->iface.node[j] = (uint8_t)_dpd.SnortStrtoul(&if_hex[i], &endptr, 16); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } if_hex[i] = '\0'; } } break; default: break; } num_pieces++; } while ((if_hex = strtok_r(NULL, DCE2_RTOKEN__IFACE_SEP, &if_hexptr)) != NULL); if (num_pieces != 5) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } /* Check for more arguments */ iface = strtok_r(NULL, DCE2_RTOKEN__ARG_SEP, &ifaceptr); if (iface != NULL) { DCE2_Free((void *)iface_data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid uuid.", DCE2_ROPT__IFACE); } } static inline void * DCE2_AllocFp(uint32_t size) { void *mem = calloc(1, (size_t)size); if (mem == NULL) { DCE2_Die("%s(%d) Out of memory!", __FILE__, __LINE__); } return mem; } static int DCE2_IfaceAddFastPatterns(void *rule_opt_data, int protocol, int direction, FPContentInfo **info) { DCE2_IfaceData *iface_data = (DCE2_IfaceData *)rule_opt_data; if ((rule_opt_data == NULL) || (info == NULL)) return -1; if ((protocol != IPPROTO_TCP) && (protocol != IPPROTO_UDP)) return -1; if (protocol == IPPROTO_TCP) { FPContentInfo *tcp_fp = (FPContentInfo *)DCE2_AllocFp(sizeof(FPContentInfo)); char *client_fp = "\x05\x00\x00"; char *server_fp = "\x05\x00\x02"; char *no_dir_fp = "\x05\x00"; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Adding fast pattern " "content for TCP rule option.\n")); switch (direction) { case FLAG_FROM_CLIENT: tcp_fp->content = (char *)DCE2_AllocFp(3); memcpy(tcp_fp->content, client_fp, 3); tcp_fp->length = 3; break; case FLAG_FROM_SERVER: tcp_fp->content = (char *)DCE2_AllocFp(3); memcpy(tcp_fp->content, server_fp, 3); tcp_fp->length = 3; break; default: tcp_fp->content = (char *)DCE2_AllocFp(2); memcpy(tcp_fp->content, no_dir_fp, 2); tcp_fp->length = 2; break; } *info = tcp_fp; } else { //DCE2_IfaceData *iface_data = (DCE2_IfaceData *)rule_opt_data; FPContentInfo *big_fp = (FPContentInfo *)DCE2_AllocFp(sizeof(FPContentInfo)); FPContentInfo *little_fp = (FPContentInfo *)DCE2_AllocFp(sizeof(FPContentInfo)); char *big_content = (char *)DCE2_AllocFp(sizeof(Uuid)); char *little_content = (char *)DCE2_AllocFp(sizeof(Uuid)); uint32_t time32; uint16_t time16; int index = 0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Adding fast pattern " "content for UDP rule option.\n")); time32 = DceRpcNtohl(&iface_data->iface.time_low, DCERPC_BO_FLAG__BIG_ENDIAN); memcpy(&big_content[index], &time32, sizeof(uint32_t)); time32 = DceRpcNtohl(&iface_data->iface.time_low, DCERPC_BO_FLAG__LITTLE_ENDIAN); memcpy(&little_content[index], &time32, sizeof(uint32_t)); index += sizeof(uint32_t); time16 = DceRpcNtohs(&iface_data->iface.time_mid, DCERPC_BO_FLAG__BIG_ENDIAN); memcpy(&big_content[index], &time16, sizeof(uint16_t)); time16 = DceRpcNtohs(&iface_data->iface.time_mid, DCERPC_BO_FLAG__LITTLE_ENDIAN); memcpy(&little_content[index], &time16, sizeof(uint16_t)); index += sizeof(uint16_t); time16 = DceRpcNtohs(&iface_data->iface.time_high_and_version, DCERPC_BO_FLAG__BIG_ENDIAN); memcpy(&big_content[index], &time16, sizeof(uint16_t)); time16 = DceRpcNtohs(&iface_data->iface.time_high_and_version, DCERPC_BO_FLAG__LITTLE_ENDIAN); memcpy(&little_content[index], &time16, sizeof(uint16_t)); index += sizeof(uint16_t); big_content[index] = iface_data->iface.clock_seq_and_reserved; little_content[index] = iface_data->iface.clock_seq_and_reserved; index += sizeof(uint8_t); big_content[index] = iface_data->iface.clock_seq_low; little_content[index] = iface_data->iface.clock_seq_low; index += sizeof(uint8_t); memcpy(&big_content[index], iface_data->iface.node, 6); memcpy(&little_content[index], iface_data->iface.node, 6); big_fp->content = big_content; big_fp->length = sizeof(Uuid); little_fp->content = little_content; little_fp->length = sizeof(Uuid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, " Iface: %s\n Big endian: %s\n Little endian: %s\n", DCE2_UuidToStr(&iface_data->iface, DCERPC_BO_FLAG__NONE), DCE2_UuidToStr((Uuid *)big_fp->content, DCERPC_BO_FLAG__NONE), DCE2_UuidToStr((Uuid *)little_fp->content, DCERPC_BO_FLAG__NONE));); big_fp->next = little_fp; *info = big_fp; } return 0; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_OpnumInit(struct _SnortConfig *sc, char *name, char *args, void **data) { uint8_t opnum_mask[DCE2_OPNUM__MAX_INDEX]; /* 65536 bits */ char *args_end; uint16_t num_opnums = 0; unsigned int i; int opnum_lo = DCE2_SENTINEL; int opnum_hi = 0; if (strcasecmp(name, DCE2_ROPT__OPNUM) != 0) return 0; /* Must have arguments */ if (DCE2_IsEmptyStr(args)) { DCE2_RoptError("\"%s\" rule option: No arguments. Must supply " "the value of the opnum.", DCE2_ROPT__OPNUM); } /* Include NULL byte for parsing */ args_end = args + (strlen(args) + 1); memset(opnum_mask, 0, sizeof(opnum_mask)); DCE2_ParseOpnumList(&args, args_end, opnum_mask); /* Must have at least one bit set or the parsing would have errored */ for (i = 0; i < DCE2_OPNUM__MAX; i++) { if (DCE2_OpnumIsSet(opnum_mask, 0, DCE2_OPNUM__MAX - 1, (uint16_t)i)) { num_opnums++; if (opnum_lo == DCE2_SENTINEL) opnum_lo = (uint16_t)i; opnum_hi = (uint16_t)i; } } if (num_opnums == 1) { DCE2_OpnumSingle *odata = (DCE2_OpnumSingle *)DCE2_Alloc(sizeof(DCE2_OpnumSingle), DCE2_MEM_TYPE__ROPTION); if (odata == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for opnum data.", __FILE__, __LINE__); } odata->odata.type = DCE2_OPNUM_TYPE__SINGLE; odata->opnum = (uint16_t)opnum_lo; *data = (void *)odata; } else { int opnum_range = opnum_hi - opnum_lo; int mask_size = (opnum_range / 8) + 1; DCE2_OpnumMultiple *odata = (DCE2_OpnumMultiple *)DCE2_Alloc(sizeof(DCE2_OpnumMultiple), DCE2_MEM_TYPE__ROPTION); if (odata == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for opnum data.", __FILE__, __LINE__); } odata->mask = (uint8_t *)DCE2_Alloc(mask_size, DCE2_MEM_TYPE__ROPTION); if (odata->mask == NULL) { DCE2_Free((void *)odata, sizeof(DCE2_OpnumMultiple), DCE2_MEM_TYPE__ROPTION); DCE2_Die("%s(%d) Failed to allocate memory for opnum data.", __FILE__, __LINE__); } odata->odata.type = DCE2_OPNUM_TYPE__MULTIPLE; odata->mask_size = (uint16_t)mask_size; odata->opnum_lo = (uint16_t)opnum_lo; odata->opnum_hi = (uint16_t)opnum_hi; /* Set the opnum bits in our reduced size opnum mask */ for (i = (unsigned int)opnum_lo; i <= (unsigned int)opnum_hi; i++) { if (DCE2_OpnumIsSet(opnum_mask, 0, DCE2_OPNUM__MAX - 1, (uint16_t)i)) DCE2_OpnumSet(odata->mask, (uint16_t)(i - opnum_lo)); } *data = (void *)odata; } return 1; } /******************************************************************** * Function: * * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_ParseOpnumList(char **ptr, char *end, uint8_t *opnum_mask) { char *lo_start = NULL; char *hi_start = NULL; DCE2_OpnumListState state = DCE2_OPNUM_LIST_STATE__START; uint16_t lo_opnum = 0, hi_opnum = 0; while (*ptr < end) { char c = **ptr; if (state == DCE2_OPNUM_LIST_STATE__END) break; switch (state) { case DCE2_OPNUM_LIST_STATE__START: if (DCE2_IsOpnumChar(c)) { lo_start = *ptr; state = DCE2_OPNUM_LIST_STATE__OPNUM_LO; } else if (!DCE2_IsSpaceChar(c)) { DCE2_RoptError("\"%s\" rule option: Invalid opnum list: %s.", DCE2_ROPT__OPNUM, *ptr); } break; case DCE2_OPNUM_LIST_STATE__OPNUM_LO: if (!DCE2_IsOpnumChar(c)) { DCE2_Ret status = DCE2_GetValue(lo_start, *ptr, &lo_opnum, 0, DCE2_INT_TYPE__UINT16, 10); if (status != DCE2_RET__SUCCESS) { DCE2_RoptError("\"%s\" rule option: Invalid opnum: %.*s", DCE2_ROPT__OPNUM, *ptr - lo_start, lo_start); } if (DCE2_IsOpnumRangeChar(c)) { state = DCE2_OPNUM_LIST_STATE__OPNUM_RANGE; } else { DCE2_OpnumSet(opnum_mask, lo_opnum); state = DCE2_OPNUM_LIST_STATE__OPNUM_END; continue; } } break; case DCE2_OPNUM_LIST_STATE__OPNUM_RANGE: if (DCE2_IsOpnumChar(c)) { hi_start = *ptr; state = DCE2_OPNUM_LIST_STATE__OPNUM_HI; } else { DCE2_OpnumSetRange(opnum_mask, lo_opnum, UINT16_MAX); state = DCE2_OPNUM_LIST_STATE__OPNUM_END; continue; } break; case DCE2_OPNUM_LIST_STATE__OPNUM_HI: if (!DCE2_IsOpnumChar(c)) { DCE2_Ret status = DCE2_GetValue(hi_start, *ptr, &hi_opnum, 0, DCE2_INT_TYPE__UINT16, 10); if (status != DCE2_RET__SUCCESS) { DCE2_RoptError("\"%s\" rule option: Invalid opnum: %.*s", DCE2_ROPT__OPNUM, *ptr - hi_start, hi_start); } DCE2_OpnumSetRange(opnum_mask, lo_opnum, hi_opnum); state = DCE2_OPNUM_LIST_STATE__OPNUM_END; continue; } break; case DCE2_OPNUM_LIST_STATE__OPNUM_END: if (DCE2_IsListSepChar(c)) { state = DCE2_OPNUM_LIST_STATE__START; } else if (DCE2_IsConfigEndChar(c)) { state = DCE2_OPNUM_LIST_STATE__END; continue; } else if (!DCE2_IsSpaceChar(c)) { DCE2_RoptError("\"%s\" rule option: Invalid opnum list: %s.", DCE2_ROPT__OPNUM, *ptr); } break; default: DCE2_Die("%s(%d) Invalid opnum list state: %d", __FILE__, __LINE__, state); break; } (*ptr)++; } if (state != DCE2_OPNUM_LIST_STATE__END) { DCE2_RoptError("\"%s\" rule option: Invalid opnum list: %s", DCE2_ROPT__OPNUM, *ptr); } } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DCE2_OpnumIsSet(const uint8_t *opnum_mask, const uint16_t opnum_lo, const uint16_t opnum_hi, const uint16_t opnum) { uint16_t otmp = opnum - opnum_lo; if ((opnum < opnum_lo) || (opnum > opnum_hi)) return 0; return opnum_mask[(otmp / 8)] & (1 << (otmp % 8)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_OpnumSet(uint8_t *opnum_mask, const uint16_t opnum) { opnum_mask[(opnum / 8)] |= (1 << (opnum % 8)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_OpnumSetRange(uint8_t *opnum_mask, uint16_t lo_opnum, uint16_t hi_opnum) { uint16_t i; if (lo_opnum > hi_opnum) { uint16_t tmp = lo_opnum; lo_opnum = hi_opnum; hi_opnum = tmp; } for (i = lo_opnum; i <= hi_opnum; i++) DCE2_OpnumSet(opnum_mask, i); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_StubDataInit(struct _SnortConfig *sc, char *name, char *args, void **data) { if (strcasecmp(name, DCE2_ROPT__STUB_DATA) != 0) return 0; /* Must not have arguments */ if (!DCE2_IsEmptyStr(args)) { DCE2_RoptError("\"%s\" rule option: This option has no arguments.", DCE2_ROPT__STUB_DATA); } /* Set it to something even though we don't need it */ *data = (void *)1; return 1; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_ByteTestInit(struct _SnortConfig *sc, char *name, char *args, void **data) { char *token, *saveptr = NULL; int tok_num = 0; DCE2_ByteTestData *bt_data; if (strcasecmp(name, DCE2_ROPT__BYTE_TEST) != 0) return 0; bt_data = (DCE2_ByteTestData *)DCE2_Alloc(sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); if (bt_data == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for byte test data structure.", __FILE__, __LINE__); } bt_data->operator = DCE2_BT_OP__NONE; /* Must have arguments */ if (DCE2_IsEmptyStr(args)) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: No arguments.", DCE2_ROPT__BYTE_TEST); } /* Get argument */ token = strtok_r(args, DCE2_RTOKEN__OPT_SEP, &saveptr); if (token == NULL) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_Die("%s(%d) strtok_r() returned NULL when string argument " "was not NULL.", __FILE__, __LINE__); } do { tok_num++; token = DCE2_PruneWhiteSpace(token); if (tok_num == 1) /* Number of bytes to convert */ { char *endptr; unsigned long int num_bytes = _dpd.SnortStrtoul(token, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid number of bytes to " "convert: %s. Should be one of 1, 2 or 4.", DCE2_ROPT__BYTE_TEST, token); } if ((num_bytes != 1) && (num_bytes != 2) && (num_bytes != 4)) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid number of bytes to " "convert: %s. Should be one of 1, 2 or 4.", DCE2_ROPT__BYTE_TEST, token); } bt_data->num_bytes = num_bytes; } else if (tok_num == 2) /* Operator */ { /* Should only be one byte */ if (strlen(token) > 2) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s", DCE2_ROPT__BYTE_TEST, token); } /* If two bytes first must be '!' */ if (strlen(token) == 2) { if (*token != DCE2_RARG__NE) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s", DCE2_ROPT__BYTE_TEST, token); } else { bt_data->invert = 1; } token++; } switch (*token) { case DCE2_RARG__LT: bt_data->operator = DCE2_BT_OP__LT; break; case DCE2_RARG__EQ: bt_data->operator = DCE2_BT_OP__EQ; break; case DCE2_RARG__GT: bt_data->operator = DCE2_BT_OP__GT; break; case DCE2_RARG__AND: bt_data->operator = DCE2_BT_OP__AND; break; case DCE2_RARG__XOR: bt_data->operator = DCE2_BT_OP__XOR; break; default: DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s", DCE2_ROPT__BYTE_TEST, token); break; } } else if (tok_num == 3) /* Value to compare to */ { char *endptr; unsigned long int value = _dpd.SnortStrtoul(token, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (value > UINT32_MAX)) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid compare value: %s. Must be " "between 0 and %u inclusive.", DCE2_ROPT__BYTE_TEST, token, UINT32_MAX); } bt_data->value = value; } else if (tok_num == 4) /* Offset in packet data */ { char *endptr; long int offset = _dpd.SnortStrtol(token, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (offset > (long int)UINT16_MAX) || (offset < (-1 * (long int)UINT16_MAX))) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid offset: %s. Must be " "between -%u and %u inclusive.", DCE2_ROPT__BYTE_TEST, token, UINT16_MAX, UINT16_MAX); } bt_data->offset = offset; } else if ((tok_num == 5) || (tok_num == 6)) { if (strcasecmp(token, DCE2_RARG__RELATIVE) == 0) { /* Can't configure it twice */ if (bt_data->relative) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Can't configure \"%s\" " "more than once.", DCE2_ROPT__BYTE_TEST, DCE2_RARG__RELATIVE); } bt_data->relative = 1; } else if (strcasecmp(token, DCE2_RARG__DCE_OVERRIDE) != 0) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s.", DCE2_ROPT__BYTE_TEST, token); } } else { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Too many arguments.", DCE2_ROPT__BYTE_TEST); } } while ((token = strtok_r(NULL, DCE2_RTOKEN__OPT_SEP, &saveptr)) != NULL); if (tok_num < DCE2_BTEST__MIN_ARGS) { DCE2_Free((void *)bt_data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Not enough arguments.", DCE2_ROPT__BYTE_TEST); } *data = (void *)bt_data; return 1; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_ByteJumpInit(struct _SnortConfig *sc, char *name, char *args, void **data) { char *token, *saveptr = NULL; int tok_num = 0; DCE2_ByteJumpData *bj_data; int post_offset_configured = 0; if (strcasecmp(name, DCE2_ROPT__BYTE_JUMP) != 0) return 0; bj_data = (DCE2_ByteJumpData *)DCE2_Alloc(sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); if (bj_data == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for byte jump data structure.", __FILE__, __LINE__); } bj_data->multiplier = DCE2_SENTINEL; /* Must have arguments */ if (DCE2_IsEmptyStr(args)) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: No arguments.", DCE2_ROPT__BYTE_JUMP); } /* Get argument */ token = strtok_r(args, DCE2_RTOKEN__OPT_SEP, &saveptr); if (token == NULL) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_Die("%s(%d) strtok_r() returned NULL when string argument " "was not NULL.", __FILE__, __LINE__); } do { tok_num++; token = DCE2_PruneWhiteSpace(token); if (tok_num == 1) /* Number of bytes to convert */ { char *endptr; unsigned long int num_bytes = _dpd.SnortStrtoul(token, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid number of bytes to " "convert: %s. Should be one of 1, 2 or 4.", DCE2_ROPT__BYTE_JUMP, token); } if ((num_bytes != 4) && (num_bytes != 2) && (num_bytes != 1)) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid number of bytes to " "convert: %s. Should be one of 1, 2 or 4.", DCE2_ROPT__BYTE_JUMP, token); } bj_data->num_bytes = num_bytes; } else if (tok_num == 2) /* Offset in packet data */ { char *endptr; long int offset = _dpd.SnortStrtol(token, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (offset > (long int)UINT16_MAX) || (offset < (-1 * (long int)UINT16_MAX))) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid offset: %s. Must be " "between -%u and %u inclusive.", DCE2_ROPT__BYTE_JUMP, token, UINT16_MAX, UINT16_MAX); } bj_data->offset = offset; } else if ((tok_num > DCE2_BJUMP__MIN_ARGS) && (tok_num <= DCE2_BJUMP__MAX_ARGS)) { char *arg, *argptr; /* Detach arg to get potenial sub-arg */ arg = strtok_r(token, DCE2_RTOKEN__ARG_SEP, &argptr); if (arg == NULL) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_Die("%s(%d) strtok_r() returned NULL when string argument " "was not NULL.", __FILE__, __LINE__); } if (strcasecmp(arg, DCE2_RARG__RELATIVE) == 0) { /* Can't configure it twice */ if (bj_data->relative) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Can't configure \"%s\" " "more than once.", DCE2_ROPT__BYTE_TEST, DCE2_RARG__RELATIVE); } bj_data->relative = 1; } else if (strcasecmp(arg, DCE2_RARG__ALIGN) == 0) { if (bj_data->align) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Can't configure \"%s\" " "more than once.", DCE2_ROPT__BYTE_TEST, DCE2_RARG__ALIGN); } bj_data->align = 1; } else if (strcasecmp(arg, DCE2_RARG__MULTIPLIER) == 0) { char *endptr; unsigned long int multiplier; if (bj_data->multiplier != DCE2_SENTINEL) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Can't configure \"%s\" " "more than once.", DCE2_ROPT__BYTE_TEST, DCE2_RARG__MULTIPLIER); } arg = strtok_r(NULL, DCE2_RTOKEN__ARG_SEP, &argptr); if (arg == NULL) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: \"%s\" requires an argument.", DCE2_ROPT__BYTE_JUMP, DCE2_RARG__MULTIPLIER); } multiplier = _dpd.SnortStrtoul(arg, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (multiplier <= 1) || (multiplier > UINT16_MAX)) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid multiplier: %s. " "Must be between 2 and %u inclusive.", DCE2_ROPT__BYTE_JUMP, arg, UINT16_MAX); } bj_data->multiplier = multiplier; } else if (strcasecmp(arg, DCE2_RARG__POST_OFFSET) == 0) { char *endptr; long int post_offset; if (post_offset_configured) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Can't configure \"%s\" " "more than once.", DCE2_ROPT__BYTE_TEST, DCE2_RARG__POST_OFFSET); } arg = strtok_r(NULL, DCE2_RTOKEN__ARG_SEP, &argptr); if (arg == NULL) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: \"%s\" requires an argument.", DCE2_ROPT__BYTE_JUMP, DCE2_RARG__POST_OFFSET); } post_offset = _dpd.SnortStrtol(arg, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (post_offset > (long int)UINT16_MAX) || (post_offset < (-1 * (long int)UINT16_MAX))) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid post offset " "value: %s. Must be between -%u to %u inclusive", DCE2_ROPT__BYTE_JUMP, arg, UINT16_MAX, UINT16_MAX); } bj_data->post_offset = post_offset; post_offset_configured = 1; } else if (strcasecmp(arg, DCE2_RARG__DCE_OVERRIDE) != 0) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Invalid argument: %s.", DCE2_ROPT__BYTE_JUMP, arg); } } else { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Too many arguments.", DCE2_ROPT__BYTE_JUMP); } } while ((token = strtok_r(NULL, DCE2_RTOKEN__OPT_SEP, &saveptr)) != NULL); if (tok_num < DCE2_BJUMP__MIN_ARGS) { DCE2_Free((void *)bj_data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); DCE2_RoptError("\"%s\" rule option: Not enough arguments.", DCE2_ROPT__BYTE_JUMP); } *data = (void *)bj_data; return 1; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_IfaceEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; DCE2_SsnData *sd; DCE2_Roptions *ropts; DCE2_IfaceData *iface_data; int ret = RULE_NOMATCH; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Evaluating \"%s\" rule option.\n", DCE2_ROPT__IFACE)); if (!DCE2_RoptDoEval(p)) return RULE_NOMATCH; sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_DCE2); if ((sd == NULL) || DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if (ropts->first_frag == DCE2_SENTINEL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "First frag not set - not evaluating.\n")); return RULE_NOMATCH; } iface_data = (DCE2_IfaceData *)data; if (iface_data == NULL) return RULE_NOMATCH; if (!iface_data->any_frag && !ropts->first_frag) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Not a first fragment and rule set to only look at " "first fragment.\n")); return RULE_NOMATCH; } /* Compare the uuid */ DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Comparing \"%s\" to \"%s\"\n", DCE2_UuidToStr(&ropts->iface, DCERPC_BO_FLAG__NONE), DCE2_UuidToStr(&iface_data->iface, DCERPC_BO_FLAG__NONE))); if (DCE2_UuidCompare((void *)&ropts->iface, (void *)&iface_data->iface) != 0) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Uuids don't match\n")); return RULE_NOMATCH; } if (iface_data->operator == DCE2_IF_OP__NONE) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Match\n", DCE2_ROPT__IFACE)); return RULE_MATCH; } switch (iface_data->operator) { case DCE2_IF_OP__LT: if (IsTCP(p) && (iface_data->iface_vers_maj != DCE2_SENTINEL)) { if ((int)ropts->iface_vers_maj < iface_data->iface_vers_maj) ret = RULE_MATCH; } else { if (ropts->iface_vers < iface_data->iface_vers) ret = RULE_MATCH; } break; case DCE2_IF_OP__EQ: if (IsTCP(p) && (iface_data->iface_vers_maj != DCE2_SENTINEL)) { if ((int)ropts->iface_vers_maj == iface_data->iface_vers_maj) ret = RULE_MATCH; } else { if (ropts->iface_vers == iface_data->iface_vers) ret = RULE_MATCH; } break; case DCE2_IF_OP__GT: if (IsTCP(p) && (iface_data->iface_vers_maj != DCE2_SENTINEL)) { if ((int)ropts->iface_vers_maj > iface_data->iface_vers_maj) ret = RULE_MATCH; } else { if (ropts->iface_vers > iface_data->iface_vers) ret = RULE_MATCH; } break; case DCE2_IF_OP__NE: if (IsTCP(p) && (iface_data->iface_vers_maj != DCE2_SENTINEL)) { if ((int)ropts->iface_vers_maj != iface_data->iface_vers_maj) ret = RULE_MATCH; } else { if (ropts->iface_vers != iface_data->iface_vers) ret = RULE_MATCH; } break; default: break; } return ret; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_OpnumEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; DCE2_OpnumData *opnum_data = (DCE2_OpnumData *)data; DCE2_SsnData *sd; DCE2_Roptions *ropts; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Evaluating \"%s\" rule option.\n", DCE2_ROPT__OPNUM)); if (!DCE2_RoptDoEval(p)) return RULE_NOMATCH; sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_DCE2); if ((sd == NULL) || DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if (ropts->opnum == DCE2_SENTINEL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Opnum not set - not evaluating.\n")); return RULE_NOMATCH; } switch (opnum_data->type) { case DCE2_OPNUM_TYPE__SINGLE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Rule opnum: %u, ropts opnum: %u\n", ((DCE2_OpnumSingle *)opnum_data)->opnum, ropts->opnum)); if (ropts->opnum == ((DCE2_OpnumSingle *)opnum_data)->opnum) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Match\n", DCE2_ROPT__OPNUM)); return RULE_MATCH; } break; case DCE2_OPNUM_TYPE__MULTIPLE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Multiple opnums: ropts opnum: %u\n", ropts->opnum)); { DCE2_OpnumMultiple *omult = (DCE2_OpnumMultiple *)opnum_data; if (DCE2_OpnumIsSet(omult->mask, omult->opnum_lo, omult->opnum_hi, (uint16_t)ropts->opnum)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Match\n", DCE2_ROPT__OPNUM)); return RULE_MATCH; } } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid opnum type: %d", __FILE__, __LINE__, opnum_data->type); break; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Fail\n", DCE2_ROPT__OPNUM)); return RULE_NOMATCH; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_StubDataEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; DCE2_SsnData *sd; DCE2_Roptions *ropts; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Evaluating \"%s\" rule option.\n", DCE2_ROPT__STUB_DATA)); if (!DCE2_RoptDoEval(p)) return RULE_NOMATCH; sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_DCE2); if ((sd == NULL) || DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if (ropts->stub_data != NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Setting cursor to stub data: %p.\n", ropts->stub_data)); *cursor = ropts->stub_data; _dpd.SetAltDetect((uint8_t *)ropts->stub_data, (uint16_t)(p->payload_size - (ropts->stub_data - p->payload))); return RULE_MATCH; } return RULE_NOMATCH; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_ByteTestEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; DCE2_SsnData *sd; DCE2_Roptions *ropts; DCE2_ByteTestData *bt_data; const uint8_t *start_ptr; uint16_t dsize; const uint8_t *bt_ptr; uint32_t pkt_value; DceRpcBoFlag byte_order; int ret = RULE_NOMATCH; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Evaluating \"%s\" rule option.\n", DCE2_ROPT__BYTE_TEST)); if (*cursor == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Cursor is NULL - not evaluating.\n")); return RULE_NOMATCH; } if (!DCE2_RoptDoEval(p)) return RULE_NOMATCH; sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_DCE2); if ((sd == NULL) || DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if ((ropts->data_byte_order == DCE2_SENTINEL) || (ropts->hdr_byte_order == DCE2_SENTINEL)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Data byte order or header byte order not set " "in rule options - not evaluating.\n")); return RULE_NOMATCH; } bt_data = (DCE2_ByteTestData *)data; if (bt_data == NULL) return RULE_NOMATCH; if (_dpd.Is_DetectFlag(SF_FLAG_ALT_DETECT)) { _dpd.GetAltDetect((uint8_t **)&start_ptr, &dsize); DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Using Alternative Detect buffer!\n");); } else { start_ptr = p->payload; dsize = p->payload_size; } /* Make sure we don't read past the end of the payload or before * beginning of payload */ if (bt_data->relative) { if ((bt_data->offset < 0) && (*cursor + bt_data->offset) < start_ptr) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset is negative and puts cursor before beginning " "of payload - not evaluating.\n")); return RULE_NOMATCH; } if ((*cursor + bt_data->offset + bt_data->num_bytes) > (start_ptr + dsize)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset plus number of bytes to read puts cursor past " "end of payload - not evaluating.\n")); return RULE_NOMATCH; } bt_ptr = *cursor + bt_data->offset; } else { if (bt_data->offset < 0) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset is negative but is not relative - " "not evaluating.\n")); return RULE_NOMATCH; } else if ((start_ptr + bt_data->offset + bt_data->num_bytes) > (start_ptr + dsize)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset plus number of bytes to read puts cursor past " "end of payload - not evaluating.\n")); return RULE_NOMATCH; } bt_ptr = start_ptr + bt_data->offset; } /* Determine which byte order to use */ if (ropts->stub_data == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Stub data is NULL. Setting byte order to that " "of the header.\n")); byte_order = (DceRpcBoFlag)ropts->hdr_byte_order; } else if (bt_ptr < ropts->stub_data) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Reading data in the header. Setting byte order " "to that of the header.\n")); byte_order = (DceRpcBoFlag)ropts->hdr_byte_order; } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Reading data in the stub. Setting byte order " "to that of the stub data.\n")); byte_order = (DceRpcBoFlag)ropts->data_byte_order; } /* Get the value */ switch (bt_data->num_bytes) { case 1: pkt_value = *((uint8_t *)bt_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Got 1 byte: %u.\n", pkt_value)); break; case 2: pkt_value = DceRpcNtohs((uint16_t *)bt_ptr, byte_order); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Got 2 bytes: %u.\n", pkt_value)); break; case 4: pkt_value = DceRpcNtohl((uint32_t *)bt_ptr, byte_order); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Got 4 bytes: %u.\n", pkt_value)); break; default: return RULE_NOMATCH; } /* Invert the return value. */ if (bt_data->invert) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Applying not flag.\n")); ret = RULE_MATCH; } switch (bt_data->operator) { case DCE2_BT_OP__LT: if (pkt_value < bt_data->value) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Packet value (%u) < Option value (%u).\n", pkt_value, bt_data->value)); if (ret == RULE_MATCH) ret = RULE_NOMATCH; else ret = RULE_MATCH; } break; case DCE2_BT_OP__EQ: if (pkt_value == bt_data->value) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Packet value (%u) == Option value (%u).\n", pkt_value, bt_data->value)); if (ret == RULE_MATCH) ret = RULE_NOMATCH; else ret = RULE_MATCH; } break; case DCE2_BT_OP__GT: if (pkt_value > bt_data->value) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Packet value (%u) > Option value (%u).\n", pkt_value, bt_data->value)); if (ret == RULE_MATCH) ret = RULE_NOMATCH; else ret = RULE_MATCH; } break; case DCE2_BT_OP__AND: if (pkt_value & bt_data->value) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Packet value (%08x) & Option value (%08x).\n", pkt_value, bt_data->value)); if (ret == RULE_MATCH) ret = RULE_NOMATCH; else ret = RULE_MATCH; } break; case DCE2_BT_OP__XOR: if (pkt_value ^ bt_data->value) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Packet value (%08x) ^ Option value (%08x).\n", pkt_value, bt_data->value)); if (ret == RULE_MATCH) ret = RULE_NOMATCH; else ret = RULE_MATCH; } break; default: return RULE_NOMATCH; } #ifdef DEBUG_MSGS if (ret == RULE_MATCH) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Match.\n", DCE2_ROPT__BYTE_TEST)); } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Fail.\n", DCE2_ROPT__BYTE_TEST)); } #endif return ret; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_ByteJumpEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; DCE2_SsnData *sd; DCE2_Roptions *ropts; DCE2_ByteJumpData *bj_data; const uint8_t *start_ptr; uint16_t dsize; const uint8_t *bj_ptr; uint32_t jmp_value; DceRpcBoFlag byte_order; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Evaluating \"%s\" rule option.\n", DCE2_ROPT__BYTE_JUMP)); if (*cursor == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Cursor is NULL - not evaluating.\n")); return RULE_NOMATCH; } if (!DCE2_RoptDoEval(p)) return RULE_NOMATCH; sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_DCE2); if ((sd == NULL) || DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if ((ropts->data_byte_order == DCE2_SENTINEL) || (ropts->hdr_byte_order == DCE2_SENTINEL)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Data byte order or header byte order not set " "in rule options - not evaluating.\n")); return RULE_NOMATCH; } bj_data = (DCE2_ByteJumpData *)data; if (bj_data == NULL) return RULE_NOMATCH; if (_dpd.Is_DetectFlag(SF_FLAG_ALT_DETECT)) { _dpd.GetAltDetect((uint8_t **)&start_ptr, &dsize); DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Using Alternative Detect buffer!\n");); } else { start_ptr = p->payload; dsize = p->payload_size; } /* Make sure we don't read past the end of the payload or before * beginning of payload */ if (bj_data->relative) { if ((bj_data->offset < 0) && (*cursor + bj_data->offset) < start_ptr) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset is negative and puts cursor before beginning " "of payload - not evaluating.\n")); return RULE_NOMATCH; } if ((*cursor + bj_data->offset + bj_data->num_bytes) > (start_ptr + dsize)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset plus number of bytes to read puts cursor past " "end of payload - not evaluating.\n")); return RULE_NOMATCH; } bj_ptr = *cursor + bj_data->offset; } else { if (bj_data->offset < 0) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset is negative but is not relative - " "not evaluating.\n")); return RULE_NOMATCH; } else if ((start_ptr + bj_data->offset + bj_data->num_bytes) > (start_ptr + dsize)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Offset plus number of bytes to read puts cursor past " "end of payload - not evaluating.\n")); return RULE_NOMATCH; } bj_ptr = start_ptr + bj_data->offset; } /* Determine which byte order to use */ if (ropts->stub_data == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Stub data is NULL. " "Setting byte order to that of the header.\n")); byte_order = (DceRpcBoFlag)ropts->hdr_byte_order; } else if (bj_ptr < ropts->stub_data) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Reading data in the header. Setting byte order " "to that of the header.\n")); byte_order = (DceRpcBoFlag)ropts->hdr_byte_order; } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Reading data in the stub. Setting byte order " "to that of the stub data.\n")); byte_order = (DceRpcBoFlag)ropts->data_byte_order; } /* Get the value */ switch (bj_data->num_bytes) { case 1: jmp_value = *((uint8_t *)bj_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Got 1 byte: %u.\n", jmp_value)); break; case 2: jmp_value = DceRpcNtohs((uint16_t *)bj_ptr, byte_order); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Got 2 bytes: %u.\n", jmp_value)); break; case 4: jmp_value = DceRpcNtohl((uint32_t *)bj_ptr, byte_order); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Got 4 bytes: %u.\n", jmp_value)); break; default: return 0; } if (bj_data->multiplier != DCE2_SENTINEL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Applying multiplier: %u * %u = %u.\n", jmp_value, bj_data->multiplier, jmp_value * bj_data->multiplier)); jmp_value *= bj_data->multiplier; } if (bj_data->align && (jmp_value & 3)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Aligning to 4 byte boundary: %u => %u.\n", jmp_value, jmp_value + (4 - (jmp_value & 3)))); jmp_value += (4 - (jmp_value & 3)); } bj_ptr += bj_data->num_bytes + jmp_value + bj_data->post_offset; if ((bj_ptr < start_ptr) || (bj_ptr >= (start_ptr + dsize))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Fail. Jump puts us past end of payload.\n", DCE2_ROPT__BYTE_JUMP)); return RULE_NOMATCH; } *cursor = bj_ptr; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "\"%s\" Match.\n", DCE2_ROPT__BYTE_JUMP)); return RULE_MATCH; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DCE2_RoptDoEval(SFSnortPacket *p) { if ((p->payload_size == 0) || (p->stream_session == NULL) || (!IsTCP(p) && !IsUDP(p))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "No payload or no " "session pointer or not TCP or UDP - not evaluating.\n")); return 0; } return 1; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_IfaceCleanup(void *data) { if (data == NULL) return; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MEMORY, "Cleaning Iface data: %u bytes.\n", sizeof(DCE2_IfaceData))); DCE2_Free(data, sizeof(DCE2_IfaceData), DCE2_MEM_TYPE__ROPTION); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_OpnumCleanup(void *data) { DCE2_OpnumData *odata = (DCE2_OpnumData *)data; if (data == NULL) return; switch (odata->type) { case DCE2_OPNUM_TYPE__SINGLE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MEMORY, "Cleaning Single opnum data: %u bytes.\n", sizeof(DCE2_OpnumSingle))); DCE2_Free((void *)odata, sizeof(DCE2_OpnumSingle), DCE2_MEM_TYPE__ROPTION); break; case DCE2_OPNUM_TYPE__MULTIPLE: { DCE2_OpnumMultiple *omult = (DCE2_OpnumMultiple *)odata; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MEMORY, "Cleaning Multiple opnum data: %u bytes.\n", sizeof(DCE2_OpnumMultiple) + omult->mask_size)); if (omult->mask != NULL) DCE2_Free((void *)omult->mask, omult->mask_size, DCE2_MEM_TYPE__ROPTION); DCE2_Free((void *)omult, sizeof(DCE2_OpnumMultiple), DCE2_MEM_TYPE__ROPTION); } break; default: break; } } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_ByteTestCleanup(void *data) { if (data == NULL) return; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MEMORY, "Cleaning ByteTest data: %u bytes.\n", sizeof(DCE2_ByteTestData))); DCE2_Free(data, sizeof(DCE2_ByteTestData), DCE2_MEM_TYPE__ROPTION); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_ByteJumpCleanup(void *data) { if (data == NULL) return; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MEMORY, "Cleaning ByteJump data: %u bytes.\n", sizeof(DCE2_ByteJumpData))); DCE2_Free(data, sizeof(DCE2_ByteJumpData), DCE2_MEM_TYPE__ROPTION); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static uint32_t DCE2_IfaceHash(void *data) { uint32_t a, b, c; DCE2_IfaceData *iface_data = (DCE2_IfaceData *)data; if (iface_data == NULL) return 0; a = iface_data->iface.time_low; b = (iface_data->iface.time_mid << 16) | (iface_data->iface.time_high_and_version); c = (iface_data->iface.clock_seq_and_reserved << 24) | (iface_data->iface.clock_seq_low << 16) | (iface_data->iface.node[0] << 8) | (iface_data->iface.node[1]); mix(a, b, c); a += (iface_data->iface.node[2] << 24) | (iface_data->iface.node[3] << 16) | (iface_data->iface.node[4] << 8) | (iface_data->iface.node[5]); b += iface_data->iface_vers; c += iface_data->iface_vers_maj; mix(a, b, c); a += iface_data->iface_vers_min; b += iface_data->operator; c += iface_data->any_frag; final(a, b, c); return c; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static uint32_t DCE2_OpnumHash(void *data) { uint32_t a = 0, b = 0, c = 0; DCE2_OpnumData *odata = (DCE2_OpnumData *)data; if (odata == NULL) return 0; switch (odata->type) { case DCE2_OPNUM_TYPE__SINGLE: { DCE2_OpnumSingle *osingle = (DCE2_OpnumSingle *)odata; a = odata->type; b = osingle->opnum; c = 10; final(a, b, c); } break; case DCE2_OPNUM_TYPE__MULTIPLE: { DCE2_OpnumMultiple *omult = (DCE2_OpnumMultiple *)odata; unsigned int i; a = odata->type; b = omult->mask_size; c = 0; /* Don't care about potential wrapping if it exists */ for (i = 0; i < omult->mask_size; i++) c += omult->mask[i]; mix(a, b, c); a = omult->opnum_lo; b = omult->opnum_hi; c = 10; final(a, b, c); } break; default: DCE2_Die("%s(%d) Invalid opnum type: %d", __FILE__, __LINE__, odata->type); break; } return c; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static uint32_t DCE2_ByteTestHash(void *data) { uint32_t a, b, c; DCE2_ByteTestData *bt_data = (DCE2_ByteTestData *)data; if (bt_data == NULL) return 0; a = bt_data->num_bytes; b = bt_data->value; c = bt_data->invert; mix(a, b, c); a += bt_data->operator; b += bt_data->offset; c += bt_data->relative; final(a, b, c); return c; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static uint32_t DCE2_ByteJumpHash(void *data) { uint32_t a, b, c; DCE2_ByteJumpData *bj_data = (DCE2_ByteJumpData *)data; if (bj_data == NULL) return 0; a = bj_data->num_bytes; b = bj_data->offset; c = bj_data->relative; mix(a, b, c); a += bj_data->multiplier; b += bj_data->align; final(a, b, c); return c; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_IfaceKeyCompare(void *l, void *r) { DCE2_IfaceData *left = (DCE2_IfaceData *)l; DCE2_IfaceData *right = (DCE2_IfaceData *)r; if ((left == NULL) || (right == NULL)) return PREPROC_OPT_NOT_EQUAL; if ((DCE2_UuidCompare(&left->iface, &right->iface) == 0) && (left->iface_vers == right->iface_vers) && (left->iface_vers_maj == right->iface_vers_maj) && (left->iface_vers_min == right->iface_vers_min) && (left->operator == right->operator) && (left->any_frag == right->any_frag)) { return PREPROC_OPT_EQUAL; } return PREPROC_OPT_NOT_EQUAL; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_OpnumKeyCompare(void *l, void *r) { DCE2_OpnumData *left = (DCE2_OpnumData *)l; DCE2_OpnumData *right = (DCE2_OpnumData *)r; if ((left == NULL) || (right == NULL)) return PREPROC_OPT_NOT_EQUAL; if (left->type != right->type) return PREPROC_OPT_NOT_EQUAL; switch (left->type) { case DCE2_OPNUM_TYPE__SINGLE: { DCE2_OpnumSingle *lsingle = (DCE2_OpnumSingle *)left; DCE2_OpnumSingle *rsingle = (DCE2_OpnumSingle *)right; if (lsingle->opnum != rsingle->opnum) return PREPROC_OPT_NOT_EQUAL; } break; case DCE2_OPNUM_TYPE__MULTIPLE: { unsigned int i; DCE2_OpnumMultiple *lmult = (DCE2_OpnumMultiple *)left; DCE2_OpnumMultiple *rmult = (DCE2_OpnumMultiple *)right; if ((lmult->mask_size != rmult->mask_size) || (lmult->opnum_lo != rmult->opnum_lo) || (lmult->opnum_hi != rmult->opnum_hi)) { return PREPROC_OPT_NOT_EQUAL; } for (i = 0; i < lmult->mask_size; i++) { if (lmult->mask[i] != rmult->mask[i]) return PREPROC_OPT_NOT_EQUAL; } } break; default: DCE2_Die("%s(%d) Invalid opnum type: %d", __FILE__, __LINE__, left->type); break; } return PREPROC_OPT_EQUAL; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_ByteTestKeyCompare(void *l, void *r) { DCE2_ByteTestData *left = (DCE2_ByteTestData *)l; DCE2_ByteTestData *right = (DCE2_ByteTestData *)r; if ((left == NULL) || (right == NULL)) return PREPROC_OPT_NOT_EQUAL; if ((left->num_bytes == right->num_bytes) && (left->value == right->value) && (left->invert == right->invert) && (left->operator == right->operator) && (left->offset == right->offset) && (left->relative == right->relative)) { return PREPROC_OPT_EQUAL; } return PREPROC_OPT_NOT_EQUAL; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_ByteJumpKeyCompare(void *l, void *r) { DCE2_ByteJumpData *left = (DCE2_ByteJumpData *)l; DCE2_ByteJumpData *right = (DCE2_ByteJumpData *)r; if ((left == NULL) || (right == NULL)) return PREPROC_OPT_NOT_EQUAL; if ((left->num_bytes == right->num_bytes) && (left->offset == right->offset) && (left->relative == right->relative) && (left->multiplier == right->multiplier) && (left->align == right->align)) { return PREPROC_OPT_EQUAL; } return PREPROC_OPT_NOT_EQUAL; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_PrintRoptions(DCE2_Roptions *ropts) { printf(" First frag: %s\n", ropts->first_frag == 1 ? "yes" : (ropts->first_frag == 0 ? "no" : "unset")); if (ropts->first_frag == DCE2_SENTINEL) { printf(" Iface: unset\n"); printf(" Iface version: unset\n"); } else { printf(" Iface: %s\n", DCE2_UuidToStr(&ropts->iface, DCERPC_BO_FLAG__NONE)); printf(" Iface version: %u\n", ropts->iface_vers_maj); } if (ropts->opnum == DCE2_SENTINEL) printf(" Opnum: unset\n"); else printf(" Opnum: %u\n", ropts->opnum); printf(" Header byte order: %s\n", ropts->hdr_byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN ? "little endian" : (ropts->hdr_byte_order == DCERPC_BO_FLAG__BIG_ENDIAN ? "big endian" : "unset")); printf(" Data byte order: %s\n", ropts->data_byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN ? "little endian" : (ropts->data_byte_order == DCERPC_BO_FLAG__BIG_ENDIAN ? "big endian" : "unset")); if (ropts->stub_data != NULL) printf(" Stub data: %p\n", ropts->stub_data); else printf(" Stub data: NULL\n"); } /******************************************************************** * Function: DCE2_RoptError() * * Prints rule option error and dies. * * Arguments: * const char * * Format string * ... * Arguments to format string * * Returns: None * ********************************************************************/ static NORETURN void DCE2_RoptError(const char *format, ...) { char buf[1024]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); buf[sizeof(buf) - 1] = '\0'; DCE2_Die("%s(%d): %s Please consult documentation.", *_dpd.config_file, *_dpd.config_line, buf); } /********************************** * Function: DCE2_GetByteOrder() * * Gets the byte order needed for a byte_test, byte_jump, or byte_extract. * * Arguments: * Packet * * packet being evaluated * int32_t * offset into the packet payload where the rule will be evaluated. * calling function is responsible for checking that the offset is in-bounds. * * Returns: * DCE2_SENTINEL (-1) if byte order not set, or otherwise not evaluating * BIG (0) if byte order is big-endian * LITTLE (1) if byte order is little-endian * **********************************/ #define BIG 0 #define LITTLE 1 int DCE2_GetByteOrder(void *data, int32_t offset) { DCE2_SsnData *sd; DCE2_Roptions *ropts; DceRpcBoFlag byte_order; const uint8_t *data_ptr; SFSnortPacket *p = (SFSnortPacket *)data; if (p == NULL) return -1; sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_DCE2); if ((sd == NULL) || DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "No session data - not evaluating.\n")); return -1; } ropts = &sd->ropts; if ((ropts->data_byte_order == DCE2_SENTINEL) || (ropts->hdr_byte_order == DCE2_SENTINEL)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Data byte order or header byte order not set " "in rule options - not evaluating.\n")); return -1; } /* Determine which byte order to use */ data_ptr = p->payload + offset; if (ropts->stub_data == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Stub data is NULL. " "Setting byte order to that of the header.\n")); byte_order = (DceRpcBoFlag)ropts->hdr_byte_order; } else if (data_ptr < ropts->stub_data) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Reading data in the header. Setting byte order " "to that of the header.\n")); byte_order = (DceRpcBoFlag)ropts->hdr_byte_order; } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, "Reading data in the stub. Setting byte order " "to that of the stub data.\n")); byte_order = (DceRpcBoFlag)ropts->data_byte_order; } /* Return ints, since this enum doesn't exist back in Snort-land. */ if (byte_order == DCERPC_BO_FLAG__BIG_ENDIAN) return BIG; if (byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN) return LITTLE; return -1; } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_list.c0000644000175000017500000015261714241075646021576 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides list, queue and stack data structures and methods for use * with the preprocessor. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_list.h" #include "dce2_memory.h" #include "dce2_debug.h" #include "dce2_utils.h" #include "sf_types.h" /******************************************************************** * Private function prototyes ********************************************************************/ static void DCE2_ListInsertTail(DCE2_List *, DCE2_ListNode *); static void DCE2_ListInsertHead(DCE2_List *, DCE2_ListNode *); static void DCE2_ListInsertBefore(DCE2_List *, DCE2_ListNode *, DCE2_ListNode *); /******************************************************************** * Function: DCE2_ListNew() * * Creates and returns a new list object. * * Arguments: * DCE2_ListType * The type of list this should be - sorted, splayed, etc. * DCE2_ListKeyCompare * The comparison function to call when comparing two keys * for inserting, finding, etc. * DCE2_ListDataFree * An optional function to call to free data in the list. * If NULL is passed in, the user will have to manually free * the data. * DCE2_ListKeyFree * An optional function to call to free keys used in the list. * If NULL is passed in, the user will have to manually free * the keys. * int * Flags that affect processing of the list. * See DCE2_ListFlags for possible combinations. * DCE2_MemType * The memory type that dynamically allocated data should be * associated with. * * Returns: * DCE2_List * * Pointer to a valid list object. * NULL if an error occurs. * ********************************************************************/ DCE2_List * DCE2_ListNew(DCE2_ListType type, DCE2_ListKeyCompare kc, DCE2_ListDataFree df, DCE2_ListKeyFree kf, int flags, DCE2_MemType mtype) { DCE2_List *list; /* Must have a key compare function */ if (kc == NULL) return NULL; list = (DCE2_List *)DCE2_Alloc(sizeof(DCE2_List), mtype); if (list == NULL) return NULL; list->type = type; list->compare = kc; list->data_free = df; list->key_free = kf; list->flags = flags; list->mtype = mtype; return list; } /******************************************************************** * Function: DCE2_ListFind() * * Trys to find a node in the list using key passed in. If list * is splayed, found node is moved to front of list. The data * associated with the node is returned. * * Arguments: * DCE2_List * * A pointer to the list object. * void * * Pointer to a key. * * Returns: * void * * If the key is found, the data associated with the node * is returned. * NULL is returned if the item cannot be found given the key. * ********************************************************************/ void * DCE2_ListFind(DCE2_List *list, void *key) { DCE2_ListNode *n; if (list == NULL) return NULL; for (n = list->head; n != NULL; n = n->next) { int comp = list->compare(key, n->key); if (comp == 0) { /* Found it, break out */ break; } else if ((comp < 0) && (list->type == DCE2_LIST_TYPE__SORTED)) { /* Don't look any more if the list is sorted */ return NULL; } } if (n != NULL) { /* If list is splayed, move found node to front of list */ if ((list->type == DCE2_LIST_TYPE__SPLAYED) && (n != list->head)) { n->prev->next = n->next; if (n->next != NULL) n->next->prev = n->prev; else /* it's the tail */ list->tail = n->prev; n->prev = NULL; n->next = list->head; list->head->prev = n; list->head = n; } return n->data; } return NULL; } /******************************************************************** * Function: DCE2_ListFindKey() * * Trys to find a node in the list using key passed in. If list * is splayed, found node is moved to front of list. Returns * whether or not the key is associated with a node in the list. * * Arguments: * DCE2_List * * A pointer to the list object. * void * * Pointer to a key. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if the key is found. * DCE2_RET__ERROR if the key is not found. * ********************************************************************/ DCE2_Ret DCE2_ListFindKey(DCE2_List *list, void *key) { DCE2_ListNode *n; if (list == NULL) return DCE2_RET__ERROR; for (n = list->head; n != NULL; n = n->next) { int comp = list->compare(key, n->key); if (comp == 0) { /* Found it, break out */ break; } else if ((comp < 0) && (list->type == DCE2_LIST_TYPE__SORTED)) { /* Don't look any more if the list is sorted */ return DCE2_RET__ERROR; } } if (n != NULL) { /* If list is splayed, move found node to front of list */ if ((list->type == DCE2_LIST_TYPE__SPLAYED) && (n != list->head)) { n->prev->next = n->next; if (n->next != NULL) n->next->prev = n->prev; else /* it's the tail */ list->tail = n->prev; n->prev = NULL; n->next = list->head; list->head->prev = n; list->head = n; } return DCE2_RET__SUCCESS; } return DCE2_RET__ERROR; } /******************************************************************** * Function: DCE2_ListInsert() * * Adds a new node to the list with the key and data supplied. * If no duplicates are allowed in the key is searched for first * to see if a node is already present in the list. If sorted, * the node is inserted into the list based on the key compare * function associated with the list object. * * Arguments: * DCE2_List * * A pointer to the list object. * void * * Pointer to a key to associate with data. * void * * Pointer to the data to insert into the list. * * Returns: * DCE2_Ret * DCE2_RET__DUPLICATE if an entry with the key is already * in the list and no duplicates are allowed. * DCE2_RET__SUCCESS if a new node with key and data is * successfully inserted into the list. * DCE2_RET__ERROR if memory cannot be allocated for the * new node or a NULL list object was passed in. * ********************************************************************/ DCE2_Ret DCE2_ListInsert(DCE2_List *list, void *key, void *data) { DCE2_ListNode *n; DCE2_ListNode *last = NULL; int dup_check = 0; if (list == NULL) return DCE2_RET__ERROR; if (list->flags & DCE2_LIST_FLAG__NO_DUPS) { for (last = list->head; last != NULL; last = last->next) { int comp = list->compare(key, last->key); if (comp == 0) { /* It's already in the list */ return DCE2_RET__DUPLICATE; } else if ((comp < 0) && (list->type == DCE2_LIST_TYPE__SORTED)) { /* Break out here so as to insert after this node since * the list is sorted */ break; } } dup_check = 1; } n = (DCE2_ListNode *)DCE2_Alloc(sizeof(DCE2_ListNode), list->mtype); if (n == NULL) return DCE2_RET__ERROR; n->key = key; n->data = data; if ((list->type != DCE2_LIST_TYPE__SORTED) || (list->head == NULL)) { if (list->flags & DCE2_LIST_FLAG__INS_TAIL) DCE2_ListInsertTail(list, n); else DCE2_ListInsertHead(list, n); } else if (dup_check) /* and the list is sorted */ { if (last == NULL) DCE2_ListInsertTail(list, n); else DCE2_ListInsertBefore(list, n, last); } else { DCE2_ListNode *tmp; for (tmp = list->head; tmp != NULL; tmp = tmp->next) { if (list->compare(key, tmp->key) <= 0) break; } if (tmp == NULL) DCE2_ListInsertTail(list, n); else if (tmp == list->head) DCE2_ListInsertHead(list, n); else DCE2_ListInsertBefore(list, n, tmp); } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ListRemove() * * Removes the node in the list with the specified key. If * data free and key free functions were given with the creation * of the list object, they are called with the data and key * respectively. * * Arguments: * DCE2_List * * A pointer to the list object. * void * * Pointer to a key. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if a node in the list with the specified * key cannot be found or the list object passed in is NULL. * DCE2_RET__SUCCESS if the node is successfully removed from * the list. * ********************************************************************/ DCE2_Ret DCE2_ListRemove(DCE2_List *list, void *key) { DCE2_ListNode *n; if (list == NULL) return DCE2_RET__ERROR; for (n = list->head; n != NULL; n = n->next) { int comp = list->compare(key, n->key); if (comp == 0) { /* Found it */ break; } else if ((comp < 0) && (list->type == DCE2_LIST_TYPE__SORTED)) { /* Won't find it after this since the list is sorted */ return DCE2_RET__ERROR; } } if (n == NULL) return DCE2_RET__ERROR; if (n == list->head) list->head = n->next; if (n == list->tail) list->tail = n->prev; if (n->prev != NULL) n->prev->next = n->next; if (n->next != NULL) n->next->prev = n->prev; if (list->key_free != NULL) list->key_free(n->key); if (list->data_free != NULL) list->data_free(n->data); DCE2_Free((void *)n, sizeof(DCE2_ListNode), list->mtype); list->num_nodes--; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ListFirst() * * Returns a pointer to the data of the first node in the list. * Sets a current pointer to the first node in the list for * iterating over the list. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: * void * * The data in the first node in the list. * NULL if the list object passed in is NULL, or there are * no items in the list. * ********************************************************************/ void * DCE2_ListFirst(DCE2_List *list) { if (list == NULL) return NULL; list->current = list->head; list->next = NULL; if (list->current != NULL) return list->current->data; return NULL; } /******************************************************************** * Function: DCE2_ListNext() * * Increments the current pointer in the list to the next node in * the list and returns the data associated with it. This in * combination with DCE2_ListFirst is useful in a for loop to * iterate over the items in a list. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: * void * * The data in the next node in the list. * NULL if the list object passed in is NULL, or we are at * the end of the list and there are no next nodes. * ********************************************************************/ void * DCE2_ListNext(DCE2_List *list) { if (list == NULL) return NULL; if (list->next != NULL) { list->current = list->next; list->next = NULL; return list->current->data; } else if (list->current != NULL) { list->current = list->current->next; if (list->current != NULL) return list->current->data; } return NULL; } /******************************************************************** * Function: DCE2_ListLast() * * Returns a pointer to the data of the last node in the list. * Sets a current pointer to the last node in the list for * iterating over the list backwards. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: * void * * The data in the last node in the list. * NULL if the list object passed in is NULL, or there are * no items in the list. * ********************************************************************/ void * DCE2_ListLast(DCE2_List *list) { if (list == NULL) return NULL; list->current = list->tail; list->prev = NULL; if (list->current != NULL) return list->current->data; return NULL; } /******************************************************************** * Function: DCE2_ListPrev() * * Puts the current pointer in the list to the previous node in * the list and returns the data associated with it. This in * combination with DCE2_ListLast is useful in a for loop to * iterate over the items in a list in backwards order. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: * void * * The data in the previous node in the list. * NULL if the list object passed in is NULL, or we are at * the beginning of the list and there are no previous nodes. * ********************************************************************/ void * DCE2_ListPrev(DCE2_List *list) { if (list == NULL) return NULL; if (list->prev != NULL) { list->current = list->prev; list->prev = NULL; return list->current->data; } else if (list->current != NULL) { list->current = list->current->prev; if (list->current != NULL) return list->current->data; } return NULL; } /******************************************************************** * Function: DCE2_ListRemoveCurrent() * * Removes the current node pointed to in the list. This is set * when a call to DCE2_ListFirst or DCE2_ListNext is called. For * either of these if data is returned and the user want to remove * that data from the list, this function should be called. * Sets a next pointer, so a next call to DCE2_ListNext will point * to the node after the deleted one. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: None * ********************************************************************/ void DCE2_ListRemoveCurrent(DCE2_List *list) { if (list == NULL) return; if (list->current == NULL) return; list->next = list->current->next; list->prev = list->current->prev; if (list->current == list->head) list->head = list->current->next; if (list->current == list->tail) list->tail = list->current->prev; if (list->current->prev != NULL) list->current->prev->next = list->current->next; if (list->current->next != NULL) list->current->next->prev = list->current->prev; if (list->key_free != NULL) list->key_free(list->current->key); if (list->data_free != NULL) list->data_free(list->current->data); DCE2_Free((void *)list->current, sizeof(DCE2_ListNode), list->mtype); list->current = NULL; list->num_nodes--; } /******************************************************************** * Function: DCE2_ListEmpty() * * Removes all of the nodes in a list. Does not delete the list * object itself. Calls data free and key free functions for * data and key if they are not NULL. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: None * ********************************************************************/ void DCE2_ListEmpty(DCE2_List *list) { DCE2_ListNode *n; if (list == NULL) return; n = list->head; while (n != NULL) { DCE2_ListNode *tmp = n->next; if (list->data_free != NULL) list->data_free(n->data); if (list->key_free != NULL) list->key_free(n->key); DCE2_Free((void *)n, sizeof(DCE2_ListNode), list->mtype); n = tmp; } list->head = list->tail = list->current = NULL; list->num_nodes = 0; } /******************************************************************** * Function: DCE2_ListDestroy() * * Destroys the list object and all of the data associated with it. * * Arguments: * DCE2_List * * A pointer to the list object. * * Returns: None * ********************************************************************/ void DCE2_ListDestroy(DCE2_List *list) { if (list == NULL) return; DCE2_ListEmpty(list); DCE2_Free(list, sizeof(DCE2_List), list->mtype); } /******************************************************************** * Function: DCE2_ListInsertTail() * * Private function for inserting a node at the end of the list. * * Arguments: * DCE2_List * * A pointer to the list object. * DCE2_ListNode * * A pointer to the list node to insert. * * Returns: None * ********************************************************************/ static void DCE2_ListInsertTail(DCE2_List *list, DCE2_ListNode *n) { if ((list == NULL) || (n == NULL)) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) List and/or list node passed in was NULL", __FILE__, __LINE__); return; } if (list->tail == NULL) { list->tail = list->head = n; n->prev = n->next = NULL; } else { n->prev = list->tail; n->next = NULL; list->tail->next = n; list->tail = n; } list->num_nodes++; } /******************************************************************** * Function: DCE2_ListInsertHead() * * Private function for inserting a node at the front of the list. * * Arguments: * DCE2_List * * A pointer to the list object. * DCE2_ListNode * * A pointer to the list node to insert. * * Returns: None * ********************************************************************/ static void DCE2_ListInsertHead(DCE2_List *list, DCE2_ListNode *n) { if ((list == NULL) || (n == NULL)) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) List and/or list node passed in was NULL", __FILE__, __LINE__); return; } if (list->head == NULL) { list->head = list->tail = n; n->prev = n->next = NULL; } else { n->prev = NULL; n->next = list->head; list->head->prev = n; list->head = n; } list->num_nodes++; } /******************************************************************** * Function: DCE2_ListInsertBefore() * * Private function for inserting a node before a given node in * the list. * * Arguments: * DCE2_List * * A pointer to the list object. * DCE2_ListNode * * A pointer to the list node to insert. * DCE2_ListNode * * A pointer to the list node to insert this node before. * * Returns: None * ********************************************************************/ static void DCE2_ListInsertBefore(DCE2_List *list, DCE2_ListNode *insert, DCE2_ListNode *front) { if ((list == NULL) || (insert == NULL) || (front == NULL)) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) List, insert node and/or front node passed in " "was NULL", __FILE__, __LINE__); return; } if (front == list->head) { DCE2_ListInsertHead(list, insert); } else { insert->prev = front->prev; insert->next = front; front->prev->next = insert; front->prev = insert; list->num_nodes++; } } /******************************************************************** * Function: DCE2_QueueNew() * * Creates and initializes a new queue object. * * Arguments: * DCE2_QueueDataFree * An optional free function for the data inserted into * the queue. If NULL is passed in, the user will be * responsible for freeing data left in the queue. * DCE2_MemType * The type of memory to associate dynamically allocated * memory with. * * Returns: * DCE2_Queue * * Pointer to a new queue object. * NULL if unable to allocate memory for the object. * ********************************************************************/ DCE2_Queue * DCE2_QueueNew(DCE2_QueueDataFree df, DCE2_MemType mtype) { DCE2_Queue *queue; queue = (DCE2_Queue *)DCE2_Alloc(sizeof(DCE2_Queue), mtype); if (queue == NULL) return NULL; queue->data_free = df; queue->mtype = mtype; return queue; } /******************************************************************** * Function: DCE2_QueueEnqueue() * * Inserts data into the queue. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * void * * Pointer to the data to insert into the queue. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if memory cannot be allocated for a new * queue node or the queue object passed in is NULL. * DCE2_RET__SUCCESS if the data is successfully added to * the queue. * ********************************************************************/ DCE2_Ret DCE2_QueueEnqueue(DCE2_Queue *queue, void *data) { DCE2_QueueNode *n; if (queue == NULL) return DCE2_RET__ERROR; n = (DCE2_QueueNode *)DCE2_Alloc(sizeof(DCE2_QueueNode), queue->mtype); if (n == NULL) return DCE2_RET__ERROR; n->data = data; if (queue->tail == NULL) { queue->head = queue->tail = n; n->next = NULL; } else { queue->tail->next = n; n->prev = queue->tail; queue->tail = n; } queue->num_nodes++; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_QueueDequeue() * * Removes and returns the data in the first node in the queue. * Note that the user will have to free the data returned. The * data free function only applies to data that is in the queue * when it is emptied or destroyed. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: * void * * The data in the first node in the queue. * NULL if there are no items in the queue or the queue object * passed in is NULL. * ********************************************************************/ void * DCE2_QueueDequeue(DCE2_Queue *queue) { DCE2_QueueNode *n; if (queue == NULL) return NULL; n = queue->head; if (n != NULL) { void *data = n->data; if (queue->head == queue->tail) { queue->head = queue->tail = NULL; } else { queue->head->next->prev = NULL; queue->head = queue->head->next; } DCE2_Free((void *)n, sizeof(DCE2_QueueNode), queue->mtype); queue->num_nodes--; return data; } return NULL; } /******************************************************************** * Function: DCE2_QueueFirst() * * Returns a pointer to the data of the first node in the queue. * Sets a current pointer to the first node in the queue for * iterating over the queue. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: * void * * The data in the first node in the queue. * NULL if the queue object passed in is NULL, or there are * no items in the queue. * ********************************************************************/ void * DCE2_QueueFirst(DCE2_Queue *queue) { if (queue == NULL) return NULL; queue->current = queue->head; queue->next = NULL; if (queue->current != NULL) return queue->current->data; return NULL; } /******************************************************************** * Function: DCE2_QueueNext() * * Increments the current pointer in the queue to the next node in * the queue and returns the data associated with it. This in * combination with DCE2_QueueFirst is useful in a for loop to * iterate over the items in a queue. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: * void * * The data in the next node in the queue. * NULL if the queue object passed in is NULL, or we are at * the end of the queue and there are no next nodes. * ********************************************************************/ void * DCE2_QueueNext(DCE2_Queue *queue) { if (queue == NULL) return NULL; if (queue->next != NULL) { queue->current = queue->next; queue->next = NULL; return queue->current->data; } else if (queue->current != NULL) { queue->current = queue->current->next; if (queue->current != NULL) return queue->current->data; } return NULL; } /******************************************************************** * Function: DCE2_QueueLast() * * Returns a pointer to the data of the last node in the queue. * Sets a current pointer to the last node in the queue for * iterating over the queue backwards. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: * void * * The data in the last node in the queue. * NULL if the queue object passed in is NULL, or there are * no items in the queue. * ********************************************************************/ void * DCE2_QueueLast(DCE2_Queue *queue) { if (queue == NULL) return NULL; queue->current = queue->tail; queue->prev = NULL; if (queue->current != NULL) return queue->current->data; return NULL; } /******************************************************************** * Function: DCE2_QueuePrev() * * Puts the current pointer in the queue to the previous node in * the queue and returns the data associated with it. This in * combination with DCE2_QueueLast is useful in a for loop to * iterate over the items in a queue in backwards order. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: * void * * The data in the previous node in the queue. * NULL if the queue object passed in is NULL, or we are at * the beginning of the queue and there are no previous nodes. * ********************************************************************/ void * DCE2_QueuePrev(DCE2_Queue *queue) { if (queue == NULL) return NULL; if (queue->prev != NULL) { queue->current = queue->prev; queue->prev = NULL; return queue->current->data; } else if (queue->current != NULL) { queue->current = queue->current->prev; if (queue->current != NULL) return queue->current->data; } return NULL; } /******************************************************************** * Function: DCE2_QueueRemoveCurrent() * * Removes the current node pointed to in the queue. This is set * when a call to DCE2_QueueFirst or DCE2_QueueNext is called. For * either of these if data is returned and the user want to remove * that data from the queue, this function should be called. * Sets a next pointer, so a next call to DCE2_QueueNext will point * to the node after the deleted one. * * Arguments: * DCE2_Queue * * A pointer to the list object. * * Returns: None * ********************************************************************/ void DCE2_QueueRemoveCurrent(DCE2_Queue *queue) { if (queue == NULL) return; if (queue->current == NULL) return; queue->next = queue->current->next; queue->prev = queue->current->prev; if (queue->current == queue->head) queue->head = queue->current->next; if (queue->current == queue->tail) queue->tail = queue->current->prev; if (queue->current->prev != NULL) queue->current->prev->next = queue->current->next; if (queue->current->next != NULL) queue->current->next->prev = queue->current->prev; if (queue->data_free != NULL) queue->data_free(queue->current->data); DCE2_Free((void *)queue->current, sizeof(DCE2_QueueNode), queue->mtype); queue->current = NULL; queue->num_nodes--; } /******************************************************************** * Function: DCE2_QueueEmpty() * * Removes all of the nodes in a queue. Does not delete the queue * object itself. Calls data free function for data if it is * not NULL. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: None * ********************************************************************/ void DCE2_QueueEmpty(DCE2_Queue *queue) { DCE2_QueueNode *n; if (queue == NULL) return; n = queue->head; while (n != NULL) { DCE2_QueueNode *tmp = n->next; if (queue->data_free != NULL) queue->data_free(n->data); DCE2_Free((void *)n, sizeof(DCE2_QueueNode), queue->mtype); n = tmp; } queue->head = queue->tail = queue->current = NULL; queue->num_nodes = 0; } /******************************************************************** * Function: DCE2_QueueDestroy() * * Destroys the queue object and all of the data associated with it. * * Arguments: * DCE2_Queue * * A pointer to the queue object. * * Returns: None * ********************************************************************/ void DCE2_QueueDestroy(DCE2_Queue *queue) { if (queue == NULL) return; DCE2_QueueEmpty(queue); DCE2_Free((void *)queue, sizeof(DCE2_Queue), queue->mtype); } /******************************************************************** * Function: DCE2_StackNew() * * Creates and initializes a new stack object. * * Arguments: * DCE2_StackDataFree * An optional free function for the data inserted into * the stack. If NULL is passed in, the user will be * responsible for freeing data left in the stack. * DCE2_MemType * The type of memory to associate dynamically allocated * memory with. * * Returns: * DCE2_Stack * * Pointer to a new stack object. * NULL if unable to allocate memory for the object. * ********************************************************************/ DCE2_Stack * DCE2_StackNew(DCE2_StackDataFree df, DCE2_MemType mtype) { DCE2_Stack *stack; stack = (DCE2_Stack *)DCE2_Alloc(sizeof(DCE2_Stack), mtype); if (stack == NULL) return NULL; stack->data_free = df; stack->mtype = mtype; return stack; } /******************************************************************** * Function: DCE2_StackPush() * * Inserts data onto the stack. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * void * * Pointer to the data to insert onto the stack. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if memory cannot be allocated for a new * stack node or the stack object passed in is NULL. * DCE2_RET__SUCCESS if the data is successfully added to * the stack. * ********************************************************************/ DCE2_Ret DCE2_StackPush(DCE2_Stack *stack, void *data) { DCE2_StackNode *n; if (stack == NULL) return DCE2_RET__ERROR; n = (DCE2_StackNode *)DCE2_Alloc(sizeof(DCE2_StackNode), stack->mtype); if (n == NULL) return DCE2_RET__ERROR; n->data = data; if (stack->tail == NULL) { stack->tail = stack->head = n; n->prev = NULL; n->next = NULL; } else { stack->tail->next = n; n->prev = stack->tail; stack->tail = n; } stack->num_nodes++; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_StackPop() * * Removes and returns the data in the last node in the stack. * Note that the user will have to free the data returned. The * data free function only applies to data that is in the stack * when it is emptied or destroyed. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: * void * * The data in the last node in the stack. * NULL if there are no items in the stack or the stack object * passed in is NULL. * ********************************************************************/ void * DCE2_StackPop(DCE2_Stack *stack) { DCE2_StackNode *n; if (stack == NULL) return NULL; n = stack->tail; if (n != NULL) { void *data = n->data; stack->tail = stack->tail->prev; if (stack->tail == NULL) stack->head = NULL; DCE2_Free((void *)n, sizeof(DCE2_StackNode), stack->mtype); stack->num_nodes--; return data; } return NULL; } /******************************************************************** * Function: DCE2_StackFirst() * * Returns a pointer to the data of the first node in the stack. * Sets a current pointer to the first node in the stack for * iterating over the stack. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: * void * * The data in the first node in the stack. * NULL if the stack object passed in is NULL, or there are * no items in the stack. * ********************************************************************/ void * DCE2_StackFirst(DCE2_Stack *stack) { if (stack == NULL) return NULL; stack->current = stack->head; if (stack->current != NULL) return stack->current->data; return NULL; } /******************************************************************** * Function: DCE2_StackNext() * * Increments the current pointer in the stack to the next node in * the stack and returns the data associated with it. This in * combination with DCE2_StackFirst is useful in a for loop to * iterate over the items in a stack. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: * void * * The data in the next node in the stack. * NULL if the stack object passed in is NULL, or we are at * the end of the stack and there are no next nodes. * ********************************************************************/ void * DCE2_StackNext(DCE2_Stack *stack) { if (stack == NULL) return NULL; if (stack->current != NULL) { stack->current = stack->current->next; if (stack->current != NULL) return stack->current->data; } return NULL; } /******************************************************************** * Function: DCE2_StackLast() * * Returns a pointer to the data of the last node in the stack. * Sets a current pointer to the last node in the stack for * iterating over the stack backwards. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: * void * * The data in the last node in the stack. * NULL if the stack object passed in is NULL, or there are * no items in the stack. * ********************************************************************/ void * DCE2_StackLast(DCE2_Stack *stack) { if (stack == NULL) return NULL; stack->current = stack->tail; if (stack->current != NULL) return stack->current->data; return NULL; } /******************************************************************** * Function: DCE2_StackPrev() * * Puts the current pointer in the stack to the previous node in * the stack and returns the data associated with it. This in * combination with DCE2_StackLast is useful in a for loop to * iterate over the items in a stack in backwards order. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: * void * * The data in the previous node in the stack. * NULL if the stack object passed in is NULL, or we are at * the beginning of the stack and there are no previous nodes. * ********************************************************************/ void * DCE2_StackPrev(DCE2_Stack *stack) { if (stack == NULL) return NULL; if (stack->current != NULL) { stack->current = stack->current->prev; if (stack->current != NULL) return stack->current->data; } return NULL; } /******************************************************************** * Function: DCE2_StackEmpty() * * Removes all of the nodes in a stack. Does not delete the stack * object itself. Calls data free function for data if it is * not NULL. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: None * ********************************************************************/ void DCE2_StackEmpty(DCE2_Stack *stack) { DCE2_StackNode *n; if (stack == NULL) return; n = stack->head; while (n != NULL) { DCE2_StackNode *tmp = n->next; if (stack->data_free != NULL) stack->data_free(n->data); DCE2_Free((void *)n, sizeof(DCE2_StackNode), stack->mtype); n = tmp; } stack->head = stack->tail = stack->current = NULL; stack->num_nodes = 0; } /******************************************************************** * Function: DCE2_StackDestroy() * * Destroys the stack object and all of the data associated with it. * * Arguments: * DCE2_Stack * * A pointer to the stack object. * * Returns: None * ********************************************************************/ void DCE2_StackDestroy(DCE2_Stack *stack) { if (stack == NULL) return; DCE2_StackEmpty(stack); DCE2_Free((void *)stack, sizeof(DCE2_Stack), stack->mtype); } /******************************************************************** * Function: DCE2_CQueueNew() * * Creates and initializes a new circular queue object. The * circular queue uses a fixed size array and uses indexes to * indicate the start and end of the queue. This type of * queue can become full since it is a fixed size. Used for * performance reasons since new nodes do not need to be * allocated on the fly. * * Arguments: * int * The size that should be allocated for the circular * queue storage. * DCE2_CQueueDataFree * An optional free function for the data inserted into * the queue. If NULL is passed in, the user will be * responsible for freeing data left in the queue. * DCE2_MemType * The type of memory to associate dynamically allocated * memory with. * * Returns: * DCE2_CQueue * * Pointer to a new queue object. * NULL if unable to allocate memory for the object or the * array for storage. * ********************************************************************/ DCE2_CQueue * DCE2_CQueueNew(int size, DCE2_CQueueDataFree df, DCE2_MemType mtype) { DCE2_CQueue *cqueue; if (size <= 0) return NULL; cqueue = (DCE2_CQueue *)DCE2_Alloc(sizeof(DCE2_CQueue), mtype); if (cqueue == NULL) return NULL; cqueue->data_free = df; cqueue->mtype = mtype; cqueue->queue = DCE2_Alloc(size * sizeof(void *), mtype); if (cqueue->queue == NULL) { DCE2_Free(cqueue, sizeof(DCE2_CQueue), mtype); return NULL; } cqueue->size = size; cqueue->head_idx = 0; cqueue->tail_idx = DCE2_SENTINEL; cqueue->cur_idx = DCE2_SENTINEL; return cqueue; } /******************************************************************** * Function: DCE2_CQueueEnqueue() * * Inserts data into the circular queue. * * Arguments: * DCE2_CQueue * * A pointer to the queue object. * void * * Pointer to the data to insert into the queue. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if the queue is full or the queue object * passed in is NULL. * DCE2_RET__SUCCESS if the data is successfully added to * the queue. * ********************************************************************/ DCE2_Ret DCE2_CQueueEnqueue(DCE2_CQueue *cqueue, void *data) { if (cqueue == NULL) return DCE2_RET__ERROR; if (cqueue->num_nodes == (uint32_t)cqueue->size) return DCE2_RET__ERROR; if (cqueue->tail_idx == DCE2_SENTINEL) cqueue->tail_idx = cqueue->head_idx; cqueue->queue[cqueue->tail_idx] = data; if ((cqueue->tail_idx + 1) == cqueue->size) cqueue->tail_idx = 0; else cqueue->tail_idx++; cqueue->num_nodes++; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_CQueueDequeue() * * Removes and returns the data in the first node in the queue. * Note that the user will have to free the data returned. The * data free function only applies to data that is in the queue * when it is emptied or destroyed. * * Arguments: * DCE2_CQueue * * A pointer to the queue object. * * Returns: * void * * The data in the first node in the queue. * NULL if there are no items in the queue or the queue object * passed in is NULL. * ********************************************************************/ void * DCE2_CQueueDequeue(DCE2_CQueue *cqueue) { void *data; if (cqueue == NULL) return NULL; if (cqueue->num_nodes == 0) return NULL; data = cqueue->queue[cqueue->head_idx]; cqueue->queue[cqueue->head_idx] = NULL; if ((cqueue->head_idx + 1) == cqueue->size) cqueue->head_idx = 0; else cqueue->head_idx++; if (cqueue->head_idx == cqueue->tail_idx) cqueue->tail_idx = DCE2_SENTINEL; cqueue->num_nodes--; return data; } /******************************************************************** * Function: DCE2_CQueueFirst() * * Returns a pointer to the data of the first node in the queue. * Sets a current index to the first node in the queue for * iterating over the queue. * * Arguments: * DCE2_CQueue * * A pointer to the queue object. * * Returns: * void * * The data in the first node in the queue. * NULL if the queue object passed in is NULL, or there are * no items in the queue. * ********************************************************************/ void * DCE2_CQueueFirst(DCE2_CQueue *cqueue) { if (cqueue == NULL) return NULL; if (cqueue->tail_idx == DCE2_SENTINEL) return NULL; cqueue->cur_idx = cqueue->head_idx; return cqueue->queue[cqueue->cur_idx]; } /******************************************************************** * Function: DCE2_CQueueNext() * * Increments the current index in the queue to the next node in * the queue and returns the data associated with it. This in * combination with DCE2_CQueueFirst is useful in a for loop to * iterate over the items in a queue. * * Arguments: * DCE2_CQueue * * A pointer to the queue object. * * Returns: * void * * The data in the next node in the queue. * NULL if the queue object passed in is NULL, or we are at * the end of the queue and there are no next nodes. * ********************************************************************/ void * DCE2_CQueueNext(DCE2_CQueue *cqueue) { if (cqueue == NULL) return NULL; if ((cqueue->tail_idx == DCE2_SENTINEL) || (cqueue->cur_idx == DCE2_SENTINEL)) return NULL; if ((cqueue->cur_idx + 1) == cqueue->size) cqueue->cur_idx = 0; else cqueue->cur_idx++; if (cqueue->cur_idx == cqueue->tail_idx) { cqueue->cur_idx = DCE2_SENTINEL; return NULL; } return cqueue->queue[cqueue->cur_idx]; } /******************************************************************** * Function: DCE2_CQueueEmpty() * * Removes all of the nodes in a queue. Does not delete the queue * object itself or the storage array. Calls data free function * for data if it is not NULL. * * Arguments: * DCE2_CQueue * * A pointer to the queue object. * * Returns: None * ********************************************************************/ void DCE2_CQueueEmpty(DCE2_CQueue *cqueue) { if (cqueue == NULL) return; while (!DCE2_CQueueIsEmpty(cqueue)) { void *data = DCE2_CQueueDequeue(cqueue); if ((data != NULL) && (cqueue->data_free != NULL)) cqueue->data_free(data); } cqueue->num_nodes = 0; cqueue->head_idx = 0; cqueue->tail_idx = DCE2_SENTINEL; cqueue->cur_idx = DCE2_SENTINEL; } /******************************************************************** * Function: DCE2_CQueueDestroy() * * Destroys the queue object and all of the data associated with it. * * Arguments: * DCE2_CQueue * * A pointer to the queue object. * * Returns: None * ********************************************************************/ void DCE2_CQueueDestroy(DCE2_CQueue *cqueue) { if (cqueue == NULL) return; DCE2_CQueueEmpty(cqueue); DCE2_Free((void *)cqueue->queue, (cqueue->size * sizeof(void *)), cqueue->mtype); DCE2_Free((void *)cqueue, sizeof(DCE2_CQueue), cqueue->mtype); } /******************************************************************** * Function: DCE2_CStackNew() * * Creates and initializes a new static sized stack object. The * static stack uses a fixed size array and uses indexes to * indicate the start and end of the stack. This type of * stack can become full since it is a fixed size. Used for * performance reasons since new nodes do not need to be * allocated on the fly. * * Arguments: * int * The size that should be allocated for the static * stack storage. * DCE2_CStackDataFree * An optional free function for the data inserted into * the stack. If NULL is passed in, the user will be * responsible for freeing data left in the stack. * DCE2_MemType * The type of memory to associate dynamically allocated * memory with. * * Returns: * DCE2_CStack * * Pointer to a new stack object. * NULL if unable to allocate memory for the object or the * array for storage. * ********************************************************************/ DCE2_CStack * DCE2_CStackNew(int size, DCE2_CStackDataFree df, DCE2_MemType mtype) { DCE2_CStack *cstack; if (size <= 0) return NULL; cstack = (DCE2_CStack *)DCE2_Alloc(sizeof(DCE2_CStack), mtype); if (cstack == NULL) return NULL; cstack->data_free = df; cstack->mtype = mtype; cstack->stack = DCE2_Alloc(size * sizeof(void *), mtype); if (cstack->stack == NULL) { DCE2_Free(cstack, sizeof(DCE2_CStack), mtype); return NULL; } cstack->size = size; cstack->tail_idx = DCE2_SENTINEL; cstack->cur_idx = DCE2_SENTINEL; return cstack; } /******************************************************************** * Function: DCE2_CStackPush() * * Inserts data into the static stack. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * void * * Pointer to the data to insert into the stack. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if the stack is full or the stack object * passed in is NULL. * DCE2_RET__SUCCESS if the data is successfully added to * the stack. * ********************************************************************/ DCE2_Ret DCE2_CStackPush(DCE2_CStack *cstack, void *data) { if (cstack == NULL) return DCE2_RET__ERROR; if (cstack->num_nodes == (uint32_t)cstack->size) return DCE2_RET__ERROR; if (cstack->tail_idx == DCE2_SENTINEL) cstack->tail_idx = 0; else cstack->tail_idx++; cstack->stack[cstack->tail_idx] = data; cstack->num_nodes++; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_CStackPop() * * Removes and returns the data in the last node in the stack. * Note that the user will have to free the data returned. The * data free function only applies to data that is in the stack * when it is emptied or destroyed. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * * Returns: * void * * The data in the last node in the stack. * NULL if there are no items in the stack or the stack object * passed in is NULL. * ********************************************************************/ void * DCE2_CStackPop(DCE2_CStack *cstack) { void *data; if (cstack == NULL) return NULL; if (cstack->num_nodes == 0) return NULL; data = cstack->stack[cstack->tail_idx]; cstack->stack[cstack->tail_idx] = NULL; if (cstack->tail_idx == 0) cstack->tail_idx = DCE2_SENTINEL; else cstack->tail_idx--; cstack->num_nodes--; return data; } /******************************************************************** * Function: DCE2_CStackTop() * * Returns the data on top of the stack. Does not remove the data * from the stack. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * * Returns: * void * * The data on top of the stack. * NULL if there are no items in the stack or the stack object * passed in is NULL. * ********************************************************************/ void * DCE2_CStackTop(DCE2_CStack *cstack) { if (cstack == NULL) return NULL; if (cstack->num_nodes == 0) return NULL; return cstack->stack[cstack->tail_idx]; } /******************************************************************** * Function: DCE2_CStackFirst() * * Returns a pointer to the data of the first node in the stack * array. Sets a current index to the first node in the stack for * iterating over the stack. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * * Returns: * void * * The data in the first node in the stack. * NULL if the stack object passed in is NULL, or there are * no items in the stack. * ********************************************************************/ void * DCE2_CStackFirst(DCE2_CStack *cstack) { if (cstack == NULL) return NULL; if (cstack->num_nodes == 0) return NULL; cstack->cur_idx = 0; return cstack->stack[cstack->cur_idx]; } /******************************************************************** * Function: DCE2_CStackNext() * * Increments the current index in the stack to the next node in * the stack and returns the data associated with it. This in * combination with DCE2_CStackFirst is useful in a for loop to * iterate over the items in a stack. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * * Returns: * void * * The data in the next node in the stack. * NULL if the stack object passed in is NULL, or we are at * the end of the stack and there are no next nodes. * ********************************************************************/ void * DCE2_CStackNext(DCE2_CStack *cstack) { if (cstack == NULL) return NULL; if ((uint32_t)(cstack->cur_idx + 1) == cstack->num_nodes) return NULL; cstack->cur_idx++; return cstack->stack[cstack->cur_idx]; } /******************************************************************** * Function: DCE2_CStackEmpty() * * Removes all of the nodes in a stack. Does not delete the stack * object itself or the storage array. Calls data free function * for data if it is not NULL. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * * Returns: None * ********************************************************************/ void DCE2_CStackEmpty(DCE2_CStack *cstack) { if (cstack == NULL) return; while (!DCE2_CStackIsEmpty(cstack)) { void *data = DCE2_CStackPop(cstack); if ((data != NULL) && (cstack->data_free != NULL)) cstack->data_free(data); } cstack->num_nodes = 0; cstack->tail_idx = DCE2_SENTINEL; cstack->cur_idx = DCE2_SENTINEL; } /******************************************************************** * Function: DCE2_CStackDestroy() * * Destroys the stack object and all of the data associated with it. * * Arguments: * DCE2_CStack * * A pointer to the stack object. * * Returns: None * ********************************************************************/ void DCE2_CStackDestroy(DCE2_CStack *cstack) { if (cstack == NULL) return; DCE2_CStackEmpty(cstack); DCE2_Free((void *)cstack->stack, (cstack->size * sizeof(void *)), cstack->mtype); DCE2_Free((void *)cstack, sizeof(DCE2_CStack), cstack->mtype); } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_session.h0000644000175000017500000005356214241075657022314 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_SESSION_H_ #define _DCE2_SESSION_H_ #include "dce2_utils.h" #include "dce2_config.h" #include "dce2_memory.h" #include "dce2_roptions.h" #include "dcerpc.h" #include "sf_snort_packet.h" #include "stream_api.h" #include "sf_dynamic_preprocessor.h" /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_SsnFlag { DCE2_SSN_FLAG__NONE = 0x0000, DCE2_SSN_FLAG__SEEN_CLIENT = 0x0001, DCE2_SSN_FLAG__SEEN_SERVER = 0x0002, DCE2_SSN_FLAG__AUTODETECTED = 0x0010, DCE2_SSN_FLAG__PAF_ABORT = 0x0020, DCE2_SSN_FLAG__NO_INSPECT = 0x0040, DCE2_SSN_FLAG__SMB2 = 0x0080, DCE2_SSN_FLAG__ALL = 0xffff } DCE2_SsnFlag; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_SsnData { DCE2_TransType trans; DCE2_Policy server_policy; DCE2_Policy client_policy; int flags; const DCE2_ServerConfig *sconfig; const SFSnortPacket *wire_pkt; uint64_t alert_mask; DCE2_Roptions ropts; int autodetect_dir; uint32_t cli_seq; uint32_t cli_nseq; uint32_t srv_seq; uint32_t srv_nseq; tSfPolicyId policy_id; tSfPolicyUserContextId config; } DCE2_SsnData; /******************************************************************** * Extern variables ********************************************************************/ extern uint8_t dce2_no_inspect; /******************************************************************** * Inline function prototypes ********************************************************************/ static inline int DCE2_SsnIsEstablished(const SFSnortPacket *); static inline int DCE2_SsnIsMidstream(const SFSnortPacket *); static inline void DCE2_SsnSetAppData(const SFSnortPacket *, void *, StreamAppDataFree); static inline void * DCE2_SsnGetAppData(const SFSnortPacket *); static inline int DCE2_SsnGetReassembly(const SFSnortPacket *); static inline void DCE2_SsnSetReassembly(const SFSnortPacket *); static inline int DCE2_SsnIsRebuilt(const SFSnortPacket *); static inline int DCE2_SsnIsStreamInsert(const SFSnortPacket *); static inline void DCE2_SsnFlush(SFSnortPacket *); static inline int DCE2_SsnFromServer(const SFSnortPacket *); static inline int DCE2_SsnFromClient(const SFSnortPacket *); static inline bool DCE2_SsnIsPafActive(const SFSnortPacket *); static inline void DCE2_SsnSetSeenClient(DCE2_SsnData *); static inline int DCE2_SsnSeenClient(DCE2_SsnData *); static inline void DCE2_SsnSetSeenServer(DCE2_SsnData *); static inline int DCE2_SsnSeenServer(DCE2_SsnData *); static inline void DCE2_SsnSetAutodetected(DCE2_SsnData *, const SFSnortPacket *); static inline int DCE2_SsnAutodetected(DCE2_SsnData *); static inline int DCE2_SsnAutodetectDir(DCE2_SsnData *); static inline void DCE2_SsnSetNoInspect(const SFSnortPacket *); static inline int DCE2_SsnNoInspect(DCE2_SsnData *sd); static inline void DCE2_SsnSetPolicy(DCE2_SsnData *, DCE2_Policy); static inline DCE2_Policy DCE2_SsnGetPolicy(DCE2_SsnData *); static inline DCE2_Policy DCE2_SsnGetServerPolicy(DCE2_SsnData *); static inline DCE2_Policy DCE2_SsnGetClientPolicy(DCE2_SsnData *); static inline bool DCE2_SsnIsWindowsPolicy(DCE2_SsnData *); static inline bool DCE2_SsnIsSambaPolicy(DCE2_SsnData *); static inline bool DCE2_SsnIsServerWindowsPolicy(DCE2_SsnData *); static inline bool DCE2_SsnIsServerSambaPolicy(DCE2_SsnData *); /******************************************************************** * Function: DCE2_SsnIsEstablished() * * Purpose: Returns whether or not the session is established * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * int - non-zero if the session is established. * zero if the session is not established. * ********************************************************************/ static inline int DCE2_SsnIsEstablished(const SFSnortPacket *p) { return _dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_ESTABLISHED; } /******************************************************************** * Function: DCE2_SsnIsEstablished() * * Purpose: Returns whether or not the session was picked * up midstream. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * int - non-zero if the session was picked up midstream. * zero if the session was not picked up midstream. * ********************************************************************/ static inline int DCE2_SsnIsMidstream(const SFSnortPacket *p) { return _dpd.sessionAPI->get_session_flags (p->stream_session) & SSNFLAG_MIDSTREAM; } /******************************************************************** * Function: DCE2_SsnSetAppData() * * Purpose: Sets application data associated with session. * * Arguments: * SFSnortPacket * - pointer to packet * void * - pointer to data to store on session. * StreamAppDataFree - free function for freeing data stored * on session * * Note: Both data and free function can be NULL and have the * effect of removing the session data. * * Returns: None * ********************************************************************/ static inline void DCE2_SsnSetAppData(const SFSnortPacket *p, void *data, StreamAppDataFree sdfree) { _dpd.sessionAPI->set_application_data(p->stream_session, PP_DCE2, data, sdfree); } /******************************************************************** * Function: DCE2_SsnGetAppData() * * Purpose: Gets application data stored with session. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * void * - the data stored on the session. * ********************************************************************/ static inline void * DCE2_SsnGetAppData(const SFSnortPacket *p) { return _dpd.sessionAPI->get_application_data(p->stream_session, PP_DCE2); } /******************************************************************** * Function: DCE2_SsnGetReassembly() * * Purpose: Gets reassembly direction for the session. * * Arguments: * DCE2_SsnData * - session data pointer * * Returns: * int - the reassembly direction * SSN_DIR_NONE, SSN_DIR_FROM_CLIENT, SSN_DIR_FROM_SERVER or SSN_DIR_BOTH * ********************************************************************/ static inline int DCE2_SsnGetReassembly(const SFSnortPacket *p) { return (int)_dpd.streamAPI->get_reassembly_direction(p->stream_session); } /******************************************************************** * Function: DCE2_SsnSetReassembly() * * Purpose: Sets reassembly direction for the session to * SSN_DIR_BOTH since the preprocessor looks at both * client and server packets. * * Arguments: * DCE2_SsnData * - session data pointer * * Returns: None * ********************************************************************/ static inline void DCE2_SsnSetReassembly(const SFSnortPacket *p) { _dpd.streamAPI->set_reassembly(p->stream_session, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_BOTH, STREAM_FLPOLICY_SET_ABSOLUTE); } /******************************************************************** * Function: DCE2_SsnIsRebuilt() * * Purpose: Returns whether or not the packet is a stream * reassembled packet. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * int - non-zero if the packet is stream reassembled. * zero if the packet is not stream reassembled. * ********************************************************************/ static inline int DCE2_SsnIsRebuilt(const SFSnortPacket *p) { return (PacketHasPAFPayload(p)); } /******************************************************************** * Function: DCE2_SsnIsStreamInsert() * * Purpose: Returns whether or not the packet is a stream * inserted packet. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * int - non-zero if the packet is stream inserted. * zero if the packet is not stream inserted. * ********************************************************************/ static inline int DCE2_SsnIsStreamInsert(const SFSnortPacket *p) { return p->flags & FLAG_STREAM_INSERT; } /******************************************************************** * Function: DCE2_SsnFlush() * * Purpose: Flushes the stream inserted packets on the opposite * side of the conversation. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: None * ********************************************************************/ static inline void DCE2_SsnFlush(SFSnortPacket *p) { _dpd.streamAPI->response_flush_stream(p); } /******************************************************************** * Function: DCE2_SsnFromServer() * * Purpose: Returns whether or not this packet is from * the server. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * int - non-zero if the packet is from the server. * zero if the packet is not from the server. * ********************************************************************/ static inline int DCE2_SsnFromServer(const SFSnortPacket *p) { return p->flags & FLAG_FROM_SERVER; } /******************************************************************** * Function: DCE2_SsnFromClient() * * Purpose: Returns whether or not this packet is from * the client. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * int - non-zero if the packet is from the client. * zero if the packet is not from the client. * ********************************************************************/ static inline int DCE2_SsnFromClient(const SFSnortPacket *p) { return p->flags & FLAG_FROM_CLIENT; } /******************************************************************** * Function: DCE2_SsnIsPafActive() * * Purpose: Checks stream api to see if PAF is active for both sides * of the session. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * bool - true if paf is active * false if not * ********************************************************************/ static inline bool DCE2_SsnIsPafActive(const SFSnortPacket *p) { if ((p->stream_session == NULL) || (_dpd.streamAPI->is_paf_active(p->stream_session, true) && _dpd.streamAPI->is_paf_active(p->stream_session, false))) return true; return false; } /******************************************************************** * Function: DCE2_SsnSetSeenClient() * * Purpose: Sets a flag that indicates that we have seen the * client side of the conversation. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: None * ********************************************************************/ static inline void DCE2_SsnSetSeenClient(DCE2_SsnData *sd) { sd->flags |= DCE2_SSN_FLAG__SEEN_CLIENT; } /******************************************************************** * Function: DCE2_SsnSeenClient() * * Purpose: Returns whether or not we've seen the client side * of the conversation on this session. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * int - non-zero if we've seen the client * zero if we haven't seen the client * ********************************************************************/ static inline int DCE2_SsnSeenClient(DCE2_SsnData *sd) { return sd->flags & DCE2_SSN_FLAG__SEEN_CLIENT; } /******************************************************************** * Function: DCE2_SsnSetSeenServer() * * Purpose: Sets a flag that indicates that we have seen the * server side of the conversation. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: None * ********************************************************************/ static inline void DCE2_SsnSetSeenServer(DCE2_SsnData *sd) { sd->flags |= DCE2_SSN_FLAG__SEEN_SERVER; } /******************************************************************** * Function: DCE2_SsnSeenServer() * * Purpose: Returns whether or not we've seen the server side * of the conversation on this session. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * int - non-zero if we've seen the server * zero if we haven't seen the server * ********************************************************************/ static inline int DCE2_SsnSeenServer(DCE2_SsnData *sd) { return sd->flags & DCE2_SSN_FLAG__SEEN_SERVER; } /******************************************************************** * Function: DCE2_SsnSetAutodetected() * * Purpose: Sets flag that indicates that this session * was autodetected. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: None * ********************************************************************/ static inline void DCE2_SsnSetAutodetected(DCE2_SsnData *sd, const SFSnortPacket *p) { sd->flags |= DCE2_SSN_FLAG__AUTODETECTED; sd->autodetect_dir = p->flags & (FLAG_FROM_CLIENT | FLAG_FROM_SERVER); } /******************************************************************** * Function: DCE2_SsnAutodetected() * * Purpose: Returns whether or not this session was autodetected. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * int - non-zero if session was autodetected * zero if session was not autodetected * ********************************************************************/ static inline int DCE2_SsnAutodetected(DCE2_SsnData *sd) { return sd->flags & DCE2_SSN_FLAG__AUTODETECTED; } /******************************************************************** * Function: DCE2_SsnAutodetectDir() * * Purpose: Returns what direction the session was autodetected on. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * int - FLAG_FROM_CLIENT if autodetected on client packet * FLAG_FROM_SERVER if autodetected on server packet * zero if session was not autodetected * ********************************************************************/ static inline int DCE2_SsnAutodetectDir(DCE2_SsnData *sd) { return sd->autodetect_dir; } /******************************************************************** * Function: DCE2_SsnClearAutodetected() * * Clears the autodetect flag. To be used after it has been * determined this is definitely DCE/RPC traffic. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: None * ********************************************************************/ static inline void DCE2_SsnClearAutodetected(DCE2_SsnData *sd) { sd->flags &= ~DCE2_SSN_FLAG__AUTODETECTED; sd->autodetect_dir = 0; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_SsnSetNoInspect(const SFSnortPacket *p) { _dpd.sessionAPI->set_application_data(p->stream_session, PP_DCE2, (void *)&dce2_no_inspect, NULL); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DCE2_SsnNoInspect(DCE2_SsnData *sd) { return (void *)sd == (void *)&dce2_no_inspect; } /******************************************************************** * Function: DCE2_SsnSetPolicy() * * Purpose: * Convenience function to set policy for session, client or server. * Sets policy for the current direction packet is from. * * Arguments: * DCE2_SsnData * - pointer to session data * DCE2_Policy - the policy to set client/server to * * Returns: None * ********************************************************************/ static inline void DCE2_SsnSetPolicy(DCE2_SsnData *sd, DCE2_Policy policy) { if (DCE2_SsnFromClient(sd->wire_pkt)) sd->client_policy = policy; else sd->server_policy = policy; } /******************************************************************** * Function: DCE2_SsnGetPolicy() * * Purpose: * Convenience function to get policy for session, client or server * determined by the direction the traffic is flowing to. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: None * DCE2_Policy - If from the client returns server policy. * If from server returns client policy. * ********************************************************************/ static inline DCE2_Policy DCE2_SsnGetPolicy(DCE2_SsnData *sd) { if (DCE2_SsnFromClient(sd->wire_pkt)) return sd->server_policy; else return sd->client_policy; } /******************************************************************** * Function: DCE2_SsnGetServerPolicy() * * Purpose: * Returns server policy for session * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: None * DCE2_Policy - The server policy * ********************************************************************/ static inline DCE2_Policy DCE2_SsnGetServerPolicy(DCE2_SsnData *sd) { return sd->server_policy; } /******************************************************************** * Function: DCE2_SsnGetClientPolicy() * * Purpose: * Returns client policy for session * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: None * DCE2_Policy - The client policy * ********************************************************************/ static inline DCE2_Policy DCE2_SsnGetClientPolicy(DCE2_SsnData *sd) { return sd->client_policy; } /******************************************************************** * Function: DCE2_SsnIsWindowsPolicy() * * Purpose: * Convenience function to determine if policy traffic is going to * is a Windows one. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * bool - true if Samba, false if not * ********************************************************************/ static inline bool DCE2_SsnIsWindowsPolicy(DCE2_SsnData *sd) { DCE2_Policy policy = DCE2_SsnGetPolicy(sd); switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: return true; default: break; } return false; } /******************************************************************** * Function: DCE2_SsnIsSambaPolicy() * * Purpose: * Convenience function to determine if policy traffic is going to * is a Samba one. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * bool - true if Samba, false if not * ********************************************************************/ static inline bool DCE2_SsnIsSambaPolicy(DCE2_SsnData *sd) { DCE2_Policy policy = DCE2_SsnGetPolicy(sd); switch (policy) { case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: return true; default: break; } return false; } /******************************************************************** * Function: DCE2_SsnIsServerWindowsPolicy() * * Purpose: * Convenience function to determine if server policy is a * Windows one. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * bool - true if Samba, false if not * ********************************************************************/ static inline bool DCE2_SsnIsServerWindowsPolicy(DCE2_SsnData *sd) { DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: return true; default: break; } return false; } /******************************************************************** * Function: DCE2_SsnIsServerSambaPolicy() * * Purpose: * Convenience function to determine if server policy is a * Samba one. * * Arguments: * DCE2_SsnData * - pointer to session data * * Returns: * bool - true if Samba, false if not * ********************************************************************/ static inline bool DCE2_SsnIsServerSambaPolicy(DCE2_SsnData *sd) { DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); switch (policy) { case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: return true; default: break; } return false; } #endif /* _DCE2_SESSION_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dcerpc2_buffer_dump.h0000644000175000017500000000340114241075676023620 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file dcerpc2_buffer_dump.h ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during DCERPC2 inspection. */ #ifndef __DCERPC2_BUFFER_DUMP_H__ #define __DCERPC2_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { DCERPC_SMB1_DUMP, DCERPC_SMB2_DUMP, DCERPC_TCP_DUMP, DCERPC_UDP_DUMP, DCERPC_HTTP_STATE_CLIENT_DUMP, DCERPC_HTTP_STATE_SERVER_DUMP, DCERPC_HTTP_STATE_RPC_DATA_DUMP } DCERPC2_BUFFER_DUMP; void dumpBuffer(DCERPC2_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getDCERPC2Buffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_co.h0000644000175000017500000001022014241075630021201 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _DCE2_CO_H_ #define _DCE2_CO_H_ #include "dce2_session.h" #include "dce2_list.h" #include "dce2_utils.h" #include "dcerpc.h" #include "sf_types.h" #include "sf_snort_packet.h" /******************************************************************** * Macros ********************************************************************/ #define DCE2_MOCK_HDR_LEN__CO_CLI (sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest)) #define DCE2_MOCK_HDR_LEN__CO_SRV (sizeof(DceRpcCoHdr) + sizeof(DceRpcCoResponse)) /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_CoFragTracker { DCE2_Buffer *cli_stub_buf; DCE2_Buffer *srv_stub_buf; int opnum; /* Opnum that is ultimatley used for request */ int ctx_id; /* Context id that is ultimatley used for request */ /* These are set on a first fragment received */ int expected_call_id; /* Expected call id for fragments */ int expected_opnum; /* Expected call id for fragments */ int expected_ctx_id; /* Expected call id for fragments */ } DCE2_CoFragTracker; typedef struct _DCE2_CoSeg { DCE2_Buffer *buf; /* If there is enough data in segmentation buffer for header, * this will be set to the frag length in the header */ uint16_t frag_len; } DCE2_CoSeg; typedef struct _DCE2_CoTracker { DCE2_List *ctx_ids; /* splayed list so most recently used goes to front of list */ int got_bind; /* got an accepted bind */ /* Queue of pending client bind or alter context request context items * Since the actual context id number doesn't have to occur sequentially * in the context list in the client packet, need to keep track to match * up server response since server doesn't reply with actual context id * numbers, but in the order they were in the client packet */ DCE2_Queue *pending_ctx_ids; /* Keeps track of fragmentation buffer and frag specfic data */ DCE2_CoFragTracker frag_tracker; int max_xmit_frag; /* The maximum negotiated size of a client request */ int data_byte_order; /* Depending on policy is from bind or request */ int ctx_id; /* The current context id of the request */ int opnum; /* The current opnum of the request */ int call_id; /* The current call id of the request */ const uint8_t *stub_data; /* Current pointer to stub data in the request */ /* For transport segmentation */ DCE2_CoSeg cli_seg; DCE2_CoSeg srv_seg; } DCE2_CoTracker; /******************************************************************** * Public function prototypes ********************************************************************/ void DCE2_CoInitRdata(uint8_t *, int); void DCE2_CoProcess(DCE2_SsnData *, DCE2_CoTracker *, const uint8_t *, uint16_t); void DCE2_CoInitTracker(DCE2_CoTracker *); void DCE2_CoCleanTracker(DCE2_CoTracker *); #endif /* _DCE2_CO_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_utils.c0000644000175000017500000003575314241075673021764 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "dce2_utils.h" #include "dce2_debug.h" #include "dce2_config.h" #include "snort_dce2.h" #include "sf_types.h" #include "snort_bounds.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ DCE2_Buffer * DCE2_BufferNew(uint32_t initial_size, uint32_t min_add_size, DCE2_MemType mem_type) { DCE2_Buffer *buf = (DCE2_Buffer *)DCE2_Alloc(sizeof(DCE2_Buffer), mem_type); if (buf == NULL) return NULL; if (initial_size != 0) { buf->data = (uint8_t *)DCE2_Alloc(initial_size, mem_type); if (buf->data == NULL) { DCE2_Free((void *)buf, sizeof(DCE2_Buffer), mem_type); return NULL; } } buf->size = initial_size; buf->len = 0; buf->mtype = mem_type; buf->min_add_size = min_add_size; buf->offset = 0; return buf; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ DCE2_Ret DCE2_BufferAddData(DCE2_Buffer *buf, const uint8_t *data, uint32_t data_len, uint32_t data_offset, DCE2_BufferMinAddFlag mflag) { DCE2_Ret status; if ((buf == NULL) || (data == NULL)) return DCE2_RET__ERROR; /* Return success for this since ultimately nothing _was_ added */ if (data_len == 0) return DCE2_RET__SUCCESS; if (buf->data == NULL) { uint32_t size = data_offset + data_len; if ((size < buf->min_add_size) && (mflag == DCE2_BUFFER_MIN_ADD_FLAG__USE)) size = buf->min_add_size; buf->data = (uint8_t *)DCE2_Alloc(size, buf->mtype); if (buf->data == NULL) return DCE2_RET__ERROR; buf->size = size; } else if ((data_offset + data_len) > buf->size) { uint8_t *tmp; uint32_t new_size = data_offset + data_len; if (((new_size - buf->size) < buf->min_add_size) && (mflag == DCE2_BUFFER_MIN_ADD_FLAG__USE)) new_size = buf->size + buf->min_add_size; tmp = (uint8_t *)DCE2_ReAlloc(buf->data, buf->size, new_size, buf->mtype); if (tmp == NULL) return DCE2_RET__ERROR; buf->data = tmp; buf->size = new_size; } status = DCE2_Memcpy(buf->data + data_offset, data, data_len, buf->data, buf->data + buf->size); if (status != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to copy data into buffer.", __FILE__, __LINE__); return DCE2_RET__ERROR; } if ((data_offset + data_len) > buf->len) buf->len = data_offset + data_len; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: * * Must have allocated data in buffer and data_len must fit in * buffer. * * Arguments: * * Returns: * ********************************************************************/ DCE2_Ret DCE2_BufferMoveData(DCE2_Buffer *buf, uint32_t data_offset, const uint8_t *move, uint32_t move_len) { DCE2_Ret status; uint8_t *offset, *end; if ((buf == NULL) || (buf->data == NULL) || (move == NULL)) return DCE2_RET__ERROR; /* Return success for this since ultimately nothing _was_ moved */ if (move_len == 0) return DCE2_RET__SUCCESS; offset = buf->data + data_offset; end = buf->data + buf->len; /* Moved data must be within current data */ if ((move < buf->data) || ((move + move_len) > end)) return DCE2_RET__ERROR; /* No move required */ if (move == offset) return DCE2_RET__SUCCESS; /* Would have to do two moves. One for the data and one to realign data * with start of moved data. Don't want to succeed on the first and fail * on the second and leave the buffer in a bad state. Don't want to use * an offset in data buffer because want to keep the size the same. */ if (move == buf->data) { uint32_t tmp_size = buf->len; uint8_t *tmp = (uint8_t *)DCE2_Alloc(tmp_size, buf->mtype); uint8_t *tmp_offset, *tmp_end; uint32_t new_len; if (tmp == NULL) return DCE2_RET__ERROR; tmp_offset = tmp + data_offset; tmp_end = tmp + tmp_size; status = DCE2_Memcpy(tmp, buf->data, buf->len, tmp, tmp_end); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)tmp, tmp_size, buf->mtype); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to move data in buffer.", __FILE__, __LINE__); return DCE2_RET__ERROR; } status = DCE2_Memmove(tmp_offset, tmp, move_len, tmp_offset, tmp_end); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)tmp, tmp_size, buf->mtype); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to move data in buffer.", __FILE__, __LINE__); return DCE2_RET__ERROR; } if (tmp_offset > (tmp + move_len)) tmp_offset = tmp + move_len; new_len = tmp_end - tmp_offset; status = DCE2_Memcpy(buf->data, tmp_offset, new_len, buf->data, end); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)tmp, tmp_size, buf->mtype); DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to move data in buffer.", __FILE__, __LINE__); return DCE2_RET__ERROR; } buf->len = new_len; DCE2_Free((void *)tmp, tmp_size, buf->mtype); } else { status = DCE2_Memmove(offset, move, move_len, offset, end); if (status != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to move data in buffer", __FILE__, __LINE__); return DCE2_RET__ERROR; } /* If we have a new end of data, adjust length */ if ((move + move_len) == end) buf->len = data_offset + move_len; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_BufferDestroy(DCE2_Buffer *buf) { if (buf == NULL) return; if (buf->data != NULL) DCE2_Free((void *)buf->data, buf->size, buf->mtype); DCE2_Free((void *)buf, sizeof(DCE2_Buffer), buf->mtype); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ DCE2_Ret DCE2_HandleSegmentation(DCE2_Buffer *seg_buf, const uint8_t *data_ptr, uint16_t data_len, uint32_t need_len, uint16_t *data_used) { uint32_t copy_len; DCE2_Ret status; /* Initialize in case we return early without adding * any data to the buffer */ *data_used = 0; if (seg_buf == NULL) return DCE2_RET__ERROR; /* Don't need anything - call it desegmented. Really return * an error - this shouldn't happen */ if (need_len == 0) return DCE2_RET__ERROR; /* Already have enough data for need */ if (DCE2_BufferLength(seg_buf) >= need_len) return DCE2_RET__SUCCESS; /* No data and need length > 0 - must still be segmented */ if (data_len == 0) return DCE2_RET__SEG; /* Already know that need length is greater than buffer length */ copy_len = need_len - DCE2_BufferLength(seg_buf); if (copy_len > data_len) copy_len = data_len; status = DCE2_BufferAddData(seg_buf, data_ptr, copy_len, DCE2_BufferLength(seg_buf), DCE2_BUFFER_MIN_ADD_FLAG__USE); if (status != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; /* copy_len <= data_len <= UINT16_MAX */ *data_used = (uint16_t)copy_len; if (DCE2_BufferLength(seg_buf) == need_len) return DCE2_RET__SUCCESS; return DCE2_RET__SEG; } /******************************************************************** * Function: DCE2_Die() * * Purpose: Fatal errors. Calls DynamicPreprocessorFatalMessage. * It's just quicker to type. * * Arguments: None * const char * - format string * ... - format arguments * * Returns: None * ********************************************************************/ NORETURN void DCE2_Die(const char *format, ...) { char buf[1024]; va_list ap; DCE2_FreeGlobals(); if (format == NULL) { _dpd.errMsg("ERROR: %s(%d) => %s: format is NULL.\n", __FILE__, __LINE__, DCE2_GNAME); DynamicPreprocessorFatalMessage("%s: Dieing.\n", DCE2_GNAME, buf); } va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); buf[sizeof(buf) - 1] = '\0'; DynamicPreprocessorFatalMessage("%s: %s\n", DCE2_GNAME, buf); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_Log(DCE2_LogType ltype, const char *format, ...) { char buf[1024]; va_list ap; if (format == NULL) { _dpd.errMsg("ERROR: %s(%d) => %s: format is NULL.\n", __FILE__, __LINE__, DCE2_GNAME); return; } va_start(ap, format); vsnprintf(buf, sizeof(buf), format, ap); va_end(ap); buf[sizeof(buf) - 1] = '\0'; switch (ltype) { case DCE2_LOG_TYPE__LOG: _dpd.logMsg("LOG: %s: %s\n", DCE2_GNAME, buf); break; case DCE2_LOG_TYPE__WARN: _dpd.errMsg("WARN: %s: %s\n", DCE2_GNAME, buf); break; case DCE2_LOG_TYPE__ERROR: _dpd.errMsg("ERROR: %s: %s\n", DCE2_GNAME, buf); break; default: _dpd.errMsg("ERROR: %s(%d) => %s: Invalid log type: %d.\n", __FILE__, __LINE__, DCE2_GNAME, ltype); break; } } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ const char * DCE2_UuidToStr(const Uuid *uuid, DceRpcBoFlag byte_order) { #define UUID_BUF_SIZE 50 static char uuid_buf1[UUID_BUF_SIZE]; static char uuid_buf2[UUID_BUF_SIZE]; static int buf_num = 0; char *uuid_buf; if (buf_num == 0) { uuid_buf = uuid_buf1; buf_num = 1; } else { uuid_buf = uuid_buf2; buf_num = 0; } snprintf(uuid_buf, UUID_BUF_SIZE, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", DceRpcHtonl(&uuid->time_low, byte_order), DceRpcHtons(&uuid->time_mid, byte_order), DceRpcHtons(&uuid->time_high_and_version, byte_order), uuid->clock_seq_and_reserved, uuid->clock_seq_low, uuid->node[0], uuid->node[1], uuid->node[2], uuid->node[3], uuid->node[4], uuid->node[5]); uuid_buf[UUID_BUF_SIZE - 1] = '\0'; return uuid_buf; } #ifdef DEBUG_MSGS /******************************************************************** * Function: DCE2_PrintPktData() * * Purpose: Prints packet data in hex and ascii. * * Arguments: None * const uint8_t * - pointer to data to print * const uint16_t - size of data * * Returns: None * ********************************************************************/ void DCE2_PrintPktData(const uint8_t *data, const uint16_t len) { unsigned int i, j = 0, line_len = 0; uint8_t hex_buf[16]; uint8_t char_buf[16]; for (i = 0; i < len; i++) { hex_buf[j] = data[i]; if (isascii((int)data[i]) && isprint((int)data[i])) char_buf[j] = data[i]; else char_buf[j] = '.'; if (line_len == 15) { unsigned int k, sub_line_len = 0; for (k = 0; k <= j; k++) { printf("%02x ", hex_buf[k]); if (sub_line_len >= 7) { printf(" "); sub_line_len = 0; } else { sub_line_len++; } } printf(" "); sub_line_len = 0; for (k = 0; k <= j; k++) { printf("%c", char_buf[k]); if (sub_line_len >= 7) { printf(" "); sub_line_len = 0; } else { sub_line_len++; } } printf("\n"); j = line_len = 0; } else { j++; line_len++; } } if (line_len > 0) { unsigned int k, sub_line_len = 0; for (k = 0; k < j; k++) { printf("%02x ", hex_buf[k]); if (sub_line_len >= 7) { printf(" "); sub_line_len = 0; } else { sub_line_len++; } } if (k < 8) printf(" "); else printf(" "); while (k < 16) { printf(" "); k++; } sub_line_len = 0; for (k = 0; k < j; k++) { printf("%c", char_buf[k]); if (sub_line_len >= 7) { printf(" "); sub_line_len = 0; } else { sub_line_len++; } } } printf("\n"); } #endif snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_memory.h0000644000175000017500000001511714241075651022125 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_MEMORY_H_ #define _DCE2_MEMORY_H_ /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_MemType { DCE2_MEM_TYPE__CONFIG, /* Configuration */ DCE2_MEM_TYPE__ROPTION, /* Rule options */ DCE2_MEM_TYPE__RT, /* Routing tables */ DCE2_MEM_TYPE__INIT, /* Other initialization */ DCE2_MEM_TYPE__SMB_SSN, /* SMB session data */ DCE2_MEM_TYPE__SMB_SEG, /* SMB segmentation buffer */ DCE2_MEM_TYPE__SMB_UID, /* SMB uid tracking */ DCE2_MEM_TYPE__SMB_TID, /* SMB tid tracking */ DCE2_MEM_TYPE__SMB_FID, /* SMB fid tracking */ DCE2_MEM_TYPE__SMB_FILE, /* SMB file tracking */ DCE2_MEM_TYPE__SMB_REQ, /* SMB request/response tracking */ DCE2_MEM_TYPE__TCP_SSN, /* TCP session data */ DCE2_MEM_TYPE__CO_SEG, /* TCP segmentation buffer */ DCE2_MEM_TYPE__CO_FRAG, /* TCP fragmentation data */ DCE2_MEM_TYPE__CO_CTX, /* TCP context tracking */ DCE2_MEM_TYPE__UDP_SSN, /* UDP session data */ DCE2_MEM_TYPE__CL_ACT, /* UDP activity tracking */ DCE2_MEM_TYPE__CL_FRAG, /* UDP fragment tracking */ DCE2_MEM_TYPE__HTTP_SSN /* HTTP session data */ } DCE2_MemType; typedef enum _DCE2_MemState { DCE2_MEM_STATE__OKAY, DCE2_MEM_STATE__MEMCAP } DCE2_MemState; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_Memory { uint32_t total; uint32_t total_max; uint32_t rtotal; /* Run time total */ uint32_t rtotal_max; uint32_t config; uint32_t config_max; uint32_t roptions; uint32_t roptions_max; uint32_t rt; uint32_t rt_max; uint32_t init; uint32_t init_max; uint32_t smb_total; /* total memory allocated for SMB */ uint32_t smb_total_max; /* max total memory allocated for SMB */ uint32_t smb_ssn; /* amount allocated for session structures */ uint32_t smb_ssn_max; /* max amount allocated for session structures */ uint32_t smb_seg; /* amount allocated for segmentation buffers */ uint32_t smb_seg_max; /* max amount allocated for segmentation buffers */ uint32_t smb_uid; /* amount allocated for uid tracking */ uint32_t smb_uid_max; /* max amount allocated for uid tracking */ uint32_t smb_tid; /* amount allocated for tid tracking */ uint32_t smb_tid_max; /* max amount allocated for tid tracking */ uint32_t smb_fid; /* amount allocated for fid tracking */ uint32_t smb_fid_max; /* max amount allocated for fid tracking */ uint32_t smb_file; /* amount allocated for file tracking */ uint32_t smb_file_max; /* max amount allocated for file tracking */ uint32_t smb_req; /* amount allocated for request tracking */ uint32_t smb_req_max; /* max amount allocated for request tracking */ uint32_t tcp_total; /* total memory allocated for TCP */ uint32_t tcp_total_max; /* max total memory allocated for TCP */ uint32_t tcp_ssn; /* amount allocated for session structures */ uint32_t tcp_ssn_max; /* max amount allocated for session structures */ uint32_t udp_total; /* total memory allocated for UDP */ uint32_t udp_total_max; /* max total memory allocated for UDP */ uint32_t udp_ssn; /* amount allocated for session structures */ uint32_t udp_ssn_max; /* max amount allocated for session structures */ uint32_t http_total; /* total memory allocated for UDP */ uint32_t http_total_max; /* max total memory allocated for UDP */ uint32_t http_ssn; /* amount allocated for session structures */ uint32_t http_ssn_max; /* max amount allocated for session structures */ uint32_t co_total; /* total memory allocated for CO */ uint32_t co_total_max; /* max total memory allocated for CO */ uint32_t co_seg; /* amount allocated for segmentation */ uint32_t co_seg_max; /* max amount allocated for segmentation */ uint32_t co_frag; /* amount allocated for frag tracking */ uint32_t co_frag_max; /* max amount allocated for frag tracking */ uint32_t co_ctx; /* amount allocated for contexts */ uint32_t co_ctx_max; /* max amount allocated for contexts */ uint32_t cl_total; /* total memory allocated for CL */ uint32_t cl_total_max; /* max total memory allocated for CL */ uint32_t cl_act; /* amount allocated for activity trackers */ uint32_t cl_act_max; /* max amount allocated for activity trackers */ uint32_t cl_frag; /* amount allocated for frag tracking */ uint32_t cl_frag_max; /* max amount allocated for frag tracking */ } DCE2_Memory; /******************************************************************** * Extern variables ********************************************************************/ extern DCE2_Memory dce2_memory; extern DCE2_MemState dce2_mem_state; /******************************************************************** * Public functions ********************************************************************/ void DCE2_RegMem(uint32_t, DCE2_MemType); void DCE2_UnRegMem(uint32_t, DCE2_MemType); void * DCE2_Alloc(uint32_t, DCE2_MemType); void DCE2_Free(void *, uint32_t, DCE2_MemType); void * DCE2_ReAlloc(void *, uint32_t, uint32_t, DCE2_MemType); void DCE2_FreeAll(void); void DCE2_MemInit(void); size_t DCE2_MemInUse(); #endif /* _DCE2_MEMORY_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_udp.c0000644000175000017500000000647714241075671021413 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_udp.h" #include "snort_dce2.h" #include "dce2_cl.h" #include "dce2_memory.h" #include "dce2_stats.h" #include "sf_types.h" #ifdef DUMP_BUFFER #include "dcerpc2_buffer_dump.h" #endif /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ DCE2_UdpSsnData * DCE2_UdpSsnInit(void) { DCE2_UdpSsnData *usd = DCE2_Alloc(sizeof(DCE2_UdpSsnData), DCE2_MEM_TYPE__UDP_SSN); if (usd == NULL) return NULL; DCE2_ResetRopts(&usd->sd.ropts); dce2_stats.udp_sessions++; return usd; } /******************************************************************** * Function: DCE2_UdpProcess() * * Purpose: Main entry point for DCE/RPC over UDP processing. * * Arguments: * DCE2_UdpSsnData * - a pointer to the data structure associated * with this session. * * Returns: None * ********************************************************************/ void DCE2_UdpProcess(DCE2_UdpSsnData *usd) { #ifdef DUMP_BUFFER dumpBuffer(DCERPC_UDP_DUMP,usd->sd.wire_pkt->payload,usd->sd.wire_pkt->payload_size); #endif dce2_stats.udp_pkts++; DCE2_ClProcess(&usd->sd, &usd->cl_tracker); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_UdpDataFree(DCE2_UdpSsnData *usd) { if (usd == NULL) return; DCE2_ClCleanTracker(&usd->cl_tracker); } /******************************************************************** * Function: DCE2_UdpSsnFree() * * Purpose: Callback to session for freeing sessiond data. * * Arguments: * void * - pointer to the memory to be freed. * * Returns: None * ********************************************************************/ void DCE2_UdpSsnFree(void *data) { DCE2_UdpSsnData *usd = (DCE2_UdpSsnData *)data; if (usd == NULL) return; DCE2_UdpDataFree(usd); DCE2_Free((void *)usd, sizeof(DCE2_UdpSsnData), DCE2_MEM_TYPE__UDP_SSN); } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_event.c0000644000175000017500000004677114241075635021745 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Handles processing of events generated by the preprocessor. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_event.h" #include "dce2_memory.h" #include "dce2_config.h" #include "dce2_stats.h" #include "smb.h" #include "dcerpc.h" #include "sf_dynamic_preprocessor.h" #include #include /******************************************************************** * Global variables ********************************************************************/ /* Used to print events and their arguments to. Each event gets * a buffer and 255 chars to print to. The reason for the multiple * buffers is that if multiple events fire, we don't want to overwrite * one before it's been written via an output plugin. Only one event * type per session is ever logged. */ static char dce2_event_bufs[DCE2_EVENT__MAX][256]; /* Used to hold event information */ static DCE2_EventNode dce2_events[DCE2_EVENT__MAX]; /* Used for matching a pdu string to a pdu type */ char *dce2_pdu_types[DCERPC_PDU_TYPE__MAX]; /****************************************************************** * Function: DCE2_EventsInit() * * Initializes global data. * * Arguments: None * * Returns: None * ******************************************************************/ void DCE2_EventsInit(void) { DCE2_Event event; char gname[100]; unsigned int i; static const DCE2_EventNode events[DCE2_EVENT__MAX] = { { DCE2_EVENT_FLAG__NONE, DCE2_EVENT__NO_EVENT, "Have to use this because can't have an event sid of zero" }, { DCE2_EVENT_FLAG__MEMCAP, DCE2_EVENT__MEMCAP, "Memory cap exceeded" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_NBSS_TYPE, "SMB - Bad NetBIOS Session Service session type" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_TYPE, "SMB - Bad SMB message type" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_ID, "SMB - Bad SMB Id (not \\xffSMB for SMB1 or not \\xfeSMB for SMB2)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_WCT, "SMB - Bad word count or structure size: %u" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_BCC, "SMB - Bad byte count: %u" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_FORMAT, "SMB - Bad format type: %u" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_OFF, "SMB - Bad offset: %p not between %p and %p" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_TDCNT_ZERO, "SMB - Zero total data count" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_NB_LT_SMBHDR, "SMB - NetBIOS data length (%u) less than SMB header length (%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_NB_LT_COM, "SMB - Remaining NetBIOS data length (%u) less than command length (%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_NB_LT_BCC, "SMB - Remaining NetBIOS data length (%u) less than command byte count (%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_NB_LT_DSIZE, "SMB - Remaining NetBIOS data length (%u) less than command data size (%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_TDCNT_LT_DSIZE, "SMB - Remaining total data count (%u) less than this command data size (%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_DSENT_GT_TDCNT, "SMB - Total data sent ("STDu64") greater than command total data expected (%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BCC_LT_DSIZE, "SMB - Byte count (%u) less than command data size ("STDu64")" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_INVALID_DSIZE, "SMB - Invalid command data size (%u) for byte count (%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_EXCESSIVE_TREE_CONNECTS, "SMB - Excessive Tree Connect requests (>%u) with pending Tree Connect responses" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_EXCESSIVE_READS, "SMB - Excessive Read requests (>%u) with pending Read responses" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_EXCESSIVE_CHAINING, "SMB - Excessive command chaining (>%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_MULT_CHAIN_SS, "SMB - Multiple chained login requests" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_MULT_CHAIN_TC, "SMB - Multiple chained tree connect requests" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_CHAIN_SS_LOGOFF, "SMB - Chained/Compounded login followed by logoff" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_CHAIN_TC_TDIS, "SMB - Chained/Compounded tree connect followed by tree disconnect" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_CHAIN_OPEN_CLOSE, "SMB - Chained/Compounded open pipe followed by close pipe" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_INVALID_SHARE, "SMB - Invalid share access: %s" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_BAD_MAJ_VERSION, "Connection-oriented DCE/RPC - Invalid major version: %u" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_BAD_MIN_VERSION, "Connection-oriented DCE/RPC - Invalid minor version: %u" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_BAD_PDU_TYPE, "Connection-oriented DCE/RPC - Invalid pdu type: 0x%02x" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_FLEN_LT_HDR, "Connection-oriented DCE/RPC - Fragment length (%u) less than header size (%u)" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_FLEN_LT_SIZE, "Connection-oriented DCE/RPC - %s: Remaining fragment length (%u) less than size needed (%u)" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_ZERO_CTX_ITEMS, "Connection-oriented DCE/RPC - %s: No context items specified" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_ZERO_TSYNS, "Connection-oriented DCE/RPC - %s: No transfer syntaxes specified" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG, "Connection-oriented DCE/RPC - %s: Fragment length on non-last fragment (%u) less than " "maximum negotiated fragment transmit size for client (%u)" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG, "Connection-oriented DCE/RPC - %s: Fragment length (%u) greater than " "maximum negotiated fragment transmit size (%u)" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_ALTER_CHANGE_BYTE_ORDER, "Connection-oriented DCE/RPC - Alter Context byte order different from Bind" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_FRAG_DIFF_CALL_ID, "Connection-oriented DCE/RPC - Call id (%u) of non first/last fragment different " "from call id established for fragmented request (%u)" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_FRAG_DIFF_OPNUM, "Connection-oriented DCE/RPC - Opnum (%u) of non first/last fragment different " "from opnum established for fragmented request (%u)" }, { DCE2_EVENT_FLAG__CO, DCE2_EVENT__CO_FRAG_DIFF_CTX_ID, "Connection-oriented DCE/RPC - Context id (%u) of non first/last fragment different " "from context id established for fragmented request (%u)" }, { DCE2_EVENT_FLAG__CL, DCE2_EVENT__CL_BAD_MAJ_VERSION, "Connection-less DCE/RPC - Invalid major version: %u" }, { DCE2_EVENT_FLAG__CL, DCE2_EVENT__CL_BAD_PDU_TYPE, "Connection-less DCE/RPC - Invalid pdu type: 0x%02x" }, { DCE2_EVENT_FLAG__CL, DCE2_EVENT__CL_DATA_LT_HDR, "Connection-less DCE/RPC - Data length (%u) less than header size (%u)" }, { DCE2_EVENT_FLAG__CL, DCE2_EVENT__CL_BAD_SEQ_NUM, "Connection-less DCE/RPC - %s: Bad sequence number" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_V1, "SMB - Invalid SMB version 1 seen" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_V2, "SMB - Invalid SMB version 2 seen" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_INVALID_BINDING, "SMB - Invalid user, tree connect, file binding" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB2_EXCESSIVE_COMPOUNDING, "SMB - Excessive command compounding (>%u)" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_DCNT_ZERO, "SMB - Zero data count" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_DCNT_MISMATCH, "SMB - Data count mismatch %u in command / %u in format" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_MAX_REQS_EXCEEDED, "SMB - Maximum number of outstanding requests exceeded: %u" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_REQS_SAME_MID, "SMB - Outstanding requests with same MID", }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_DEPR_DIALECT_NEGOTIATED, "SMB - Deprecated dialect negotiated" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_DEPR_COMMAND_USED, "SMB - Deprecated command used: %s" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_UNUSUAL_COMMAND_USED, "SMB - Unusual command used: %s" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_INVALID_SETUP_COUNT, "SMB - Invalid setup count for %s/%s command: %u" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_MULTIPLE_NEGOTIATIONS, "SMB - Client attempted multiple dialect negotiations on session" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS, "SMB - Client attempted to create or set a file's attributes to readonly/hidden/system" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_INVALID_FILE_OFFSET, "SMB - File offset provided is greater than file size specified" }, { DCE2_EVENT_FLAG__SMB, DCE2_EVENT__SMB_BAD_NEXT_COMMAND_OFFSET, "SMB - Nextcommand specified in SMB2 header is beyond payload boundary" } }; snprintf(gname, sizeof(gname) - 1, "(%s) ", DCE2_GNAME); gname[sizeof(gname) - 1] = '\0'; for (event = DCE2_EVENT__NO_EVENT; event < DCE2_EVENT__MAX; event++) { int size = strlen(gname) + strlen(events[event].format) + 1; /* This is a check to make sure all of the events in the array are * in the same order as the enum, so we index the right thing when * alerting - DO NOT REMOVE THIS CHECK */ if (events[event].event != event) { DCE2_Die("%s(%d) Events are not in the right order.", __FILE__, __LINE__); } dce2_events[event].format = (char *)DCE2_Alloc(size, DCE2_MEM_TYPE__INIT); if (dce2_events[event].format == NULL) { DCE2_Die("%s(%d) Could not allocate memory for events array.", __FILE__, __LINE__); } dce2_events[event].format[size - 1] = '\0'; snprintf(dce2_events[event].format, size, "%s%s", gname, events[event].format); if (dce2_events[event].format[size - 1] != '\0') { DCE2_Die("%s(%d) Event string truncated.", __FILE__, __LINE__); } dce2_events[event].eflag = events[event].eflag; dce2_events[event].event = events[event].event; } for (i = 0; i < (sizeof(dce2_pdu_types) / sizeof(char *)); i++) { char *type; switch (i) { case DCERPC_PDU_TYPE__REQUEST: type = "Request"; break; case DCERPC_PDU_TYPE__PING: type = "Ping"; break; case DCERPC_PDU_TYPE__RESPONSE: type = "Response"; break; case DCERPC_PDU_TYPE__FAULT: type = "Fault"; break; case DCERPC_PDU_TYPE__WORKING: type = "Working"; break; case DCERPC_PDU_TYPE__NOCALL: type = "NoCall"; break; case DCERPC_PDU_TYPE__REJECT: type = "Reject"; break; case DCERPC_PDU_TYPE__ACK: type = "Ack"; break; case DCERPC_PDU_TYPE__CL_CANCEL: type = "Cancel"; break; case DCERPC_PDU_TYPE__FACK: type = "Fack"; break; case DCERPC_PDU_TYPE__CANCEL_ACK: type = "Cancel Ack"; break; case DCERPC_PDU_TYPE__BIND: type = "Bind"; break; case DCERPC_PDU_TYPE__BIND_ACK: type = "Bind Ack"; break; case DCERPC_PDU_TYPE__BIND_NACK: type = "Bind Nack"; break; case DCERPC_PDU_TYPE__ALTER_CONTEXT: type = "Alter Context"; break; case DCERPC_PDU_TYPE__ALTER_CONTEXT_RESP: type = "Alter Context Response"; break; case DCERPC_PDU_TYPE__AUTH3: type = "Auth3"; break; case DCERPC_PDU_TYPE__SHUTDOWN: type = "Shutdown"; break; case DCERPC_PDU_TYPE__CO_CANCEL: type = "Cancel"; break; case DCERPC_PDU_TYPE__ORPHANED: type = "Orphaned"; break; case DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP: type = "Microsoft Exchange/Outlook 2003"; break; default: type = "Unknown DCE/RPC type"; break; } dce2_pdu_types[i] = (char *)DCE2_Alloc(strlen(type) + 1, DCE2_MEM_TYPE__INIT); strncpy(dce2_pdu_types[i], type, strlen(type)); dce2_pdu_types[i][strlen(type)] = '\0'; #ifdef DCE2_EVENT_PRINT_DEBUG printf("%s\n", dce2_pdu_types[i]); #endif } } /****************************************************************** * Function: DCE2_Alert() * * Potentially generates an alert if an event is triggered. * * Arguments: * DCE2_SsnData * * This is the current session data structure being used * when the event was triggered. It is not a necessary * argument if no session data is currently available, for * example if the event is a memcap event - pass in NULL in * this case. * DCE2_Event * The event type that was triggered. * ... * The arguments to the format for the event. * * Returns: None * ******************************************************************/ void DCE2_Alert(DCE2_SsnData *sd, DCE2_Event e, ...) { va_list ap; #ifdef DEBUG_MSGS // When debugging want to see all of the alerts generated va_start(ap, e); vsnprintf(dce2_event_bufs[e], sizeof(dce2_event_bufs[e]) - 1, dce2_events[e].format, ap); va_end(ap); dce2_event_bufs[e][sizeof(dce2_event_bufs[e]) - 1] = '\0'; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ALL, "DCE2 Alert => %s\n", dce2_event_bufs[e])); #endif if (sd != NULL) { // NOTE This check needs to change if the number of preprocessor events // should exceed 63 /* Only log a specific alert once per session */ if (sd->alert_mask & ((uint64_t)1 << e)) return; /* set bit for this alert so we don't alert on again * in this session */ sd->alert_mask |= ((uint64_t)1 << e); } if (!DCE2_GcAlertOnEvent(dce2_events[e].eflag)) return; dce2_stats.events++; #ifndef DEBUG_MSGS va_start(ap, e); vsnprintf(dce2_event_bufs[e], sizeof(dce2_event_bufs[e]) - 1, dce2_events[e].format, ap); va_end(ap); dce2_event_bufs[e][sizeof(dce2_event_bufs[e]) - 1] = '\0'; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ALL, "DCE2 Alert => %s\n", dce2_event_bufs[e])); #endif _dpd.alertAdd(GENERATOR_DCE2, e, 1, 0, 3, dce2_event_bufs[e], 0); } /****************************************************************** * Function: DCE2_EventsFree() * * Frees any global data that was dynamically allocated. * * Arguments: None * * Returns: None * ******************************************************************/ void DCE2_EventsFree(void) { unsigned int i; for (i = 0; i < DCE2_EVENT__MAX; i++) { if (dce2_events[i].format != NULL) { DCE2_Free((void *)dce2_events[i].format, strlen(dce2_events[i].format) + 1, DCE2_MEM_TYPE__INIT); dce2_events[i].format = NULL; } } for (i = 0; i < (sizeof(dce2_pdu_types) / sizeof(char *)); i++) { if (dce2_pdu_types[i] != NULL) { DCE2_Free((void *)dce2_pdu_types[i], strlen(dce2_pdu_types[i]) + 1, DCE2_MEM_TYPE__INIT); dce2_pdu_types[i] = NULL; } } } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_roptions.h0000644000175000017500000000451414241075656022476 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_ROPTIONS_H_ #define _DCE2_ROPTIONS_H_ #include "dcerpc.h" /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_Roptions { /* dce_iface */ int first_frag; /* Set to sentinel if not applicable */ Uuid iface; /* For connectionless */ uint32_t iface_vers; /* For connectionless */ /* For connection-oriented */ uint16_t iface_vers_maj; uint16_t iface_vers_min; /* dce_opnum */ int opnum; /* Set to sentinel if not applicable */ /* dce_byte_test */ int hdr_byte_order; /* Set to sentinel if not applicable */ int data_byte_order; /* Set to sentinel if not applicable */ /* dce_stub_data */ const uint8_t *stub_data; /* Set to NULL if not applicable */ } DCE2_Roptions; /******************************************************************** * Public function prototypes ********************************************************************/ struct _SnortConfig; void DCE2_RegRuleOptions(struct _SnortConfig *); void DCE2_PrintRoptions(DCE2_Roptions *); int DCE2_GetByteOrder(void *, int32_t); #endif /* _DCE2_ROPTIONS_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/Makefile.in0000644000175000017500000006504114242725546021623 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@am__append_1 = ../libsf_dynamic_utils.la @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@am__append_2 = ../include/appdata_adjuster.c ../include/sfxhash.c ../include/sfhashfcn.c ../include/sfmemcap.c ../include/sfprimetable.c ../include/reg_test.h ../include/reg_test.c @BUILD_BUFFER_DUMP_TRUE@am__append_3 = \ @BUILD_BUFFER_DUMP_TRUE@dcerpc2_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@dcerpc2_buffer_dump.h subdir = src/dynamic-preprocessors/dcerpc2 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_dce2_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ $(am__append_1) am__libsf_dce2_preproc_la_SOURCES_DIST = includes/dcerpc.h \ includes/smb.h dce2_debug.c dce2_debug.h dce2_utils.c \ dce2_utils.h dce2_list.c dce2_list.h dce2_memory.c \ dce2_memory.h dce2_stats.c dce2_stats.h dce2_event.c \ dce2_event.h dce2_config.c dce2_config.h dce2_roptions.c \ dce2_roptions.h dce2_session.h spp_dce2.c spp_dce2.h \ snort_dce2.c snort_dce2.h dce2_smb.c dce2_smb.h dce2_smb2.c \ dce2_smb2.h dce2_tcp.c dce2_tcp.h dce2_co.c dce2_co.h \ dce2_udp.c dce2_udp.h dce2_cl.c dce2_cl.h dce2_http.c \ dce2_http.h dce2_paf.c dce2_paf.h dcerpc2_buffer_dump.c \ dcerpc2_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = dcerpc2_buffer_dump.lo am_libsf_dce2_preproc_la_OBJECTS = dce2_debug.lo dce2_utils.lo \ dce2_list.lo dce2_memory.lo dce2_stats.lo dce2_event.lo \ dce2_config.lo dce2_roptions.lo spp_dce2.lo snort_dce2.lo \ dce2_smb.lo dce2_smb2.lo dce2_tcp.lo dce2_co.lo dce2_udp.lo \ dce2_cl.lo dce2_http.lo dce2_paf.lo $(am__objects_1) @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@am__objects_2 = appdata_adjuster.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfxhash.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfhashfcn.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfmemcap.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfprimetable.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ reg_test.lo @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_dce2_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo sf_ip.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfrt.lo sfrt_dir.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo $(am__objects_2) libsf_dce2_preproc_la_OBJECTS = $(am_libsf_dce2_preproc_la_OBJECTS) \ $(nodist_libsf_dce2_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_dce2_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_dce2_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_dce2_preproc_la_SOURCES) \ $(nodist_libsf_dce2_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_dce2_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_dce2_preproc.la libsf_dce2_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_dce2_preproc_la_LIBADD = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ $(am__append_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_dce2_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sf_ip.c ../include/sfrt.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sfrt_dir.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@ $(am__append_2) libsf_dce2_preproc_la_SOURCES = includes/dcerpc.h includes/smb.h \ dce2_debug.c dce2_debug.h dce2_utils.c dce2_utils.h \ dce2_list.c dce2_list.h dce2_memory.c dce2_memory.h \ dce2_stats.c dce2_stats.h dce2_event.c dce2_event.h \ dce2_config.c dce2_config.h dce2_roptions.c dce2_roptions.h \ dce2_session.h spp_dce2.c spp_dce2.h snort_dce2.c snort_dce2.h \ dce2_smb.c dce2_smb.h dce2_smb2.c dce2_smb2.h dce2_tcp.c \ dce2_tcp.h dce2_co.c dce2_co.h dce2_udp.c dce2_udp.h dce2_cl.c \ dce2_cl.h dce2_http.c dce2_http.h dce2_paf.c dce2_paf.h \ $(am__append_3) EXTRA_DIST = \ sf_dce2.vcxproj \ sf_dce2.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/dcerpc2/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/dcerpc2/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_dce2_preproc.la: $(libsf_dce2_preproc_la_OBJECTS) $(libsf_dce2_preproc_la_DEPENDENCIES) $(EXTRA_libsf_dce2_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_dce2_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_dce2_preproc_la_OBJECTS) $(libsf_dce2_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sf_ip.lo: ../include/sf_ip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_ip.lo `test -f '../include/sf_ip.c' || echo '$(srcdir)/'`../include/sf_ip.c sfrt.lo: ../include/sfrt.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt.lo `test -f '../include/sfrt.c' || echo '$(srcdir)/'`../include/sfrt.c sfrt_dir.lo: ../include/sfrt_dir.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt_dir.lo `test -f '../include/sfrt_dir.c' || echo '$(srcdir)/'`../include/sfrt_dir.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c appdata_adjuster.lo: ../include/appdata_adjuster.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o appdata_adjuster.lo `test -f '../include/appdata_adjuster.c' || echo '$(srcdir)/'`../include/appdata_adjuster.c sfxhash.lo: ../include/sfxhash.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfxhash.lo `test -f '../include/sfxhash.c' || echo '$(srcdir)/'`../include/sfxhash.c sfhashfcn.lo: ../include/sfhashfcn.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfhashfcn.lo `test -f '../include/sfhashfcn.c' || echo '$(srcdir)/'`../include/sfhashfcn.c sfmemcap.lo: ../include/sfmemcap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfmemcap.lo `test -f '../include/sfmemcap.c' || echo '$(srcdir)/'`../include/sfmemcap.c sfprimetable.lo: ../include/sfprimetable.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfprimetable.lo `test -f '../include/sfprimetable.c' || echo '$(srcdir)/'`../include/sfprimetable.c reg_test.lo: ../include/reg_test.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o reg_test.lo `test -f '../include/reg_test.c' || echo '$(srcdir)/'`../include/reg_test.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/dcerpc2/spp_dce2.h0000644000175000017500000000455214241075704021417 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef SPP_DCE2_H #define SPP_DCE2_H /******************************************************************** * Externs ********************************************************************/ #ifdef SNORT_RELOAD #include "dce2_memory.h" #endif #ifdef PERF_PROFILING #include "profiler.h" extern PreprocStats dce2_pstat_main; extern PreprocStats dce2_pstat_session; extern PreprocStats dce2_pstat_new_session; extern PreprocStats dce2_pstat_session_state; extern PreprocStats dce2_pstat_detect; extern PreprocStats dce2_pstat_log; extern PreprocStats dce2_pstat_smb_seg; extern PreprocStats dce2_pstat_smb_req; extern PreprocStats dce2_pstat_smb_uid; extern PreprocStats dce2_pstat_smb_tid; extern PreprocStats dce2_pstat_smb_fid; extern PreprocStats dce2_pstat_smb_file; extern PreprocStats dce2_pstat_smb_file_detect; extern PreprocStats dce2_pstat_smb_file_api; extern PreprocStats dce2_pstat_smb_fingerprint; extern PreprocStats dce2_pstat_smb_negotiate; extern PreprocStats dce2_pstat_co_seg; extern PreprocStats dce2_pstat_co_frag; extern PreprocStats dce2_pstat_co_reass; extern PreprocStats dce2_pstat_co_ctx; extern PreprocStats dce2_pstat_cl_acts; extern PreprocStats dce2_pstat_cl_frag; extern PreprocStats dce2_pstat_cl_reass; #endif #endif /* SPP_DCE2_H */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_paf.h0000644000175000017500000000263314241075654021365 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __DCE2_PAF_H__ #define __DCE2_PAF_H__ #include "sfPolicy.h" #include "sf_types.h" #include "stream_api.h" #include "dce2_utils.h" int DCE2_PafRegisterPort(struct _SnortConfig *, uint16_t, tSfPolicyId, DCE2_TransType); #ifdef TARGET_BASED int DCE2_PafRegisterService(struct _SnortConfig *, uint16_t, tSfPolicyId, DCE2_TransType); #endif #endif /* __DCE2_PAF_H__ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_config.h0000644000175000017500000011412514241075632022060 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _DCE2_CONFIG_H_ #define _DCE2_CONFIG_H_ #include "dce2_debug.h" #include "dce2_utils.h" #include "dce2_list.h" #include "sf_types.h" #include "sf_ip.h" #include "sfrt.h" #include "sf_snort_packet.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" /******************************************************************** * Macros ********************************************************************/ #define DCE2_GNAME "dcerpc2" #define DCE2_SNAME "dcerpc2_server" #define DCE2_CFG_TOK__DASH '-' #define DCE2_CFG_TOK__UNDERSCORE '_' #define DCE2_CFG_TOK__QUOTE '"' #define DCE2_CFG_TOK__LIST_START '[' #define DCE2_CFG_TOK__LIST_END ']' #define DCE2_CFG_TOK__OPT_SEP ',' #define DCE2_CFG_TOK__LIST_SEP ',' #define DCE2_CFG_TOK__PORT_RANGE ':' #define DCE2_CFG_TOK__OPNUM_RANGE '-' #define DCE2_CFG_TOK__DOT '.' #define DCE2_CFG_TOK__IP6_TET_SEP ':' #define DCE2_CFG_TOK__IP4_TET_SEP '.' #define DCE2_CFG_TOK__IP_PREFIX_SEP '/' #define DCE2_CFG_TOK__MINUS '-' #define DCE2_CFG_TOK__PLUS '+' #define DCE2_CFG_TOK__HEX_SEP 'x' #define DCE2_CFG_TOK__HEX_OCT_START '0' #define DCE2_CFG_TOK__END '\0' #define DCE2_PORTS__MAX (UINT16_MAX + 1) #define DCE2_PORTS__MAX_INDEX (DCE2_PORTS__MAX / 8) #define DCE2_MEMCAP__DEFAULT (100 * 1024) /* 100 MB */ /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_Policy { DCE2_POLICY__NONE, DCE2_POLICY__WIN2000, DCE2_POLICY__WINXP, DCE2_POLICY__WINVISTA, DCE2_POLICY__WIN2003, DCE2_POLICY__WIN2008, DCE2_POLICY__WIN7, DCE2_POLICY__SAMBA, DCE2_POLICY__SAMBA_3_0_37, DCE2_POLICY__SAMBA_3_0_22, DCE2_POLICY__SAMBA_3_0_20, DCE2_POLICY__MAX } DCE2_Policy; typedef enum _DCE2_DetectFlag { DCE2_DETECT_FLAG__NULL = 0x0000, DCE2_DETECT_FLAG__NONE = 0x0001, DCE2_DETECT_FLAG__SMB = 0x0002, DCE2_DETECT_FLAG__TCP = 0x0004, DCE2_DETECT_FLAG__UDP = 0x0008, DCE2_DETECT_FLAG__HTTP_PROXY = 0x0010, DCE2_DETECT_FLAG__HTTP_SERVER = 0x0020, DCE2_DETECT_FLAG__ALL = 0xffff } DCE2_DetectFlag; typedef enum _DCE2_EventFlag { DCE2_EVENT_FLAG__NULL = 0x0000, DCE2_EVENT_FLAG__NONE = 0x0001, DCE2_EVENT_FLAG__MEMCAP = 0x0002, DCE2_EVENT_FLAG__SMB = 0x0004, DCE2_EVENT_FLAG__CO = 0x0008, DCE2_EVENT_FLAG__CL = 0x0010, DCE2_EVENT_FLAG__ALL = 0xffff } DCE2_EventFlag; typedef enum _DCE2_ValidSmbVersionFlag { DCE2_VALID_SMB_VERSION_FLAG__NULL = 0x0000, DCE2_VALID_SMB_VERSION_FLAG__V1 = 0x0001, DCE2_VALID_SMB_VERSION_FLAG__V2 = 0x0002, DCE2_VALID_SMB_VERSION_FLAG__ALL = 0xffff } DCE2_ValidSmbVersionFlag; typedef enum _DCE2_SmbFingerprintFlag { DCE2_SMB_FINGERPRINT__NONE = 0x0000, DCE2_SMB_FINGERPRINT__CLIENT = 0x0001, DCE2_SMB_FINGERPRINT__SERVER = 0x0002 } DCE2_SmbFingerprintFlag; /* Whether an option is on or off: CS - configuration switch */ typedef enum _DCE2_CS { DCE2_CS__DISABLED = 0, DCE2_CS__ENABLED } DCE2_CS; typedef enum _DCE2_SmbFileInspection { DCE2_SMB_FILE_INSPECTION__OFF = 0, DCE2_SMB_FILE_INSPECTION__ON, DCE2_SMB_FILE_INSPECTION__ONLY } DCE2_SmbFileInspection; typedef enum _DCE2_WordCharPosition { DCE2_WORD_CHAR_POSITION__START, DCE2_WORD_CHAR_POSITION__MIDDLE, DCE2_WORD_CHAR_POSITION__END } DCE2_WordCharPosition; typedef enum _DCE2_WordListState { DCE2_WORD_LIST_STATE__START, DCE2_WORD_LIST_STATE__WORD_START, DCE2_WORD_LIST_STATE__QUOTE, DCE2_WORD_LIST_STATE__WORD, DCE2_WORD_LIST_STATE__WORD_END, DCE2_WORD_LIST_STATE__END } DCE2_WordListState; typedef enum _DCE2_ValueState { DCE2_VALUE_STATE__START, DCE2_VALUE_STATE__MODIFIER, DCE2_VALUE_STATE__HEX_OR_OCT, DCE2_VALUE_STATE__DECIMAL, DCE2_VALUE_STATE__HEX_START, DCE2_VALUE_STATE__HEX, DCE2_VALUE_STATE__OCTAL } DCE2_ValueState; typedef enum _DCE2_PortListState { DCE2_PORT_LIST_STATE__START, DCE2_PORT_LIST_STATE__PORT_START, DCE2_PORT_LIST_STATE__PORT_LO, DCE2_PORT_LIST_STATE__PORT_RANGE, DCE2_PORT_LIST_STATE__PORT_HI, DCE2_PORT_LIST_STATE__PORT_END, DCE2_PORT_LIST_STATE__END } DCE2_PortListState; typedef enum _DCE2_IpListState { DCE2_IP_LIST_STATE__START, DCE2_IP_LIST_STATE__IP_START, DCE2_IP_LIST_STATE__IP_END, DCE2_IP_LIST_STATE__END } DCE2_IpListState; typedef enum _DCE2_IpState { DCE2_IP_STATE__START, DCE2_IP_STATE__IP } DCE2_IpState; typedef enum _DCE2_IntType { DCE2_INT_TYPE__INT8, DCE2_INT_TYPE__UINT8, DCE2_INT_TYPE__INT16, DCE2_INT_TYPE__UINT16, DCE2_INT_TYPE__INT32, DCE2_INT_TYPE__UINT32, DCE2_INT_TYPE__INT64, DCE2_INT_TYPE__UINT64 } DCE2_IntType; /******************************************************************** * Structures ********************************************************************/ /* Global configuration struct */ typedef struct _DCE2_GlobalConfig { int disabled; uint32_t memcap; int event_mask; DCE2_CS dce_defrag; int max_frag_len; uint16_t reassemble_threshold; int smb_fingerprint_policy; bool legacy_mode; } DCE2_GlobalConfig; typedef struct _DCE2_SmbShare { char *unicode_str; unsigned int unicode_str_len; char *ascii_str; unsigned int ascii_str_len; } DCE2_SmbShare; /* Server configuration struct */ typedef struct _DCE2_ServerConfig { DCE2_Policy policy; uint8_t smb_ports[DCE2_PORTS__MAX_INDEX]; uint8_t tcp_ports[DCE2_PORTS__MAX_INDEX]; uint8_t udp_ports[DCE2_PORTS__MAX_INDEX]; uint8_t http_proxy_ports[DCE2_PORTS__MAX_INDEX]; uint8_t http_server_ports[DCE2_PORTS__MAX_INDEX]; uint8_t auto_smb_ports[DCE2_PORTS__MAX_INDEX]; uint8_t auto_tcp_ports[DCE2_PORTS__MAX_INDEX]; uint8_t auto_udp_ports[DCE2_PORTS__MAX_INDEX]; uint8_t auto_http_proxy_ports[DCE2_PORTS__MAX_INDEX]; uint8_t auto_http_server_ports[DCE2_PORTS__MAX_INDEX]; uint8_t smb_max_chain; uint8_t smb2_max_compound; DCE2_CS autodetect_http_proxy_ports; DCE2_SmbFileInspection smb_file_inspection; int64_t smb_file_depth; DCE2_List *smb_invalid_shares; int valid_smb_versions_mask; /* Used when freeing from routing table */ uint32_t ref_count; } DCE2_ServerConfig; typedef struct _DCE2_Config { DCE2_GlobalConfig *gconfig; DCE2_ServerConfig *dconfig; table_t *sconfigs; uint32_t ref_count; #ifdef DCE2_LOG_EXTRA_DATA uint32_t xtra_logging_smb_file_name_id; #endif } DCE2_Config; /******************************************************************** * Extern variables ********************************************************************/ extern DCE2_Config *dce2_eval_config; extern tSfPolicyUserContextId dce2_config; extern DCE2_Config *dce2_eval_config; /******************************************************************** * Inline function prototypes ********************************************************************/ static inline uint32_t DCE2_GcMemcap(void); static inline int DCE2_GcMaxFrag(void); static inline uint16_t DCE2_GcMaxFragLen(void); static inline int DCE2_GcAlertOnEvent(DCE2_EventFlag); static inline int DCE2_GcReassembleEarly(void); static inline uint16_t DCE2_GcReassembleThreshold(void); static inline DCE2_CS DCE2_GcDceDefrag(void); static inline bool DCE2_GcSmbFingerprintClient(void); static inline bool DCE2_GcSmbFingerprintServer(void); static inline DCE2_Policy DCE2_ScPolicy(const DCE2_ServerConfig *); static inline int DCE2_ScIsDetectPortSet(const DCE2_ServerConfig *, const uint16_t, const DCE2_TransType); static inline int DCE2_ScIsAutodetectPortSet(const DCE2_ServerConfig *, const uint16_t, const DCE2_TransType); static inline DCE2_CS DCE2_ScAutodetectHttpProxyPorts(const DCE2_ServerConfig *); static inline uint8_t DCE2_ScSmbMaxChain(const DCE2_ServerConfig *); static inline DCE2_List * DCE2_ScSmbInvalidShares(const DCE2_ServerConfig *); static inline bool DCE2_ScSmbFileInspection(const DCE2_ServerConfig *); static inline bool DCE2_ScSmbFileInspectionOnly(const DCE2_ServerConfig *); static inline int64_t DCE2_ScSmbFileDepth(const DCE2_ServerConfig *); static inline uint8_t DCE2_ScSmb2MaxCompound(const DCE2_ServerConfig *); static inline uint8_t DCE2_ScIsValidSmbVersion(const DCE2_ServerConfig *, DCE2_ValidSmbVersionFlag); static inline bool DCE2_IsPortSet(const uint8_t *, const uint16_t); static inline void DCE2_SetPort(uint8_t *, const uint16_t); static inline void DCE2_SetPortRange(uint8_t *, uint16_t, uint16_t); static inline void DCE2_ClearPorts(uint8_t *); static inline int DCE2_IsWordChar(const char, const DCE2_WordCharPosition); static inline int DCE2_IsGraphChar(const char); static inline int DCE2_IsQuoteChar(const char); static inline int DCE2_IsListSepChar(const char); static inline int DCE2_IsOptEndChar(const char); static inline int DCE2_IsSpaceChar(const char); static inline int DCE2_IsConfigEndChar(const char); static inline int DCE2_IsPortChar(const char); static inline int DCE2_IsPortRangeChar(const char); static inline int DCE2_IsListStartChar(const char); static inline int DCE2_IsListEndChar(const char); static inline int DCE2_IsIpChar(const char); static inline DCE2_Ret DCE2_CheckAndSetMask(int, int *); /******************************************************************** * Public function prototypes ********************************************************************/ void DCE2_GlobalConfigure(DCE2_Config *, char *); void DCE2_ServerConfigure(struct _SnortConfig *, DCE2_Config *, char *); int DCE2_CreateDefaultServerConfig(struct _SnortConfig *, DCE2_Config *, tSfPolicyId); int DCE2_ScCheckTransports(DCE2_Config *); const DCE2_ServerConfig * DCE2_ScGetConfig(const SFSnortPacket *); int DCE2_ScIsPortSet(const DCE2_ServerConfig *, const uint16_t, const DCE2_TransType); int DCE2_ScIsDetectPortSet(const DCE2_ServerConfig *, const uint16_t, const DCE2_TransType); int DCE2_ScIsAutodetectPortSet(const DCE2_ServerConfig *, const uint16_t, const DCE2_TransType); int DCE2_ScIsNoAutoPortSet(const DCE2_ServerConfig *, const uint16_t); DCE2_Ret DCE2_ParseValue(char **, char *, void *, DCE2_IntType); DCE2_Ret DCE2_GetValue(char *, char *, void *, int, DCE2_IntType, uint8_t); DCE2_Ret DCE2_ParseIpList(char **, char *, DCE2_Queue *); DCE2_Ret DCE2_ParseIp(char **, char *, sfcidr_t *); DCE2_Ret DCE2_ParsePortList(char **, char *, uint8_t *); void DCE2_FreeConfigs(tSfPolicyUserContextId config); void DCE2_FreeConfig(DCE2_Config *); /******************************************************************** * Function: DCE2_GcMemcap() * * Convenience function for getting the memcap configured for * the preprocessor. * * Arguments: None * * Returns: * uint32_t * The memcap configured for the preprocessor. * ********************************************************************/ static inline uint32_t DCE2_GcMemcap(void) { return dce2_eval_config->gconfig->memcap; } /******************************************************************** * Function: DCE2_GcMaxFrag() * * Convenience function for checking if the maximum fragment length * was configured for the preprocessor. * * Arguments: None * * Returns: * int * 1 if it was configured. * 0 if it was not configured. * ********************************************************************/ static inline int DCE2_GcMaxFrag(void) { if (dce2_eval_config->gconfig->max_frag_len != DCE2_SENTINEL) return 1; return 0; } /******************************************************************** * Function: DCE2_GcMaxFragLen() * * Convenience function for getting the maximum fragment length * that is configured for the preprocessor. If not configured, * just return the maximum the return value can hold. One should * check if configured first. * * Arguments: None * * Returns: * uint16_t * The maximum fragment length configured. * UINT16_MAX if not configured. * ********************************************************************/ static inline uint16_t DCE2_GcMaxFragLen(void) { if (DCE2_GcMaxFrag()) return (uint16_t)dce2_eval_config->gconfig->max_frag_len; return UINT16_MAX; } /******************************************************************** * Function: DCE2_GcAlertOnEvent() * * Convenience function for determining if we are configured * to alert on a certain event type. * * Arguments: * DCE2_EventFlag * The event type to check to see if we are configured * to alert on. * * Returns: * int * Non-zero if we are configured to alert on this event type. * Zero if we are not configured to alert on this event type. * ********************************************************************/ static inline int DCE2_GcAlertOnEvent(DCE2_EventFlag eflag) { return dce2_eval_config->gconfig->event_mask & eflag; } /******************************************************************** * Function: DCE2_GcDceDefrag() * * Convenience function for determining if we are configured * to do DCE/RPC defragmentation. * * Arguments: None * * Returns: * DCE2_CS * DCE2_CS__ENABLED if we are configured to do DCE/RPC * defragmentation. * DCE2_CS__DISABLED if we are not configured to do DCE/RPC * defragmentation. * ********************************************************************/ static inline DCE2_CS DCE2_GcDceDefrag(void) { return dce2_eval_config->gconfig->dce_defrag; } /******************************************************************** * Function: DCE2_GcReassembleEarly() * * Convenience function for checking if the reassemble threshold * was configured for the preprocessor. * * Arguments: None * * Returns: * int * 1 if it was configured. * 0 if it was not configured. * ********************************************************************/ static inline int DCE2_GcReassembleEarly(void) { if (dce2_eval_config->gconfig->reassemble_threshold > 0) return 1; return 0; } /******************************************************************** * Function: DCE2_GcReassembleThreshold() * * Convenience function for getting the reassemble threshold * that is configured for the preprocessor. If not configured, * just return the maximum the return value can hold. One should * check if configured first. * * Arguments: None * * Returns: * uint16_t * The reassemble threshold configured. * UINT16_MAX if not configured. * ********************************************************************/ static inline uint16_t DCE2_GcReassembleThreshold(void) { if (DCE2_GcReassembleEarly()) return dce2_eval_config->gconfig->reassemble_threshold; return UINT16_MAX; } /******************************************************************** * Function: DCE2_GcSmbFingerprintClient() * * Convenience function for finding out if the preprocessor is * configured to fingerprint the client policy base off SMB * traffic. * * Arguments: None * * Returns: * bool - true if configure to fingerprint client, false if not * ********************************************************************/ static inline bool DCE2_GcSmbFingerprintClient(void) { return dce2_eval_config->gconfig->smb_fingerprint_policy & DCE2_SMB_FINGERPRINT__CLIENT ? true : false; } /******************************************************************** * Function: DCE2_GcSmbFingerprintServer() * * Convenience function for finding out if the preprocessor is * configured to fingerprint the server policy base off SMB * traffic. * * Arguments: None * * Returns: * bool - true if configure to fingerprint server, false if not * ********************************************************************/ static inline bool DCE2_GcSmbFingerprintServer(void) { return dce2_eval_config->gconfig->smb_fingerprint_policy & DCE2_SMB_FINGERPRINT__SERVER ? true : false; } /******************************************************************** * Function: DCE2_GcIsLegacyMode() * * Convenience function for getting whether it is in the legacy mode * * Arguments: None * * Returns: true: legacy mode * false: normal mode * ********************************************************************/ static inline bool DCE2_GcIsLegacyMode(void) { return dce2_eval_config->gconfig->legacy_mode; } /******************************************************************** * Function: DCE2_ScPolicy() * * Convenience function for getting the policy the server * configuration is configured for. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * * Returns: * DCE2_Policy * The policy the server configuration is configured for. * DCE2_POLICY__NONE if a NULL pointer is passed in. * ********************************************************************/ static inline DCE2_Policy DCE2_ScPolicy(const DCE2_ServerConfig *sc) { if (sc == NULL) return DCE2_POLICY__NONE; return sc->policy; } /********************************************************************* * Function: DCE2_ScIsDetectPortSet() * * Determines if the server configuration is configured to detect * on the port and transport passed in. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * const uint16_t * The port to check. * const DCE2_TransType * The transport to check for the port. * * Returns: * int * 1 if configured to detect on this port for the given * transport. * 0 if not configured to detect on this port for the given * transport, or if the server configuration passed in * is NULL. * *********************************************************************/ static inline int DCE2_ScIsDetectPortSet(const DCE2_ServerConfig *sc, const uint16_t port, const DCE2_TransType ttype) { const uint8_t *port_array; if (sc == NULL) return 0; switch (ttype) { case DCE2_TRANS_TYPE__SMB: port_array = sc->smb_ports; break; case DCE2_TRANS_TYPE__TCP: port_array = sc->tcp_ports; break; case DCE2_TRANS_TYPE__UDP: port_array = sc->udp_ports; break; case DCE2_TRANS_TYPE__HTTP_PROXY: port_array = sc->http_proxy_ports; break; case DCE2_TRANS_TYPE__HTTP_SERVER: port_array = sc->http_server_ports; break; default: return 0; } return DCE2_IsPortSet(port_array, port); } /********************************************************************* * Function: DCE2_ScIsAutodetectPortSet() * * Determines if the server configuration is configured to autodetect * on the port and transport passed in. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * const uint16_t * The port to check. * const DCE2_TransType * The transport to check for the port. * * Returns: * int * 1 if configured to autodetect on this port for the given * transport. * 0 if not configured to autodetect on this port for the given * transport, or if the server configuration passed in * is NULL. * *********************************************************************/ static inline int DCE2_ScIsAutodetectPortSet(const DCE2_ServerConfig *sc, const uint16_t port, const DCE2_TransType ttype) { const uint8_t *port_array; if (sc == NULL) return 0; switch (ttype) { case DCE2_TRANS_TYPE__SMB: port_array = sc->auto_smb_ports; break; case DCE2_TRANS_TYPE__TCP: port_array = sc->auto_tcp_ports; break; case DCE2_TRANS_TYPE__UDP: port_array = sc->auto_udp_ports; break; case DCE2_TRANS_TYPE__HTTP_PROXY: port_array = sc->auto_http_proxy_ports; break; case DCE2_TRANS_TYPE__HTTP_SERVER: port_array = sc->auto_http_server_ports; break; default: return 0; } return DCE2_IsPortSet(port_array, port); } /******************************************************************** * Function: DCE2_ScAutodetectHttpProxyPorts() * * Convenience function to determine if the server configuration * is configured to autodetect on all rpc over http proxy detect * ports. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * * Returns: * DCE2_CS * DCE2_CS__ENABLED if configured to autodetect on all rpc * over http proxy ports. This is also returned it the * server configuration passed in is NULL. * DCE2_CS__DISABLED if not configured to autodetect on all * rpc over http proxy ports. * ********************************************************************/ static inline DCE2_CS DCE2_ScAutodetectHttpProxyPorts(const DCE2_ServerConfig *sc) { if (sc == NULL) return DCE2_CS__ENABLED; return sc->autodetect_http_proxy_ports; } /******************************************************************** * Function: DCE2_ScSmbMaxChain() * * Convenience function to get the SMB maximum amount of command * chaining allowed. A value of 0 means unlimited. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * * Returns: * uint8_t * The value for the maximum amount of command chaining. * 0 is returned if the server configuration passed in is NULL. * ********************************************************************/ static inline uint8_t DCE2_ScSmbMaxChain(const DCE2_ServerConfig *sc) { if (sc == NULL) return 0; return sc->smb_max_chain; } /******************************************************************** * Function: DCE2_ScSmbInvalidShares() * * Returns the list of SMB invalid shares configured. If no * shares were configured, this will return a NULL list. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * * Returns: * DCE2_List * * Pointer to the list containing the SMB invalid share * strings. * NULL if no shares were configured or the server * configuration passed in is NULL. * ********************************************************************/ static inline DCE2_List * DCE2_ScSmbInvalidShares(const DCE2_ServerConfig *sc) { if (sc == NULL) return NULL; return sc->smb_invalid_shares; } static inline bool DCE2_ScSmbFileInspection(const DCE2_ServerConfig *sc) { if (sc == NULL) return false; return ((sc->smb_file_inspection == DCE2_SMB_FILE_INSPECTION__ON) || (sc->smb_file_inspection == DCE2_SMB_FILE_INSPECTION__ONLY)); } static inline bool DCE2_ScSmbFileInspectionOnly(const DCE2_ServerConfig *sc) { if (sc == NULL) return false; return sc->smb_file_inspection == DCE2_SMB_FILE_INSPECTION__ONLY; } static inline int64_t DCE2_ScSmbFileDepth(const DCE2_ServerConfig *sc) { if (!DCE2_ScSmbFileInspection(sc)) return -1; return sc->smb_file_depth; } /******************************************************************** * Function: DCE2_ScSmb2MaxChain() * * Convenience function to get the SMB maximum amount of command * compounding allowed. A value of 0 means unlimited. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * * Returns: * uint8_t * The value for the maximum amount of command compounding. * 0 is returned if the server configuration passed in is NULL. * ********************************************************************/ static inline uint8_t DCE2_ScSmb2MaxCompound(const DCE2_ServerConfig *sc) { if (sc == NULL) return 0; return sc->smb2_max_compound; } /******************************************************************** * Function: DCE2_ScIsValidSmbVersion() * * Convenience function to check if an smb version flag is set. * * Arguments: * const DCE2_ServerConfig * * Pointer to the server configuration to check. * const DCE2_ValidSmbVersionFlag * The version flag to test * * Returns: * int * non-zero if the flag is set * 0 if the flag is not set * ********************************************************************/ static inline uint8_t DCE2_ScIsValidSmbVersion( const DCE2_ServerConfig *sc, DCE2_ValidSmbVersionFlag vflag) { if (sc == NULL) return 0; return sc->valid_smb_versions_mask & vflag; } /********************************************************************* * Function: DCE2_IsPortSet() * * Checks to see if a port is set in one in the port array mask * passed in. * * Arguments: * uint8_t * * Pointer to a port array mask. * const uint16_t * The port to check for in the mask. * * Returns: * int * Non-zero if the port is set. * Zero if the port is not set. * *********************************************************************/ static inline bool DCE2_IsPortSet(const uint8_t *port_array, const uint16_t port) { return isPortEnabled( port_array, port ); } /********************************************************************* * Function: DCE2_SetPort() * * Sets a port in the port array mask passed in. * * Arguments: * uint8_t * * Pointer to a port array mask. * const uint16_t * The port to set in the port array mask. * * Returns: None * *********************************************************************/ static inline void DCE2_SetPort(uint8_t *port_array, const uint16_t port) { enablePort( port_array, port ); } /********************************************************************* * Function: DCE2_SetPortRange() * * Sets ports from lo to hi in one of the transport port * configurations. * * Arguments: * uint8_t * * Pointer to a port array mask. * uint16_t * The lo port to start setting ports in the port array mask. * uint16_t * The hi port to end setting ports in the port array mask. * * Returns: None * *********************************************************************/ static inline void DCE2_SetPortRange(uint8_t *port_array, uint16_t lo_port, uint16_t hi_port) { unsigned int i; if (lo_port > hi_port) { uint16_t tmp = lo_port; lo_port = hi_port; hi_port = tmp; } for (i = lo_port; i <= hi_port; i++) DCE2_SetPort(port_array, (uint16_t)i); } /******************************************************************** * Function: DCE2_ClearPorts() * * Clears all of the port bits set in the port array mask. * * Arguments: * uint8_t * * Pointer to a port array mask. * * Returns: None * ********************************************************************/ static inline void DCE2_ClearPorts(uint8_t *port_array) { memset(port_array, 0, DCE2_PORTS__MAX_INDEX); } /******************************************************************** * Function: DCE2_IsWordChar() * * Determines if a character is a valid word character based on * position in the word. Of course, this is the preprocessor's * definition. Mainly used for restricting preprocessor option * names and set argument names. * * Arguments: * const char * The character to make the determination on. * DCE2_WordCharPosition * The position in the word the character is. * * Returns: * int * 1 if a valid word character. * 0 if not a valid word character. * ********************************************************************/ static inline int DCE2_IsWordChar(const char c, const DCE2_WordCharPosition pos) { if (pos == DCE2_WORD_CHAR_POSITION__START) { if (isalpha((int)c)) return 1; } else if (pos == DCE2_WORD_CHAR_POSITION__MIDDLE) { if (isalpha((int)c) || isdigit((int)c) || (c == DCE2_CFG_TOK__DASH) || (c == DCE2_CFG_TOK__UNDERSCORE) || (c == DCE2_CFG_TOK__DOT)) { return 1; } } else if (pos == DCE2_WORD_CHAR_POSITION__END) { if (isalpha((int)c) || isdigit((int)c)) return 1; } return 0; } /******************************************************************** * Function: DCE2_IsListSepChar() * * Determines if the character passed in is a character that * separates values in lists. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid list separator character. * 0 if not a valid list separator character. * ********************************************************************/ static inline int DCE2_IsListSepChar(const char c) { if (c == DCE2_CFG_TOK__LIST_SEP) return 1; return 0; } /******************************************************************** * Function: DCE2_IsOptEndChar() * * Determines if the character passed in is a character that * marks the end of an option and start of a new option. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid option end character. * 0 if not a valid option end character. * ********************************************************************/ static inline int DCE2_IsOptEndChar(const char c) { if (c == DCE2_CFG_TOK__OPT_SEP) return 1; return 0; } /******************************************************************** * Function: DCE2_IsSpaceChar() * * Determines if the character passed in is a character that * the preprocessor considers a to be a space character. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid space character. * 0 if not a valid space character. * ********************************************************************/ static inline int DCE2_IsSpaceChar(const char c) { if (isspace((int)c)) return 1; return 0; } /******************************************************************** * Function: DCE2_IsConfigEndChar() * * Determines if the character passed in is a character that * the preprocessor considers a to be an end of configuration * character. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid end of configuration character. * 0 if not a valid end of configuration character. * ********************************************************************/ static inline int DCE2_IsConfigEndChar(const char c) { if (c == DCE2_CFG_TOK__END) return 1; return 0; } /******************************************************************** * Function: DCE2_IsPortChar() * * Determines if the character passed in is a character that * the preprocessor considers a to be a valid character for a port. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid port character. * 0 if not a valid port character. * ********************************************************************/ static inline int DCE2_IsPortChar(const char c) { if (isdigit((int)c)) return 1; return 0; } /******************************************************************** * Function: DCE2_IsPortRangeChar() * * Determines if the character passed in is a character that can be * placed before, between or after a port to specify a port range. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid port range character. * 0 if not a valid port range character. * ********************************************************************/ static inline int DCE2_IsPortRangeChar(const char c) { if (c == DCE2_CFG_TOK__PORT_RANGE) return 1; return 0; } /******************************************************************** * Function: DCE2_IsOpnumChar() * * Determines if the character passed in is a character that * the preprocessor considers a to be a valid character for a * DCE/RPC opnum. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid DCE/RPC opnum character. * 0 if not a valid DCE/RPC opnum character. * ********************************************************************/ static inline int DCE2_IsOpnumChar(const char c) { if (isdigit((int)c)) return 1; return 0; } /******************************************************************** * Function: DCE2_IsOpnumRangeChar() * * Determines if the character passed in is a character that is * used to indicate a range of DCE/RPC opnums. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid DCE/RPC opnum range character. * 0 if not a valid DCE/RPC opnum range character. * ********************************************************************/ static inline int DCE2_IsOpnumRangeChar(const char c) { if (c == DCE2_CFG_TOK__OPNUM_RANGE) return 1; return 0; } /******************************************************************** * Function: DCE2_IsListStartChar() * * Determines if the character passed in is a character that is * used to indicate the start of a list. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid start of list character. * 0 if not a valid start of list character. * ********************************************************************/ static inline int DCE2_IsListStartChar(const char c) { if (c == DCE2_CFG_TOK__LIST_START) return 1; return 0; } /******************************************************************** * Function: DCE2_IsListEndChar() * * Determines if the character passed in is a character that is * used to indicate the end of a list. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid end of list character. * 0 if not a valid end of list character. * ********************************************************************/ static inline int DCE2_IsListEndChar(const char c) { if (c == DCE2_CFG_TOK__LIST_END) return 1; return 0; } /******************************************************************** * Function: DCE2_IsQuoteChar() * * Determines if the character passed in is a what the preprocessor * considers to be a quote character. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid quote character. * 0 if not a valid quote character. * ********************************************************************/ static inline int DCE2_IsQuoteChar(const char c) { if (c == DCE2_CFG_TOK__QUOTE) return 1; return 0; } /******************************************************************** * Function: DCE2_IsIpChar() * * Determines if the character passed in is a character that can * be used in an IP address - IPv4 or IPv6. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid IP character. * 0 if not a valid IP character. * ********************************************************************/ static inline int DCE2_IsIpChar(const char c) { if (isxdigit((int)c) || (c == DCE2_CFG_TOK__IP6_TET_SEP) || (c == DCE2_CFG_TOK__IP4_TET_SEP) || (c == DCE2_CFG_TOK__IP_PREFIX_SEP)) { return 1; } return 0; } /******************************************************************** * Function: DCE2_IsGraphChar() * * Determines is the character passed in is a graphical character. * Characters excluded are what the preprocessor considers as * meta characters or space characters. * * Arguments: * const char * The character to make the determination on. * * Returns: * int * 1 if a valid graphical character. * 0 if not a valid graphical character. * ********************************************************************/ static inline int DCE2_IsGraphChar(const char c) { if (!DCE2_IsListStartChar(c) && !DCE2_IsListEndChar(c) && !DCE2_IsQuoteChar(c) && !DCE2_IsListSepChar(c) && !DCE2_IsSpaceChar(c)) return 1; return 0; } /********************************************************************* * Function: DCE2_CheckAndSetMask() * * Checks to see if a flag passed in is already set in the mask * passed in. If it is, error is returned. If it is not, the * flag is set in the mask. * * Arguments: * int * The flag to check and set. * int * * The mask to check and set the flag against. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if the flag is already set in the mask. * DCE2_RET__SUCCESS if the flag is not already set in the mask. * *********************************************************************/ static inline DCE2_Ret DCE2_CheckAndSetMask(int flag, int *mask) { if (*mask & flag) return DCE2_RET__ERROR; *mask |= flag; return DCE2_RET__SUCCESS; } void DCE2_RegisterPortsWithSession( struct _SnortConfig *sc, DCE2_ServerConfig *policy ); #endif /* _DCE2_CONFIG_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/snort_dce2.h0000644000175000017500000001067314241075702021761 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _SNORT_DCE2_H_ #define _SNORT_DCE2_H_ #include "dce2_utils.h" #include "dce2_session.h" #include "sf_snort_packet.h" #include "sf_types.h" #include "snort_debug.h" #ifdef SNORT_RELOAD #include "appdata_adjuster.h" #endif /******************************************************************** * Macros ********************************************************************/ #define DCE2_PROTO_REF_STR__NBSS "netbios-ssn" /* Essentially SMB */ #define DCE2_PROTO_REF_STR__DCERPC "dcerpc" #ifdef SNORT_RELOAD extern APPDATA_ADJUSTER *ada; #endif /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_RpktType { DCE2_RPKT_TYPE__NULL = 0, DCE2_RPKT_TYPE__SMB_SEG, DCE2_RPKT_TYPE__SMB_TRANS, DCE2_RPKT_TYPE__SMB_CO_SEG, DCE2_RPKT_TYPE__SMB_CO_FRAG, DCE2_RPKT_TYPE__TCP_CO_SEG, DCE2_RPKT_TYPE__TCP_CO_FRAG, DCE2_RPKT_TYPE__UDP_CL_FRAG, DCE2_RPKT_TYPE__MAX } DCE2_RpktType; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_ProtoIds { int16_t dcerpc; int16_t nbss; } DCE2_ProtoIds; /******************************************************************** * Extern variables ********************************************************************/ extern DCE2_ProtoIds dce2_proto_ids; extern DCE2_CStack *dce2_pkt_stack; /******************************************************************** * Public function prototypes ********************************************************************/ DCE2_Ret DCE2_Process(SFSnortPacket *); void DCE2_InitRpkts(void); SFSnortPacket * DCE2_GetRpkt(const SFSnortPacket *, DCE2_RpktType, const uint8_t *, uint32_t); DCE2_Ret DCE2_AddDataToRpkt(SFSnortPacket *, DCE2_RpktType, const uint8_t *, uint32_t); DCE2_Ret DCE2_PushPkt(SFSnortPacket *); void DCE2_PopPkt(void); void DCE2_Detect(DCE2_SsnData *); void DCE2_FileDetect(DCE2_SsnData *); uint16_t DCE2_GetRpktMaxData(DCE2_SsnData *, DCE2_RpktType); void DCE2_FreeGlobals(void); void DCE2_SetNoInspect(DCE2_SsnData *); /******************************************************************** * Inline function prototypes ********************************************************************/ static inline void DCE2_ResetRopts(DCE2_Roptions *); static inline void DCE2_DisableDetect(SFSnortPacket *); static inline void DCE2_EnableDetect(void); /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: None * ********************************************************************/ static inline void DCE2_ResetRopts(DCE2_Roptions *ropts) { ropts->first_frag = DCE2_SENTINEL; ropts->opnum = DCE2_SENTINEL; ropts->hdr_byte_order = DCE2_SENTINEL; ropts->data_byte_order = DCE2_SENTINEL; ropts->stub_data = NULL; } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ static inline void DCE2_DisableDetect(SFSnortPacket *p) { _dpd.disableAllDetect(p); } static inline void DCE2_EnableDetect(void) { _dpd.enableContentDetect(); } #endif /* _SNORT_DCE2_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_event.h0000644000175000017500000001346114241075636021741 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Handles processing of events generated by the preprocessor. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _DCE2_EVENT_H_ #define _DCE2_EVENT_H_ #include "dce2_session.h" #include "dce2_config.h" #include "snort_debug.h" #include "dcerpc.h" #include "sf_types.h" /******************************************************************** * Macros ********************************************************************/ #define GENERATOR_DCE2 133 /******************************************************************** * Externs ********************************************************************/ extern char *dce2_pdu_types[DCERPC_PDU_TYPE__MAX]; /******************************************************************** * Enumerations ********************************************************************/ /* Since this is mirrored in generators.h via #defines, any * additions to this should go at the end, just before * DCE2_EVENT__MAX. It is important the the sids stay the * same in generators.h as these are also in gen-msg.map */ typedef enum _DCE2_Event { DCE2_EVENT__NO_EVENT = 0, DCE2_EVENT__MEMCAP, DCE2_EVENT__SMB_BAD_NBSS_TYPE, DCE2_EVENT__SMB_BAD_TYPE, DCE2_EVENT__SMB_BAD_ID, DCE2_EVENT__SMB_BAD_WCT, DCE2_EVENT__SMB_BAD_BCC, DCE2_EVENT__SMB_BAD_FORMAT, DCE2_EVENT__SMB_BAD_OFF, DCE2_EVENT__SMB_TDCNT_ZERO, DCE2_EVENT__SMB_NB_LT_SMBHDR, DCE2_EVENT__SMB_NB_LT_COM, DCE2_EVENT__SMB_NB_LT_BCC, DCE2_EVENT__SMB_NB_LT_DSIZE, DCE2_EVENT__SMB_TDCNT_LT_DSIZE, DCE2_EVENT__SMB_DSENT_GT_TDCNT, DCE2_EVENT__SMB_BCC_LT_DSIZE, DCE2_EVENT__SMB_INVALID_DSIZE, DCE2_EVENT__SMB_EXCESSIVE_TREE_CONNECTS, DCE2_EVENT__SMB_EXCESSIVE_READS, DCE2_EVENT__SMB_EXCESSIVE_CHAINING, DCE2_EVENT__SMB_MULT_CHAIN_SS, DCE2_EVENT__SMB_MULT_CHAIN_TC, DCE2_EVENT__SMB_CHAIN_SS_LOGOFF, DCE2_EVENT__SMB_CHAIN_TC_TDIS, DCE2_EVENT__SMB_CHAIN_OPEN_CLOSE, DCE2_EVENT__SMB_INVALID_SHARE, DCE2_EVENT__CO_BAD_MAJ_VERSION, DCE2_EVENT__CO_BAD_MIN_VERSION, DCE2_EVENT__CO_BAD_PDU_TYPE, DCE2_EVENT__CO_FLEN_LT_HDR, DCE2_EVENT__CO_FLEN_LT_SIZE, DCE2_EVENT__CO_ZERO_CTX_ITEMS, DCE2_EVENT__CO_ZERO_TSYNS, DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG, DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG, DCE2_EVENT__CO_ALTER_CHANGE_BYTE_ORDER, DCE2_EVENT__CO_FRAG_DIFF_CALL_ID, DCE2_EVENT__CO_FRAG_DIFF_OPNUM, DCE2_EVENT__CO_FRAG_DIFF_CTX_ID, DCE2_EVENT__CL_BAD_MAJ_VERSION, DCE2_EVENT__CL_BAD_PDU_TYPE, DCE2_EVENT__CL_DATA_LT_HDR, DCE2_EVENT__CL_BAD_SEQ_NUM, DCE2_EVENT__SMB_V1, DCE2_EVENT__SMB_V2, DCE2_EVENT__SMB_INVALID_BINDING, DCE2_EVENT__SMB2_EXCESSIVE_COMPOUNDING, DCE2_EVENT__SMB_DCNT_ZERO, DCE2_EVENT__SMB_DCNT_MISMATCH, DCE2_EVENT__SMB_MAX_REQS_EXCEEDED, DCE2_EVENT__SMB_REQS_SAME_MID, DCE2_EVENT__SMB_DEPR_DIALECT_NEGOTIATED, DCE2_EVENT__SMB_DEPR_COMMAND_USED, DCE2_EVENT__SMB_UNUSUAL_COMMAND_USED, DCE2_EVENT__SMB_INVALID_SETUP_COUNT, DCE2_EVENT__SMB_MULTIPLE_NEGOTIATIONS, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS, DCE2_EVENT__SMB_INVALID_FILE_OFFSET, DCE2_EVENT__SMB_BAD_NEXT_COMMAND_OFFSET, DCE2_EVENT__MAX } DCE2_Event; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_EventNode { DCE2_EventFlag eflag; DCE2_Event event; char *format; } DCE2_EventNode; /******************************************************************** * Public Function Prototypes ********************************************************************/ void DCE2_EventsInit(void); void DCE2_Alert(DCE2_SsnData *, DCE2_Event, ...); void DCE2_EventsFree(void); /******************************************************************** * Inline Function Prototypes ********************************************************************/ static inline int DCE2_SsnAlerted(DCE2_SsnData *, DCE2_Event); /****************************************************************** * Function: DCE2_SsnAlerted() * * Checks to see if we have already generated an alert on this * session for the event type passed in. * * Arguments: * DCE2_SsnData * * The session data structure. * DCE2_Event * The event to check for. * * Returns: * int * 1 if we have already alerted for this event type on this * session. * 0 if we have not alerted for this event type on this * session. * ******************************************************************/ static inline int DCE2_SsnAlerted(DCE2_SsnData *sd, DCE2_Event e) { if (sd->alert_mask & (1 << e)) return 1; return 0; } #endif /* _DCE2_EVENT_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dcerpc2_buffer_dump.c0000644000175000017500000000436414241075675023623 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file dcerpc2_buffer_dump.c ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during DCERPC2 inspection. */ #include "dcerpc2_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_DCERPC2_BUFFER_DUMP] = {{"DCERPC_SMB1_DUMP", "", 0}, {"DCERPC_SMB2_DUMP", "", 0}, {"DCERPC_TCP_DUMP", "", 0}, {"DCERPC_UDP_DUMP", "", 0}, {"DCERPC_HTTP_STATE_CLIENT_DUMP", "", 0}, {"DCERPC_HTTP_STATE_SERVER_DUMP", "", 0}, {"DCERPC_HTTP_STATE_RPC_DATA_DUMP", "", 0}}; void dumpBuffer(DCERPC2_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *)content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_DCERPC2_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getDCERPC2Buffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/dcerpc2/snort_dce2.c0000644000175000017500000012311214241075701021744 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_dce2.h" #include "spp_dce2.h" #include "dce2_config.h" #include "dce2_utils.h" #include "dce2_list.h" #include "dce2_stats.h" #include "dce2_session.h" #include "dce2_event.h" #include "dce2_smb.h" #include "dce2_udp.h" #include "dce2_tcp.h" #include "dce2_http.h" #include "dce2_co.h" #include "dce2_cl.h" #include "dce2_memory.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "sfrt.h" #include "profiler.h" #include "sfPolicy.h" #include "sf_seqnums.h" /******************************************************************** * Global variables ********************************************************************/ DCE2_CStack *dce2_pkt_stack = NULL; DCE2_ProtoIds dce2_proto_ids; static SFSnortPacket* dce2_rpkt[DCE2_RPKT_TYPE__MAX] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; #ifdef SNORT_RELOAD APPDATA_ADJUSTER *ada; #endif static int dce2_detected = 0; // Used to indicate that a session is no longer being looked at // It will be the session data that is returned so a pointer check // is sufficient to tell if the preprocessor shouldn't look at the session. uint8_t dce2_no_inspect; /******************************************************************** * Macros ********************************************************************/ #define DCE2_PKT_STACK__SIZE 10 /******************************************************************** * Private function prototypes ********************************************************************/ static DCE2_SsnData * DCE2_NewSession(SFSnortPacket *, tSfPolicyId); static DCE2_TransType DCE2_GetTransport(SFSnortPacket *, const DCE2_ServerConfig *, int *); static DCE2_TransType DCE2_GetDetectTransport(SFSnortPacket *, const DCE2_ServerConfig *); static DCE2_TransType DCE2_GetAutodetectTransport(SFSnortPacket *, const DCE2_ServerConfig *); static DCE2_Ret DCE2_SetSsnState(DCE2_SsnData *, SFSnortPacket *); static void DCE2_SsnFree(void *); /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ static DCE2_SsnData * DCE2_NewSession(SFSnortPacket *p, tSfPolicyId policy_id) { DCE2_SsnData *sd = NULL; DCE2_TransType trans; const DCE2_ServerConfig *sc = DCE2_ScGetConfig(p); int autodetected = 0; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_new_session); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Creating new session: ")); trans = DCE2_GetTransport(p, sc, &autodetected); switch (trans) { case DCE2_TRANS_TYPE__SMB: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "SMB transport ... ")); sd = (DCE2_SsnData *)DCE2_SmbSsnInit(p); break; case DCE2_TRANS_TYPE__TCP: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "TCP transport ... ")); sd = (DCE2_SsnData *)DCE2_TcpSsnInit(); break; case DCE2_TRANS_TYPE__UDP: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "UDP transport ... ")); sd = (DCE2_SsnData *)DCE2_UdpSsnInit(); break; case DCE2_TRANS_TYPE__HTTP_PROXY: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "RPC over HTTP proxy transport ... ")); sd = (DCE2_SsnData *)DCE2_HttpProxySsnInit(); break; case DCE2_TRANS_TYPE__HTTP_SERVER: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "RPC over HTTP server transport ... ")); sd = (DCE2_SsnData *)DCE2_HttpServerSsnInit(); break; case DCE2_TRANS_TYPE__NONE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Not configured to " "look at this traffic or unable to autodetect - not inspecting.\n")); PREPROC_PROFILE_END(dce2_pstat_new_session); return NULL; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, trans); PREPROC_PROFILE_END(dce2_pstat_new_session); return NULL; } if (sd == NULL) { PREPROC_PROFILE_END(dce2_pstat_new_session); return NULL; } DCE2_SsnSetAppData(p, (void *)sd, DCE2_SsnFree); #ifdef SNORT_RELOAD ada_add( ada, (void *) sd, p->stream_session ); #endif dce2_stats.sessions++; dce2_stats.sessions_active++; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Created (%p)\n", (void *)sd)); sd->trans = trans; sd->server_policy = DCE2_ScPolicy(sc); sd->client_policy = DCE2_POLICY__WINXP; // Default to Windows XP sd->sconfig = sc; sd->wire_pkt = p; sd->policy_id = policy_id; sd->config = dce2_config; ((DCE2_Config *)sfPolicyUserDataGet(sd->config, policy_id))->ref_count++; if (autodetected) { dce2_stats.sessions_autodetected++; #ifdef DEBUG if (DCE2_SsnFromServer(p)) dce2_stats.autoports[p->src_port][trans]++; else dce2_stats.autoports[p->dst_port][trans]++; #endif DCE2_SsnSetAutodetected(sd, p); } /* If we've determined a transport, make sure we're doing * reassembly on the session */ if (IsTCP(p)) { int rs_dir = DCE2_SsnGetReassembly(p); if (!_dpd.isPafEnabled() && (rs_dir != SSN_DIR_BOTH)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Setting client/server reassembly to FOOTPRINT for this session.\n")); DCE2_SsnSetReassembly(p); } if (!DCE2_SsnIsRebuilt(p)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Got non-rebuilt packet\n")); if (DCE2_SsnIsStreamInsert(p)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Stream inserted - not inspecting.\n")); PREPROC_PROFILE_END(dce2_pstat_new_session); return NULL; } else if ((DCE2_SsnFromClient(p) && (rs_dir == SSN_DIR_FROM_SERVER)) || (DCE2_SsnFromServer(p) && (rs_dir == SSN_DIR_FROM_CLIENT)) || (rs_dir == SSN_DIR_BOTH)) { /* Reassembly was already set for this session, but stream * decided not to use the packet so it's probably not good */ DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Got non-stream inserted packet - not inspecting\n")); PREPROC_PROFILE_END(dce2_pstat_new_session); return NULL; } } } PREPROC_PROFILE_END(dce2_pstat_new_session); return sd; } /********************************************************************* * Function: DCE2_Process() * * Purpose: Main entry point for DCE/RPC processing. * * Arguments: * SFSnortPacket * - pointer to packet structure * * Returns: * DCE2_Ret - status * *********************************************************************/ DCE2_Ret DCE2_Process(SFSnortPacket *p) { tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); DCE2_SsnData *sd = (DCE2_SsnData *)DCE2_SsnGetAppData(p); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_session); if ((sd != NULL) && DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Session set to " "not inspect. Returning\n")); PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } dce2_eval_config = (DCE2_Config *)sfPolicyUserDataGet(dce2_config, policy_id); if (sd != NULL) dce2_eval_config = (DCE2_Config *)sfPolicyUserDataGet(sd->config, sd->policy_id); if (dce2_eval_config == NULL) { PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } if (sd == NULL) { sd = DCE2_NewSession(p, policy_id); if (sd == NULL) { PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } } else { sd->wire_pkt = p; if (_dpd.isPafEnabled() && !DCE2_SsnIsPafActive(p)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "PAF was aborted on " "one or both sides - aborting session inspection\n")); DCE2_SetNoInspect(sd); PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } if (IsTCP(p) && !DCE2_SsnIsRebuilt(p)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Got non-rebuilt packet " "on session (%p)\n", (void *)sd)); if (DCE2_SsnIsStreamInsert(p)) { if (!_dpd.isPafEnabled()) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Flushing opposite direction.\n")); DCE2_SsnFlush(p); } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Stream inserted - not inspecting.\n")); } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Got non-stream inserted packet " "- not inspecting\n")); } PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } else if (DCE2_SsnAutodetected(sd) && !(p->flags & sd->autodetect_dir)) { /* Try to autodetect in opposite direction */ if ((sd->trans != DCE2_TRANS_TYPE__HTTP_PROXY) && (sd->trans != DCE2_TRANS_TYPE__HTTP_SERVER) && (DCE2_GetAutodetectTransport(p, sd->sconfig) != sd->trans)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Bad autodetect.\n")); DCE2_SetNoInspect(sd); dce2_stats.bad_autodetects++; PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } DCE2_SsnClearAutodetected(sd); } } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Session pointer: %p\n", (void *)sd)); if (IsTCP(p) && (DCE2_SetSsnState(sd, p) != DCE2_RET__SUCCESS)) { PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } if (DCE2_PushPkt((void *)p) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to push packet onto packet stack.", __FILE__, __LINE__); PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } p->flags |= FLAG_ALLOW_MULTIPLE_DETECT; dce2_detected = 0; PREPROC_PROFILE_END(dce2_pstat_session); switch (sd->trans) { case DCE2_TRANS_TYPE__SMB: DCE2_SmbProcess((DCE2_SmbSsnData *)sd); break; case DCE2_TRANS_TYPE__TCP: DCE2_TcpProcess((DCE2_TcpSsnData *)sd); break; case DCE2_TRANS_TYPE__UDP: DCE2_UdpProcess((DCE2_UdpSsnData *)sd); break; case DCE2_TRANS_TYPE__HTTP_PROXY: DCE2_HttpProcessProxy((DCE2_HttpSsnData *)sd); break; case DCE2_TRANS_TYPE__HTTP_SERVER: DCE2_HttpProcessServer((DCE2_HttpSsnData *)sd); break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, sd->trans); return DCE2_RET__NOT_INSPECTED; } if (sd->flags & DCE2_SSN_FLAG__NO_INSPECT) { DCE2_SetNoInspect(sd); DCE2_PopPkt(); PREPROC_PROFILE_END(dce2_pstat_session); return DCE2_RET__NOT_INSPECTED; } if (!dce2_detected) DCE2_Detect(sd); DCE2_ResetRopts(&sd->ropts); DCE2_PopPkt(); if (dce2_mem_state == DCE2_MEM_STATE__MEMCAP) { DCE2_SetNoInspect(sd); dce2_mem_state = DCE2_MEM_STATE__OKAY; return DCE2_RET__NOT_INSPECTED; } if (DCE2_SsnAutodetected(sd)) return DCE2_RET__NOT_INSPECTED; return DCE2_RET__INSPECTED; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_SetNoInspect(DCE2_SsnData *sd) { if (sd == NULL) return; dce2_stats.sessions_aborted++; DCE2_SsnSetNoInspect(sd->wire_pkt); } /******************************************************************** * Function: DCE2_SetSsnState() * * Purpose: * Checks for missing packets and overlapping data on session * * Arguments: * DCE2_SsnData * - session data pointer * SFSnortPacket * - packet structure * * Returns: * DCE2_RET__SUCCESS * DCE2_RET__ERROR * ********************************************************************/ static DCE2_Ret DCE2_SetSsnState(DCE2_SsnData *sd, SFSnortPacket *p) { uint32_t pkt_seq = ntohl(p->tcp_header->sequence); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_session_state); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Payload size: %u\n", p->payload_size)); if (DCE2_SsnFromClient(p) && !DCE2_SsnSeenClient(sd)) { uint32_t pkt_ack = ntohl(p->tcp_header->acknowledgement); if (DCE2_SsnSeenServer(sd) && SEQ_LT(sd->cli_seq, pkt_seq) && (sd->trans != DCE2_TRANS_TYPE__HTTP_SERVER)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Missing packets on session - aborting session inspection\n")); DCE2_SetNoInspect(sd); PREPROC_PROFILE_END(dce2_pstat_session_state); return DCE2_RET__ERROR; } DCE2_SsnSetSeenClient(sd); sd->cli_seq = pkt_seq; sd->cli_nseq = pkt_seq + p->payload_size; if (!DCE2_SsnSeenServer(sd)) sd->srv_seq = sd->srv_nseq = pkt_ack; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Initial client => seq: %u, " "next seq: %u\n", sd->cli_seq, sd->cli_nseq)); } else if (DCE2_SsnFromServer(p) && !DCE2_SsnSeenServer(sd)) { uint32_t pkt_ack = ntohl(p->tcp_header->acknowledgement); if ((DCE2_SsnSeenClient(sd) && SEQ_LT(sd->srv_seq, pkt_seq)) || (!DCE2_SsnSeenClient(sd) && (sd->trans != DCE2_TRANS_TYPE__HTTP_SERVER))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Missing packets on session - aborting session inspection\n")); PREPROC_PROFILE_END(dce2_pstat_session_state); DCE2_SetNoInspect(sd); return DCE2_RET__ERROR; } DCE2_SsnSetSeenServer(sd); sd->srv_seq = pkt_seq; sd->srv_nseq = pkt_seq + p->payload_size; if (!DCE2_SsnSeenClient(sd)) sd->cli_seq = sd->cli_nseq = pkt_ack; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Initial server => seq: %u, " "next seq: %u\n", sd->srv_seq, sd->srv_nseq)); } else { uint32_t *ssn_seq; uint32_t *ssn_nseq; if (DCE2_SsnFromClient(p)) { ssn_seq = &sd->cli_seq; ssn_nseq = &sd->cli_nseq; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Client last => seq: %u, " "next seq: %u\n", sd->cli_seq, sd->cli_nseq)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "This packet => seq: %u, " "next seq: %u\n", pkt_seq, pkt_seq + p->payload_size)); } else { ssn_seq = &sd->srv_seq; ssn_nseq = &sd->srv_nseq; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Server last => seq: %u, " "next seq: %u\n", sd->srv_seq, sd->srv_nseq)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "This packet => seq: %u, " "next seq: %u\n", pkt_seq, pkt_seq + p->payload_size)); } if (*ssn_nseq != pkt_seq) { if (SEQ_LT(*ssn_nseq, pkt_seq)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Next expected sequence number (%u) is less than " "this sequence number (%u).\n", *ssn_nseq, pkt_seq)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Missing packets on session - aborting session inspection\n")); DCE2_SetNoInspect(sd); PREPROC_PROFILE_END(dce2_pstat_session_state); return DCE2_RET__ERROR; } else { /* Got some kind of overlap. This shouldn't happen since we're doing * reassembly on both sides and not looking at non-reassembled packets * Actually this can happen if the stream seg list is empty */ DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Overlap => seq: %u, " "next seq: %u - aborting session inspection\n", pkt_seq, pkt_seq + p->payload_size)); DCE2_SetNoInspect(sd); PREPROC_PROFILE_END(dce2_pstat_session_state); return DCE2_RET__ERROR; } } *ssn_seq = pkt_seq; *ssn_nseq = pkt_seq + p->payload_size; } PREPROC_PROFILE_END(dce2_pstat_session_state); return DCE2_RET__SUCCESS; } /********************************************************************* * Function: DCE2_GetTransport() * * Determines whether or not we should look at this traffic and if * so, what transport it should be classified as. * * Arguments: * SFSnortPacket * * Pointer to packet structure. * const DCE2_ServerConfig * * The server configuration associated with the packet's IP. * int * * Pointer to a value that will be filled in with whether * or not the packet was autodetected. * Non-zero if autodetected * Zero if not autodetected * * Returns: * DCE2_TransType * DCE2_TRANS_TYPE__NONE if a transport could not be * determined or target based labeled the session as * traffic we are not interested in. * DCE2_TRANS_TYPE__SMB if the traffic is determined to be * DCE/RPC over SMB. * DCE2_TRANS_TYPE__TCP if the traffic is determined to be * DCE/RPC over TCP. * DCE2_TRANS_TYPE__UDP if the traffic is determined to be * DCE/RPC over UDP. * DCE2_TRANS_TYPE__HTTP_PROXY if the traffic is determined * to be DCE/RPC over HTTP proxy. * DCE2_TRANS_TYPE__HTTP_SERVER if the traffic is determined * to be DCE/RPC over HTTP server. * *********************************************************************/ static DCE2_TransType DCE2_GetTransport(SFSnortPacket *p, const DCE2_ServerConfig *sc, int *autodetected) { DCE2_TransType trans = DCE2_TRANS_TYPE__NONE; #ifdef TARGET_BASED int16_t proto_id = 0; #endif *autodetected = 0; #ifdef TARGET_BASED if (_dpd.isAdaptiveConfigured()) { proto_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if (proto_id == SFTARGET_UNKNOWN_PROTOCOL) return DCE2_TRANS_TYPE__NONE; } if (proto_id != 0) { if (proto_id == dce2_proto_ids.dcerpc) { if (IsTCP(p)) { return DCE2_TRANS_TYPE__TCP; } else { return DCE2_TRANS_TYPE__UDP; } } else if (proto_id == dce2_proto_ids.nbss) { return DCE2_TRANS_TYPE__SMB; } } else #endif { trans = DCE2_GetDetectTransport(p, sc); if (trans == DCE2_TRANS_TYPE__NONE) { trans = DCE2_GetAutodetectTransport(p, sc); *autodetected = 1; } else if ((trans == DCE2_TRANS_TYPE__HTTP_PROXY) && (DCE2_ScAutodetectHttpProxyPorts(sc) == DCE2_CS__ENABLED)) { trans = DCE2_HttpAutodetectProxy(p); *autodetected = 1; } } return trans; } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ static DCE2_TransType DCE2_GetDetectTransport(SFSnortPacket *p, const DCE2_ServerConfig *sc) { DCE2_TransType trans = DCE2_TRANS_TYPE__NONE; uint16_t port; if (DCE2_SsnFromServer(p)) port = p->src_port; else port = p->dst_port; /* Check our configured ports to see if we should continue processing */ if (IsTCP(p)) { if (DCE2_ScIsDetectPortSet(sc, port, DCE2_TRANS_TYPE__SMB)) trans = DCE2_TRANS_TYPE__SMB; else if (DCE2_ScIsDetectPortSet(sc, port, DCE2_TRANS_TYPE__TCP)) trans = DCE2_TRANS_TYPE__TCP; else if (DCE2_ScIsDetectPortSet(sc, port, DCE2_TRANS_TYPE__HTTP_PROXY)) trans = DCE2_TRANS_TYPE__HTTP_PROXY; else if (DCE2_ScIsDetectPortSet(sc, port, DCE2_TRANS_TYPE__HTTP_SERVER)) trans = DCE2_TRANS_TYPE__HTTP_SERVER; } else /* it's UDP */ { if (DCE2_ScIsDetectPortSet(sc, port, DCE2_TRANS_TYPE__UDP)) trans = DCE2_TRANS_TYPE__UDP; } return trans; } /********************************************************************* * Function: DCE2_GetAutodetectTransport() * * * Arguments: * * Returns: * *********************************************************************/ static DCE2_TransType DCE2_GetAutodetectTransport(SFSnortPacket *p, const DCE2_ServerConfig *sc) { DCE2_TransType trans = DCE2_TRANS_TYPE__NONE; uint16_t port; if (DCE2_SsnFromServer(p)) port = p->src_port; else port = p->dst_port; if (IsTCP(p)) { /* Look for raw DCE/RCP over TCP first, since it's * more likely not to have configured a port for this. */ if (DCE2_ScIsAutodetectPortSet(sc, port, DCE2_TRANS_TYPE__TCP)) { trans = DCE2_TcpAutodetect(p); if (trans != DCE2_TRANS_TYPE__NONE) return trans; } if (DCE2_ScIsAutodetectPortSet(sc, port, DCE2_TRANS_TYPE__HTTP_SERVER)) { trans = DCE2_HttpAutodetectServer(p); if (trans != DCE2_TRANS_TYPE__NONE) return trans; } if (DCE2_ScIsAutodetectPortSet(sc, port, DCE2_TRANS_TYPE__HTTP_PROXY)) { trans = DCE2_HttpAutodetectProxy(p); if (trans != DCE2_TRANS_TYPE__NONE) return trans; } if (DCE2_ScIsAutodetectPortSet(sc, port, DCE2_TRANS_TYPE__SMB)) { trans = DCE2_SmbAutodetect(p); if (trans != DCE2_TRANS_TYPE__NONE) return trans; } } else /* it's UDP */ { if (DCE2_ScIsAutodetectPortSet(sc, port, DCE2_TRANS_TYPE__UDP)) { trans = DCE2_UdpAutodetect(p); if (trans != DCE2_TRANS_TYPE__NONE) return trans; } } return DCE2_TRANS_TYPE__NONE; } /********************************************************************* * Function: DCE2_InitRpkts() * * Purpose: Allocate and initialize reassembly packets. * * Arguments: None * * Returns: None * *********************************************************************/ void DCE2_InitRpkts(void) { int i; dce2_pkt_stack = DCE2_CStackNew(DCE2_PKT_STACK__SIZE, NULL, DCE2_MEM_TYPE__INIT); if (dce2_pkt_stack == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for packet stack.", __FILE__, __LINE__); } for ( i = 0; i < DCE2_RPKT_TYPE__MAX; i++ ) dce2_rpkt[i] = _dpd.encodeNew(); } /********************************************************************* * Function: DCE2_GetRpkt() * * Purpose: * * Arguments: * SFSnortPacket * - pointer to packet off wire * const uint8_t * - pointer to data to attach to reassembly packet * uint16_t - length of data * * Returns: * SFSnortPacket * - pointer to reassembly packet * *********************************************************************/ SFSnortPacket * DCE2_GetRpkt(const SFSnortPacket *wire_pkt, DCE2_RpktType rpkt_type, const uint8_t *data, uint32_t data_len) { DCE2_Ret status; SFSnortPacket *rpkt; uint16_t payload_len = 0; uint16_t data_overhead = 0; rpkt = dce2_rpkt[rpkt_type]; switch (rpkt_type) { case DCE2_RPKT_TYPE__SMB_SEG: _dpd.encodeFormat(ENC_DYN_FWD, wire_pkt, rpkt, PSEUDO_PKT_SMB_SEG); break; case DCE2_RPKT_TYPE__SMB_TRANS: // TBD these memset()s could be encapsulated by the various // init functions which should also return the data_overhead. // Better still pass in rpkt and let the init function update // payload, etc. Also, some memsets could probably be avoided // by explicitly setting the unitialized header fields. _dpd.encodeFormat(ENC_DYN_FWD, wire_pkt, rpkt, PSEUDO_PKT_SMB_TRANS); if (DCE2_SsnFromClient(wire_pkt)) { data_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI; memset((void*)rpkt->payload, 0, data_overhead); DCE2_SmbInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_CLIENT); } else { data_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV; memset((void*)rpkt->payload, 0, data_overhead); DCE2_SmbInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_SERVER); } break; case DCE2_RPKT_TYPE__SMB_CO_SEG: _dpd.encodeFormat(ENC_DYN_FWD, wire_pkt, rpkt, PSEUDO_PKT_DCE_SEG); if (DCE2_SsnFromClient(wire_pkt)) { data_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI; memset((void*)rpkt->payload, 0, data_overhead); DCE2_SmbInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_CLIENT); } else { data_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV; memset((void*)rpkt->payload, 0, data_overhead); DCE2_SmbInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_SERVER); } break; case DCE2_RPKT_TYPE__SMB_CO_FRAG: _dpd.encodeFormat(ENC_DYN_FWD, wire_pkt, rpkt, PSEUDO_PKT_DCE_FRAG); if (DCE2_SsnFromClient(wire_pkt)) { data_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI + DCE2_MOCK_HDR_LEN__CO_CLI; memset((void*)rpkt->payload, 0, data_overhead); DCE2_SmbInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_CLIENT); DCE2_CoInitRdata((uint8_t *)rpkt->payload + DCE2_MOCK_HDR_LEN__SMB_CLI, FLAG_FROM_CLIENT); } else { data_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV + DCE2_MOCK_HDR_LEN__CO_SRV; memset((void*)rpkt->payload, 0, data_overhead); DCE2_SmbInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_SERVER); DCE2_CoInitRdata((uint8_t *)rpkt->payload + DCE2_MOCK_HDR_LEN__SMB_SRV, FLAG_FROM_SERVER); } break; case DCE2_RPKT_TYPE__TCP_CO_SEG: _dpd.encodeFormat(ENC_DYN_FWD, wire_pkt, rpkt, PSEUDO_PKT_DCE_SEG); break; case DCE2_RPKT_TYPE__TCP_CO_FRAG: _dpd.encodeFormat(ENC_DYN_FWD, wire_pkt, rpkt, PSEUDO_PKT_DCE_FRAG); if (DCE2_SsnFromClient(wire_pkt)) { data_overhead = DCE2_MOCK_HDR_LEN__CO_CLI; memset((void*)rpkt->payload, 0, data_overhead); DCE2_CoInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_CLIENT); } else { data_overhead = DCE2_MOCK_HDR_LEN__CO_SRV; memset((void*)rpkt->payload, 0, data_overhead); DCE2_CoInitRdata((uint8_t *)rpkt->payload, FLAG_FROM_SERVER); } break; case DCE2_RPKT_TYPE__UDP_CL_FRAG: _dpd.encodeFormat(ENC_DYN_FWD, wire_pkt, rpkt, PSEUDO_PKT_DCE_FRAG); data_overhead = DCE2_MOCK_HDR_LEN__CL; memset((void*)rpkt->payload, 0, data_overhead); DCE2_ClInitRdata((uint8_t *)rpkt->payload); break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid reassembly packet type: %d", __FILE__, __LINE__, rpkt_type); return NULL; } payload_len = rpkt->max_payload; if ((data_overhead + data_len) > payload_len) data_len -= (data_overhead + data_len) - payload_len; status = DCE2_Memcpy( (void *)(rpkt->payload + data_overhead), (void *)data, (size_t)data_len, (void *)rpkt->payload, (void *)((uint8_t *)rpkt->payload + payload_len)); if (status != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to copy data into reassembly packet.", __FILE__, __LINE__); return NULL; } rpkt->payload_size = (uint16_t)(data_overhead + data_len); _dpd.encodeUpdate(rpkt); if (wire_pkt->family == AF_INET) { rpkt->ip4h->ip_len = rpkt->ip4_header->data_length; } else { IP6RawHdr* ip6h = (IP6RawHdr*)rpkt->raw_ip6_header; if ( ip6h ) rpkt->ip6h->len = ip6h->ip6_payload_len; } rpkt->flags |= FLAG_STREAM_EST; if (DCE2_SsnFromClient(wire_pkt)) rpkt->flags |= FLAG_FROM_CLIENT; else rpkt->flags |= FLAG_FROM_SERVER; rpkt->stream_session = wire_pkt->stream_session; return rpkt; } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ DCE2_Ret DCE2_AddDataToRpkt(SFSnortPacket *rpkt, DCE2_RpktType rtype, const uint8_t *data, uint32_t data_len) { int hdr_overhead = 0; const uint8_t *pkt_data_end; const uint8_t *payload_end; DCE2_Ret status; if ((rpkt == NULL) || (data == NULL) || (data_len == 0)) return DCE2_RET__ERROR; if (rpkt->payload == NULL) return DCE2_RET__ERROR; /* This is a check to make sure we don't overwrite header data */ switch (rtype) { case DCE2_RPKT_TYPE__SMB_CO_SEG: if (DCE2_SsnFromClient(rpkt)) hdr_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI; else hdr_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV; break; case DCE2_RPKT_TYPE__SMB_CO_FRAG: if (DCE2_SsnFromClient(rpkt)) hdr_overhead = DCE2_MOCK_HDR_LEN__SMB_CLI + DCE2_MOCK_HDR_LEN__CO_CLI; else hdr_overhead = DCE2_MOCK_HDR_LEN__SMB_SRV + DCE2_MOCK_HDR_LEN__CO_SRV; break; case DCE2_RPKT_TYPE__TCP_CO_FRAG: if (DCE2_SsnFromClient(rpkt)) hdr_overhead = DCE2_MOCK_HDR_LEN__CO_CLI; else hdr_overhead = DCE2_MOCK_HDR_LEN__CO_SRV; break; case DCE2_RPKT_TYPE__UDP_CL_FRAG: hdr_overhead = DCE2_MOCK_HDR_LEN__CL; break; default: break; } if (rpkt->payload_size < hdr_overhead) return DCE2_RET__ERROR; pkt_data_end = rpkt->pkt_data + rpkt->max_payload; payload_end = rpkt->payload + rpkt->payload_size; if ((payload_end + data_len) > pkt_data_end) data_len = pkt_data_end - payload_end; status = DCE2_Memcpy((void *)payload_end, (void *)data, (size_t)data_len, (void *)payload_end, (void *)pkt_data_end); if (status != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to copy data into reassembly packet.", __FILE__, __LINE__); return DCE2_RET__ERROR; } rpkt->payload_size += (uint16_t)data_len; // there is room for optimization here since the update was done // earlier - that my be eliminated, but only in this case one // approach is to move the updates to push pkt - but don't want // to update non-dce2 pseudo pkts; perhaps a flag check there // will suffice. _dpd.encodeUpdate(rpkt); if (rpkt->family == AF_INET) { rpkt->ip4h->ip_len = rpkt->ip4_header->data_length; } else { IP6RawHdr* ip6h = (IP6RawHdr*)rpkt->raw_ip6_header; if ( ip6h ) rpkt->ip6h->len = ip6h->ip6_payload_len; } return DCE2_RET__SUCCESS; } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ DCE2_Ret DCE2_PushPkt(SFSnortPacket *p) { SFSnortPacket *top_pkt = (SFSnortPacket *)DCE2_CStackTop(dce2_pkt_stack); if (top_pkt != NULL) { PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_log); _dpd.pushAlerts(); _dpd.logAlerts((void *)top_pkt); _dpd.resetAlerts(); _dpd.popAlerts(); PREPROC_PROFILE_END(dce2_pstat_log); } if (DCE2_CStackPush(dce2_pkt_stack, (void *)p) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; return DCE2_RET__SUCCESS; } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ void DCE2_PopPkt(void) { SFSnortPacket *pop_pkt = (SFSnortPacket *)DCE2_CStackPop(dce2_pkt_stack); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_log); if (pop_pkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) No packet to pop off stack.", __FILE__, __LINE__); PREPROC_PROFILE_END(dce2_pstat_log); return; } _dpd.pushAlerts(); _dpd.logAlerts((void *)pop_pkt); _dpd.resetAlerts(); _dpd.popAlerts(); PREPROC_PROFILE_END(dce2_pstat_log); } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ void DCE2_Detect(DCE2_SsnData *sd) { SFSnortPacket *top_pkt = (SFSnortPacket *)DCE2_CStackTop(dce2_pkt_stack); PROFILE_VARS; if (top_pkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) No packet on top of stack.", __FILE__, __LINE__); return; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Detecting ------------------------------------------------\n")); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ROPTIONS, " Rule options:\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__ROPTIONS, DCE2_PrintRoptions(&sd->ropts);); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Payload:\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(top_pkt->payload, top_pkt->payload_size);); DCE2_DEBUG_CODE(DCE2_DEBUG__ROPTIONS, if (sd->ropts.stub_data != NULL) { printf("\nStub data:\n"); DCE2_PrintPktData(sd->ropts.stub_data, top_pkt->payload_size - (sd->ropts.stub_data - top_pkt->payload)); }); PREPROC_PROFILE_START(dce2_pstat_detect); _dpd.pushAlerts(); _dpd.detect(top_pkt); _dpd.popAlerts(); PREPROC_PROFILE_END(dce2_pstat_detect); /* Always reset rule option data after detecting */ DCE2_ResetRopts(&sd->ropts); dce2_detected = 1; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "----------------------------------------------------------\n")); } void DCE2_FileDetect(DCE2_SsnData *sd) { SFSnortPacket *top_pkt = (SFSnortPacket *)DCE2_CStackTop(dce2_pkt_stack); PROFILE_VARS; if (top_pkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) No packet on top of stack.", __FILE__, __LINE__); return; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Detecting ------------------------------------------------\n")); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Payload:\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(top_pkt->payload, top_pkt->payload_size);); PREPROC_PROFILE_START(dce2_pstat_smb_file_detect); _dpd.pushAlerts(); _dpd.detect(top_pkt); _dpd.popAlerts(); PREPROC_PROFILE_END(dce2_pstat_smb_file_detect); // Reset file data pointer after detecting _dpd.setFileDataPtr(NULL, 0); dce2_detected = 1; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "----------------------------------------------------------\n")); } /********************************************************************* * Function: * * Purpose: * * Arguments: * * Returns: * *********************************************************************/ // TBD this function could be called on the actual rpkt // to very easily get the exact available payload space and // then truncate the data as needed. That avoids the calculations // here which inevitably include tacit assumptions about the // rpkt which may not be true (nor future proof). uint16_t DCE2_GetRpktMaxData(DCE2_SsnData *sd, DCE2_RpktType rtype) { const SFSnortPacket *p = sd->wire_pkt; uint16_t overhead; uint8_t* base, *last; int n = p->next_layer_index - 1; if ( n < 2 ) return 0; base = p->proto_layers[1].proto_start; last = p->proto_layers[n].proto_start + p->proto_layers[n].proto_length; overhead = last - base; switch (rtype) { case DCE2_RPKT_TYPE__SMB_SEG: case DCE2_RPKT_TYPE__SMB_TRANS: break; case DCE2_RPKT_TYPE__SMB_CO_SEG: if (DCE2_SsnFromClient(p)) overhead += DCE2_MOCK_HDR_LEN__SMB_CLI; else overhead += DCE2_MOCK_HDR_LEN__SMB_SRV; break; case DCE2_RPKT_TYPE__SMB_CO_FRAG: if (DCE2_SsnFromClient(p)) overhead += DCE2_MOCK_HDR_LEN__SMB_CLI + DCE2_MOCK_HDR_LEN__CO_CLI; else overhead += DCE2_MOCK_HDR_LEN__SMB_SRV + DCE2_MOCK_HDR_LEN__CO_SRV; break; case DCE2_RPKT_TYPE__TCP_CO_SEG: break; case DCE2_RPKT_TYPE__TCP_CO_FRAG: if (DCE2_SsnFromClient(p)) overhead += DCE2_MOCK_HDR_LEN__CO_CLI; else overhead += DCE2_MOCK_HDR_LEN__CO_SRV; break; case DCE2_RPKT_TYPE__UDP_CL_FRAG: overhead += DCE2_MOCK_HDR_LEN__CL; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid reassembly packet type: %d", __FILE__, __LINE__, rtype); return 0; } return (IP_MAXPKT - overhead); } /****************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ******************************************************************/ void DCE2_FreeGlobals(void) { int i; if (dce2_pkt_stack != NULL) { DCE2_CStackDestroy(dce2_pkt_stack); dce2_pkt_stack = NULL; } for ( i = 0; i < DCE2_RPKT_TYPE__MAX; i++ ) { if ( dce2_rpkt[i] != NULL ) { _dpd.encodeDelete(dce2_rpkt[i]); dce2_rpkt[i] = NULL; } } DCE2_EventsFree(); } static void DCE2_SsnFree(void *data) { DCE2_SsnData *sd = (DCE2_SsnData *)data; #ifdef SNORT_RELOAD DCE2_Config *pPolicyConfig; tSfPolicyUserContextId config; tSfPolicyId policy_id; #endif if (sd == NULL) return; #ifdef SNORT_RELOAD ada_appdata_freed( ada, data ); config = sd->config; policy_id = sd->policy_id; #endif switch (sd->trans) { case DCE2_TRANS_TYPE__SMB: DCE2_SmbSsnFree((DCE2_SmbSsnData *)sd); break; case DCE2_TRANS_TYPE__TCP: DCE2_TcpSsnFree((DCE2_TcpSsnData *)sd); break; case DCE2_TRANS_TYPE__UDP: DCE2_UdpSsnFree((DCE2_UdpSsnData *)sd); break; case DCE2_TRANS_TYPE__HTTP_SERVER: case DCE2_TRANS_TYPE__HTTP_PROXY: DCE2_HttpSsnFree((DCE2_HttpSsnData *)sd); break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, sd->trans); return; } #ifdef SNORT_RELOAD pPolicyConfig = (DCE2_Config *)sfPolicyUserDataGet(config, policy_id); if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (config != dce2_config)) { sfPolicyUserDataClear (config, policy_id); DCE2_FreeConfig(pPolicyConfig); /* No more outstanding policies for this config */ if (sfPolicyUserPolicyGetActive(config) == 0) DCE2_FreeConfigs(config); } } #endif dce2_stats.sessions_active--; } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/sf_dce2.vcxproj0000444000175000017500000004440414230012554022457 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {30A90482-54E5-49E1-8FD3-4AB70F049D07} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false .\includes;..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ true .\Release\sf_dce2.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_dce2.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_dce2.bsc true true Console .\Release\sf_dce2.dll .\Release\sf_dce2.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false .\includes;..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ true .\Release\sf_dce2.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_dce2.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_dce2.bsc true true Console .\Release\sf_dce2.dll .\Release\sf_dce2.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false .\includes;..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ true .\Debug\sf_dce2.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_dce2.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_dce2.bsc true true true Console .\Debug\sf_dce2.dll .\Debug\sf_dce2.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false .\includes;..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ true .\Debug\sf_dce2.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_dce2.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_dce2.bsc true true true Console .\Debug\sf_dce2.dll .\Debug\sf_dce2.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_smb.h0000644000175000017500000003157114241075661021401 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_SMB_H_ #define _DCE2_SMB_H_ #include "dce2_session.h" #include "dce2_tcp.h" #include "dce2_list.h" #include "dce2_utils.h" #include "smb.h" #include "sf_snort_packet.h" #include "sf_types.h" #include "snort_debug.h" /******************************************************************** * Macros ********************************************************************/ // Used for reassembled packets #define DCE2_MOCK_HDR_LEN__SMB_CLI \ (sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq)) #define DCE2_MOCK_HDR_LEN__SMB_SRV \ (sizeof(NbssHdr) + sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp)) // This is for ease of comparison so a 32 bit numeric compare can be done // instead of a string compare. #define DCE2_SMB_ID 0xff534d42 /* \xffSMB */ #define DCE2_SMB2_ID 0xfe534d42 /* \xfeSMB */ #define DCE2_SMB_ID_SIZE 4 // MS-FSCC Section 2.1.5 - Pathname #define DCE2_SMB_MAX_PATH_LEN 32760 #define DCE2_SMB_MAX_COMP_LEN 255 /******************************************************************** * Externs ********************************************************************/ extern SmbAndXCom smb_chain_map[SMB_MAX_NUM_COMS]; extern const char *smb_com_strings[SMB_MAX_NUM_COMS]; extern const char *smb_transaction_sub_command_strings[TRANS_SUBCOM_MAX]; extern const char *smb_transaction2_sub_command_strings[TRANS2_SUBCOM_MAX]; extern const char *smb_nt_transact_sub_command_strings[NT_TRANSACT_SUBCOM_MAX]; extern uint8_t smb_file_name[2*DCE2_SMB_MAX_PATH_LEN + UTF_16_LE_BOM_LEN + 2]; extern uint16_t smb_file_name_len; /******************************************************************** * Enums ********************************************************************/ typedef enum _DCE2_SmbSsnState { DCE2_SMB_SSN_STATE__START = 0x00, DCE2_SMB_SSN_STATE__NEGOTIATED = 0x01, DCE2_SMB_SSN_STATE__FP_CLIENT = 0x02, // Fingerprinted client DCE2_SMB_SSN_STATE__FP_SERVER = 0x04 // Fingerprinted server } DCE2_SmbSsnState; typedef enum _DCE2_SmbDataState { DCE2_SMB_DATA_STATE__NETBIOS_HEADER, DCE2_SMB_DATA_STATE__SMB_HEADER, DCE2_SMB_DATA_STATE__NETBIOS_PDU } DCE2_SmbDataState; typedef enum _DCE2_SmbPduState { DCE2_SMB_PDU_STATE__COMMAND, DCE2_SMB_PDU_STATE__RAW_DATA } DCE2_SmbPduState; typedef enum _DCE2_SmbFileDirection { DCE2_SMB_FILE_DIRECTION__UNKNOWN = 0, DCE2_SMB_FILE_DIRECTION__UPLOAD, DCE2_SMB_FILE_DIRECTION__DOWNLOAD } DCE2_SmbFileDirection; /* This structure is to maintain that we have received a pending veridct in case of upload & we will not delete the trackers*/ typedef enum _DCE2_SmbRetransmitPending { DCE2_SMB_RETRANSMIT_PENDING__UNSET = 0, DCE2_SMB_RETRANSMIT_PENDING__SET } DCE2_SmbRetransmitPending; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_SmbWriteAndXRaw { int remaining; // A signed integer so it can be negative DCE2_Buffer *buf; } DCE2_SmbWriteAndXRaw; typedef struct _DCE2_SmbFileChunk { uint64_t offset; uint32_t length; uint8_t *data; } DCE2_SmbFileChunk; typedef struct _DCE2_SmbFileTracker { union { struct { int file_id; // A signed integer so it can be set to sentinel uint16_t u_id; uint16_t tree_id; } id_smb1; struct { uint64_t file_id; } id_smb2; } file_key; bool is_ipc; bool is_smb2; uint16_t file_name_len; uint8_t *file_name; union { struct { // If pipe has been set to byte mode via TRANS_SET_NMPIPE_STATE bool byte_mode; // For Windows 2000 bool used; // For WriteAndX requests that use raw mode flag // Windows only DCE2_SmbWriteAndXRaw *writex_raw; // Connection-oriented DCE/RPC tracker DCE2_CoTracker *co_tracker; } nmpipe; struct { uint64_t file_size; uint64_t file_offset; uint64_t bytes_processed; DCE2_List *file_chunks; uint32_t bytes_queued; DCE2_SmbFileDirection file_direction; bool sequential_only; } file; } tracker; #define fid_v1 file_key.id_smb1.file_id #define uid_v1 file_key.id_smb1.u_id #define tid_v1 file_key.id_smb1.tree_id #define fid_v2 file_key.id_smb2.file_id #define fp_byte_mode tracker.nmpipe.byte_mode #define fp_used tracker.nmpipe.used #define fp_writex_raw tracker.nmpipe.writex_raw #define fp_co_tracker tracker.nmpipe.co_tracker #define ff_file_size tracker.file.file_size #define ff_file_offset tracker.file.file_offset #define ff_bytes_processed tracker.file.bytes_processed #define ff_file_direction tracker.file.file_direction #define ff_file_chunks tracker.file.file_chunks #define ff_bytes_queued tracker.file.bytes_queued #define ff_sequential_only tracker.file.sequential_only } DCE2_SmbFileTracker; typedef enum _DCE2_SmbVersion { DCE2_SMB_VERISON_NULL, DCE2_SMB_VERISON_1, DCE2_SMB_VERISON_2 } DCE2_SmbVersion; typedef struct _Smb2Request { uint64_t message_id; /* identifies a message uniquely on connection */ uint16_t command; union { struct { uint64_t offset; /* data offset */ uint64_t file_id; /* file id */ }read_req; struct { char *file_name; /*file name*/ uint16_t file_name_len; /*size*/ bool durable_reconnect; /*durable reconenct? */ }create_req; }; struct _Smb2Request *next; struct _Smb2Request *previous; } Smb2Request; typedef struct _DCE2_SmbTransactionTracker { int smb_type; uint8_t subcom; bool one_way; bool disconnect_tid; bool pipe_byte_mode; uint32_t tdcnt; uint32_t dsent; DCE2_Buffer *dbuf; uint32_t tpcnt; uint32_t psent; DCE2_Buffer *pbuf; // For Transaction2/Query File Information uint16_t info_level; } DCE2_SmbTransactionTracker; typedef struct _DCE2_SmbRequestTracker { int smb_com; int mid; // A signed integer so it can be set to sentinel uint16_t uid; uint16_t tid; uint16_t pid; // For WriteRaw bool writeraw_writethrough; uint32_t writeraw_remaining; uint16_t file_name_len; // For Transaction/Transaction2/NtTransact DCE2_SmbTransactionTracker ttracker; // Client can chain a write to an open. Need to write data, but also // need to associate tracker with fid returned from server DCE2_Queue *ft_queue; // This is a reference to an existing file tracker DCE2_SmbFileTracker *ftracker; // Used for requests to cache data that will ultimately end up in // the file tracker upon response. uint8_t *file_name; uint64_t file_size; uint64_t file_offset; bool sequential_only; // For TreeConnect to know whether it's to IPC bool is_ipc; } DCE2_SmbRequestTracker; typedef struct _DCE2_SmbSsnData { DCE2_SsnData sd; // This member must be first DCE2_Policy policy; int dialect_index; int ssn_state_flags; DCE2_SmbDataState cli_data_state; DCE2_SmbDataState srv_data_state; DCE2_SmbPduState pdu_state; int uid; // A signed integer so it can be set to sentinel int tid; // A signed integer so it can be set to sentinel DCE2_List *uids; DCE2_List *tids; // For tracking files and named pipes DCE2_SmbFileTracker ftracker; DCE2_List *ftrackers; // List of DCE2_SmbFileTracker // For tracking requests / responses DCE2_SmbRequestTracker rtracker; DCE2_Queue *rtrackers; // The current pid/mid node for this request/response DCE2_SmbRequestTracker *cur_rtracker; // Used for TCP segmentation to get full PDU DCE2_Buffer *cli_seg; DCE2_Buffer *srv_seg; // These are used for commands we don't need to process uint32_t cli_ignore_bytes; uint32_t srv_ignore_bytes; // The file API supports one concurrent upload/download per session. // This is a reference to a file tracker so shouldn't be freed. DCE2_SmbFileTracker *fapi_ftracker; Smb2Request *smb2_requests; #ifdef ACTIVE_RESPONSE DCE2_SmbFileTracker *fb_ftracker; bool block_pdus; #endif bool smbfound; bool smbretransmit; uint16_t max_outstanding_requests; uint16_t outstanding_requests; // Maximum file depth as returned from file API int64_t max_file_depth; } DCE2_SmbSsnData; typedef struct _DCE2SmbFsm { char input; int next_state; int fail_state; } DCE2_SmbFsm; /******************************************************************** * Inline function prototypes ********************************************************************/ static inline DCE2_TransType DCE2_SmbAutodetect(const SFSnortPacket *); static inline void DCE2_SmbSetFingerprintedClient(DCE2_SmbSsnData *); static inline bool DCE2_SmbFingerprintedClient(DCE2_SmbSsnData *); static inline void DCE2_SmbSetFingerprintedServer(DCE2_SmbSsnData *); static inline bool DCE2_SmbFingerprintedServer(DCE2_SmbSsnData *); /******************************************************************** * Public function prototypes ********************************************************************/ void DCE2_SmbInitGlobals(void); void DCE2_SmbInitRdata(uint8_t *, int); void DCE2_SmbSetRdata(DCE2_SmbSsnData *, uint8_t *, uint16_t); DCE2_SmbSsnData * DCE2_SmbSsnInit(SFSnortPacket *); void DCE2_SmbProcess(DCE2_SmbSsnData *); void DCE2_SmbDataFree(DCE2_SmbSsnData *); void DCE2_SmbSsnFree(void *); #ifdef ACTIVE_RESPONSE void DCE2_SmbInitDeletePdu(void); #endif void DCE2_Process_Retransmitted(SFSnortPacket *); /********************************************************************* * Function: DCE2_SmbAutodetect() * * Purpose: Tries to determine if a packet is likely to be SMB. * * Arguments: * const uint8_t * - pointer to packet data. * uint16_t - packet data length. * * Returns: * DCE2_TranType * *********************************************************************/ static inline DCE2_TransType DCE2_SmbAutodetect(const SFSnortPacket *p) { if (p->payload_size > (sizeof(NbssHdr) + sizeof(SmbNtHdr))) { NbssHdr *nb_hdr = (NbssHdr *)p->payload; switch (NbssType(nb_hdr)) { case NBSS_SESSION_TYPE__MESSAGE: { SmbNtHdr *smb_hdr = (SmbNtHdr *)(p->payload + sizeof(NbssHdr)); if ((SmbId(smb_hdr) == DCE2_SMB_ID) || (SmbId(smb_hdr) == DCE2_SMB2_ID)) { return DCE2_TRANS_TYPE__SMB; } } break; default: break; } } return DCE2_TRANS_TYPE__NONE; } static inline void DCE2_SmbSetFingerprintedClient(DCE2_SmbSsnData *ssd) { ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__FP_CLIENT; } static inline bool DCE2_SmbFingerprintedClient(DCE2_SmbSsnData *ssd) { return ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__FP_CLIENT; } static inline void DCE2_SmbSetFingerprintedServer(DCE2_SmbSsnData *ssd) { ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__FP_SERVER; } static inline bool DCE2_SmbFingerprintedServer(DCE2_SmbSsnData *ssd) { return ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__FP_SERVER; } static inline bool DCE2_SmbFileDirUnknown(DCE2_SmbFileDirection dir) { return dir == DCE2_SMB_FILE_DIRECTION__UNKNOWN; } static inline bool DCE2_SmbFileUpload(DCE2_SmbFileDirection dir) { return dir == DCE2_SMB_FILE_DIRECTION__UPLOAD; } static inline bool DCE2_SmbFileDownload(DCE2_SmbFileDirection dir) { return dir == DCE2_SMB_FILE_DIRECTION__DOWNLOAD; } #endif /* _DCE2_SMB_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_smb2.c0000644000175000017500000011310214241075662021446 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * SMB2 file processing * Author(s): Hui Cao ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "dce2_smb2.h" #include "file_api.h" #include "dce2_session.h" #include "sf_snort_packet.h" #include "dce2_stats.h" #include "dce2_config.h" #include "snort_dce2.h" #include "dce2_event.h" #include "dce2_list.h" #ifdef DUMP_BUFFER #include "dcerpc2_buffer_dump.h" #endif #define UNKNOWN_FILE_SIZE ~0 static void *fileCache = NULL; static void DCE2_Smb2Inspect(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, const uint8_t *end); /******************************************************************** * Function: DCE2_Smb2GetFileName() * * Purpose: * Parses data passed in and returns a BOM prepended byte stream. * * Arguments: * const uint8_t * - pointer to data * uint32_t - data length * uint16_t * - Returns the length of the output buffer including the NULL terminated bytes * * Returns: * uint8_t * - NULL terminated byte stream (UTF-16LE with BOM) * ********************************************************************/ static uint8_t * DCE2_Smb2GetFileName(const uint8_t *data, uint32_t data_len, uint16_t *file_name_len) { uint8_t *fname = NULL; int i; if (data_len < 2) return NULL; if(data_len > 2*DCE2_SMB_MAX_PATH_LEN) return NULL; for (i = 0; i < data_len; i += 2) { uint16_t uchar = SmbNtohs((uint16_t *)(data + i)); if (uchar == 0) break; } fname = (uint8_t *)DCE2_Alloc(i + UTF_16_LE_BOM_LEN + 2, DCE2_MEM_TYPE__SMB_SSN); if (fname == NULL) return NULL; memcpy(fname, UTF_16_LE_BOM, UTF_16_LE_BOM_LEN);//Prepend with BOM memcpy(fname + UTF_16_LE_BOM_LEN, data, i); *file_name_len = i + UTF_16_LE_BOM_LEN + 2; return fname; } static inline uint32_t Smb2Tid(const Smb2Hdr *hdr) { return SmbNtohl(&(((Smb2SyncHdr *)hdr)->tree_id)); } static int DCE2_Smb2TidCompare(const void *a, const void *b) { uint32_t x = (uint32_t)(uintptr_t)a; uint32_t y = (uint32_t)(uintptr_t)b; if (x == y) return 0; /* Only care about equality for finding */ return -1; } static inline void DCE2_Smb2InsertTid(DCE2_SmbSsnData *ssd, const uint32_t tid, const uint8_t share_type) { bool is_ipc = (share_type != SMB2_SHARE_TYPE_DISK); if (!is_ipc && (!DCE2_ScSmbFileInspection(ssd->sd.sconfig) || ((ssd->max_file_depth == -1) && DCE2_ScSmbFileDepth(ssd->sd.sconfig) == -1))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not inserting TID (%u) " "because it's not IPC and not inspecting normal file " "data.", tid)); return; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Inserting Tid: %u\n", tid)); if (ssd->tids == NULL) { ssd->tids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_Smb2TidCompare, NULL, NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__SMB_TID); if (ssd->tids == NULL) { return; } } DCE2_ListInsert(ssd->tids, (void *)(uintptr_t)tid, (void *)(uintptr_t)share_type); } static inline uint8_t DCE2_Smb2ShareType(DCE2_SmbSsnData *ssd, const uint32_t tid) { uint8_t share_type = (uint8_t)(uintptr_t)DCE2_ListFind(ssd->tids, (void *)(uintptr_t)tid); return share_type; } static DCE2_Ret DCE2_Smb2FindTid(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr) { /* Still process async commands*/ if (SmbNtohl(&(smb_hdr->flags)) & SMB2_FLAGS_ASYNC_COMMAND) return DCE2_RET__SUCCESS; return DCE2_ListFindKey(ssd->tids, (void *)(uintptr_t)Smb2Tid(smb_hdr)); } static inline void DCE2_Smb2RemoveTid(DCE2_SmbSsnData *ssd, const uint32_t tid) { DCE2_ListRemove(ssd->tids, (void *)(uintptr_t)tid); } static inline Smb2Request *DCE2_Smb2StoreRequest(DCE2_SmbSsnData *ssd, uint64_t message_id, uint16_t command) { Smb2Request *request = ssd->smb2_requests; ssd->max_outstanding_requests = 128; /* windows client max */ while (request) { if (request->message_id == message_id) return request; request = request->next; } request = (Smb2Request *) DCE2_Alloc(sizeof(*request), DCE2_MEM_TYPE__SMB_SSN); if (!request) return NULL; ssd->outstanding_requests++; if (ssd->outstanding_requests >= ssd->max_outstanding_requests) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_MAX_REQS_EXCEEDED, ssd->max_outstanding_requests); DCE2_Free(request, sizeof(*request), DCE2_MEM_TYPE__SMB_SSN ); ssd->outstanding_requests--; return NULL; } request->message_id = message_id; request->command = command; request->next = ssd->smb2_requests; request->previous = NULL; if (ssd->smb2_requests) ssd->smb2_requests->previous = request; ssd->smb2_requests = request; return request; } static inline void DCE2_Smb2StoreReadRequest(DCE2_SmbSsnData *ssd, uint64_t message_id, uint16_t command, uint64_t offset, uint64_t file_id) { Smb2Request *request = NULL; if((request = DCE2_Smb2StoreRequest(ssd, message_id, command)) != NULL) { request->read_req.offset = offset; request->read_req.file_id = file_id; } } static inline void DCE2_Smb2StoreCreateRequest(DCE2_SmbSsnData *ssd, uint64_t message_id, uint16_t command, char *file_name, uint16_t file_name_len, bool durable_reconnect) { Smb2Request *request = NULL; if((request = DCE2_Smb2StoreRequest(ssd, message_id, command)) != NULL) { request->create_req.file_name = file_name; request->create_req.file_name_len = file_name_len; request->create_req.durable_reconnect = durable_reconnect; } } static inline Smb2Request* DCE2_Smb2GetRequest(DCE2_SmbSsnData *ssd, uint64_t message_id) { Smb2Request *request = ssd->smb2_requests; while (request) { if (request->message_id == message_id) return request; request = request->next; } return NULL; } static inline void DCE2_Smb2RemoveRequest(DCE2_SmbSsnData *ssd, Smb2Request* request) { if (request->previous) { request->previous->next = request->next; } if (request->next) { request->next->previous = request->previous; } if (request == ssd->smb2_requests) { ssd->smb2_requests = request->next; } ssd->outstanding_requests--; DCE2_Free(request, sizeof (*request), DCE2_MEM_TYPE__SMB_SSN); return; } static inline void DCE2_Smb2FreeFileName(DCE2_SmbFileTracker *ftracker) { if (ftracker->file_name) { DCE2_Free(ftracker->file_name, ftracker->file_name_len, DCE2_MEM_TYPE__SMB_SSN); ftracker->file_name = NULL; } ftracker->file_name_len = 0; } static inline void DCE2_Smb2ResetFileName(DCE2_SmbFileTracker *ftracker) { DCE2_UnRegMem(ftracker->file_name_len, DCE2_MEM_TYPE__SMB_SSN); ftracker->file_name = NULL; ftracker->file_name_len = 0; } static inline void DCE2_Smb2ProcessFileData(DCE2_SmbSsnData *ssd, const uint8_t *file_data, uint32_t data_size, bool upload) { int64_t file_detection_depth = DCE2_ScSmbFileDepth(ssd->sd.sconfig); int64_t detection_size = 0; if (file_detection_depth == 0) detection_size = data_size; else if ( ssd->ftracker.tracker.file.file_offset < (uint64_t)file_detection_depth) { if ( file_detection_depth - ssd->ftracker.tracker.file.file_offset < data_size ) detection_size = file_detection_depth - ssd->ftracker.tracker.file.file_offset; else detection_size = data_size; } if (detection_size) { _dpd.setFileDataPtr((uint8_t *)file_data, (detection_size > UINT16_MAX) ? UINT16_MAX : (uint16_t)detection_size); DCE2_FileDetect(&ssd->sd); } /*Do not inspect if offset exceeds max_file_depth. If max_file_depth is negative then do not call file_segment_process*/ if ((ssd->max_file_depth >= 0) && ((ssd->max_file_depth == 0) || (ssd->ftracker.tracker.file.file_offset < ssd->max_file_depth))) { _dpd.fileAPI->file_segment_process(fileCache, (void *)ssd->sd.wire_pkt, ssd->ftracker.fid_v2, ssd->ftracker.tracker.file.file_size, file_data, data_size, ssd->ftracker.tracker.file.file_offset, upload); } } /******************************************************************** * * Process tree connect command * Share type is defined here * ********************************************************************/ static void DCE2_Smb2TreeConnect(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, uint8_t *smb_data, const uint8_t *end) { /* Using structure size to decide whether it is response or request*/ uint16_t structure_size; Smb2TreeConnectResponseHdr *smb_tree_connect_hdr = (Smb2TreeConnectResponseHdr *)smb_data; if ((const uint8_t *)smb_tree_connect_hdr + SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE > end) return; structure_size = SmbNtohs(&(smb_tree_connect_hdr->structure_size)); if (structure_size == SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE) { DCE2_Smb2InsertTid(ssd, Smb2Tid(smb_hdr), smb_tree_connect_hdr->share_type); } return; } /******************************************************************** * * Process tree connect command * Share type is defined here * ********************************************************************/ static void DCE2_Smb2TreeDisconnect(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, uint8_t *smb_data, const uint8_t *end) { /* Using structure size to decide whether it is response or request*/ uint16_t structure_size; Smb2TreeDisConnectHdr *smb_tree_disconnect_hdr = (Smb2TreeDisConnectHdr *)smb_data; if ((const uint8_t *)smb_tree_disconnect_hdr + SMB2_TREE_DISCONNECT_STRUC_SIZE > end) return; structure_size = SmbNtohs(&(smb_tree_disconnect_hdr->structure_size)); if (structure_size == SMB2_TREE_DISCONNECT_STRUC_SIZE) { DCE2_Smb2RemoveTid(ssd, Smb2Tid(smb_hdr)); } return; } bool IsSmb2DurableReconnect(Smb2ACreateRequestHdr *smb_create_hdr, const uint8_t *end) { const uint8_t *data = (uint8_t *) smb_create_hdr + SmbNtohl(&smb_create_hdr->create_contexts_offset) - SMB2_HEADER_LENGTH; uint32_t remaining = SmbNtohl(&smb_create_hdr->create_contexts_length); while (remaining > sizeof(Smb2CreateContextHdr) && data < end) { Smb2CreateContextHdr *context = (Smb2CreateContextHdr *)data; uint32_t next = SmbNtohl(&context->next); uint16_t name_offset = SmbNtohs(&context->name_offset) ; uint16_t name_length = SmbNtohs(&context->name_length) ; uint16_t data_offset = SmbNtohs(&context->data_offset); uint32_t data_length = SmbNtohl(&context->data_length); /* Check for general error condition */ if ((next & 0x7) != 0 || next > remaining || name_offset != 16 || name_length < 4 || name_offset + name_length > remaining || (data_offset & 0x7) != 0 || (data_offset && (data_offset < name_offset + name_length)) || (data_offset > remaining) || (data_offset + data_length > remaining)) { return false; } if((strncmp((char *)context+name_offset, SMB2_CREATE_DURABLE_RECONNECT_V2, name_length) == 0) || (strncmp((char *)context+name_offset, SMB2_CREATE_DURABLE_RECONNECT, name_length) == 0)) { return true; } if(!next) break; data += next; remaining -= next; } return false; } /******************************************************************** * * Process create request, first command for a file processing * Update file name * ********************************************************************/ static void DCE2_Smb2CreateRequest(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, Smb2ACreateRequestHdr *smb_create_hdr,const uint8_t *end) { uint16_t name_offset = SmbNtohs(&(smb_create_hdr->name_offset)); uint8_t *fname = NULL; uint16_t fname_len = 0; uint64_t message_id; bool durable_reconnect = false; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing create request command!\n")); DCE2_Smb2InitFileTracker(&ssd->ftracker, false, 0); message_id = SmbNtohq((const uint64_t *)(&(smb_hdr->message_id))); if (name_offset > SMB2_HEADER_LENGTH) { uint16_t size; uint8_t *file_data = (uint8_t *) smb_create_hdr + smb_create_hdr->name_offset - SMB2_HEADER_LENGTH ; if (file_data >= end) return; size = SmbNtohs(&(smb_create_hdr->name_length)); if (!size || (file_data + size > end)) return; fname = DCE2_Smb2GetFileName(file_data, size, &fname_len); durable_reconnect = IsSmb2DurableReconnect(smb_create_hdr, end); } DCE2_Smb2StoreCreateRequest(ssd, message_id, SMB2_COM_CREATE, (char *)fname, fname_len, durable_reconnect); } /******************************************************************** * * Process create response, need to update file id * For downloading, file size is decided here * ********************************************************************/ static void DCE2_Smb2CreateResponse(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, Smb2ACreateResponseHdr *smb_create_hdr, const uint8_t *end) { uint64_t fileId_persistent; uint64_t file_size = UNKNOWN_FILE_SIZE; uint64_t message_id; Smb2Request *request = NULL; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing create response command!\n")); message_id = SmbNtohq((const uint64_t *)(&(smb_hdr->message_id))); request = DCE2_Smb2GetRequest(ssd, message_id); if (!request || request->command != SMB2_COM_CREATE) { return; } fileId_persistent = SmbNtohq((const uint64_t *)(&(smb_create_hdr->fileId_persistent))); ssd->ftracker.fid_v2 = fileId_persistent; if (smb_create_hdr->end_of_file) { file_size = SmbNtohq((const uint64_t *)(&(smb_create_hdr->end_of_file))); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Get file size %u!\n", file_size )); ssd->ftracker.tracker.file.file_size = file_size; } if (request->create_req.file_name && request->create_req.file_name_len) { _dpd.fileAPI->file_cache_update_entry(fileCache, (void *)ssd->sd.wire_pkt, ssd->ftracker.fid_v2, (uint8_t *) request->create_req.file_name, request->create_req.file_name_len, file_size, !request->create_req.durable_reconnect, false); DCE2_UnRegMem(request->create_req.file_name_len, DCE2_MEM_TYPE__SMB_SSN); } DCE2_Smb2RemoveRequest(ssd, request); } /******************************************************************** * * Process create command * ********************************************************************/ static void DCE2_Smb2Create(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, uint8_t *smb_data, const uint8_t *end) { uint16_t structure_size; Smb2ACreateRequestHdr *smb_create_hdr = (Smb2ACreateRequestHdr *)smb_data; structure_size = SmbNtohs(&(smb_create_hdr->structure_size)); /* Using structure size to decide whether it is response or request */ if (structure_size == SMB2_CREATE_REQUEST_STRUC_SIZE) { if ((const uint8_t *)smb_create_hdr + SMB2_CREATE_REQUEST_STRUC_SIZE - 1 > end) return; DCE2_Smb2CreateRequest(ssd, smb_hdr, (Smb2ACreateRequestHdr *)smb_create_hdr, end); } else if (structure_size == SMB2_CREATE_RESPONSE_STRUC_SIZE) { if ((const uint8_t *)smb_create_hdr + SMB2_CREATE_RESPONSE_STRUC_SIZE -1 > end) return; DCE2_Smb2CreateResponse(ssd, smb_hdr, (Smb2ACreateResponseHdr *)smb_create_hdr, end); } else if (structure_size == SMB2_ERROR_RESPONSE_STRUC_SIZE) { uint64_t message_id; Smb2Request *request; if ((const uint8_t *)smb_create_hdr + SMB2_ERROR_RESPONSE_STRUC_SIZE - 1 > end) return; if((SmbNtohl(&(smb_hdr->flags)) & SMB2_FLAGS_ASYNC_COMMAND) && (SmbNtohl(&(smb_hdr->status)) == SMB2_STATUS_PENDING )) return; /*Response error, clean up request state*/ message_id = SmbNtohq((const uint64_t *)(&(smb_hdr->message_id))); request = DCE2_Smb2GetRequest(ssd, message_id); if (request && request->create_req.file_name) { DCE2_Free(request->create_req.file_name, request->create_req.file_name_len, DCE2_MEM_TYPE__SMB_SSN); } if(request) DCE2_Smb2RemoveRequest(ssd, request); } else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Wrong format for smb create command!\n")); return; } /******************************************************************** * * Process close command * For some upload, file_size is decided here. * ********************************************************************/ static void DCE2_Smb2CloseCmd(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, uint8_t *smb_data, const uint8_t *end) { /* Using structure size to decide whether it is response or request*/ uint16_t structure_size; Smb2CloseRequestHdr *smb_close_hdr = (Smb2CloseRequestHdr *)smb_data; uint64_t fileId_persistent; if ((const uint8_t *)smb_close_hdr + SMB2_CLOSE_REQUEST_STRUC_SIZE > end) return; fileId_persistent = SmbNtohq((const uint64_t *)(&(smb_close_hdr->fileId_persistent))); structure_size = SmbNtohs(&(smb_close_hdr->structure_size)); if ((structure_size == SMB2_CLOSE_REQUEST_STRUC_SIZE) && !ssd->ftracker.tracker.file.file_size && ssd->ftracker.tracker.file.file_offset) { bool upload = DCE2_SsnFromClient(ssd->sd.wire_pkt) ? true : false; ssd->ftracker.tracker.file.file_size = ssd->ftracker.tracker.file.file_offset; _dpd.fileAPI->file_cache_update_entry(fileCache, (void *)ssd->sd.wire_pkt, fileId_persistent, NULL, 0, ssd->ftracker.tracker.file.file_size, false, false); DCE2_Smb2ProcessFileData(ssd, NULL, 0, upload); } } /******************************************************************** * * Process set info command * For upload, file_size is decided here. * ********************************************************************/ static void DCE2_Smb2SetInfo(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, uint8_t *smb_data, const uint8_t *end) { /* Using structure size to decide whether it is response or request*/ uint16_t structure_size; Smb2ASetInfoRequestHdr *smb_set_info_hdr = (Smb2ASetInfoRequestHdr *)smb_data; uint64_t fileId_persistent; if ((const uint8_t *)smb_set_info_hdr + SMB2_SET_INFO_REQUEST_STRUC_SIZE > end) return; fileId_persistent = SmbNtohq((const uint64_t *)(&(smb_set_info_hdr->fileId_persistent))); structure_size = SmbNtohs(&(smb_set_info_hdr->structure_size)); if (structure_size == SMB2_SET_INFO_REQUEST_STRUC_SIZE) { uint8_t *file_data = (uint8_t *) smb_set_info_hdr + SMB2_SET_INFO_REQUEST_STRUC_SIZE - 1; if (smb_set_info_hdr->file_info_class == SMB2_FILE_ENDOFFILE_INFO) { uint64_t file_size; file_size = SmbNtohq((const uint64_t *)file_data); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Get file size %u!\n", file_size )); ssd->ftracker.tracker.file.file_size = file_size; _dpd.fileAPI->file_cache_update_entry(fileCache, (void *)ssd->sd.wire_pkt, fileId_persistent, NULL, 0, file_size, false, false); } } return; } /******************************************************************** * * Process read request * ********************************************************************/ static void DCE2_Smb2ReadRequest(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, Smb2ReadRequestHdr *smb_read_hdr, const uint8_t *end) { uint64_t message_id, offset; uint64_t fileId_persistent; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing read request command!\n")); message_id = SmbNtohq((const uint64_t *)(&(smb_hdr->message_id))); offset = SmbNtohq((const uint64_t *)(&(smb_read_hdr->offset))); fileId_persistent = SmbNtohq((const uint64_t *)(&(smb_read_hdr->fileId_persistent))); DCE2_Smb2StoreReadRequest(ssd, message_id, SMB2_COM_READ, offset, fileId_persistent); if (fileId_persistent && (ssd->ftracker.fid_v2 != fileId_persistent)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Persistent file ID changed read request command!\n")); ssd->ftracker.fid_v2 = fileId_persistent; } if (ssd->ftracker.tracker.file.file_size && (offset > ssd->ftracker.tracker.file.file_size)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_FILE_OFFSET); } } /******************************************************************** * * Process read response * ********************************************************************/ static void DCE2_Smb2ReadResponse(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, Smb2ReadResponseHdr *smb_read_hdr, const uint8_t *end) { uint8_t *file_data = (uint8_t *) smb_read_hdr + SMB2_READ_RESPONSE_STRUC_SIZE - 1; int data_size = end - file_data; uint32_t total_data_length; uint64_t message_id; uint16_t data_offset; Smb2Request *request; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing read response command!\n")); message_id = SmbNtohq((const uint64_t *)(&(smb_hdr->message_id))); request = DCE2_Smb2GetRequest(ssd, message_id); if (!request || request->command != SMB2_COM_READ) { return; } data_offset = SmbNtohs((const uint16_t *)(&(smb_read_hdr->data_offset))); if (data_offset + (const uint8_t *)smb_hdr > end) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, data_offset + smb_hdr, smb_hdr, end); } ssd->ftracker.tracker.file.file_offset = request->read_req.offset; ssd->ftracker.fid_v2 = request->read_req.file_id; ssd->ftracker.tracker.file.file_direction = DCE2_SMB_FILE_DIRECTION__DOWNLOAD; DCE2_Smb2RemoveRequest(ssd, request); DCE2_Smb2ProcessFileData(ssd, file_data, data_size, false); ssd->ftracker.tracker.file.file_offset += data_size; total_data_length = SmbNtohl((const uint32_t *)&(smb_read_hdr->length)); if (total_data_length > (uint32_t)data_size) ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; } /******************************************************************** * * Process read command * ********************************************************************/ static void DCE2_Smb2Read(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, uint8_t *smb_data, const uint8_t *end) { uint16_t structure_size; Smb2ReadRequestHdr *smb_read_hdr = (Smb2ReadRequestHdr *)smb_data; structure_size = SmbNtohs(&(smb_read_hdr->structure_size)); /* Using structure size to decide whether it is response or request*/ if (structure_size == SMB2_READ_REQUEST_STRUC_SIZE) { if ((const uint8_t *)smb_read_hdr + SMB2_READ_REQUEST_STRUC_SIZE - 1 > end) return; DCE2_Smb2ReadRequest(ssd, smb_hdr, (Smb2ReadRequestHdr *)smb_read_hdr, end); } else if (structure_size == SMB2_READ_RESPONSE_STRUC_SIZE) { if ((const uint8_t *)smb_read_hdr + SMB2_READ_RESPONSE_STRUC_SIZE - 1 > end) return; DCE2_Smb2ReadResponse(ssd, smb_hdr, (Smb2ReadResponseHdr *)smb_read_hdr, end); } else { uint64_t message_id; Smb2Request *request; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Wrong format for smb read command!\n")); message_id = SmbNtohq((const uint64_t *)(&(smb_hdr->message_id))); request = DCE2_Smb2GetRequest(ssd, message_id); if (!request) { return; } DCE2_Smb2RemoveRequest(ssd, request); } return; } /******************************************************************** * * Process write request * ********************************************************************/ static void DCE2_Smb2WriteRequest(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, Smb2WriteRequestHdr *smb_write_hdr, const uint8_t *end) { uint8_t *file_data = (uint8_t *) smb_write_hdr + SMB2_WRITE_REQUEST_STRUC_SIZE - 1; int data_size = end - file_data; uint64_t fileId_persistent, offset; uint16_t data_offset; uint32_t total_data_length; uint64_t file_size = UNKNOWN_FILE_SIZE; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing write request command!\n")); fileId_persistent = SmbNtohq((const uint64_t *)(&(smb_write_hdr->fileId_persistent))); if (fileId_persistent && (ssd->ftracker.fid_v2 != fileId_persistent)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Persistent file ID changed read request command!\n")); ssd->ftracker.fid_v2 = fileId_persistent; } data_offset = SmbNtohs((const uint16_t *)(&(smb_write_hdr->data_offset))); if (data_offset + (const uint8_t *)smb_hdr > end) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, data_offset + smb_hdr, smb_hdr, end); } offset = SmbNtohq((const uint64_t *)(&(smb_write_hdr->offset))); if (ssd->ftracker.tracker.file.file_size && (offset > ssd->ftracker.tracker.file.file_size)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_FILE_OFFSET); } ssd->ftracker.tracker.file.file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; ssd->ftracker.tracker.file.file_offset = offset; if(offset == 0) { /* Try to update the file size with a max file size if file size is not already updated.This is done to handle some SMB clients which will not send SetInfo before uploading the file. This updates if size is not updated already in file_cache */ _dpd.fileAPI->file_cache_update_entry(fileCache, (void *)ssd->sd.wire_pkt, fileId_persistent, NULL, 0, file_size, false, true); } DCE2_Smb2ProcessFileData(ssd, file_data, data_size, true); ssd->ftracker.tracker.file.file_offset += data_size; total_data_length = SmbNtohl((const uint32_t *)&(smb_write_hdr->length)); if (total_data_length > (uint32_t)data_size) ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; } /******************************************************************** * * Process write response * ********************************************************************/ static void DCE2_Smb2WriteResponse(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, Smb2WriteResponseHdr *smb_write_hdr, const uint8_t *end) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing write response command!\n")); } /******************************************************************** * * Process write command * ********************************************************************/ static void DCE2_Smb2Write(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, uint8_t *smb_data, const uint8_t *end) { uint16_t structure_size; Smb2WriteRequestHdr *smb_write_hdr = (Smb2WriteRequestHdr *)smb_data; structure_size = SmbNtohs(&(smb_write_hdr->structure_size)); /* Using structure size to decide whether it is response or request*/ if (structure_size == SMB2_WRITE_REQUEST_STRUC_SIZE) { if ((const uint8_t *)smb_write_hdr + SMB2_WRITE_REQUEST_STRUC_SIZE - 1 > end) return; DCE2_Smb2WriteRequest(ssd, smb_hdr, (Smb2WriteRequestHdr *)smb_write_hdr, end); } else if (structure_size == SMB2_WRITE_RESPONSE_STRUC_SIZE) { if ((const uint8_t *)smb_write_hdr + SMB2_WRITE_RESPONSE_STRUC_SIZE - 1 > end) return; DCE2_Smb2WriteResponse(ssd, smb_hdr, (Smb2WriteResponseHdr *)smb_write_hdr, end); } else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Wrong format for smb write command!\n")); return; } /******************************************************************** * * Purpose: * Process SMB2 commands. * * Arguments: * DCE2_SmbSsnData * - the session data structure. * const Smb2Hdr * - pointer to the SMB2 header. * const uint8_t * - pointer to end of payload. * Returns: None * ********************************************************************/ static void DCE2_Smb2Inspect(DCE2_SmbSsnData *ssd, const Smb2Hdr *smb_hdr, const uint8_t *end) { uint8_t *smb_data = (uint8_t *)smb_hdr + SMB2_HEADER_LENGTH; uint16_t command = SmbNtohs(&(smb_hdr->command)); DCE2_Ret smb2_create_ret = DCE2_RET__SUCCESS; switch (command) { case SMB2_COM_CREATE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Create command.\n")); dce2_stats.smb2_create++; /*If Tid is found, call DCE2_Smb2Create only if its a DISK_SHARE or its a ASYNC COMMAND*/ smb2_create_ret = DCE2_Smb2FindTid(ssd, smb_hdr); if ((smb2_create_ret == DCE2_RET__SUCCESS) && ((SmbNtohl(&(smb_hdr->flags)) & SMB2_FLAGS_ASYNC_COMMAND) || (DCE2_Smb2ShareType(ssd, Smb2Tid(smb_hdr)) == SMB2_SHARE_TYPE_DISK))) { DCE2_Smb2Create(ssd, smb_hdr, smb_data, end); } /*If Tid is not found, add the Tid as DISK_SHARE and call DCE2_Smb2Create*/ else if (smb2_create_ret != DCE2_RET__SUCCESS) { DCE2_Smb2InsertTid(ssd, Smb2Tid(smb_hdr), SMB2_SHARE_TYPE_DISK); DCE2_Smb2Create(ssd, smb_hdr, smb_data, end); } break; case SMB2_COM_READ: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Read command.\n")); dce2_stats.smb2_read++; if (DCE2_Smb2FindTid(ssd, smb_hdr) != DCE2_RET__SUCCESS) return; DCE2_Smb2Read(ssd, smb_hdr, smb_data, end); break; case SMB2_COM_WRITE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Write command.\n")); dce2_stats.smb2_write++; if (DCE2_Smb2FindTid(ssd, smb_hdr) != DCE2_RET__SUCCESS) return; DCE2_Smb2Write(ssd, smb_hdr, smb_data, end); break; case SMB2_COM_SET_INFO: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Set info command.\n")); dce2_stats.smb2_set_info++; if (DCE2_Smb2FindTid(ssd, smb_hdr) != DCE2_RET__SUCCESS) return; DCE2_Smb2SetInfo(ssd, smb_hdr, smb_data, end); break; case SMB2_COM_CLOSE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Close command.\n")); dce2_stats.smb2_close++; if (DCE2_Smb2FindTid(ssd, smb_hdr) != DCE2_RET__SUCCESS) return; DCE2_Smb2CloseCmd(ssd, smb_hdr, smb_data, end); break; case SMB2_COM_TREE_CONNECT: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Tree connect command.\n")); dce2_stats.smb2_tree_connect++; DCE2_Smb2TreeConnect(ssd, smb_hdr, smb_data, end); break; case SMB2_COM_TREE_DISCONNECT: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Tree disconnect command.\n")); dce2_stats.smb2_tree_disconnect++; DCE2_Smb2TreeDisconnect(ssd, smb_hdr, smb_data, end); break; default: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "command ignored!\n")); break; } return; } /******************************************************************** * Function: DCE2_SmbProcess() * * Purpose: * This is the main entry point for SMB processing. * * Arguments: * DCE2_SmbSsnData * - the session data structure. * * Returns: None * ********************************************************************/ void DCE2_Smb2Process(DCE2_SmbSsnData *ssd) { const SFSnortPacket *p = ssd->sd.wire_pkt; const uint8_t *data_ptr = p->payload; uint16_t data_len = p->payload_size; Smb2Hdr *smb_hdr; const uint8_t *end = data_ptr + data_len; uint32_t next_command_offset = 0; #ifdef DUMP_BUFFER dumpBuffer(DCERPC_SMB2_DUMP,data_ptr,data_len); #endif if (ssd && ssd->pdu_state != DCE2_SMB_PDU_STATE__RAW_DATA) { /*Check header length*/ if (data_len < sizeof(NbssHdr) + SMB2_HEADER_LENGTH) return; if (!ssd->ftracker.is_smb2) { DCE2_Smb2InitFileTracker(&(ssd->ftracker), 0, 0); } } /* Process the header */ if (PacketHasStartOfPDU(p)) { smb_hdr = (Smb2Hdr *)(data_ptr + sizeof(NbssHdr)); /* * SMB protocol allows multiple smb commands * to be clubbed in a single packet. * So loop through to parse all the smb * commands. */ do { DCE2_Smb2Inspect(ssd, (Smb2Hdr *)smb_hdr, end); /* * In case of message compounding, find the offset * of the next smb command. */ next_command_offset = SmbNtohl(&(smb_hdr->next_command)); if (next_command_offset + (uint8_t *)smb_hdr > end) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_NEXT_COMMAND_OFFSET); return; } if (next_command_offset) { smb_hdr = (Smb2Hdr *)((uint8_t *)smb_hdr + next_command_offset); } } while (next_command_offset && smb_hdr); } else if (ssd->pdu_state == DCE2_SMB_PDU_STATE__RAW_DATA) { /*continue processing raw data*/ bool upload = DCE2_SsnFromClient(ssd->sd.wire_pkt) ? true : false; DCE2_Smb2ProcessFileData(ssd, data_ptr, data_len, upload); ssd->ftracker.tracker.file.file_offset += data_len; } } /* Initialize smb2 file tracker */ DCE2_Ret DCE2_Smb2InitFileTracker( DCE2_SmbFileTracker *ftracker, const bool is_ipc, const uint64_t fid) { if (ftracker == NULL) return DCE2_RET__ERROR; DCE2_Smb2FreeFileName(ftracker); ftracker->fid_v2 = fid; ftracker->is_ipc = is_ipc; ftracker->is_smb2 = true; ftracker->ff_file_size = 0; ftracker->ff_file_offset = 0; ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN; return DCE2_RET__SUCCESS; } void DCE2_Smb2UpdateStats(void) { if (fileCache) { FileCacheStatus *status = _dpd.fileAPI->file_cache_status(fileCache); dce2_stats.smb2_prunes = status->prunes; dce2_stats.smb2_memory_in_use = status->segment_mem_in_use; dce2_stats.smb2_memory_in_use_max = status->segment_mem_in_use_max; } } /* Check whether the packet is smb2 */ DCE2_SmbVersion DCE2_Smb2Version(const SFSnortPacket *p) { /* SMB2 requires PAF enabled*/ if (!_dpd.isPafEnabled() || !DCE2_SsnIsPafActive(p)) { return DCE2_SMB_VERISON_NULL; } /* Only check reassembled SMB2 packet*/ if (IsTCP(p) && DCE2_SsnIsRebuilt(p) && (p->payload_size > sizeof(NbssHdr) + sizeof(DCE2_SMB_ID))) { Smb2Hdr *smb_hdr = (Smb2Hdr *)(p->payload + sizeof(NbssHdr)); uint32_t smb_version_id = SmbId((SmbNtHdr *)smb_hdr); if (smb_version_id == DCE2_SMB_ID) return DCE2_SMB_VERISON_1; else if (smb_version_id == DCE2_SMB2_ID) return DCE2_SMB_VERISON_2; } return DCE2_SMB_VERISON_NULL; } void DCE2_Smb2CleanRequests(Smb2Request *requests) { Smb2Request *request = requests; while (request) { Smb2Request *next; next = request->next; DCE2_Free(request, sizeof (*request), DCE2_MEM_TYPE__SMB_SSN); request = next; } } void DCE2_Smb2Init(uint64_t memcap) { if (!fileCache) { uint64_t mem_for_smb2 = memcap >> 1; fileCache = _dpd.fileAPI->file_cache_create (mem_for_smb2, 5); DCE2_RegMem(mem_for_smb2, DCE2_MEM_TYPE__SMB_SSN); } } void DCE2_Smb2Close(void) { if (fileCache) { DCE2_Smb2UpdateStats(); _dpd.fileAPI->file_cache_free (fileCache); fileCache = NULL; } } bool DCE2_IsFileCache(void *ptr) { return fileCache == ptr; } #ifdef SNORT_RELOAD /********************************************************************* * Function: DCE2_Smb2AdjustFileCache * * Purpose: Adapts file cache to new config in bursts of effort * * Arguments: work - size of work burst * cache_enabled - cache is enabled in new config * * Returns: true when file cache meets memcaps, else false * *********************************************************************/ bool DCE2_Smb2AdjustFileCache(uint8_t work, bool cache_enabled) { //TODO (nice to have) Adjust number of rows in hash table based on new size /* Delete files until new max files cap is met * Delete files until new segment memcap is met * Delete file cache if no longer used * return true when done, else false */ bool file_cache_adapted = _dpd.fileAPI->file_cache_shrink_to_memcap(fileCache, &work); if (file_cache_adapted && !cache_enabled) DCE2_Smb2Close(); return file_cache_adapted; } void DCE2_SetSmbMemcap(uint64_t smbMemcap) { _dpd.fileAPI->file_cache_set_memcap(fileCache, smbMemcap); } #endif snort-2.9.20/src/dynamic-preprocessors/dcerpc2/Makefile.am0000444000175000017500000000331514230012554021565 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_dce2_preproc.la libsf_dce2_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_dce2_preproc_la_LIBADD = ../libsf_dynamic_preproc.la if BUILD_SNORT_RELOAD libsf_dce2_preproc_la_LIBADD += ../libsf_dynamic_utils.la endif else nodist_libsf_dce2_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sf_ip.c \ ../include/sfrt.c \ ../include/sfrt_dir.c \ ../include/sfPolicyUserData.c if BUILD_SNORT_RELOAD nodist_libsf_dce2_preproc_la_SOURCES += ../include/appdata_adjuster.c ../include/sfxhash.c ../include/sfhashfcn.c ../include/sfmemcap.c ../include/sfprimetable.c ../include/reg_test.h ../include/reg_test.c endif endif libsf_dce2_preproc_la_SOURCES = \ includes/dcerpc.h \ includes/smb.h \ dce2_debug.c \ dce2_debug.h \ dce2_utils.c \ dce2_utils.h \ dce2_list.c \ dce2_list.h \ dce2_memory.c \ dce2_memory.h \ dce2_stats.c \ dce2_stats.h \ dce2_event.c \ dce2_event.h \ dce2_config.c \ dce2_config.h \ dce2_roptions.c \ dce2_roptions.h \ dce2_session.h \ spp_dce2.c \ spp_dce2.h \ snort_dce2.c \ snort_dce2.h \ dce2_smb.c \ dce2_smb.h \ dce2_smb2.c \ dce2_smb2.h \ dce2_tcp.c \ dce2_tcp.h \ dce2_co.c \ dce2_co.h \ dce2_udp.c \ dce2_udp.h \ dce2_cl.c \ dce2_cl.h \ dce2_http.c \ dce2_http.h \ dce2_paf.c \ dce2_paf.h if BUILD_BUFFER_DUMP libsf_dce2_preproc_la_SOURCES += \ dcerpc2_buffer_dump.c \ dcerpc2_buffer_dump.h endif EXTRA_DIST = \ sf_dce2.vcxproj \ sf_dce2.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_cl.c0000644000175000017500000010156314241075625021210 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Module for handling connectionless DCE/RPC processing. Provides * functionality for tracking sub-sessions or activities within a * connectionless conversation and for tracking and reassembling fragments * within each activity. Also sets appropriate data for use with * preprocessor rule options. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_dce2.h" #include "dce2_cl.h" #include "snort_dce2.h" #include "dce2_list.h" #include "dce2_memory.h" #include "dce2_utils.h" #include "dce2_stats.h" #include "dce2_session.h" #include "dce2_event.h" #include "dcerpc.h" #include "sf_types.h" #include "snort_debug.h" #include "profiler.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" /******************************************************************** * Global variables ********************************************************************/ static uint8_t dce2_cl_rbuf[IP_MAXPKT]; /******************************************************************** * Macros ********************************************************************/ #define DCE2_CL__MAX_SEQ_NUM UINT32_MAX /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_ClFragNode { uint32_t frag_number; uint16_t frag_len; uint8_t *frag_data; } DCE2_ClFragNode; typedef struct _DCE2_ClFragTracker { Uuid iface; /* only set on first fragment received */ uint32_t iface_vers; /* only set on first fragment received */ int opnum; /* set to that of first fragment, i.e fragment number == 0. * initialize to a sentinel */ int data_byte_order; /* set to that of first fragment, i.e fragment number == 0. * initialize to sentinel */ DCE2_List *frags; /* sorted by fragment number */ int num_expected_frags; /* set when we get last frag */ } DCE2_ClFragTracker; typedef struct _DCE2_ClActTracker { Uuid act; uint32_t seq_num; uint8_t seq_num_invalid; DCE2_ClFragTracker frag_tracker; #if 0 /* Not currently used. These are related to getting a sequence number that * is at the end of the sequence number space */ uint32_t last_pkt_sec; uint8_t no_frags; uint8_t no_requests; #endif } DCE2_ClActTracker; /******************************************************************** * Private function prototypes ********************************************************************/ static DCE2_Ret DCE2_ClHdrChecks(DCE2_SsnData *, const DceRpcClHdr *); static DCE2_ClActTracker * DCE2_ClGetActTracker(DCE2_ClTracker *, DceRpcClHdr *); static DCE2_ClActTracker * DCE2_ClInsertActTracker(DCE2_ClTracker *, DceRpcClHdr *); static void DCE2_ClRequest(DCE2_SsnData *, DCE2_ClActTracker *, DceRpcClHdr *, const uint8_t *, uint16_t); static void DCE2_ClHandleFrag(DCE2_SsnData *, DCE2_ClActTracker *, DceRpcClHdr *, const uint8_t *, uint16_t); static void DCE2_ClFragReassemble(DCE2_SsnData*, DCE2_ClActTracker *, const DceRpcClHdr *); static void DCE2_ClResetFragTracker(DCE2_ClFragTracker *); static inline void DCE2_ClSetRdata(DCE2_ClActTracker *, const DceRpcClHdr *, uint8_t *, uint16_t); /* Callbacks */ static int DCE2_ClFragCompare(const void *, const void *); static void DCE2_ClActDataFree(void *); static void DCE2_ClActKeyFree(void *); static void DCE2_ClFragDataFree(void *); /******************************************************************** * Function: DCE2_ClInitRdata() * * Initializes static values in the global CL data reassembly * buffer. These values should never need to be changed after * this initialization. * * Arguments: * uint8_t * * Pointer to the data reassembly buffer. * * Returns: None * ********************************************************************/ void DCE2_ClInitRdata(uint8_t *buf) { DceRpcClHdr *cl_hdr = (DceRpcClHdr *)buf; /* Set some relevant fields. These should never get reset */ cl_hdr->rpc_vers = DCERPC_PROTO_MAJOR_VERS__4; cl_hdr->ptype = DCERPC_PDU_TYPE__REQUEST; cl_hdr->drep[0] = 0x10; /* Little endian */ } /******************************************************************** * Function: DCE2_ClSetRdata() * * Sets relevant data fields in the reassembly packet. * * Arguments: * DCE2_ClActTracker * * Pointer to the activity tracker associated with the * reassemble packet. * DceRpcClHdr * * Pointer to the connectionless header in the wire packet. * uint8_t * * Pointer to the start of the reassembly buffer. * uint16_t * The length of the stub data. * * Returns: None * ********************************************************************/ static inline void DCE2_ClSetRdata(DCE2_ClActTracker *at, const DceRpcClHdr *pkt_cl_hdr, uint8_t *cl_ptr, uint16_t stub_len) { DCE2_ClFragTracker *ft = &at->frag_tracker; DceRpcClHdr *cl_hdr = (DceRpcClHdr *)cl_ptr; uint16_t opnum = (ft->opnum != DCE2_SENTINEL) ? (uint16_t)ft->opnum : DceRpcClOpnum(pkt_cl_hdr); cl_hdr->len = DceRpcHtons(&stub_len, DCERPC_BO_FLAG__LITTLE_ENDIAN); DCE2_CopyUuid(&cl_hdr->object, &pkt_cl_hdr->object, DceRpcClByteOrder(cl_hdr)); DCE2_CopyUuid(&cl_hdr->if_id, &ft->iface, DCERPC_BO_FLAG__LITTLE_ENDIAN); DCE2_CopyUuid(&cl_hdr->act_id, &at->act, DCERPC_BO_FLAG__LITTLE_ENDIAN); cl_hdr->if_vers = DceRpcHtonl(&ft->iface_vers, DCERPC_BO_FLAG__LITTLE_ENDIAN); cl_hdr->opnum = DceRpcHtons(&opnum, DCERPC_BO_FLAG__LITTLE_ENDIAN); } /******************************************************************** * Function: DCE2_ClProcess() * * Main entry point for connectionless DCE/RPC processing. Gets * the activity tracker associated with this session and passes * along to client or server handling. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_ClTracker * * Pointer to the connectionless tracker structure. * * Returns: None * ********************************************************************/ void DCE2_ClProcess(DCE2_SsnData *sd, DCE2_ClTracker *clt) { DceRpcClHdr *cl_hdr; DCE2_ClActTracker *at; const uint8_t *data_ptr = sd->wire_pkt->payload; uint16_t data_len = sd->wire_pkt->payload_size; PROFILE_VARS; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Cl processing ...\n")); dce2_stats.cl_pkts++; if (data_len < sizeof(DceRpcClHdr)) { if (!DCE2_SsnAutodetected(sd)) DCE2_Alert(sd, DCE2_EVENT__CL_DATA_LT_HDR, data_len, sizeof(DceRpcClHdr)); return; } cl_hdr = (DceRpcClHdr *)data_ptr; DCE2_MOVE(data_ptr, data_len, sizeof(DceRpcClHdr)); if (DCE2_ClHdrChecks(sd, cl_hdr) != DCE2_RET__SUCCESS) return; PREPROC_PROFILE_START(dce2_pstat_cl_acts); at = DCE2_ClGetActTracker(clt, cl_hdr); PREPROC_PROFILE_END(dce2_pstat_cl_acts); if (at == NULL) return; if (DCE2_SsnFromClient(sd->wire_pkt)) { switch (DceRpcClPduType(cl_hdr)) { case DCERPC_PDU_TYPE__REQUEST: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Request\n")); dce2_stats.cl_request++; DCE2_ClRequest(sd, at, cl_hdr, data_ptr, data_len); break; case DCERPC_PDU_TYPE__ACK: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Ack\n")); dce2_stats.cl_ack++; break; case DCERPC_PDU_TYPE__CL_CANCEL: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Cancel\n")); dce2_stats.cl_cancel++; break; case DCERPC_PDU_TYPE__FACK: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Fack\n")); dce2_stats.cl_cli_fack++; break; case DCERPC_PDU_TYPE__PING: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Ping\n")); dce2_stats.cl_ping++; break; case DCERPC_PDU_TYPE__RESPONSE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Response from client. Changing stream direction.")); _dpd.streamAPI->update_direction(sd->wire_pkt->stream_session, SSN_DIR_FROM_RESPONDER, GET_SRC_IP(((SFSnortPacket *)sd->wire_pkt)), sd->wire_pkt->src_port); break; default: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Other pdu type\n")); dce2_stats.cl_other_req++; break; } } else { switch (DceRpcClPduType(cl_hdr)) { case DCERPC_PDU_TYPE__RESPONSE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Response\n")); dce2_stats.cl_response++; break; case DCERPC_PDU_TYPE__REJECT: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Reject\n")); dce2_stats.cl_reject++; if (DceRpcClSeqNum(cl_hdr) == at->seq_num) { DCE2_ClResetFragTracker(&at->frag_tracker); at->seq_num_invalid = 1; } break; case DCERPC_PDU_TYPE__CANCEL_ACK: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Cancel Ack\n")); dce2_stats.cl_cancel_ack++; break; case DCERPC_PDU_TYPE__FACK: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Fack\n")); dce2_stats.cl_srv_fack++; break; case DCERPC_PDU_TYPE__FAULT: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Fault\n")); dce2_stats.cl_fault++; break; case DCERPC_PDU_TYPE__NOCALL: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "No call\n")); dce2_stats.cl_nocall++; break; case DCERPC_PDU_TYPE__WORKING: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Working\n")); dce2_stats.cl_working++; break; default: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Other pdu type\n")); dce2_stats.cl_other_resp++; break; } } } /******************************************************************** * Function: DCE2_ClHdrChecks() * * Checks to make sure header fields are sane. If they aren't, * alert on the header anomaly. If we've autodetected the session, * however, don't alert, but set a header anomaly flag, so we can * re-autodetect on the next go around. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DceRpcClHdr * * Pointer to the connectionless header in the packet. * * Returns: * DCE2_Ret * DCE2_RET__ERROR * We should not continue to inspect. * DCE2_RET__SUCCESS * Continue inspection. * ********************************************************************/ static DCE2_Ret DCE2_ClHdrChecks(DCE2_SsnData *sd, const DceRpcClHdr *cl_hdr) { if (DceRpcClRpcVers(cl_hdr) != DCERPC_PROTO_MAJOR_VERS__4) { /* If we autodetected the session, we probably guessed wrong */ if (!DCE2_SsnAutodetected(sd)) DCE2_Alert(sd, DCE2_EVENT__CL_BAD_MAJ_VERSION, DceRpcClRpcVers(cl_hdr)); return DCE2_RET__ERROR; } if (DceRpcClPduType(cl_hdr) >= DCERPC_PDU_TYPE__MAX) { if (!DCE2_SsnAutodetected(sd)) DCE2_Alert(sd, DCE2_EVENT__CL_BAD_PDU_TYPE, DceRpcClPduType(cl_hdr)); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_ClGetActTracker() * * Searches for activity tracker in list using activity UUID in * packet. If the activity tracker list is NULL, a new one is * created. If the activity tracker is not found, it is inserted * into the list. * * Arguments: * DCE2_ClTracker * * Pointer to the connectionless tracker. * DceRpcClHdr * * Pointer to the connectionless header in the packet. * * Returns: * DCE2_ClActTracker * * A valid pointer to an activity tracker on success. * NULL on error. * ********************************************************************/ static DCE2_ClActTracker * DCE2_ClGetActTracker(DCE2_ClTracker *clt, DceRpcClHdr *cl_hdr) { DCE2_ClActTracker *at = NULL; /* Try to find a currently active activity tracker */ if (clt->act_trackers != NULL) { Uuid uuid; DCE2_CopyUuid(&uuid, &cl_hdr->act_id, DceRpcClByteOrder(cl_hdr)); at = DCE2_ListFind(clt->act_trackers, (void *)&uuid); } else { /* Create a new activity tracker list */ clt->act_trackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_UuidCompare, DCE2_ClActDataFree, DCE2_ClActKeyFree, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__CL_ACT); if (clt->act_trackers == NULL) return NULL; } /* Didn't find a currently active activity tracker */ if (at == NULL) { /* Insert a new activity tracker */ at = DCE2_ClInsertActTracker(clt, cl_hdr); if (at == NULL) return NULL; } return at; } /******************************************************************** * Function: DCE2_ClInsertActTracker() * * Creates and inserts a new activity tracker into a list. * * Arguments: * DCE2_ClTracker * * Pointer to connectionless tracker. * DceRpcClHdr * * Pointer to the connectionless header in the packet. * * Returns: * DCE2_ClActTracker * * A valid pointer to an activity tracker on success. * NULL on error. * ********************************************************************/ static DCE2_ClActTracker * DCE2_ClInsertActTracker(DCE2_ClTracker *clt, DceRpcClHdr *cl_hdr) { Uuid *uuid = (Uuid *)DCE2_Alloc(sizeof(Uuid), DCE2_MEM_TYPE__CL_ACT); DCE2_ClActTracker *at; DCE2_Ret status; if (uuid == NULL) return NULL; at = (DCE2_ClActTracker *)DCE2_Alloc(sizeof(DCE2_ClActTracker), DCE2_MEM_TYPE__CL_ACT); if (at == NULL) { DCE2_Free((void *)uuid, sizeof(Uuid), DCE2_MEM_TYPE__CL_ACT); return NULL; } DCE2_CopyUuid(uuid, &cl_hdr->act_id, DceRpcClByteOrder(cl_hdr)); DCE2_CopyUuid(&at->act, &cl_hdr->act_id, DceRpcClByteOrder(cl_hdr)); status = DCE2_ListInsert(clt->act_trackers, (void *)uuid, (void *)at); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)uuid, sizeof(Uuid), DCE2_MEM_TYPE__CL_ACT); DCE2_Free((void *)at, sizeof(DCE2_ClActTracker), DCE2_MEM_TYPE__CL_ACT); return NULL; } return at; } /******************************************************************** * Function: DCE2_ClRequest() * * Handles a client request. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_ClActTracker * * Pointer to the connectionless activity tracker. * DceRpcClHdr * * Pointer to the connectionless header in the packet. * const uint8_t * * Pointer to current position in the packet payload. * uint16_t * Length of packet payload left from current pointer * position. * * Returns: None * ********************************************************************/ static void DCE2_ClRequest(DCE2_SsnData *sd, DCE2_ClActTracker *at, DceRpcClHdr *cl_hdr, const uint8_t *data_ptr, uint16_t data_len) { uint32_t seq_num = DceRpcClSeqNum(cl_hdr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CL, "Processing Request ...\n")); if (seq_num > at->seq_num) { /* This is the normal case where the sequence number is incremented * for each request. Set the new sequence number and mark it valid. */ at->seq_num = seq_num; at->seq_num_invalid = 0; /* If there are any fragments, the new sequence number invalidates * all of the frags that might be currently stored. */ DCE2_ClResetFragTracker(&at->frag_tracker); } else if ((seq_num < at->seq_num) || at->seq_num_invalid) { #if 0 /* If we get a seqence number less than what we're at, the * server won't look at it. If we get the same sequence number, * but we've already processed a previous request, it's bad. * Fragments will have the same sequence number, but we won't * mark the seq number invalid until we've gotten all of them. */ /* Comment for now since we're not able to detect retransmits */ DCE2_Alert(sd, DCE2_EVENT__CL_BAD_SEQ_NUM, dce2_pdu_types[DceRpcClPduType(cl_hdr)]); #endif return; } DCE2_ResetRopts(&sd->ropts); if (DceRpcClFrag(cl_hdr)) /* It's a frag */ { dce2_stats.cl_fragments++; if (DCE2_GcDceDefrag()) { DCE2_ClHandleFrag(sd, at, cl_hdr, data_ptr, data_len); return; } } else /* It's a full request */ { if ((at->frag_tracker.frags != NULL) && !DCE2_ListIsEmpty(at->frag_tracker.frags)) { /* If we get a full request, i.e. not a frag, any frags * we have collected are invalidated */ DCE2_ClResetFragTracker(&at->frag_tracker); } else if (seq_num != DCE2_CL__MAX_SEQ_NUM) { /* This sequence number is now invalid. 0xffffffff is the end of * the sequence number space and can be reused */ at->seq_num_invalid = 1; } else { /* Got the last sequence number in the sequence number space */ dce2_stats.cl_max_seqnum++; } } /* Cache relevant values for rule option processing */ sd->ropts.first_frag = DceRpcClFirstFrag(cl_hdr); DCE2_CopyUuid(&sd->ropts.iface, DceRpcClIface(cl_hdr), DceRpcClByteOrder(cl_hdr)); sd->ropts.iface_vers = DceRpcClIfaceVers(cl_hdr); sd->ropts.hdr_byte_order = DceRpcClByteOrder(cl_hdr); sd->ropts.data_byte_order = DceRpcClByteOrder(cl_hdr); sd->ropts.opnum = DceRpcClOpnum(cl_hdr); sd->ropts.stub_data = (uint8_t *)cl_hdr + sizeof(DceRpcClHdr); DCE2_Detect(sd); } /******************************************************************** * Function: DCE2_ClHandleFrag() * * Handles connectionless fragments. Creates a new fragment list * if necessary and inserts fragment into list. Sets rule option * values based on the fragment. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_ClActTracker * * Pointer to the connectionless activity tracker. * DceRpcClHdr * * Pointer to the connectionless header in the packet. * const uint8_t * * Pointer to current position in the packet payload. * uint16_t * Length of packet payload left from current pointer * position. * * Returns: None * ********************************************************************/ static void DCE2_ClHandleFrag(DCE2_SsnData *sd, DCE2_ClActTracker *at, DceRpcClHdr *cl_hdr, const uint8_t *data_ptr, uint16_t data_len) { DCE2_ClFragTracker *ft = &at->frag_tracker; DCE2_ClFragNode *fn; uint16_t frag_len; int status; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_cl_frag); /* If the frag length is less than data length there might be authentication * data that we don't want to include, otherwise just set to data len */ if (DceRpcClLen(cl_hdr) < data_len) frag_len = DceRpcClLen(cl_hdr); else frag_len = data_len; if (frag_len == 0) { PREPROC_PROFILE_END(dce2_pstat_cl_frag); return; } if (frag_len > dce2_stats.cl_max_frag_size) dce2_stats.cl_max_frag_size = frag_len; if (DCE2_GcMaxFrag() && (frag_len > DCE2_GcMaxFragLen())) frag_len = DCE2_GcMaxFragLen(); if (ft->frags == NULL) { /* Create new list if we don't have one already */ ft->frags = DCE2_ListNew(DCE2_LIST_TYPE__SORTED, DCE2_ClFragCompare, DCE2_ClFragDataFree, NULL, DCE2_LIST_FLAG__NO_DUPS | DCE2_LIST_FLAG__INS_TAIL, DCE2_MEM_TYPE__CL_FRAG); if (ft->frags == NULL) { PREPROC_PROFILE_END(dce2_pstat_cl_frag); return; } } else { /* If we already have a fragment in the list with the same fragment number, * that fragment will take precedence over this fragment and this fragment * will not be used by the server */ fn = (DCE2_ClFragNode *)DCE2_ListFind(ft->frags, (void *)(uintptr_t)DceRpcClFragNum(cl_hdr)); if (fn != NULL) { PREPROC_PROFILE_END(dce2_pstat_cl_frag); return; } } /* Create a new frag node to insert into the list */ fn = (DCE2_ClFragNode *)DCE2_Alloc(sizeof(DCE2_ClFragNode), DCE2_MEM_TYPE__CL_FRAG); if (fn == NULL) { PREPROC_PROFILE_END(dce2_pstat_cl_frag); DCE2_ClFragReassemble(sd, at, cl_hdr); return; } fn->frag_number = DceRpcClFragNum(cl_hdr); fn->frag_len = frag_len; /* Allocate space for the fragment data */ fn->frag_data = (uint8_t *)DCE2_Alloc(frag_len, DCE2_MEM_TYPE__CL_FRAG); if (fn->frag_data == NULL) { DCE2_Free((void *)fn, sizeof(DCE2_ClFragNode), DCE2_MEM_TYPE__CL_FRAG); PREPROC_PROFILE_END(dce2_pstat_cl_frag); DCE2_ClFragReassemble(sd, at, cl_hdr); return; } /* Copy the fragment data in the packet to the space just allocated */ status = DCE2_Memcpy(fn->frag_data, data_ptr, frag_len, fn->frag_data, fn->frag_data + frag_len); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)fn->frag_data, frag_len, DCE2_MEM_TYPE__CL_FRAG); DCE2_Free((void *)fn, sizeof(DCE2_ClFragNode), DCE2_MEM_TYPE__CL_FRAG); PREPROC_PROFILE_END(dce2_pstat_cl_frag); DCE2_ClFragReassemble(sd, at, cl_hdr); return; } if (DCE2_ListIsEmpty(ft->frags)) { /* If this is the first fragment we've received, set interface uuid */ DCE2_CopyUuid(&ft->iface, DceRpcClIface(cl_hdr), DceRpcClByteOrder(cl_hdr)); ft->iface_vers = DceRpcClIfaceVers(cl_hdr); } if (DceRpcClLastFrag(cl_hdr)) { /* Set number of expected frags on last frag */ ft->num_expected_frags = DceRpcClFragNum(cl_hdr) + 1; } else if (DceRpcClFirstFrag(cl_hdr)) { /* Set opum and byte order on first frag */ ft->opnum = DceRpcClOpnum(cl_hdr); ft->data_byte_order = DceRpcClByteOrder(cl_hdr); } /* Insert frag node into the list */ status = DCE2_ListInsert(ft->frags, (void *)(uintptr_t)fn->frag_number, (void *)fn); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)fn->frag_data, frag_len, DCE2_MEM_TYPE__CL_FRAG); DCE2_Free((void *)fn, sizeof(DCE2_ClFragNode), DCE2_MEM_TYPE__CL_FRAG); PREPROC_PROFILE_END(dce2_pstat_cl_frag); DCE2_ClFragReassemble(sd, at, cl_hdr); return; } /* Fragment number field in header is uint16_t */ if ((ft->num_expected_frags != DCE2_SENTINEL) && (uint16_t)ft->frags->num_nodes == (uint16_t)ft->num_expected_frags) { PREPROC_PROFILE_END(dce2_pstat_cl_frag); /* We got all of the frags - reassemble */ DCE2_ClFragReassemble(sd, at, cl_hdr); at->seq_num_invalid = 1; return; } PREPROC_PROFILE_END(dce2_pstat_cl_frag); /* Cache relevant values for rule option processing */ sd->ropts.first_frag = DceRpcClFirstFrag(cl_hdr); DCE2_CopyUuid(&sd->ropts.iface, &ft->iface, DCERPC_BO_FLAG__NONE); sd->ropts.iface_vers = ft->iface_vers; sd->ropts.hdr_byte_order = DceRpcClByteOrder(cl_hdr); if (ft->data_byte_order != DCE2_SENTINEL) sd->ropts.data_byte_order = ft->data_byte_order; else sd->ropts.data_byte_order = DceRpcClByteOrder(cl_hdr); if (ft->opnum != DCE2_SENTINEL) sd->ropts.opnum = ft->opnum; else sd->ropts.opnum = DceRpcClOpnum(cl_hdr); sd->ropts.stub_data = (uint8_t *)cl_hdr + sizeof(DceRpcClHdr); DCE2_Detect(sd); } /******************************************************************** * Function: DCE2_ClFragCompare() * * Callback to fragment list for sorting the nodes in the list * by fragment number. Values passed in are the fragment numbers. * * Arguments: * const void * * First fragment number to compare. * const void * * Second fragment number to compare. * * Returns: * int * 1 if first value is greater than second value * -1 if first value is less than second value * 0 if first value equals second value * ********************************************************************/ static int DCE2_ClFragCompare(const void *a, const void *b) { int x = (int)(uintptr_t)a; int y = (int)(uintptr_t)b; if (x > y) return 1; if (x < y) return -1; return 0; } /******************************************************************** * Function: DCE2_ClFragReassemble() * * Reassembles fragments into reassembly buffer and copies to * reassembly packet. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_ClActTracker * * Pointer to the connectionless activity tracker. * DceRpcClHdr * * Pointer to the connectionless header in the packet. * * Returns: None * ********************************************************************/ static void DCE2_ClFragReassemble(DCE2_SsnData *sd, DCE2_ClActTracker *at, const DceRpcClHdr *cl_hdr) { DCE2_ClFragTracker *ft = &at->frag_tracker; DCE2_ClFragNode *fnode; uint8_t *rdata = dce2_cl_rbuf; uint16_t rlen = sizeof(dce2_cl_rbuf); uint32_t stub_len = 0; const uint8_t *stub_data = NULL; SFSnortPacket *rpkt = NULL; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_cl_reass); for (fnode = (DCE2_ClFragNode *)DCE2_ListFirst(ft->frags); fnode != NULL; fnode = (DCE2_ClFragNode *)DCE2_ListNext(ft->frags)) { if (fnode->frag_len > rlen) { DCE2_Log(DCE2_LOG_TYPE__WARN, "%s(%d) Size of fragments exceeds reassembly buffer size. " "Using as many fragments as will fit.", __FILE__, __LINE__); break; } if (DCE2_Memcpy(rdata, fnode->frag_data, fnode->frag_len, rdata, rdata + rlen) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to copy data into fragment " "reassembly buffer.", __FILE__, __LINE__); break; } DCE2_MOVE(rdata, rlen, fnode->frag_len); stub_len += fnode->frag_len; } switch (sd->trans) { case DCE2_TRANS_TYPE__UDP: rpkt = DCE2_GetRpkt(sd->wire_pkt, DCE2_RPKT_TYPE__UDP_CL_FRAG, dce2_cl_rbuf, stub_len); if (rpkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to create reassembly packet.", __FILE__, __LINE__); PREPROC_PROFILE_END(dce2_pstat_cl_reass); return; } DCE2_ClSetRdata(at, cl_hdr, (uint8_t *)rpkt->payload, (uint16_t)(rpkt->payload_size - DCE2_MOCK_HDR_LEN__CL)); stub_data = rpkt->payload + DCE2_MOCK_HDR_LEN__CL; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, sd->trans); return; } PREPROC_PROFILE_END(dce2_pstat_cl_reass); if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to push packet onto packet stack.", __FILE__, __LINE__); return; } /* Cache relevant values for rule option processing */ sd->ropts.first_frag = 1; DCE2_CopyUuid(&sd->ropts.iface, &ft->iface, DCERPC_BO_FLAG__NONE); sd->ropts.iface_vers = ft->iface_vers; sd->ropts.hdr_byte_order = DceRpcClByteOrder(cl_hdr); if (ft->data_byte_order != DCE2_SENTINEL) sd->ropts.data_byte_order = ft->data_byte_order; else sd->ropts.data_byte_order = DceRpcClByteOrder(cl_hdr); if (ft->opnum != DCE2_SENTINEL) sd->ropts.opnum = ft->opnum; else sd->ropts.opnum = DceRpcClOpnum(cl_hdr); sd->ropts.stub_data = stub_data; DCE2_Detect(sd); DCE2_PopPkt(); dce2_stats.cl_frag_reassembled++; } /******************************************************************** * Function: DCE2_ClResetFragTracker() * * Destroys the fragment tracker's fragment list and resets opnum, * byte order and number of expected frags to a sentinel. * * Arguments: * DCE2_ClFragTracker * * Pointer to the fragment tracker to reset. * * Returns: None * ********************************************************************/ static void DCE2_ClResetFragTracker(DCE2_ClFragTracker *ft) { if (ft == NULL) return; if (ft->frags != NULL) { DCE2_ListDestroy(ft->frags); ft->frags = NULL; } ft->opnum = DCE2_SENTINEL; ft->data_byte_order = DCE2_SENTINEL; ft->num_expected_frags = DCE2_SENTINEL; } /******************************************************************** * Function: DCE2_ClCleanTracker() * * Destroys all the activity tracker list, which cleans out and * frees all data associated with each activity tracker in the * list. * * Arguments: * DCE2_ClTracker * * Pointer to connectionless tracker. * * Returns: None * ********************************************************************/ void DCE2_ClCleanTracker(DCE2_ClTracker *clt) { if (clt == NULL) return; /* Destroy activity trackers list - this will have the * effect of freeing everything inside of it */ DCE2_ListDestroy(clt->act_trackers); clt->act_trackers = NULL; } /******************************************************************** * Function: DCE2_ClActDataFree() * * Callback to activity tracker list for freeing activity trackers. * * Arguments: * void * * Activity tracker to free. * * Returns: None * ********************************************************************/ static void DCE2_ClActDataFree(void *data) { DCE2_ClActTracker *at = (DCE2_ClActTracker *)data; if (at == NULL) return; DCE2_ListDestroy(at->frag_tracker.frags); at->frag_tracker.frags = NULL; DCE2_Free((void *)at, sizeof(DCE2_ClActTracker), DCE2_MEM_TYPE__CL_ACT); } /******************************************************************** * Function: DCE2_ClActKeyFree() * * Callback to activity tracker list for freeing the key (this is * the activity UUID). Since key is dynamically allocated, we need * to free it. * * Arguments: * void * * The activity UUID to free. * * Returns: None * ********************************************************************/ static void DCE2_ClActKeyFree(void *key) { if (key == NULL) return; DCE2_Free(key, sizeof(Uuid), DCE2_MEM_TYPE__CL_ACT); } /******************************************************************** * Function: DCE2_ClFragDataFree() * * Callback to fragment list for freeing data kept in list. Need * to free the frag node and the data attached to it. * * Arguments: * void * * Pointer to fragment data (a frag node). * * Returns: None * ********************************************************************/ static void DCE2_ClFragDataFree(void *data) { DCE2_ClFragNode *fn = (DCE2_ClFragNode *)data; if (fn == NULL) return; if (fn->frag_data != NULL) DCE2_Free((void *)fn->frag_data, fn->frag_len, DCE2_MEM_TYPE__CL_FRAG); DCE2_Free((void *)fn, sizeof(DCE2_ClFragNode), DCE2_MEM_TYPE__CL_FRAG); } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_debug.c0000644000175000017500000000737114241075633021701 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides functions for debugging the preprocessor. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef HAVE_STDINT_H #include #endif /* HAVE_CONFIG_H */ #include #include #include #include #include "sf_types.h" #include "dce2_debug.h" #include "dce2_utils.h" /******************************************************************** * Function: DCE2_GetDebugLevel() * * Gets the debugging level set by the DCE2 debugging environment * variable on the first call. Subsequent calls will used the * cached value. * * Arguments: None * * Returns: * uint32_t * The debugging level set by the environment variable. * ********************************************************************/ static uint32_t DCE2_GetDebugLevel(void) { static int debug_init = 0; static uint32_t debug_level = 0; const char* value; if (debug_init) return debug_level; value = getenv(DCE2_DEBUG_VARIABLE); if (value != NULL) { char *endptr; debug_level = _dpd.SnortStrtoul(value, &endptr, 0); if ((errno == ERANGE) || (*endptr != '\0')) { DCE2_Log(DCE2_LOG_TYPE__WARN, "\"%s\" value out of range or not a number: %s. " "Debugging will not be turned on.", DCE2_DEBUG_VARIABLE, value); debug_level = 0; } } debug_init = 1; return debug_level; } /******************************************************************** * Function: DCE2_DebugThis() * * Determines based on the level if debugging is turned on. * * Arguments: * int * The level to check for. * * Returns: * int * 1 if debugging is turned on. * 0 if debugging is not turned on. * ********************************************************************/ int DCE2_DebugThis(int level) { if (level & DCE2_GetDebugLevel()) return 1; return 0; } /******************************************************************** * Function: DCE2_DebugMsg() * * Prints message to stdout if debugging is on for specified level. * * Arguments: * int * The level the message refers to. * const char * * The format string. * ... * The arguments to the format string. * * Returns: None * ********************************************************************/ void DCE2_DebugMsg(int level, const char *format, ...) { va_list ap; if (!DCE2_DebugThis(level)) return; va_start(ap, format); vfprintf(stdout, format, ap); va_end(ap); } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_memory.c0000644000175000017500000004332014241075650022114 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_memory.h" #include "dce2_utils.h" #include "dce2_config.h" #include "dce2_event.h" #include "memory_stats.h" #include #include #include /******************************************************************** * Macros ********************************************************************/ #define DCE2_MEMCAP_OK 0 #define DCE2_MEMCAP_EXCEEDED 1 /******************************************************************** * Global variables ********************************************************************/ DCE2_Memory dce2_memory; DCE2_MemState dce2_mem_state = DCE2_MEM_STATE__OKAY; /******************************************************************** * Private function prototypes ********************************************************************/ static void DCE2_RegMemSmb(uint32_t, DCE2_MemType); static void DCE2_RegMemCo(uint32_t, DCE2_MemType); static void DCE2_RegMemCl(uint32_t, DCE2_MemType); static int DCE2_CheckMemcap(uint32_t, DCE2_MemType); /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_RegMem(uint32_t size, DCE2_MemType mtype) { switch (mtype) { case DCE2_MEM_TYPE__CONFIG: dce2_memory.config += size; if (dce2_memory.config > dce2_memory.config_max) dce2_memory.config_max = dce2_memory.config; break; case DCE2_MEM_TYPE__ROPTION: dce2_memory.roptions += size; if (dce2_memory.roptions > dce2_memory.roptions_max) dce2_memory.roptions_max = dce2_memory.roptions; break; case DCE2_MEM_TYPE__RT: dce2_memory.rt += size; if (dce2_memory.rt > dce2_memory.rt_max) dce2_memory.rt_max = dce2_memory.rt; break; case DCE2_MEM_TYPE__INIT: dce2_memory.init += size; if (dce2_memory.init > dce2_memory.init_max) dce2_memory.init_max = dce2_memory.init; break; case DCE2_MEM_TYPE__SMB_SSN: case DCE2_MEM_TYPE__SMB_SEG: case DCE2_MEM_TYPE__SMB_UID: case DCE2_MEM_TYPE__SMB_TID: case DCE2_MEM_TYPE__SMB_FID: case DCE2_MEM_TYPE__SMB_FILE: case DCE2_MEM_TYPE__SMB_REQ: DCE2_RegMemSmb(size, mtype); break; case DCE2_MEM_TYPE__TCP_SSN: dce2_memory.tcp_ssn += size; if (dce2_memory.tcp_ssn > dce2_memory.tcp_ssn_max) dce2_memory.tcp_ssn_max = dce2_memory.tcp_ssn; dce2_memory.tcp_total += size; if (dce2_memory.tcp_total > dce2_memory.tcp_total_max) dce2_memory.tcp_total_max = dce2_memory.tcp_total; break; case DCE2_MEM_TYPE__CO_SEG: case DCE2_MEM_TYPE__CO_FRAG: case DCE2_MEM_TYPE__CO_CTX: DCE2_RegMemCo(size, mtype); break; case DCE2_MEM_TYPE__UDP_SSN: dce2_memory.udp_ssn += size; if (dce2_memory.udp_ssn > dce2_memory.udp_ssn_max) dce2_memory.udp_ssn_max = dce2_memory.udp_ssn; dce2_memory.udp_total += size; if (dce2_memory.udp_total > dce2_memory.udp_total_max) dce2_memory.udp_total_max = dce2_memory.udp_total; break; case DCE2_MEM_TYPE__HTTP_SSN: dce2_memory.http_ssn += size; if (dce2_memory.http_ssn > dce2_memory.http_ssn_max) dce2_memory.http_ssn_max = dce2_memory.http_ssn; dce2_memory.http_total += size; if (dce2_memory.http_total > dce2_memory.http_total_max) dce2_memory.http_total_max = dce2_memory.http_total; break; case DCE2_MEM_TYPE__CL_ACT: case DCE2_MEM_TYPE__CL_FRAG: DCE2_RegMemCl(size, mtype); break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid memory type: %d", __FILE__, __LINE__, mtype); break; } switch (mtype) { case DCE2_MEM_TYPE__CONFIG: case DCE2_MEM_TYPE__ROPTION: case DCE2_MEM_TYPE__RT: case DCE2_MEM_TYPE__INIT: break; default: dce2_memory.rtotal += size; if (dce2_memory.rtotal > dce2_memory.rtotal_max) dce2_memory.rtotal_max = dce2_memory.rtotal; } dce2_memory.total += size; if (dce2_memory.total > dce2_memory.total_max) dce2_memory.total_max = dce2_memory.total; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_RegMemSmb(uint32_t size, DCE2_MemType mtype) { switch (mtype) { case DCE2_MEM_TYPE__SMB_SSN: dce2_memory.smb_ssn += size; if (dce2_memory.smb_ssn > dce2_memory.smb_ssn_max) dce2_memory.smb_ssn_max = dce2_memory.smb_ssn; break; case DCE2_MEM_TYPE__SMB_SEG: dce2_memory.smb_seg += size; if (dce2_memory.smb_seg > dce2_memory.smb_seg_max) dce2_memory.smb_seg_max = dce2_memory.smb_seg; break; case DCE2_MEM_TYPE__SMB_UID: dce2_memory.smb_uid += size; if (dce2_memory.smb_uid > dce2_memory.smb_uid_max) dce2_memory.smb_uid_max = dce2_memory.smb_uid; break; case DCE2_MEM_TYPE__SMB_TID: dce2_memory.smb_tid += size; if (dce2_memory.smb_tid > dce2_memory.smb_tid_max) dce2_memory.smb_tid_max = dce2_memory.smb_tid; break; case DCE2_MEM_TYPE__SMB_FID: dce2_memory.smb_fid += size; if (dce2_memory.smb_fid > dce2_memory.smb_fid_max) dce2_memory.smb_fid_max = dce2_memory.smb_fid; break; case DCE2_MEM_TYPE__SMB_FILE: dce2_memory.smb_file += size; if (dce2_memory.smb_file > dce2_memory.smb_file_max) dce2_memory.smb_file_max = dce2_memory.smb_file; break; case DCE2_MEM_TYPE__SMB_REQ: dce2_memory.smb_req += size; if (dce2_memory.smb_req > dce2_memory.smb_req_max) dce2_memory.smb_req_max = dce2_memory.smb_req; break; default: return; } dce2_memory.smb_total += size; if (dce2_memory.smb_total > dce2_memory.smb_total_max) dce2_memory.smb_total_max = dce2_memory.smb_total; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_RegMemCo(uint32_t size, DCE2_MemType mtype) { switch (mtype) { case DCE2_MEM_TYPE__CO_SEG: dce2_memory.co_seg += size; if (dce2_memory.co_seg > dce2_memory.co_seg_max) dce2_memory.co_seg_max = dce2_memory.co_seg; break; case DCE2_MEM_TYPE__CO_FRAG: dce2_memory.co_frag += size; if (dce2_memory.co_frag > dce2_memory.co_frag_max) dce2_memory.co_frag_max = dce2_memory.co_frag; break; case DCE2_MEM_TYPE__CO_CTX: dce2_memory.co_ctx += size; if (dce2_memory.co_ctx > dce2_memory.co_ctx_max) dce2_memory.co_ctx_max = dce2_memory.co_ctx; break; default: return; } dce2_memory.co_total += size; if (dce2_memory.co_total > dce2_memory.co_total_max) dce2_memory.co_total_max = dce2_memory.co_total; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_RegMemCl(uint32_t size, DCE2_MemType mtype) { switch (mtype) { case DCE2_MEM_TYPE__CL_ACT: dce2_memory.cl_act += size; if (dce2_memory.cl_act > dce2_memory.cl_act_max) dce2_memory.cl_act_max = dce2_memory.cl_act; break; case DCE2_MEM_TYPE__CL_FRAG: dce2_memory.cl_frag += size; if (dce2_memory.cl_frag > dce2_memory.cl_frag_max) dce2_memory.cl_frag_max = dce2_memory.cl_frag; break; default: return; } dce2_memory.cl_total += size; if (dce2_memory.cl_total > dce2_memory.cl_total_max) dce2_memory.cl_total_max = dce2_memory.cl_total; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_UnRegMem(uint32_t size, DCE2_MemType mtype) { switch (mtype) { case DCE2_MEM_TYPE__CONFIG: dce2_memory.config -= size; break; case DCE2_MEM_TYPE__ROPTION: dce2_memory.roptions -= size; break; case DCE2_MEM_TYPE__RT: dce2_memory.rt -= size; break; case DCE2_MEM_TYPE__INIT: dce2_memory.init -= size; break; case DCE2_MEM_TYPE__SMB_SSN: dce2_memory.smb_total -= size; dce2_memory.smb_ssn -= size; break; case DCE2_MEM_TYPE__SMB_SEG: dce2_memory.smb_total -= size; dce2_memory.smb_seg -= size; break; case DCE2_MEM_TYPE__SMB_UID: dce2_memory.smb_total -= size; dce2_memory.smb_uid -= size; break; case DCE2_MEM_TYPE__SMB_TID: dce2_memory.smb_total -= size; dce2_memory.smb_tid -= size; break; case DCE2_MEM_TYPE__SMB_FID: dce2_memory.smb_total -= size; dce2_memory.smb_fid -= size; break; case DCE2_MEM_TYPE__SMB_FILE: dce2_memory.smb_total -= size; dce2_memory.smb_file -= size; break; case DCE2_MEM_TYPE__SMB_REQ: dce2_memory.smb_total -= size; dce2_memory.smb_req -= size; break; case DCE2_MEM_TYPE__TCP_SSN: dce2_memory.tcp_total -= size; dce2_memory.tcp_ssn -= size; break; case DCE2_MEM_TYPE__CO_SEG: dce2_memory.co_total -= size; dce2_memory.co_seg -= size; break; case DCE2_MEM_TYPE__CO_FRAG: dce2_memory.co_total -= size; dce2_memory.co_frag -= size; break; case DCE2_MEM_TYPE__CO_CTX: dce2_memory.co_total -= size; dce2_memory.co_ctx -= size; break; case DCE2_MEM_TYPE__UDP_SSN: dce2_memory.udp_total -= size; dce2_memory.udp_ssn -= size; break; case DCE2_MEM_TYPE__CL_ACT: dce2_memory.cl_total -= size; dce2_memory.cl_act -= size; break; case DCE2_MEM_TYPE__HTTP_SSN: dce2_memory.http_total -= size; dce2_memory.http_ssn -= size; break; case DCE2_MEM_TYPE__CL_FRAG: dce2_memory.cl_total -= size; dce2_memory.cl_frag -= size; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid memory type: %d", __FILE__, __LINE__, mtype); break; } switch (mtype) { case DCE2_MEM_TYPE__CONFIG: case DCE2_MEM_TYPE__ROPTION: case DCE2_MEM_TYPE__RT: case DCE2_MEM_TYPE__INIT: break; default: dce2_memory.rtotal -= size; } dce2_memory.total -= size; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_CheckMemcap(uint32_t size, DCE2_MemType mtype) { switch (mtype) { /*Avoid checking memcap for configurations*/ case DCE2_MEM_TYPE__CONFIG: case DCE2_MEM_TYPE__ROPTION: case DCE2_MEM_TYPE__RT: case DCE2_MEM_TYPE__INIT: break; default: if (dce2_mem_state == DCE2_MEM_STATE__MEMCAP) break; if ((dce2_memory.rtotal + size) > DCE2_GcMemcap()) { DCE2_Alert(NULL, DCE2_EVENT__MEMCAP); dce2_mem_state = DCE2_MEM_STATE__MEMCAP; return DCE2_MEMCAP_EXCEEDED; } break; } return DCE2_MEMCAP_OK; } uint32_t check_memory_category (DCE2_MemType mtype) { switch (mtype) { case DCE2_MEM_TYPE__CONFIG: case DCE2_MEM_TYPE__ROPTION: return PP_MEM_CATEGORY_CONFIG; case DCE2_MEM_TYPE__RT: return PP_MEM_CATEGORY_MISC; case DCE2_MEM_TYPE__INIT: return PP_MEM_CATEGORY_CONFIG; case DCE2_MEM_TYPE__SMB_SSN: case DCE2_MEM_TYPE__SMB_SEG: case DCE2_MEM_TYPE__SMB_UID: case DCE2_MEM_TYPE__SMB_TID: case DCE2_MEM_TYPE__SMB_FID: case DCE2_MEM_TYPE__SMB_FILE: case DCE2_MEM_TYPE__SMB_REQ: case DCE2_MEM_TYPE__TCP_SSN: case DCE2_MEM_TYPE__CO_SEG: case DCE2_MEM_TYPE__CO_FRAG: case DCE2_MEM_TYPE__CO_CTX: case DCE2_MEM_TYPE__UDP_SSN: case DCE2_MEM_TYPE__CL_ACT: case DCE2_MEM_TYPE__CL_FRAG: case DCE2_MEM_TYPE__HTTP_SSN: return PP_MEM_CATEGORY_SESSION; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid memory type: %d", __FILE__, __LINE__, mtype); return PP_MEM_MAX_CATEGORY; } } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void * DCE2_Alloc(uint32_t size, DCE2_MemType mtype) { void *mem; if (DCE2_CheckMemcap(size, mtype) != DCE2_MEMCAP_OK) return NULL; mem = _dpd.snortAlloc(1, (size_t)size, PP_DCE2, check_memory_category(mtype)); if (mem == NULL) { DCE2_Die("%s(%d) Out of memory!", __FILE__, __LINE__); } DCE2_RegMem(size, mtype); return mem; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_Free(void *mem, uint32_t size, DCE2_MemType mtype) { if (mem == NULL) return; DCE2_UnRegMem(size, mtype); _dpd.snortFree(mem, (size_t)size, PP_DCE2, check_memory_category(mtype)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void * DCE2_ReAlloc(void *old_mem, uint32_t old_size, uint32_t new_size, DCE2_MemType mtype) { void *new_mem; DCE2_Ret status; if (old_mem == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Old memory passed in was NULL.", __FILE__, __LINE__); return NULL; } else if (new_size < old_size) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) New size is less than old size.", __FILE__, __LINE__); return NULL; } else if (new_size == old_size) { return old_mem; } if (DCE2_CheckMemcap(new_size - old_size, mtype) == DCE2_MEMCAP_EXCEEDED) return NULL; new_mem = DCE2_Alloc(new_size, mtype); if (new_mem == NULL) return NULL; status = DCE2_Memcpy(new_mem, old_mem, old_size, new_mem, (void *)((uint8_t *)new_mem + new_size)); if (status != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to copy old memory into new memory.", __FILE__, __LINE__); DCE2_Free(new_mem, new_size, mtype); return NULL; } DCE2_Free(old_mem, old_size, mtype); return new_mem; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_MemInit(void) { memset(&dce2_memory, 0, sizeof(dce2_memory)); } size_t DCE2_MemInUse() { return (size_t) dce2_memory.rtotal; } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_paf.c0000644000175000017500000005364714241075652021371 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include "sf_types.h" #include "sfPolicy.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "dce2_utils.h" #include "dce2_session.h" #include "dce2_smb.h" #include "dce2_debug.h" #include "snort_dce2.h" #include "includes/dcerpc.h" #include "includes/smb.h" #define DCE2_SMB_PAF_SHIFT(x64, x8) { x64 <<= 8; x64 |= (uint64_t)x8; } static uint8_t dce2_smbpaf_id = 0; static uint8_t dce2_tcppaf_id = 0; // Enumerations for PAF states typedef enum _DCE2_PafSmbStates { DCE2_PAF_SMB_STATES__0 = 0, // NetBIOS type DCE2_PAF_SMB_STATES__1, // Added bit of NetBIOS length DCE2_PAF_SMB_STATES__2, // First byte of NetBIOS length DCE2_PAF_SMB_STATES__3, // Second byte of NetBIOS length // Junk states DCE2_PAF_SMB_STATES__4, // 0xff DCE2_PAF_SMB_STATES__5, // 'S' DCE2_PAF_SMB_STATES__6, // 'M' DCE2_PAF_SMB_STATES__7 // 'B' } DCE2_PafSmbStates; typedef enum _DCE2_PafTcpStates { DCE2_PAF_TCP_STATES__0 = 0, DCE2_PAF_TCP_STATES__1, DCE2_PAF_TCP_STATES__2, DCE2_PAF_TCP_STATES__3, DCE2_PAF_TCP_STATES__4, // Byte order DCE2_PAF_TCP_STATES__5, DCE2_PAF_TCP_STATES__6, DCE2_PAF_TCP_STATES__7, DCE2_PAF_TCP_STATES__8, // First byte of fragment length DCE2_PAF_TCP_STATES__9 // Second byte of fragment length } DCE2_PafTcpStates; // State tracker for DCE/RPC over SMB PAF typedef struct _DCE2_PafSmbState { DCE2_PafSmbStates state; uint64_t nb_hdr; // Enough for NetBIOS header and 4 bytes SMB header } DCE2_PafSmbState; // State tracker for DCE/RPC over TCP PAF typedef struct _DCE2_PafTcpState { DCE2_PafTcpStates state; DceRpcBoFlag byte_order; uint16_t frag_len; } DCE2_PafTcpState; // Local function prototypes static inline bool DCE2_PafSmbIsValidNetbiosHdr(uint32_t, bool, SmbNtHdr *, uint32_t *); static inline bool DCE2_PafAbort(void *, uint64_t); static PAF_Status DCE2_SmbPaf(void *, void **, const uint8_t *, uint32_t, uint64_t *, uint32_t *, uint32_t *); static PAF_Status DCE2_TcpPaf(void *, void **, const uint8_t *, uint32_t, uint64_t *, uint32_t *, uint32_t *); /********************************************************************* * Function: DCE2_PafAbort() * * Purpose: Queries the dcerpc2 session data to see if paf abort * flag is set. * * Arguments: * void * - stream session pointer * uint32_t - flags passed in to callback. * Should have PKT_FROM_CLIENT or PKT_FROM_SERVER set. * * Returns: * bool - true if we should abort PAF, false if not. * *********************************************************************/ static inline bool DCE2_PafAbort(void *ssn, uint64_t flags) { DCE2_SsnData *sd; sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(ssn, PP_DCE2); if ((sd != NULL) && DCE2_SsnNoInspect(sd)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Aborting PAF because of session data check.\n")); return true; } return false; } /********************************************************************* * Function: DCE2_PafSmbIsValidNetbiosHdr() * * Purpose: Validates that the NetBIOS header is valid. If in * junk states, header type must be Session Message. * * Arguments: * uint32_t - the 4 bytes of the NetBIOS header * bool - whether we're in a junk data state or not * SmbNtHdr * - Pointer to SMB header protocol identifier * uint32_t * - output parameter - Length in the NetBIOS header * * Returns: * bool - true if valid, false if not * *********************************************************************/ static inline bool DCE2_PafSmbIsValidNetbiosHdr(uint32_t nb_hdr, bool junk, SmbNtHdr *nt_hdr, uint32_t *nb_len) { uint8_t type = (uint8_t)(nb_hdr >> 24); uint8_t bit = (uint8_t)((nb_hdr & 0x00ff0000) >> 16); uint32_t smb_id = nt_hdr ? SmbId(nt_hdr): 0; uint32_t nbs_hdr = 0; if (junk) { if (type != NBSS_SESSION_TYPE__MESSAGE) return false; } else { switch (type) { case NBSS_SESSION_TYPE__MESSAGE: case NBSS_SESSION_TYPE__REQUEST: case NBSS_SESSION_TYPE__POS_RESPONSE: case NBSS_SESSION_TYPE__NEG_RESPONSE: case NBSS_SESSION_TYPE__RETARGET_RESPONSE: case NBSS_SESSION_TYPE__KEEP_ALIVE: break; default: return false; } } /*The bit should be checked only for SMB1, because the length in NetBIOS header should not exceed 0x1FFFF. See [MS-SMB] 2.1 Transport * There is no such limit for SMB2 or SMB3 */ if (smb_id == DCE2_SMB_ID) { if ((bit != 0x00) && (bit != 0x01)) return false; } nbs_hdr = htonl(nb_hdr); if(smb_id == DCE2_SMB2_ID) *nb_len = NbssLen2((const NbssHdr *)&nbs_hdr); else *nb_len = NbssLen((const NbssHdr *)&nbs_hdr); return true; } /********************************************************************* * Function: DCE2_SmbPaf() * * Purpose: The DCE/RPC over SMB PAF callback. * Inspects a byte at a time changing state and shifting * bytes onto the 64bit nb_hdr member. At state 3 * determines if NetBIOS header is valid and if so sets * flush point. If not valid goes to states 4-7 where * there is the possibility that junk data was inserted * before request/response. Needs to validate SMB ID at * this point. At state 7 determines if NetBIOS header * is valid and that the SMB ID is present. Stays in * state 7 until this is the case. * * Arguments: * void * - stream5 session pointer * void ** - SMB state tracking structure * const uint8_t * - payload data to inspect * uint32_t - length of payload data * uint32_t - flags to check whether client or server * uint32_t * - pointer to set flush point * uint32_t * - pointer to set header flush point * * Returns: * PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise * *********************************************************************/ PAF_Status DCE2_SmbPaf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint64_t *flags, uint32_t *fp, uint32_t *fp_eoh) { DCE2_PafSmbState *ss = *(DCE2_PafSmbState **)user; uint32_t n = 0; PAF_Status ps = PAF_SEARCH; uint32_t nb_len = 0; SmbNtHdr *nt_hdr = NULL; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_START_MSG)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "SMB: %u bytes of data\n", len)); #ifdef DEBUG_MSGS DCE2_DEBUG_CODE(DCE2_DEBUG__PAF, printf("Session pointer: %p\n", _dpd.sessionAPI->get_application_data(ssn, PP_DCE2));) if (*flags & FLAG_FROM_CLIENT) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Packet from Client\n")); else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Packet from Server\n")); #endif if (DCE2_PafAbort(ssn, *flags)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_ABORT; } if (ss == NULL) { // beware - we allocate here but s5 calls free() directly // so no pointers allowed ss = calloc(1, sizeof(DCE2_PafSmbState)); if (ss == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_ABORT; } *user = ss; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Start state: %u\n", ss->state)); while (n < len) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, " State %d : 0x%02x", ss->state, data[n])); #ifdef DEBUG_MSGS if (isprint(data[n])) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, " '%c'\n", data[n])); else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "\n")); #endif switch (ss->state) { case DCE2_PAF_SMB_STATES__0: ss->nb_hdr = (uint64_t)data[n]; ss->state++; break; case DCE2_PAF_SMB_STATES__3: DCE2_SMB_PAF_SHIFT(ss->nb_hdr, data[n]); /*(data + n + 1) points to the SMB header protocol identifier (0xFF,'SMB' or 0xFE,'SMB'), which follows the NetBIOS header*/ if ( len >= (DCE2_SMB_ID_SIZE + n + 1)) { nt_hdr = (SmbNtHdr *)(data + n + 1); } if (DCE2_PafSmbIsValidNetbiosHdr((uint32_t)ss->nb_hdr, false, nt_hdr, &nb_len)) { *fp = (nb_len + sizeof(NbssHdr) + n) - ss->state; ss->state = DCE2_PAF_SMB_STATES__0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Setting flush point: %u\n", *fp)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_FLUSH; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Invalid NetBIOS header - " "entering junk data states.\n")); ss->state++; break; case DCE2_PAF_SMB_STATES__7: DCE2_SMB_PAF_SHIFT(ss->nb_hdr, data[n]); /*(data + n - sizeof(DCE2_SMB_ID) + 1) points to the smb_idf field in SmbNtHdr (0xFF,'SMB' or 0xFE,'SMB'), which follows the NetBIOS header*/ nt_hdr = (SmbNtHdr *)(data + n - sizeof(DCE2_SMB_ID) + 1); /*ss->nb_hdr is the value to 4 bytes of NetBIOS header + 4 bytes of SMB header protocol identifier . Right shift by 32 bits to get the value of NetBIOS header*/ if (!DCE2_PafSmbIsValidNetbiosHdr((uint32_t)(ss->nb_hdr >> 32), true, nt_hdr, &nb_len)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Invalid NetBIOS header - " "staying in State 7.\n")); break; } if (((uint32_t)ss->nb_hdr != DCE2_SMB_ID) && ((uint32_t)ss->nb_hdr != DCE2_SMB2_ID)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Invalid SMB ID - " "staying in State 7.\n")); break; } *fp = (nb_len + sizeof(NbssHdr) + n) - ss->state; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Setting flush point: %u\n", *fp)); ss->state = DCE2_PAF_SMB_STATES__0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_FLUSH; default: DCE2_SMB_PAF_SHIFT(ss->nb_hdr, data[n]); ss->state++; break; } n++; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return ps; } /********************************************************************* * Function: DCE2_TcpPaf() * * Purpose: The DCE/RPC over TCP PAF callback. * Inspects a byte at a time changing state. At state 4 * gets byte order of PDU. At states 8 and 9 gets * fragment length and sets flush point if no more data. * Otherwise accumulates flush points because there can * be multiple PDUs in a single TCP segment (evasion case). * * Arguments: * void * - stream5 session pointer * void ** - TCP state tracking structure * const uint8_t * - payload data to inspect * uint32_t - length of payload data * uint32_t - flags to check whether client or server * uint32_t * - pointer to set flush point * uint32_t * - pointer to set header flush point * * Returns: * PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise * *********************************************************************/ PAF_Status DCE2_TcpPaf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint64_t *flags, uint32_t *fp, uint32_t *fp_eoh) { DCE2_PafTcpState *ds = *(DCE2_PafTcpState **)user; uint32_t n = 0; int start_state; PAF_Status ps = PAF_SEARCH; uint32_t tmp_fp = 0; DCE2_SsnData *sd = (DCE2_SsnData *)_dpd.sessionAPI->get_application_data(ssn, PP_DCE2); int num_requests = 0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_START_MSG)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "TCP: %u bytes of data\n", len)); DCE2_DEBUG_CODE(DCE2_DEBUG__PAF, printf("Session pointer: %p\n", _dpd.sessionAPI->get_application_data(ssn, PP_DCE2));) #ifdef DEBUG_MSGS if (*flags & FLAG_FROM_CLIENT) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Packet from Client\n")); else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Packet from Server\n")); #endif if (DCE2_PafAbort(ssn, *flags)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_ABORT; } if (sd == NULL) { // Need packet to see if it's an autodetect port then do an autodetect // if autodetect port and not autodetected // return PAF_ABORT bool autodetected = false; #ifdef TARGET_BASED if (_dpd.isAdaptiveConfigured()) { int16_t proto_id = _dpd.sessionAPI->get_application_protocol_id(ssn); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "No session data - checking adaptive " "to see if it's DCE/RPC.\n")); if (proto_id == dce2_proto_ids.dcerpc) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Adaptive says it's " "DCE/RPC - no need to autodetect\n")); autodetected = true; } else if (proto_id != 0) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Adaptive says it's " "not DCE/RPC - aborting\n")); return PAF_ABORT; } } if (!autodetected) { #endif DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "No session data - autodetecting\n")); if (len >= sizeof(DceRpcCoHdr)) { DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)data; if ((DceRpcCoVersMaj(co_hdr) == DCERPC_PROTO_MAJOR_VERS__5) && (DceRpcCoVersMin(co_hdr) == DCERPC_PROTO_MINOR_VERS__0) && (((*flags & FLAG_FROM_CLIENT) && DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND) || ((*flags & FLAG_FROM_SERVER) && DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND_ACK)) && (DceRpcCoFragLen(co_hdr) >= sizeof(DceRpcCoHdr))) { autodetected = true; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Autodetected!\n")); } } else if ((*data == DCERPC_PROTO_MAJOR_VERS__5) && (*flags & FLAG_FROM_CLIENT)) { autodetected = true; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Autodetected!\n")); } #ifdef TARGET_BASED } #endif if (!autodetected) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Couldn't autodetect - aborting\n")); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_ABORT; } } if (ds == NULL) { // beware - we allocate here but s5 calls free() directly // so no pointers allowed ds = calloc(1, sizeof(DCE2_PafTcpState)); if (ds == NULL) return PAF_ABORT; *user = ds; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Start state: %u\n", ds->state)); start_state = (uint8_t)ds->state; // determines how many bytes already looked at while (n < len) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, " State %d : 0x%02x", ds->state, data[n])); #ifdef DEBUG_MSGS if (isprint(data[n])) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, " '%c'\n", data[n])); else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "\n")); #endif switch (ds->state) { case DCE2_PAF_TCP_STATES__4: // Get byte order ds->byte_order = DceRpcByteOrder(data[n]); ds->state++; #ifdef DEBUG_MSGS if (ds->byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Byte order: Little endian\n")); else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Byte order: Big endian\n")); #endif break; case DCE2_PAF_TCP_STATES__8: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "First byte of fragment length\n")); if (ds->byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN) ds->frag_len = data[n]; else ds->frag_len = data[n] << 8; ds->state++; break; case DCE2_PAF_TCP_STATES__9: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Second byte of fragment length\n")); if (ds->byte_order == DCERPC_BO_FLAG__LITTLE_ENDIAN) ds->frag_len |= data[n] << 8; else ds->frag_len |= data[n]; /* If we get a bad frag length abort */ if (ds->frag_len < sizeof(DceRpcCoHdr)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_ABORT; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Fragment length: %u\n", ds->frag_len)); /* Increment n here so we can continue */ n += ds->frag_len - (uint8_t)ds->state; num_requests++; /* Might have multiple PDUs in one segment. If the last PDU is partial, * flush just before it */ if ((num_requests == 1) || (n <= len)) tmp_fp += ds->frag_len; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Requests: %u\n", num_requests)); ds->state = DCE2_PAF_TCP_STATES__0; continue; // we incremented n already default: ds->state++; break; } n++; } if (tmp_fp != 0) { *fp = tmp_fp - start_state; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "Setting flush point: %u\n", *fp)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return PAF_FLUSH; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__PAF, "%s\n", DCE2_DEBUG__PAF_END_MSG)); return ps; } /********************************************************************* * Function: DCE2_PafRegisterPort() * Function: DCE2_PafRegisterService() * * Purpose: Registers callbacks for interested ports and services. * SMB and TCP ports are mutually exclusive so only one or * the other will be registered for any given port. * * Arguments: * uint16_t - port or service to register * tSfPolicyId - the policy to register for * DCE2_TransType - the type of DCE/RPC transport to register for. * * Returns: * int - 0 for success. * *********************************************************************/ int DCE2_PafRegisterPort (struct _SnortConfig *sc, uint16_t port, tSfPolicyId pid, DCE2_TransType trans) { if (!_dpd.isPafEnabled()) return 0; switch (trans) { case DCE2_TRANS_TYPE__SMB: dce2_smbpaf_id = _dpd.streamAPI->register_paf_port(sc, pid, port, 0, DCE2_SmbPaf, true); dce2_smbpaf_id = _dpd.streamAPI->register_paf_port(sc, pid, port, 1, DCE2_SmbPaf, true); break; case DCE2_TRANS_TYPE__TCP: dce2_tcppaf_id = _dpd.streamAPI->register_paf_port(sc, pid, port, 0, DCE2_TcpPaf, true); dce2_tcppaf_id = _dpd.streamAPI->register_paf_port(sc, pid, port, 1, DCE2_TcpPaf, true); break; default: DCE2_Die("Invalid transport type sent to paf registration function"); break; } return 0; } #ifdef TARGET_BASED int DCE2_PafRegisterService (struct _SnortConfig *sc, uint16_t app_id, tSfPolicyId pid, DCE2_TransType trans) { if (!_dpd.isPafEnabled()) return 0; switch (trans) { case DCE2_TRANS_TYPE__SMB: dce2_smbpaf_id = _dpd.streamAPI->register_paf_service(sc, pid, app_id, 0, DCE2_SmbPaf, true); dce2_smbpaf_id = _dpd.streamAPI->register_paf_service(sc, pid, app_id, 1, DCE2_SmbPaf, true); break; case DCE2_TRANS_TYPE__TCP: dce2_tcppaf_id = _dpd.streamAPI->register_paf_service(sc, pid, app_id, 0, DCE2_TcpPaf, true); dce2_tcppaf_id = _dpd.streamAPI->register_paf_service(sc, pid, app_id, 1, DCE2_TcpPaf, true); break; default: DCE2_Die("Invalid transport type sent to paf registration function"); break; } return 0; } #endif snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_http.h0000644000175000017500000001361314241075640021571 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides session handling of an RPC over HTTP transport. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _DCE2_HTTP_H_ #define _DCE2_HTTP_H_ #include "dce2_session.h" #include "dce2_utils.h" #include "dce2_co.h" #include "dce2_tcp.h" #include "sf_types.h" #include "sf_snort_packet.h" /******************************************************************** * Macros ********************************************************************/ #define DCE2_HTTP_PROXY__RPC_CONNECT_STR "RPC_CONNECT" #define DCE2_HTTP_SERVER__RPC_VERS_STR "ncacn_http/1.0" /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_HttpState { DCE2_HTTP_STATE__NONE, DCE2_HTTP_STATE__INIT_CLIENT, DCE2_HTTP_STATE__INIT_SERVER, DCE2_HTTP_STATE__RPC_DATA } DCE2_HttpState; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_HttpSsnData { DCE2_SsnData sd; DCE2_HttpState state; DCE2_CoTracker co_tracker; } DCE2_HttpSsnData; /******************************************************************** * Inline function prototypes ********************************************************************/ static inline DCE2_TransType DCE2_HttpAutodetectProxy(const SFSnortPacket *); static inline DCE2_TransType DCE2_HttpAutodetectServer(const SFSnortPacket *); static inline int DCE2_HttpDecode(const SFSnortPacket *); /******************************************************************** * Public function prototypes ********************************************************************/ DCE2_HttpSsnData * DCE2_HttpProxySsnInit(void); DCE2_HttpSsnData * DCE2_HttpServerSsnInit(void); void DCE2_HttpProcessProxy(DCE2_HttpSsnData *); void DCE2_HttpProcessServer(DCE2_HttpSsnData *); void DCE2_HttpDataFree(DCE2_HttpSsnData *); void DCE2_HttpSsnFree(void *); /******************************************************************** * Function: DCE2_HttpAutodetectProxy() * * Tries to autodetect an RPC over HTTP proxy. Looks for session * setup strings. * * Arguments: * const SFSnortPacket * * Pointer to the packet going through the system. * * Returns: * DCE2_TransType * DCE2_TRANS_TYPE__HTTP_PROXY if a proxy is autodetected. * DCE2_TRANS_TYPE__NONE if a proxy is not autodetected. * ********************************************************************/ static inline DCE2_TransType DCE2_HttpAutodetectProxy(const SFSnortPacket *p) { const char *buf = NULL; unsigned buf_len = 0; if (DCE2_SsnFromServer(p)) return DCE2_TRANS_TYPE__NONE; /* Use the http decode buffer if possible */ if (DCE2_HttpDecode(p)) { buf = (char*)_dpd.getHttpBuffer(HTTP_BUFFER_METHOD, &buf_len); } if (buf == NULL) { buf = (char *)p->payload; buf_len = p->payload_size; } if (buf_len >= strlen(DCE2_HTTP_PROXY__RPC_CONNECT_STR)) { if (strncmp(buf, DCE2_HTTP_PROXY__RPC_CONNECT_STR, strlen(DCE2_HTTP_PROXY__RPC_CONNECT_STR)) == 0) return DCE2_TRANS_TYPE__HTTP_PROXY; } return DCE2_TRANS_TYPE__NONE; } /******************************************************************** * Function: DCE2_HttpAutodetectServer() * * Tries to autodetect an RPC over HTTP server. Looks for session * setup strings. * * Arguments: * const SFSnortPacket * * Pointer to the packet going through the system. * * Returns: * DCE2_TransType * DCE2_TRANS_TYPE__HTTP_SERVER if a server is autodetected. * DCE2_TRANS_TYPE__NONE if a server is not autodetected. * ********************************************************************/ static inline DCE2_TransType DCE2_HttpAutodetectServer(const SFSnortPacket *p) { if (DCE2_SsnFromClient(p)) return DCE2_TRANS_TYPE__NONE; if (p->payload_size >= strlen(DCE2_HTTP_SERVER__RPC_VERS_STR)) { if (strncmp((char *)p->payload, DCE2_HTTP_SERVER__RPC_VERS_STR, strlen(DCE2_HTTP_SERVER__RPC_VERS_STR)) == 0) { return DCE2_TRANS_TYPE__HTTP_SERVER; } } return DCE2_TRANS_TYPE__NONE; } /******************************************************************** * Function: DCE2_HttpDecode() * * Returns whether or not this packet was decoded by http_inspect. * * Arguments: * SFSnortPacket * - pointer to packet * * Returns: * int * Non-zero if the packet was http_inspect decoded * Zero if the packet was not http_inspect decoded * ********************************************************************/ static inline int DCE2_HttpDecode(const SFSnortPacket *p) { return p->flags & FLAG_HTTP_DECODE; } #endif /* _DCE2_HTTP_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_tcp.c0000644000175000017500000000644414241075666021407 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_tcp.h" #include "snort_dce2.h" #include "dce2_co.h" #include "dce2_memory.h" #include "dce2_stats.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #ifdef DUMP_BUFFER #include "dcerpc2_buffer_dump.h" #endif /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ DCE2_TcpSsnData * DCE2_TcpSsnInit(void) { DCE2_TcpSsnData *tsd = DCE2_Alloc(sizeof(DCE2_TcpSsnData), DCE2_MEM_TYPE__TCP_SSN); if (tsd == NULL) return NULL; DCE2_CoInitTracker(&tsd->co_tracker); DCE2_ResetRopts(&tsd->sd.ropts); dce2_stats.tcp_sessions++; return tsd; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_TcpProcess(DCE2_TcpSsnData *tsd) { const SFSnortPacket *p = tsd->sd.wire_pkt; const uint8_t *data_ptr = p->payload; uint16_t data_len = p->payload_size; #ifdef DUMP_BUFFER dumpBuffer(DCERPC_TCP_DUMP,data_ptr,data_len); #endif DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__MAIN, "Processing TCP packet.\n")); dce2_stats.tcp_pkts++; DCE2_CoProcess(&tsd->sd, &tsd->co_tracker, data_ptr, data_len); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_TcpDataFree(DCE2_TcpSsnData *tsd) { if (tsd == NULL) return; DCE2_CoCleanTracker(&tsd->co_tracker); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_TcpSsnFree(void *ssn) { DCE2_TcpSsnData *tsd = (DCE2_TcpSsnData *)ssn; if (tsd == NULL) return; DCE2_TcpDataFree(tsd); DCE2_Free((void *)tsd, sizeof(DCE2_TcpSsnData), DCE2_MEM_TYPE__TCP_SSN); } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_tcp.h0000644000175000017500000000664614241075667021421 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_TCP_H_ #define _DCE2_TCP_H_ #include "dce2_session.h" #include "dce2_co.h" #include "dce2_utils.h" #include "dcerpc.h" #include "sf_snort_packet.h" #include "sf_types.h" #include "snort_debug.h" /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_TcpSsnData { DCE2_SsnData sd; DCE2_CoTracker co_tracker; } DCE2_TcpSsnData; /******************************************************************** * Inline function prototypes ********************************************************************/ static inline DCE2_TransType DCE2_TcpAutodetect(const SFSnortPacket *); /******************************************************************** * Public function prototypes ********************************************************************/ DCE2_TcpSsnData * DCE2_TcpSsnInit(void); void DCE2_TcpProcess(DCE2_TcpSsnData *); void DCE2_TcpDataFree(DCE2_TcpSsnData *); void DCE2_TcpSsnFree(void *); /********************************************************************* * Function: DCE2_TcpAutodetect() * * Purpose: Tries to determine if a packet is likely to be DCE/RPC * over TCP. * * Arguments: * const uint8_t * - pointer to packet data. * uint16_t - packet data length. * * Returns: * DCE2_TranType * *********************************************************************/ static inline DCE2_TransType DCE2_TcpAutodetect(const SFSnortPacket *p) { if (p->payload_size >= sizeof(DceRpcCoHdr)) { DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)p->payload; if ((DceRpcCoVersMaj(co_hdr) == DCERPC_PROTO_MAJOR_VERS__5) && (DceRpcCoVersMin(co_hdr) == DCERPC_PROTO_MINOR_VERS__0) && ((DCE2_SsnFromClient(p) && DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND) || (DCE2_SsnFromServer(p) && DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND_ACK)) && (DceRpcCoFragLen(co_hdr) >= sizeof(DceRpcCoHdr))) { return DCE2_TRANS_TYPE__TCP; } } else if ((*p->payload == DCERPC_PROTO_MAJOR_VERS__5) && DCE2_SsnFromClient(p)) { return DCE2_TRANS_TYPE__TCP; } return DCE2_TRANS_TYPE__NONE; } #endif /* _DCE2_TCP_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_co.c0000644000175000017500000031307114241075627021214 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Module for handling connection-oriented DCE/RPC processing. Provides * context id, interface UUID correlation and tracking for use with the * preprocessor rule options. Provides desegmentation and defragmentation. * Sets appropriate data for use with the preprocessor rule options. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_dce2.h" #include "dce2_co.h" #include "dce2_tcp.h" #include "dce2_smb.h" #include "snort_dce2.h" #include "dce2_memory.h" #include "dce2_utils.h" #include "dce2_stats.h" #include "dce2_event.h" #include "dce2_debug.h" #include "dcerpc.h" #include "profiler.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" /******************************************************************** * Macros ********************************************************************/ #define DCE2_CO__MIN_ALLOC_SIZE 50 #define DCE2_MAX_XMIT_SIZE_FUZZ 500 /******************************************************************** * Global variables ********************************************************************/ static int co_reassembled = 0; /******************************************************************** * Enumerations ********************************************************************/ typedef enum _DCE2_CoRpktType { DCE2_CO_RPKT_TYPE__SEG, DCE2_CO_RPKT_TYPE__FRAG, DCE2_CO_RPKT_TYPE__ALL } DCE2_CoRpktType; typedef enum _DCE2_CoCtxState { DCE2_CO_CTX_STATE__ACCEPTED, DCE2_CO_CTX_STATE__REJECTED, DCE2_CO_CTX_STATE__PENDING } DCE2_CoCtxState; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_CoCtxIdNode { uint16_t ctx_id; /* The context id */ Uuid iface; /* The presentation syntax uuid for the interface */ uint16_t iface_vers_maj; /* The major version of the interface */ uint16_t iface_vers_min; /* The minor version of the interface */ /* Whether or not the server accepted or rejected the client bind/alter context * request. Initially set to pending until server response */ DCE2_CoCtxState state; } DCE2_CoCtxIdNode; /******************************************************************** * Private function prototypes ********************************************************************/ static DCE2_Ret DCE2_CoHdrChecks(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *); static void DCE2_CoDecode(DCE2_SsnData *, DCE2_CoTracker *, const uint8_t *, uint16_t); static void DCE2_CoSegDecode(DCE2_SsnData *, DCE2_CoTracker *, DCE2_CoSeg *); static void DCE2_CoBind(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *, const uint8_t *, uint16_t); static void DCE2_CoAlterCtx(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *, const uint8_t *, uint16_t); static void DCE2_CoCtxReq(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *, const uint8_t, const uint8_t *, uint16_t); static void DCE2_CoBindAck(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *, const uint8_t *, uint16_t); static void DCE2_CoRequest(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *, const uint8_t *, uint16_t); static void DCE2_CoResponse(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *, const uint8_t *, uint16_t); static void DCE2_CoHandleFrag(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *, const uint8_t *, uint16_t); static inline DCE2_Ret DCE2_CoHandleSegmentation(DCE2_CoSeg *, const uint8_t *, uint16_t, uint16_t, uint16_t *); static void DCE2_CoReassemble(DCE2_SsnData *, DCE2_CoTracker *, DCE2_CoRpktType); static inline void DCE2_CoFragReassemble(DCE2_SsnData *, DCE2_CoTracker *); static DCE2_Ret DCE2_CoSetIface(DCE2_SsnData *, DCE2_CoTracker *, uint16_t); static int DCE2_CoCtxCompare(const void *, const void *); static void DCE2_CoCtxFree(void *); static inline void DCE2_CoSetRopts(DCE2_SsnData *, DCE2_CoTracker *, const DceRpcCoHdr *); static inline void DCE2_CoSetRdata(DCE2_SsnData *, DCE2_CoTracker *, uint8_t *, uint16_t); static inline void DCE2_CoResetFragTracker(DCE2_CoFragTracker *); static inline void DCE2_CoResetTracker(DCE2_CoTracker *); static inline DCE2_Ret DCE2_CoInitCtxStorage(DCE2_CoTracker *); static inline void DCE2_CoEraseCtxIds(DCE2_CoTracker *); static inline void DCE2_CoSegAlert(DCE2_SsnData *, DCE2_CoTracker *, DCE2_Event); static inline SFSnortPacket * DCE2_CoGetSegRpkt(DCE2_SsnData *, const uint8_t *, uint32_t); static inline DCE2_RpktType DCE2_CoGetRpktType(DCE2_SsnData *, DCE2_BufType); static SFSnortPacket * DCE2_CoGetRpkt(DCE2_SsnData *, DCE2_CoTracker *, DCE2_CoRpktType, DCE2_RpktType *); static inline DCE2_CoSeg * DCE2_CoGetSegPtr(DCE2_SsnData *, DCE2_CoTracker *); static inline DCE2_Buffer * DCE2_CoGetFragBuf(DCE2_SsnData *, DCE2_CoFragTracker *); static inline int DCE2_CoIsSegBuf(DCE2_SsnData *, DCE2_CoTracker *, const uint8_t *); static void DCE2_CoEarlyReassemble(DCE2_SsnData *, DCE2_CoTracker *); static DCE2_Ret DCE2_CoSegEarlyRequest(DCE2_CoTracker *, const uint8_t *, uint32_t); static int DCE2_CoGetAuthLen(DCE2_SsnData *, const DceRpcCoHdr *, const uint8_t *, uint16_t); /******************************************************************** * Function: DCE2_CoInitRdata() * * Initializes header of defragmentation reassembly packet. * Sets relevant fields in header that will not have to change * from reassembly to reassembly. The reassembly buffer used is * big enough for the header. * * Arguments: * uint8_t * * Pointer to the place in the reassembly packet to set * the header data. * * Returns: None * ********************************************************************/ void DCE2_CoInitRdata(uint8_t *co_ptr, int dir) { DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)co_ptr; /* Set some relevant fields. These should never get reset */ co_hdr->pversion.major = DCERPC_PROTO_MAJOR_VERS__5; co_hdr->pfc_flags = (DCERPC_CO_PFC_FLAGS__FIRST_FRAG | DCERPC_CO_PFC_FLAGS__LAST_FRAG); co_hdr->packed_drep[0] = 0x10; /* Little endian */ if (dir == FLAG_FROM_CLIENT) co_hdr->ptype = DCERPC_PDU_TYPE__REQUEST; else co_hdr->ptype = DCERPC_PDU_TYPE__RESPONSE; } /******************************************************************** * Function: DCE2_CoSetRdata() * * Sets relevant fields in the defragmentation reassembly packet * based on data gathered from the session and reassembly phase. * The reassembly buffer used is big enough for the headers. * * Arguments: * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * uint8_t * * Pointer to the place in the reassembly packet where the * header starts. * uint16_t * The length of the stub data. * * Returns: None * ********************************************************************/ static inline void DCE2_CoSetRdata(DCE2_SsnData *sd, DCE2_CoTracker *cot, uint8_t *co_ptr, uint16_t stub_len) { DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)co_ptr; /* If we've set the fragment tracker context id or opnum, use them. */ uint16_t ctx_id = (cot->frag_tracker.ctx_id != DCE2_SENTINEL) ? (uint16_t)cot->frag_tracker.ctx_id : (uint16_t)cot->ctx_id; uint16_t opnum = (cot->frag_tracker.opnum != DCE2_SENTINEL) ? (uint16_t)cot->frag_tracker.opnum : (uint16_t)cot->opnum; if (DCE2_SsnFromClient(sd->wire_pkt)) { DceRpcCoRequest *co_req = (DceRpcCoRequest *)((uint8_t *)co_hdr + sizeof(DceRpcCoHdr)); /* Doesn't really matter if this wraps ... it is basically just for presentation */ uint16_t flen = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest) + stub_len; co_hdr->frag_length = DceRpcHtons(&flen, DCERPC_BO_FLAG__LITTLE_ENDIAN); co_req->context_id = DceRpcHtons(&ctx_id, DCERPC_BO_FLAG__LITTLE_ENDIAN); co_req->opnum = DceRpcHtons(&opnum, DCERPC_BO_FLAG__LITTLE_ENDIAN); } else { DceRpcCoResponse *co_resp = (DceRpcCoResponse *)((uint8_t *)co_hdr + sizeof(DceRpcCoHdr)); uint16_t flen = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoResponse) + stub_len; co_hdr->frag_length = DceRpcHtons(&flen, DCERPC_BO_FLAG__LITTLE_ENDIAN); co_resp->context_id = DceRpcHtons(&ctx_id, DCERPC_BO_FLAG__LITTLE_ENDIAN); } } /******************************************************************** * Function: DCE2_CoProcess() * * Main entry point for connection-oriented DCE/RPC processing. * Since there can be more than one DCE/RPC pdu in the packet, it * loops through the packet data until none is left. It handles * transport layer segmentation and buffers data until it gets the * full pdu, then hands off to pdu processing. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * const uint8_t * * Pointer to packet data * uint16_t * Packet data length * * Returns: None * ********************************************************************/ void DCE2_CoProcess(DCE2_SsnData *sd, DCE2_CoTracker *cot, const uint8_t *data_ptr, uint16_t data_len) { DCE2_CoSeg *seg = DCE2_CoGetSegPtr(sd, cot); DCE2_Ret status; uint32_t num_frags = 0; dce2_stats.co_pdus++; co_reassembled = 0; while (data_len > 0) { num_frags++; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "DCE/RPC message number: %u\n", num_frags)); /* Fast track full fragments */ if (DCE2_BufferIsEmpty(seg->buf)) { const uint8_t *frag_ptr = data_ptr; uint16_t frag_len; uint16_t data_used; /* Not enough data left for a header. Buffer it and return */ if (data_len < sizeof(DceRpcCoHdr)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Not enough data in packet for DCE/RPC Connection-oriented header.\n")); DCE2_CoHandleSegmentation(seg, data_ptr, data_len, sizeof(DceRpcCoHdr), &data_used); /* Just break out of loop in case early detect is enabled */ break; } if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr *)data_ptr) != DCE2_RET__SUCCESS) return; frag_len = DceRpcCoFragLen((DceRpcCoHdr *)data_ptr); /* Not enough data left for the pdu. */ if (data_len < frag_len) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Not enough data in packet for fragment length: %u\n", frag_len)); /* Set frag length so we don't have to check it again in seg code */ seg->frag_len = frag_len; DCE2_CoHandleSegmentation(seg, data_ptr, data_len, frag_len, &data_used); break; } DCE2_MOVE(data_ptr, data_len, frag_len); /* Got a full DCE/RPC pdu */ DCE2_CoDecode(sd, cot, frag_ptr, frag_len); /* If we're configured to do defragmentation only detect on first frag * since we'll detect on reassembled */ if (!DCE2_GcDceDefrag() || ((num_frags == 1) && !co_reassembled)) DCE2_Detect(sd); /* Reset if this is a last frag */ if (DceRpcCoLastFrag((DceRpcCoHdr *)frag_ptr)) num_frags = 0; } else /* We've already buffered data */ { uint16_t data_used = 0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Segmentation buffer has %u bytes\n", DCE2_BufferLength(seg->buf))); /* Need more data to get header */ if (DCE2_BufferLength(seg->buf) < sizeof(DceRpcCoHdr)) { status = DCE2_CoHandleSegmentation(seg, data_ptr, data_len, sizeof(DceRpcCoHdr), &data_used); /* Still not enough for header */ if (status != DCE2_RET__SUCCESS) break; /* Move the length of the amount of data we used to get header */ DCE2_MOVE(data_ptr, data_len, data_used); if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr *)DCE2_BufferData(seg->buf)) != DCE2_RET__SUCCESS) { int data_back; DCE2_BufferEmpty(seg->buf); /* Move back to original packet header */ data_back = -data_used; DCE2_MOVE(data_ptr, data_len, data_back); /*Check the original packet*/ if (DCE2_CoHdrChecks(sd, cot, (DceRpcCoHdr *)data_ptr) != DCE2_RET__SUCCESS) return; else { /*Only use the original packet, ignore the data in seg_buffer*/ num_frags = 0; continue; } } seg->frag_len = DceRpcCoFragLen((DceRpcCoHdr *)DCE2_BufferData(seg->buf)); } /* Need more data for full pdu */ if (DCE2_BufferLength(seg->buf) < seg->frag_len) { status = DCE2_CoHandleSegmentation(seg, data_ptr, data_len, seg->frag_len, &data_used); /* Still not enough */ if (status != DCE2_RET__SUCCESS) break; DCE2_MOVE(data_ptr, data_len, data_used); } /* Do this before calling DCE2_CoSegDecode since it will empty * seg buffer */ if (DceRpcCoLastFrag((DceRpcCoHdr *)seg->buf->data)) num_frags = 0; /* Got the full DCE/RPC pdu. Need to create new packet before decoding */ DCE2_CoSegDecode(sd, cot, seg); if ( !data_used ) break; } } if (DCE2_GcReassembleEarly() && !co_reassembled) DCE2_CoEarlyReassemble(sd, cot); } /******************************************************************** * Function: DCE2_CoHandleSegmentation() * * Wrapper around DCE2_HandleSegmentation() to allocate a new * buffer object if necessary. * * Arguments: * DCE2_CoSeg * * Pointer to a connection-oriented segmentation structure. * uint8_t * * Pointer to the current data cursor in packet. * uint16_t * Length of data from current data cursor. * uint16_t * Length of data that we need in order to consider * desegmentation complete. * uint16_t * * Pointer to basically a return value for the amount of * data in the packet that was actually used for * desegmentation. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if an error occured. Nothing can * be trusted. * DCE2_RET__SEG if there is still more desegmentation * to go, i.e. the need length has not been met by * the data length. * DCE2_RET__SUCCESS if desegmentation is complete, * i.e. the need length was met. * ********************************************************************/ static inline DCE2_Ret DCE2_CoHandleSegmentation(DCE2_CoSeg *seg, const uint8_t *data_ptr, uint16_t data_len, uint16_t need_len, uint16_t *data_used) { DCE2_Ret status; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_co_seg); if (seg == NULL) { PREPROC_PROFILE_END(dce2_pstat_co_seg); return DCE2_RET__ERROR; } if (seg->buf == NULL) { seg->buf = DCE2_BufferNew(need_len, DCE2_CO__MIN_ALLOC_SIZE, DCE2_MEM_TYPE__CO_SEG); if (seg->buf == NULL) { PREPROC_PROFILE_END(dce2_pstat_co_seg); return DCE2_RET__ERROR; } } status = DCE2_HandleSegmentation(seg->buf, data_ptr, data_len, need_len, data_used); PREPROC_PROFILE_END(dce2_pstat_co_seg); return status; } /******************************************************************** * Function: DCE2_CoHdrChecks() * * Checks some relevant fields in the header to make sure they're * sane. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the header struct layed over the packet data. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if we should not continue processing. * DCE2_RET__SUCCESS if we should continue processing. * ********************************************************************/ static DCE2_Ret DCE2_CoHdrChecks(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr) { uint16_t frag_len = DceRpcCoFragLen(co_hdr); DceRpcPduType pdu_type = DceRpcCoPduType(co_hdr); int is_seg_buf = DCE2_CoIsSegBuf(sd, cot, (uint8_t *)co_hdr); if (frag_len < sizeof(DceRpcCoHdr)) { /* Assume we autodetected incorrectly or that DCE/RPC is not running * over the SMB named pipe */ if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB)) { if (is_seg_buf) DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_FLEN_LT_HDR); else DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_HDR, frag_len, sizeof(DceRpcCoHdr)); } return DCE2_RET__ERROR; } if (DceRpcCoVersMaj(co_hdr) != DCERPC_PROTO_MAJOR_VERS__5) { if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB)) { if (is_seg_buf) DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_BAD_MAJ_VERSION); else DCE2_Alert(sd, DCE2_EVENT__CO_BAD_MAJ_VERSION, DceRpcCoVersMaj(co_hdr)); } return DCE2_RET__ERROR; } if (DceRpcCoVersMin(co_hdr) != DCERPC_PROTO_MINOR_VERS__0) { if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB)) { if (is_seg_buf) DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_BAD_MIN_VERSION); else DCE2_Alert(sd, DCE2_EVENT__CO_BAD_MIN_VERSION, DceRpcCoVersMin(co_hdr)); } return DCE2_RET__ERROR; } if (pdu_type >= DCERPC_PDU_TYPE__MAX) { if (!DCE2_SsnAutodetected(sd) && (sd->trans != DCE2_TRANS_TYPE__SMB)) { if (is_seg_buf) DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_BAD_PDU_TYPE); else DCE2_Alert(sd, DCE2_EVENT__CO_BAD_PDU_TYPE, DceRpcCoPduType(co_hdr)); } return DCE2_RET__ERROR; } if (DCE2_SsnFromClient(sd->wire_pkt) && (cot->max_xmit_frag != DCE2_SENTINEL)) { if (frag_len > cot->max_xmit_frag) { if (is_seg_buf) DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG); else DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG, dce2_pdu_types[pdu_type], frag_len, cot->max_xmit_frag); } else if (!DceRpcCoLastFrag(co_hdr) && (pdu_type == DCERPC_PDU_TYPE__REQUEST) && ((((int)cot->max_xmit_frag - DCE2_MAX_XMIT_SIZE_FUZZ) < 0) || ((int)frag_len < ((int)cot->max_xmit_frag - DCE2_MAX_XMIT_SIZE_FUZZ)))) { /* If client needs to fragment the DCE/RPC request, it shouldn't be less than the * maximum xmit size negotiated. Only if it's not a last fragment. Make this alert * only if it is considerably less - have seen legitimate fragments that are just * slightly less the negotiated fragment size. */ if (is_seg_buf) DCE2_CoSegAlert(sd, cot, DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG); else DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG, dce2_pdu_types[pdu_type], frag_len, cot->max_xmit_frag); } /* Continue processing */ } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_CoDecode() * * Main processing for the DCE/RPC pdu types. Most are not * implemented as, currently, they are not necessary and only * stats are kept for them. Important are the bind, alter context * and request. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * const uint8_t * * Pointer to the start of the DCE/RPC pdu in the packet data. * uint16_t * Fragment length of the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoDecode(DCE2_SsnData *sd, DCE2_CoTracker *cot, const uint8_t *frag_ptr, uint16_t frag_len) { /* Already checked that we have enough data for header */ const DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)frag_ptr; int pdu_type = DceRpcCoPduType(co_hdr); /* We've got the main header. Move past it to the * start of the pdu */ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoHdr)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "PDU type: ")); /* Client specific pdu types - some overlap with server */ if (DCE2_SsnFromClient(sd->wire_pkt)) { switch (pdu_type) { case DCERPC_PDU_TYPE__BIND: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Bind\n")); dce2_stats.co_bind++; /* Make sure context id list and queue are initialized */ if (DCE2_CoInitCtxStorage(cot) != DCE2_RET__SUCCESS) return; DCE2_CoBind(sd, cot, co_hdr, frag_ptr, frag_len); break; case DCERPC_PDU_TYPE__ALTER_CONTEXT: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Alter Context\n")); dce2_stats.co_alter_ctx++; if (DCE2_CoInitCtxStorage(cot) != DCE2_RET__SUCCESS) return; DCE2_CoAlterCtx(sd, cot, co_hdr, frag_ptr, frag_len); break; case DCERPC_PDU_TYPE__REQUEST: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Request\n")); dce2_stats.co_request++; if (DCE2_ListIsEmpty(cot->ctx_ids) && DCE2_QueueIsEmpty(cot->pending_ctx_ids)) { return; } DCE2_CoRequest(sd, cot, co_hdr, frag_ptr, frag_len); break; case DCERPC_PDU_TYPE__AUTH3: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Auth3\n")); dce2_stats.co_auth3++; break; case DCERPC_PDU_TYPE__CO_CANCEL: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Cancel\n")); dce2_stats.co_cancel++; break; case DCERPC_PDU_TYPE__ORPHANED: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Orphaned\n")); dce2_stats.co_orphaned++; break; case DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Microsoft Request To Send RPC over HTTP\n")); dce2_stats.co_ms_pdu++; break; default: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Unknown (0x%02x)\n", pdu_type)); dce2_stats.co_other_req++; break; } } else { switch (pdu_type) { case DCERPC_PDU_TYPE__BIND_ACK: case DCERPC_PDU_TYPE__ALTER_CONTEXT_RESP: if (pdu_type == DCERPC_PDU_TYPE__BIND_ACK) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Bind Ack\n")); dce2_stats.co_bind_ack++; } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Alter Context Response\n")); dce2_stats.co_alter_ctx_resp++; } if (DCE2_QueueIsEmpty(cot->pending_ctx_ids)) return; /* Bind ack and alter context response have the same * header structure, just different pdu type */ DCE2_CoBindAck(sd, cot, co_hdr, frag_ptr, frag_len); /* Got the bind/alter response - clear out the pending queue */ DCE2_QueueEmpty(cot->pending_ctx_ids); break; case DCERPC_PDU_TYPE__BIND_NACK: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Bind Nack\n")); dce2_stats.co_bind_nack++; /* Bind nack in Windows seems to blow any previous context away */ switch (DCE2_SsnGetServerPolicy(sd)) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: DCE2_CoEraseCtxIds(cot); break; default: break; } cot->got_bind = 0; break; case DCERPC_PDU_TYPE__RESPONSE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Response\n")); dce2_stats.co_response++; DCE2_CoResponse(sd, cot, co_hdr, frag_ptr, frag_len); break; case DCERPC_PDU_TYPE__FAULT: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Fault\n")); dce2_stats.co_fault++; /* Clear out the client side */ DCE2_QueueEmpty(cot->pending_ctx_ids); DCE2_BufferEmpty(cot->cli_seg.buf); DCE2_BufferEmpty(cot->frag_tracker.cli_stub_buf); DCE2_CoResetTracker(cot); break; case DCERPC_PDU_TYPE__SHUTDOWN: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Shutdown\n")); dce2_stats.co_shutdown++; break; case DCERPC_PDU_TYPE__REJECT: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Reject\n")); dce2_stats.co_reject++; DCE2_QueueEmpty(cot->pending_ctx_ids); break; case DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Microsoft Request To Send RPC over HTTP\n")); dce2_stats.co_ms_pdu++; break; default: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Unknown (0x%02x)\n", pdu_type)); dce2_stats.co_other_resp++; break; } } } /******************************************************************** * Function: DCE2_CoBind() * * Handles the processing of a client bind request. There are * differences between Windows and Samba and even early Samba in * how multiple binds on the session are handled. Processing of * the context id bindings is handed off. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the main header in the packet data. * const uint8_t * * Pointer to the current processing point of the DCE/RPC * pdu in the packet data. * uint16_t * Fragment length left in the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoBind(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len) { DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); DceRpcCoBind *bind = (DceRpcCoBind *)frag_ptr; if (frag_len < sizeof(DceRpcCoBind)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, sizeof(DceRpcCoBind)); return; } DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoBind)); switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: /* Windows will not accept more than one bind */ if (!DCE2_ListIsEmpty(cot->ctx_ids)) { /* Delete context id list if anything there */ DCE2_CoEraseCtxIds(cot); return; } /* Byte order of stub data will be that of the bind */ cot->data_byte_order = DceRpcCoByteOrder(co_hdr); break; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: if (cot->got_bind) return; break; case DCE2_POLICY__SAMBA_3_0_20: /* Accepts multiple binds */ break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); return; } cot->max_xmit_frag = (int)DceRpcCoBindMaxXmitFrag(co_hdr, bind); DCE2_CoCtxReq(sd, cot, co_hdr, DceRpcCoNumCtxItems(bind), frag_ptr, frag_len); } /******************************************************************** * Function: DCE2_CoAlterCtx() * * Handles the processing of a client alter context request. * Again, differences in how this is handled - whether we've seen * a bind yet or not, altering the data byte order. Processing * of the context id bindings is handed off. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the main header in the packet data. * const uint8_t * * Pointer to the current processing point of the DCE/RPC * pdu in the packet data. * uint16_t * Fragment length left in the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoAlterCtx(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len) { DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); DceRpcCoAltCtx *alt_ctx = (DceRpcCoAltCtx *)frag_ptr; if (frag_len < sizeof(DceRpcCoAltCtx)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, sizeof(DceRpcCoAltCtx)); return; } DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoAltCtx)); switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: /* Windows will not accept an alter context before * bind and will bind_nak it */ if (DCE2_ListIsEmpty(cot->ctx_ids)) return; if (cot->data_byte_order != (int)DceRpcCoByteOrder(co_hdr)) { /* This is anomalous behavior. Alert, but continue processing */ if (cot->data_byte_order != DCE2_SENTINEL) DCE2_Alert(sd, DCE2_EVENT__CO_ALTER_CHANGE_BYTE_ORDER); } break; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: /* Nothing for Samba */ break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } /* Alter context is typedef'ed as a bind */ DCE2_CoCtxReq(sd, cot, co_hdr, DceRpcCoNumCtxItems((DceRpcCoBind *)alt_ctx), frag_ptr, frag_len); } /******************************************************************** * Function: DCE2_CoCtxReq() * * Handles parsing the context id list out of the packet. * Context ids and associated uuids are stored in a queue and * dequeued upon server response. Server response doesn't * indicate by context id which bindings were accepted or * rejected, but the index or order they were in in the client * bind or alter context, hence the queue. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the main header in the packet data. * const uint8_t * The number of context items in the bind or alter context. * const uint8_t * * Pointer to the current processing point of the DCE/RPC * pdu in the packet data. * uint16_t * Fragment length left in the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoCtxReq(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr, const uint8_t num_ctx_items, const uint8_t *frag_ptr, uint16_t frag_len) { DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); unsigned int i; DCE2_Ret status; if (num_ctx_items == 0) { DCE2_Alert(sd, DCE2_EVENT__CO_ZERO_CTX_ITEMS, dce2_pdu_types[DceRpcCoPduType(co_hdr)]); return; } for (i = 0; i < num_ctx_items; i++) { DceRpcCoContElem *ctx_elem = (DceRpcCoContElem *)frag_ptr; uint16_t ctx_id; uint8_t num_tsyns; const Uuid *iface; uint16_t if_vers_maj; uint16_t if_vers_min; DCE2_CoCtxIdNode *ctx_node; int j; PROFILE_VARS; if (frag_len < sizeof(DceRpcCoContElem)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, sizeof(DceRpcCoContElem)); return; } ctx_id = DceRpcCoContElemCtxId(co_hdr, ctx_elem); num_tsyns = DceRpcCoContElemNumTransSyntaxes(ctx_elem); iface = DceRpcCoContElemIface(ctx_elem); if_vers_maj = DceRpcCoContElemIfaceVersMaj(co_hdr, ctx_elem); if_vers_min = DceRpcCoContElemIfaceVersMin(co_hdr, ctx_elem); /* No transfer syntaxes */ if (num_tsyns == 0) { DCE2_Alert(sd, DCE2_EVENT__CO_ZERO_TSYNS, dce2_pdu_types[DceRpcCoPduType(co_hdr)]); return; } DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoContElem)); /* Don't really care about the transfer syntaxes */ for (j = 0; j < num_tsyns; j++) { if (frag_len < sizeof(DceRpcCoSynId)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, sizeof(DceRpcCoSynId)); return; } DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoSynId)); } PREPROC_PROFILE_START(dce2_pstat_co_ctx); /* If there is already an accepted node with in the list * with this ctx, just return */ if (policy == DCE2_POLICY__SAMBA_3_0_20) { ctx_node = DCE2_ListFind(cot->ctx_ids, (void *)(uintptr_t)ctx_id); if ((ctx_node != NULL) && (ctx_node->state != DCE2_CO_CTX_STATE__REJECTED)) { PREPROC_PROFILE_END(dce2_pstat_co_ctx); return; } } ctx_node = (DCE2_CoCtxIdNode *)DCE2_Alloc(sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX); if (ctx_node == NULL) { PREPROC_PROFILE_END(dce2_pstat_co_ctx); return; } /* Add context id to pending queue */ status = DCE2_QueueEnqueue(cot->pending_ctx_ids, ctx_node); if (status != DCE2_RET__SUCCESS) { DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX); PREPROC_PROFILE_END(dce2_pstat_co_ctx); return; } /* This node will get moved to the context id list upon server response */ ctx_node->ctx_id = ctx_id; DCE2_CopyUuid(&ctx_node->iface, iface, DceRpcCoByteOrder(co_hdr)); ctx_node->iface_vers_maj = if_vers_maj; ctx_node->iface_vers_min = if_vers_min; ctx_node->state = DCE2_CO_CTX_STATE__PENDING; PREPROC_PROFILE_END(dce2_pstat_co_ctx); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Added Context item to queue.\n" " Context id: %u\n" " Interface: %s\n" " Interface major version: %u\n" " Interface minor version: %u\n", ctx_node->ctx_id, DCE2_UuidToStr(&ctx_node->iface, DCERPC_BO_FLAG__NONE), ctx_node->iface_vers_maj, ctx_node->iface_vers_min)); switch (policy) { case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: /* Samba only ever looks at one context item. Not sure * if this is an alertable offense */ return; default: break; } } } /******************************************************************** * Function: DCE2_CoBindAck() * * Handles the processing of a server bind ack or a server alter * context response since they share the same header. * Moves context id items from the pending queue into a list * ultimately used by the rule options and sets each context item * as accepted or rejected based on the server response. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the main header in the packet data. * const uint8_t * * Pointer to the current processing point of the DCE/RPC * pdu in the packet data. * uint16_t * Fragment length left in the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoBindAck(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len) { DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); DceRpcCoBindAck *bind_ack = (DceRpcCoBindAck *)frag_ptr; uint16_t sec_addr_len; const uint8_t *ctx_data; uint16_t ctx_len; uint16_t pad = 0; DceRpcCoContResultList *ctx_list; uint8_t num_ctx_results; unsigned int i; uint16_t max_recv_frag; DCE2_Ret status; if (frag_len < sizeof(DceRpcCoBindAck)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, sizeof(DceRpcCoBindAck)); return; } DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoBindAck)); /* Set what should be the maximum amount of data a client can send in a fragment */ max_recv_frag = DceRpcCoBindAckMaxRecvFrag(co_hdr, bind_ack); if ((cot->max_xmit_frag == DCE2_SENTINEL) || (max_recv_frag < cot->max_xmit_frag)) cot->max_xmit_frag = (int)max_recv_frag; sec_addr_len = DceRpcCoSecAddrLen(co_hdr, bind_ack); ctx_data = frag_ptr; ctx_len = frag_len; /* First move past secondary address */ if (ctx_len < sec_addr_len) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], ctx_len, sec_addr_len); return; } DCE2_MOVE(ctx_data, ctx_len, sec_addr_len); /* padded to 4 octet */ if ((sizeof(DceRpcCoBindAck) + sec_addr_len) & 3) pad = (4 - ((sizeof(DceRpcCoBindAck) + sec_addr_len) & 3)); if (ctx_len < pad) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], ctx_len, pad); return; } DCE2_MOVE(ctx_data, ctx_len, pad); /* Now we're at the start of the context item results */ if (ctx_len < sizeof(DceRpcCoContResultList)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], ctx_len, sizeof(DceRpcCoContResultList)); return; } ctx_list = (DceRpcCoContResultList *)ctx_data; num_ctx_results = DceRpcCoContNumResults(ctx_list); DCE2_MOVE(ctx_data, ctx_len, sizeof(DceRpcCoContResultList)); for (i = 0; i < num_ctx_results; i++) { DceRpcCoContResult *ctx_result; uint16_t result; DCE2_CoCtxIdNode *ctx_node, *existing_ctx_node; PROFILE_VARS; if (ctx_len < sizeof(DceRpcCoContResult)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], ctx_len, sizeof(DceRpcCoContResult)); return; } ctx_result = (DceRpcCoContResult *)ctx_data; result = DceRpcCoContRes(co_hdr, ctx_result); DCE2_MOVE(ctx_data, ctx_len, sizeof(DceRpcCoContResult)); if (DCE2_QueueIsEmpty(cot->pending_ctx_ids)) return; PREPROC_PROFILE_START(dce2_pstat_co_ctx); /* Dequeue context item in pending queue - this will get put in the permanent * context id list or free'd */ ctx_node = (DCE2_CoCtxIdNode *)DCE2_QueueDequeue(cot->pending_ctx_ids); if (ctx_node == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to dequeue a context id node.", __FILE__, __LINE__); PREPROC_PROFILE_END(dce2_pstat_co_ctx); return; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Adding Context item to context item list.\n" " Context id: %u\n" " Interface: %s\n" " Interface major version: %u\n" " Interface minor version: %u\n", ctx_node->ctx_id, DCE2_UuidToStr(&ctx_node->iface, DCERPC_BO_FLAG__NONE), ctx_node->iface_vers_maj, ctx_node->iface_vers_min)); if (result == DCERPC_CO_CONT_DEF_RESULT__ACCEPTANCE) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Server accepted context item.\n")); ctx_node->state = DCE2_CO_CTX_STATE__ACCEPTED; if (DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__BIND_ACK) cot->got_bind = 1; } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Server rejected context item.\n")); ctx_node->state = DCE2_CO_CTX_STATE__REJECTED; cot->got_bind = 0; } existing_ctx_node = (DCE2_CoCtxIdNode *)DCE2_ListFind(cot->ctx_ids, (void *)(uintptr_t)ctx_node->ctx_id); if (existing_ctx_node != NULL) { switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: if (ctx_node->state == DCE2_CO_CTX_STATE__REJECTED) break; if (existing_ctx_node->state == DCE2_CO_CTX_STATE__REJECTED) { existing_ctx_node->ctx_id = ctx_node->ctx_id; DCE2_CopyUuid(&existing_ctx_node->iface, &ctx_node->iface, DCERPC_BO_FLAG__NONE); existing_ctx_node->iface_vers_maj = ctx_node->iface_vers_maj; existing_ctx_node->iface_vers_min = ctx_node->iface_vers_min; existing_ctx_node->state = ctx_node->state; } break; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: /* Samba actually alters the context. Windows keeps the old */ if (ctx_node->state != DCE2_CO_CTX_STATE__REJECTED) { existing_ctx_node->ctx_id = ctx_node->ctx_id; DCE2_CopyUuid(&existing_ctx_node->iface, &ctx_node->iface, DCERPC_BO_FLAG__NONE); existing_ctx_node->iface_vers_maj = ctx_node->iface_vers_maj; existing_ctx_node->iface_vers_min = ctx_node->iface_vers_min; existing_ctx_node->state = ctx_node->state; } break; default: break; } DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX); } else { status = DCE2_ListInsert(cot->ctx_ids, (void *)(uintptr_t)ctx_node->ctx_id, (void *)ctx_node); if (status != DCE2_RET__SUCCESS) { /* Hit memcap */ DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Failed to add context id node to list.\n")); PREPROC_PROFILE_END(dce2_pstat_co_ctx); return; } } PREPROC_PROFILE_END(dce2_pstat_co_ctx); } } /******************************************************************** * Function: DCE2_CoRequest() * * Handles a DCE/RPC request from the client. This is were the * client actually asks the server to do stuff on it's behalf. * If it's a first/last fragment, set relevant rule option * data and return. If it's a true fragment, do some target * based futzing to set the right opnum and context id for * the to be reassembled packet. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the main header in the packet data. * const uint8_t * * Pointer to the current processing point of the DCE/RPC * pdu in the packet data. * uint16_t * Fragment length left in the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoRequest(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len) { DceRpcCoRequest *rhdr = (DceRpcCoRequest *)frag_ptr; uint16_t req_size = sizeof(DceRpcCoRequest); DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); /* Account for possible object uuid */ if (DceRpcCoObjectFlag(co_hdr)) req_size += sizeof(Uuid); if (frag_len < req_size) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, req_size); return; } switch (policy) { /* After 3.0.37 up to 3.5.2 byte order of stub data is always * interpreted as little endian */ case DCE2_POLICY__SAMBA: cot->data_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN; break; case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: cot->data_byte_order = DceRpcCoByteOrder(co_hdr); break; default: break; } /* Move past header */ DCE2_MOVE(frag_ptr, frag_len, req_size); /* If for some reason we had some fragments queued */ if (DceRpcCoFirstFrag(co_hdr) && !DceRpcCoLastFrag(co_hdr) && !DCE2_BufferIsEmpty(cot->frag_tracker.cli_stub_buf)) { DCE2_CoFragReassemble(sd, cot); DCE2_BufferEmpty(cot->frag_tracker.cli_stub_buf); DCE2_CoResetFragTracker(&cot->frag_tracker); } cot->stub_data = frag_ptr; cot->opnum = DceRpcCoOpnum(co_hdr, rhdr); cot->ctx_id = DceRpcCoCtxId(co_hdr, rhdr); cot->call_id = DceRpcCoCallId(co_hdr); if (DceRpcCoFirstFrag(co_hdr) && DceRpcCoLastFrag(co_hdr)) { int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "First and last fragment.\n")); if (auth_len == -1) return; DCE2_CoSetRopts(sd, cot, co_hdr); } else { DCE2_CoFragTracker *ft = &cot->frag_tracker; int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len); dce2_stats.co_req_fragments++; #ifdef DEBUG_MSGS DCE2_DEBUG_CODE(DCE2_DEBUG__CO, if (DceRpcCoFirstFrag(co_hdr)) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "First fragment.\n")); else if (DceRpcCoLastFrag(co_hdr)) DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Last fragment.\n")); else DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Middle fragment.\n")); DCE2_PrintPktData(frag_ptr, frag_len);); #endif if (auth_len == -1) return; if (DCE2_BufferIsEmpty(ft->cli_stub_buf)) { ft->expected_opnum = cot->opnum; ft->expected_ctx_id = cot->ctx_id; ft->expected_call_id = cot->call_id; } else { /* Don't return for these, because we can still process and servers * will still accept and deal with the anomalies in their own way */ if ((ft->expected_opnum != DCE2_SENTINEL) && (ft->expected_opnum != cot->opnum)) { DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_OPNUM, cot->opnum, ft->expected_opnum); } if ((ft->expected_ctx_id != DCE2_SENTINEL) && (ft->expected_ctx_id != cot->ctx_id)) { DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CTX_ID, cot->ctx_id, ft->expected_ctx_id); } if ((ft->expected_call_id != DCE2_SENTINEL) && (ft->expected_call_id != cot->call_id)) { DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CALL_ID, cot->call_id, ft->expected_call_id); } } /* Possibly set opnum in frag tracker */ switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: if (DceRpcCoLastFrag(co_hdr)) ft->opnum = cot->opnum; break; case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: if (DceRpcCoFirstFrag(co_hdr)) ft->opnum = cot->opnum; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } /* Possibly set context id in frag tracker */ switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: if (DceRpcCoFirstFrag(co_hdr)) { ft->ctx_id = cot->ctx_id; } else if ((ft->expected_call_id != DCE2_SENTINEL) && (ft->expected_call_id != cot->call_id)) { /* Server won't accept frag */ return; } break; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: if (DceRpcCoLastFrag(co_hdr)) { ft->ctx_id = cot->ctx_id; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } DCE2_CoSetRopts(sd, cot, co_hdr); /* If we're configured to do defragmentation */ if (DCE2_GcDceDefrag()) { /* Don't want to include authentication data in fragment */ DCE2_CoHandleFrag(sd, cot, co_hdr, frag_ptr, (uint16_t)(frag_len - (uint16_t)auth_len)); } } } /******************************************************************** * Function: DCE2_CoResponse() * * Handles a DCE/RPC response from the server. * Samba responds to SMB bind write, request write before read with * a response to the request and doesn't send a bind ack. Get the * context id from the pending context id list and put in stable * list. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the main header in the packet data. * const uint8_t * * Pointer to the current processing point of the DCE/RPC * pdu in the packet data. * uint16_t * Fragment length left in the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoResponse(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len) { DceRpcCoResponse *rhdr = (DceRpcCoResponse *)frag_ptr; uint16_t ctx_id; DCE2_Policy policy = DCE2_SsnGetServerPolicy(sd); if (frag_len < sizeof(DceRpcCoResponse)) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, sizeof(DceRpcCoResponse)); return; } switch (policy) { case DCE2_POLICY__SAMBA: cot->data_byte_order = DCERPC_BO_FLAG__LITTLE_ENDIAN; break; case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: cot->data_byte_order = DceRpcCoByteOrder(co_hdr); break; default: break; } ctx_id = DceRpcCoCtxIdResp(co_hdr, rhdr); /* If pending queue is not empty, add this context id as accepted and all * others as pending */ while (!DCE2_QueueIsEmpty(cot->pending_ctx_ids)) { DCE2_Ret status; DCE2_CoCtxIdNode *ctx_node = (DCE2_CoCtxIdNode *)DCE2_QueueDequeue(cot->pending_ctx_ids); if (ctx_node == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to dequeue a context id node.", __FILE__, __LINE__); return; } if (ctx_node->ctx_id == ctx_id) ctx_node->state = DCE2_CO_CTX_STATE__ACCEPTED; status = DCE2_ListInsert(cot->ctx_ids, (void *)(uintptr_t)ctx_node->ctx_id, (void *)ctx_node); if (status != DCE2_RET__SUCCESS) { /* Might be a duplicate in there already. If there is we would have used it * anyway before looking at the pending queue. Just get rid of it */ DCE2_Free((void *)ctx_node, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX); return; } } /* Move past header */ DCE2_MOVE(frag_ptr, frag_len, sizeof(DceRpcCoResponse)); /* If for some reason we had some fragments queued */ if (DceRpcCoFirstFrag(co_hdr) && !DCE2_BufferIsEmpty(cot->frag_tracker.srv_stub_buf)) { DCE2_CoFragReassemble(sd, cot); DCE2_BufferEmpty(cot->frag_tracker.srv_stub_buf); DCE2_CoResetFragTracker(&cot->frag_tracker); } cot->stub_data = frag_ptr; /* Opnum not in response header - have to use previous client's */ cot->ctx_id = ctx_id; cot->call_id = DceRpcCoCallId(co_hdr); if (DceRpcCoFirstFrag(co_hdr) && DceRpcCoLastFrag(co_hdr)) { int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "First and last fragment.\n")); if (auth_len == -1) return; DCE2_CoSetRopts(sd, cot, co_hdr); } else { //DCE2_CoFragTracker *ft = &cot->frag_tracker; int auth_len = DCE2_CoGetAuthLen(sd, co_hdr, frag_ptr, frag_len); dce2_stats.co_resp_fragments++; if (auth_len == -1) return; #if 0 /* TBD - Target based foo */ /* Don't return for these, because we can still process */ if ((ft->expected_opnum != DCE2_SENTINEL) && (ft->expected_opnum != cot->opnum)) { DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_OPNUM, cot->opnum, ft->expected_opnum); } if ((ft->expected_ctx_id != DCE2_SENTINEL) && (ft->expected_ctx_id != cot->ctx_id)) { DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CTX_ID, cot->ctx_id, ft->expected_ctx_id); } if ((ft->expected_call_id != DCE2_SENTINEL) && (ft->expected_call_id != cot->call_id)) { DCE2_Alert(sd, DCE2_EVENT__CO_FRAG_DIFF_CALL_ID, cot->call_id, ft->expected_call_id); } #endif DCE2_CoSetRopts(sd, cot, co_hdr); /* If we're configured to do defragmentation */ if (DCE2_GcDceDefrag()) { DCE2_CoHandleFrag(sd, cot, co_hdr, frag_ptr, (uint16_t)(frag_len - (uint16_t)auth_len)); } } } /******************************************************************** * Function: DCE2_CoHandleFrag() * * Handles adding a fragment to the defragmentation buffer. * Does overflow checking. Maximum length of fragmentation buffer * is based on the maximum packet length Snort can handle. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to the main header in the packet data. * const uint8_t * * Pointer to the current processing point of the DCE/RPC * pdu in the packet data. * uint16_t * Fragment length left in the pdu. * * Returns: None * ********************************************************************/ static void DCE2_CoHandleFrag(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len) { DCE2_Buffer *frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker); uint32_t size = (frag_len < DCE2_CO__MIN_ALLOC_SIZE) ? DCE2_CO__MIN_ALLOC_SIZE : frag_len; uint16_t max_frag_data; DCE2_BufferMinAddFlag mflag = DCE2_BUFFER_MIN_ADD_FLAG__USE; DCE2_Ret status; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_co_frag); if (DCE2_SsnFromClient(sd->wire_pkt)) { if (frag_len > dce2_stats.co_cli_max_frag_size) dce2_stats.co_cli_max_frag_size = frag_len; if (dce2_stats.co_cli_min_frag_size == 0 || frag_len < dce2_stats.co_cli_min_frag_size) dce2_stats.co_cli_min_frag_size = frag_len; } else { if (frag_len > dce2_stats.co_srv_max_frag_size) dce2_stats.co_srv_max_frag_size = frag_len; if (dce2_stats.co_srv_min_frag_size == 0 || frag_len < dce2_stats.co_srv_min_frag_size) dce2_stats.co_srv_min_frag_size = frag_len; } if (frag_buf == NULL) { if (DCE2_SsnFromServer(sd->wire_pkt)) { cot->frag_tracker.srv_stub_buf = DCE2_BufferNew(size, DCE2_CO__MIN_ALLOC_SIZE, DCE2_MEM_TYPE__CO_FRAG); frag_buf = cot->frag_tracker.srv_stub_buf; } else { cot->frag_tracker.cli_stub_buf = DCE2_BufferNew(size, DCE2_CO__MIN_ALLOC_SIZE, DCE2_MEM_TYPE__CO_FRAG); frag_buf = cot->frag_tracker.cli_stub_buf; } if (frag_buf == NULL) { PREPROC_PROFILE_START(dce2_pstat_co_frag); return; } } /* If there's already data in the buffer and this is a first frag * we probably missed packets */ if (DceRpcCoFirstFrag(co_hdr) && !DCE2_BufferIsEmpty(frag_buf)) { DCE2_CoResetFragTracker(&cot->frag_tracker); DCE2_BufferEmpty(frag_buf); } /* Check for potential overflow */ if (sd->trans == DCE2_TRANS_TYPE__SMB) max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__SMB_CO_FRAG); else max_frag_data = DCE2_GetRpktMaxData(sd, DCE2_RPKT_TYPE__TCP_CO_FRAG); if (DCE2_GcMaxFrag() && (frag_len > DCE2_GcMaxFragLen())) frag_len = DCE2_GcMaxFragLen(); if ((DCE2_BufferLength(frag_buf) + frag_len) > max_frag_data) frag_len = max_frag_data - (uint16_t)DCE2_BufferLength(frag_buf); if (frag_len != 0) { /* If it's the last fragment we're going to flush so just alloc * exactly what we need ... or if there is more data than can fit * in the reassembly buffer */ if (DceRpcCoLastFrag(co_hdr) || (DCE2_BufferLength(frag_buf) == max_frag_data)) mflag = DCE2_BUFFER_MIN_ADD_FLAG__IGNORE; status = DCE2_BufferAddData(frag_buf, frag_ptr, frag_len, DCE2_BufferLength(frag_buf), mflag); if (status != DCE2_RET__SUCCESS) { PREPROC_PROFILE_END(dce2_pstat_co_frag); /* Either hit memcap or a memcpy failed - reassemble */ DCE2_CoFragReassemble(sd, cot); DCE2_BufferEmpty(frag_buf); return; } } PREPROC_PROFILE_END(dce2_pstat_co_frag); /* Reassemble if we got a last frag ... */ if (DceRpcCoLastFrag(co_hdr)) { DCE2_CoFragReassemble(sd, cot); DCE2_BufferEmpty(frag_buf); /* Set this for the server response since response doesn't * contain client opnum used */ cot->opnum = cot->frag_tracker.opnum; DCE2_CoResetFragTracker(&cot->frag_tracker); /* Return early - rule opts will be set in reassembly handler */ return; } else if (DCE2_BufferLength(frag_buf) == max_frag_data) { /* ... or can't fit any more data in the buffer * Don't reset frag tracker */ DCE2_CoFragReassemble(sd, cot); DCE2_BufferEmpty(frag_buf); return; } } /******************************************************************** * Function: DCE2_CoFragReassemble() * * Wrapper for the generic reassembly function. Calls generic * reassembly function specifying that we want to do fragmentation * reassembly. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * * Returns: None * ********************************************************************/ static inline void DCE2_CoFragReassemble(DCE2_SsnData *sd, DCE2_CoTracker *cot) { DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__FRAG); } /******************************************************************** * Function: DCE2_CoReassemble() * * Gets a reassemly packet based on the transport and the type of * reassembly we want to do. Sets rule options and calls detect * on the reassembled packet. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DCE2_CoRpktType * Specifies whether we want to do segmenation, fragmentation * or fragmentation and segmentation reassembly. * * Returns: None * ********************************************************************/ static void DCE2_CoReassemble(DCE2_SsnData *sd, DCE2_CoTracker *cot, DCE2_CoRpktType co_rtype) { DCE2_RpktType rpkt_type; DceRpcCoHdr *co_hdr; SFSnortPacket *rpkt; int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : DCE2_MOCK_HDR_LEN__SMB_SRV; int co_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__CO_CLI : DCE2_MOCK_HDR_LEN__CO_SRV; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_co_reass); rpkt = DCE2_CoGetRpkt(sd, cot, co_rtype, &rpkt_type); if (rpkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Could not create DCE/RPC frag reassembled packet.\n", __FILE__, __LINE__); PREPROC_PROFILE_END(dce2_pstat_co_reass); return; } switch (rpkt_type) { case DCE2_RPKT_TYPE__SMB_CO_FRAG: case DCE2_RPKT_TYPE__SMB_CO_SEG: DCE2_SmbSetRdata((DCE2_SmbSsnData *)sd, (uint8_t *)rpkt->payload, (uint16_t)(rpkt->payload_size - smb_hdr_len)); if (rpkt_type == DCE2_RPKT_TYPE__SMB_CO_FRAG) { DCE2_CoSetRdata(sd, cot, (uint8_t *)rpkt->payload + smb_hdr_len, (uint16_t)(rpkt->payload_size - (smb_hdr_len + co_hdr_len))); if (DCE2_SsnFromClient(sd->wire_pkt)) dce2_stats.co_cli_frag_reassembled++; else dce2_stats.co_srv_frag_reassembled++; } else { if (DCE2_SsnFromClient(sd->wire_pkt)) dce2_stats.co_cli_seg_reassembled++; else dce2_stats.co_srv_seg_reassembled++; } co_hdr = (DceRpcCoHdr *)(rpkt->payload + smb_hdr_len); cot->stub_data = rpkt->payload + smb_hdr_len + co_hdr_len; break; case DCE2_RPKT_TYPE__TCP_CO_FRAG: case DCE2_RPKT_TYPE__TCP_CO_SEG: if (rpkt_type == DCE2_RPKT_TYPE__TCP_CO_FRAG) { DCE2_CoSetRdata(sd, cot, (uint8_t *)rpkt->payload, (uint16_t)(rpkt->payload_size - co_hdr_len)); if (DCE2_SsnFromClient(sd->wire_pkt)) dce2_stats.co_cli_frag_reassembled++; else dce2_stats.co_srv_frag_reassembled++; } else { if (DCE2_SsnFromClient(sd->wire_pkt)) dce2_stats.co_cli_seg_reassembled++; else dce2_stats.co_srv_seg_reassembled++; } co_hdr = (DceRpcCoHdr *)rpkt->payload; cot->stub_data = rpkt->payload + co_hdr_len; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid rpkt type: %d", __FILE__, __LINE__, rpkt_type); PREPROC_PROFILE_END(dce2_pstat_co_reass); return; } PREPROC_PROFILE_END(dce2_pstat_co_reass); /* Push packet onto stack */ if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to push packet onto packet stack.", __FILE__, __LINE__); return; } DCE2_CoSetRopts(sd, cot, co_hdr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Reassembled CO fragmented packet:\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__CO, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); DCE2_Detect(sd); DCE2_PopPkt(); co_reassembled = 1; } /******************************************************************** * Function: DCE2_CoSetIface() * * Sets the interface UUID for the rules options. Looks in the * context id list. If nothing found there, it looks in the pending * list (in case we never saw the server response because of * missed packets) to see if something is there. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * uint16_t * The context id to use for the lookup. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if the interface UUID could not be found * based on the context id passed in. * DCE2_RET__SUCESS if the interface UUID could be found and * the appropriate rule options could be set. * ********************************************************************/ static DCE2_Ret DCE2_CoSetIface(DCE2_SsnData *sd, DCE2_CoTracker *cot, uint16_t ctx_id) { DCE2_CoCtxIdNode *ctx_id_node; PROFILE_VARS; /* This should be set if we've gotten a Bind */ if (cot->ctx_ids == NULL) return DCE2_RET__ERROR; PREPROC_PROFILE_START(dce2_pstat_co_ctx); ctx_id_node = (DCE2_CoCtxIdNode *)DCE2_ListFind(cot->ctx_ids, (void *)(uintptr_t)ctx_id); if (ctx_id_node == NULL) /* context id not found in list */ { /* See if it's in the queue. An easy evasion would be to stagger the writes * and reads such that we see a request before seeing the server bind ack */ if (cot->pending_ctx_ids != NULL) { for (ctx_id_node = (DCE2_CoCtxIdNode *)DCE2_QueueFirst(cot->pending_ctx_ids); ctx_id_node != NULL; ctx_id_node = (DCE2_CoCtxIdNode *)DCE2_QueueNext(cot->pending_ctx_ids)) { if (ctx_id_node->ctx_id == ctx_id) break; } } if (ctx_id_node == NULL) { PREPROC_PROFILE_END(dce2_pstat_co_ctx); return DCE2_RET__ERROR; } } if (ctx_id_node->state == DCE2_CO_CTX_STATE__REJECTED) { PREPROC_PROFILE_END(dce2_pstat_co_ctx); return DCE2_RET__ERROR; } DCE2_CopyUuid(&sd->ropts.iface, &ctx_id_node->iface, DCERPC_BO_FLAG__NONE); sd->ropts.iface_vers_maj = ctx_id_node->iface_vers_maj; sd->ropts.iface_vers_min = ctx_id_node->iface_vers_min; PREPROC_PROFILE_END(dce2_pstat_co_ctx); return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_CoCtxCompare() * * Callback to context id list for finding the right interface * UUID node. Values passed in are context ids which are used as * the keys for the list. * * Arguments: * const void * * First context id to compare. * const void * * Second context id to compare. * * Returns: * int * 0 if first value equals second value * -1 if first value does not equal the second value * ********************************************************************/ static int DCE2_CoCtxCompare(const void *a, const void *b) { int x = (int)(uintptr_t)a; int y = (int)(uintptr_t)b; if (x == y) return 0; /* Only care about equality for finding */ return -1; } /******************************************************************** * Function: DCE2_CoCtxFree() * * Callback to context id list for freeing context id nodes in * the list. * * Arguments: * void * * Context id node to free. * * Returns: None * ********************************************************************/ static void DCE2_CoCtxFree(void *data) { if (data == NULL) return; DCE2_Free(data, sizeof(DCE2_CoCtxIdNode), DCE2_MEM_TYPE__CO_CTX); } /******************************************************************** * Function: DCE2_CoInitTracker() * * Initializes fields in the connection-oriented tracker to * sentinels. Many decisions are made based on whether or not * these fields have been set. * * Arguments: * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * * Returns: None * ********************************************************************/ void DCE2_CoInitTracker(DCE2_CoTracker *cot) { if (cot == NULL) return; cot->max_xmit_frag = DCE2_SENTINEL; cot->data_byte_order = DCE2_SENTINEL; cot->ctx_id = DCE2_SENTINEL; cot->opnum = DCE2_SENTINEL; cot->call_id = DCE2_SENTINEL; cot->stub_data = NULL; cot->got_bind = 0; cot->frag_tracker.opnum = DCE2_SENTINEL; cot->frag_tracker.ctx_id = DCE2_SENTINEL; cot->frag_tracker.expected_call_id = DCE2_SENTINEL; cot->frag_tracker.expected_opnum = DCE2_SENTINEL; cot->frag_tracker.expected_ctx_id = DCE2_SENTINEL; } /******************************************************************** * Function: DCE2_CoResetTracker() * * Resets frag tracker fields after having reassembled. * * Arguments: * DCE2_CoFragTracker * * Pointer to the relevant connection-oriented frag tracker. * * Returns: None * ********************************************************************/ static inline void DCE2_CoResetFragTracker(DCE2_CoFragTracker *ft) { if (ft == NULL) return; ft->opnum = DCE2_SENTINEL; ft->ctx_id = DCE2_SENTINEL; ft->expected_call_id = DCE2_SENTINEL; ft->expected_ctx_id = DCE2_SENTINEL; ft->expected_opnum = DCE2_SENTINEL; } /******************************************************************** * Function: DCE2_CoResetTracker() * * Resets fields that are transient for requests after the bind or * alter context. The context id and opnum are dependent on the * request and in the case of fragmented requests are set until all * fragments are received. If we got a full request or all of the * fragments, these should be reset. * * Arguments: * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * * Returns: None * ********************************************************************/ static inline void DCE2_CoResetTracker(DCE2_CoTracker *cot) { if (cot == NULL) return; cot->ctx_id = DCE2_SENTINEL; cot->opnum = DCE2_SENTINEL; cot->call_id = DCE2_SENTINEL; cot->stub_data = NULL; DCE2_CoResetFragTracker(&cot->frag_tracker); } /******************************************************************** * Function: DCE2_CoCleanTracker() * * Destroys all dynamically allocated data associated with * connection-oriented tracker. * * Arguments: * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * * Returns: None * ********************************************************************/ void DCE2_CoCleanTracker(DCE2_CoTracker *cot) { if (cot == NULL) return; DCE2_BufferDestroy(cot->frag_tracker.cli_stub_buf); cot->frag_tracker.cli_stub_buf = NULL; DCE2_BufferDestroy(cot->frag_tracker.srv_stub_buf); cot->frag_tracker.srv_stub_buf = NULL; DCE2_BufferDestroy(cot->cli_seg.buf); cot->cli_seg.buf = NULL; DCE2_BufferDestroy(cot->srv_seg.buf); cot->srv_seg.buf = NULL; DCE2_ListDestroy(cot->ctx_ids); cot->ctx_ids = NULL; DCE2_QueueDestroy(cot->pending_ctx_ids); cot->pending_ctx_ids = NULL; DCE2_CoInitTracker(cot); } /******************************************************************** * Function: DCE2_CoEraseCtxIds() * * Empties out the context id list and the pending context id * queue. Does not free the list and queue - might need to still * use them. * * Arguments: * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * * Returns: None * ********************************************************************/ static inline void DCE2_CoEraseCtxIds(DCE2_CoTracker *cot) { if (cot == NULL) return; DCE2_QueueEmpty(cot->pending_ctx_ids); DCE2_ListEmpty(cot->ctx_ids); } /******************************************************************** * Function: DCE2_CoSegAlert() * * We have to alert with the appropriate data so the alert actually * makes sense. In this case, the alert was generated based on * data in a segmentation buffer. We have to create a packet * using the segmentation buffer before actually alerting. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DCE2_Event * The event that was generated. * * Returns: None * ********************************************************************/ static inline void DCE2_CoSegAlert(DCE2_SsnData *sd, DCE2_CoTracker *cot, DCE2_Event event) { SFSnortPacket *rpkt; DCE2_Buffer *buf; DceRpcCoHdr *co_hdr; uint16_t frag_len; DceRpcPduType pdu_type; if (DCE2_SsnFromClient(sd->wire_pkt)) buf = cot->cli_seg.buf; else buf = cot->srv_seg.buf; /* This should be called from the desegmentation code after there is * enough data for a connection oriented header. All of the alerts * here require a header. */ if (DCE2_BufferIsEmpty(buf) || (DCE2_BufferLength(buf) < sizeof(DceRpcCoHdr))) { return; } rpkt = DCE2_CoGetSegRpkt(sd, DCE2_BufferData(buf), DCE2_BufferLength(buf)); if (rpkt == NULL) return; co_hdr = (DceRpcCoHdr *)DCE2_BufferData(buf); frag_len = DceRpcCoFragLen(co_hdr); pdu_type = DceRpcCoPduType(co_hdr); if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to push packet onto packet stack.", __FILE__, __LINE__); return; } switch (event) { case DCE2_EVENT__CO_FLEN_LT_HDR: DCE2_Alert(sd, event, frag_len, sizeof(DceRpcCoHdr)); break; case DCE2_EVENT__CO_BAD_MAJ_VERSION: DCE2_Alert(sd, event, DceRpcCoVersMaj(co_hdr)); break; case DCE2_EVENT__CO_BAD_MIN_VERSION: DCE2_Alert(sd, event, DceRpcCoVersMin(co_hdr)); break; case DCE2_EVENT__CO_BAD_PDU_TYPE: DCE2_Alert(sd, event, DceRpcCoPduType(co_hdr)); break; case DCE2_EVENT__CO_FRAG_GT_MAX_XMIT_FRAG: DCE2_Alert(sd, event, dce2_pdu_types[pdu_type], frag_len, cot->max_xmit_frag); break; case DCE2_EVENT__CO_FRAG_LT_MAX_XMIT_FRAG: DCE2_Alert(sd, event, dce2_pdu_types[pdu_type], frag_len, cot->max_xmit_frag); break; default: break; } DCE2_PopPkt(); } /******************************************************************** * Function: DCE2_CoGetSegRpkt() * * Gets and returns a reassembly packet based on a segmentation * buffer. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * const uint8_t * * Pointer to the start of data in the segmentation buffer. * uint32_t * The length of the data in the segmentation buffer. * * Returns: * SFSnortPacket * * A valid pointer to a reassembled packet on success. * NULL on error. * ********************************************************************/ static inline SFSnortPacket * DCE2_CoGetSegRpkt(DCE2_SsnData *sd, const uint8_t *data_ptr, uint32_t data_len) { SFSnortPacket *rpkt = NULL; int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : DCE2_MOCK_HDR_LEN__SMB_SRV; switch (sd->trans) { case DCE2_TRANS_TYPE__SMB: rpkt = DCE2_GetRpkt(sd->wire_pkt, DCE2_RPKT_TYPE__SMB_CO_SEG, data_ptr, data_len); if (rpkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to create reassembly packet.", __FILE__, __LINE__); return NULL; } DCE2_SmbSetRdata((DCE2_SmbSsnData *)sd, (uint8_t *)rpkt->payload, (uint16_t)(rpkt->payload_size - smb_hdr_len)); break; case DCE2_TRANS_TYPE__TCP: case DCE2_TRANS_TYPE__HTTP_PROXY: case DCE2_TRANS_TYPE__HTTP_SERVER: rpkt = DCE2_GetRpkt(sd->wire_pkt, DCE2_RPKT_TYPE__TCP_CO_SEG, data_ptr, data_len); if (rpkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to create reassembly packet.", __FILE__, __LINE__); return NULL; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, sd->trans); break; } return rpkt; } /******************************************************************** * Function: DCE2_CoInitCtxStorage() * * Allocates, if necessary, and initializes the context id list * and the context id pending queue. * * Arguments: * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * * Returns: * DCE2_Ret * DCE2_RET__ERROR * We were not able to allocate data for new lists. * DCE2_RET__SUCCESS * We were able to allocate and initialize new lists. * ********************************************************************/ static inline DCE2_Ret DCE2_CoInitCtxStorage(DCE2_CoTracker *cot) { if (cot == NULL) return DCE2_RET__ERROR; if (cot->ctx_ids == NULL) { cot->ctx_ids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_CoCtxCompare, DCE2_CoCtxFree, NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__CO_CTX); if (cot->ctx_ids == NULL) return DCE2_RET__ERROR; } if (cot->pending_ctx_ids == NULL) { cot->pending_ctx_ids = DCE2_QueueNew(DCE2_CoCtxFree, DCE2_MEM_TYPE__CO_CTX); if (cot->pending_ctx_ids == NULL) { DCE2_ListDestroy(cot->ctx_ids); cot->ctx_ids = NULL; return DCE2_RET__ERROR; } } else if (!DCE2_QueueIsEmpty(cot->pending_ctx_ids)) { DCE2_QueueEmpty(cot->pending_ctx_ids); } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_CoEarlyReassemble() * * Checks to see if we should send a reassembly packet based on * the current data in fragmentation and segmentation buffers * to the detection engine. Whether we do or not is based on * whether or not we are configured to do so. The number of bytes * in the fragmentation and segmentation buffers are calulated * and if they exceed the amount we are configured for, we * reassemble. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * * Returns: None * ********************************************************************/ static void DCE2_CoEarlyReassemble(DCE2_SsnData *sd, DCE2_CoTracker *cot) { DCE2_Buffer *frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker); if (DCE2_SsnFromServer(sd->wire_pkt)) return; if (!DCE2_BufferIsEmpty(frag_buf)) { uint32_t bytes = DCE2_BufferLength(frag_buf); uint32_t seg_bytes = 0; if (!DCE2_BufferIsEmpty(cot->cli_seg.buf)) { uint16_t hdr_size = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest); if (DCE2_BufferLength(cot->cli_seg.buf) > hdr_size) { DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)DCE2_BufferData(cot->cli_seg.buf); if (DceRpcCoPduType(co_hdr) == DCERPC_PDU_TYPE__REQUEST) { seg_bytes = DCE2_BufferLength(cot->cli_seg.buf) - hdr_size; if ((UINT32_MAX - bytes) < seg_bytes) seg_bytes = UINT32_MAX - bytes; bytes += seg_bytes; } } } if (bytes >= DCE2_GcReassembleThreshold()) { if (seg_bytes == 0) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Early reassemble - DCE/RPC fragments\n")); DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__FRAG); } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Early reassemble - DCE/RPC fragments and segments\n")); DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__ALL); } } } else if (!DCE2_BufferIsEmpty(cot->cli_seg.buf)) { uint16_t hdr_size = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest); uint32_t bytes = DCE2_BufferLength(cot->cli_seg.buf); if (bytes < hdr_size) return; if (bytes >= DCE2_GcReassembleThreshold()) { DCE2_Ret status; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Early reassemble - DCE/RPC segments\n")); status = DCE2_CoSegEarlyRequest(cot, DCE2_BufferData(cot->cli_seg.buf), bytes); if (status != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Not enough data in seg buffer to set rule option data.\n")); return; } DCE2_CoReassemble(sd, cot, DCE2_CO_RPKT_TYPE__SEG); } } } /******************************************************************** * Function: DCE2_CoGetRpkt() * * Creates a reassembled packet based on the kind of data * (fragment, segment or both) we want to put in the reassembled * packet. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DCE2_CoRpktType * Whether we want a defrag, deseg or defrag + deseg * reassembled packet. * DCE2_RpktType * * This is set based on the transport for the session, and * potentially used by the caller to set fields in the * reassembled packet. * * Returns: * SFSnortPacket * * Pointer to the reassembled packet. * ********************************************************************/ static SFSnortPacket * DCE2_CoGetRpkt(DCE2_SsnData *sd, DCE2_CoTracker *cot, DCE2_CoRpktType co_rtype, DCE2_RpktType *rtype) { DCE2_CoSeg *seg_buf = DCE2_CoGetSegPtr(sd, cot); DCE2_Buffer *frag_buf = DCE2_CoGetFragBuf(sd, &cot->frag_tracker); const uint8_t *frag_data = NULL, *seg_data = NULL; uint32_t frag_len = 0, seg_len = 0; SFSnortPacket *rpkt = NULL; *rtype = DCE2_RPKT_TYPE__NULL; switch (co_rtype) { case DCE2_CO_RPKT_TYPE__ALL: if (!DCE2_BufferIsEmpty(frag_buf)) { frag_data = DCE2_BufferData(frag_buf); frag_len = DCE2_BufferLength(frag_buf); } if (!DCE2_BufferIsEmpty(seg_buf->buf)) { seg_data = DCE2_BufferData(seg_buf->buf); seg_len = DCE2_BufferLength(seg_buf->buf); } break; case DCE2_CO_RPKT_TYPE__FRAG: if (!DCE2_BufferIsEmpty(frag_buf)) { frag_data = DCE2_BufferData(frag_buf); frag_len = DCE2_BufferLength(frag_buf); } break; case DCE2_CO_RPKT_TYPE__SEG: if (!DCE2_BufferIsEmpty(seg_buf->buf)) { seg_data = DCE2_BufferData(seg_buf->buf); seg_len = DCE2_BufferLength(seg_buf->buf); } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid CO rpkt type: %d", __FILE__, __LINE__, co_rtype); return NULL; } /* Seg stub data will be added to end of frag data */ if ((frag_data != NULL) && (seg_data != NULL)) { uint16_t hdr_size = sizeof(DceRpcCoHdr) + sizeof(DceRpcCoRequest); /* Need to just extract the stub data from the seg buffer * if there is enough data there */ if (seg_len > hdr_size) { DceRpcCoHdr *co_hdr = (DceRpcCoHdr *)seg_data; /* Don't use it if it's not a request and therefore doesn't * belong with the frag data. This is an insanity check - * shouldn't have seg data that's not a request if there are * frags queued up */ if (DceRpcCoPduType(co_hdr) != DCERPC_PDU_TYPE__REQUEST) { seg_data = NULL; seg_len = 0; } else { DCE2_MOVE(seg_data, seg_len, hdr_size); } } else /* Not enough stub data in seg buffer */ { seg_data = NULL; seg_len = 0; } } if (frag_data != NULL) *rtype = DCE2_CoGetRpktType(sd, DCE2_BUF_TYPE__FRAG); else if (seg_data != NULL) *rtype = DCE2_CoGetRpktType(sd, DCE2_BUF_TYPE__SEG); if (*rtype == DCE2_RPKT_TYPE__NULL) return NULL; if (frag_data != NULL) { rpkt = DCE2_GetRpkt(sd->wire_pkt, *rtype, frag_data, frag_len); if (rpkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to create reassembly packet.", __FILE__, __LINE__); return NULL; } if (seg_data != NULL) { /* If this fails, we'll still have the frag data */ DCE2_AddDataToRpkt(rpkt, *rtype, seg_data, seg_len); } } else if (seg_data != NULL) { rpkt = DCE2_GetRpkt(sd->wire_pkt, *rtype, seg_data, seg_len); if (rpkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to create reassembly packet.", __FILE__, __LINE__); return NULL; } } return rpkt; } /******************************************************************** * Function: DCE2_CoSegDecode() * * Creates a reassembled packet from the segmentation buffer and * sends off to be decoded. It's also detected on since the * detection engine has yet to see this data. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DCE2_CoSeg * * Pointer to the client or server segmentation buffer struct. * * Returns: None * ********************************************************************/ static void DCE2_CoSegDecode(DCE2_SsnData *sd, DCE2_CoTracker *cot, DCE2_CoSeg *seg) { const uint8_t *frag_ptr; uint16_t frag_len; SFSnortPacket *rpkt; int smb_hdr_len = DCE2_SsnFromClient(sd->wire_pkt) ? DCE2_MOCK_HDR_LEN__SMB_CLI : DCE2_MOCK_HDR_LEN__SMB_SRV; PROFILE_VARS; if (DCE2_SsnFromClient(sd->wire_pkt)) dce2_stats.co_cli_seg_reassembled++; else dce2_stats.co_srv_seg_reassembled++; PREPROC_PROFILE_START(dce2_pstat_co_reass); rpkt = DCE2_CoGetSegRpkt(sd, DCE2_BufferData(seg->buf), DCE2_BufferLength(seg->buf)); PREPROC_PROFILE_END(dce2_pstat_co_reass); // FIXTHIS - don't toss data until success response to // allow for retransmission of last segment of pdu. if // we don't do it here 2 things break: // (a) we can't alert on this packet; and // (b) subsequent pdus aren't desegmented correctly. DCE2_BufferEmpty(seg->buf); if (rpkt == NULL) return; /* Set the start of the connection oriented pdu to where it * is in the reassembled packet */ switch (sd->trans) { case DCE2_TRANS_TYPE__SMB: frag_ptr = rpkt->payload + smb_hdr_len; frag_len = rpkt->payload_size - smb_hdr_len; break; case DCE2_TRANS_TYPE__TCP: case DCE2_TRANS_TYPE__HTTP_PROXY: case DCE2_TRANS_TYPE__HTTP_SERVER: frag_ptr = rpkt->payload; frag_len = rpkt->payload_size; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, sd->trans); return; } if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to push packet onto packet stack.", __FILE__, __LINE__); return; } /* All is good. Decode the pdu */ DCE2_CoDecode(sd, cot, frag_ptr, frag_len); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__CO, "Reassembled CO segmented packet\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__CO, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); /* Call detect since this is a reassembled packet that the * detection engine hasn't seen yet */ if (!co_reassembled) DCE2_Detect(sd); DCE2_PopPkt(); } /******************************************************************** * Function: DCE2_CoSetRopts() * * Sets values necessary for the rule options. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * DceRpcCoHdr * * Pointer to connection-oriented header in packet. * * Returns: None * ********************************************************************/ static inline void DCE2_CoSetRopts(DCE2_SsnData *sd, DCE2_CoTracker *cot, const DceRpcCoHdr *co_hdr) { DCE2_CoFragTracker *ft = &cot->frag_tracker; int opnum = (ft->opnum != DCE2_SENTINEL) ? ft->opnum : cot->opnum; int ctx_id = (ft->ctx_id != DCE2_SENTINEL) ? ft->ctx_id : cot->ctx_id; int data_byte_order = (cot->data_byte_order != DCE2_SENTINEL) ? cot->data_byte_order : (int)DceRpcCoByteOrder(co_hdr); if (DCE2_CoSetIface(sd, cot, (uint16_t)ctx_id) != DCE2_RET__SUCCESS) sd->ropts.first_frag = DCE2_SENTINEL; else sd->ropts.first_frag = DceRpcCoFirstFrag(co_hdr); sd->ropts.hdr_byte_order = DceRpcCoByteOrder(co_hdr); sd->ropts.data_byte_order = data_byte_order; sd->ropts.opnum = opnum; sd->ropts.stub_data = cot->stub_data; } /******************************************************************** * Function: DCE2_CoGetRpktType() * * Determines the type of reassembly packet we need to use * based on the transport and buffer type. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_BufType * The type of buffer we're using - fragmentation or * segmentation. * * Returns: * DCE2_RpktType * The type of reassembly packet type we should be using * given the transport and buffer type. * ********************************************************************/ static inline DCE2_RpktType DCE2_CoGetRpktType(DCE2_SsnData *sd, DCE2_BufType btype) { DCE2_RpktType rtype = DCE2_RPKT_TYPE__NULL; switch (sd->trans) { case DCE2_TRANS_TYPE__SMB: switch (btype) { case DCE2_BUF_TYPE__SEG: rtype = DCE2_RPKT_TYPE__SMB_CO_SEG; break; case DCE2_BUF_TYPE__FRAG: rtype = DCE2_RPKT_TYPE__SMB_CO_FRAG; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid buffer type: %d", __FILE__, __LINE__, btype); break; } break; case DCE2_TRANS_TYPE__TCP: case DCE2_TRANS_TYPE__HTTP_PROXY: case DCE2_TRANS_TYPE__HTTP_SERVER: switch (btype) { case DCE2_BUF_TYPE__SEG: rtype = DCE2_RPKT_TYPE__TCP_CO_SEG; break; case DCE2_BUF_TYPE__FRAG: rtype = DCE2_RPKT_TYPE__TCP_CO_FRAG; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid buffer type: %d", __FILE__, __LINE__, btype); break; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid transport type: %d", __FILE__, __LINE__, sd->trans); break; } return rtype; } /******************************************************************** * Function: DCE2_CoIsSegBuf() * * Determines if the pointer passed in is within a segmentation * buffer. * * Arguments: * DCE2_SsnData * * Pointer to the session data structure. * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * const uint8_t * * The pointer to test. * * Returns: * int * 1 if the pointer is in a segmentation buffer. * 0 if the pointer is not within a segmentation buffer. * ********************************************************************/ static inline int DCE2_CoIsSegBuf(DCE2_SsnData *sd, DCE2_CoTracker *cot, const uint8_t *ptr) { DCE2_Buffer *seg_buf; if (DCE2_SsnFromServer(sd->wire_pkt)) seg_buf = cot->srv_seg.buf; else seg_buf = cot->cli_seg.buf; if (DCE2_BufferIsEmpty(seg_buf)) return 0; /* See if we're looking at a segmentation buffer */ if ((ptr < DCE2_BufferData(seg_buf)) || (ptr > (DCE2_BufferData(seg_buf) + DCE2_BufferLength(seg_buf)))) { return 0; } return 1; } /******************************************************************** * Function: DCE2_CoSegEarlyRequest() * * Used to set rule option data if we are doing an early * reassembly on data in the segmentation buffer. If we are * taking directly from the segmentation buffer, none of the * rule option data will be set since processing doesn't get to * that point. Only do if this is a Request PDU. * * Arguments: * DCE2_CoTracker * * Pointer to the relevant connection-oriented tracker. * const uint8_t * * Pointer to the segmentation buffer data. * uint16_t * Length of the segmentation buffer data. * * Returns: * DCE2_Ret * DCE2_RET__SUCCESS if there is enough data in buffer to * set rule option data and we should continue processing. * DCE2_RET__ERROR if there is not enough data in segmentation * buffer to set rule option data and we should not * continue processing. * ********************************************************************/ static DCE2_Ret DCE2_CoSegEarlyRequest(DCE2_CoTracker *cot, const uint8_t *seg_ptr, uint32_t seg_len) { const DceRpcCoHdr *co_hdr; const DceRpcCoRequest *rhdr; uint16_t req_size = sizeof(DceRpcCoRequest); if (seg_len < sizeof(DceRpcCoHdr)) return DCE2_RET__ERROR; co_hdr = (DceRpcCoHdr *)seg_ptr; DCE2_MOVE(seg_ptr, seg_len, sizeof(DceRpcCoHdr)); if (DceRpcCoPduType(co_hdr) != DCERPC_PDU_TYPE__REQUEST) return DCE2_RET__ERROR; rhdr = (DceRpcCoRequest *)seg_ptr; /* Account for possible object uuid */ if (DceRpcCoObjectFlag(co_hdr)) req_size += sizeof(Uuid); if (seg_len < req_size) return DCE2_RET__ERROR; cot->opnum = DceRpcCoOpnum(co_hdr, rhdr); cot->ctx_id = DceRpcCoCtxId(co_hdr, rhdr); cot->call_id = DceRpcCoCallId(co_hdr); return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_CoGetSegPtr() * * Returns the appropriate segmentation buffer. * * Arguments: * DCE2_SsnData * * Pointer to session data. * DCE2_CoTracker * * Pointer to connection-oriented tracker. * * Returns: * DCE2_CoSeg * * Pointer to client or server segmenation buffer. * ********************************************************************/ static inline DCE2_CoSeg * DCE2_CoGetSegPtr(DCE2_SsnData *sd, DCE2_CoTracker *cot) { if (DCE2_SsnFromServer(sd->wire_pkt)) return &cot->srv_seg; return &cot->cli_seg; } /******************************************************************** * Function: DCE2_CoGetFragBuf() * * Returns the appropriate fragmentation buffer. * * Arguments: * DCE2_SsnData * * Pointer to session data. * DCE2_CoFragTracker * * Pointer to connection-oriented fragmentation tracker. * * Returns: * DCE2_Buffer * * Pointer to client or server fragmentation buffer. * ********************************************************************/ static inline DCE2_Buffer * DCE2_CoGetFragBuf(DCE2_SsnData *sd, DCE2_CoFragTracker *ft) { if (DCE2_SsnFromServer(sd->wire_pkt)) return ft->srv_stub_buf; return ft->cli_stub_buf; } static int DCE2_CoGetAuthLen(DCE2_SsnData *sd, const DceRpcCoHdr *co_hdr, const uint8_t *frag_ptr, uint16_t frag_len) { DceRpcCoAuthVerifier *auth_hdr; uint16_t auth_len = DceRpcCoAuthLen(co_hdr); if (auth_len == 0) return 0; auth_len += sizeof(DceRpcCoAuthVerifier); /* This means the auth len was bogus */ if (auth_len > frag_len) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, auth_len); return -1; } auth_hdr = (DceRpcCoAuthVerifier *)(frag_ptr + (frag_len - auth_len)); if (DceRpcCoAuthLevel(auth_hdr) == DCERPC_CO_AUTH_LEVEL__PKT_PRIVACY) { /* Data is encrypted - don't inspect */ return -1; } auth_len += DceRpcCoAuthPad(auth_hdr); /* This means the auth pad len was bogus */ if (auth_len > frag_len) { DCE2_Alert(sd, DCE2_EVENT__CO_FLEN_LT_SIZE, dce2_pdu_types[DceRpcCoPduType(co_hdr)], frag_len, auth_len); return -1; } return (int)auth_len; } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/includes/0000755000175000017500000000000014242725716021355 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/dcerpc2/includes/dcerpc.h0000644000175000017500000007640614241075677023006 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef DCERPC_H #define DCERPC_H #ifdef HAVE_CONFIG_H #include "config.h" /* For WORDS_BIGENDIAN */ #endif /******************************************************************** * Enumerations ********************************************************************/ /* DCE/RPC byte order flag */ typedef enum _DceRpcBoFlag { DCERPC_BO_FLAG__NONE, DCERPC_BO_FLAG__BIG_ENDIAN, DCERPC_BO_FLAG__LITTLE_ENDIAN } DceRpcBoFlag; /* * Common to Connectionless and Connection Oriented */ typedef enum _DceRpcPduType { DCERPC_PDU_TYPE__REQUEST = 0, DCERPC_PDU_TYPE__PING, DCERPC_PDU_TYPE__RESPONSE, DCERPC_PDU_TYPE__FAULT, DCERPC_PDU_TYPE__WORKING, DCERPC_PDU_TYPE__NOCALL, DCERPC_PDU_TYPE__REJECT, DCERPC_PDU_TYPE__ACK, DCERPC_PDU_TYPE__CL_CANCEL, DCERPC_PDU_TYPE__FACK, DCERPC_PDU_TYPE__CANCEL_ACK, DCERPC_PDU_TYPE__BIND, DCERPC_PDU_TYPE__BIND_ACK, DCERPC_PDU_TYPE__BIND_NACK, DCERPC_PDU_TYPE__ALTER_CONTEXT, DCERPC_PDU_TYPE__ALTER_CONTEXT_RESP, DCERPC_PDU_TYPE__AUTH3, DCERPC_PDU_TYPE__SHUTDOWN, DCERPC_PDU_TYPE__CO_CANCEL, DCERPC_PDU_TYPE__ORPHANED, DCERPC_PDU_TYPE__MICROSOFT_PROPRIETARY_OUTLOOK2003_RPC_OVER_HTTP, DCERPC_PDU_TYPE__MAX } DceRpcPduType; /* Version 4 is for Connectionless * Version 5 is for Connection oriented */ typedef enum _DceRpcProtoMajorVers { DCERPC_PROTO_MAJOR_VERS__4 = 4, DCERPC_PROTO_MAJOR_VERS__5 = 5 } DceRpcProtoMajorVers; typedef enum _DceRpcProtoMinorVers { DCERPC_PROTO_MINOR_VERS__0 = 0, DCERPC_PROTO_MINOR_VERS__1 = 1 } DceRpcProtoMinorVers; /* * Connectionless */ typedef enum _DceRpcClFlags1 { DCERPC_CL_FLAGS1__RESERVED_01 = 0x01, DCERPC_CL_FLAGS1__LASTFRAG = 0x02, DCERPC_CL_FLAGS1__FRAG = 0x04, DCERPC_CL_FLAGS1__NOFACK = 0x08, DCERPC_CL_FLAGS1__MAYBE = 0x10, DCERPC_CL_FLAGS1__IDEMPOTENT = 0x20, DCERPC_CL_FLAGS1__BROADCAST = 0x40, DCERPC_CL_FLAGS1__RESERVED_80 = 0x80 } DceRpcClFlags1; typedef enum _DceRpcClFlags2 { DCERPC_CL_FLAGS2__RESERVED_01 = 0x01, DCERPC_CL_FLAGS2__CANCEL_PENDING = 0x02, DCERPC_CL_FLAGS2__RESERVED_04 = 0x04, DCERPC_CL_FLAGS2__RESERVED_08 = 0x08, DCERPC_CL_FLAGS2__RESERVED_10 = 0x10, DCERPC_CL_FLAGS2__RESERVED_20 = 0x20, DCERPC_CL_FLAGS2__RESERVED_40 = 0x40, DCERPC_CL_FLAGS2__RESERVED_80 = 0x80 } DceRpcClFlags2; typedef enum _DCERPC_AuthProto { DCERPC_AUTH_PROTO__NONE = 0, DCERPC_AUTH_PROTO__OSF_DCERPC_PK_AUTH = 1 } DCERPC_AuthProto; /* * Connection oriented */ typedef enum _DceRpcCoPfcFlags { DCERPC_CO_PFC_FLAGS__FIRST_FRAG = 0x01, DCERPC_CO_PFC_FLAGS__LAST_FRAG = 0x02, DCERPC_CO_PFC_FLAGS__PENDING_CANCEL = 0x04, DCERPC_CO_PFC_FLAGS__RESERVED_1 = 0x08, DCERPC_CO_PFC_FLAGS__CONC_MPX = 0x10, DCERPC_CO_PFC_FLAGS__DID_NOT_EXECUTE = 0x20, DCERPC_CO_PFC_FLAGS__MAYBE = 0x40, DCERPC_CO_PFC_FLAGS__OBJECT_UUID = 0x80 } DceRpcCoPfcFlags; /* Presentation context definition result */ typedef enum _DceRpcCoContDefResult { DCERPC_CO_CONT_DEF_RESULT__ACCEPTANCE = 0, DCERPC_CO_CONT_DEF_RESULT__USER_REJECTION, DCERPC_CO_CONT_DEF_RESULT__PROVIDER_REJECTION } DceRpcCoContDefResult; /* Presentation provider rejection reason */ typedef enum _DceRpcCoProvRejReason { DCERPC_CO_PROV_REJ_REASON__REASON_NOT_SPECIFIED = 0, DCERPC_CO_PROV_REJ_REASON__ABSTRACT_SYNTAX_NOT_SUPPORTED, DCERPC_CO_PROV_REJ_REASON__PROPOSED_TRANSFER_SYNTAXES_NOT_SUPPORTED, DCERPC_CO_PROV_REJ_REASON__LOCAL_LIMIT_EXCEEDED } DceRpcCoProvRejReason; typedef enum _DceRpcCoBindNakReason { DCERPC_CO_BIND_NAK_REASON__REASON_NOT_SPECIFIED = 0, DCERPC_CO_BIND_NAK_REASON__TEMPORARY_CONGESTION, DCERPC_CO_BIND_NAK_REASON__LOCAL_LIMIT_EXECEEDED, DCERPC_CO_BIND_NAK_REASON__CALLED_PADDR_UNKNOWN, DCERPC_CO_BIND_NAK_REASON__PROTOCOL_VERSION_NOT_SUPPORTED, DCERPC_CO_BIND_NAK_REASON__DEFAULT_CONTEXT_NOT_SUPPORTED, DCERPC_CO_BIND_NAK_REASON__USER_DATA_NOT_READABLE, DCERPC_CO_BIND_NAK_REASON__NO_PSAP_AVAILABLE } DceRpcCoBindNakReason; typedef enum _DceRpcCoAuthLevelType { DCERPC_CO_AUTH_LEVEL__NONE = 1, DCERPC_CO_AUTH_LEVEL__CONNECT, DCERPC_CO_AUTH_LEVEL__CALL, DCERPC_CO_AUTH_LEVEL__PKT, DCERPC_CO_AUTH_LEVEL__PKT_INTEGRITY, DCERPC_CO_AUTH_LEVEL__PKT_PRIVACY } DceRpcCoAuthLevelType; /******************************************************************** * Structures ********************************************************************/ #ifdef WIN32 #pragma pack(push, dcerpc_hdrs, 1) #else #pragma pack(1) #endif typedef struct _Uuid { uint32_t time_low; uint16_t time_mid; uint16_t time_high_and_version; uint8_t clock_seq_and_reserved; uint8_t clock_seq_low; uint8_t node[6]; } Uuid; /* * Connectionless */ typedef struct _DceRpcClHdr /* Connectionless header */ { uint8_t rpc_vers; uint8_t ptype; uint8_t flags1; uint8_t flags2; uint8_t drep[3]; uint8_t serial_hi; Uuid object; Uuid if_id; Uuid act_id; uint32_t server_boot; uint32_t if_vers; uint32_t seqnum; uint16_t opnum; uint16_t ihint; uint16_t ahint; uint16_t len; uint16_t fragnum; uint8_t auth_proto; uint8_t serial_lo; } DceRpcClHdr; /* ack PDU contains no body */ /* cancel PDU */ typedef struct _DceRpcClCancel { uint32_t vers; uint32_t cancel_id; } DceRpcClCancel; /* cancel_ack PDU */ typedef struct _DceRpcClCancelAck { uint32_t vers; uint32_t cancel_id; int server_is_accepting; } DceRpcClCancelAck; /* fack PDU */ typedef struct _DceRpcClFack { uint8_t vers; uint8_t pad1; uint16_t window_size; uint32_t max_tpdu; uint32_t max_frag_size; uint16_t serial_num; uint16_t selack_len; uint32_t selack[1]; /* variable length */ } DceRpcClFack; /* fault PDU */ typedef struct _DceRpcClFault { uint32_t status; /* status code */ } DceRpcClFault; /* nocall PDU (negative reply to ping) contains no body */ /* ping PDU contains no body */ /* reject PDU is the same as fack */ typedef DceRpcClFault DceRpcClReject; /* request PDU contains stub data as body */ /* response PDU contains stub data as body */ /* working PDU (positive reply to ping) contains no body */ /* * Connection oriented */ typedef struct _DceRpcCoVersion { uint8_t major; uint8_t minor; } DceRpcCoVersion; /* Connection oriented common header */ typedef struct _DceRpcCoHdr { DceRpcCoVersion pversion; uint8_t ptype; uint8_t pfc_flags; uint8_t packed_drep[4]; uint16_t frag_length; uint16_t auth_length; uint32_t call_id; } DceRpcCoHdr; /* Presentation syntax id */ typedef struct _DceRpcCoSynId { Uuid if_uuid; uint32_t if_version; } DceRpcCoSynId; /* Presentation context element */ typedef struct _DceRpcCoContElem { uint16_t p_cont_id; uint8_t n_transfer_syn; /* number of transfer syntaxes */ uint8_t reserved; DceRpcCoSynId abstract_syntax; #if 0 DceRpcCoSynId transfer_syntaxes[]; /* variable length */ #endif } DceRpcCoContElem; #if 0 /* Put this in the Bind header */ /* Presentation context list */ typedef struct _DceRpcCoContList { uint8_t n_context_elem; /* number of context elements */ uint8_t reserved; uint16_t reserved2; #if 0 DceRpcCoContElem p_cont_elem[]; /* variable length */ #endif } DceRpcCoContList; #endif /* Presentation result */ typedef struct _DceRpcCoContResult { uint16_t result; uint16_t reason; DceRpcCoSynId transfer_syntax; } DceRpcCoContResult; typedef struct _DceRpcCoContResultList { uint8_t n_results; uint8_t reserved; uint16_t reserved2; #if 0 DceRpcCoContResult p_results[]; /* variable length */ #endif } DceRpcCoContResultList; /* DCE version supported */ typedef struct _DceRpcCoVerSup { uint8_t n_protocols; /* number of protocols */ #if 0 DceRpcCoVersion protocols[]; /* variable length */ #endif } DceRpcCoVerSup; /* Bind */ typedef struct _DceRpcCoBind { uint16_t max_xmit_frag; uint16_t max_recv_frag; uint32_t assoc_group_id; uint8_t n_context_elem; /* number of context elements */ uint8_t reserved; uint16_t reserved2; #if 0 uint16_t p_cont_id; uint8_t n_tranfer_syn; /* number of transfer syntaxes */ uint8_t reserved; DceRpcCoSynId abstract_syntax; #endif #if 0 DceRpcCoContList p_context_elem_list; /* variable length */ auth_verifier_co_t auth_verifier; #endif } DceRpcCoBind; /* Bind response */ typedef struct _DceRpcCoBindAck { uint16_t max_xmit_frag; uint16_t max_recv_frag; uint32_t assoc_group_id; uint16_t sec_addr_len; #if 0 char sec_addr[]; /* variable length */ uint8_t pad2[align(4)]; /* this is really to align the above field whose last member is a variable len str. It will be 0-3 bytes long. */ DceRpcCoContResultList p_context_elem; aut_verifier_co_t auth_verifier; #endif } DceRpcCoBindAck; typedef DceRpcCoBind DceRpcCoAltCtx; typedef DceRpcCoBindAck DceRpcCoAltCtxResp; typedef struct _DceRpcCoBindNak { DceRpcCoBindNakReason provider_reject_reason; #if 0 DceRpcCoVerSup versions; /* variable length */ #endif } DceRpcCoBindNak; #if 0 typedef struct _DceRpcCoCancel { auth_verifier_co_t auth_verifier; } DceRpcCoCancel; #endif typedef struct _DceRpcCoFault { uint32_t alloc_hint; uint16_t context_id; uint8_t cancel_count; uint8_t reserved; uint32_t status; uint8_t reserved2[4]; #if 0 uint8_t stub data[] /* 8 octet aligned if auth_verifier, which will take care of the pad. */ auth_verifier_co_t auth_verifier; #endif } DceRpcCoFault; #if 0 typedef struct _DceRpcCoOrphaned { auth_verifier_co_t auth_verifier; } DceRpcCoOrphaned; #endif typedef struct _DceRpcCoRequest { uint32_t alloc_hint; uint16_t context_id; uint16_t opnum; #if 0 Uuid object; /* only if object flag is set */ uint8_t stub data[]; /* 8 octet aligned if auth_verifier, which will take care of the pad. */ auth_verifier_co_t auth_verifier; #endif } DceRpcCoRequest; typedef struct _DceRpcCoResponse { uint32_t alloc_hint; uint16_t context_id; uint8_t cancel_count; uint8_t reserved; #if 0 uint8_t stub data[] /* 8 octet aligned if auth_verifier, which will take care of the pad. */ auth_verifier_co_t auth_verifier; #endif } DceRpcCoResponse; #if 0 typedef struct _DceRpcCoShutdown { // nothing } DceRpcCoShutdown; #endif typedef struct _DceRpcCoAuthVerifier { #if 0 uint8_t auth_pad[]; /* variable length to restore 4 byte alignment */ #endif uint8_t auth_type; uint8_t auth_level; uint8_t auth_pad_length; uint8_t auth_reserved; uint32_t auth_context_id; #if 0 uint8_t auth_value[]; /* variable auth_length */ #endif } DceRpcCoAuthVerifier; /* Optional Data used with Reject/Disconnect header * These do not share the common header, but are special * cases (pretty much the same as the common header) */ typedef uint16_t DceRpcReasonCode; typedef struct _DceRpcCoOptData { DceRpcCoVersion pversion; uint8_t reserved[2]; uint8_t packed_drep[4]; uint32_t reject_status; uint8_t reserved2[4]; } DceRpcCoOptData; typedef struct _DceRpcCoRejHdr { DceRpcReasonCode reason_code; DceRpcCoOptData rpc_info; } DceRpcCoRejHdr; /* Disconnect header same as Reject header */ typedef DceRpcCoRejHdr DceRpcCoDiscHdr; #ifdef WIN32 #pragma pack(pop, dcerpc_hdrs) #else #pragma pack() #endif /******************************************************************** * Inline functions prototypes ********************************************************************/ static inline DceRpcBoFlag DceRpcByteOrder(const uint8_t); static inline uint16_t DceRpcNtohs(const uint16_t *, const DceRpcBoFlag); static inline uint16_t DceRpcHtons(const uint16_t *, const DceRpcBoFlag); static inline uint32_t DceRpcNtohl(const uint32_t *, const DceRpcBoFlag); static inline uint32_t DceRpcHtonl(const uint32_t *, const DceRpcBoFlag); /* Connectionless */ static inline uint8_t DceRpcClRpcVers(const DceRpcClHdr *); static inline DceRpcBoFlag DceRpcClByteOrder(const DceRpcClHdr *); static inline uint32_t DceRpcClIfaceVers(const DceRpcClHdr *); static inline uint16_t DceRpcClOpnum(const DceRpcClHdr *); static inline uint32_t DceRpcClSeqNum(const DceRpcClHdr *); static inline uint16_t DceRpcClFragNum(const DceRpcClHdr *); static inline int DceRpcClFragFlag(const DceRpcClHdr *); static inline int DceRpcClLastFrag(const DceRpcClHdr *); static inline int DceRpcClFirstFrag(const DceRpcClHdr *); static inline uint16_t DceRpcClLen(const DceRpcClHdr *); static inline int DceRpcClFrag(const DceRpcClHdr *); /* Connection oriented */ static inline uint8_t DceRpcCoVersMaj(const DceRpcCoHdr *); static inline uint8_t DceRpcCoVersMin(const DceRpcCoHdr *); static inline DceRpcPduType DceRpcCoPduType(const DceRpcCoHdr *); static inline int DceRpcCoFirstFrag(const DceRpcCoHdr *); static inline int DceRpcCoLastFrag(const DceRpcCoHdr *); static inline int DceRpcCoObjectFlag(const DceRpcCoHdr *); static inline DceRpcBoFlag DceRpcCoByteOrder(const DceRpcCoHdr *); static inline uint16_t DceRpcCoFragLen(const DceRpcCoHdr *); static inline uint16_t DceRpcCoAuthLen(const DceRpcCoHdr *); static inline uint32_t DceRpcCoCallId(const DceRpcCoHdr *); static inline uint16_t DceRpcCoCtxId(const DceRpcCoHdr *, const DceRpcCoRequest *); static inline uint16_t DceRpcCoCtxIdResp(const DceRpcCoHdr *, const DceRpcCoResponse *); static inline uint16_t DceRpcCoOpnum(const DceRpcCoHdr *, const DceRpcCoRequest *); static inline uint16_t DceRpcCoBindMaxXmitFrag(const DceRpcCoHdr *, const DceRpcCoBind *); static inline uint16_t DceRpcCoBindAckMaxRecvFrag(const DceRpcCoHdr *, const DceRpcCoBindAck *); static inline uint8_t DceRpcCoNumCtxItems(const DceRpcCoBind *); static inline uint16_t DceRpcCoContElemCtxId(const DceRpcCoHdr *, const DceRpcCoContElem *); static inline uint8_t DceRpcCoContElemNumTransSyntaxes(const DceRpcCoContElem *); static inline const Uuid * DceRpcCoContElemIface(const DceRpcCoContElem *); static inline uint16_t DceRpcCoContElemIfaceVersMaj(const DceRpcCoHdr *, const DceRpcCoContElem *); static inline uint16_t DceRpcCoContElemIfaceVersMin(const DceRpcCoHdr *, const DceRpcCoContElem *); static inline uint16_t DceRpcCoSecAddrLen(const DceRpcCoHdr *, const DceRpcCoBindAck *); static inline uint8_t DceRpcCoContNumResults(const DceRpcCoContResultList *); static inline uint16_t DceRpcCoContRes(const DceRpcCoHdr *, const DceRpcCoContResult *); static inline uint16_t DceRpcCoAuthPad(const DceRpcCoAuthVerifier *); static inline uint8_t DceRpcCoAuthLevel(const DceRpcCoAuthVerifier *); /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DceRpcBoFlag DceRpcByteOrder(const uint8_t value) { if ((value & 0x10) >> 4) return DCERPC_BO_FLAG__LITTLE_ENDIAN; return DCERPC_BO_FLAG__BIG_ENDIAN; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcNtohs(const uint16_t *ptr, const DceRpcBoFlag bo_flag) { uint16_t value; if (ptr == NULL) return 0; #ifdef WORDS_MUSTALIGN value = *((uint8_t *)ptr) << 8 | *((uint8_t *)ptr + 1); #else value = *ptr; #endif /* WORDS_MUSTALIGN */ if (bo_flag == DCERPC_BO_FLAG__NONE) return value; #ifdef WORDS_BIGENDIAN if (bo_flag == DCERPC_BO_FLAG__BIG_ENDIAN) #else if (bo_flag == DCERPC_BO_FLAG__LITTLE_ENDIAN) #endif /* WORDS_BIGENDIAN */ return value; return ((value & 0xff00) >> 8) | ((value & 0x00ff) << 8); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcHtons(const uint16_t *ptr, const DceRpcBoFlag bo_flag) { return DceRpcNtohs(ptr, bo_flag); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint32_t DceRpcNtohl(const uint32_t *ptr, const DceRpcBoFlag bo_flag) { uint32_t value; if (ptr == NULL) return 0; #ifdef WORDS_MUSTALIGN value = *((uint8_t *)ptr) << 24 | *((uint8_t *)ptr + 1) << 16 | *((uint8_t *)ptr + 2) << 8 | *((uint8_t *)ptr + 3); #else value = *ptr; #endif /* WORDS_MUSTALIGN */ if (bo_flag == DCERPC_BO_FLAG__NONE) return value; #ifdef WORDS_BIGENDIAN if (bo_flag == DCERPC_BO_FLAG__BIG_ENDIAN) #else if (bo_flag == DCERPC_BO_FLAG__LITTLE_ENDIAN) #endif /* WORDS_BIGENDIAN */ return value; return ((value & 0xff000000) >> 24) | ((value & 0x00ff0000) >> 8) | ((value & 0x0000ff00) << 8) | ((value & 0x000000ff) << 24); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint32_t DceRpcHtonl(const uint32_t *ptr, const DceRpcBoFlag bo_flag) { return DceRpcNtohl(ptr, bo_flag); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcClRpcVers(const DceRpcClHdr *cl) { return cl->rpc_vers; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcClPduType(const DceRpcClHdr *cl) { return cl->ptype; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DceRpcBoFlag DceRpcClByteOrder(const DceRpcClHdr *cl) { return DceRpcByteOrder(cl->drep[0]); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline const Uuid * DceRpcClIface(const DceRpcClHdr *cl) { return &cl->if_id; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint32_t DceRpcClIfaceVers(const DceRpcClHdr *cl) { return DceRpcNtohl(&cl->if_vers, DceRpcClByteOrder(cl)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcClOpnum(const DceRpcClHdr *cl) { return DceRpcNtohs(&cl->opnum, DceRpcClByteOrder(cl)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint32_t DceRpcClSeqNum(const DceRpcClHdr *cl) { return DceRpcNtohl(&cl->seqnum, DceRpcClByteOrder(cl)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcClFragNum(const DceRpcClHdr *cl) { return DceRpcNtohs(&cl->fragnum, DceRpcClByteOrder(cl)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DceRpcClFragFlag(const DceRpcClHdr *cl) { return cl->flags1 & DCERPC_CL_FLAGS1__FRAG; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DceRpcClLastFrag(const DceRpcClHdr *cl) { return cl->flags1 & DCERPC_CL_FLAGS1__LASTFRAG; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DceRpcClFirstFrag(const DceRpcClHdr *cl) { return (DceRpcClFragFlag(cl) && (DceRpcClFragNum(cl) == 0)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcClLen(const DceRpcClHdr *cl) { return DceRpcNtohs(&cl->len, DceRpcClByteOrder(cl)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DceRpcClFrag(const DceRpcClHdr *cl) { if (DceRpcClFragFlag(cl)) { if (DceRpcClLastFrag(cl) && (DceRpcClFragNum(cl) == 0)) return 0; return 1; } return 0; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcCoVersMaj(const DceRpcCoHdr *co) { return co->pversion.major; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcCoVersMin(const DceRpcCoHdr *co) { return co->pversion.minor; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DceRpcPduType DceRpcCoPduType(const DceRpcCoHdr *co) { return (DceRpcPduType)co->ptype; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DceRpcCoFirstFrag(const DceRpcCoHdr *co) { return co->pfc_flags & DCERPC_CO_PFC_FLAGS__FIRST_FRAG; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DceRpcCoLastFrag(const DceRpcCoHdr *co) { return co->pfc_flags & DCERPC_CO_PFC_FLAGS__LAST_FRAG; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline int DceRpcCoObjectFlag(const DceRpcCoHdr *co) { return co->pfc_flags & DCERPC_CO_PFC_FLAGS__OBJECT_UUID; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DceRpcBoFlag DceRpcCoByteOrder(const DceRpcCoHdr *co) { return DceRpcByteOrder(co->packed_drep[0]); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoFragLen(const DceRpcCoHdr *co) { return DceRpcNtohs(&co->frag_length, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoAuthLen(const DceRpcCoHdr *co) { return DceRpcNtohs(&co->auth_length, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint32_t DceRpcCoCallId(const DceRpcCoHdr *co) { return DceRpcNtohl(&co->call_id, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoOpnum(const DceRpcCoHdr *co, const DceRpcCoRequest *cor) { return DceRpcNtohs(&cor->opnum, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoCtxId(const DceRpcCoHdr *co, const DceRpcCoRequest *cor) { return DceRpcNtohs(&cor->context_id, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoCtxIdResp(const DceRpcCoHdr *co, const DceRpcCoResponse *cor) { return DceRpcNtohs(&cor->context_id, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoBindMaxXmitFrag(const DceRpcCoHdr *co, const DceRpcCoBind *cob) { return DceRpcNtohs(&cob->max_xmit_frag, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoBindAckMaxRecvFrag(const DceRpcCoHdr *co, const DceRpcCoBindAck *coba) { return DceRpcNtohs(&coba->max_recv_frag, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcCoNumCtxItems(const DceRpcCoBind *cob) { return cob->n_context_elem; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoContElemCtxId(const DceRpcCoHdr *co, const DceRpcCoContElem *coce) { return DceRpcNtohs(&coce->p_cont_id, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcCoContElemNumTransSyntaxes(const DceRpcCoContElem *coce) { return coce->n_transfer_syn; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline const Uuid * DceRpcCoContElemIface(const DceRpcCoContElem *coce) { return &coce->abstract_syntax.if_uuid; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoContElemIfaceVersMaj(const DceRpcCoHdr *co, const DceRpcCoContElem *coce) { return (uint16_t)(DceRpcNtohl(&coce->abstract_syntax.if_version, DceRpcCoByteOrder(co)) & 0x0000ffff); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoContElemIfaceVersMin(const DceRpcCoHdr *co, const DceRpcCoContElem *coce) { return (uint16_t)(DceRpcNtohl(&coce->abstract_syntax.if_version, DceRpcCoByteOrder(co)) >> 16); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoSecAddrLen(const DceRpcCoHdr *co, const DceRpcCoBindAck *coba) { return DceRpcNtohs(&coba->sec_addr_len, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcCoContNumResults(const DceRpcCoContResultList *cocrl) { return cocrl->n_results; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoContRes(const DceRpcCoHdr *co, const DceRpcCoContResult *cocr) { return DceRpcNtohs(&cocr->result, DceRpcCoByteOrder(co)); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint16_t DceRpcCoAuthPad(const DceRpcCoAuthVerifier *coav) { return coav->auth_pad_length; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline uint8_t DceRpcCoAuthLevel(const DceRpcCoAuthVerifier *coav) { return coav->auth_level; } #endif /* DCERPC_H */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/includes/smb.h0000644000175000017500000036375714241075700022322 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _SMB_H_ #define _SMB_H_ #ifdef HAVE_CONFIG_H #include "config.h" /* For WORDS_BIGENDIAN */ #endif #include "snort_debug.h" /* For inline */ #include "sf_types.h" /******************************************************************** * Macros ********************************************************************/ #define NBSS_SESSION_TYPE__MESSAGE 0x00 #define NBSS_SESSION_TYPE__REQUEST 0x81 #define NBSS_SESSION_TYPE__POS_RESPONSE 0x82 #define NBSS_SESSION_TYPE__NEG_RESPONSE 0x83 #define NBSS_SESSION_TYPE__RETARGET_RESPONSE 0x84 #define NBSS_SESSION_TYPE__KEEP_ALIVE 0x85 // SMB dialects #define SMB_DIALECT_PCLAN10 "PCLAN1.0" // Core Protocol #define SMB_DIALECT_PC_NET_PROG10 "PC NETWORK PROGRAM 1.0" // Core Protocol #define SMB_DIALECT_XENIX11 "xenix1.1" // Xenix Extensions #define SMB_DIALECT_XENIX_CORE "XENIX CORE" // Xenix Extensions #define SMB_DIALECT_MS_NET103 "MICROSOFT NETWORKS 1.03" // CorePlus #define SMB_DIALECT_LANMAN10 "LANMAN1.0" // LAN Manager 1.0 #define SMB_DIALECT_MS_NET30 "MICROSOFT NETWORKS 3.0" // DOS LAN Manager 1.0 #define SMB_DIALECT_WIN_FOR_WKGS31a "Windows for Workgroups 3.1a" // Not documented in MS-CIFS but always used #define SMB_DIALECT_LANMAN12 "LANMAN1.2" // LAN Manager 1.2 #define SMB_DIALECT_DOS_LM12X002 "DOS LM1.2X002" // DOS LAN Manager 2.0 #define SMB_DIALECT_LM12X002 "LM1.2X002" // LAN Manager 2.0 #define SMB_DIALECT_DOS_LANMAN21 "DOS LANMAN2.1" // DOS LAN Manager 2.1 #define SMB_DIALECT_LANMAN21 "LANMAN2.1" // LAN Manager 2.1 #define SMB_DIALECT_SAMBA "Samba" // Some Samba specific thing #define SMB_DIALECT_NT_LANMAN10 "NT LANMAN 1.0" // Only seen with Samba #define SMB_DIALECT_NT_LM_012 "NT LM 0.12" // NT LAN Manager #define SMB_DIALECT_SMB_2002 "SMB 2.002" // SMB 2.002 #define SMB_DIALECT_SMB_212 "SMB 2.???" // SMB 2.1 or 2.2 #define SMB_FLG__TYPE 0x80 #define SMB_TYPE__REQUEST 0 #define SMB_TYPE__RESPONSE 1 #define SMB_FLG2__UNICODE 0x8000 #define SMB_FLG2__NT_CODES 0x4000 #define SMB_NT_STATUS_SEVERITY__SUCCESS 0 #define SMB_NT_STATUS_SEVERITY__INFORMATIONAL 1 #define SMB_NT_STATUS_SEVERITY__WARNING 2 #define SMB_NT_STATUS_SEVERITY__ERROR 3 #define SMB_NT_STATUS__SUCCESS 0x00000000 #define SMB_NT_STATUS__INVALID_DEVICE_REQUEST 0xc0000010 #define SMB_NT_STATUS__RANGE_NOT_LOCKED 0xc000007e #define SMB_NT_STATUS__PIPE_BROKEN 0xc000014b #define SMB_NT_STATUS__PIPE_DISCONNECTED 0xc00000b0 #define SMB_ERROR_CLASS__SUCCESS 0x00 #define SMB_ERROR_CLASS__ERRDOS 0x01 #define SMB_ERROR_CLASS__ERRSRV 0x02 #define SMB_ERROR_CLASS__ERRHRD 0x03 #define SMB_ERROR_CLASS__ERRXOS 0x04 #define SMB_ERROR_CLASS__ERRMX1 0xe1 #define SMB_ERROR_CLASS__ERRMX2 0xe2 #define SMB_ERROR_CLASS__ERRMX3 0xe3 #define SMB_ERROR_CLASS__ERRCMD 0xff #define SMB_ERRSRV__INVALID_DEVICE 0x0007 #define SMB_ERRDOS__NOT_LOCKED 0x009e #define SMB_ERRDOS__BAD_PIPE 0x00e6 #define SMB_ERRDOS__PIPE_NOT_CONNECTED 0x00e9 #define SMB_ERRDOS__MORE_DATA 0x00ea /* SMB formats (smb_fmt) Dialect, Pathname and ASCII are all * NULL terminated ASCII strings unless Unicode is specified * in the NT LM 1.0 SMB header in which case they are NULL * terminated unicode strings */ #define SMB_FMT__DATA_BLOCK 1 #define SMB_FMT__DIALECT 2 #define SMB_FMT__ASCII 4 static inline bool SmbFmtDataBlock(const uint8_t fmt) { return fmt == SMB_FMT__DATA_BLOCK ? true : false; } static inline bool SmbFmtDialect(const uint8_t fmt) { return fmt == SMB_FMT__DIALECT ? true : false; } static inline bool SmbFmtAscii(const uint8_t fmt) { return fmt == SMB_FMT__ASCII ? true : false; } /* SMB command codes */ #define SMB_COM_CREATE_DIRECTORY 0x00 #define SMB_COM_DELETE_DIRECTORY 0x01 #define SMB_COM_OPEN 0x02 #define SMB_COM_CREATE 0x03 #define SMB_COM_CLOSE 0x04 #define SMB_COM_FLUSH 0x05 #define SMB_COM_DELETE 0x06 #define SMB_COM_RENAME 0x07 #define SMB_COM_QUERY_INFORMATION 0x08 #define SMB_COM_SET_INFORMATION 0x09 #define SMB_COM_READ 0x0A #define SMB_COM_WRITE 0x0B #define SMB_COM_LOCK_BYTE_RANGE 0x0C #define SMB_COM_UNLOCK_BYTE_RANGE 0x0D #define SMB_COM_CREATE_TEMPORARY 0x0E #define SMB_COM_CREATE_NEW 0x0F #define SMB_COM_CHECK_DIRECTORY 0x10 #define SMB_COM_PROCESS_EXIT 0x11 #define SMB_COM_SEEK 0x12 #define SMB_COM_LOCK_AND_READ 0x13 #define SMB_COM_WRITE_AND_UNLOCK 0x14 #define SMB_COM_READ_RAW 0x1A #define SMB_COM_READ_MPX 0x1B #define SMB_COM_READ_MPX_SECONDARY 0x1C #define SMB_COM_WRITE_RAW 0x1D #define SMB_COM_WRITE_MPX 0x1E #define SMB_COM_WRITE_MPX_SECONDARY 0x1F #define SMB_COM_WRITE_COMPLETE 0x20 #define SMB_COM_QUERY_SERVER 0x21 #define SMB_COM_SET_INFORMATION2 0x22 #define SMB_COM_QUERY_INFORMATION2 0x23 #define SMB_COM_LOCKING_ANDX 0x24 #define SMB_COM_TRANSACTION 0x25 #define SMB_COM_TRANSACTION_SECONDARY 0x26 #define SMB_COM_IOCTL 0x27 #define SMB_COM_IOCTL_SECONDARY 0x28 #define SMB_COM_COPY 0x29 #define SMB_COM_MOVE 0x2A #define SMB_COM_ECHO 0x2B #define SMB_COM_WRITE_AND_CLOSE 0x2C #define SMB_COM_OPEN_ANDX 0x2D #define SMB_COM_READ_ANDX 0x2E #define SMB_COM_WRITE_ANDX 0x2F #define SMB_COM_NEW_FILE_SIZE 0x30 #define SMB_COM_CLOSE_AND_TREE_DISC 0x31 #define SMB_COM_TRANSACTION2 0x32 #define SMB_COM_TRANSACTION2_SECONDARY 0x33 #define SMB_COM_FIND_CLOSE2 0x34 #define SMB_COM_FIND_NOTIFY_CLOSE 0x35 #define SMB_COM_TREE_CONNECT 0x70 #define SMB_COM_TREE_DISCONNECT 0x71 #define SMB_COM_NEGOTIATE 0x72 #define SMB_COM_SESSION_SETUP_ANDX 0x73 #define SMB_COM_LOGOFF_ANDX 0x74 #define SMB_COM_TREE_CONNECT_ANDX 0x75 #define SMB_COM_SECURITY_PACKAGE_ANDX 0x7E #define SMB_COM_QUERY_INFORMATION_DISK 0x80 #define SMB_COM_SEARCH 0x81 #define SMB_COM_FIND 0x82 #define SMB_COM_FIND_UNIQUE 0x83 #define SMB_COM_FIND_CLOSE 0x84 #define SMB_COM_NT_TRANSACT 0xA0 #define SMB_COM_NT_TRANSACT_SECONDARY 0xA1 #define SMB_COM_NT_CREATE_ANDX 0xA2 #define SMB_COM_NT_CANCEL 0xA4 #define SMB_COM_NT_RENAME 0xA5 #define SMB_COM_OPEN_PRINT_FILE 0xC0 #define SMB_COM_WRITE_PRINT_FILE 0xC1 #define SMB_COM_CLOSE_PRINT_FILE 0xC2 #define SMB_COM_GET_PRINT_QUEUE 0xC3 #define SMB_COM_READ_BULK 0xD8 #define SMB_COM_WRITE_BULK 0xD9 #define SMB_COM_WRITE_BULK_DATA 0xDA #define SMB_COM_INVALID 0xFE #define SMB_COM_NO_ANDX_COMMAND 0xFF #define SMB_MAX_NUM_COMS 256 /* Size of word count field + Word count * 2 bytes + Size of byte count field */ #define SMB_COM_SIZE(wct) (sizeof(uint8_t) + ((wct) * sizeof(uint16_t)) + sizeof(uint16_t)) #define SMB_FILE_TYPE_DISK 0x0000 #define SMB_FILE_TYPE_BYTE_MODE_PIPE 0x0001 #define SMB_FILE_TYPE_MESSAGE_MODE_PIPE 0x0002 #define SMB_FILE_TYPE_PRINTER 0x0003 #define SMB_FILE_TYPE_COMMON_DEVICE 0x0004 #define SMB_FILE_ATTRIBUTE_NORMAL 0x0000 #define SMB_FILE_ATTRIBUTE_READONLY 0x0001 #define SMB_FILE_ATTRIBUTE_HIDDEN 0x0002 #define SMB_FILE_ATTRIBUTE_SYSTEM 0x0004 #define SMB_FILE_ATTRIBUTE_VOLUME 0x0008 #define SMB_FILE_ATTRIBUTE_DIRECTORY 0x0010 #define SMB_FILE_ATTRIBUTE_ARCHIVE 0x0020 #define SMB_SEARCH_ATTRIBUTE_READONLY 0x0100 #define SMB_SEARCH_ATTRIBUTE_HIDDEN 0x0200 #define SMB_SEARCH_ATTRIBUTE_SYSTEM 0x0400 #define SMB_SEARCH_ATTRIBUTE_DIRECTORY 0x1000 #define SMB_SEARCH_ATTRIBUTE_ARCHIVE 0x2000 #define SMB_FILE_ATTRIBUTE_OTHER 0xC8C0 // Reserved #define SMB_EXT_FILE_ATTR_READONLY 0x00000001 #define SMB_EXT_FILE_ATTR_HIDDEN 0x00000002 #define SMB_EXT_FILE_ATTR_SYSTEM 0x00000004 #define SMB_EXT_FILE_ATTR_DIRECTORY 0x00000010 #define SMB_EXT_FILE_ATTR_ARCHIVE 0x00000020 #define SMB_EXT_FILE_ATTR_NORMAL 0x00000080 #define SMB_EXT_FILE_ATTR_TEMPORARY 0x00000100 #define SMB_EXT_FILE_ATTR_COMPRESSED 0x00000800 #define SMB_EXT_FILE_POSIX_SEMANTICS 0x01000000 #define SMB_EXT_FILE_BACKUP_SEMANTICS 0x02000000 #define SMB_EXT_FILE_DELETE_ON_CLOSE 0x04000000 #define SMB_EXT_FILE_SEQUENTIAL_SCAN 0x08000000 #define SMB_EXT_FILE_RANDOM_ACCESS 0x10000000 #define SMB_EXT_FILE_NO_BUFFERING 0x20000000 #define SMB_EXT_FILE_WRITE_THROUGH 0x80000000 /******************************************************************** * Enums ********************************************************************/ typedef enum _SmbAndXCom { SMB_ANDX_COM__NONE, SMB_ANDX_COM__OPEN_ANDX, SMB_ANDX_COM__READ_ANDX, SMB_ANDX_COM__WRITE_ANDX, SMB_ANDX_COM__TREE_CONNECT_ANDX, SMB_ANDX_COM__SESSION_SETUP_ANDX, SMB_ANDX_COM__LOGOFF_ANDX, SMB_ANDX_COM__NT_CREATE_ANDX, SMB_ANDX_COM__MAX } SmbAndXCom; typedef enum _SmbTransactionSubcommand { TRANS_UNKNOWN_0000 = 0x0000, TRANS_SET_NMPIPE_STATE = 0x0001, TRANS_UNKNOWN_0002 = 0x0002, TRANS_UNKNOWN_0003 = 0x0003, TRANS_UNKNOWN_0004 = 0x0004, TRANS_UNKNOWN_0005 = 0x0005, TRANS_UNKNOWN_0006 = 0x0006, TRANS_UNKNOWN_0007 = 0x0007, TRANS_UNKNOWN_0008 = 0x0008, TRANS_UNKNOWN_0009 = 0x0009, TRANS_UNKNOWN_000A = 0x000A, TRANS_UNKNOWN_000B = 0x000B, TRANS_UNKNOWN_000C = 0x000C, TRANS_UNKNOWN_000D = 0x000D, TRANS_UNKNOWN_000E = 0x000E, TRANS_UNKNOWN_000F = 0x000F, TRANS_UNKNOWN_0010 = 0x0010, TRANS_RAW_READ_NMPIPE = 0x0011, TRANS_UNKNOWN_0012 = 0x0012, TRANS_UNKNOWN_0013 = 0x0013, TRANS_UNKNOWN_0014 = 0x0014, TRANS_UNKNOWN_0015 = 0x0015, TRANS_UNKNOWN_0016 = 0x0016, TRANS_UNKNOWN_0017 = 0x0017, TRANS_UNKNOWN_0018 = 0x0018, TRANS_UNKNOWN_0019 = 0x0019, TRANS_UNKNOWN_001A = 0x001A, TRANS_UNKNOWN_001B = 0x001B, TRANS_UNKNOWN_001C = 0x001C, TRANS_UNKNOWN_001D = 0x001D, TRANS_UNKNOWN_001E = 0x001E, TRANS_UNKNOWN_001F = 0x001F, TRANS_UNKNOWN_0020 = 0x0020, TRANS_QUERY_NMPIPE_STATE = 0x0021, TRANS_QUERY_NMPIPE_INFO = 0x0022, TRANS_PEEK_NMPIPE = 0x0023, TRANS_UNKNOWN_0024 = 0x0024, TRANS_UNKNOWN_0025 = 0x0025, TRANS_TRANSACT_NMPIPE = 0x0026, TRANS_UNKNOWN_0027 = 0x0027, TRANS_UNKNOWN_0028 = 0x0028, TRANS_UNKNOWN_0029 = 0x0029, TRANS_UNKNOWN_002A = 0x002A, TRANS_UNKNOWN_002B = 0x002B, TRANS_UNKNOWN_002C = 0x002C, TRANS_UNKNOWN_002D = 0x002D, TRANS_UNKNOWN_002E = 0x002E, TRANS_UNKNOWN_002F = 0x002F, TRANS_UNKNOWN_0030 = 0x0030, TRANS_RAW_WRITE_NMPIPE = 0x0031, TRANS_UNKNOWN_0032 = 0x0032, TRANS_UNKNOWN_0033 = 0x0033, TRANS_UNKNOWN_0034 = 0x0034, TRANS_UNKNOWN_0035 = 0x0035, TRANS_READ_NMPIPE = 0x0036, TRANS_WRITE_NMPIPE = 0x0037, TRANS_UNKNOWN_0038 = 0x0038, TRANS_UNKNOWN_0039 = 0x0039, TRANS_UNKNOWN_003A = 0x003A, TRANS_UNKNOWN_003B = 0x003B, TRANS_UNKNOWN_003C = 0x003C, TRANS_UNKNOWN_003D = 0x003D, TRANS_UNKNOWN_003E = 0x003E, TRANS_UNKNOWN_003F = 0x003F, TRANS_UNKNOWN_0040 = 0x0040, TRANS_UNKNOWN_0041 = 0x0041, TRANS_UNKNOWN_0042 = 0x0042, TRANS_UNKNOWN_0043 = 0x0043, TRANS_UNKNOWN_0044 = 0x0044, TRANS_UNKNOWN_0045 = 0x0045, TRANS_UNKNOWN_0046 = 0x0046, TRANS_UNKNOWN_0047 = 0x0047, TRANS_UNKNOWN_0048 = 0x0048, TRANS_UNKNOWN_0049 = 0x0049, TRANS_UNKNOWN_004A = 0x004A, TRANS_UNKNOWN_004B = 0x004B, TRANS_UNKNOWN_004C = 0x004C, TRANS_UNKNOWN_004D = 0x004D, TRANS_UNKNOWN_004E = 0x004E, TRANS_UNKNOWN_004F = 0x004F, TRANS_UNKNOWN_0050 = 0x0050, TRANS_UNKNOWN_0051 = 0x0051, TRANS_UNKNOWN_0052 = 0x0052, TRANS_WAIT_NMPIPE = 0x0053, TRANS_CALL_NMPIPE = 0x0054, TRANS_SUBCOM_MAX = 0x0055 } SmbTransactionSubcommand; typedef enum _SmbTransaction2Subcommand { TRANS2_OPEN2 = 0x0000, TRANS2_FIND_FIRST2 = 0x0001, TRANS2_FIND_NEXT2 = 0x0002, TRANS2_QUERY_FS_INFORMATION = 0x0003, TRANS2_SET_FS_INFORMATION = 0x0004, TRANS2_QUERY_PATH_INFORMATION = 0x0005, TRANS2_SET_PATH_INFORMATION = 0x0006, TRANS2_QUERY_FILE_INFORMATION = 0x0007, TRANS2_SET_FILE_INFORMATION = 0x0008, TRANS2_FSCTL = 0x0009, TRANS2_IOCTL2 = 0x000A, TRANS2_FIND_NOTIFY_FIRST = 0x000B, TRANS2_FIND_NOTIFY_NEXT = 0x000C, TRANS2_CREATE_DIRECTORY = 0x000D, TRANS2_SESSION_SETUP = 0x000E, TRANS2_UNKNOWN_000F = 0x000F, TRANS2_GET_DFS_REFERRAL = 0x0010, TRANS2_REPORT_DFS_INCONSISTENCY = 0x0011, TRANS2_SUBCOM_MAX = 0x0012 } SmbTransaction2Subcommand; typedef enum _SmbNtTransactSubcommand { NT_TRANSACT_UNKNOWN_0000 = 0x0000, NT_TRANSACT_CREATE = 0x0001, NT_TRANSACT_IOCTL = 0x0002, NT_TRANSACT_SET_SECURITY_DESC = 0x0003, NT_TRANSACT_NOTIFY_CHANGE = 0x0004, NT_TRANSACT_RENAME = 0x0005, NT_TRANSACT_QUERY_SECURITY_DESC = 0x0006, NT_TRANSACT_SUBCOM_MAX = 0x0007 } SmbNtTransactSubcommand; /******************************************************************** * Structures and inline accessor functions ********************************************************************/ /* Pack the structs since we'll be laying them on top of packet data */ #ifdef WIN32 #pragma pack(push,smb_hdrs,1) #else #pragma pack(1) #endif /******************************************************************** * NetBIOS Session Service header ********************************************************************/ typedef struct _NbssHdr { uint8_t type; uint8_t flags; /* Treat flags as the upper byte to length */ uint16_t length; } NbssHdr; /*NbssLen should be used by SMB1 */ static inline uint32_t NbssLen(const NbssHdr *nb) { /* Treat first bit of flags as the upper byte to length * [MS-SMB] 2.1 Transport - Length can be maximum 0x1FFFF*/ return ((nb->flags & 0x01) << 16) | ntohs(nb->length); } /*NbssLen2 should be used by SMB2/SMB3 */ static inline uint32_t NbssLen2(const NbssHdr *nb) { /*The Length is 3 bytes. [MS-SMB2] 2.1 Transport*/ return ((nb->flags << 16) | ntohs(nb->length)); } static inline uint8_t NbssType(const NbssHdr *nb) { return nb->type; } /******************************************************************** * SMB headers * * Length of header is always 32 but meaning of fields differs * according to dialect ********************************************************************/ typedef struct _SmbCoreHdr { uint8_t smb_idf[4]; /* contains 0xFF, 'SMB' */ uint8_t smb_com; /* command code */ uint8_t smb_rcls; /* error code class */ uint8_t smb_reh; /* reserved (contains AH if DOS INT-24 ERR) */ uint16_t smb_err; /* error code */ uint8_t smb_reb; /* reserved */ uint16_t smb_res[7]; /* reserved */ uint16_t smb_tid; /* tree id # */ uint16_t smb_pid; /* caller's process id # */ uint16_t smb_uid; /* user id # */ uint16_t smb_mid; /* multiplex id # */ #if 0 uint8_t smb_wct; /* count of parameter words */ uint16_t smb_vwv[]; /* variable # words of params */ uint16_t smb_bcc; /* # bytes of data following */ uint8_t smb_data[]; /* data bytes */ #endif } SmbCoreHdr; typedef struct _SmbLm10Hdr { uint8_t smb_idf[4]; /* contains 0xFF, 'SMB' */ uint8_t smb_com; /* command code */ uint8_t smb_rcls; /* error class */ uint8_t smb_reh; /* reserved for future */ uint16_t smb_err; /* error code */ uint8_t smb_flg; /* flags */ uint16_t smb_res[7]; /* reserved for future */ uint16_t smb_tid; /* authenticated resource identifier */ uint16_t smb_pid; /* caller's process id */ uint16_t smb_uid; /* unauthenticated user id */ uint16_t smb_mid; /* multiplex id */ #if 0 uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_vwv[]; /* variable number of 16-bit words */ uint16_t smb_bcc; /* count of bytes that follow */ uint8_t smb_buf[]; /* variable number of bytes */ #endif } SmbLm10Hdr; typedef struct _SmbLm20Hdr { uint8_t smb_idf[4]; /* contains 0xFF,’SMB’ */ uint8_t smb_com; /* command code */ uint8_t smb_rcls; /* error class */ uint8_t smb_reh; /* reserved for future */ uint16_t smb_err; /* error code */ uint8_t smb_flg; /* flags */ uint16_t smb_flg2; /* flags */ uint16_t smb_res[6]; /* reserved for future */ uint16_t smb_tid; /* authenticated resource identifier */ uint16_t smb_pid; /* caller’s process id */ uint16_t smb_uid; /* authenticated user id */ uint16_t smb_mid; /* multiplex id */ #if 0 uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_vwv[]; /* variable number of 16-bit words */ uint16_t smb_bcc; /* count of bytes that follow */ uint8_t smb_buf[]; /* variable number of bytes */ #endif } SmbLm20Hdr; typedef struct _SmbNtHdr { uint8_t smb_idf[4]; /* contains 0xFF, 'SMB' */ uint8_t smb_com; /* command code */ union { struct { uint8_t smb_class; /* dos error class */ uint8_t smb_res; /* reserved for future */ uint16_t smb_code; /* dos error code */ } smb_status; uint32_t nt_status; /* nt status */ } smb_status; uint8_t smb_flg; /* flags */ uint16_t smb_flg2; /* flags */ uint16_t smb_pid_high; uint64_t smb_signature; uint16_t smb_res; /* reserved for future */ uint16_t smb_tid; /* tree id */ uint16_t smb_pid; /* caller's process id */ uint16_t smb_uid; /* authenticated user id */ uint16_t smb_mid; /* multiplex id */ #if 0 uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_vwv[]; /* variable number of 16-bit words */ uint16_t smb_bcc; /* count of bytes that follow */ uint8_t smb_buf[]; /* variable number of bytes */ #endif } SmbNtHdr; static inline uint16_t SmbNtohs(const uint16_t *ptr) { uint16_t value; if (ptr == NULL) return 0; #ifdef WORDS_MUSTALIGN value = *((uint8_t *)ptr) << 8 | *((uint8_t *)ptr + 1); #else value = *ptr; #endif /* WORDS_MUSTALIGN */ #ifdef WORDS_BIGENDIAN return ((value & 0xff00) >> 8) | ((value & 0x00ff) << 8); #else return value; #endif /* WORDS_BIGENDIAN */ } static inline uint16_t SmbHtons(const uint16_t *ptr) { return SmbNtohs(ptr); } static inline uint32_t SmbNtohl(const uint32_t *ptr) { uint32_t value; if (ptr == NULL) return 0; #ifdef WORDS_MUSTALIGN value = *((uint8_t *)ptr) << 24 | *((uint8_t *)ptr + 1) << 16 | *((uint8_t *)ptr + 2) << 8 | *((uint8_t *)ptr + 3); #else value = *ptr; #endif /* WORDS_MUSTALIGN */ #ifdef WORDS_BIGENDIAN return ((value & 0xff000000) >> 24) | ((value & 0x00ff0000) >> 8) | ((value & 0x0000ff00) << 8) | ((value & 0x000000ff) << 24); #else return value; #endif /* WORDS_BIGENDIAN */ } static inline uint32_t SmbHtonl(const uint32_t *ptr) { return SmbNtohl(ptr); } static inline uint64_t SmbNtohq(const uint64_t *ptr) { uint64_t value; if (ptr == NULL) return 0; #ifdef WORDS_MUSTALIGN value = *((uint8_t *)ptr) << 56 | *((uint8_t *)ptr + 1) << 48 | *((uint8_t *)ptr + 2) << 40 | *((uint8_t *)ptr + 3) << 32 | *((uint8_t *)ptr + 4) << 24 | *((uint8_t *)ptr + 5) << 16 | *((uint8_t *)ptr + 6) << 8 | *((uint8_t *)ptr + 7); #else value = *ptr; #endif /* WORDS_MUSTALIGN */ #ifdef WORDS_BIGENDIAN return ((value & 0xff00000000000000) >> 56) | ((value & 0x00ff000000000000) >> 40) | ((value & 0x0000ff0000000000) >> 24) | ((value & 0x000000ff00000000) >> 8) | ((value & 0x00000000ff000000) << 8) | ((value & 0x0000000000ff0000) << 24) | ((value & 0x000000000000ff00) << 40) | ((value & 0x00000000000000ff) << 56); #else return value; #endif /* WORDS_BIGENDIAN */ } static inline uint64_t SmbHtonq(const uint64_t *ptr) { return SmbNtohq(ptr); } static inline uint32_t SmbId(const SmbNtHdr *hdr) { #ifdef WORDS_MUSTALIGN uint8_t *idf = (uint8_t *)hdr->smb_idf; return *idf << 24 | *(idf + 1) << 16 | *(idf + 2) << 8 | *(idf + 3); #else uint32_t *tmp = (uint32_t *)hdr->smb_idf; return ntohl(*tmp); #endif /* WORDS_MUSTALIGN */ } static inline uint8_t SmbCom(const SmbNtHdr *hdr) { return hdr->smb_com; } #if 0 // So no compiler warning for being unused // Refers to older pre-NT status codes static bool SmbStatusSmbCodes(const SmbNtHdr *hdr) { if (SmbNtohs(&hdr->smb_flg2) & SMB_FLG2__NT_CODES) return false; return true; } #endif static bool SmbStatusNtCodes(const SmbNtHdr *hdr) { if (SmbNtohs(&hdr->smb_flg2) & SMB_FLG2__NT_CODES) return true; return false; } static inline uint32_t SmbNtStatus(const SmbNtHdr *hdr) { return SmbNtohl(&hdr->smb_status.nt_status); } static inline uint8_t SmbNtStatusSeverity(const SmbNtHdr *hdr) { return (uint8_t)(SmbNtStatus(hdr) >> 30); } static inline uint8_t SmbStatusClass(const SmbNtHdr *hdr) { return hdr->smb_status.smb_status.smb_class; } static inline uint16_t SmbStatusCode(const SmbNtHdr *hdr) { return SmbNtohs(&hdr->smb_status.smb_status.smb_code); } // This function is obviously deficient. Need to do a lot more // testing, research and reading MS-CIFS, MS-SMB and MS-ERREF. static inline bool SmbError(const SmbNtHdr *hdr) { if (SmbStatusNtCodes(hdr)) { /* Nt status codes are being used. First 2 bits indicate * severity. */ switch (SmbNtStatusSeverity(hdr)) { case SMB_NT_STATUS_SEVERITY__SUCCESS: case SMB_NT_STATUS_SEVERITY__INFORMATIONAL: case SMB_NT_STATUS_SEVERITY__WARNING: return false; case SMB_NT_STATUS_SEVERITY__ERROR: default: break; } } else { switch (SmbStatusClass(hdr)) { case SMB_ERROR_CLASS__SUCCESS: return false; case SMB_ERROR_CLASS__ERRDOS: if (SmbStatusCode(hdr) == SMB_ERRDOS__MORE_DATA) return false; break; case SMB_ERROR_CLASS__ERRSRV: case SMB_ERROR_CLASS__ERRHRD: case SMB_ERROR_CLASS__ERRCMD: default: break; } } return true; } static inline bool SmbBrokenPipe(const SmbNtHdr *hdr) { if (SmbStatusNtCodes(hdr)) { uint32_t nt_status = SmbNtStatus(hdr); if ((nt_status == SMB_NT_STATUS__PIPE_BROKEN) || (nt_status == SMB_NT_STATUS__PIPE_DISCONNECTED)) return true; } else { if (SmbStatusClass(hdr) == SMB_ERROR_CLASS__ERRDOS) { uint16_t smb_status = SmbStatusCode(hdr); if ((smb_status == SMB_ERRDOS__BAD_PIPE) || (smb_status == SMB_ERRDOS__PIPE_NOT_CONNECTED)) return true; } } return false; } static inline bool SmbErrorInvalidDeviceRequest(const SmbNtHdr *hdr) { if (SmbStatusNtCodes(hdr)) { if (SmbNtStatus(hdr) == SMB_NT_STATUS__INVALID_DEVICE_REQUEST) return true; } else { if ((SmbStatusClass(hdr) == SMB_ERROR_CLASS__ERRSRV) && (SmbStatusCode(hdr) == SMB_ERRSRV__INVALID_DEVICE)) return true; } return false; } static inline bool SmbErrorRangeNotLocked(const SmbNtHdr *hdr) { if (SmbStatusNtCodes(hdr)) { if (SmbNtStatus(hdr) == SMB_NT_STATUS__RANGE_NOT_LOCKED) return true; } else { if ((SmbStatusClass(hdr) == SMB_ERROR_CLASS__ERRDOS) && (SmbStatusCode(hdr) == SMB_ERRDOS__NOT_LOCKED)) return true; } return false; } static inline int SmbType(const SmbNtHdr *hdr) { if (hdr->smb_flg & SMB_FLG__TYPE) return SMB_TYPE__RESPONSE; return SMB_TYPE__REQUEST; } static inline bool SmbUnicode(const SmbNtHdr *hdr) { return (SmbNtohs(&hdr->smb_flg2) & SMB_FLG2__UNICODE) ? true : false; } static inline uint16_t SmbUid(const SmbNtHdr *hdr) { return SmbNtohs(&hdr->smb_uid); } static inline uint16_t SmbTid(const SmbNtHdr *hdr) { return SmbNtohs(&hdr->smb_tid); } // PidHigh doesn't seem to matter ever static inline uint16_t SmbPid(const SmbNtHdr *hdr) { return SmbNtohs(&hdr->smb_pid); } static inline uint16_t SmbMid(const SmbNtHdr *hdr) { return SmbNtohs(&hdr->smb_mid); } /******************************************************************** * Common fields to all commands ********************************************************************/ typedef struct _SmbCommon { uint8_t smb_wct; } SmbCommon; static inline uint8_t SmbWct(const SmbCommon *hdr) { return hdr->smb_wct; } /* Common fields to all AndX commands */ typedef struct _SmbAndXCommon { uint8_t smb_wct; uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ } SmbAndXCommon; static inline uint8_t SmbAndXCom2(const SmbAndXCommon *andx) { return andx->smb_com2; } static inline uint16_t SmbAndXOff2(const SmbAndXCommon *andx) { return SmbNtohs(&andx->smb_off2); } /* For server empty respones indicating client error or interim response */ typedef struct _SmbEmptyCom { uint8_t smb_wct; /* value = 0 */ uint16_t smb_bcc; /* value = 0 */ } SmbEmptyCom; static inline uint8_t SmbEmptyComWct(const SmbEmptyCom *ec) { return ec->smb_wct; } static inline uint16_t SmbEmptyComBcc(const SmbEmptyCom *ec) { return SmbNtohs(&ec->smb_bcc); } static inline uint16_t SmbBcc(const uint8_t *ptr, uint16_t com_size) { /* com_size must be at least the size of the command encasing */ if (com_size < sizeof(SmbEmptyCom)) return 0; return SmbNtohs((uint16_t *)(ptr + com_size - sizeof(uint16_t))); } /******************************************************************** * SMB_COM_OPEN ********************************************************************/ typedef struct _SmbOpenReq /* smb_wct = 2 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_mode; /* r/w/share */ uint16_t smb_attr; /* attribute */ uint16_t smb_bcc; /* min = 2 */ #if 0 uint8_t smb_fmt; /* ASCII -- 04 */ uint8_t smb_buf[]; /* file pathname */ #endif } SmbOpenReq; typedef struct _SmbOpenResp /* smb_wct = 7 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle */ uint16_t smb_attr; /* attribute */ uint32_t smb_time; /* time1 low */ uint32_t smb_file_size; /* file size low */ uint16_t smb_access; /* access allowed */ uint16_t smb_bcc; /* must be 0 */ } SmbOpenResp; #define SMB_OPEN_ACCESS_MODE__READ 0x0000 #define SMB_OPEN_ACCESS_MODE__WRITE 0x0001 #define SMB_OPEN_ACCESS_MODE__READ_WRITE 0x0002 #define SMB_OPEN_ACCESS_MODE__EXECUTE 0x0003 static inline uint16_t SmbOpenRespFid(const SmbOpenResp *resp) { return SmbNtohs(&resp->smb_fid); } static inline uint32_t SmbOpenRespFileSize(const SmbOpenResp *resp) { return SmbNtohl(&resp->smb_file_size); } static inline uint16_t SmbOpenRespFileAttrs(const SmbOpenResp *resp) { return SmbNtohs(&resp->smb_attr); } static inline bool SmbFileAttrsDirectory(const uint16_t file_attrs) { if (file_attrs & SMB_FILE_ATTRIBUTE_DIRECTORY) return true; return false; } static inline uint16_t SmbOpenRespAccessMode(const SmbOpenResp *resp) { return SmbNtohs(&resp->smb_access); } static inline bool SmbOpenForWriting(const uint16_t access_mode) { return access_mode == SMB_OPEN_ACCESS_MODE__WRITE; } /******************************************************************** * SMB_COM_CREATE ********************************************************************/ typedef struct _SmbCreateReq /* smb_wct = 3 */ { uint8_t smb_wct; uint16_t smb_file_attrs; uint32_t smb_creation_time; uint16_t smb_bcc; #if 0 uint8_t smb_fmt; /* ASCII -- 04 */ uint8_t smb_buf[]; /* file pathname */ #endif } SmbCreateReq; typedef struct _SmbCreateResp /* smb_wct = 1 */ { uint8_t smb_wct; uint16_t smb_fid; uint16_t smb_bcc; } SmbCreateResp; static inline uint16_t SmbCreateReqFileAttrs(const SmbCreateReq *req) { return SmbNtohs(&req->smb_file_attrs); } static inline bool SmbAttrDirectory(const uint16_t file_attrs) { if (file_attrs & SMB_FILE_ATTRIBUTE_DIRECTORY) return true; return false; } static inline bool SmbAttrHidden(const uint16_t file_attrs) { if (file_attrs & SMB_FILE_ATTRIBUTE_HIDDEN) return true; return false; } static inline bool SmbAttrSystem(const uint16_t file_attrs) { if (file_attrs & SMB_FILE_ATTRIBUTE_SYSTEM) return true; return false; } static inline uint16_t SmbCreateRespFid(const SmbCreateResp *resp) { return SmbNtohs(&resp->smb_fid); } /******************************************************************** * SMB_COM_CLOSE ********************************************************************/ typedef struct _SmbCloseReq /* smb_wct = 3 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle */ uint16_t smb_tlow; /* time low */ uint16_t smb_thigh; /* time high */ uint16_t smb_bcc; /* must be 0 */ } SmbCloseReq; typedef struct _SmbCloseResp /* smb_wct = 0 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_bcc; /* must be 0 */ } SmbCloseResp; static inline uint16_t SmbCloseReqFid(const SmbCloseReq *req) { return SmbNtohs(&req->smb_fid); } /******************************************************************** * SMB_COM_DELETE ********************************************************************/ typedef struct _SmbDeleteReq /* smb_wct = 1 */ { uint8_t smb_wct; uint16_t smb_search_attrs; uint16_t smb_bcc; #if 0 uint8_t smb_fmt; /* ASCII -- 04 */ uint8_t smb_buf[]; /* filename */ #endif } SmbDeleteReq; typedef struct _SmbDeleteResp /* smb_wct = 0 */ { uint8_t smb_wct; uint16_t smb_bcc; } SmbDeleteResp; /******************************************************************** * SMB_COM_RENAME ********************************************************************/ typedef struct _SmbRenameReq /* smb_wct = 1 */ { uint8_t smb_wct; uint16_t smb_attrs; uint16_t smb_bcc; #if 0 uint8_t smb_fmt; /* ASCII -- 04 */ uint8_t smb_buf[]; /* old filename */ uint8_t smb_fmt2; /* ASCII -- 04 */ uint8_t smb_buf2[]; /* new filename */ #endif } SmbRenameReq; typedef struct _SmbRenameResp /* smb_wct = 0 */ { uint8_t smb_wct; uint16_t smb_bcc; } SmbRenameResp; /******************************************************************** * SMB_COM_READ ********************************************************************/ typedef struct _SmbReadReq /* smb_wct = 5 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle */ uint16_t smb_cnt; /* count of bytes */ uint32_t smb_off; /* offset */ uint16_t smb_left; /* count left */ uint16_t smb_bcc; /* must be 0 */ } SmbReadReq; typedef struct _SmbReadResp /* smb_wct = 5 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_cnt; /* count */ uint16_t smb_res[4]; /* reserved (MBZ) */ uint16_t smb_bcc; /* length of data + 3 */ #if 0 uint8_t smb_fmt; /* Data Block -- 01 */ uint16_t smb_dlen; /* length of data */ uint8_t smb_buf[]; /* data */ #endif } SmbReadResp; static inline uint16_t SmbReadReqFid(const SmbReadReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint32_t SmbReadReqOffset(const SmbReadReq *req) { return SmbNtohl(&req->smb_off); } static inline uint16_t SmbReadRespCount(const SmbReadResp *resp) { return SmbNtohs(&resp->smb_cnt); } /******************************************************************** * SMB_COM_WRITE ********************************************************************/ typedef struct _SmbWriteReq /* smb_wct = 5 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle */ uint16_t smb_cnt; /* count of bytes */ uint32_t smb_offset; /* file offset in bytes */ uint16_t smb_left; /* count left */ uint16_t smb_bcc; /* length of data + 3 */ #if 0 uint16_t smb_fmt; /* Data Block -- 01 */ uint16_t smb_dlen; /* length of data */ uint8_t smb_buf[]; /* data */ #endif } SmbWriteReq; typedef struct _SmbWriteResp /* smb_wct = 1 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_cnt; /* count */ uint16_t smb_bcc; /* must be 0 */ } SmbWriteResp; static inline uint16_t SmbWriteReqFid(const SmbWriteReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint16_t SmbWriteReqCount(const SmbWriteReq *req) { return SmbNtohs(&req->smb_cnt); } static inline uint32_t SmbWriteReqOffset(const SmbWriteReq *req) { return SmbNtohl(&req->smb_offset); } static inline uint16_t SmbWriteRespCount(const SmbWriteResp *resp) { return SmbNtohs(&resp->smb_cnt); } /******************************************************************** * SMB_COM_CREATE_NEW ********************************************************************/ typedef struct _SmbCreateNewReq /* smb_wct = 3 */ { uint8_t smb_wct; uint16_t smb_file_attrs; uint32_t smb_creation_time; uint16_t smb_bcc; #if 0 uint8_t smb_fmt; /* ASCII -- 04 */ uint8_t smb_buf[]; /* file pathname */ #endif } SmbCreateNewReq; typedef struct _SmbCreateNewResp /* smb_wct = 1 */ { uint8_t smb_wct; uint16_t smb_fid; uint16_t smb_bcc; } SmbCreateNewResp; static inline uint16_t SmbCreateNewReqFileAttrs(const SmbCreateNewReq *req) { return SmbNtohs(&req->smb_file_attrs); } static inline uint16_t SmbCreateNewRespFid(const SmbCreateNewResp *resp) { return SmbNtohs(&resp->smb_fid); } /******************************************************************** * SMB_COM_LOCK_AND_READ ********************************************************************/ typedef struct _SmbLockAndReadReq /* smb_wct = 5 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; uint16_t smb_cnt; uint32_t smb_read_offset; uint16_t smb_remaining; uint16_t smb_bcc; /* must be 0 */ } SmbLockAndReadReq; typedef struct _SmbLockAndReadResp /* smb_wct = 5 */ { uint8_t smb_wct; uint16_t smb_cnt; uint16_t reserved[4]; uint16_t smb_bcc; #if 0 uint16_t smb_fmt; /* Data Block -- 01 */ uint16_t smb_dlen; /* length of data */ uint8_t smb_buf[]; /* data */ #endif } SmbLockAndReadResp; static inline uint16_t SmbLockAndReadReqFid(const SmbLockAndReadReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint32_t SmbLockAndReadReqOffset(const SmbLockAndReadReq *req) { return SmbNtohl(&req->smb_read_offset); } static inline uint16_t SmbLockAndReadRespCount(const SmbLockAndReadResp *resp) { return SmbNtohs(&resp->smb_cnt); } /******************************************************************** * SMB_COM_WRITE_AND_UNLOCK ********************************************************************/ typedef struct _SmbWriteAndUnlockReq { uint8_t smb_wct; uint16_t smb_fid; uint16_t smb_cnt; uint32_t smb_write_offset; uint16_t smb_estimate_of_remaining; uint16_t smb_bcc; #if 0 uint16_t smb_fmt; /* Data Block -- 01 */ uint16_t smb_dlen; /* length of data */ uint8_t smb_buf[]; /* data */ #endif } SmbWriteAndUnlockReq; typedef struct _SmbWriteAndUnlockResp /* smb_wct = 1 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_cnt; /* count */ uint16_t smb_bcc; /* must be 0 */ } SmbWriteAndUnlockResp; static inline uint16_t SmbWriteAndUnlockReqFid(const SmbWriteAndUnlockReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint16_t SmbWriteAndUnlockReqCount(const SmbWriteAndUnlockReq *req) { return SmbNtohs(&req->smb_cnt); } static inline uint32_t SmbWriteAndUnlockReqOffset(const SmbWriteAndUnlockReq *req) { return SmbNtohl(&req->smb_write_offset); } /******************************************************************** * SMB_COM_READ_RAW ********************************************************************/ typedef struct _SmbReadRawReq /* smb_wct = 8 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle */ uint32_t smb_offset; /* offset in file to begin read */ uint16_t smb_maxcnt; /* max number of bytes to return (max 65,535) */ uint16_t smb_mincnt; /* min number of bytes to return (normally 0) */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_rsvd; /* reserved */ uint16_t smb_bcc; /* value = 0 */ } SmbReadRawReq; typedef struct _SmbReadRawExtReq /* smb_wct = 10 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle */ uint32_t smb_offset; /* offset in file to begin read */ uint16_t smb_maxcnt; /* max number of bytes to return (max 65,535) */ uint16_t smb_mincnt; /* min number of bytes to return (normally 0) */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_rsvd; /* reserved */ uint32_t smb_off_high; /* high offset in file to begin write */ uint16_t smb_bcc; /* value = 0 */ } SmbReadRawExtReq; /* Read Raw response is raw data wrapped in NetBIOS header */ static inline uint16_t SmbReadRawReqFid(const SmbReadRawReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint64_t SmbReadRawReqOffset(const SmbReadRawExtReq *req) { if (req->smb_wct == 8) return (uint64_t)SmbNtohl(&req->smb_offset); return (uint64_t)SmbNtohl(&req->smb_off_high) << 32 | (uint64_t)SmbNtohl(&req->smb_offset); } /******************************************************************** * SMB_COM_WRITE_RAW ********************************************************************/ typedef struct _SmbWriteRawReq { uint8_t smb_wct; /* value = 12 */ uint16_t smb_fid; /* file handle */ uint16_t smb_tcount; /* total bytes (including this buf, 65,535 max ) */ uint16_t smb_rsvd; /* reserved */ uint32_t smb_offset; /* offset in file to begin write */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_wmode; /* write mode: bit0 - complete write to disk and send final result response bit1 - return smb_remaining (pipes/devices only) */ uint32_t smb_rsvd2; /* reserved */ uint16_t smb_dsize; /* number of data bytes this buffer (min value = 0) */ uint16_t smb_doff; /* offset (from start of SMB hdr) to data bytes */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_dsize) */ #endif } SmbWriteRawReq; typedef struct _SmbWriteRawExtReq { uint8_t smb_wct; /* value = 14 */ uint16_t smb_fid; /* file handle */ uint16_t smb_tcount; /* total bytes (including this buf, 65,535 max ) */ uint16_t smb_rsvd; /* reserved */ uint32_t smb_offset; /* offset in file to begin write */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_wmode; /* write mode: bit0 - complete write to disk and send final result response bit1 - return smb_remaining (pipes/devices only) */ uint32_t smb_rsvd2; /* reserved */ uint16_t smb_dsize; /* number of data bytes this buffer (min value = 0) */ uint16_t smb_doff; /* offset (from start of SMB hdr) to data bytes */ uint32_t smb_off_high; /* high offset in file to begin write */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_dsize) */ #endif } SmbWriteRawExtReq; typedef struct _SmbWriteRawInterimResp { uint8_t smb_wct; /* value = 1 */ uint16_t smb_remaining; /* bytes remaining to be read (pipes/devices only) */ uint16_t smb_bcc; /* value = 0 */ } SmbWriteRawInterimResp; static inline uint16_t SmbWriteRawReqTotalCount(const SmbWriteRawReq *req) { return SmbNtohs(&req->smb_tcount); } static inline bool SmbWriteRawReqWriteThrough(const SmbWriteRawReq *req) { return SmbNtohs(&req->smb_wmode) & 0x0001; } static inline uint16_t SmbWriteRawReqFid(const SmbWriteRawReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint16_t SmbWriteRawReqDataOff(const SmbWriteRawReq *req) { return SmbNtohs(&req->smb_doff); } static inline uint16_t SmbWriteRawReqDataCnt(const SmbWriteRawReq *req) { return SmbNtohs(&req->smb_dsize); } static inline uint64_t SmbWriteRawReqOffset(const SmbWriteRawExtReq *req) { if (req->smb_wct == 12) return (uint64_t)SmbNtohl(&req->smb_offset); return (uint64_t)SmbNtohl(&req->smb_off_high) << 32 | (uint64_t)SmbNtohl(&req->smb_offset); } static inline uint16_t SmbWriteRawInterimRespRemaining(const SmbWriteRawInterimResp *resp) { return SmbNtohs(&resp->smb_remaining); } /******************************************************************** * SMB_COM_WRITE_COMPLETE - final response to an SMB_COM_WRITE_RAW ********************************************************************/ typedef struct _SmbWriteCompleteResp { uint8_t smb_wct; /* value = 1 */ uint16_t smb_count; /* total number of bytes written */ uint16_t smb_bcc; /* value = 0 */ } SmbWriteCompleteResp; static inline uint16_t SmbWriteCompleteRespCount(const SmbWriteCompleteResp *resp) { return SmbNtohs(&resp->smb_count); } /******************************************************************** * SMB_COM_TRANSACTION ********************************************************************/ typedef struct _SmbTransactionReq /* smb_wct = 14 + value of smb_suwcnt */ { /* Note all subcommands use a setup count of 2 */ uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_tpscnt; /* total number of parameter bytes being sent */ uint16_t smb_tdscnt; /* total number of data bytes being sent */ uint16_t smb_mprcnt; /* max number of parameter bytes to return */ uint16_t smb_mdrcnt; /* max number of data bytes to return */ uint8_t smb_msrcnt; /* max number of setup words to return */ uint8_t smb_rsvd; /* reserved (pad above to word) */ uint16_t smb_flags; /* additional information: bit 0 - if set, also disconnect TID in smb_tid bit 1 - if set, transaction is one way (no final response) */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_rsvd1; /* reserved */ uint16_t smb_pscnt; /* number of parameter bytes being sent this buffer */ uint16_t smb_psoff; /* offset (from start of SMB hdr) to parameter bytes */ uint16_t smb_dscnt; /* number of data bytes being sent this buffer */ uint16_t smb_dsoff; /* offset (from start of SMB hdr) to data bytes */ uint8_t smb_suwcnt; /* set up word count */ uint8_t smb_rsvd2; /* reserved (pad above to word) */ uint16_t smb_setup1; /* function (see below) TRANS_SET_NM_PIPE_STATE = 0x0001 TRANS_RAW_READ_NMPIPE = 0x0011 TRANS_QUERY_NMPIPE_STATE = 0x0021 TRANS_QUERY_NMPIPE_INFO = 0x0022 TRANS_PEEK_NMPIPE = 0x0023 TRANS_TRANSACT_NMPIPE = 0x0026 TRANS_RAW_WRITE_NMPIPE = 0x0031 TRANS_READ_NMPIPE = 0x0036 TRANS_WRITE_NMPIPE = 0x0037 TRANS_WAIT_NMPIPE = 0x0053 TRANS_CALL_NMPIPE = 0x0054 */ uint16_t smb_setup2; /* FID (handle) of pipe (if needed), or priority */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_name[]; /* name of transaction */ uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_param[*]; /* param bytes (* = value of smb_pscnt) */ uint8_t smb_pad1[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_dscnt) */ #endif } SmbTransactionReq; typedef struct _SmbTransactionInterimResp /* smb_wct = 0 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_bcc; /* must be 0 */ } SmbTransactionInterimResp; typedef struct _SmbTransactionResp /* smb_wct = 10 + value of smb_suwcnt */ { /* Note all subcommands use a setup count of 0 */ uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_tprcnt; /* total number of parameter bytes being returned */ uint16_t smb_tdrcnt; /* total number of data bytes being returned */ uint16_t smb_rsvd; /* reserved */ uint16_t smb_prcnt; /* number of parameter bytes being returned this buf */ uint16_t smb_proff; /* offset (from start of SMB hdr) to parameter bytes */ uint16_t smb_prdisp; /* byte displacement for these parameter bytes */ uint16_t smb_drcnt; /* number of data bytes being returned this buffer */ uint16_t smb_droff; /* offset (from start of SMB hdr) to data bytes */ uint16_t smb_drdisp; /* byte displacement for these data bytes */ uint8_t smb_suwcnt; /* set up return word count */ uint8_t smb_rsvd1; /* reserved (pad above to word) */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_param[*]; /* param bytes (* = value of smb_prcnt) */ uint8_t smb_pad1[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_drcnt) */ #endif } SmbTransactionResp; static inline uint16_t SmbTransactionReqSubCom(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_setup1); } static inline uint16_t SmbTransactionReqFid(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_setup2); } static inline bool SmbTransactionReqDisconnectTid(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_flags) & 0x0001 ? true : false; } static inline bool SmbTransactionReqOneWay(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_flags) & 0x0002 ? true : false; } static inline uint8_t SmbTransactionReqSetupCnt(const SmbTransactionReq *req) { return req->smb_suwcnt; } static inline uint16_t SmbTransactionReqTotalDataCnt(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_tdscnt); } static inline uint16_t SmbTransactionReqDataCnt(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_dscnt); } static inline uint16_t SmbTransactionReqDataOff(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_dsoff); } static inline uint16_t SmbTransactionReqTotalParamCnt(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_tpscnt); } static inline uint16_t SmbTransactionReqParamCnt(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_pscnt); } static inline uint16_t SmbTransactionReqParamOff(const SmbTransactionReq *req) { return SmbNtohs(&req->smb_psoff); } static inline uint16_t SmbTransactionRespTotalDataCnt(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_tdrcnt); } static inline uint16_t SmbTransactionRespDataCnt(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_drcnt); } static inline uint16_t SmbTransactionRespDataOff(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_droff); } static inline uint16_t SmbTransactionRespDataDisp(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_drdisp); } static inline uint16_t SmbTransactionRespTotalParamCnt(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_tprcnt); } static inline uint16_t SmbTransactionRespParamCnt(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_prcnt); } static inline uint16_t SmbTransactionRespParamOff(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_proff); } static inline uint16_t SmbTransactionRespParamDisp(const SmbTransactionResp *resp) { return SmbNtohs(&resp->smb_prdisp); } // Flags for TRANS_SET_NMPIPE_STATE parameters #define PIPE_STATE_NON_BLOCKING 0x8000 #define PIPE_STATE_MESSAGE_MODE 0x0100 /******************************************************************** * SMB_COM_TRANSACTION_SECONDARY * Continuation command for SMB_COM_TRANSACTION requests if all * data wasn't sent. ********************************************************************/ typedef struct _SmbTransactionSecondaryReq /* smb_wct = 8 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_tpscnt; /* total number of parameter bytes being sent */ uint16_t smb_tdscnt; /* total number of data bytes being sent */ uint16_t smb_pscnt; /* number of parameter bytes being sent this buffer */ uint16_t smb_psoff; /* offset (from start of SMB hdr) to parameter bytes */ uint16_t smb_psdisp; /* byte displacement for these parameter bytes */ uint16_t smb_dscnt; /* number of data bytes being sent this buffer */ uint16_t smb_dsoff; /* offset (from start of SMB hdr) to data bytes */ uint16_t smb_dsdisp; /* byte displacement for these data bytes */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_param[*]; /* param bytes (* = value of smb_pscnt) */ uint8_t smb_pad1[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_dscnt) */ #endif } SmbTransactionSecondaryReq; static inline uint16_t SmbTransactionSecondaryReqTotalDataCnt(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_tdscnt); } static inline uint16_t SmbTransactionSecondaryReqDataCnt(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_dscnt); } static inline uint16_t SmbTransactionSecondaryReqDataOff(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_dsoff); } static inline uint16_t SmbTransactionSecondaryReqDataDisp(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_dsdisp); } static inline uint16_t SmbTransactionSecondaryReqTotalParamCnt(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_tpscnt); } static inline uint16_t SmbTransactionSecondaryReqParamCnt(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_pscnt); } static inline uint16_t SmbTransactionSecondaryReqParamOff(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_psoff); } static inline uint16_t SmbTransactionSecondaryReqParamDisp(const SmbTransactionSecondaryReq *req) { return SmbNtohs(&req->smb_psdisp); } /******************************************************************** * SMB_COM_WRITE_AND_CLOSE ********************************************************************/ typedef struct _SmbWriteAndCloseReq /* smb_wct = 6 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle (close after write) */ uint16_t smb_count; /* number of bytes to write */ uint32_t smb_offset; /* offset in file to begin write */ uint32_t smb_mtime; /* modification time */ uint16_t smb_bcc; /* 1 (for pad) + value of smb_count */ #if 0 uint8_t smb_pad; /* force data to dword boundary */ uint8_t smb_data[]; /* data */ #endif } SmbWriteAndCloseReq; typedef struct _SmbWriteAndCloseExtReq /* smb_wct = 12 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_fid; /* file handle (close after write) */ uint16_t smb_count; /* number of bytes to write */ uint32_t smb_offset; /* offset in file to begin write */ uint32_t smb_mtime; /* modification time */ uint32_t smb_rsvd1; /* Optional */ uint32_t smb_rsvd2; /* Optional */ uint32_t smb_rsvd3; /* Optional */ uint16_t smb_bcc; /* 1 (for pad) + value of smb_count */ #if 0 uint8_t smb_pad; /* force data to dword boundary */ uint8_t smb_data[]; /* data */ #endif } SmbWriteAndCloseExtReq; typedef struct _SmbWriteAndCloseResp /* smb_wct = 1 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_count; /* number of bytes written */ uint16_t smb_bcc; /* must be 0 */ } SmbWriteAndCloseResp; static inline uint16_t SmbWriteAndCloseReqFid(const SmbWriteAndCloseReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint16_t SmbWriteAndCloseReqCount(const SmbWriteAndCloseReq *req) { return SmbNtohs(&req->smb_count); } static inline uint32_t SmbWriteAndCloseReqOffset(const SmbWriteAndCloseReq *req) { return SmbNtohl(&req->smb_offset); } static inline uint16_t SmbWriteAndCloseRespCount(const SmbWriteAndCloseResp *resp) { return SmbNtohs(&resp->smb_count); } /******************************************************************** * SMB_COM_OPEN_ANDX ********************************************************************/ typedef struct _SmbOpenAndXReq /* smb_wct = 15 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_flags; /* additional information: bit 0 - if set, return additional information bit 1 - if set, set single user total file lock (if only access) bit 2 - if set, the server should notify the consumer on any action which can modify the file (delete, setattrib, rename, etc.). if not set, the server need only notify the consumer on another open request. This bit only has meaning if bit 1 is set. */ uint16_t smb_mode; /* file open mode */ uint16_t smb_sattr; /* search attributes */ uint16_t smb_attr; /* file attributes (for create) */ uint32_t smb_time; /* create time */ uint16_t smb_ofun; /* open function */ uint32_t smb_size; /* bytes to reserve on "create" or "truncate" */ uint32_t smb_timeout; /* max milliseconds to wait for resource to open */ uint32_t smb_rsvd; /* reserved (must be zero) */ uint16_t smb_bcc; /* minimum value = 1 */ #if 0 uint8_t smb_pathname[]; /* file pathname */ #endif } SmbOpenAndXReq; typedef struct _SmbOpenAndXResp /* smb_wct = 15 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_fid; /* file handle */ uint16_t smb_attribute; /* attributes of file or device */ uint32_t smb_time; /* last modification time */ uint32_t smb_size; /* current file size */ uint16_t smb_access; /* access permissions actually allowed */ uint16_t smb_type; /* file type */ uint16_t smb_state; /* state of IPC device (e.g. pipe) */ uint16_t smb_action; /* action taken */ uint32_t smb_fileid; /* server unique file id */ uint16_t smb_rsvd; /* reserved */ uint16_t smb_bcc; /* value = 0 */ } SmbOpenAndXResp; static inline uint32_t SmbOpenAndXReqAllocSize(const SmbOpenAndXReq *req) { return SmbNtohl(&req->smb_size); } static inline uint16_t SmbOpenAndXReqFileAttrs(const SmbOpenAndXReq *req) { return SmbNtohs(&req->smb_attr); } static inline uint16_t SmbOpenAndXRespFid(const SmbOpenAndXResp *resp) { return SmbNtohs(&resp->smb_fid); } static inline uint16_t SmbOpenAndXRespFileAttrs(const SmbOpenAndXResp *resp) { return SmbNtohs(&resp->smb_attribute); } static inline uint32_t SmbOpenAndXRespFileSize(const SmbOpenAndXResp *resp) { return SmbNtohl(&resp->smb_size); } static inline uint16_t SmbOpenAndXRespResourceType(const SmbOpenAndXResp *resp) { return SmbNtohs(&resp->smb_type); } #define SMB_OPEN_RESULT__EXISTED 0x0001 #define SMB_OPEN_RESULT__CREATED 0x0002 #define SMB_OPEN_RESULT__TRUNCATED 0x0003 static inline uint16_t SmbOpenAndXRespOpenResults(const SmbOpenAndXResp *resp) { return SmbNtohs(&resp->smb_action); } static inline bool SmbOpenResultRead(const uint16_t open_results) { return ((open_results & 0x00FF) == SMB_OPEN_RESULT__EXISTED); } static inline bool SmbResourceTypeDisk(const uint16_t resource_type) { return resource_type == SMB_FILE_TYPE_DISK; } /******************************************************************** * SMB_COM_READ_ANDX ********************************************************************/ typedef struct _SmbReadAndXReq /* smb_wct = 10 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_fid; /* file handle */ uint32_t smb_offset; /* offset in file to begin read */ uint16_t smb_maxcnt; /* max number of bytes to return */ uint16_t smb_mincnt; /* min number of bytes to return */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_countleft; /* bytes remaining to satisfy user’s request */ uint16_t smb_bcc; /* value = 0 */ } SmbReadAndXReq; typedef struct _SmbReadAndXExtReq /* smb_wct = 12 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_fid; /* file handle */ uint32_t smb_offset; /* low offset in file to begin read */ uint16_t smb_maxcnt; /* max number of bytes to return */ uint16_t smb_mincnt; /* min number of bytes to return */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_countleft; /* bytes remaining to satisfy user’s request */ uint32_t smb_off_high; /* high offset in file to begin read */ uint16_t smb_bcc; /* value = 0 */ } SmbReadAndXExtReq; typedef struct _SmbReadAndXResp /* smb_wct = 12 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_remaining; /* bytes remaining to be read (pipes/devices only) */ uint32_t smb_rsvd; /* reserved */ uint16_t smb_dsize; /* number of data bytes (minimum value = 0) */ uint16_t smb_doff; /* offset (from start of SMB hdr) to data bytes */ uint16_t smb_dsize_high; /* high bytes of data size */ uint32_t smb_rsvd1; /* reserved */ uint32_t smb_rsvd2; /* reserved */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_dsize) */ #endif } SmbReadAndXResp; static inline uint16_t SmbReadAndXReqFid(const SmbReadAndXReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint64_t SmbReadAndXReqOffset(const SmbReadAndXExtReq *req) { if (req->smb_wct == 10) return (uint64_t)SmbNtohl(&req->smb_offset); return (uint64_t)SmbNtohl(&req->smb_off_high) << 32 | (uint64_t)SmbNtohl(&req->smb_offset); } static inline uint16_t SmbReadAndXRespDataOff(const SmbReadAndXResp *req) { return SmbNtohs(&req->smb_doff); } static inline uint32_t SmbReadAndXRespDataCnt(const SmbReadAndXResp *resp) { return (uint32_t)SmbNtohs(&resp->smb_dsize_high) << 16 | (uint32_t)SmbNtohs(&resp->smb_dsize); } /******************************************************************** * SMB_COM_WRITE_ANDX ********************************************************************/ typedef struct _SmbWriteAndXReq /* smb_wct = 12 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_fid; /* file handle */ uint32_t smb_offset; /* offset in file to begin write */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_wmode; /* write mode: bit0 - complete write before return (write through) bit1 - return smb_remaining (pipes/devices only) bit2 - use WriteRawNamedPipe (pipes only) bit3 - this is the start of a message (pipes only) */ uint16_t smb_countleft; /* bytes remaining to write to satisfy user’s request */ uint16_t smb_dsize_high; /* high bytes of data size */ uint16_t smb_dsize; /* number of data bytes in buffer (min value = 0) */ uint16_t smb_doff; /* offset (from start of SMB hdr) to data bytes */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_dsize) */ #endif } SmbWriteAndXReq; typedef struct _SmbWriteAndXExtReq /* smb_wct = 14 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_fid; /* file handle */ uint32_t smb_offset; /* low offset in file to begin write */ uint32_t smb_timeout; /* number of milliseconds to wait for completion */ uint16_t smb_wmode; /* write mode: bit0 - complete write before return (write through) bit1 - return smb_remaining (pipes/devices only) bit2 - use WriteRawNamedPipe (pipes only) bit3 - this is the start of a message (pipes only) */ uint16_t smb_countleft; /* bytes remaining to write to satisfy user’s request */ uint16_t smb_dsize_high; /* high bytes of data size */ uint16_t smb_dsize; /* number of data bytes in buffer (min value = 0) */ uint16_t smb_doff; /* offset (from start of SMB hdr) to data bytes */ uint32_t smb_off_high; /* high offset in file to begin write */ uint16_t smb_bcc; /* total bytes (including pad bytes) following */ #if 0 uint8_t smb_pad[]; /* (optional) to pad to word or dword boundary */ uint8_t smb_data[*]; /* data bytes (* = value of smb_dsize) */ #endif } SmbWriteAndXExtReq; typedef struct _SmbWriteAndXResp /* smb_wct = 6 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_count; /* number of bytes written */ uint16_t smb_remaining; /* bytes remaining to be read (pipes/devices only) */ uint16_t smb_count_high; /* high order bytes of data count */ uint16_t smb_rsvd; /* reserved */ uint16_t smb_bcc; /* value = 0 */ } SmbWriteAndXResp; static inline uint16_t SmbWriteAndXReqFid(const SmbWriteAndXReq *req) { return SmbNtohs(&req->smb_fid); } static inline uint16_t SmbWriteAndXReqDataOff(const SmbWriteAndXReq *req) { return SmbNtohs(&req->smb_doff); } static inline uint16_t SmbWriteAndXReqRemaining(const SmbWriteAndXReq *req) { return SmbNtohs(&req->smb_countleft); } static inline uint64_t SmbWriteAndXReqOffset(const SmbWriteAndXExtReq *req) { if (req->smb_wct == 12) return (uint64_t)SmbNtohl(&req->smb_offset); return (uint64_t)SmbNtohl(&req->smb_off_high) << 32 | (uint64_t)SmbNtohl(&req->smb_offset); } static inline uint32_t SmbWriteAndXReqDataCnt(const SmbWriteAndXReq *req) { return (uint32_t)SmbNtohs(&req->smb_dsize_high) << 16 | (uint32_t)SmbNtohs(&req->smb_dsize); } static inline uint16_t SmbWriteAndXReqWriteMode(const SmbWriteAndXReq *req) { return SmbNtohs(&req->smb_wmode); } static inline bool SmbWriteAndXReqStartRaw(const SmbWriteAndXReq *req) { return ((SmbNtohs(&req->smb_wmode) & 0x000c) == 0x000c) ? true : false; } static inline bool SmbWriteAndXReqRaw(const SmbWriteAndXReq *req) { return ((SmbNtohs(&req->smb_wmode) & 0x000c) == 0x0004) ? true : false; } static inline uint16_t SmbWriteAndXRespCnt(const SmbWriteAndXResp *resp) { return SmbNtohs(&resp->smb_count); } /******************************************************************** * SMB_COM_TRANSACTION2 ********************************************************************/ typedef struct _SmbTransaction2Req { uint8_t smb_wct; uint16_t smb_total_param_count; uint16_t smb_total_data_count; uint16_t smb_max_param_count; uint16_t smb_max_data_count; uint8_t smb_max_setup_count; uint8_t smb_res; uint16_t smb_flags; uint32_t smb_timeout; uint16_t smb_res2; uint16_t smb_param_count; uint16_t smb_param_offset; uint16_t smb_data_count; uint16_t smb_data_offset; uint8_t smb_setup_count; /* Should be 1 for all subcommands */ uint8_t smb_res3; uint16_t smb_setup; /* This is the subcommand */ uint16_t smb_bcc; #if 0 uint8_t smb_name[]; /* ASCII or Unicode depending on smb header flags */ uint8_t pad1[]; uint8_t smb_trans2_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_trans2_data[smb_data_count]; #endif } SmbTransaction2Req; typedef struct _SmbTransaction2InterimResp { uint8_t smb_wct; uint16_t smb_bcc; } SmbTransaction2InterimResp; typedef struct _SmbTransaction2Resp { uint8_t smb_wct; uint16_t smb_total_param_count; uint16_t smb_total_data_count; uint16_t smb_res; uint16_t smb_param_count; uint16_t smb_param_offset; uint16_t smb_param_disp; uint16_t smb_data_count; uint16_t smb_data_offset; uint16_t smb_data_disp; uint16_t smb_setup_count; /* 0 or 1 word */ uint8_t smb_res2; #if 0 uint16_t smb_setup[smb_setup_count]; uint16_t smb_bcc; uint8_t pad1[]; uint8_t smb_trans2_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_trans2_data[smb_data_count]; #endif } SmbTransaction2Resp; static inline uint16_t SmbTransaction2ReqSubCom(const SmbTransaction2Req *req) { return SmbNtohs(&req->smb_setup); } static inline uint16_t SmbTransaction2ReqTotalParamCnt(const SmbTransaction2Req *req) { return SmbNtohs(&req->smb_total_param_count); } static inline uint16_t SmbTransaction2ReqParamCnt(const SmbTransaction2Req *req) { return SmbNtohs(&req->smb_param_count); } static inline uint16_t SmbTransaction2ReqParamOff(const SmbTransaction2Req *req) { return SmbNtohs(&req->smb_param_offset); } static inline uint16_t SmbTransaction2ReqTotalDataCnt(const SmbTransaction2Req *req) { return SmbNtohs(&req->smb_total_data_count); } static inline uint16_t SmbTransaction2ReqDataCnt(const SmbTransaction2Req *req) { return SmbNtohs(&req->smb_data_count); } static inline uint16_t SmbTransaction2ReqDataOff(const SmbTransaction2Req *req) { return SmbNtohs(&req->smb_data_offset); } static inline uint8_t SmbTransaction2ReqSetupCnt(const SmbTransaction2Req *req) { return req->smb_setup_count; } static inline uint16_t SmbTransaction2RespTotalParamCnt(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_total_param_count); } static inline uint16_t SmbTransaction2RespParamCnt(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_param_count); } static inline uint16_t SmbTransaction2RespParamOff(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_param_offset); } static inline uint16_t SmbTransaction2RespParamDisp(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_param_disp); } static inline uint16_t SmbTransaction2RespTotalDataCnt(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_total_data_count); } static inline uint16_t SmbTransaction2RespDataCnt(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_data_count); } static inline uint16_t SmbTransaction2RespDataOff(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_data_offset); } static inline uint16_t SmbTransaction2RespDataDisp(const SmbTransaction2Resp *resp) { return SmbNtohs(&resp->smb_data_disp); } typedef struct _SmbTrans2Open2ReqParams { uint16_t Flags; uint16_t AccessMode; uint16_t Reserved1; uint16_t FileAttributes; uint32_t CreationTime; uint16_t OpenMode; uint32_t AllocationSize; uint16_t Reserved[5]; #if 0 SMB_STRING FileName; #endif } SmbTrans2Open2ReqParams; typedef SmbTransaction2Req SmbTrans2Open2Req; static inline uint16_t SmbTrans2Open2ReqAccessMode(const SmbTrans2Open2ReqParams *req) { return SmbNtohs(&req->AccessMode); } static inline uint16_t SmbTrans2Open2ReqFileAttrs(const SmbTrans2Open2ReqParams *req) { return SmbNtohs(&req->FileAttributes); } static inline uint16_t SmbTrans2Open2ReqOpenMode(const SmbTrans2Open2ReqParams *req) { return SmbNtohs(&req->OpenMode); } static inline uint32_t SmbTrans2Open2ReqAllocSize(const SmbTrans2Open2ReqParams *req) { return SmbNtohl(&req->AllocationSize); } typedef struct _SmbTrans2Open2RespParams { uint16_t smb_fid; uint16_t file_attributes; uint32_t creation_time; uint32_t file_data_size; uint16_t access_mode; uint16_t resource_type; uint16_t nm_pipe_status; uint16_t action_taken; uint32_t reserved; uint16_t extended_attribute_error_offset; uint32_t extended_attribute_length; } SmbTrans2Open2RespParams; static inline uint16_t SmbTrans2Open2RespFid(const SmbTrans2Open2RespParams *resp) { return SmbNtohs(&resp->smb_fid); } static inline uint16_t SmbTrans2Open2RespFileAttrs(const SmbTrans2Open2RespParams *resp) { return SmbNtohs(&resp->file_attributes); } static inline uint32_t SmbTrans2Open2RespFileDataSize(const SmbTrans2Open2RespParams *resp) { return SmbNtohl(&resp->file_data_size); } static inline uint16_t SmbTrans2Open2RespResourceType(const SmbTrans2Open2RespParams *resp) { return SmbNtohs(&resp->resource_type); } static inline uint16_t SmbTrans2Open2RespActionTaken(const SmbTrans2Open2RespParams *resp) { return SmbNtohs(&resp->action_taken); } typedef struct _SmbTrans2Open2Resp { uint8_t smb_wct; uint16_t smb_total_param_count; uint16_t smb_total_data_count; uint16_t smb_res; uint16_t smb_param_count; uint16_t smb_param_offset; uint16_t smb_param_disp; uint16_t smb_data_count; uint16_t smb_data_offset; uint16_t smb_data_disp; uint16_t smb_setup_count; /* 0 */ uint8_t smb_res2; uint16_t smb_bcc; #if 0 uint8_t pad1[]; uint8_t smb_trans2_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_trans2_data[smb_data_count]; #endif } SmbTrans2Open2Resp; // See MS-CIFS Section 2.2.2.3.3 #define SMB_INFO_STANDARD 0x0001 #define SMB_INFO_QUERY_EA_SIZE 0x0002 #define SMB_INFO_QUERY_EAS_FROM_LIST 0x0003 #define SMB_INFO_QUERY_ALL_EAS 0x0004 #define SMB_INFO_IS_NAME_VALID 0x0006 #define SMB_QUERY_FILE_BASIC_INFO 0x0101 #define SMB_QUERY_FILE_STANDARD_INFO 0x0102 #define SMB_QUERY_FILE_EA_INFO 0x0103 #define SMB_QUERY_FILE_NAME_INFO 0x0104 #define SMB_QUERY_FILE_ALL_INFO 0x0107 #define SMB_QUERY_FILE_ALT_NAME_INFO 0x0108 #define SMB_QUERY_FILE_STREAM_INFO 0x0109 #define SMB_QUERY_FILE_COMPRESSION_INFO 0x010b // See MS-SMB Section 2.2.2.3.5 // For added value, see below from MS-FSCC #define SMB_INFO_PASSTHROUGH 0x03e8 #define SMB_INFO_PT_FILE_STANDARD_INFO SMB_INFO_PASSTHROUGH+5 #define SMB_INFO_PT_FILE_ALL_INFO SMB_INFO_PASSTHROUGH+18 #define SMB_INFO_PT_FILE_STREAM_INFO SMB_INFO_PASSTHROUGH+22 #define SMB_INFO_PT_NETWORK_OPEN_INFO SMB_INFO_PASSTHROUGH+34 #if 0 See MS-FSCC Section 2.4 File Information Classes FileDirectoryInformation 1 //Query FileFullDirectoryInformation 2 //Query FileBothDirectoryInformation 3 //Query FileBasicInformation 4 //Query, Set FileStandardInformation 5 //Query FileInternalInformation 6 //Query FileEaInformation 7 //Query FileAccessInformation 8 //Query FileNameInformation 9 //LOCAL<71> FileRenameInformation 10 //Set FileLinkInformation 11 //Set FileNamesInformation 12 //Query FileDispositionInformation 13 //Set FilePositionInformation 14 //Query, Set FileFullEaInformation 15 //Query, Set FileModeInformation 16 //Query, Set<69> FileAlignmentInformation 17 //Query FileAllInformation 18 //Query FileAllocationInformation 19 //Set FileEndOfFileInformation 20 //Set FileAlternateNameInformation 21 //Query FileStreamInformation 22 //Query FilePipeInformation 23 //Query, Set FilePipeLocalInformation 24 //Query FilePipeRemoteInformation 25 //Query FileMailslotQueryInformation 26 //LOCAL<67> FileMailslotSetInformation 27 //LOCAL<68> FileCompressionInformation 28 //Query FileObjectIdInformation 29 //LOCAL<73> FileMoveClusterInformation 31 //<70> FileQuotaInformation 32 //Query, Set<74> FileReparsePointInformation 33 //LOCAL<75> FileNetworkOpenInformation 34 //Query FileAttributeTagInformation 35 //Query FileTrackingInformation 36 //LOCAL<79> FileIdBothDirectoryInformation 37 //Query FileIdFullDirectoryInformation 38 //Query FileValidDataLengthInformation 39 //Set FileShortNameInformation 40 //Set FileSfioReserveInformation 44 //LOCAL<76> FileSfioVolumeInformation 45 //<77> FileHardLinkInformation 46 //LOCAL<65> FileNormalizedNameInformation 48 //<72> FileIdGlobalTxDirectoryInformation 50 //LOCAL<66> FileStandardLinkInformation 54 //LOCAL<78> #endif typedef struct _SmbTrans2QueryFileInfoReqParams { uint16_t fid; uint16_t information_level; } SmbTrans2QueryFileInfoReqParams; static inline uint16_t SmbTrans2QueryFileInfoReqFid(const SmbTrans2QueryFileInfoReqParams *req) { return SmbNtohs(&req->fid); } static inline uint16_t SmbTrans2QueryFileInfoReqInfoLevel(const SmbTrans2QueryFileInfoReqParams *req) { return SmbNtohs(&req->information_level); } typedef struct _SmbQueryInfoStandard { uint16_t CreationDate; uint16_t CreationTime; uint16_t LastAccessDate; uint16_t LastAccessTime; uint16_t LastWriteDate; uint16_t LastWriteTime; uint32_t FileDataSize; uint32_t AllocationSize; uint16_t Attributes; } SmbQueryInfoStandard; static inline uint32_t SmbQueryInfoStandardFileDataSize(const SmbQueryInfoStandard *q) { return SmbNtohl(&q->FileDataSize); } typedef struct _SmbQueryInfoQueryEaSize { uint16_t CreationDate; uint16_t CreationTime; uint16_t LastAccessDate; uint16_t LastAccessTime; uint16_t LastWriteDate; uint16_t LastWriteTime; uint32_t FileDataSize; uint32_t AllocationSize; uint16_t Attributes; uint32_t EaSize; } SmbQueryInfoQueryEaSize; static inline uint32_t SmbQueryInfoQueryEaSizeFileDataSize(const SmbQueryInfoQueryEaSize *q) { return SmbNtohl(&q->FileDataSize); } typedef struct _SmbQueryFileStandardInfo { uint64_t AllocationSize; uint64_t EndOfFile; uint32_t NumberOfLinks; uint8_t DeletePending; uint8_t Directory; uint16_t Reserved; } SmbQueryFileStandardInfo; static inline uint64_t SmbQueryFileStandardInfoEndOfFile(const SmbQueryFileStandardInfo *q) { return SmbNtohq(&q->EndOfFile); } typedef struct _SmbQueryFileAllInfo { // Basic Info uint64_t CreationTime; uint64_t LastAccessTime; uint64_t LastWriteTime; uint64_t LastChangeTime; uint32_t ExtFileAttributes; uint32_t Reserved1; uint64_t AllocationSize; uint64_t EndOfFile; uint32_t NumberOfLinks; uint8_t DeletePending; uint8_t Directory; uint16_t Reserved2; uint32_t EaSize; uint32_t FileNameLength; #if 0 uint16_t FileName[FileNameLength/2]; #endif } SmbQueryFileAllInfo; static inline uint64_t SmbQueryFileAllInfoEndOfFile(const SmbQueryFileAllInfo *q) { return SmbNtohq(&q->EndOfFile); } typedef struct _SmbQueryPTFileAllInfo { // Basic Info uint64_t CreationTime; uint64_t LastAccessTime; uint64_t LastWriteTime; uint64_t LastChangeTime; uint32_t ExtFileAttributes; uint32_t Reserved1; // Standard Info uint64_t AllocationSize; uint64_t EndOfFile; uint32_t NumberOfLinks; uint8_t DeletePending; uint8_t Directory; uint16_t Reserved2; // Internal Info uint64_t IndexNumber; // EA Info uint32_t EaSize; // Access Info uint32_t AccessFlags; // Position Info uint64_t CurrentByteOffset; // Mode Info uint32_t Mode; // Alignment Info uint32_t AlignmentRequirement; // Name Info uint32_t FileNameLength; #if 0 uint16_t FileName[FileNameLength/2]; #endif } SmbQueryPTFileAllInfo; static inline uint64_t SmbQueryPTFileAllInfoEndOfFile(const SmbQueryPTFileAllInfo *q) { return SmbNtohq(&q->EndOfFile); } typedef struct _SmbQueryPTNetworkOpenInfo { uint64_t CreationTime; uint64_t LastAccessTime; uint64_t LastWriteTime; uint64_t LastChangeTime; uint64_t AllocationSize; uint64_t EndOfFile; uint32_t FileAttributes; uint32_t Reserved; } SmbQueryPTNetworkOpenInfo; static inline uint64_t SmbQueryPTNetworkOpenInfoEndOfFile(const SmbQueryPTNetworkOpenInfo *q) { return SmbNtohq(&q->EndOfFile); } typedef struct _SmbQueryPTFileStreamInfo { uint32_t NextEntryOffset; uint32_t StreamNameLength; uint64_t StreamSize; uint64_t StreamAllocationSize; #if 0 StreamName (variable) #endif } SmbQueryPTFileStreamInfo; static inline uint64_t SmbQueryPTFileStreamInfoStreamSize(const SmbQueryPTFileStreamInfo *q) { return SmbNtohq(&q->StreamSize); } typedef struct _SmbTrans2QueryFileInformationResp { uint8_t smb_wct; uint16_t smb_total_param_count; uint16_t smb_total_data_count; uint16_t smb_res; uint16_t smb_param_count; uint16_t smb_param_offset; uint16_t smb_param_disp; uint16_t smb_data_count; uint16_t smb_data_offset; uint16_t smb_data_disp; uint16_t smb_setup_count; /* 0 */ uint8_t smb_res2; uint16_t smb_bcc; #if 0 uint8_t pad1[]; uint8_t smb_trans2_params[smb_param_count]; uint8_t pad2[]; // Will be one of the SmbQuery* structures above uint8_t smb_trans2_data[smb_data_count]; #endif } SmbTrans2QueryFileInformationResp; #define SMB_INFO_SET_EAS 0x0002 #define SMB_SET_FILE_BASIC_INFO 0x0101 #define SMB_SET_FILE_DISPOSITION_INFO 0x0102 #define SMB_SET_FILE_ALLOCATION_INFO 0x0103 #define SMB_SET_FILE_END_OF_FILE_INFO 0x0104 // For added value, see above File Information Classes #define SMB_INFO_PT_SET_FILE_BASIC_FILE_INFO SMB_INFO_PASSTHROUGH+4 #define SMB_INFO_PT_SET_FILE_END_OF_FILE_INFO SMB_INFO_PASSTHROUGH+20 typedef struct _SmbTrans2SetFileInfoReqParams { uint16_t fid; uint16_t information_level; uint16_t reserved; } SmbTrans2SetFileInfoReqParams; static inline uint16_t SmbTrans2SetFileInfoReqFid(const SmbTrans2SetFileInfoReqParams *req) { return SmbNtohs(&req->fid); } static inline uint16_t SmbTrans2SetFileInfoReqInfoLevel(const SmbTrans2SetFileInfoReqParams *req) { return SmbNtohs(&req->information_level); } static inline bool SmbSetFileInfoEndOfFile(const uint16_t info_level) { return ((info_level == SMB_SET_FILE_END_OF_FILE_INFO) || (info_level == SMB_INFO_PT_SET_FILE_END_OF_FILE_INFO)); } typedef struct _SmbSetFileBasicInfo { uint64_t CreationTime; uint64_t LastAccessTime; uint64_t LastWriteTime; uint64_t ChangeTime; uint32_t ExtFileAttributes; uint32_t Reserved; } SmbSetFileBasicInfo; static inline uint32_t SmbSetFileInfoExtFileAttrs(const SmbSetFileBasicInfo *info) { return SmbNtohl(&info->ExtFileAttributes); } static inline bool SmbSetFileInfoSetFileBasicInfo(const uint16_t info_level) { return ((info_level == SMB_SET_FILE_BASIC_INFO) || (info_level == SMB_INFO_PT_SET_FILE_BASIC_FILE_INFO)); } static inline bool SmbExtAttrReadOnly(const uint32_t ext_file_attrs) { if (ext_file_attrs & SMB_EXT_FILE_ATTR_SYSTEM) return true; return false; } static inline bool SmbExtAttrHidden(const uint32_t ext_file_attrs) { if (ext_file_attrs & SMB_EXT_FILE_ATTR_HIDDEN) return true; return false; } static inline bool SmbExtAttrSystem(const uint32_t ext_file_attrs) { if (ext_file_attrs & SMB_EXT_FILE_ATTR_SYSTEM) return true; return false; } static inline bool SmbEvasiveFileAttrs(const uint32_t ext_file_attrs) { return (SmbExtAttrReadOnly(ext_file_attrs) && SmbExtAttrHidden(ext_file_attrs) && SmbExtAttrSystem(ext_file_attrs)); } /******************************************************************** * SMB_COM_TRANSACTION2_SECONDARY * Continuation command for SMB_COM_TRANSACTION2 requests if all * data wasn't sent. ********************************************************************/ typedef struct _SmbTransaction2SecondaryReq { uint8_t smb_wct; uint16_t smb_total_param_count; uint16_t smb_total_data_count; uint16_t smb_param_count; uint16_t smb_param_offset; uint16_t smb_param_disp; uint16_t smb_data_count; uint16_t smb_data_offset; uint16_t smb_data_disp; uint16_t smb_fid; uint16_t smb_bcc; #if 0 uint8_t pad1[]; uint8_t smb_trans2_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_trans2_data[smb_data_count]; #endif } SmbTransaction2SecondaryReq; static inline uint16_t SmbTransaction2SecondaryReqTotalParamCnt(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_total_param_count); } static inline uint16_t SmbTransaction2SecondaryReqParamCnt(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_param_count); } static inline uint16_t SmbTransaction2SecondaryReqParamOff(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_param_offset); } static inline uint16_t SmbTransaction2SecondaryReqParamDisp(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_param_disp); } static inline uint16_t SmbTransaction2SecondaryReqTotalDataCnt(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_total_data_count); } static inline uint16_t SmbTransaction2SecondaryReqDataCnt(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_data_count); } static inline uint16_t SmbTransaction2SecondaryReqDataOff(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_data_offset); } static inline uint16_t SmbTransaction2SecondaryReqDataDisp(const SmbTransaction2SecondaryReq *req) { return SmbNtohs(&req->smb_data_disp); } /********************************************************************* * SMB_COM_TREE_CONNECT *********************************************************************/ typedef struct _SmbTreeConnectReq /* smb_wct = 0 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_bcc; /* min = 4 */ #if 0 uint8_t smb_fmt; /* ASCII -- 04 */ uint8_t smb_buf[]; /* path/username */ uint8_t smb_fmt2; /* ASCII -- 04 */ uint8_t smb_buf2[]; /* password */ uint8_t smb_fmt3; /* ASCII -- 04 */ uint8_t smb_buf3[]; /* dev name ( or LPT1) */ #endif } SmbTreeConnectReq; typedef struct _SmbTreeConnectResp /* smb_wct = 2 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_xmit; /* max xmit size */ uint16_t smb_tid; /* tree id */ uint16_t smb_bcc; } SmbTreeConnectResp; /******************************************************************** * SMB_COM_TREE_DISCONNECT ********************************************************************/ typedef struct _SmbTreeDisconnectReq /* smb_wct = 0 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_bcc; /* must be 0 */ } SmbTreeDisconnectReq; typedef struct _SmbTreeDisconnectResp /* smb_wct = 0 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_bcc; /* must be 0 */ } SmbTreeDisconnectResp; /******************************************************************** * SMB_COM_NEGOTIATE ********************************************************************/ typedef struct _SmbNegotiateReq /* smb_wct = 0 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_bcc; /* min = 2 */ #if 0 uint8_t smb_fmt; /* Dialect -- 02 */ uint8_t smb_buf[]; /* dialect0 */ . . . uint8_t smb_fmt; /* Dialect -- 02 */ uint8_t smb_bufn[]; /* dialectn*/ #endif } SmbCore_NegotiateProtocolReq; /* This is the Core Protocol response */ typedef struct _SmbCore_NegotiateProtocolResp /* smb_wct = 1 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_index; /* index */ uint16_t smb_bcc; /* must be 0 */ } SmbCore_NegotiateProtocolResp; /* This is the Lanman response */ typedef struct _SmbLm10_NegotiateProtocolResp /* smb_wct = 13 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_index; /* index identifying dialect selected */ uint16_t smb_secmode; /* security mode: bit 0, 1 = User level, 0 = Share level bit 1, 1 = encrypt passwords, 0 = do not encrypt passwords */ uint16_t smb_maxxmt; /* max transmit buffer size server supports, 1K min */ uint16_t smb_maxmux; /* max pending multiplexed requests server supports */ uint16_t smb_maxvcs; /* max VCs per server/consumer session supported */ uint16_t smb_blkmode; /* block read/write mode support: bit 0, Read Block Raw supported (65535 bytes max) bit 1, Write Block Raw supported (65535 bytes max) */ uint32_t smb_sesskey; /* Session Key (unique token identifying session) */ uint16_t smb_srv_time; /* server's current time (hhhhh mmmmmm xxxxx) */ uint16_t smb_srv_tzone; /* server's current data (yyyyyyy mmmm ddddd) */ uint32_t smb_rsvd; /* reserved */ uint16_t smb_bcc; /* value = (size of smb_cryptkey) */ #if 0 uint8_t smb_cryptkey[]; /* Key used for password encryption */ #endif } SmbLm10_NegotiateProtocolResp; /* This is the Lanman 2.1 response */ typedef struct _SmbLm21_NegotiateProtocolResp /* smb_wct = 13 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_index; /* index identifying dialect selected */ uint16_t smb_secmode; /* security mode: bit 0, 1 = User level, 0 = Share level bit 1, 1 = encrypt passwords, 0 = do not encrypt passwords */ uint16_t smb_maxxmt; /* max transmit buffer size server supports, 1K min */ uint16_t smb_maxmux; /* max pending multiplexed requests server supports */ uint16_t smb_maxvcs; /* max VCs per server/consumer session supported */ uint16_t smb_blkmode; /* block read/write mode support: bit 0, Read Block Raw supported (65535 bytes max) bit 1, Write Block Raw supported (65535 bytes max) */ uint32_t smb_sesskey; /* Session Key (unique token identifying session) */ uint16_t smb_srv_time; /* server's current time (hhhhh mmmmmm xxxxx) */ uint16_t smb_srv_tzone; /* server's current data (yyyyyyy mmmm ddddd) */ uint16_t smb_cryptkeylen; /* length of smb_cryptkey */ uint16_t smb_rsvd; /* reserved */ uint16_t smb_bcc; /* value = (size of smb_cryptkey) */ #if 0 uint8_t smb_cryptkey[]; /* Key used for password encryption */ uint8_t smb_domain[] /* Null terminated server domain */ #endif } SmbLm21_NegotiateProtocolResp; /* This is the NT response */ typedef struct _SmbNt_NegotiateProtocolResp /* smb_wct = 17 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint16_t smb_index; /* index identifying dialect selected */ uint8_t smb_secmode; /* security mode: bit 0, 1 = User level, 0 = Share level bit 1, 1 = encrypt passwords, 0 = do not encrypt passwords */ uint16_t smb_maxmux; /* max pending multiplexed requests server supports */ uint16_t smb_maxvcs; /* max VCs per server/consumer session supported */ uint32_t smb_maxbuf; /* maximum buffer size supported */ uint32_t smb_maxraw; /* maximum raw buffer size supported */ uint32_t smb_sesskey; /* Session Key (unique token identifying session) */ uint32_t smb_cap; /* capabilities */ struct { uint32_t low_time; int32_t high_time; } smb_srv_time; /* server time */ uint16_t smb_srv_tzone; /* server's current data (yyyyyyy mmmm ddddd) */ uint8_t smb_challenge_len; /* Challenge length */ uint16_t smb_bcc; /* value = (size of smb_cryptkey) */ #if 0 uint8_t smb_challenge[]; /* challenge used for password encryption */ uint8_t smb_domain[]; /* domain name */ uint8_t smb_server[]; /* server name */ // or uint8_t smb_server_guid[16]; uint8_t smb_security_blob[]; #endif } SmbNt_NegotiateProtocolResp; static inline uint16_t SmbNegotiateRespDialectIndex(const SmbCore_NegotiateProtocolResp *resp) { return SmbNtohs(&resp->smb_index); } static inline uint16_t SmbLm_NegotiateRespMaxMultiplex(const SmbLm10_NegotiateProtocolResp *resp) { return SmbNtohs(&resp->smb_maxmux); } static inline uint16_t SmbNt_NegotiateRespMaxMultiplex(const SmbNt_NegotiateProtocolResp *resp) { return SmbNtohs(&resp->smb_maxmux); } /******************************************************************** * SMB_COM_SESSION_SETUP_ANDX ********************************************************************/ typedef struct _SmbLm10_SessionSetupAndXReq /* smb_wct = 10 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bufsize; /* the consumers max buffer size */ uint16_t smb_mpxmax; /* actual maximum multiplexed pending requests */ uint16_t smb_vc_num; /* 0 = first (only), non zero - additional VC number */ uint32_t smb_sesskey; /* Session Key (valid only if smb_vc_num != 0) */ uint16_t smb_apasslen; /* size of account password (smb_apasswd) */ uint32_t smb_rsvd; /* reserved */ uint16_t smb_bcc; /* minimum value = 0 */ #if 0 uint8_t smb_apasswd[*]; /* account password (* = smb_apasslen value) */ uint8_t smb_aname[]; /* account name string */ #endif } SmbLm10_SessionSetupAndXReq; typedef struct _SmbLm10_SessionSetupAndXResp /* smb_wct = 3 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_action; /* request mode: bit0 = Logged in successfully - BUT as GUEST */ uint16_t smb_bcc; /* value = 0 */ } SmbLm10_SessionSetupAndXResp; /* Extended request as defined in LM 2.0 document */ typedef struct _SmbLm20_SessionSetupAndXReq /* smb_wct = 10 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bufsize; /* the consumers max buffer size */ uint16_t smb_mpxmax; /* actual maximum multiplexed pending requests */ uint16_t smb_vc_num; /* 0 = first (only), non zero - additional VC number */ uint32_t smb_sesskey; /* Session Key (valid only if smb_vc_num != 0) */ uint16_t smb_apasslen; /* size of account password (smb_apasswd) */ uint16_t smb_encryptlen; /* size of encryption key (smb_encrypt) */ uint16_t smb_encryptoff; /* offet (from SMB hdr start) to smb_encrypt */ uint16_t smb_bcc; /* minimum value = 0 */ #if 0 uint8_t smb_apasswd[*]; /* account password (* = smb_apasslen value) */ uint8_t smb_aname[]; /* account name string */ uint8_t smb_encrypt[*]; /* encryption key. (* = smb_encryptlen value) */ #endif } SmbLm20_SessionSetupAndXReq; /* Extended response as defined in LM 2.0 document */ typedef struct _SmbLm20_SessionSetupAndXResp /* smb_wct = 3 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_action; /* request mode: bit0 = Logged in successfully - BUT as GUEST */ uint16_t smb_bcc; /* min value = 0 */ #if 0 smb_encresp[]; /* server response to request encryption key */ #endif } SmbLm20_SessionSetupAndXResp; /* Extended request as defined in LM 2.1 document */ typedef struct _SmbLm21_SessionSetupAndXReq /* smb_wct = 10 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bufsize; /* the consumers max buffer size */ uint16_t smb_mpxmax; /* actual maximum multiplexed pending requests */ uint16_t smb_vc_num; /* 0 = first (only), non zero - additional VC number */ uint32_t smb_sesskey; /* Session Key (valid only if smb_vc_num != 0) */ uint16_t smb_apasslen; /* size of account password (smb_apasswd) */ uint32_t smb_rsvd; /* reserved */ uint16_t smb_bcc; /* minimum value = 0 */ #if 0 uint8_t smb_apasswd[*]; /* account password (* = smb_apasslen value) */ uint8_t smb_aname[]; /* account name string */ uint8_t smb_domain[]; /* name of domain that client was authenticated on */ uint8_t smb_mativeos[]; /* native operation system of client */ uint8_t smb_nativelm[]; /* native LAN Manager type */ #endif } SmbLm21_SessionSetupAndXReq; /* Extended response as defined in LM 2.1 document */ typedef struct _SmbLm21_SessionSetupAndXResp /* smb_wct = 3 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_action; /* request mode: bit0 = Logged in successfully - BUT as GUEST */ uint16_t smb_bcc; /* min value = 0 */ #if 0 uint8_t smb_nativeos[]; /* server's native operating system */ uint8_t smb_nativelm[]; /* server's native LM type */ #endif } SmbLm21_SessionSetupAndXResp; /* Extended request as defined in NT LM 1.0 document */ typedef struct _SmbNt10_SessionSetupAndXReq /* smb_wct = 13 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bufsize; /* the consumers max buffer size */ uint16_t smb_mpxmax; /* actual maximum multiplexed pending requests */ uint16_t smb_vc_num; /* 0 = first (only), non zero - additional VC number */ uint32_t smb_sesskey; /* Session Key (valid only if smb_vc_num != 0) */ uint16_t smb_oem_passlen; /* case insensitive password length */ uint16_t smb_unicode_passlen; /* case sensitive password length */ uint32_t smb_rsvd; /* reserved */ uint32_t smb_cap; /* capabilities */ uint16_t smb_bcc; /* minimum value = 0 */ #if 0 uint8_t smb_oem_passwd[*]; /* case insensitive password (* = smb_ci_passlen) */ uint8_t smb_unicode_passwd[*]; /* case sensitive password (* = smb_cs_passlen) */ uint8_t pad[]; /* if unicode to align */ uint8_t smb_aname[]; /* ascii or unicode account name string */ uint8_t smb_domain[]; /* ascii or unicode name of domain that client was authenticated on */ uint8_t smb_nativeos[]; /* ascii or unicode native operation system of client */ uint8_t smb_nativelm[]; /* ascii or unicode native LAN Manager type */ #endif } SmbNt10_SessionSetupAndXReq; static inline uint16_t SmbSessionSetupAndXReqMaxMultiplex(const SmbLm10_SessionSetupAndXReq *req) { return SmbNtohs(&req->smb_mpxmax); } static inline uint16_t SmbNt10SessionSetupAndXReqOemPassLen(const SmbNt10_SessionSetupAndXReq *req) { return SmbNtohs(&req->smb_oem_passlen); } static inline uint16_t SmbNt10SessionSetupAndXReqUnicodePassLen(const SmbNt10_SessionSetupAndXReq *req) { return SmbNtohs(&req->smb_unicode_passlen); } /* Extended request for security blob */ typedef struct _SmbNt10_SessionSetupAndXExtReq /* smb_wct = 12 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bufsize; /* the consumers max buffer size */ uint16_t smb_mpxmax; /* actual maximum multiplexed pending requests */ uint16_t smb_vc_num; /* 0 = first (only), non zero - additional VC number */ uint32_t smb_sesskey; /* Session Key (valid only if smb_vc_num != 0) */ uint16_t smb_blob_len; /* length of security blob */ uint32_t smb_rsvd; /* reserved */ uint32_t smb_cap; /* capabilities */ uint16_t smb_bcc; /* minimum value = 0 */ #if 0 uint8_t smb_blob[]; /* security blob */ uint8_t smb_nativeos[]; /* ascii or unicode native operation system of client */ uint8_t smb_nativelm[]; /* ascii or unicode native LAN Manager type */ #endif } SmbNt10_SessionSetupAndXExtReq; static inline uint16_t SmbSessionSetupAndXReqBlobLen(const SmbNt10_SessionSetupAndXExtReq *req) { return SmbNtohs(&req->smb_blob_len); } /* Response as defined in NT LM 1.0 document */ typedef struct _SmbNt10_SessionSetupAndXResp /* smb_wct = 3 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_action; /* request mode: bit0 = Logged in successfully - BUT as GUEST */ uint16_t smb_bcc; /* min value = 0 */ #if 0 uint8_t pad[]; /* if unicode is used to align */ uint8_t smb_nativeos[]; /* ascii or unicode server's native operating system */ uint8_t smb_nativelm[]; /* ascii or unicode server's native LM type */ uint8_t smb_domain[]; /* ascii or unicode logon domain of the user */ #endif } SmbNt10_SessionSetupAndXResp; /* Extended response for security blob */ typedef struct _SmbNt10_SessionSetupAndXExtResp /* smb_wct = 4 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_action; /* request mode: bit0 = Logged in successfully - BUT as GUEST */ uint16_t smb_blob_len; /* length of security blob */ uint16_t smb_bcc; /* min value = 0 */ #if 0 uint8_t smb_blob[]; /* security blob */ uint8_t smb_nativeos[]; /* ascii or unicode server's native operating system */ uint8_t smb_nativelm[]; /* ascii or unicode server's native LM type */ #endif } SmbNt10_SessionSetupAndXExtResp; static inline uint16_t SmbSessionSetupAndXRespBlobLen(const SmbNt10_SessionSetupAndXExtResp *resp) { return SmbNtohs(&resp->smb_blob_len); } /******************************************************************** * SMB_COM_LOGOFF_ANDX ********************************************************************/ typedef struct _SmbLogoffAndXReq /* smb_wct = 2 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bcc; /* value = 0 */ } SmbLogoffAndXReq; typedef struct _SmbLogoffAndXResp /* smb_wct = 2 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bcc; /* value = 0 */ } SmbLogoffAndXResp; /********************************************************************* * SMB_COM_TREE_CONNECT_ANDX *********************************************************************/ typedef struct _SmbTreeConnectAndXReq /* smb_wct = 4 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_reh2; /* reserved (must be zero) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_flags; /* additional information: bit 0 - if set, disconnect TID in current smb_tid */ uint16_t smb_spasslen; /* length of smb_spasswd */ uint16_t smb_bcc; /* minimum value = 3 */ #if 0 uint8_t smb_spasswd[*]; /* net-name password (* = smb_spasslen value) */ uint8_t pad[]; /* if unicode to align */ uint8_t smb_path[]; /* server name and net-name */ uint8_t smb_service[]; /* service name string */ #endif } SmbTreeConnectAndXReq; typedef struct _SmbLm10_TreeConnectAndXResp /* smb_wct = 2 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_bcc; /* min value = 3 */ #if 0 uint8_t smb_service[]; /* service type connected to (string) */ #endif } SmbLm10_TreeConnectAndXResp; typedef struct _SmbTreeConnectAndXResp /* smb_wct = 3 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_optsupp; /* bit mask indicating advanced OS features available bit0 = 1, exclusive search bits supported */ uint16_t smb_bcc; /* min value = 3 */ #if 0 uint8_t smb_service[]; /* service type connected to - ASCII */ uint8_t pad[]; /* if unicode to align */ uint8_t smb_nativefs[]; /* native file system for this connection */ #endif } SmbTreeConnectAndXResp; typedef struct _SmbTreeConnectAndXExtResp /* smb_wct = 7 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint16_t smb_optsupp; /* bit mask indicating advanced OS features available */ uint32_t smb_share_access; /* maximal share access rights */ uint32_t smb_guest_access; /* guest maximal share access rights */ uint16_t smb_bcc; /* min value = 3 */ #if 0 uint8_t smb_service[]; /* service type connected to - ASCII */ uint8_t pad[]; /* if unicode to align */ uint8_t smb_nativefs[]; /* native file system for this connection */ #endif } SmbTreeConnectAndXExtResp; static inline uint16_t SmbTreeConnectAndXReqPassLen(const SmbTreeConnectAndXReq *req) { return SmbNtohs(&req->smb_spasslen); } /******************************************************************** * SMB_COM_NT_TRANSACT ********************************************************************/ #define SMB_CREATE_OPTIONS__FILE_SEQUENTIAL_ONLY 0x00000004 typedef struct _SmbNtTransactReq { uint8_t smb_wct; uint8_t smb_max_setup_count; uint16_t smb_res; uint32_t smb_total_param_count; uint32_t smb_total_data_count; uint32_t smb_max_param_count; uint32_t smb_max_data_count; uint32_t smb_param_count; uint32_t smb_param_offset; uint32_t smb_data_count; uint32_t smb_data_offset; uint8_t smb_setup_count; uint16_t smb_function; #if 0 uint16_t smb_setup[smb_setup_count]; uint16_t smb_bcc; uint8_t pad1[]; uint8_t smb_nt_trans_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_nt_trans_data[smb_data_count]; #endif } SmbNtTransactReq; typedef struct _SmbNtTransactInterimResp { uint8_t smb_wct; uint16_t smb_bcc; } SmbNtTransactInterimResp; typedef struct _SmbNtTransactResp { uint8_t smb_wct; uint8_t smb_res[3]; uint32_t smb_total_param_count; uint32_t smb_total_data_count; uint32_t smb_param_count; uint32_t smb_param_offset; uint32_t smb_param_disp; uint32_t smb_data_count; uint32_t smb_data_offset; uint32_t smb_data_disp; uint8_t smb_setup_count; #if 0 uint16_t smb_setup[smb_setup_count]; uint16_t smb_bcc; uint8_t pad1[]; uint8_t smb_nt_trans_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_nt_trans_data[smb_data_count]; #endif } SmbNtTransactResp; static inline uint16_t SmbNtTransactReqSubCom(const SmbNtTransactReq *req) { return SmbNtohs(&req->smb_function); } static inline uint8_t SmbNtTransactReqSetupCnt(const SmbNtTransactReq *req) { return req->smb_setup_count; } static inline uint32_t SmbNtTransactReqTotalParamCnt(const SmbNtTransactReq *req) { return SmbNtohl(&req->smb_total_param_count); } static inline uint32_t SmbNtTransactReqParamCnt(const SmbNtTransactReq *req) { return SmbNtohl(&req->smb_param_count); } static inline uint32_t SmbNtTransactReqParamOff(const SmbNtTransactReq *req) { return SmbNtohl(&req->smb_param_offset); } static inline uint32_t SmbNtTransactReqTotalDataCnt(const SmbNtTransactReq *req) { return SmbNtohl(&req->smb_total_data_count); } static inline uint32_t SmbNtTransactReqDataCnt(const SmbNtTransactReq *req) { return SmbNtohl(&req->smb_data_count); } static inline uint32_t SmbNtTransactReqDataOff(const SmbNtTransactReq *req) { return SmbNtohl(&req->smb_data_offset); } static inline uint32_t SmbNtTransactRespTotalParamCnt(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_total_param_count); } static inline uint32_t SmbNtTransactRespParamCnt(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_param_count); } static inline uint32_t SmbNtTransactRespParamOff(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_param_offset); } static inline uint32_t SmbNtTransactRespParamDisp(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_param_disp); } static inline uint32_t SmbNtTransactRespTotalDataCnt(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_total_data_count); } static inline uint32_t SmbNtTransactRespDataCnt(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_data_count); } static inline uint32_t SmbNtTransactRespDataOff(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_data_offset); } static inline uint32_t SmbNtTransactRespDataDisp(const SmbNtTransactResp *resp) { return SmbNtohl(&resp->smb_data_disp); } typedef struct _SmbNtTransactCreateReqParams { uint32_t flags; uint32_t root_dir_fid; uint32_t desired_access; uint64_t allocation_size; uint32_t ext_file_attributes; uint32_t share_access; uint32_t create_disposition; uint32_t create_options; uint32_t security_descriptor_length; uint32_t ea_length; uint32_t name_length; uint32_t impersonation_level; uint8_t security_flags; #if 0 uint8_t name[name_length]; #endif } SmbNtTransactCreateReqParams; #if 0 // Not used now typedef struct _SmbNtTransactCreateReqData { uint8_t security_descriptor[security_descriptor_length]; uint8_t extended_attributes[ea_length]; } SmbNtTransactCreateReqData; #endif static inline uint64_t SmbNtTransactCreateReqAllocSize(const SmbNtTransactCreateReqParams *req) { return SmbNtohq(&req->allocation_size); } static inline uint32_t SmbNtTransactCreateReqFileNameLength(const SmbNtTransactCreateReqParams *req) { return SmbNtohl(&req->name_length); } static inline uint32_t SmbNtTransactCreateReqFileAttrs(const SmbNtTransactCreateReqParams *req) { return SmbNtohl(&req->ext_file_attributes); } static inline bool SmbNtTransactCreateReqSequentialOnly(const SmbNtTransactCreateReqParams *req) { return (SmbNtohl(&req->create_options) & SMB_CREATE_OPTIONS__FILE_SEQUENTIAL_ONLY); } typedef struct _SmbNtTransactCreateReq { uint8_t smb_wct; uint8_t smb_max_setup_count; uint16_t smb_res; uint32_t smb_total_param_count; uint32_t smb_total_data_count; uint32_t smb_max_param_count; uint32_t smb_max_data_count; uint32_t smb_param_count; uint32_t smb_param_offset; uint32_t smb_data_count; uint32_t smb_data_offset; uint8_t smb_setup_count; /* Must be 0x00 */ uint16_t smb_function; /* NT_TRANSACT_CREATE */ uint16_t smb_bcc; #if 0 uint8_t pad1[]; uint8_t smb_nt_trans_params[smb_param_count]; /* SmbNtTransCreateParams */ uint8_t pad2[]; uint8_t smb_nt_trans_data[smb_data_count]; #endif } SmbNtTransactCreateReq; typedef struct _SmbNtTransactCreateRespParams { uint8_t op_lock_level; uint8_t reserved; uint16_t smb_fid; uint32_t create_action; uint32_t ea_error_offset; uint64_t creation_time; uint64_t last_access_time; uint64_t last_write_time; uint64_t last_change_time; uint32_t ext_file_attributes; uint64_t allocation_size; uint64_t end_of_file; uint16_t resource_type; uint16_t nm_pipe_status; uint8_t directory; } SmbNtTransactCreateRespParams; static inline uint16_t SmbNtTransactCreateRespFid(const SmbNtTransactCreateRespParams *resp) { return SmbNtohs(&resp->smb_fid); } static inline uint32_t SmbNtTransactCreateRespCreateAction(const SmbNtTransactCreateRespParams *resp) { return SmbNtohl(&resp->create_action); } static inline uint64_t SmbNtTransactCreateRespEndOfFile(const SmbNtTransactCreateRespParams *resp) { return SmbNtohq(&resp->end_of_file); } static inline uint16_t SmbNtTransactCreateRespResourceType(const SmbNtTransactCreateRespParams *resp) { return SmbNtohs(&resp->resource_type); } static inline bool SmbNtTransactCreateRespDirectory(const SmbNtTransactCreateRespParams *resp) { return (resp->directory ? true : false); } typedef struct _SmbNtTransactCreateResp { uint8_t smb_wct; uint8_t smb_res[3]; uint32_t smb_total_param_count; uint32_t smb_total_data_count; uint32_t smb_param_count; uint32_t smb_param_offset; uint32_t smb_param_disp; uint32_t smb_data_count; uint32_t smb_data_offset; uint32_t smb_data_disp; uint8_t smb_setup_count; /* 0x00 */ uint16_t smb_bcc; #if 0 uint8_t pad1[]; uint8_t smb_nt_trans_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_nt_trans_data[smb_data_count]; #endif } SmbNtTransactCreateResp; /******************************************************************** * SMB_COM_NT_TRANSACT_SECONDARY ********************************************************************/ typedef struct _SmbNtTransactSecondaryReq { uint8_t smb_wct; uint8_t smb_res[3]; uint32_t smb_total_param_count; uint32_t smb_total_data_count; uint32_t smb_param_count; uint32_t smb_param_offset; uint32_t smb_param_disp; uint32_t smb_data_count; uint32_t smb_data_offset; uint32_t smb_data_disp; uint8_t smb_res2; #if 0 uint8_t pad1[]; uint8_t smb_nt_trans_params[smb_param_count]; uint8_t pad2[]; uint8_t smb_nt_trans_data[smb_data_count]; #endif } SmbNtTransactSecondaryReq; static inline uint32_t SmbNtTransactSecondaryReqTotalParamCnt(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_total_param_count); } static inline uint32_t SmbNtTransactSecondaryReqParamCnt(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_param_count); } static inline uint32_t SmbNtTransactSecondaryReqParamOff(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_param_offset); } static inline uint32_t SmbNtTransactSecondaryReqParamDisp(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_param_disp); } static inline uint32_t SmbNtTransactSecondaryReqTotalDataCnt(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_total_data_count); } static inline uint32_t SmbNtTransactSecondaryReqDataCnt(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_data_count); } static inline uint32_t SmbNtTransactSecondaryReqDataOff(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_data_offset); } static inline uint32_t SmbNtTransactSecondaryReqDataDisp(const SmbNtTransactSecondaryReq *req) { return SmbNtohl(&req->smb_data_disp); } /******************************************************************** * SMB_COM_NT_CREATE_ANDX ********************************************************************/ #define SMB_CREATE_DISPOSITSION__FILE_SUPERCEDE 0x00000000 #define SMB_CREATE_DISPOSITSION__FILE_OPEN 0x00000001 #define SMB_CREATE_DISPOSITSION__FILE_CREATE 0x00000002 #define SMB_CREATE_DISPOSITSION__FILE_OPEN_IF 0x00000003 #define SMB_CREATE_DISPOSITSION__FILE_OVERWRITE 0x00000004 #define SMB_CREATE_DISPOSITSION__FILE_OVERWRITE_IF 0x00000005 typedef struct _SmbNtCreateAndXReq /* smb_wct = 24 */ { uint8_t smb_wct; /* count of 16-bit words that follow */ uint8_t smb_com2; /* secondary (X) command, 0xFF = none */ uint8_t smb_res2; /* reserved (pad to word) */ uint16_t smb_off2; /* offset (from SMB hdr start) to next cmd (@smb_wct) */ uint8_t smb_res; /* reserved */ uint16_t smb_name_len; /* length of name of file */ uint32_t smb_flags; /* flags */ uint32_t smb_root_fid; /* fid for previously opened directory */ uint32_t smb_access; /* specifies the type of file access */ uint64_t smb_alloc_size; /* initial allocation size of the file */ uint32_t smb_file_attrs; /* specifies the file attributes for the file */ uint32_t smb_share_access; /* the type of share access */ uint32_t smb_create_disp; /* actions to take if file does or does not exist */ uint32_t smb_create_opts; /* options used when creating or opening file */ uint32_t smb_impersonation_level; /* security impersonation level */ uint8_t smb_security_flags; /* security flags */ uint16_t smb_bcc; /* byte count */ #if 0 uint8_t * file_name[]; /* name of file to open - ascii or unicode */ #endif } SmbNtCreateAndXReq; typedef struct _SmbNtCreateAndXResp /* smb_wct = 34 */ { uint8_t smb_wct; uint8_t smb_com2; uint8_t smb_res2; uint16_t smb_off2; uint8_t smb_oplock_level; uint16_t smb_fid; uint32_t smb_create_disposition; uint64_t smb_creation_time; uint64_t smb_last_access_time; uint64_t smb_last_write_time; uint64_t smb_change_time; uint32_t smb_file_attrs; uint64_t smb_alloc_size; uint64_t smb_eof; uint16_t smb_resource_type; uint16_t smb_nm_pipe_state; uint8_t smb_directory; uint16_t smb_bcc; } SmbNtCreateAndXResp; // Word count is always set to 42 though there are actually 50 words typedef struct _SmbNtCreateAndXExtResp /* smb_wct = 42 */ { uint8_t smb_wct; uint8_t smb_com2; uint8_t smb_res2; uint16_t smb_off2; uint8_t smb_oplock_level; uint16_t smb_fid; uint32_t smb_create_disposition; uint64_t smb_creation_time; uint64_t smb_last_access_time; uint64_t smb_last_write_time; uint64_t smb_change_time; uint32_t smb_file_attrs; uint64_t smb_alloc_size; uint64_t smb_eof; uint16_t smb_resource_type; uint16_t smb_nm_pipe_state; uint8_t smb_directory; uint8_t smb_volume_guid[16]; uint64_t smb_fileid; uint32_t smb_max_access_rights; uint32_t smb_guest_access_rights; uint16_t smb_bcc; } SmbNtCreateAndXExtResp; static inline uint16_t SmbNtCreateAndXReqFileNameLen(const SmbNtCreateAndXReq *req) { return SmbNtohs(&req->smb_name_len); } static inline uint32_t SmbNtCreateAndXReqCreateDisposition(const SmbNtCreateAndXReq *req) { return SmbNtohl(&req->smb_create_disp); } static inline bool SmbCreateDispositionRead(const uint32_t create_disposition) { return (create_disposition == SMB_CREATE_DISPOSITSION__FILE_OPEN) || (create_disposition > SMB_CREATE_DISPOSITSION__FILE_OVERWRITE_IF); } static inline uint64_t SmbNtCreateAndXReqAllocSize(const SmbNtCreateAndXReq *req) { return SmbNtohq(&req->smb_alloc_size); } static inline bool SmbNtCreateAndXReqSequentialOnly(const SmbNtCreateAndXReq *req) { return (SmbNtohl(&req->smb_create_opts) & SMB_CREATE_OPTIONS__FILE_SEQUENTIAL_ONLY); } static inline uint32_t SmbNtCreateAndXReqFileAttrs(const SmbNtCreateAndXReq *req) { return SmbNtohl(&req->smb_file_attrs); } static inline uint16_t SmbNtCreateAndXRespFid(const SmbNtCreateAndXResp *resp) { return SmbNtohs(&resp->smb_fid); } static inline uint32_t SmbNtCreateAndXRespCreateDisposition(const SmbNtCreateAndXResp *resp) { return SmbNtohl(&resp->smb_create_disposition); } static inline bool SmbNtCreateAndXRespDirectory(const SmbNtCreateAndXResp *resp) { return (resp->smb_directory ? true : false); } static inline uint16_t SmbNtCreateAndXRespResourceType(const SmbNtCreateAndXResp *resp) { return SmbNtohs(&resp->smb_resource_type); } static inline uint64_t SmbNtCreateAndXRespEndOfFile(const SmbNtCreateAndXResp *resp) { return SmbNtohq(&resp->smb_eof); } #ifdef WIN32 #pragma pack(pop,smb_hdrs) #else #pragma pack() #endif #endif /* _SMB_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_cl.h0000644000175000017500000000412714241075626021214 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _DCE2_CL_H_ #define _DCE2_CL_H_ #include "dce2_list.h" #include "dce2_session.h" #include "sf_types.h" /******************************************************************** * Macros ********************************************************************/ #define DCE2_MOCK_HDR_LEN__CL (sizeof(DceRpcClHdr)) /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_ClTracker { DCE2_List *act_trackers; /* List of activity trackers */ } DCE2_ClTracker; /******************************************************************** * Public function prototypes ********************************************************************/ void DCE2_ClInitRdata(uint8_t *); void DCE2_ClProcess(DCE2_SsnData *, DCE2_ClTracker *); void DCE2_ClCleanTracker(DCE2_ClTracker *); #endif /* _DCE2_CL_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_debug.h0000644000175000017500000000614314241075634021703 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides macros and functions for debugging the preprocessor. * If Snort is not configured to do debugging, macros are empty. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _DCE2_DEBUG_H_ #define _DCE2_DEBUG_H_ #include /******************************************************************** * Public function prototypes ********************************************************************/ // Use like this: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__ALL, "msg\n");) void DCE2_DebugMsg(int, const char *, ...); int DCE2_DebugThis(int level); /******************************************************************** * Macros ********************************************************************/ #define DCE2_DEBUG_VARIABLE "DCE2_DEBUG" #define DCE2_DEBUG__NONE 0x00000000 #define DCE2_DEBUG__ROPTIONS 0x00000001 #define DCE2_DEBUG__CONFIG 0x00000002 #define DCE2_DEBUG__MAIN 0x00000004 #define DCE2_DEBUG__SMB 0x00000008 #define DCE2_DEBUG__CO 0x00000010 #define DCE2_DEBUG__EVENT 0x00000020 #define DCE2_DEBUG__MEMORY 0x00000040 #define DCE2_DEBUG__HTTP 0x00000080 #define DCE2_DEBUG__CL 0x00000100 #define DCE2_DEBUG__PAF 0x00000200 #define DCE2_DEBUG__ALL 0xffffffff #define DCE2_DEBUG__START_MSG "DCE/RPC Preprocessor *************************************" #define DCE2_DEBUG__END_MSG "**********************************************************" #define DCE2_DEBUG__PAF_START_MSG "DCE/RPC PAF ==============================================" #define DCE2_DEBUG__PAF_END_MSG "==========================================================" #ifdef DEBUG #include #define DCE2_ASSERT(code) assert(code) #else #define DCE2_ASSERT(code) #endif #ifdef DEBUG_MSGS #define DCE2_DEBUG_VAR(code) code #define DCE2_DEBUG_CODE(level, code) { if (DCE2_DebugThis(level)) { code } } #else #define DCE2_DEBUG_VAR(code) #define DCE2_DEBUG_CODE(level, code) #endif #endif /* _DCE2_DEBUG_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_stats.c0000644000175000017500000001261114241075664021746 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "dce2_stats.h" #include "dce2_utils.h" #include "dce2_memory.h" #include "dce2_config.h" #include /******************************************************************** * Global variables ********************************************************************/ DCE2_Stats dce2_stats; char **dce2_trans_strs = NULL; /******************************************************************** * Private function prototypes ********************************************************************/ static inline void DCE2_CreateTransStr(char **, DCE2_TransType, char *); /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_StatsInit(void) { memset(&dce2_stats, 0, sizeof(dce2_stats)); if (dce2_trans_strs == NULL) { DCE2_TransType ttype; dce2_trans_strs = (char **)DCE2_Alloc((DCE2_TRANS_TYPE__MAX * sizeof(char *)), DCE2_MEM_TYPE__INIT); if (dce2_trans_strs == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for transport string " "array", __FILE__, __LINE__); } for (ttype = DCE2_TRANS_TYPE__NONE; ttype < DCE2_TRANS_TYPE__MAX; ttype++) { switch (ttype) { case DCE2_TRANS_TYPE__NONE: break; case DCE2_TRANS_TYPE__SMB: { char *str = "SMB"; DCE2_CreateTransStr(dce2_trans_strs, ttype, str); } break; case DCE2_TRANS_TYPE__TCP: { char *str = "TCP"; DCE2_CreateTransStr(dce2_trans_strs, ttype, str); } break; case DCE2_TRANS_TYPE__UDP: { char *str = "UDP"; DCE2_CreateTransStr(dce2_trans_strs, ttype, str); } break; case DCE2_TRANS_TYPE__HTTP_PROXY: { char *str = "HTTP Proxy"; DCE2_CreateTransStr(dce2_trans_strs, ttype, str); } break; case DCE2_TRANS_TYPE__HTTP_SERVER: { char *str = "HTTP Server"; DCE2_CreateTransStr(dce2_trans_strs, ttype, str); } break; default: DCE2_Die("%s(%d) Invalid transport type for allocing " "transport strings: %d", __FILE__, __LINE__, ttype); break; } } } } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_CreateTransStr(char **trans_buf, DCE2_TransType ttype, char *trans_str) { if ((trans_buf == NULL) || (trans_str == NULL)) return; trans_buf[ttype] = (char *)DCE2_Alloc(strlen(trans_str) + 1, DCE2_MEM_TYPE__INIT); if (trans_buf[ttype] == NULL) { DCE2_Die("%s(%d) Failed to allocate memory for transport string", __FILE__, __LINE__); } snprintf(trans_buf[ttype], strlen(trans_str) + 1, "%s", trans_str); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_StatsFree(void) { if (dce2_trans_strs != NULL) { unsigned int i; for (i = DCE2_TRANS_TYPE__NONE; i < DCE2_TRANS_TYPE__MAX; i++) { if (dce2_trans_strs[i] != NULL) { DCE2_Free((void *)dce2_trans_strs[i], strlen(dce2_trans_strs[i]) + 1, DCE2_MEM_TYPE__INIT); } } DCE2_Free((void *)dce2_trans_strs, (DCE2_TRANS_TYPE__MAX * sizeof(char *)), DCE2_MEM_TYPE__INIT); dce2_trans_strs = NULL; } } snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_stats.h0000644000175000017500000001107514241075665021757 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _DCE2_STATS_H_ #define _DCE2_STATS_H_ #include "dce2_utils.h" #include "smb.h" #include "sf_types.h" /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_Stats { uint64_t sessions; uint64_t sessions_active; uint64_t sessions_autodetected; uint64_t sessions_aborted; uint64_t bad_autodetects; uint64_t events; #ifdef DEBUG uint64_t autoports[65535][DCE2_TRANS_TYPE__MAX]; #endif uint64_t smb_sessions; uint64_t smb_pkts; uint64_t smb_ignored_bytes; uint64_t smb_cli_seg_reassembled; uint64_t smb_srv_seg_reassembled; uint64_t smb_max_outstanding_requests; uint64_t smb_com_stats[2][SMB_MAX_NUM_COMS]; uint64_t smb_chained_stats[2][SMB_ANDX_COM__MAX][SMB_MAX_NUM_COMS]; // The +1 is for codes beyond the range of the highest valid subcommand code // Indicates a bogus subcommand uint64_t smb_trans_subcom_stats[2][TRANS_SUBCOM_MAX+1]; uint64_t smb_trans2_subcom_stats[2][TRANS2_SUBCOM_MAX+1]; uint64_t smb_nt_transact_subcom_stats[2][NT_TRANSACT_SUBCOM_MAX+1]; uint64_t smb_files_processed; uint64_t tcp_sessions; uint64_t tcp_pkts; uint64_t udp_sessions; uint64_t udp_pkts; uint64_t http_proxy_sessions; uint64_t http_proxy_pkts; uint64_t http_server_sessions; uint64_t http_server_pkts; uint64_t co_pdus; uint64_t co_bind; uint64_t co_bind_ack; uint64_t co_alter_ctx; uint64_t co_alter_ctx_resp; uint64_t co_bind_nack; uint64_t co_request; uint64_t co_response; uint64_t co_cancel; uint64_t co_orphaned; uint64_t co_fault; uint64_t co_auth3; uint64_t co_shutdown; uint64_t co_reject; uint64_t co_ms_pdu; uint64_t co_other_req; uint64_t co_other_resp; uint64_t co_req_fragments; uint64_t co_resp_fragments; uint64_t co_cli_max_frag_size; uint64_t co_cli_min_frag_size; uint64_t co_cli_seg_reassembled; uint64_t co_cli_frag_reassembled; uint64_t co_srv_max_frag_size; uint64_t co_srv_min_frag_size; uint64_t co_srv_seg_reassembled; uint64_t co_srv_frag_reassembled; uint64_t cl_pkts; uint64_t cl_request; uint64_t cl_ack; uint64_t cl_cancel; uint64_t cl_cli_fack; uint64_t cl_ping; uint64_t cl_response; uint64_t cl_reject; uint64_t cl_cancel_ack; uint64_t cl_srv_fack; uint64_t cl_fault; uint64_t cl_nocall; uint64_t cl_working; uint64_t cl_other_req; uint64_t cl_other_resp; uint64_t cl_fragments; uint64_t cl_max_frag_size; uint64_t cl_frag_reassembled; uint64_t cl_max_seqnum; /* SMB2 stats */ uint64_t smb2_prunes; uint64_t smb2_memory_in_use; uint64_t smb2_memory_in_use_max; uint64_t smb2_create; uint64_t smb2_write; uint64_t smb2_read; uint64_t smb2_set_info; uint64_t smb2_tree_connect; uint64_t smb2_tree_disconnect; uint64_t smb2_close; } DCE2_Stats; /******************************************************************** * Extern variables ********************************************************************/ extern DCE2_Stats dce2_stats; extern char **dce2_trans_strs; /******************************************************************** * Public function prototypes ********************************************************************/ void DCE2_StatsInit(void); void DCE2_StatsFree(void); #endif /* _DCE2_STATS_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/sf_dce2.dsp0000444000175000017500000001707614230012554021557 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_dce2" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_dce2 - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_dce2.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_dce2.mak" CFG="sf_dce2 - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_dce2 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_dce2 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_dce2 - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I ".\includes" /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FR /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_dce2 - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I ".\includes" /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FR /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_dce2 - Win32 Release" # Name "sf_dce2 - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\dce2_cl.c # End Source File # Begin Source File SOURCE=.\dce2_co.c # End Source File # Begin Source File SOURCE=.\dce2_config.c # End Source File # Begin Source File SOURCE=.\dce2_debug.c # End Source File # Begin Source File SOURCE=.\dce2_event.c # End Source File # Begin Source File SOURCE=.\dce2_http.c # End Source File # Begin Source File SOURCE=.\dce2_list.c # End Source File # Begin Source File SOURCE=.\dce2_memory.c # End Source File # Begin Source File SOURCE=.\dce2_paf.c # End Source File # Begin Source File SOURCE=.\dce2_roptions.c # End Source File # Begin Source File SOURCE=.\dce2_smb.c # End Source File # Begin Source File SOURCE=.\dce2_smb2.c # End Source File # Begin Source File SOURCE=.\dce2_stats.c # End Source File # Begin Source File SOURCE=.\dce2_tcp.c # End Source File # Begin Source File SOURCE=.\dce2_udp.c # End Source File # Begin Source File SOURCE=.\dce2_utils.c # End Source File # Begin Source File SOURCE="..\..\win32\WIN32-Code\inet_aton.c" # End Source File # Begin Source File SOURCE="..\..\win32\WIN32-Code\inet_pton.c" # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_ip.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=..\include\sfrt.c # End Source File # Begin Source File SOURCE=..\include\sfrt_dir.c # End Source File # Begin Source File SOURCE=.\snort_dce2.c # End Source File # Begin Source File SOURCE=.\spp_dce2.c # End Source File # Begin Source File SOURCE="..\..\win32\WIN32-Code\strtok_r.c" # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\dce2_cl.h # End Source File # Begin Source File SOURCE=.\dce2_co.h # End Source File # Begin Source File SOURCE=.\dce2_config.h # End Source File # Begin Source File SOURCE=.\dce2_debug.h # End Source File # Begin Source File SOURCE=.\dce2_event.h # End Source File # Begin Source File SOURCE=.\dce2_http.h # End Source File # Begin Source File SOURCE=.\dce2_list.h # End Source File # Begin Source File SOURCE=.\dce2_memory.h # End Source File # Begin Source File SOURCE=.\dce2_paf.h # End Source File # Begin Source File SOURCE=.\dce2_roptions.h # End Source File # Begin Source File SOURCE=.\dce2_session.h # End Source File # Begin Source File SOURCE=.\dce2_smb.h # End Source File # Begin Source File SOURCE=.\dce2_smb2.h # End Source File # Begin Source File SOURCE=.\dce2_stats.h # End Source File # Begin Source File SOURCE=.\dce2_tcp.h # End Source File # Begin Source File SOURCE=.\dce2_udp.h # End Source File # Begin Source File SOURCE=.\dce2_utils.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\snort_dce2.h # End Source File # Begin Source File SOURCE=.\spp_dce2.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_smb2.h0000644000175000017500000003163014241075663021461 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * SMB2 file processing * Author(s): Hui Cao ****************************************************************************/ #ifndef _DCE2_SMB2_H_ #define _DCE2_SMB2_H_ #include "sf_types.h" #include "dce2_smb.h" #include "dce2_list.h" #define SMB2_FLAGS_ASYNC_COMMAND 0x00000002 #define SMB2_STATUS_PENDING 0x00000103 typedef struct _Smb2Hdr { uint8_t smb_idf[4]; /* contains 0xFE,’SMB’ */ uint16_t structure_size; /* This MUST be set to 64 */ uint16_t credit_charge; /* # of credits that this request consumes */ uint32_t status; /* depends */ uint16_t command; /* command code */ uint16_t credit; /* # of credits requesting/granted */ uint32_t flags; /* flags */ uint32_t next_command; /* used for compounded request */ uint64_t message_id; /* identifies a message uniquely on connection */ uint64_t async_sync; /* used for async and sync differently */ uint64_t session_id; /* identifies the established session for the command*/ uint8_t signature[16] ; /* signature of the message */ } Smb2Hdr; typedef struct _Smb2ASyncHdr { uint8_t smb_idf[4]; /* contains 0xFE,’SMB’ */ uint16_t structure_size; /* This MUST be set to 64 */ uint16_t credit_charge; /* # of credits that this request consumes */ uint32_t status; /* depends */ uint16_t command; /* command code */ uint16_t credit; /* # of credits requesting/granted */ uint32_t flags; /* flags */ uint32_t next_command; /* used for compounded request */ uint64_t message_id; /* identifies a message uniquely on connection */ uint64_t async_id; /* handle operations asynchronously */ uint64_t session_id; /* identifies the established session for the command*/ uint8_t signature[16] ; /* signature of the message */ } Smb2ASyncHdr; typedef struct _Smb2SyncHdr { uint8_t smb_idf[4]; /* contains 0xFE,’SMB’ */ uint16_t structure_size; /* This MUST be set to 64 */ uint16_t credit_charge; /* # of credits that this request consumes */ uint32_t status; /* depends */ uint16_t command; /* command code */ uint16_t credit; /* # of credits requesting/granted */ uint32_t flags; /* flags */ uint32_t next_command; /* used for compounded request */ uint64_t message_id; /* identifies a message uniquely on connection */ uint32_t reserved; /* reserved */ uint32_t tree_id; /* identifies the tree connect for the command */ uint64_t session_id; /* identifies the established session for the command*/ uint8_t signature[16] ; /* signature of the message */ } Smb2SyncHdr; typedef struct _Smb2ErrorResponseHdr { uint16_t structure_size; /* This MUST be set to 9 */ uint16_t reserved; /* reserved */ uint32_t byte_count; /* The number of bytes of error_data */ uint8_t error_data[1]; /* If byte_count is 0, this MUST be 0*/ } Smb2ErrorResponseHdr; /* SMB2 command codes */ #define SMB2_COM_NEGOTIATE 0x00 #define SMB2_COM_SESSION_SETUP 0x01 #define SMB2_COM_LOGOFF 0x02 #define SMB2_COM_TREE_CONNECT 0x03 #define SMB2_COM_TREE_DISCONNECT 0x04 #define SMB2_COM_CREATE 0x05 #define SMB2_COM_CLOSE 0x06 #define SMB2_COM_FLUSH 0x07 #define SMB2_COM_READ 0x08 #define SMB2_COM_WRITE 0x09 #define SMB2_COM_LOCK 0x0A #define SMB2_COM_IOCTL 0x0B #define SMB2_COM_CANCEL 0x0C #define SMB2_COM_ECHO 0x0D #define SMB2_COM_QUERY_DIRECTORY 0x0E #define SMB2_COM_CHANGE_NOTIFY 0x0F #define SMB2_COM_QUERY_INFO 0x10 #define SMB2_COM_SET_INFO 0x11 #define SMB2_COM_OPLOCK_BREAK 0x12 typedef struct _Smb2WriteRequestHdr { uint16_t structure_size; /* This MUST be set to 49 */ uint16_t data_offset; /* offset in bytes from the beginning of smb2 header */ uint32_t length; /* length of data being written in bytes */ uint64_t offset; /* offset in the destination file */ uint64_t fileId_persistent; /* fileId that is persistent */ uint64_t fileId_volatile; /* fileId that is volatile */ uint32_t channel; /* channel */ uint32_t remaining_bytes; /* subsequent bytes the client intends to write*/ uint16_t write_channel_info_offset; /* channel data info */ uint16_t write_channel_info_length; /* channel data info */ uint32_t flags; /* flags*/ } Smb2WriteRequestHdr; typedef struct _Smb2WriteResponseHdr { uint16_t structure_size; /* This MUST be set to 17 */ uint16_t reserved; /* reserved */ uint32_t count; /* The number of bytes written */ uint32_t remaining; /* MUST be 0*/ uint16_t write_channel_info_offset; /* channel data info */ uint16_t write_channel_info_length; /* channel data info */ } Smb2WriteResponseHdr; typedef struct _Smb2ReadRequestHdr { uint16_t structure_size; /* This MUST be set to 49 */ uint8_t padding; /* Padding */ uint8_t flags; /* Flags */ uint32_t length; /* length of data to read from the file */ uint64_t offset; /* offset in the destination file */ uint64_t fileId_persistent; /* fileId that is persistent */ uint64_t fileId_volatile; /* fileId that is volatile */ uint32_t minimum_count; /* The minimum # of bytes to be read */ uint32_t channel; /* channel */ uint32_t remaining_bytes; /* subsequent bytes the client intends to read*/ uint16_t read_channel_info_offset; /* channel data info */ uint16_t read_channel_info_length; /* channel data info */ } Smb2ReadRequestHdr; typedef struct _Smb2ReadResponseHdr { uint16_t structure_size; /* This MUST be set to 17 */ uint8_t data_offset; /* offset in bytes from beginning of smb2 header*/ uint8_t reserved; /* reserved */ uint32_t length; /* The number of bytes being returned in response */ uint32_t remaining; /* The number of data being sent on the channel*/ uint32_t reserved2; /* reserved */ } Smb2ReadResponseHdr; typedef struct _Smb2SetInfoRequestHdr { uint16_t structure_size; /* This MUST be set to 33 */ uint8_t info_type; /* info type */ uint8_t file_info_class; /* file info class after header */ uint32_t buffer_length; /* buffer length */ uint16_t buffer_offset; /* buffer offset */ uint16_t reserved; /* reserved */ uint32_t additional_info; /* additional information */ uint64_t fileId_persistent;/* fileId that is persistent */ uint64_t fileId_volatile; /* fileId that is volatile */ } Smb2ASetInfoRequestHdr; typedef struct _Smb2CreateRequestHdr { uint16_t structure_size; /* This MUST be set to 57 */ uint8_t security_flags; /* security flag, should be 0 */ uint8_t requested_oplock_level; /* */ uint32_t impersonation_level; /* */ uint64_t smb_create_flags; /* should be 0 */ uint64_t reserved; /* can be any value */ uint32_t desired_access; /* */ uint32_t file_attributes; /* */ uint32_t share_access; /* READ WRITE DELETE etc */ uint32_t create_disposition; /* actions when file exists*/ uint32_t create_options; /* options for creating file*/ uint16_t name_offset; /* file name offset from SMB2 header */ uint16_t name_length; /* length of file name */ uint32_t create_contexts_offset; /* offset of contexts from beginning of header */ uint32_t create_contexts_length; /* length of contexts */ } Smb2ACreateRequestHdr; typedef struct _Smb2CreateResponseHdr { uint16_t structure_size; /* This MUST be set to 89 */ uint8_t oplock_level; /* oplock level granted, values limited */ uint8_t flags; /* flags, values limited */ uint32_t create_action; /* action taken, values limited */ uint64_t creation_time; /* time created */ uint64_t last_access_time; /* access time */ uint64_t last_write_time; /* write time */ uint64_t change_time; /* time modified*/ uint64_t allocation_size; /* size allocated */ uint64_t end_of_file; /* file size*/ uint32_t file_attributes; /* attributes of the file*/ uint32_t reserved2; /* */ uint64_t fileId_persistent; /* fileId that is persistent */ uint64_t fileId_volatile; /* fileId that is volatile */ uint32_t create_contexts_offset; /* */ uint32_t create_contexts_length; /* */ } Smb2ACreateResponseHdr; typedef struct _Smb2CloseRequestHdr { uint16_t structure_size; /* This MUST be set to 24 */ uint16_t flags; /* flags */ uint32_t reserved; /* can be any value */ uint64_t fileId_persistent; /* fileId that is persistent */ uint64_t fileId_volatile; /* fileId that is volatile */ } Smb2CloseRequestHdr; #define SMB2_SHARE_TYPE_DISK 0x01 #define SMB2_SHARE_TYPE_PIPE 0x02 #define SMB2_SHARE_TYPE_PRINT 0x03 typedef struct _Smb2TreeConnectResponseHdr { uint16_t structure_size; /* This MUST be set to 16 */ uint8_t share_type; /* type of share being accessed */ uint8_t reserved; /* reserved */ uint32_t share_flags; /* properties for this share*/ uint32_t capabilities; /* various capabilities for this share */ uint32_t maximal_access; /* maximal access for the user */ } Smb2TreeConnectResponseHdr; typedef struct _Smb2TreeDisConnectHdr { uint16_t structure_size; /* This MUST be set to 4 */ uint16_t reserved; /* reserved */ } Smb2TreeDisConnectHdr; typedef struct _Smb2CreateContextHdr { uint32_t next; /* next context header*/ uint16_t name_offset; /* name offset */ uint16_t name_length; /* name length */ uint16_t reserved; /* reserved */ uint16_t data_offset; /* data offset */ uint32_t data_length; /* data length */ } Smb2CreateContextHdr; #define SMB2_HEADER_LENGTH 64 #define SMB2_ERROR_RESPONSE_STRUC_SIZE 9 #define SMB2_CREATE_REQUEST_STRUC_SIZE 57 #define SMB2_CREATE_RESPONSE_STRUC_SIZE 89 #define SMB2_CREATE_REQUEST_DATA_OFFSET 120 #define SMB2_CLOSE_REQUEST_STRUC_SIZE 24 #define SMB2_WRITE_REQUEST_STRUC_SIZE 49 #define SMB2_WRITE_RESPONSE_STRUC_SIZE 17 #define SMB2_READ_REQUEST_STRUC_SIZE 49 #define SMB2_READ_RESPONSE_STRUC_SIZE 17 #define SMB2_SET_INFO_REQUEST_STRUC_SIZE 33 #define SMB2_SET_INFO_RESPONSE_STRUC_SIZE 2 #define SMB2_TREE_CONNECT_RESPONSE_STRUC_SIZE 16 #define SMB2_TREE_DISCONNECT_STRUC_SIZE 4 #define SMB2_FILE_ENDOFFILE_INFO 0x14 #define SMB2_CREATE_DURABLE_RECONNECT "DHnC" #define SMB2_CREATE_DURABLE_RECONNECT_V2 "DH2C" /* Initialize smb2 processing, setup cap for smb2 memory usage */ void DCE2_Smb2Init(uint64_t memcap); /* Stop smb2 processing, release resources */ void DCE2_Smb2Close(void); /* Clean up all the pending requests*/ void DCE2_Smb2CleanRequests(Smb2Request *requests); /* Process smb2 message */ void DCE2_Smb2Process(DCE2_SmbSsnData *ssd); /* Initialize file tracker for smb2 processing */ DCE2_Ret DCE2_Smb2InitFileTracker( DCE2_SmbFileTracker *ftracker, const bool is_ipc, const uint64_t fid); /* Check smb version based on smb header */ DCE2_SmbVersion DCE2_Smb2Version(const SFSnortPacket *p); /* Update statistics for smb2 processing */ void DCE2_Smb2UpdateStats(void); bool DCE2_IsFileCache(void *ptr); #ifdef SNORT_RELOAD /*ensures that file cache is obeying new memcap *deletes file cache if necessary */ bool DCE2_Smb2AdjustFileCache(uint8_t work, bool cache_enabled); void DCE2_SetSmbMemcap(uint64_t smbMemcap); #endif #endif /* _DCE2_SMB2_H_ */ snort-2.9.20/src/dynamic-preprocessors/dcerpc2/dce2_smb.c0000644000175000017500000133561314241075660021400 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_dce2.h" #include "dce2_smb.h" #include "dce2_tcp.h" #include "dce2_co.h" #include "snort_dce2.h" #include "dce2_config.h" #include "dce2_memory.h" #include "dce2_utils.h" #include "dce2_debug.h" #include "dce2_stats.h" #include "dce2_event.h" #include "smb.h" #include "sf_snort_packet.h" #include "sf_types.h" #include "stream_api.h" #include "session_api.h" #include "profiler.h" #include "snort_debug.h" #include "sf_dynamic_preprocessor.h" #include "file_api.h" #include "dce2_smb2.h" #ifdef DUMP_BUFFER #include "dcerpc2_buffer_dump.h" #endif #ifndef WIN32 #include /* for ntohl */ #endif /* WIN32 */ /******************************************************************** * Enums ********************************************************************/ typedef enum _DCE2_SmbComError { // No errors associated with the command DCE2_SMB_COM_ERROR__COMMAND_OK = 0x0000, // An error was reported in the SMB response header DCE2_SMB_COM_ERROR__STATUS_ERROR = 0x0001, // An invalid word count makes it unlikely any data accessed will be correct // and if accessed the possibility of accessing out of bounds data DCE2_SMB_COM_ERROR__INVALID_WORD_COUNT = 0x0002, // An invalid byte count just means the byte count is not right for // the command processed. The command can still be processed but // the byte count should not be used. In general, the byte count // should not be used since Windows and Samba often times ignore it DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT = 0x0004, // Not enough data to process command so don't try to access any // of the command's header or data. DCE2_SMB_COM_ERROR__BAD_LENGTH = 0x0008 } DCE2_SmbComError; /******************************************************************** * Structures ********************************************************************/ typedef struct _DCE2_SmbComInfo { int smb_type; // SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE int cmd_error; // mask of DCE2_SmbComError uint8_t smb_com; uint8_t word_count; uint16_t byte_count; uint16_t cmd_size; } DCE2_SmbComInfo; unsigned smb_upload_ret_cb_id = 0; // Inline accessor functions for DCE2_SmbComInfo static inline bool DCE2_ComInfoIsResponse(const DCE2_SmbComInfo *com_info) { return (com_info->smb_type == SMB_TYPE__RESPONSE) ? true : false; } static inline bool DCE2_ComInfoIsRequest(const DCE2_SmbComInfo *com_info) { return (com_info->smb_type == SMB_TYPE__REQUEST) ? true : false; } static inline uint8_t DCE2_ComInfoWordCount(const DCE2_SmbComInfo *com_info) { return com_info->word_count; } static inline uint8_t DCE2_ComInfoSmbCom(const DCE2_SmbComInfo *com_info) { return com_info->smb_com; } static inline uint16_t DCE2_ComInfoByteCount(const DCE2_SmbComInfo *com_info) { return com_info->byte_count; } static inline uint16_t DCE2_ComInfoCommandSize(const DCE2_SmbComInfo *com_info) { return com_info->cmd_size; } static inline bool DCE2_ComInfoIsStatusError(const DCE2_SmbComInfo *com_info) { return (com_info->cmd_error & DCE2_SMB_COM_ERROR__STATUS_ERROR) ? true : false; } static inline bool DCE2_ComInfoIsInvalidWordCount(const DCE2_SmbComInfo *com_info) { return (com_info->cmd_error & DCE2_SMB_COM_ERROR__INVALID_WORD_COUNT) ? true : false; } static inline bool DCE2_ComInfoIsBadLength(const DCE2_SmbComInfo *com_info) { return (com_info->cmd_error & DCE2_SMB_COM_ERROR__BAD_LENGTH) ? true : false; } // If this returns false, the command should not be processed static inline bool DCE2_ComInfoCanProcessCommand(const DCE2_SmbComInfo *com_info) { if (DCE2_ComInfoIsBadLength(com_info) || DCE2_ComInfoIsStatusError(com_info) || DCE2_ComInfoIsInvalidWordCount(com_info)) return false; return true; } /******************************************************************** * Global variables ********************************************************************/ typedef DCE2_Ret (*DCE2_SmbComFunc)(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_SmbComFunc smb_com_funcs[SMB_MAX_NUM_COMS]; static uint8_t smb_wcts[SMB_MAX_NUM_COMS][2][32]; static uint16_t smb_bccs[SMB_MAX_NUM_COMS][2][2]; static DCE2_SmbComFunc smb_chain_funcs[DCE2_POLICY__MAX][SMB_ANDX_COM__MAX][SMB_MAX_NUM_COMS]; static bool smb_deprecated_coms[SMB_MAX_NUM_COMS]; static bool smb_unusual_coms[SMB_MAX_NUM_COMS]; // File name of the current file we are tracking for logging since the // file tracker may be gone before logging occurs. uint8_t smb_file_name[2*DCE2_SMB_MAX_PATH_LEN + UTF_16_LE_BOM_LEN + 2]; uint16_t smb_file_name_len; // Exported SmbAndXCom smb_chain_map[SMB_MAX_NUM_COMS]; const char *smb_com_strings[SMB_MAX_NUM_COMS] = { "Create Directory", // 0x00 "Delete Directory", // 0x01 "Open", // 0x02 "Create", // 0x03 "Close", // 0x04 "Flush", // 0x05 "Delete", // 0x06 "Rename", // 0x07 "Query Information", // 0x08 "Set Information", // 0x09 "Read", // 0x0A "Write", // 0x0B "Lock Byte Range", // 0x0C "Unlock Byte Range", // 0x0D "Create Temporary", // 0x0E "Create New", // 0x0F "Check Directory", // 0x10 "Process Exit", // 0x11 "Seek", // 0x12 "Lock And Read", // 0x13 "Write And Unlock", // 0x14 "Unknown", // 0X15 "Unknown", // 0X16 "Unknown", // 0X17 "Unknown", // 0X18 "Unknown", // 0X19 "Read Raw", // 0x1A "Read Mpx", // 0x1B "Read Mpx Secondary", // 0x1C "Write Raw", // 0x1D "Write Mpx", // 0x1E "Write Mpx Secondary", // 0x1F "Write Complete", // 0x20 "Query Server", // 0x21 "Set Information2", // 0x22 "Query Information2", // 0x23 "Locking AndX", // 0x24 "Transaction", // 0x25 "Transaction Secondary", // 0x26 "Ioctl", // 0x27 "Ioctl Secondary", // 0x28 "Copy", // 0x29 "Move", // 0x2A "Echo", // 0x2B "Write And Close", // 0x2C "Open AndX", // 0x2D "Read AndX", // 0x2E "Write AndX", // 0x2F "New File Size", // 0x30 "Close And Tree Disc", // 0x31 "Transaction2", // 0x32 "Transaction2 Secondary", // 0x33 "Find Close2", // 0x34 "Find Notify Close", // 0x35 "Unknown", // 0X36 "Unknown", // 0X37 "Unknown", // 0X38 "Unknown", // 0X39 "Unknown", // 0X3A "Unknown", // 0X3B "Unknown", // 0X3C "Unknown", // 0X3D "Unknown", // 0X3E "Unknown", // 0X3F "Unknown", // 0X40 "Unknown", // 0X41 "Unknown", // 0X42 "Unknown", // 0X43 "Unknown", // 0X44 "Unknown", // 0X45 "Unknown", // 0X46 "Unknown", // 0X47 "Unknown", // 0X48 "Unknown", // 0X49 "Unknown", // 0X4A "Unknown", // 0X4B "Unknown", // 0X4C "Unknown", // 0X4D "Unknown", // 0X4E "Unknown", // 0X4F "Unknown", // 0X50 "Unknown", // 0X51 "Unknown", // 0X52 "Unknown", // 0X53 "Unknown", // 0X54 "Unknown", // 0X55 "Unknown", // 0X56 "Unknown", // 0X57 "Unknown", // 0X58 "Unknown", // 0X59 "Unknown", // 0X5A "Unknown", // 0X5B "Unknown", // 0X5C "Unknown", // 0X5D "Unknown", // 0X5E "Unknown", // 0X5F "Unknown", // 0X60 "Unknown", // 0X61 "Unknown", // 0X62 "Unknown", // 0X63 "Unknown", // 0X64 "Unknown", // 0X65 "Unknown", // 0X66 "Unknown", // 0X67 "Unknown", // 0X68 "Unknown", // 0X69 "Unknown", // 0X6A "Unknown", // 0X6B "Unknown", // 0X6C "Unknown", // 0X6D "Unknown", // 0X6E "Unknown", // 0X6F "Tree Connect", // 0x70 "Tree Disconnect", // 0x71 "Negotiate", // 0x72 "Session Setup AndX", // 0x73 "Logoff AndX", // 0x74 "Tree Connect AndX", // 0x75 "Unknown", // 0X76 "Unknown", // 0X77 "Unknown", // 0X78 "Unknown", // 0X79 "Unknown", // 0X7A "Unknown", // 0X7B "Unknown", // 0X7C "Unknown", // 0X7D "Security Package AndX", // 0x7E "Unknown", // 0X7F "Query Information Disk", // 0x80 "Search", // 0x81 "Find", // 0x82 "Find Unique", // 0x83 "Find Close", // 0x84 "Unknown", // 0X85 "Unknown", // 0X86 "Unknown", // 0X87 "Unknown", // 0X88 "Unknown", // 0X89 "Unknown", // 0X8A "Unknown", // 0X8B "Unknown", // 0X8C "Unknown", // 0X8D "Unknown", // 0X8E "Unknown", // 0X8F "Unknown", // 0X90 "Unknown", // 0X91 "Unknown", // 0X92 "Unknown", // 0X93 "Unknown", // 0X94 "Unknown", // 0X95 "Unknown", // 0X96 "Unknown", // 0X97 "Unknown", // 0X98 "Unknown", // 0X99 "Unknown", // 0X9A "Unknown", // 0X9B "Unknown", // 0X9C "Unknown", // 0X9D "Unknown", // 0X9E "Unknown", // 0X9F "Nt Transact", // 0xA0 "Nt Transact Secondary", // 0xA1 "Nt Create AndX", // 0xA2 "Unknown", // 0XA3 "Nt Cancel", // 0xA4 "Nt Rename", // 0xA5 "Unknown", // 0XA6 "Unknown", // 0XA7 "Unknown", // 0XA8 "Unknown", // 0XA9 "Unknown", // 0XAA "Unknown", // 0XAB "Unknown", // 0XAC "Unknown", // 0XAD "Unknown", // 0XAE "Unknown", // 0XAF "Unknown", // 0XB0 "Unknown", // 0XB1 "Unknown", // 0XB2 "Unknown", // 0XB3 "Unknown", // 0XB4 "Unknown", // 0XB5 "Unknown", // 0XB6 "Unknown", // 0XB7 "Unknown", // 0XB8 "Unknown", // 0XB9 "Unknown", // 0XBA "Unknown", // 0XBB "Unknown", // 0XBC "Unknown", // 0XBD "Unknown", // 0XBE "Unknown", // 0XBF "Open Print File", // 0xC0 "Write Print File", // 0xC1 "Close Print File", // 0xC2 "Get Print Queue", // 0xC3 "Unknown", // 0XC4 "Unknown", // 0XC5 "Unknown", // 0XC6 "Unknown", // 0XC7 "Unknown", // 0XC8 "Unknown", // 0XC9 "Unknown", // 0XCA "Unknown", // 0XCB "Unknown", // 0XCC "Unknown", // 0XCD "Unknown", // 0XCE "Unknown", // 0XCF "Unknown", // 0XD0 "Unknown", // 0XD1 "Unknown", // 0XD2 "Unknown", // 0XD3 "Unknown", // 0XD4 "Unknown", // 0XD5 "Unknown", // 0XD6 "Unknown", // 0XD7 "Read Bulk", // 0xD8 "Write Bulk", // 0xD9 "Write Bulk Data", // 0xDA "Unknown", // 0XDB "Unknown", // 0XDC "Unknown", // 0XDD "Unknown", // 0XDE "Unknown", // 0XDF "Unknown", // 0XE0 "Unknown", // 0XE1 "Unknown", // 0XE2 "Unknown", // 0XE3 "Unknown", // 0XE4 "Unknown", // 0XE5 "Unknown", // 0XE6 "Unknown", // 0XE7 "Unknown", // 0XE8 "Unknown", // 0XE9 "Unknown", // 0XEA "Unknown", // 0XEB "Unknown", // 0XEC "Unknown", // 0XED "Unknown", // 0XEE "Unknown", // 0XEF "Unknown", // 0XF0 "Unknown", // 0XF1 "Unknown", // 0XF2 "Unknown", // 0XF3 "Unknown", // 0XF4 "Unknown", // 0XF5 "Unknown", // 0XF6 "Unknown", // 0XF7 "Unknown", // 0XF8 "Unknown", // 0XF9 "Unknown", // 0XFA "Unknown", // 0XFB "Unknown", // 0XFC "Unknown", // 0XFD "Invalid", // 0xFE "No AndX Command" // 0xFF }; const char *smb_transaction_sub_command_strings[TRANS_SUBCOM_MAX] = { "Unknown", // 0x0000 "TRANS_SET_NMPIPE_STATE", // 0x0001 "Unknown", // 0x0002 "Unknown", // 0x0003 "Unknown", // 0x0004 "Unknown", // 0x0005 "Unknown", // 0x0006 "Unknown", // 0x0007 "Unknown", // 0x0008 "Unknown", // 0x0009 "Unknown", // 0x000A "Unknown", // 0x000B "Unknown", // 0x000C "Unknown", // 0x000D "Unknown", // 0x000E "Unknown", // 0x000F "Unknown", // 0x0010 "TRANS_RAW_READ_NMPIPE", // 0x0011 "Unknown", // 0x0012 "Unknown", // 0x0013 "Unknown", // 0x0014 "Unknown", // 0x0015 "Unknown", // 0x0016 "Unknown", // 0x0017 "Unknown", // 0x0018 "Unknown", // 0x0019 "Unknown", // 0x001A "Unknown", // 0x001B "Unknown", // 0x001C "Unknown", // 0x001D "Unknown", // 0x001E "Unknown", // 0x001F "Unknown", // 0x0020 "TRANS_QUERY_NMPIPE_STATE", // 0x0021 "TRANS_QUERY_NMPIPE_INFO", // 0x0022 "TRANS_PEEK_NMPIPE", // 0x0023 "Unknown", // 0x0024 "Unknown", // 0x0025 "TRANS_TRANSACT_NMPIPE", // 0x0026 "Unknown", // 0x0027 "Unknown", // 0x0028 "Unknown", // 0x0029 "Unknown", // 0x002A "Unknown", // 0x002B "Unknown", // 0x002C "Unknown", // 0x002D "Unknown", // 0x002E "Unknown", // 0x002F "Unknown", // 0x0030 "TRANS_RAW_WRITE_NMPIPE", // 0x0031 "Unknown", // 0x0032 "Unknown", // 0x0033 "Unknown", // 0x0034 "Unknown", // 0x0035 "TRANS_READ_NMPIPE", // 0x0036 "TRANS_WRITE_NMPIPE", // 0x0037 "Unknown", // 0x0038 "Unknown", // 0x0039 "Unknown", // 0x003A "Unknown", // 0x003B "Unknown", // 0x003C "Unknown", // 0x003D "Unknown", // 0x003E "Unknown", // 0x003F "Unknown", // 0x0040 "Unknown", // 0x0041 "Unknown", // 0x0042 "Unknown", // 0x0043 "Unknown", // 0x0044 "Unknown", // 0x0045 "Unknown", // 0x0046 "Unknown", // 0x0047 "Unknown", // 0x0048 "Unknown", // 0x0049 "Unknown", // 0x004A "Unknown", // 0x004B "Unknown", // 0x004C "Unknown", // 0x004D "Unknown", // 0x004E "Unknown", // 0x004F "Unknown", // 0x0050 "Unknown", // 0x0051 "Unknown", // 0x0052 "TRANS_WAIT_NMPIPE", // 0x0053 "TRANS_CALL_NMPIPE" // 0x0054 }; const char *smb_transaction2_sub_command_strings[TRANS2_SUBCOM_MAX] = { "TRANS2_OPEN2", // 0x0000 "TRANS2_FIND_FIRST2", // 0x0001 "TRANS2_FIND_NEXT2", // 0x0002 "TRANS2_QUERY_FS_INFORMATION", // 0x0003 "TRANS2_SET_FS_INFORMATION", // 0x0004 "TRANS2_QUERY_PATH_INFORMATION", // 0x0005 "TRANS2_SET_PATH_INFORMATION", // 0x0006 "TRANS2_QUERY_FILE_INFORMATION", // 0x0007 "TRANS2_SET_FILE_INFORMATION", // 0x0008 "TRANS2_FSCTL", // 0x0009 "TRANS2_IOCTL2", // 0x000A "TRANS2_FIND_NOTIFY_FIRST", // 0x000B "TRANS2_FIND_NOTIFY_NEXT", // 0x000C "TRANS2_CREATE_DIRECTORY", // 0x000D "TRANS2_SESSION_SETUP", // 0x000E "Unknown", // 0x000F "TRANS2_GET_DFS_REFERRAL", // 0x0010 "TRANS2_REPORT_DFS_INCONSISTENCY" // 0x0011 }; const char *smb_nt_transact_sub_command_strings[NT_TRANSACT_SUBCOM_MAX] = { "Unknown", // 0x0000 "NT_TRANSACT_CREATE", // 0x0001 "NT_TRANSACT_IOCTL", // 0x0002 "NT_TRANSACT_SET_SECURITY_DESC", // 0x0003 "NT_TRANSACT_NOTIFY_CHANGE", // 0x0004 "NT_TRANSACT_RENAME", // 0x0005 "NT_TRANSACT_QUERY_SECURITY_DESC" // 0x0006 }; /******************************************************************** * Private function prototypes ********************************************************************/ static inline int DCE2_SmbType(DCE2_SmbSsnData *); static inline void DCE2_SmbSetValidWordCount(uint8_t, uint8_t, uint8_t); static inline bool DCE2_SmbIsValidWordCount(uint8_t, uint8_t, uint8_t); static inline void DCE2_SmbSetValidByteCount(uint8_t, uint8_t, uint16_t, uint16_t); static inline bool DCE2_SmbIsValidByteCount(uint8_t, uint8_t, uint16_t); static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData *, const NbssHdr *); static DCE2_SmbRequestTracker * DCE2_SmbInspect(DCE2_SmbSsnData *, const SmbNtHdr *); static DCE2_Ret DCE2_SmbHdrChecks(DCE2_SmbSsnData *, const SmbNtHdr *); static uint32_t DCE2_IgnoreJunkData(const uint8_t *, uint16_t, uint32_t); static inline DCE2_Ret DCE2_SmbHandleSegmentation(DCE2_Buffer **, const uint8_t *, uint32_t, uint32_t); static inline DCE2_Buffer ** DCE2_SmbGetSegBuffer(DCE2_SmbSsnData *); static inline uint32_t * DCE2_SmbGetIgnorePtr(DCE2_SmbSsnData *); static inline DCE2_SmbDataState * DCE2_SmbGetDataState(DCE2_SmbSsnData *); static inline bool DCE2_SmbIsSegBuffer(DCE2_SmbSsnData *, const uint8_t *); static inline void DCE2_SmbSegAlert(DCE2_SmbSsnData *, DCE2_Event); static inline bool DCE2_SmbIsRawData(DCE2_SmbSsnData *); static void DCE2_SmbProcessRawData(DCE2_SmbSsnData *, const uint8_t *, uint32_t); static DCE2_SmbComInfo * DCE2_SmbCheckCommand(DCE2_SmbSsnData *, const SmbNtHdr *, const uint8_t, const uint8_t *, uint32_t); static void DCE2_SmbProcessCommand(DCE2_SmbSsnData *, const SmbNtHdr *, const uint8_t *, uint32_t); static inline DCE2_Ret DCE2_SmbCheckData(DCE2_SmbSsnData *, const uint8_t *, const uint8_t *, const uint32_t, const uint16_t, const uint32_t, uint16_t); static inline DCE2_Ret DCE2_SmbValidateTransactionFields(DCE2_SmbSsnData *, const uint8_t *, const uint8_t *, const uint32_t, const uint16_t, const uint32_t, const uint32_t, const uint32_t, const uint32_t, const uint32_t, const uint32_t, const uint32_t, const uint32_t); static inline DCE2_Ret DCE2_SmbValidateTransactionSent(DCE2_SmbSsnData *, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); static inline DCE2_Ret DCE2_SmbCheckTransDataParams(DCE2_SmbSsnData *, const uint8_t *, const uint8_t *, const uint32_t, const uint16_t, const uint32_t, const uint32_t, const uint32_t, const uint32_t); static inline DCE2_Ret DCE2_SmbCheckTotalCount(DCE2_SmbSsnData *, const uint32_t, const uint32_t, const uint32_t); static inline void DCE2_SmbCheckFmtData(DCE2_SmbSsnData *, const uint32_t, const uint16_t, const uint8_t, const uint16_t, const uint16_t); static inline DCE2_Ret DCE2_SmbCheckAndXOffset(DCE2_SmbSsnData *, const uint8_t *, const uint8_t *, const uint32_t); static inline void DCE2_SmbInvalidShareCheck(DCE2_SmbSsnData *, const SmbNtHdr *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTransactionGetName(const uint8_t *, uint32_t, uint16_t, bool); static inline bool DCE2_SmbIsTransactionComplete(DCE2_SmbTransactionTracker *); static DCE2_Ret DCE2_SmbUpdateTransRequest(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbUpdateTransSecondary(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbUpdateTransResponse(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbOpen(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbCreate(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbClose(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbRename(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbRead(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbWrite(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbCreateNew(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbLockAndRead(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbWriteAndUnlock(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbReadRaw(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbWriteRaw(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbWriteComplete(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static inline DCE2_Ret DCE2_SmbTransactionReq(DCE2_SmbSsnData *, DCE2_SmbTransactionTracker *, const uint8_t *, uint32_t, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTransaction(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTransactionSecondary(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbWriteAndClose(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbOpenAndX(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbReadAndX(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbWriteAndX(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static inline DCE2_Ret DCE2_SmbTrans2Open2Req(DCE2_SmbSsnData *, const uint8_t *, uint32_t, bool); static inline DCE2_Ret DCE2_SmbTrans2QueryFileInfoReq(DCE2_SmbSsnData *, const uint8_t *, uint32_t); static inline DCE2_Ret DCE2_SmbTrans2SetFileInfoReq(DCE2_SmbSsnData *, const uint8_t *, uint32_t, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTransaction2(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTransaction2Secondary(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTreeConnect(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTreeDisconnect(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbNegotiate(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbSessionSetupAndX(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbLogoffAndX(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbTreeConnectAndX(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static inline DCE2_Ret DCE2_SmbNtTransactCreateReq(DCE2_SmbSsnData *, const uint8_t *, uint32_t, bool); static DCE2_Ret DCE2_SmbNtTransact(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbNtTransactSecondary(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static DCE2_Ret DCE2_SmbNtCreateAndX(DCE2_SmbSsnData *, const SmbNtHdr *, const DCE2_SmbComInfo *, const uint8_t *, uint32_t); static inline DCE2_Ret DCE2_SmbProcessRequestData(DCE2_SmbSsnData *, const uint16_t, const uint8_t *, uint32_t, uint64_t); static inline DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData *, const uint8_t *, uint32_t); static void DCE2_SmbProcessFileData(DCE2_SmbSsnData *, DCE2_SmbFileTracker *, const uint8_t *, uint32_t, bool); static inline DCE2_SmbRequestTracker * DCE2_SmbNewRequestTracker(DCE2_SmbSsnData *, const SmbNtHdr *); static inline DCE2_Ret DCE2_SmbBufferTransactionData(DCE2_SmbTransactionTracker *, const uint8_t *, uint16_t, uint16_t); static inline DCE2_Ret DCE2_SmbBufferTransactionParameters(DCE2_SmbTransactionTracker *, const uint8_t *, uint16_t, uint16_t); static inline DCE2_SmbRequestTracker * DCE2_SmbFindRequestTracker(DCE2_SmbSsnData *, const SmbNtHdr *); static inline void DCE2_SmbRemoveRequestTracker(DCE2_SmbSsnData *, DCE2_SmbRequestTracker *); static void DCE2_SmbInsertUid(DCE2_SmbSsnData *, const uint16_t); static DCE2_Ret DCE2_SmbFindUid(DCE2_SmbSsnData *, const uint16_t); static void DCE2_SmbRemoveUid(DCE2_SmbSsnData *ssd, const uint16_t); static void DCE2_SmbInsertTid(DCE2_SmbSsnData *, const uint16_t, const bool); static DCE2_Ret DCE2_SmbFindTid(DCE2_SmbSsnData *, const uint16_t); static bool DCE2_SmbIsTidIPC(DCE2_SmbSsnData *, const uint16_t); static void DCE2_SmbRemoveTid(DCE2_SmbSsnData *, const uint16_t); static DCE2_SmbFileTracker * DCE2_SmbNewFileTracker(DCE2_SmbSsnData *, const uint16_t, const uint16_t, const uint16_t); static void DCE2_SmbQueueTmpFileTracker(DCE2_SmbSsnData *, DCE2_SmbRequestTracker *, const uint16_t, const uint16_t); static inline DCE2_SmbFileTracker * DCE2_SmbGetTmpFileTracker(DCE2_SmbRequestTracker *); static DCE2_SmbFileTracker * DCE2_SmbDequeueTmpFileTracker(DCE2_SmbSsnData *, DCE2_SmbRequestTracker *, const uint16_t); static inline DCE2_SmbFileTracker * DCE2_SmbGetFileTracker(DCE2_SmbSsnData *, const uint16_t); static DCE2_SmbFileTracker * DCE2_SmbFindFileTracker(DCE2_SmbSsnData *, const uint16_t, const uint16_t, const uint16_t); static DCE2_Ret DCE2_SmbRemoveFileTracker(DCE2_SmbSsnData *, DCE2_SmbFileTracker *); static inline void DCE2_SmbCleanFileTracker(DCE2_SmbFileTracker *); static inline void DCE2_SmbCleanTransactionTracker(DCE2_SmbTransactionTracker *); static inline void DCE2_SmbCleanRequestTracker(DCE2_SmbRequestTracker *); static int DCE2_SmbUidTidFidCompare(const void *, const void *); static void DCE2_SmbFileTrackerDataFree(void *); static void DCE2_SmbRequestTrackerDataFree(void *); static inline SFSnortPacket * DCE2_SmbGetRpkt(DCE2_SmbSsnData *, const uint8_t **, uint32_t *, DCE2_RpktType); static inline void DCE2_SmbReturnRpkt(void); static inline void DCE2_SmbSetFileName(uint8_t *, uint16_t); static uint8_t* DCE2_SmbGetString(const uint8_t *, uint32_t, bool, uint16_t *); static inline void DCE2_Update_Ftracker_from_ReqTracker(DCE2_SmbFileTracker *ftracker, DCE2_SmbRequestTracker *cur_rtracker); static inline void DCE2_SmbResetFileChunks(DCE2_SmbFileTracker *); static inline void DCE2_SmbAbortFileAPI(DCE2_SmbSsnData *); static inline DCE2_SmbRetransmitPending DCE2_SmbFinishFileAPI(DCE2_SmbSsnData *); static inline void DCE2_SmbSetNewFileAPIFileTracker(DCE2_SmbSsnData *); static int DCE2_SmbFileOffsetCompare(const void *, const void *); static void DCE2_SmbFileChunkFree(void *); static DCE2_Ret DCE2_SmbHandleOutOfOrderFileData(DCE2_SmbSsnData *, DCE2_SmbFileTracker *, const uint8_t *, uint32_t, bool); static DCE2_Ret DCE2_SmbFileAPIProcess(DCE2_SmbSsnData *, DCE2_SmbFileTracker *, const uint8_t *, uint32_t, bool); static inline void DCE2_SmbRemoveFileTrackerFromRequestTrackers(DCE2_SmbSsnData *, DCE2_SmbFileTracker *); #ifdef ACTIVE_RESPONSE static void DCE2_SmbInjectDeletePdu(DCE2_SmbSsnData *, DCE2_SmbFileTracker *); static void DCE2_SmbFinishFileBlockVerdict(DCE2_SmbSsnData *); static File_Verdict DCE2_SmbGetFileVerdict(void *, void *); #endif static inline void DCE2_SmbCleanSessionFileTracker(DCE2_SmbSsnData *, DCE2_SmbFileTracker *); /******************************************************************** * Function: DCE2_SmbType() * * Purpose: * Since Windows and Samba don't seem to care or even look at the * actual flag in the SMB header, make the determination based on * whether from client or server. * * Arguments: * DCE2_SmbSsnData * - session data structure that has the raw * packet and packet flags to make determination * * Returns: * SMB_TYPE__REQUEST if packet is from client * SMB_TYPE__RESPONSE if packet is from server * ********************************************************************/ static inline int DCE2_SmbType(DCE2_SmbSsnData *ssd) { if (DCE2_SsnFromClient(ssd->sd.wire_pkt)) return SMB_TYPE__REQUEST; else return SMB_TYPE__RESPONSE; } /******************************************************************** * Function: DCE2_SmbSetValidWordCount() * * Purpose: * Initializes global data for valid word counts for supported * SMB command requests and responses. * * Arguments: * uint8_t - the SMB command code * uint8_t - SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE * uint8_t - the valid word count * * Returns: None * ********************************************************************/ static inline void DCE2_SmbSetValidWordCount(uint8_t com, uint8_t resp, uint8_t wct) { smb_wcts[com][resp][wct/8] |= (1 << (wct % 8)); } /******************************************************************** * Function: DCE2_SmbIsValidWordCount() * * Purpose: * Checks if a word count is valid for a given command request * or response. * * Arguments: * uint8_t - the SMB command code * uint8_t - SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE * uint8_t - the word count to validate * * Returns: * bool - true if valid, false if not valid. * ********************************************************************/ static inline bool DCE2_SmbIsValidWordCount(uint8_t com, uint8_t resp, uint8_t wct) { return (smb_wcts[com][resp][wct/8] & (1 << (wct % 8))) ? true : false; } /******************************************************************** * Function: DCE2_SmbSetValidByteCount() * * Purpose: * Initializes global data for valid byte counts as a range for * supported SMB command requests and responses. * Since a byte count is 2 bytes, a 4 byte type is used to store * the range. The maximum is in the most significant 2 bytes and * the minimum in the least significant 2 bytes. * * Arguments: * uint8_t - the SMB command code * uint8_t - SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE * uint8_t - the minimum word count that is valid * uint8_t - the maximum word count that is valid * * Returns: None * ********************************************************************/ static inline void DCE2_SmbSetValidByteCount(uint8_t com, uint8_t resp, uint16_t min, uint16_t max) { smb_bccs[com][resp][0] = min; smb_bccs[com][resp][1] = max; } /******************************************************************** * Function: DCE2_SmbIsValidByteCount() * * Purpose: * Checks if a byte count is valid for a given command request * or response. * * Arguments: * uint8_t - the SMB command code * uint8_t - SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE * uint8_t - the byte count to validate * * Returns: * bool - true if valid, false if not valid. * ********************************************************************/ static inline bool DCE2_SmbIsValidByteCount(uint8_t com, uint8_t resp, uint16_t bcc) { return ((bcc < smb_bccs[com][resp][0]) || (bcc > smb_bccs[com][resp][1])) ? false : true; } /******************************************************************** * Function: DCE2_SmbGetMinByteCount() * * Purpose: * Returns the minimum byte count for the given command request * or response. * * Arguments: * uint8_t - the SMB command code * uint8_t - SMB_TYPE__REQUEST or SMB_TYPE__RESPONSE * * Returns: * uint16_t - the minimum byte count * ********************************************************************/ static inline uint16_t DCE2_SmbGetMinByteCount(uint8_t com, uint8_t resp) { return smb_bccs[com][resp][0]; } /******************************************************************** * Function: DCE2_SmbInitGlobals() * * Purpose: * Initializes global variables for SMB processing. * Sets up the functions and valid word and byte counts for SMB * commands. * Sets up AndX chain mappings and valid command chaining for * supported policies. * * Arguments: None * * Returns: None * ********************************************************************/ void DCE2_SmbInitGlobals(void) { int com; DCE2_Policy policy; SmbAndXCom andx; int i; memset(&smb_wcts, 0, sizeof(smb_wcts)); memset(&smb_bccs, 0, sizeof(smb_bccs)); if (!smb_upload_ret_cb_id) smb_upload_ret_cb_id = _dpd.streamAPI->register_event_handler(DCE2_Process_Retransmitted); // Sets up the function to call for the command and valid word and byte // counts for the command. Ensuring valid word and byte counts is very // important to processing the command as it will assume the command is // legitimate and can access data that is acutally there. Note that // commands with multiple word counts indicate a different command // structure, however most, if not all just have an extended version // of the structure for which the extended part isn't used. If the // extended part of a command structure needs to be used, be sure to // check the word count in the command function before accessing data // in the extended version of the command structure. for (com = 0; com < SMB_MAX_NUM_COMS; com++) { switch (com) { case SMB_COM_OPEN: smb_com_funcs[com] = DCE2_SmbOpen; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 2); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 7); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_CREATE: smb_com_funcs[com] = DCE2_SmbCreate; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_CLOSE: smb_com_funcs[com] = DCE2_SmbClose; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_RENAME: smb_com_funcs[com] = DCE2_SmbRename; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 1); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 4, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_READ: smb_com_funcs[com] = DCE2_SmbRead; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 5); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 3, UINT16_MAX); break; case SMB_COM_WRITE: smb_com_funcs[com] = DCE2_SmbWrite; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_CREATE_NEW: smb_com_funcs[com] = DCE2_SmbCreateNew; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 3); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_LOCK_AND_READ: smb_com_funcs[com] = DCE2_SmbLockAndRead; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 5); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 3, UINT16_MAX); break; case SMB_COM_WRITE_AND_UNLOCK: smb_com_funcs[com] = DCE2_SmbWriteAndUnlock; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 5); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_READ_RAW: smb_com_funcs[com] = DCE2_SmbReadRaw; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 8); // With optional OffsetHigh DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0); // Response is raw data, i.e. without SMB break; case SMB_COM_WRITE_RAW: smb_com_funcs[com] = DCE2_SmbWriteRaw; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12); // With optional OffsetHigh DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14); // Interim server response DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_WRITE_COMPLETE: // Final server response to SMB_COM_WRITE_RAW smb_com_funcs[com] = DCE2_SmbWriteComplete; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_TRANSACTION: smb_com_funcs[com] = DCE2_SmbTransaction; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; // Word count depends on setup count //for (i = 14; i < 256; i++) // DCE2_SmbSetValidWordCount(com, SMB_TYPE__REQUEST, i); // In reality, all subcommands of SMB_COM_TRANSACTION requests // have a setup count of 2 words. DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 16); // \PIPE\LANMAN // Not something the preprocessor is looking at as it // doesn't carry DCE/RPC but don't want to false positive // on the preprocessor event. DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14); // Word count depends on setup count //for (i = 10; i < 256; i++) // DCE2_SmbSetValidWordCount(com, SMB_TYPE__RESPONSE, i); // In reality, all subcommands of SMB_COM_TRANSACTION responses // have a setup count of 0 words. DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 10); // Interim server response // When client sends an incomplete transaction and needs to // send TransactionSecondary requests to complete request. DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0); // Exception will be made for Interim responses when // byte count is checked. DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; case SMB_COM_TRANSACTION_SECONDARY: smb_com_funcs[com] = DCE2_SmbTransactionSecondary; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 8); // Response is an SMB_COM_TRANSACTION DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); break; case SMB_COM_WRITE_AND_CLOSE: smb_com_funcs[com] = DCE2_SmbWriteAndClose; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 6); // For some reason MS-CIFS specifies a version of this command // with 6 extra words (12 bytes) of reserved, i.e. useless data. // Maybe had intentions of extending and defining the data at // some point, but there is no documentation that I could find // that does. DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 1, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_OPEN_ANDX: smb_com_funcs[com] = DCE2_SmbOpenAndX; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 15); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 15); // Extended response DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 19); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX); // MS-SMB says that Windows 2000, XP and Vista set this to // some arbitrary value that is ignored on receipt. //DCE2_SmbSetValidByteCount(com, SMB_TYPE__RESPONSE, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; case SMB_COM_READ_ANDX: smb_com_funcs[com] = DCE2_SmbReadAndX; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10); // With optional OffsetHigh DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 12); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; case SMB_COM_WRITE_ANDX: smb_com_funcs[com] = DCE2_SmbWriteAndX; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12); // With optional OffsetHigh DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 14); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 6); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 1, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_TRANSACTION2: smb_com_funcs[com] = DCE2_SmbTransaction2; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; // Word count depends on setup count //for (i = 14; i < 256; i++) // DCE2_SmbSetValidWordCount(com, SMB_TYPE__REQUEST, i); // In reality, all subcommands of SMB_COM_TRANSACTION2 // requests have a setup count of 1 word. DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 15); // Word count depends on setup count //for (i = 10; i < 256; i++) // DCE2_SmbSetValidWordCount(com, SMB_TYPE__RESPONSE, i); // In reality, all subcommands of SMB_COM_TRANSACTION2 // responses have a setup count of 0 or 1 word. DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 10); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 11); // Interim server response DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0); // Exception will be made for Interim responses when // byte count is checked. DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; case SMB_COM_TRANSACTION2_SECONDARY: smb_com_funcs[com] = DCE2_SmbTransaction2Secondary; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 9); // Response is an SMB_COM_TRANSACTION2 DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); break; case SMB_COM_TREE_CONNECT: smb_com_funcs[com] = DCE2_SmbTreeConnect; smb_deprecated_coms[com] = true; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 6, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_TREE_DISCONNECT: smb_com_funcs[com] = DCE2_SmbTreeDisconnect; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_NEGOTIATE: // Not doing anything with this command right now. smb_com_funcs[com] = DCE2_SmbNegotiate; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 0); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 1); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 13); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 17); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX); // This can vary depending on dialect so just set wide. DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; case SMB_COM_SESSION_SETUP_ANDX: smb_com_funcs[com] = DCE2_SmbSessionSetupAndX; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 10); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 12); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 13); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 4); // These can vary so just set wide. DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; case SMB_COM_LOGOFF_ANDX: smb_com_funcs[com] = DCE2_SmbLogoffAndX; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 2); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2); // Windows responds to a LogoffAndX => SessionSetupAndX with just a // LogoffAndX and with the word count field containing 3, but only // has 2 words DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); break; case SMB_COM_TREE_CONNECT_ANDX: smb_com_funcs[com] = DCE2_SmbTreeConnectAndX; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 4); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 2); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 3); // Extended response DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 7); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 3, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 2, UINT16_MAX); break; case SMB_COM_NT_TRANSACT: smb_com_funcs[com] = DCE2_SmbNtTransact; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; // Word count depends on setup count // In reality, all subcommands of SMB_COM_NT_TRANSACT // requests have a setup count of 0 or 4 words. //for (i = 19; i < 256; i++) // DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, i); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 19); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 23); // Word count depends on setup count // In reality, all subcommands of SMB_COM_NT_TRANSACT // responses have a setup count of 0 or 1 word. //for (i = 18; i < 256; i++) // DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, i); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 18); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 19); // Interim server response DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 0); // Exception will be made for Interim responses when // byte count is checked. DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; case SMB_COM_NT_TRANSACT_SECONDARY: smb_com_funcs[com] = DCE2_SmbNtTransactSecondary; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 18); // Response is an SMB_COM_NT_TRANSACT DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); break; case SMB_COM_NT_CREATE_ANDX: smb_com_funcs[com] = DCE2_SmbNtCreateAndX; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, 24); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 34); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 26); // Extended response - though there are actually 50 words DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, 42); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 2, UINT16_MAX); // MS-SMB indicates that this field should be 0 but may be // sent uninitialized so basically ignore it. //DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, 0); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; default: smb_com_funcs[com] = NULL; smb_deprecated_coms[com] = false; smb_unusual_coms[com] = false; // Just set to all valid since the specific command won't // be processed. Don't want to false positive on these. for (i = 0; i < 256; i++) { DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__REQUEST, (uint8_t)i); DCE2_SmbSetValidWordCount((uint8_t)com, SMB_TYPE__RESPONSE, (uint8_t)i); } DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__REQUEST, 0, UINT16_MAX); DCE2_SmbSetValidByteCount((uint8_t)com, SMB_TYPE__RESPONSE, 0, UINT16_MAX); break; } } // Maps commands for use in quickly determining if a command // is chainable and what command it is. for (com = 0; com < SMB_MAX_NUM_COMS; com++) { switch (com) { case SMB_COM_SESSION_SETUP_ANDX: smb_chain_map[com] = SMB_ANDX_COM__SESSION_SETUP_ANDX; break; case SMB_COM_LOGOFF_ANDX: smb_chain_map[com] = SMB_ANDX_COM__LOGOFF_ANDX; break; case SMB_COM_TREE_CONNECT_ANDX: smb_chain_map[com] = SMB_ANDX_COM__TREE_CONNECT_ANDX; break; case SMB_COM_OPEN_ANDX: smb_chain_map[com] = SMB_ANDX_COM__OPEN_ANDX; break; case SMB_COM_NT_CREATE_ANDX: smb_chain_map[com] = SMB_ANDX_COM__NT_CREATE_ANDX; break; case SMB_COM_WRITE_ANDX: smb_chain_map[com] = SMB_ANDX_COM__WRITE_ANDX; break; case SMB_COM_READ_ANDX: smb_chain_map[com] = SMB_ANDX_COM__READ_ANDX; break; default: smb_chain_map[com] = SMB_ANDX_COM__NONE; break; } } // Sets up the valid command chaining combinations per policy for (policy = DCE2_POLICY__NONE; policy < DCE2_POLICY__MAX; policy++) { for (andx = SMB_ANDX_COM__NONE; andx < SMB_ANDX_COM__MAX; andx++) { /* com is the chained command or com2 */ for (com = 0; com < SMB_MAX_NUM_COMS; com++) { DCE2_SmbComFunc com_func = NULL; switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: switch (andx) { case SMB_ANDX_COM__SESSION_SETUP_ANDX: switch (com) { case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_OPEN: case SMB_COM_OPEN_ANDX: case SMB_COM_CREATE: case SMB_COM_CREATE_NEW: com_func = smb_com_funcs[com]; break; case SMB_COM_TRANSACTION: if (policy == DCE2_POLICY__WIN2000) com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__LOGOFF_ANDX: switch (com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_TREE_CONNECT_ANDX: // Only for responses com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__TREE_CONNECT_ANDX: switch (com) { case SMB_COM_OPEN: case SMB_COM_CREATE: case SMB_COM_CREATE_NEW: com_func = smb_com_funcs[com]; break; case SMB_COM_TRANSACTION: if (policy == DCE2_POLICY__WIN2000) com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__OPEN_ANDX: break; case SMB_ANDX_COM__NT_CREATE_ANDX: switch (com) { case SMB_COM_READ_ANDX: // Only for normal files com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__WRITE_ANDX: switch (com) { case SMB_COM_CLOSE: case SMB_COM_WRITE_ANDX: case SMB_COM_READ: case SMB_COM_READ_ANDX: com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__READ_ANDX: break; default: break; } break; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: switch (andx) { case SMB_ANDX_COM__SESSION_SETUP_ANDX: switch (com) { case SMB_COM_LOGOFF_ANDX: case SMB_COM_TREE_CONNECT: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_TREE_DISCONNECT: case SMB_COM_OPEN_ANDX: case SMB_COM_NT_CREATE_ANDX: case SMB_COM_CLOSE: case SMB_COM_READ_ANDX: com_func = smb_com_funcs[com]; break; case SMB_COM_WRITE: if ((policy == DCE2_POLICY__SAMBA_3_0_22) || (policy == DCE2_POLICY__SAMBA_3_0_20)) com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__LOGOFF_ANDX: switch (com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_TREE_DISCONNECT: com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__TREE_CONNECT_ANDX: switch (com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_LOGOFF_ANDX: case SMB_COM_TREE_DISCONNECT: case SMB_COM_OPEN_ANDX: case SMB_COM_NT_CREATE_ANDX: case SMB_COM_CLOSE: case SMB_COM_WRITE: case SMB_COM_READ_ANDX: com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__OPEN_ANDX: switch (com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_LOGOFF_ANDX: case SMB_COM_TREE_CONNECT: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_TREE_DISCONNECT: case SMB_COM_OPEN_ANDX: case SMB_COM_NT_CREATE_ANDX: case SMB_COM_CLOSE: case SMB_COM_WRITE: case SMB_COM_READ_ANDX: com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__NT_CREATE_ANDX: switch (com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_TREE_CONNECT: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_OPEN_ANDX: case SMB_COM_NT_CREATE_ANDX: case SMB_COM_WRITE: case SMB_COM_READ_ANDX: com_func = smb_com_funcs[com]; break; case SMB_COM_LOGOFF_ANDX: case SMB_COM_TREE_DISCONNECT: case SMB_COM_CLOSE: if ((policy == DCE2_POLICY__SAMBA) || (policy == DCE2_POLICY__SAMBA_3_0_37)) com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__WRITE_ANDX: switch (com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_LOGOFF_ANDX: case SMB_COM_TREE_CONNECT: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_OPEN_ANDX: case SMB_COM_NT_CREATE_ANDX: case SMB_COM_CLOSE: case SMB_COM_WRITE: case SMB_COM_READ_ANDX: case SMB_COM_WRITE_ANDX: com_func = smb_com_funcs[com]; break; default: break; } break; case SMB_ANDX_COM__READ_ANDX: switch (com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_WRITE: com_func = smb_com_funcs[com]; break; case SMB_COM_LOGOFF_ANDX: case SMB_COM_TREE_CONNECT: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_TREE_DISCONNECT: case SMB_COM_OPEN_ANDX: case SMB_COM_NT_CREATE_ANDX: case SMB_COM_CLOSE: case SMB_COM_READ_ANDX: if ((policy == DCE2_POLICY__SAMBA) || (policy == DCE2_POLICY__SAMBA_3_0_37)) com_func = smb_com_funcs[com]; break; default: break; } break; default: break; } break; default: break; } smb_chain_funcs[policy][andx][com] = com_func; } } } } /******************************************************************** * Function: DCE2_SmbInitRdata() * * Purpose: * Initializes the reassembled packet structure for an SMB * reassembled packet. Uses WriteAndX and ReadAndX. * TODO Use command that was used when reassembly occurred. * One issue with this is that multiple different write/read * commands can be used to write/read the full DCE/RPC * request/response. * * Arguments: * uint8_t * - pointer to the start of the NetBIOS header where * data initialization should start. * int dir - FLAG_FROM_CLIENT or FLAG_FROM_SERVER * * Returns: None * ********************************************************************/ void DCE2_SmbInitRdata(uint8_t *nb_ptr, int dir) { NbssHdr *nb_hdr = (NbssHdr *)nb_ptr; SmbNtHdr *smb_hdr = (SmbNtHdr *)((uint8_t *)nb_hdr + sizeof(NbssHdr)); nb_hdr->type = NBSS_SESSION_TYPE__MESSAGE; memcpy((void *)smb_hdr->smb_idf, (void *)"\xffSMB", sizeof(smb_hdr->smb_idf)); if (dir == FLAG_FROM_CLIENT) { SmbWriteAndXReq *writex = (SmbWriteAndXReq *)((uint8_t *)smb_hdr + sizeof(SmbNtHdr)); uint16_t offset = sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq); smb_hdr->smb_com = SMB_COM_WRITE_ANDX; smb_hdr->smb_flg = 0x00; writex->smb_wct = 12; writex->smb_com2 = SMB_COM_NO_ANDX_COMMAND; writex->smb_doff = SmbHtons(&offset); } else { SmbReadAndXResp *readx = (SmbReadAndXResp *)((uint8_t *)smb_hdr + sizeof(SmbNtHdr)); uint16_t offset = sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp); smb_hdr->smb_com = SMB_COM_READ_ANDX; smb_hdr->smb_flg = 0x80; readx->smb_wct = 12; readx->smb_com2 = SMB_COM_NO_ANDX_COMMAND; readx->smb_doff = SmbHtons(&offset); } } /******************************************************************** * Function: DCE2_SmbSetRdata() * * Purpose: * When a reassembled packet is needed this function is called to * fill in appropriate fields to make the reassembled packet look * correct from an SMB standpoint. * * Arguments: * DCE2_SmbSsnData * - the session data structure. * uint8_t * - pointer to the start of the NetBIOS header where * data initialization should start. * uint16_t - the length of the connection-oriented DCE/RPC data. * * Returns: None * ********************************************************************/ void DCE2_SmbSetRdata(DCE2_SmbSsnData *ssd, uint8_t *nb_ptr, uint16_t co_len) { NbssHdr *nb_hdr = (NbssHdr *)nb_ptr; SmbNtHdr *smb_hdr = (SmbNtHdr *)((uint8_t *)nb_hdr + sizeof(NbssHdr)); uint16_t uid = (ssd->cur_rtracker == NULL) ? 0 : ssd->cur_rtracker->uid; uint16_t tid = (ssd->cur_rtracker == NULL) ? 0 : ssd->cur_rtracker->tid; DCE2_SmbFileTracker *ftracker = (ssd->cur_rtracker == NULL) ? NULL : ssd->cur_rtracker->ftracker; smb_hdr->smb_uid = SmbHtons((const uint16_t *)&uid); smb_hdr->smb_tid = SmbHtons((const uint16_t *)&tid); if (DCE2_SsnFromClient(ssd->sd.wire_pkt)) { SmbWriteAndXReq *writex = (SmbWriteAndXReq *)((uint8_t *)smb_hdr + sizeof(SmbNtHdr)); uint32_t nb_len = sizeof(SmbNtHdr) + sizeof(SmbWriteAndXReq) + co_len; /* The data will get truncated anyway since we can only fit * 64K in the reassembly buffer */ if (nb_len > UINT16_MAX) nb_len = UINT16_MAX; nb_hdr->length = htons((uint16_t)nb_len); if ((ftracker != NULL) && (ftracker->fid_v1 > 0)) { uint16_t fid = (uint16_t)ftracker->fid_v1; writex->smb_fid = SmbHtons(&fid); } else { writex->smb_fid = 0; } writex->smb_countleft = SmbHtons(&co_len); writex->smb_dsize = SmbHtons(&co_len); writex->smb_bcc = SmbHtons(&co_len); } else { SmbReadAndXResp *readx = (SmbReadAndXResp *)((uint8_t *)smb_hdr + sizeof(SmbNtHdr)); uint32_t nb_len = sizeof(SmbNtHdr) + sizeof(SmbReadAndXResp) + co_len; /* The data will get truncated anyway since we can only fit * 64K in the reassembly buffer */ if (nb_len > UINT16_MAX) nb_len = UINT16_MAX; nb_hdr->length = htons((uint16_t)nb_len); readx->smb_remaining = SmbHtons(&co_len); readx->smb_dsize = SmbHtons(&co_len); readx->smb_bcc = SmbHtons(&co_len); } } /******************************************************************** * Function: DCE2_SmbSsnInit() * * Purpose: * Allocates and initializes a new session data structure. * * Arguments: None * * Returns: * DCE2_SmbSsnData * - a new initialized session data structure. * ********************************************************************/ DCE2_SmbSsnData * DCE2_SmbSsnInit(SFSnortPacket *p) { DCE2_SmbSsnData *ssd = (DCE2_SmbSsnData *)DCE2_Alloc(sizeof(DCE2_SmbSsnData), DCE2_MEM_TYPE__SMB_SSN); if (ssd == NULL) return NULL; ssd->dialect_index = DCE2_SENTINEL; ssd->max_outstanding_requests = 10; // Until Negotiate/SessionSetupAndX ssd->cli_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; ssd->srv_data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; ssd->pdu_state = DCE2_SMB_PDU_STATE__COMMAND; ssd->uid = DCE2_SENTINEL; ssd->tid = DCE2_SENTINEL; ssd->ftracker.fid_v1 = DCE2_SENTINEL; ssd->rtracker.mid = DCE2_SENTINEL; ssd->smbfound = false; ssd->max_file_depth = _dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false); ssd->smbretransmit = false; DCE2_ResetRopts(&ssd->sd.ropts); dce2_stats.smb_sessions++; return ssd; } /******************************************************************** * Function: DCE2_NbssHdrChecks() * * Purpose: * Does validation of the NetBIOS header. SMB will only run over * the Session Message type. On port 139, there is always an * initial Session Request / Session Positive/Negative response * followed by the normal SMB conversation, i.e. Negotiate, * SessionSetupAndX, etc. * Side effects are potential alerts for anomolous behavior. * * Arguments: * DCE2_SmbSsnData * - the session data structure. * const NbssHdr * - pointer to the NetBIOS Session Service * header structure. Size is already validated. * * Returns: * DCE2_Ret - DCE2_RET__SUCCESS if all goes well and processing * should continue. * DCE2_RET__IGNORE if it's not something we need to * look at. * DCE2_RET__ERROR if an invalid NetBIOS Session * Service type is found. * ********************************************************************/ static DCE2_Ret DCE2_NbssHdrChecks(DCE2_SmbSsnData *ssd, const NbssHdr *nb_hdr) { const SFSnortPacket *p = ssd->sd.wire_pkt; bool is_seg_buf = DCE2_SmbIsSegBuffer(ssd, (uint8_t *)nb_hdr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "NetBIOS Session Service type: ")); switch (NbssType(nb_hdr)) { case NBSS_SESSION_TYPE__MESSAGE: /* Only want to look at session messages */ DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Session Message\n")); if (!DCE2_SmbIsRawData(ssd)) { uint32_t nb_len = NbssLen(nb_hdr); if (nb_len == 0) return DCE2_RET__IGNORE; if (nb_len < sizeof(SmbNtHdr)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "NetBIOS SS len(%u) < SMB header len(%u).\n", sizeof(SmbNtHdr), sizeof(NbssHdr) + nb_len)); if (is_seg_buf) DCE2_SmbSegAlert(ssd, DCE2_EVENT__SMB_NB_LT_SMBHDR); else DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_SMBHDR, nb_len, sizeof(SmbNtHdr)); return DCE2_RET__IGNORE; } } return DCE2_RET__SUCCESS; case NBSS_SESSION_TYPE__REQUEST: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Session Request\n")); if (DCE2_SsnFromServer(p)) { if (is_seg_buf) DCE2_SmbSegAlert(ssd, DCE2_EVENT__SMB_BAD_NBSS_TYPE); else DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_NBSS_TYPE); } break; case NBSS_SESSION_TYPE__POS_RESPONSE: case NBSS_SESSION_TYPE__NEG_RESPONSE: case NBSS_SESSION_TYPE__RETARGET_RESPONSE: DCE2_DEBUG_CODE(DCE2_DEBUG__SMB, if (NbssType(nb_hdr) == NBSS_SESSION_TYPE__POS_RESPONSE) printf("Positive Session Response\n"); else if (NbssType(nb_hdr) == NBSS_SESSION_TYPE__NEG_RESPONSE) printf("Negative Session Response\n"); else printf("Session Retarget Response\n");); if (DCE2_SsnFromClient(p)) { if (is_seg_buf) DCE2_SmbSegAlert(ssd, DCE2_EVENT__SMB_BAD_NBSS_TYPE); else DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_NBSS_TYPE); } break; case NBSS_SESSION_TYPE__KEEP_ALIVE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Session Keep Alive\n")); break; default: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Invalid Session Service type: 0x%02X\n", NbssType(nb_hdr))); if (is_seg_buf) DCE2_SmbSegAlert(ssd, DCE2_EVENT__SMB_BAD_NBSS_TYPE); else DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_NBSS_TYPE); return DCE2_RET__ERROR; } return DCE2_RET__IGNORE; } /******************************************************************** * Function: DCE2_SmbInspect() * * Purpose: * Determines whether the SMB command is something the preprocessor * needs to inspect. * This function returns a DCE2_SmbRequestTracker which tracks command * requests / responses. * * Arguments: * DCE2_SmbSsnData * - the session data structure. * const SmbNtHdr * - pointer to the SMB header. * * Returns: * DCE2_SmbRequestTracker * - NULL if it's not something we want to or can * inspect. * Otherwise an initialized structure if request * and the found structure if response. * ********************************************************************/ static DCE2_SmbRequestTracker * DCE2_SmbInspect(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr) { DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd); DCE2_SmbRequestTracker *rtracker = NULL; int smb_com = SmbCom(smb_hdr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "SMB command: %s (0x%02X)\n", smb_com_strings[smb_com], smb_com)); if (smb_com_funcs[smb_com] == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Command isn't processed " "by preprocessor.\n")); return NULL; } // See if this is something we need to inspect if (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST) { switch (smb_com) { case SMB_COM_NEGOTIATE: if (ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__NEGOTIATED) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_MULTIPLE_NEGOTIATIONS); return NULL; } break; case SMB_COM_SESSION_SETUP_ANDX: break; case SMB_COM_TREE_CONNECT: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_RENAME: case SMB_COM_LOGOFF_ANDX: if (DCE2_SmbFindUid(ssd, SmbUid(smb_hdr)) != DCE2_RET__SUCCESS) return NULL; break; default: if (DCE2_SmbFindTid(ssd, SmbTid(smb_hdr)) != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Couldn't find Tid (%u)\n", SmbTid(smb_hdr))); return NULL; } if (DCE2_SmbIsTidIPC(ssd, SmbTid(smb_hdr))) { switch (smb_com) { case SMB_COM_OPEN: case SMB_COM_CREATE: case SMB_COM_CREATE_NEW: case SMB_COM_WRITE_AND_CLOSE: case SMB_COM_WRITE_AND_UNLOCK: case SMB_COM_READ: // Samba doesn't allow these commands under an IPC tree switch (policy) { case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Samba doesn't " "process this command under an IPC tree.\n")); return NULL; default: break; } break; case SMB_COM_READ_RAW: case SMB_COM_WRITE_RAW: // Samba and Windows Vista on don't allow these commands // under an IPC tree, whether or not the raw read/write // flag is set in the Negotiate capabilities. // Windows RSTs the connection and Samba FINs it. switch (policy) { case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Samba and " "Windows Vista on don't process this " "command under an IPC tree.\n")); return NULL; default: break; } break; case SMB_COM_LOCK_AND_READ: // The lock will fail so the read won't happen return NULL; default: break; } } else // Not IPC { switch (smb_com) { // These commands are only used for IPC case SMB_COM_TRANSACTION: case SMB_COM_TRANSACTION_SECONDARY: return NULL; case SMB_COM_READ_RAW: case SMB_COM_WRITE_RAW: // Windows Vista on don't seem to support these // commands, whether or not the raw read/write // flag is set in the Negotiate capabilities. // Windows RSTs the connection. switch (policy) { case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Windows Vista on don't process " "this command.\n")); return NULL; default: break; } break; default: break; } } break; } switch (smb_com) { case SMB_COM_TRANSACTION_SECONDARY: case SMB_COM_TRANSACTION2_SECONDARY: case SMB_COM_NT_TRANSACT_SECONDARY: rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr); break; case SMB_COM_TRANSACTION: case SMB_COM_TRANSACTION2: case SMB_COM_NT_TRANSACT: // If there is already and existing request tracker // and the transaction is not complete, server will // return an error. rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr); if (rtracker != NULL) break; // Fall through default: rtracker = DCE2_SmbNewRequestTracker(ssd, smb_hdr); break; } } else { rtracker = DCE2_SmbFindRequestTracker(ssd, smb_hdr); } DCE2_DEBUG_CODE(DCE2_DEBUG__SMB, if (rtracker == NULL) printf("Failed to get request tracker.\n");); return rtracker; } /******************************************************************** * Function: DCE2_SmbHdrChecks() * * Checks some relevant fields in the header to make sure they're * sane. * Side effects are potential alerts for anomolous behavior. * * Arguments: * DCE2_SmbSsnData * * Pointer to the session data structure. * SmbNtHdr * * Pointer to the header struct layed over the packet data. * * Returns: * DCE2_Ret * DCE2_RET__IGNORE if we should continue processing, but * ignore data because of the error. * DCE2_RET__SUCCESS if we should continue processing. * ********************************************************************/ static DCE2_Ret DCE2_SmbHdrChecks(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr) { const SFSnortPacket *p = ssd->sd.wire_pkt; bool is_seg_buf = DCE2_SmbIsSegBuffer(ssd, (uint8_t *)smb_hdr); if ((DCE2_SsnFromServer(p) && (SmbType(smb_hdr) == SMB_TYPE__REQUEST)) || (DCE2_SsnFromClient(p) && (SmbType(smb_hdr) == SMB_TYPE__RESPONSE))) { if (is_seg_buf) DCE2_SmbSegAlert(ssd, DCE2_EVENT__SMB_BAD_TYPE); else DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_TYPE); // Continue looking at traffic. Neither Windows nor Samba seem // to care, or even look at this flag } if ((SmbId(smb_hdr) != DCE2_SMB_ID) && (SmbId(smb_hdr) != DCE2_SMB2_ID)) { if (is_seg_buf) DCE2_SmbSegAlert(ssd, DCE2_EVENT__SMB_BAD_ID); else DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_ID); return DCE2_RET__IGNORE; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_IgnoreJunkData() * * Purpose: * An evasion technique can be to put a bunch of junk data before * the actual SMB request and it seems the MS implementation has * no problem with it and seems to just ignore the data. This * function attempts to move past all the junk to get to the * actual NetBIOS message request. * * Arguments: * const uint8_t * - pointer to the current position in the data * being inspected * uint16_t - the amount of data left to look at * uint32_t - the amount of data to ignore if there doesn't seem * to be any junk data. Just use the length as if the bad * NetBIOS header was good. * * Returns: * uint32_t - the amount of bytes to ignore as junk. * ********************************************************************/ static uint32_t DCE2_IgnoreJunkData(const uint8_t *data_ptr, uint16_t data_len, uint32_t assumed_nb_len) { const uint8_t *tmp_ptr = data_ptr; uint32_t ignore_bytes = 0; /* Try to find \xffSMB and go back 8 bytes to beginning * of what should be a Netbios header with type Session * Message (\x00) - do appropriate buffer checks to make * sure the index is in bounds. Ignore all intervening * bytes */ while ((tmp_ptr + sizeof(uint32_t)) <= (data_ptr + data_len)) { if ((SmbId((SmbNtHdr *)tmp_ptr) == DCE2_SMB_ID) || (SmbId((SmbNtHdr *)tmp_ptr) == DCE2_SMB2_ID)) { break; } tmp_ptr++; } if ((tmp_ptr + sizeof(uint32_t)) > (data_ptr + data_len)) { ignore_bytes = data_len; } else { if ((tmp_ptr - sizeof(NbssHdr)) > data_ptr) ignore_bytes = (tmp_ptr - data_ptr) - sizeof(NbssHdr); else /* Just ignore whatever the bad NB header had as a length */ ignore_bytes = assumed_nb_len; } return ignore_bytes; } /******************************************************************** * Function: DCE2_Smb1Process() * * Purpose: * This is the main entry point for SMB1 processing. * * Arguments: * DCE2_SmbSsnData * - the session data structure. * * Returns: None * ********************************************************************/ static inline void DCE2_Smb1Process(DCE2_SmbSsnData *ssd) { const SFSnortPacket *p = ssd->sd.wire_pkt; const uint8_t *data_ptr = p->payload; uint16_t data_len = p->payload_size; uint32_t *ignore_bytes = DCE2_SmbGetIgnorePtr(ssd); DCE2_Buffer **seg_buf = DCE2_SmbGetSegBuffer(ssd); DCE2_SmbDataState *data_state = DCE2_SmbGetDataState(ssd); #ifdef DUMP_BUFFER dumpBuffer(DCERPC_SMB1_DUMP,data_ptr,data_len); #endif DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing SMB packet.\n")); dce2_stats.smb_pkts++; /* Have to account for segmentation. Even though stream will give * us larger chunks, we might end up in the middle of something */ while (data_len > 0) { // The amount of data needed in a given state to continue processing uint32_t data_need; NbssHdr *nb_hdr = NULL; SmbNtHdr *smb_hdr = NULL; uint32_t nb_len; const uint8_t *nb_ptr; DCE2_SmbRequestTracker *rtracker = NULL; DCE2_Ret status; // We are ignoring an entire PDU or junk data so state should be NETBIOS_HEADER // Note that it could be TCP segmented so ignore_bytes could be greater than // the amount of data we have if (*ignore_bytes) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Ignoring %u bytes\n", *ignore_bytes)); if (data_len <= *ignore_bytes) { *ignore_bytes -= data_len; return; } else { /* ignore bytes is less than UINT16_MAX */ DCE2_MOVE(data_ptr, data_len, (uint16_t)*ignore_bytes); *ignore_bytes = 0; } } switch (*data_state) { // This state is to verify it's a NetBIOS Session Message packet // and to get the length of the SMB PDU. Also does the SMB junk // data check. If it's not a Session Message the data isn't // processed since it won't be carrying SMB. case DCE2_SMB_DATA_STATE__NETBIOS_HEADER: data_need = sizeof(NbssHdr) - DCE2_BufferLength(*seg_buf); // See if there is enough data to process the NetBIOS header if (data_len < data_need) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Data len(%u) < NetBIOS SS header(%u). " "Queueing data.\n", data_len, data_need)); if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_len, sizeof(NbssHdr)) != DCE2_RET__SUCCESS) { DCE2_BufferEmpty(*seg_buf); } return; } // Set the NetBIOS header structure if (DCE2_BufferIsEmpty(*seg_buf)) { nb_hdr = (NbssHdr *)data_ptr; } else { // If data already buffered add the remainder for the // size of the NetBIOS header if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_need, sizeof(NbssHdr)) != DCE2_RET__SUCCESS) { DCE2_BufferEmpty(*seg_buf); return; } nb_hdr = (NbssHdr *)DCE2_BufferData(*seg_buf); } nb_len = NbssLen(nb_hdr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "NetBIOS PDU length: %u\n", nb_len)); status = DCE2_NbssHdrChecks(ssd, nb_hdr); if (status != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not a NetBIOS Session Message.\n")); if (status == DCE2_RET__IGNORE) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Valid NetBIOS header " "type so ignoring NetBIOS length bytes.\n")); *ignore_bytes = data_need + nb_len; } else // nb_ret == DCE2_RET__ERROR, i.e. invalid NetBIOS type { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not a valid NetBIOS " "header type so trying to find \\xffSMB to " "determine how many bytes to ignore.\n")); *ignore_bytes = DCE2_IgnoreJunkData(data_ptr, data_len, data_need + nb_len); } DCE2_BufferEmpty(*seg_buf); dce2_stats.smb_ignored_bytes += *ignore_bytes; continue; } if (!DCE2_BufferIsEmpty(*seg_buf)) DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need); switch (ssd->pdu_state) { case DCE2_SMB_PDU_STATE__COMMAND: *data_state = DCE2_SMB_DATA_STATE__SMB_HEADER; break; case DCE2_SMB_PDU_STATE__RAW_DATA: *data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU; // Continue here because of fall through below continue; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid SMB PDU " "state: %d\n", __FILE__, __LINE__, ssd->pdu_state); return; } // Fall through for DCE2_SMB_DATA_STATE__SMB_HEADER // This is the normal progression without segmentation. // This state is to do validation checks on the SMB header and // more importantly verify it's data that needs to be inspected. // If the TID in the SMB header is not referring to the IPC share // there won't be any DCE/RPC traffic associated with it. case DCE2_SMB_DATA_STATE__SMB_HEADER: data_need = (sizeof(NbssHdr) + sizeof(SmbNtHdr)) - DCE2_BufferLength(*seg_buf); // See if there is enough data to process the SMB header if (data_len < data_need) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Data len (%u) < " "NetBIOS SS header + SMB header (%u). Queueing data.\n", data_len, data_need)); if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_len, sizeof(NbssHdr) + sizeof(SmbNtHdr)) != DCE2_RET__SUCCESS) { DCE2_BufferEmpty(*seg_buf); *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; } return; } // Set the SMB header structure if (DCE2_BufferIsEmpty(*seg_buf)) { smb_hdr = (SmbNtHdr *)(data_ptr + sizeof(NbssHdr)); } else { if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_need, sizeof(NbssHdr) + sizeof(SmbNtHdr)) != DCE2_RET__SUCCESS) { DCE2_BufferEmpty(*seg_buf); *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; return; } smb_hdr = (SmbNtHdr *)(DCE2_BufferData(*seg_buf) + sizeof(NbssHdr)); } if (SmbId(smb_hdr) == DCE2_SMB2_ID) { ssd->sd.flags |= DCE2_SSN_FLAG__SMB2; if (!DCE2_GcIsLegacyMode()) { DCE2_Smb2InitFileTracker(&(ssd->ftracker), false, 0); DCE2_Smb2Process(ssd); } return; } // See if this is something we need to inspect rtracker = DCE2_SmbInspect(ssd, smb_hdr); if (rtracker == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not inspecting SMB packet.\n")); if (DCE2_BufferIsEmpty(*seg_buf)) { *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr *)data_ptr); } else { *ignore_bytes = (NbssLen((NbssHdr *)DCE2_BufferData(*seg_buf)) - sizeof(SmbNtHdr)) + data_need; DCE2_BufferEmpty(*seg_buf); } *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; dce2_stats.smb_ignored_bytes += *ignore_bytes; continue; } // Check the SMB header for anomolies if (DCE2_SmbHdrChecks(ssd, smb_hdr) != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Bad SMB header.\n")); if (DCE2_BufferIsEmpty(*seg_buf)) { *ignore_bytes = sizeof(NbssHdr) + NbssLen((NbssHdr *)data_ptr); } else { *ignore_bytes = (NbssLen((NbssHdr *)DCE2_BufferData(*seg_buf)) - sizeof(SmbNtHdr)) + data_need; DCE2_BufferEmpty(*seg_buf); } *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; dce2_stats.smb_ignored_bytes += *ignore_bytes; continue; } if (!DCE2_BufferIsEmpty(*seg_buf)) DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need); *data_state = DCE2_SMB_DATA_STATE__NETBIOS_PDU; // Fall through // This state ensures that we have the entire PDU before continuing // to process. case DCE2_SMB_DATA_STATE__NETBIOS_PDU: if (DCE2_BufferIsEmpty(*seg_buf)) { nb_len = NbssLen((NbssHdr *)data_ptr); data_need = sizeof(NbssHdr) + nb_len; } else { nb_len = NbssLen((NbssHdr *)DCE2_BufferData(*seg_buf)); data_need = (sizeof(NbssHdr) + nb_len) - DCE2_BufferLength(*seg_buf); } /* It's something we want to inspect so make sure we have the full NBSS packet */ if (data_len < data_need) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Data len(%u) < " "NetBIOS SS header + NetBIOS len(%u). " "Queueing data.\n", data_len, sizeof(NbssHdr) + nb_len)); if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_len, sizeof(NbssHdr) + nb_len) != DCE2_RET__SUCCESS) { DCE2_BufferEmpty(*seg_buf); *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; } return; } // data_len >= data_need which means data_need <= UINT16_MAX // So casts below of data_need to uint16_t are okay. *data_state = DCE2_SMB_DATA_STATE__NETBIOS_HEADER; if (DCE2_BufferIsEmpty(*seg_buf)) { nb_ptr = data_ptr; nb_len = data_need; DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need); } else { SFSnortPacket *rpkt; if (DCE2_SmbHandleSegmentation(seg_buf, data_ptr, data_need, sizeof(NbssHdr) + nb_len) != DCE2_RET__SUCCESS) { DCE2_BufferEmpty(*seg_buf); DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need); continue; } DCE2_MOVE(data_ptr, data_len, (uint16_t)data_need); nb_ptr = DCE2_BufferData(*seg_buf); nb_len = DCE2_BufferLength(*seg_buf); // Get reassembled packet rpkt = DCE2_SmbGetRpkt(ssd, &nb_ptr, &nb_len, DCE2_RPKT_TYPE__SMB_SEG); if (rpkt == NULL) { DCE2_BufferEmpty(*seg_buf); continue; } nb_ptr = DCE2_BufferData(*seg_buf); nb_len = DCE2_BufferLength(*seg_buf); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Segmentation buffer: len: %u, size: %u\n", DCE2_BufferLength(*seg_buf), DCE2_BufferSize(*seg_buf));); if (DCE2_SsnFromClient(ssd->sd.wire_pkt)) dce2_stats.smb_cli_seg_reassembled++; else dce2_stats.smb_srv_seg_reassembled++; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "TCP reassembled SMB PDU\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); } switch (ssd->pdu_state) { case DCE2_SMB_PDU_STATE__COMMAND: smb_hdr = (SmbNtHdr *)(nb_ptr + sizeof(NbssHdr)); DCE2_MOVE(nb_ptr, nb_len, (sizeof(NbssHdr) + sizeof(SmbNtHdr))); ssd->cur_rtracker = (rtracker != NULL) ? rtracker : DCE2_SmbFindRequestTracker(ssd, smb_hdr); if (ssd->cur_rtracker != NULL) DCE2_SmbProcessCommand(ssd, smb_hdr, nb_ptr, nb_len); break; case DCE2_SMB_PDU_STATE__RAW_DATA: DCE2_MOVE(nb_ptr, nb_len, sizeof(NbssHdr)); if (ssd->cur_rtracker != NULL) DCE2_SmbProcessRawData(ssd, nb_ptr, nb_len); // Only one raw read or write ssd->pdu_state = DCE2_SMB_PDU_STATE__COMMAND; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid SMB PDU " "state: %d\n", __FILE__, __LINE__, ssd->pdu_state); return; } if (!DCE2_BufferIsEmpty(*seg_buf)) { DCE2_SmbReturnRpkt(); DCE2_BufferDestroy(*seg_buf); *seg_buf = NULL; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid SMB Data " "state: %d\n", __FILE__, __LINE__, *data_state); return; } } } /******************************************************************** * Function: DCE2_SmbProcess() * * Purpose: * This is the main entry point for SMB processing. * * Arguments: * DCE2_SmbSsnData * - the session data structure. * * Returns: None * ********************************************************************/ void DCE2_SmbProcess(DCE2_SmbSsnData *ssd) { DCE2_SmbVersion smb_version; const SFSnortPacket *p = ssd->sd.wire_pkt; if (DCE2_GcIsLegacyMode()) { DCE2_Smb1Process(ssd); return; } smb_version = DCE2_Smb2Version(p); if ((ssd->smbfound == false) && (smb_version != DCE2_SMB_VERISON_NULL)) { _dpd.sessionAPI->disable_preproc_for_session( p->stream_session, PP_HTTPINSPECT); DCE2_EnableDetect(); ssd->smbfound=true; } if (smb_version == DCE2_SMB_VERISON_1) { if ((ssd->sd.flags & DCE2_SSN_FLAG__SMB2)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "SMB1 packet detected!\n")); ssd->sd.flags &= ~DCE2_SSN_FLAG__SMB2; DCE2_SmbCleanFileTracker(&(ssd->ftracker)); ssd->ftracker.is_smb2 = false; } } else if (smb_version == DCE2_SMB_VERISON_2) { if (!(ssd->sd.flags & DCE2_SSN_FLAG__SMB2)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "SMB2 packet detected!\n")); DCE2_SmbCleanFileTracker(&(ssd->ftracker)); DCE2_Smb2InitFileTracker(&(ssd->ftracker), 0, 0); ssd->sd.flags |= DCE2_SSN_FLAG__SMB2; } } ssd->max_file_depth = _dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false); if (ssd->sd.flags & DCE2_SSN_FLAG__SMB2) DCE2_Smb2Process(ssd); else DCE2_Smb1Process(ssd); } /******************************************************************** * Function: DCE2_SmbHandleSegmentation() * * Wrapper around DCE2_HandleSegmentation() to allocate a new * buffer object if necessary. * * Arguments: * DCE2_SmbBuffer ** * Pointer to pointer of buffer to add data to. If NULL * a new buffer will be allocated. * uint8_t * * Pointer to the current data cursor in packet. * uint32_t * Length of data to add to buffer. * uint32_t * The minimum allocation size so that small allocations * aren't consistently done. * * Returns: * DCE2_Ret * DCE2_RET__ERROR if an error occured. Nothing can * be trusted. * DCE2_RET__SUCCESS if data was successfully added. * ********************************************************************/ static inline DCE2_Ret DCE2_SmbHandleSegmentation(DCE2_Buffer **buf, const uint8_t *data_ptr, uint32_t add_len, uint32_t alloc_size) { DCE2_Ret status; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_seg); if (buf == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_seg); return DCE2_RET__ERROR; } if (*buf == NULL) { /* No initial size or min alloc size */ *buf = DCE2_BufferNew(alloc_size, alloc_size, DCE2_MEM_TYPE__SMB_SEG); if (*buf == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_seg); return DCE2_RET__ERROR; } } status = DCE2_BufferAddData(*buf, data_ptr, add_len, DCE2_BufferLength(*buf), DCE2_BUFFER_MIN_ADD_FLAG__IGNORE); DCE2_DEBUG_CODE(DCE2_DEBUG__SMB, if (status != DCE2_RET__SUCCESS) printf("Failed to add data to SMB segmentation buffer.\n");); PREPROC_PROFILE_END(dce2_pstat_smb_seg); return status; } /******************************************************************** * Function: DCE2_SmbGetSegBuffer() * * Returns the appropriate segmentation buffer. * * Arguments: * DCE2_SmbSsnData * * Pointer to SMB session data. * * Returns: * DCE2_SmbSeg * * Pointer to client or server segmenation buffer. * ********************************************************************/ static inline DCE2_Buffer ** DCE2_SmbGetSegBuffer(DCE2_SmbSsnData *ssd) { if (DCE2_SsnFromServer(ssd->sd.wire_pkt)) return &ssd->srv_seg; return &ssd->cli_seg; } /******************************************************************** * Function: DCE2_SmbGetIgnorePtr() * * Returns a pointer to the bytes we are ignoring on client or * server side. Bytes are ignored if they are associated with * data we are not interested in. * * Arguments: * DCE2_SmbSsnData * - Pointer to SMB session data. * * Returns: * uint32_t * * Pointer to the client or server ignore bytes. * ********************************************************************/ static inline uint32_t * DCE2_SmbGetIgnorePtr(DCE2_SmbSsnData *ssd) { if (DCE2_SsnFromServer(ssd->sd.wire_pkt)) return &ssd->srv_ignore_bytes; return &ssd->cli_ignore_bytes; } /******************************************************************** * Function: DCE2_SmbGetDataState() * * Returns a pointer to the data state of client or server * * Arguments: * DCE2_SmbSsnData * - Pointer to SMB session data. * * Returns: * DCE2_SmbDataState * * Pointer to the client or server data state. * ********************************************************************/ static inline DCE2_SmbDataState * DCE2_SmbGetDataState(DCE2_SmbSsnData *ssd) { if (DCE2_SsnFromServer(ssd->sd.wire_pkt)) return &ssd->srv_data_state; return &ssd->cli_data_state; } /******************************************************************** * Function: DCE2_SmbIsSegBuffer() * * Purpose: * Determines whether the pointer passed in lies within one of the * segmentation buffers or not. * * Arguments: * DCE2_SmbSsnData * * Pointer to SMB session data. * * Returns: * bool - True is the pointer lies within one of the segmentation * buffers. * False if it doesn't. * ********************************************************************/ static inline bool DCE2_SmbIsSegBuffer(DCE2_SmbSsnData *ssd, const uint8_t *ptr) { DCE2_Buffer *seg_buf; if (DCE2_SsnFromServer(ssd->sd.wire_pkt)) seg_buf = ssd->srv_seg; else seg_buf = ssd->cli_seg; if (DCE2_BufferIsEmpty(seg_buf)) return false; /* See if we're looking at a segmentation buffer */ if ((ptr < DCE2_BufferData(seg_buf)) || (ptr > (DCE2_BufferData(seg_buf) + DCE2_BufferLength(seg_buf)))) { return false; } return true; } /******************************************************************** * Function: DCE2_SmbSegAlert() * * Purpose: * To create a reassembled packet using the data in one of the * segmentation buffers in order to generate an alert with the * correct, or more complete data. * * Arguments: * DCE2_SmbSsnData * - Pointer to SMB session data. * DCE2_Event - the event code to generate and event for. * * Returns: None * ********************************************************************/ static inline void DCE2_SmbSegAlert(DCE2_SmbSsnData *ssd, DCE2_Event event) { SFSnortPacket *rpkt; DCE2_Buffer *buf; uint32_t nb_len = 0; const uint8_t *data_ptr; uint32_t data_len; if (DCE2_SsnFromClient(ssd->sd.wire_pkt)) buf = ssd->cli_seg; else buf = ssd->srv_seg; /* This should be called from the desegmentation code. */ if (DCE2_BufferIsEmpty(buf)) return; data_ptr = DCE2_BufferData(buf); data_len = DCE2_BufferLength(buf); rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_SEG); if (rpkt == NULL) return; if (DCE2_BufferLength(buf) >= sizeof(NbssHdr)) nb_len = NbssLen((NbssHdr *)DCE2_BufferData(buf)); switch (event) { case DCE2_EVENT__SMB_BAD_NBSS_TYPE: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_NBSS_TYPE); break; case DCE2_EVENT__SMB_BAD_TYPE: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_TYPE); break; case DCE2_EVENT__SMB_BAD_ID: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_ID); break; case DCE2_EVENT__SMB_NB_LT_SMBHDR: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_SMBHDR, nb_len, sizeof(SmbNtHdr)); break; default: break; } DCE2_SmbReturnRpkt(); } /******************************************************************** * Function: DCE2_SmbIsRawData() * * Purpose: * To determine if the current state is such that a raw read or * write is expected. * * Arguments: * DCE2_SmbSsnData * - Pointer to SMB session data. * * Returns: * bool - True if expecting raw data. * False if not. * ********************************************************************/ static inline bool DCE2_SmbIsRawData(DCE2_SmbSsnData *ssd) { return (ssd->pdu_state == DCE2_SMB_PDU_STATE__RAW_DATA); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbProcessRawData(DCE2_SmbSsnData *ssd, const uint8_t *nb_ptr, uint32_t nb_len) { DCE2_SmbFileTracker *ftracker = ssd->cur_rtracker->ftracker; bool remove_rtracker = false; if (ftracker == NULL) { DCE2_SmbRemoveRequestTracker(ssd, ssd->cur_rtracker); ssd->cur_rtracker = NULL; return; } if (DCE2_SsnFromClient(ssd->sd.wire_pkt)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Raw data: Write Raw\n")); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Request Fid: 0x%04X\n", ftracker->fid_v1)); dce2_stats.smb_com_stats[SMB_TYPE__REQUEST][SMB_COM_WRITE_RAW]++; if (nb_len > ssd->cur_rtracker->writeraw_remaining) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_TDCNT_LT_DSIZE, ssd->cur_rtracker->writeraw_remaining, nb_len); // If this happens, Windows never responds regardless of // WriteThrough flag, so get rid of request tracker remove_rtracker = true; } else if (!ssd->cur_rtracker->writeraw_writethrough) { // If WriteThrough flag was not set on initial request, a // SMB_COM_WRITE_COMPLETE will not be sent so need to get // rid of request tracker. remove_rtracker = true; } else { ssd->cur_rtracker->writeraw_writethrough = false; ssd->cur_rtracker->writeraw_remaining = 0; } } else { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Raw data: Read Raw\n")); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Response Fid: 0x%04X\n", ftracker->fid_v1)); dce2_stats.smb_com_stats[SMB_TYPE__RESPONSE][SMB_COM_READ_RAW]++; remove_rtracker = true; } // Only one raw read/write allowed ssd->pdu_state = DCE2_SMB_PDU_STATE__COMMAND; DCE2_SmbSetFileName(ftracker->file_name, ftracker->file_name_len); if (ftracker->is_ipc) { // Maximum possible fragment length is 16 bit if (nb_len > UINT16_MAX) nb_len = UINT16_MAX; DCE2_CoProcess(&ssd->sd, ftracker->fp_co_tracker, nb_ptr, (uint16_t)nb_len); } else { bool upload = DCE2_SsnFromClient(ssd->sd.wire_pkt) ? true : false; DCE2_SmbProcessFileData(ssd, ftracker, nb_ptr, nb_len, upload); } if (remove_rtracker) { DCE2_SmbRemoveRequestTracker(ssd, ssd->cur_rtracker); ssd->cur_rtracker = NULL; } } /******************************************************************** * Function: DCE2_SmbCheckCommand() * * Purpose: * Checks basic validity of an SMB command. * * Arguments: * DCE2_SmbSsnData * - pointer to session data structure * SmbNtHdr * - pointer to the SMB header structure * int - the SMB command code, i.e. SMB_COM_* * uint8_t * - current pointer to data, i.e. the command * uint32_t - the remaining length * * Returns: * DCE2_SmbComInfo * * Populated structure for command processing * ********************************************************************/ static DCE2_SmbComInfo * DCE2_SmbCheckCommand(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const uint8_t smb_com, const uint8_t *nb_ptr, uint32_t nb_len) { SmbAndXCom andx_com = smb_chain_map[smb_com]; const SmbCommon *sc = (SmbCommon *)nb_ptr; int chk_com_size; uint16_t smb_bcc; static DCE2_SmbComInfo com_info; com_info.smb_type = DCE2_SmbType(ssd); com_info.cmd_error = DCE2_SMB_COM_ERROR__COMMAND_OK; com_info.word_count = 0; com_info.smb_com = smb_com; com_info.cmd_size = 0; com_info.byte_count = 0; // Check for server error response if (com_info.smb_type == SMB_TYPE__RESPONSE) { const SmbEmptyCom *ec = (SmbEmptyCom *)nb_ptr; // Verify there is enough data to do checks if (nb_len < sizeof(SmbEmptyCom)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_COM, nb_len, sizeof(SmbEmptyCom)); com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH; return &com_info; } // If word and byte counts are zero and there is an error // the server didn't accept client request if ((SmbEmptyComWct(ec) == 0) && (SmbEmptyComBcc(ec) == 0) && SmbError(smb_hdr)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Response error: 0x%08X\n", SmbNtStatus(smb_hdr))); // If broken pipe, clean up data associated with open named pipe if (SmbBrokenPipe(smb_hdr)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, " Broken or disconnected pipe.\n")); DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker); } com_info.cmd_error |= DCE2_SMB_COM_ERROR__STATUS_ERROR; return &com_info; } } // Set the header size to the minimum size the command can be // without the byte count to make sure there is enough data to // get the word count. if (andx_com == SMB_ANDX_COM__NONE) chk_com_size = sizeof(SmbCommon); else chk_com_size = sizeof(SmbAndXCommon); // Verify there is enough data to do checks if (nb_len < (uint32_t)chk_com_size) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_COM, nb_len, chk_com_size); com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH; return &com_info; } com_info.word_count = SmbWct(sc); // Make sure the word count is a valid one for the command. If not // testing shows an error will be returned. And command structures // won't lie on data correctly and out of bounds data accesses are possible. if (!DCE2_SmbIsValidWordCount(smb_com, (uint8_t)com_info.smb_type, com_info.word_count)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_WCT, com_info.word_count); com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_WORD_COUNT; return &com_info; } // This gets the size of the SMB command from word count through byte count // using the advertised value in the word count field. com_info.cmd_size = (uint16_t)SMB_COM_SIZE(com_info.word_count); if (nb_len < com_info.cmd_size) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_COM, nb_len, com_info.cmd_size); com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH; return &com_info; } smb_bcc = SmbBcc(nb_ptr, com_info.cmd_size); // SMB_COM_NT_CREATE_ANDX is a special case. Who know what's going // on with the word count (see MS-CIFS and MS-SMB). A 42 word count // command seems to actually have 50 words, so who knows where the // byte count is. Just set to zero since it's not needed. if ((smb_com == SMB_COM_NT_CREATE_ANDX) && (com_info.smb_type == SMB_TYPE__RESPONSE)) smb_bcc = 0; // If byte count is deemed invalid, alert but continue processing switch (smb_com) { // Interim responses case SMB_COM_TRANSACTION: case SMB_COM_TRANSACTION2: case SMB_COM_NT_TRANSACT: // If word count is 0, byte count must be 0 if ((com_info.word_count == 0) && (com_info.smb_type == SMB_TYPE__RESPONSE)) { if (smb_bcc != 0) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_BCC, smb_bcc); com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT; } break; } // Fall through default: if (!DCE2_SmbIsValidByteCount(smb_com, (uint8_t)com_info.smb_type, smb_bcc)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_BCC, smb_bcc); com_info.cmd_error |= DCE2_SMB_COM_ERROR__INVALID_BYTE_COUNT; } break; } // Move just past byte count field which is the end of the command DCE2_MOVE(nb_ptr, nb_len, com_info.cmd_size); // Validate that there is enough data to be able to process the command if (nb_len < DCE2_SmbGetMinByteCount(smb_com, (uint8_t)com_info.smb_type)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_BCC, nb_len, DCE2_SmbGetMinByteCount(smb_com, (uint8_t)com_info.smb_type)); com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH; } // The byte count seems to be ignored by Windows and current Samba (3.5.4) // as long as it is less than the amount of data left. If more, an error // is returned. // !!!WARNING!!! the byte count should probably never be used. if (smb_bcc > nb_len) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_BCC, nb_len, smb_bcc); // Large byte count doesn't seem to matter for early Samba switch (DCE2_SsnGetPolicy(&ssd->sd)) { case DCE2_POLICY__SAMBA_3_0_20: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_37: break; default: com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH; break; } } else if ((smb_bcc == 0) && (SmbCom(smb_hdr) == SMB_COM_TRANSACTION) && (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST) && (DCE2_SsnGetPolicy(&ssd->sd) == DCE2_POLICY__SAMBA)) { // Current Samba errors on a zero byte count Transaction because it // uses it to get the Name string and if zero Name will be NULL and // it won't process it. com_info.cmd_error |= DCE2_SMB_COM_ERROR__BAD_LENGTH; } com_info.byte_count = smb_bcc; return &com_info; } /******************************************************************** * Function: DCE2_SmbProcessCommand() * * Purpose: * This is the main function for handling SMB commands and command * chaining. * It does an initial check of the command to determine validity * and gets basic information about the command. Then it calls the * specific command function (setup in DCE2_SmbInitGlobals). * If there is command chaining, it will do the chaining foo to * get to the next command. * * Arguments: * DCE2_SmbSsnData * - pointer to session data structure * SmbNtHdr * - pointer to the SMB header structure * uint8_t * - current pointer to data, i.e. the command * uint32_t - the remaining length * * Returns: None * ********************************************************************/ static void DCE2_SmbProcessCommand(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const uint8_t *nb_ptr, uint32_t nb_len) { DCE2_Ret status = DCE2_RET__ERROR; DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd); uint8_t smb_com = SmbCom(smb_hdr); int smb_type = DCE2_SmbType(ssd); int num_chained = 0; bool sess_chain = false; bool tree_chain = false; bool open_chain = false; dce2_stats.smb_com_stats[smb_type][smb_com]++; while (nb_len > 0) { SmbAndXCom andx_com = smb_chain_map[smb_com]; const SmbAndXCommon *andx_ptr = (SmbAndXCommon *)nb_ptr; uint8_t smb_com2; const uint8_t *off2_ptr; DCE2_SmbComInfo *com_info; #ifdef ACTIVE_RESPONSE if (ssd->block_pdus && (smb_type == SMB_TYPE__REQUEST)) { _dpd.inlineDropPacket((void *)ssd->sd.wire_pkt); status = DCE2_RET__IGNORE; if (*_dpd.pkt_tracer_enabled) _dpd.addPktTrace(VERDICT_REASON_SMB, snprintf(_dpd.trace, _dpd.traceMax, "SMB: gid %u, server message block file drop\n", GENERATOR_DCE2)); else _dpd.addPktTrace(VERDICT_REASON_SMB, 0); break; } #endif // Break out if command not supported if (smb_com_funcs[smb_com] == NULL) break; if (smb_deprecated_coms[smb_com]) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DEPR_COMMAND_USED, smb_com_strings[smb_com]); } if (smb_unusual_coms[smb_com]) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_UNUSUAL_COMMAND_USED, smb_com_strings[smb_com]); } com_info = DCE2_SmbCheckCommand(ssd, smb_hdr, smb_com, nb_ptr, nb_len); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing command: %s (0x%02X)\n", smb_com_strings[smb_com], smb_com)); // Note that even if the command shouldn't be processed, some of // the command functions need to know and do cleanup or some other // processing. status = smb_com_funcs[smb_com](ssd, smb_hdr, (const DCE2_SmbComInfo *)com_info, nb_ptr, nb_len); if (status != DCE2_RET__SUCCESS) break; // This command is not chainable if (andx_com == SMB_ANDX_COM__NONE) break; /********************************************************** * AndX Chaining **********************************************************/ smb_com2 = SmbAndXCom2(andx_ptr); if (smb_com2 == SMB_COM_NO_ANDX_COMMAND) break; dce2_stats.smb_chained_stats[smb_type][andx_com][smb_com2]++; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Chained SMB command: %s\n", smb_com_strings[smb_com2])); num_chained++; if (DCE2_ScSmbMaxChain(ssd->sd.sconfig) && (num_chained >= DCE2_ScSmbMaxChain(ssd->sd.sconfig))) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EXCESSIVE_CHAINING, DCE2_ScSmbMaxChain(ssd->sd.sconfig)); } // Multiple SessionSetupAndX, TreeConnectAndX, OpenAndX and NtCreateAndX // are only allowed by Samba. if (smb_com == SMB_COM_SESSION_SETUP_ANDX) sess_chain = true; // Check for multiple chained SessionSetupAndX if ((smb_com2 == SMB_COM_SESSION_SETUP_ANDX) && sess_chain) { // There is only one place to return a uid. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_MULT_CHAIN_SS); // XXX Should we continue processing? break; } // Check for chained SessionSetupAndX => .? => LogoffAndX if ((smb_com2 == SMB_COM_LOGOFF_ANDX) && sess_chain) { // This essentially deletes the uid created by the login // and doesn't make any sense. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_CHAIN_SS_LOGOFF); } if (smb_com == SMB_COM_TREE_CONNECT_ANDX) tree_chain = true; // Check for multiple chained TreeConnects if (((smb_com2 == SMB_COM_TREE_CONNECT_ANDX) || (smb_com2 == SMB_COM_TREE_CONNECT)) && tree_chain) { // There is only one place to return a tid. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_MULT_CHAIN_TC); // XXX Should we continue processing? break; } // Check for chained TreeConnectAndX => .? => TreeDisconnect if ((smb_com2 == SMB_COM_TREE_DISCONNECT) && tree_chain) { // This essentially deletes the tid created by the tree connect // and doesn't make any sense. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_CHAIN_TC_TDIS); } if ((smb_com == SMB_COM_OPEN_ANDX) || (smb_com == SMB_COM_NT_CREATE_ANDX)) open_chain = true; // Check for chained OpenAndX/NtCreateAndX => .? => Close if ((smb_com2 == SMB_COM_CLOSE) && open_chain) { // This essentially deletes the fid created by the open command // and doesn't make any sense. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_CHAIN_OPEN_CLOSE); } // Check that policy allows for such chaining if (smb_chain_funcs[policy][andx_com][smb_com2] == NULL) break; DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); // XXX Need to test out of order chaining off2_ptr = (uint8_t *)smb_hdr + SmbAndXOff2(andx_ptr); if (DCE2_SmbCheckAndXOffset(ssd, off2_ptr, nb_ptr, nb_len) != DCE2_RET__SUCCESS) break; DCE2_MOVE(nb_ptr, nb_len, (off2_ptr - nb_ptr)); // XXX Need to test more. switch (smb_com) { case SMB_COM_SESSION_SETUP_ANDX: case SMB_COM_TREE_CONNECT_ANDX: case SMB_COM_OPEN_ANDX: case SMB_COM_NT_CREATE_ANDX: switch (smb_com2) { case SMB_COM_WRITE: case SMB_COM_WRITE_ANDX: case SMB_COM_TRANSACTION: case SMB_COM_READ_ANDX: if (DCE2_SsnFromClient(ssd->sd.wire_pkt) && open_chain) { DCE2_SmbQueueTmpFileTracker(ssd, ssd->cur_rtracker, SmbUid(smb_hdr), SmbTid(smb_hdr)); } break; default: break; } break; default: break; } smb_com = smb_com2; } if (smb_type == SMB_TYPE__RESPONSE) { switch (smb_com) { case SMB_COM_TRANSACTION: case SMB_COM_TRANSACTION2: case SMB_COM_NT_TRANSACT: case SMB_COM_TRANSACTION_SECONDARY: case SMB_COM_TRANSACTION2_SECONDARY: case SMB_COM_NT_TRANSACT_SECONDARY: // This case means there was an error with the initial response // so the tracker isn't yet officially in response mode if (ssd->cur_rtracker->ttracker.smb_type == SMB_TYPE__REQUEST) { // Samba throws out entire transaction and Windows just this request if (DCE2_SsnIsServerSambaPolicy(&ssd->sd) && (status != DCE2_RET__SUCCESS)) break; if (!DCE2_SmbIsTransactionComplete(&ssd->cur_rtracker->ttracker)) return; } else { if ((status == DCE2_RET__SUCCESS) && !DCE2_SmbIsTransactionComplete(&ssd->cur_rtracker->ttracker)) return; } break; case SMB_COM_WRITE_RAW: if ((status == DCE2_RET__SUCCESS) && (ssd->cur_rtracker->writeraw_remaining != 0)) return; break; /*This is to handle packet that got verdict as pending & will be put in retry queue */ case SMB_COM_CLOSE: if (status == DCE2_RET__NOT_INSPECTED) return; default: break; } } else if (status != DCE2_RET__IGNORE) { switch (smb_com) { case SMB_COM_TRANSACTION: case SMB_COM_TRANSACTION_SECONDARY: if (DCE2_SsnIsWindowsPolicy(&ssd->sd)) { if (!ssd->cur_rtracker->ttracker.one_way || !DCE2_SmbIsTransactionComplete(&ssd->cur_rtracker->ttracker)) return; // Remove the request tracker if transaction is one-way and // all data and parameters have been sent break; } default: // Anything else, keep the request tracker return; } } DCE2_SmbRemoveRequestTracker(ssd, ssd->cur_rtracker); ssd->cur_rtracker = NULL; } /******************************************************************** * Function: DCE2_SmbCheckData() * * Purpose: * Ensures that the data size reported in an SMB command is kosher. * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * const uint8_t * - pointer to start of SMB header where offset is * taken from. * const uint8_t * - current pointer - should be right after command * structure. * const uint32_t - remaining data left in PDU from current pointer. * const uint16_t - the byte count from the SMB command * const uint16_t - reported data count in SMB command * const uint16_t - reported data offset in SMB command * * Returns: * DCE2_Ret - DCE2_RET__ERROR if data should not be processed * DCE2_RET__SUCCESS if data can be processed * ********************************************************************/ static inline DCE2_Ret DCE2_SmbCheckData(DCE2_SmbSsnData *ssd, const uint8_t *smb_hdr_ptr, const uint8_t *nb_ptr, const uint32_t nb_len, const uint16_t bcc, const uint32_t dcnt, uint16_t doff) { const uint8_t *offset = smb_hdr_ptr + doff; const uint8_t *nb_end = nb_ptr + nb_len; // Byte counts don't usually matter, so no error but still alert // Don't alert in the case where the data count is larger than what the // byte count can handle. This can happen if CAP_LARGE_READX or // CAP_LARGE_WRITEX were negotiated. if ((dcnt <= UINT16_MAX) && (bcc < dcnt)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BCC_LT_DSIZE, bcc, (uint64_t)dcnt); if (offset > nb_end) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, offset, nb_ptr, nb_end); // Error if offset is beyond data left return DCE2_RET__ERROR; } // Only check if the data count is non-zero if ((dcnt != 0) && (offset < nb_ptr)) { // Not necessarily and error if the offset puts the data // before or in the command structure. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, offset, nb_ptr, nb_end); } // Not necessarily an error if the addition of the data count goes // beyond the data left if (((offset + dcnt) > nb_end) // beyond data left || ((offset + dcnt) < offset)) // wrap { int pad = offset - nb_ptr; if (pad > 0) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_DSIZE, nb_len - pad, dcnt); else DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_DSIZE, nb_len, dcnt); } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbValidateTransactionFields() * * Purpose: * Wrapper that calls DCE2_SmbCheckTotalCount() for total parameter * count and total data count and DCE2_SmbCheckTransDataParams() * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * const uint8_t * - pointer to start of SMB header where offset is * taken from. * const uint8_t * - current pointer - should be right after command * structure. * const uint32_t - remaining data left in PDU from current pointer. * const uint16_t - the byte count * const uint32_t - reported total data count * const uint32_t - reported total parameter count * const uint32_t - reported data count * const uint32_t - reported data offset * const uint32_t - reported data displacement * const uint32_t - reported parameter count * const uint32_t - reported parameter offset * const uint32_t - reported parameter displacement * * Returns: * DCE2_Ret - DCE2_RET__ERROR if data should not be processed * DCE2_RET__SUCCESS if data can be processed * ********************************************************************/ static inline DCE2_Ret DCE2_SmbValidateTransactionFields(DCE2_SmbSsnData *ssd, const uint8_t *smb_hdr_ptr, const uint8_t *nb_ptr, const uint32_t nb_len, const uint16_t bcc, const uint32_t tdcnt, const uint32_t tpcnt, const uint32_t dcnt, const uint32_t doff, const uint32_t ddisp, const uint32_t pcnt, const uint32_t poff, const uint32_t pdisp) { if (DCE2_SmbCheckTotalCount(ssd, tdcnt, dcnt, ddisp) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_SmbCheckTotalCount(ssd, tpcnt, pcnt, pdisp) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_SmbCheckTransDataParams(ssd, smb_hdr_ptr, nb_ptr, nb_len, bcc, dcnt, doff, pcnt, poff) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbValidateTransactionSent() * * Purpose: * Checks that amount sent plus current amount is not greater than * the total count expected. * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * const uint32_t - amount of data sent so far * const uint32_t - reported total data count * const uint32_t - reported data count * const uint32_t - amount of parameters sent so far * const uint32_t - reported total parameter count * const uint32_t - reported parameter count * * Returns: * DCE2_Ret - DCE2_RET__ERROR if data should not be processed * DCE2_RET__SUCCESS if data can be processed * ********************************************************************/ static inline DCE2_Ret DCE2_SmbValidateTransactionSent(DCE2_SmbSsnData *ssd, uint32_t dsent, uint32_t dcnt, uint32_t tdcnt, uint32_t psent, uint32_t pcnt, uint32_t tpcnt) { if (((dsent + dcnt) > tdcnt) || ((psent + pcnt) > tpcnt)) { if ((dsent + dcnt) > tdcnt) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DSENT_GT_TDCNT, ((uint64_t)dsent + dcnt), tdcnt); } if ((psent + pcnt) > tpcnt) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DSENT_GT_TDCNT, ((uint64_t)psent + pcnt), tpcnt); } // Samba throws out entire transaction and Windows seems to hang in // limbo forever and never responds, so stop looking return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbCheckTransDataParams() * * Purpose: * Ensures that the data size reported in an SMB command is kosher. * Note the 32 bit values are because of the NtTransact command * though it's currently not checked. * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * const uint8_t * - pointer to start of SMB header where offset is * taken from. * const uint8_t * - current pointer - should be right after command * structure. * const uint32_t - remaining data left in PDU from current pointer. * const uint16_t - the byte count * const uint32_t - reported data count * const uint32_t - reported data offset * const uint32_t - reported parameter count * const uint32_t - reported parameter offset * * Returns: * DCE2_Ret - DCE2_RET__ERROR if data should not be processed * DCE2_RET__SUCCESS if data can be processed * ********************************************************************/ static inline DCE2_Ret DCE2_SmbCheckTransDataParams(DCE2_SmbSsnData *ssd, const uint8_t *smb_hdr_ptr, const uint8_t *nb_ptr, const uint32_t nb_len, const uint16_t bcc, const uint32_t dcnt, const uint32_t doff, const uint32_t pcnt, const uint32_t poff) { const uint8_t *doffset = smb_hdr_ptr + doff; const uint8_t *poffset = smb_hdr_ptr + poff; const uint8_t *nb_end = nb_ptr + nb_len; if (bcc < ((uint64_t)dcnt + pcnt)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BCC_LT_DSIZE, bcc, ((uint64_t)dcnt + pcnt)); // Check data offset out of bounds if ((doffset > nb_end) || (doffset < smb_hdr_ptr)) { // Beyond data left or wrap DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, doffset, nb_ptr, nb_end); return DCE2_RET__ERROR; } // Check data offset in bounds but backwards // Only check if the data count is non-zero if ((dcnt != 0) && (doffset < nb_ptr)) { // Not necessarily and error if the offset puts the data // before or in the command structure. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, doffset, nb_ptr, nb_end); } // Check the data offset + data count if (((doffset + dcnt) > nb_end) // beyond data left || ((doffset + dcnt) < doffset)) // wrap { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_DSIZE, nb_len, dcnt); return DCE2_RET__ERROR; } // Check parameter offset out of bounds if ((poffset > nb_end) || (poffset < smb_hdr_ptr)) { // Beyond data left or wrap DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, poffset, nb_ptr, nb_end); return DCE2_RET__ERROR; } // Check parameter offset in bounds but backwards // Only check if the parameter count is non-zero if ((pcnt != 0) && (poffset < nb_ptr)) { // Not necessarily and error if the offset puts the data // before or in the command structure. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, poffset, nb_ptr, nb_end); } // Check the parameter offset + parameter count if (((poffset + pcnt) > nb_end) // beyond data left || ((poffset + pcnt) < poffset)) // wrap { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_DSIZE, nb_len, pcnt); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbCheckFmtData() * * Purpose: * Checks the data count in commands with formats, e.g. * SMB_COM_WRITE, SMB_COM_WRITE_AND_CLOSE, SMB_COM_WRITE_AND_UNLOCK. * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * const uint32_t - remaining NetBIOS PDU length * const uint16_t - advertised byte count * const uint8_t - data format specifier * const uint16_t - data count reported in command * const uint16_t - data count reported in format field * * Returns: None * ********************************************************************/ static inline void DCE2_SmbCheckFmtData(DCE2_SmbSsnData *ssd, const uint32_t nb_len, const uint16_t bcc, const uint8_t fmt, const uint16_t com_dcnt, const uint16_t fmt_dcnt) { if (fmt != SMB_FMT__DATA_BLOCK) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, fmt); if (com_dcnt != fmt_dcnt) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DCNT_MISMATCH, com_dcnt, fmt_dcnt); if (com_dcnt != (bcc - 3)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_DSIZE, com_dcnt, bcc); if (nb_len < com_dcnt) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_DSIZE, nb_len, com_dcnt); } /******************************************************************** * Function: DCE2_SmbCheckTotalCount() * * Purpose: * Validates the advertised total data/param count. Makes sure the * current count isn't greater than total count, that the * displacement + count isn't greater than the total data count and * that the total data count isn't zero. Mainly relevant to Write Raw, * Transaction and Transaction Secondary commands. * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * const uint32_t - total data count * const uint32_t - data count/size * const uint32_t - data displacement * * Returns: * DCE2_Ret - DCE2_RET__SUCCESS if all is ok * DCE2_RET__ERROR if any of the checks fail. * ********************************************************************/ static inline DCE2_Ret DCE2_SmbCheckTotalCount(DCE2_SmbSsnData *ssd, const uint32_t tcnt, const uint32_t cnt, const uint32_t disp) { DCE2_Ret ret = DCE2_RET__SUCCESS; if (cnt > tcnt) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_TDCNT_LT_DSIZE, tcnt, cnt); ret = DCE2_RET__ERROR; } if (((uint64_t)disp + cnt) > tcnt) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DSENT_GT_TDCNT, ((uint64_t)disp + cnt), tcnt); ret = DCE2_RET__ERROR; } return ret; } /******************************************************************** * Function: DCE2_SmbCheckAndXOffset() * * Purpose: * Validates that the AndXOffset is within bounds of the remaining * data we have to work with. * * Arguments: * uint8_t * - pointer to where the offset would take us. * uint8_t * - pointer to bound offset * uint8_t * - length of data where offset should be within * * Returns: * DCE2_RET__SUCCESS - Offset is okay. * DCE2_RET__ERROR - Offset is bad. * ********************************************************************/ static inline DCE2_Ret DCE2_SmbCheckAndXOffset(DCE2_SmbSsnData *ssd, const uint8_t *off_ptr, const uint8_t *start_bound, const uint32_t length) { /* Offset should not point within data we just looked at or be equal to * or beyond the length of the NBSS length left */ if ((off_ptr < start_bound) || (off_ptr > (start_bound + length))) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_OFF, off_ptr, start_bound, start_bound + length); return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbInvalidShareCheck() * * Purpose: * Checks the share reported in a TreeConnect or TreeConnectAndX * against the invalid share list configured in the dcerpc2 * configuration in snort.conf. * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * SmbNtHdr * - pointer to the SMB header structure * uint8_t * - current pointer to the share to check * uint32_t - the remaining length * * Returns: None * Alerts if there is an invalid share match. * ********************************************************************/ static inline void DCE2_SmbInvalidShareCheck(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const uint8_t *nb_ptr, uint32_t nb_len) { DCE2_List *share_list = DCE2_ScSmbInvalidShares(ssd->sd.sconfig); DCE2_SmbShare *smb_share; if (share_list == NULL) return; for (smb_share = (DCE2_SmbShare *)DCE2_ListFirst(share_list); smb_share != NULL; smb_share = (DCE2_SmbShare *)DCE2_ListNext(share_list)) { unsigned int i; const char *share_str; unsigned int share_str_len; if (SmbUnicode(smb_hdr)) { share_str = smb_share->unicode_str; share_str_len = smb_share->unicode_str_len; } else { share_str = smb_share->ascii_str; share_str_len = smb_share->ascii_str_len; } /* Make sure we have enough data */ if (nb_len < share_str_len) continue; /* Test for share match */ for (i = 0; i < share_str_len; i++) { /* All share strings should have been converted to upper case and * should include null terminating bytes */ if ((nb_ptr[i] != share_str[i]) && (nb_ptr[i] != tolower((int)share_str[i]))) break; } if (i == share_str_len) { /* Should only match one share since no duplicate shares in list */ DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_SHARE, smb_share->ascii_str); break; } } } /******************************************************************** * Functions: * DCE2_SmbOpen() * DCE2_SmbCreate() * DCE2_SmbClose() * DCE2_SmbRename() * DCE2_SmbRead() * DCE2_SmbWrite() * DCE2_SmbCreateNew() * DCE2_SmbLockAndRead() * DCE2_SmbWriteAndUnlock() * DCE2_SmbReadRaw() * DCE2_SmbWriteRaw() * DCE2_SmbWriteComplete() * DCE2_SmbTransaction() * DCE2_SmbTransactionSecondary() * DCE2_SmbWriteAndClose() * DCE2_SmbOpenAndX() * DCE2_SmbReadAndX() * DCE2_SmbWriteAndX() * DCE2_SmbTransaction2() * DCE2_SmbTransaction2Secondary() * DCE2_SmbTreeConnect() * DCE2_SmbTreeDisconnect() * DCE2_SmbNegotiate() * DCE2_SmbSessionSetupAndX() * DCE2_SmbLogoffAndX() * DCE2_SmbTreeConnectAndX() * DCE2_SmbNtTransact() * DCE2_SmbNtTransactSecondary() * DCE2_SmbNtCreateAndX() * * Purpose: Process SMB command * * Arguments: * DCE2_SmbSsnData * - SMB session data structure * const SmbNtHdr * - SMB header structure (packet pointer) * const DCE2_SmbComInfo * - Basic command information structure * uint8_t * - pointer to start of command (packet pointer) * uint32_t - remaining NetBIOS length * * Returns: * DCE2_Ret - DCE2_RET__ERROR if something went wrong and/or processing * should stop * DCE2_RET__SUCCESS if processing should continue * ********************************************************************/ // SMB_COM_OPEN static DCE2_Ret DCE2_SmbOpen(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsResponse(com_info)) { DCE2_SmbFileTracker *ftracker; if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid) && (SmbFileAttrsDirectory(SmbOpenRespFileAttrs((SmbOpenResp *)nb_ptr)) || SmbOpenForWriting(SmbOpenRespAccessMode((SmbOpenResp *)nb_ptr)))) return DCE2_RET__SUCCESS; ftracker = DCE2_SmbNewFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbOpenRespFid((SmbOpenResp *)nb_ptr)); if (ftracker == NULL) return DCE2_RET__ERROR; DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker); if (!ftracker->is_ipc) { // This command can only be used to open an existing file ftracker->ff_file_size = SmbOpenRespFileSize((SmbOpenResp *)nb_ptr); } } else { // Have at least 2 bytes of data based on byte count check done earlier DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); if (!SmbFmtAscii(*nb_ptr)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, *nb_ptr); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, 1); ssd->cur_rtracker->file_name = DCE2_SmbGetString(nb_ptr, nb_len, SmbUnicode(smb_hdr), &ssd->cur_rtracker->file_name_len); } return DCE2_RET__SUCCESS; } // SMB_COM_CREATE static DCE2_Ret DCE2_SmbCreate(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsResponse(com_info)) { DCE2_SmbFileTracker *ftracker = DCE2_SmbNewFileTracker( ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbCreateRespFid((SmbCreateResp *)nb_ptr)); if (ftracker == NULL) return DCE2_RET__ERROR; DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker); // Command creates or opens and truncates file to 0 so assume // upload. if (!ftracker->is_ipc) ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; } else { if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { uint16_t file_attrs = SmbCreateReqFileAttrs((SmbCreateReq *)nb_ptr); if (SmbAttrDirectory(file_attrs)) return DCE2_RET__IGNORE; if (SmbEvasiveFileAttrs(file_attrs)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS); } // Have at least 2 bytes of data based on byte count check done earlier DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); if (!SmbFmtAscii(*nb_ptr)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, *nb_ptr); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, 1); ssd->cur_rtracker->file_name = DCE2_SmbGetString(nb_ptr, nb_len, SmbUnicode(smb_hdr), &ssd->cur_rtracker->file_name_len); } return DCE2_RET__SUCCESS; } // SMB_COM_CLOSE static DCE2_Ret DCE2_SmbClose(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { uint16_t fid = SmbCloseReqFid((SmbCloseReq *)nb_ptr); // Set this for response ssd->cur_rtracker->ftracker = DCE2_SmbGetFileTracker(ssd, fid); #ifdef ACTIVE_RESPONSE if ((ssd->fb_ftracker != NULL) && (ssd->fb_ftracker == ssd->cur_rtracker->ftracker)) { void *ssnptr = ssd->sd.wire_pkt->stream_session; void *p = (void *)ssd->sd.wire_pkt; File_Verdict verdict = DCE2_SmbGetFileVerdict(p, ssnptr); if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)) ssd->block_pdus = true; } #endif } else { return DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker); } return DCE2_RET__SUCCESS; } // SMB_COM_RENAME static DCE2_Ret DCE2_SmbRename(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { // NOTE: This command is only processed for CVE-2006-4696 where the buffer // formats are invalid and has no bearing on DCE/RPC processing. if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { // Have at least 4 bytes of data based on byte count check done earlier uint32_t i; DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); if (!SmbFmtAscii(*nb_ptr)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, *nb_ptr); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, 1); if (SmbUnicode(smb_hdr)) { for (i = 0; i < (nb_len - 1); i += 2) { if (*((uint16_t *)(nb_ptr + i)) == 0) { i += 2; // move past null terminating bytes break; } } } else { for (i = 0; i < nb_len; i++) { if (nb_ptr[i] == 0) { i++; // move past null terminating byte break; } } } // i <= nb_len DCE2_MOVE(nb_ptr, nb_len, i); if ((nb_len > 0) && !SmbFmtAscii(*nb_ptr)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, *nb_ptr); return DCE2_RET__ERROR; } } // Don't care about tracking response return DCE2_RET__ERROR; } // SMB_COM_READ static DCE2_Ret DCE2_SmbRead(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { DCE2_SmbFileTracker *ftracker = DCE2_SmbGetFileTracker(ssd, SmbReadReqFid((SmbReadReq *)nb_ptr)); // Set this for response since response doesn't have the Fid ssd->cur_rtracker->ftracker = ftracker; if ((ftracker != NULL) && !ftracker->is_ipc) ssd->cur_rtracker->file_offset = SmbReadReqOffset((SmbReadReq *)nb_ptr); } else { // Have at least 3 bytes of data based on byte count check done earlier uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint16_t com_dcnt = SmbReadRespCount((SmbReadResp *)nb_ptr); uint8_t fmt = *(nb_ptr + com_size); uint16_t fmt_dcnt = SmbNtohs((uint16_t *)(nb_ptr + com_size + 1)); DCE2_MOVE(nb_ptr, nb_len, (com_size + 3)); DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt); if (com_dcnt > nb_len) return DCE2_RET__ERROR; return DCE2_SmbProcessResponseData(ssd, nb_ptr, com_dcnt); } return DCE2_RET__SUCCESS; } // SMB_COM_WRITE static DCE2_Ret DCE2_SmbWrite(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { // Have at least 3 bytes of data based on byte count check done earlier uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint8_t fmt = *(nb_ptr + com_size); uint16_t com_dcnt = SmbWriteReqCount((SmbWriteReq *)nb_ptr); uint16_t fmt_dcnt = SmbNtohs((uint16_t *)(nb_ptr + com_size + 1)); uint16_t fid = SmbWriteReqFid((SmbWriteReq *)nb_ptr); uint32_t offset = SmbWriteReqOffset((SmbWriteReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, (com_size + 3)); DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt); if (com_dcnt == 0) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DCNT_ZERO); return DCE2_RET__ERROR; } if (com_dcnt > nb_len) com_dcnt = (uint16_t)nb_len; return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, com_dcnt, offset); } return DCE2_RET__SUCCESS; } // SMB_COM_CREATE_NEW static DCE2_Ret DCE2_SmbCreateNew(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsResponse(com_info)) { DCE2_SmbFileTracker *ftracker = DCE2_SmbNewFileTracker( ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbCreateNewRespFid((SmbCreateNewResp *)nb_ptr)); if (ftracker == NULL) return DCE2_RET__ERROR; DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker); // Command creates a new file so assume upload. if (!ftracker->is_ipc) ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; } else { if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { uint16_t file_attrs = SmbCreateNewReqFileAttrs((SmbCreateNewReq *)nb_ptr); if (SmbAttrDirectory(file_attrs)) return DCE2_RET__IGNORE; if (SmbEvasiveFileAttrs(file_attrs)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS); } // Have at least 2 bytes of data based on byte count check done earlier DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); if (!SmbFmtAscii(*nb_ptr)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, *nb_ptr); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, 1); ssd->cur_rtracker->file_name = DCE2_SmbGetString(nb_ptr, nb_len, SmbUnicode(smb_hdr), &ssd->cur_rtracker->file_name_len); } return DCE2_RET__SUCCESS; } // SMB_COM_LOCK_AND_READ static DCE2_Ret DCE2_SmbLockAndRead(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { DCE2_SmbFileTracker *ftracker = DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbLockAndReadReqFid((SmbLockAndReadReq *)nb_ptr)); // No sense in tracking response if (ftracker == NULL) return DCE2_RET__ERROR; if (!ftracker->is_ipc) ssd->cur_rtracker->file_offset = SmbLockAndReadReqOffset((SmbLockAndReadReq *)nb_ptr); // Set this for response ssd->cur_rtracker->ftracker = ftracker; } else { // Have at least 3 bytes of data based on byte count check done earlier uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint8_t fmt = *(nb_ptr + com_size); uint16_t com_dcnt = SmbLockAndReadRespCount((SmbLockAndReadResp *)nb_ptr); uint16_t fmt_dcnt = SmbNtohs((uint16_t *)(nb_ptr + com_size + 1)); DCE2_MOVE(nb_ptr, nb_len, (com_size + 3)); DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt); if (com_dcnt == 0) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DCNT_ZERO); return DCE2_RET__ERROR; } if (com_dcnt > nb_len) com_dcnt = (uint16_t)nb_len; return DCE2_SmbProcessResponseData(ssd, nb_ptr, com_dcnt); } return DCE2_RET__SUCCESS; } // SMB_COM_WRITE_AND_UNLOCK static DCE2_Ret DCE2_SmbWriteAndUnlock(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) { if (DCE2_ComInfoIsBadLength(com_info) || DCE2_ComInfoIsInvalidWordCount(com_info)) return DCE2_RET__ERROR; // These are special cases. The write succeeds but the unlock fails // so an error reponse is returned but the data was actually written. if (DCE2_ComInfoIsResponse(com_info) && DCE2_ComInfoIsStatusError(com_info)) { if (DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { if (!SmbErrorInvalidDeviceRequest(smb_hdr)) return DCE2_RET__ERROR; } else if (!SmbErrorRangeNotLocked(smb_hdr)) { return DCE2_RET__ERROR; } } } if (DCE2_ComInfoIsRequest(com_info)) { // Have at least 3 bytes of data based on byte count check done earlier uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint8_t fmt = *(nb_ptr + com_size); uint16_t com_dcnt = SmbWriteAndUnlockReqCount((SmbWriteAndUnlockReq *)nb_ptr); uint16_t fmt_dcnt = SmbNtohs((uint16_t *)(nb_ptr + com_size + 1)); uint16_t fid = SmbWriteAndUnlockReqFid((SmbWriteAndUnlockReq *)nb_ptr); uint32_t offset = SmbWriteAndUnlockReqOffset((SmbWriteAndUnlockReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, (com_size + 3)); DCE2_SmbCheckFmtData(ssd, nb_len, byte_count, fmt, com_dcnt, fmt_dcnt); if (com_dcnt == 0) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DCNT_ZERO); return DCE2_RET__ERROR; } if (com_dcnt > nb_len) com_dcnt = (uint16_t)nb_len; return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, com_dcnt, offset); } return DCE2_RET__SUCCESS; } // SMB_COM_READ_RAW static DCE2_Ret DCE2_SmbReadRaw(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { DCE2_SmbFileTracker *ftracker = DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbReadRawReqFid((SmbReadRawReq *)nb_ptr)); ssd->cur_rtracker->ftracker = ftracker; ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; if ((ftracker != NULL) && !ftracker->is_ipc) ssd->cur_rtracker->file_offset = SmbReadRawReqOffset((SmbReadRawExtReq *)nb_ptr); } else { // The server response is the raw data. Supposedly if an error occurs, // the server will send a 0 byte read. Just the NetBIOS header with // zero byte length. Client upon getting the zero read is supposed to issue // another read using ReadAndX or Read to get the error. return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } // SMB_COM_WRITE_RAW static DCE2_Ret DCE2_SmbWriteRaw(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint16_t fid = SmbWriteRawReqFid((SmbWriteRawReq *)nb_ptr); uint16_t tdcnt = SmbWriteRawReqTotalCount((SmbWriteRawReq *)nb_ptr); bool writethrough = SmbWriteRawReqWriteThrough((SmbWriteRawReq *)nb_ptr); uint16_t doff = SmbWriteRawReqDataOff((SmbWriteRawReq *)nb_ptr); uint16_t dcnt = SmbWriteRawReqDataCnt((SmbWriteRawReq *)nb_ptr); uint64_t offset = SmbWriteRawReqOffset((SmbWriteRawExtReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, com_size); if (DCE2_SmbCheckTotalCount(ssd, tdcnt, dcnt, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_SmbCheckData(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, byte_count, dcnt, doff) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; // This may move backwards DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); if (dcnt > nb_len) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_NB_LT_DSIZE, nb_len, dcnt); return DCE2_RET__ERROR; } // If all of the data wasn't written in this request, the server will // send an interim SMB_COM_WRITE_RAW response and the client will send // the rest of the data raw. In this case if the WriteThrough flag is // not set, the server will not send a final SMB_COM_WRITE_COMPLETE // response. If all of the data is in this request the server will // send an SMB_COM_WRITE_COMPLETE response regardless of whether or // not the WriteThrough flag is set. if (dcnt != tdcnt) { ssd->cur_rtracker->writeraw_writethrough = writethrough; ssd->cur_rtracker->writeraw_remaining = tdcnt - dcnt; } return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, offset); } else { DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd); // Samba messes this up and sends a request instead of an interim // response and a response instead of a Write Complete response. switch (policy) { case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: if (SmbType(smb_hdr) != SMB_TYPE__REQUEST) return DCE2_RET__SUCCESS; break; default: break; } // If all the data wasn't written initially this interim response will // be sent by the server and the raw data will ensue from the client. ssd->pdu_state = DCE2_SMB_PDU_STATE__RAW_DATA; } return DCE2_RET__SUCCESS; } // SMB_COM_WRITE_COMPLETE static DCE2_Ret DCE2_SmbWriteComplete(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; return DCE2_RET__SUCCESS; } #define TRANS_NM_PIPE_0 (0) #define TRANS_NM_PIPE_1 (TRANS_NM_PIPE_0+7) #define TRANS_NM_PIPE_2 (TRANS_NM_PIPE_1+1) #define TRANS_NM_PIPE_3 (TRANS_NM_PIPE_2+1) #define TRANS_NM_PIPE_4 (TRANS_NM_PIPE_3+5) #define TRANS_NM_PIPE_5 (TRANS_NM_PIPE_4+5) #define TRANS_NM_PIPE_6 (TRANS_NM_PIPE_5+1) #define TRANS_NM_PIPE_7 (TRANS_NM_PIPE_6+5) #define TRANS_NM_PIPE_8 (TRANS_NM_PIPE_7+3) #define TRANS_NM_PIPE_9 (TRANS_NM_PIPE_8+6) #define TRANS_NM_PIPE_FS (TRANS_NM_PIPE_9+1) #define TRANS_NM_PIPE_DONE (TRANS_NM_PIPE_FS+1) static DCE2_SmbFsm dce2_samba_pipe_fsm[] = { // Normal sequence {'\\', TRANS_NM_PIPE_0+1, TRANS_NM_PIPE_FS }, {'P' , TRANS_NM_PIPE_0+2, TRANS_NM_PIPE_FS }, {'I' , TRANS_NM_PIPE_0+3, TRANS_NM_PIPE_FS }, {'P' , TRANS_NM_PIPE_0+4, TRANS_NM_PIPE_FS }, {'E' , TRANS_NM_PIPE_0+5, TRANS_NM_PIPE_FS }, {'\\', TRANS_NM_PIPE_0+6, TRANS_NM_PIPE_1 }, {'\0', TRANS_NM_PIPE_DONE, TRANS_NM_PIPE_2 }, // Win98 {'\0', TRANS_NM_PIPE_DONE, TRANS_NM_PIPE_FS }, {'W' , TRANS_NM_PIPE_2+1, TRANS_NM_PIPE_5 }, {'K' , TRANS_NM_PIPE_3+1, TRANS_NM_PIPE_4 }, {'S' , TRANS_NM_PIPE_3+2, TRANS_NM_PIPE_FS }, {'S' , TRANS_NM_PIPE_3+3, TRANS_NM_PIPE_FS }, {'V' , TRANS_NM_PIPE_3+4, TRANS_NM_PIPE_FS }, {'C' , TRANS_NM_PIPE_9 , TRANS_NM_PIPE_FS }, {'I' , TRANS_NM_PIPE_4+1, TRANS_NM_PIPE_FS }, {'N' , TRANS_NM_PIPE_4+2, TRANS_NM_PIPE_FS }, {'R' , TRANS_NM_PIPE_4+3, TRANS_NM_PIPE_FS }, {'E' , TRANS_NM_PIPE_4+4, TRANS_NM_PIPE_FS }, {'G' , TRANS_NM_PIPE_9 , TRANS_NM_PIPE_FS }, {'S' , TRANS_NM_PIPE_5+1, TRANS_NM_PIPE_8 }, {'R' , TRANS_NM_PIPE_6+1, TRANS_NM_PIPE_5 }, {'V' , TRANS_NM_PIPE_6+2, TRANS_NM_PIPE_FS }, {'S' , TRANS_NM_PIPE_6+3, TRANS_NM_PIPE_FS }, {'V' , TRANS_NM_PIPE_6+4, TRANS_NM_PIPE_FS }, {'C' , TRANS_NM_PIPE_9 , TRANS_NM_PIPE_FS }, {'A' , TRANS_NM_PIPE_7+1, TRANS_NM_PIPE_FS }, {'M' , TRANS_NM_PIPE_7+2, TRANS_NM_PIPE_FS }, {'R' , TRANS_NM_PIPE_9 , TRANS_NM_PIPE_FS }, {'L' , TRANS_NM_PIPE_8+1, TRANS_NM_PIPE_FS }, {'S' , TRANS_NM_PIPE_8+2, TRANS_NM_PIPE_FS }, {'A' , TRANS_NM_PIPE_8+3, TRANS_NM_PIPE_FS }, {'R' , TRANS_NM_PIPE_8+4, TRANS_NM_PIPE_FS }, {'P' , TRANS_NM_PIPE_8+5, TRANS_NM_PIPE_FS }, {'C' , TRANS_NM_PIPE_9 , TRANS_NM_PIPE_FS }, {'\0', TRANS_NM_PIPE_DONE, TRANS_NM_PIPE_FS }, {0, TRANS_NM_PIPE_FS, TRANS_NM_PIPE_FS } }; // Validates Name for Samba Transaction requests static DCE2_Ret DCE2_SmbTransactionGetName(const uint8_t *nb_ptr, uint32_t nb_len, uint16_t bcc, bool unicode) { uint8_t increment = unicode ? 2 : 1; int state = TRANS_NM_PIPE_0; if ((nb_len == 0) || (bcc == 0)) return DCE2_RET__ERROR; if (bcc < nb_len) nb_len = bcc; if (unicode) DCE2_MOVE(nb_ptr, nb_len, 1); // One byte pad for unicode while ((nb_len >= increment) && (state < TRANS_NM_PIPE_FS)) { if (dce2_samba_pipe_fsm[state].input == toupper((int)nb_ptr[0])) { if (unicode && (nb_ptr[1] != 0)) break; state = dce2_samba_pipe_fsm[state].next_state; DCE2_MOVE(nb_ptr, nb_len, increment); } else { state = dce2_samba_pipe_fsm[state].fail_state; } } switch (state) { case TRANS_NM_PIPE_DONE: break; case TRANS_NM_PIPE_FS: default: return DCE2_RET__ERROR; } return DCE2_RET__SUCCESS; } // Convenience function to determine whether or not the transaction is complete // for one side, i.e. all data and parameters sent. static inline bool DCE2_SmbIsTransactionComplete(DCE2_SmbTransactionTracker *ttracker) { if ((ttracker->tdcnt == ttracker->dsent) && (ttracker->tpcnt == ttracker->psent)) return true; return false; } // SMB_COM_TRANSACTION Request static inline DCE2_Ret DCE2_SmbTransactionReq(DCE2_SmbSsnData *ssd, DCE2_SmbTransactionTracker *ttracker, const uint8_t *data_ptr, uint32_t data_len, const uint8_t *param_ptr, uint32_t param_len) { switch (ttracker->subcom) { case TRANS_TRANSACT_NMPIPE: case TRANS_WRITE_NMPIPE: if (DCE2_SmbProcessRequestData(ssd, 0, data_ptr, data_len, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case TRANS_SET_NMPIPE_STATE: // Only two parameters but more seems okay if (param_len >= 2) { if ((SmbNtohs((uint16_t *)param_ptr) & PIPE_STATE_MESSAGE_MODE)) ttracker->pipe_byte_mode = false; else ttracker->pipe_byte_mode = true; // Won't get a response if (DCE2_SsnIsWindowsPolicy(&ssd->sd) && ttracker->one_way) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting pipe to %s mode\n", ttracker->pipe_byte_mode ? "byte" : "message")); ssd->cur_rtracker->ftracker->fp_byte_mode = ttracker->pipe_byte_mode; } } break; case TRANS_READ_NMPIPE: break; default: return DCE2_RET__IGNORE; } if (DCE2_SsnIsWindowsPolicy(&ssd->sd) && ttracker->one_way && ttracker->disconnect_tid) DCE2_SmbRemoveTid(ssd, ssd->cur_rtracker->tid); return DCE2_RET__SUCCESS; } // SMB_COM_TRANSACTION static DCE2_Ret DCE2_SmbTransaction(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; // Got a matching request for an in progress transaction - don't process it, // but don't want to remove tracker. if (DCE2_ComInfoIsRequest(com_info) && !DCE2_SmbIsTransactionComplete(ttracker)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Got new transaction request " "that matches an in progress transaction - not inspecting.\n")); return DCE2_RET__ERROR; } // Avoid decoding/tracking \PIPE\LANMAN requests if (DCE2_ComInfoIsRequest(com_info) && (DCE2_ComInfoWordCount(com_info) != 16)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "\\PIPE\\LANMAN request - not inspecting\n")); return DCE2_RET__IGNORE; } if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; // Interim response is sent if client didn't send all data / parameters // in initial Transaction request and will have to complete the request // with TransactionSecondary commands. if (DCE2_ComInfoIsResponse(com_info) && (com_size == sizeof(SmbTransactionInterimResp))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, " Server Transaction interim response.\n")); return DCE2_RET__SUCCESS; } if (DCE2_ComInfoIsRequest(com_info)) { uint16_t doff, dcnt, pcnt, poff; const uint8_t *data_ptr, *param_ptr; DCE2_Ret status = DCE2_SmbUpdateTransRequest(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; ttracker->disconnect_tid = SmbTransactionReqDisconnectTid((SmbTransactionReq *)nb_ptr); ttracker->one_way = SmbTransactionReqOneWay((SmbTransactionReq *)nb_ptr); doff = SmbTransactionReqDataOff((SmbTransactionReq *)nb_ptr); dcnt = SmbTransactionReqDataCnt((SmbTransactionReq *)nb_ptr); pcnt = SmbTransactionReqParamCnt((SmbTransactionReq *)nb_ptr); poff = SmbTransactionReqParamOff((SmbTransactionReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); data_ptr = nb_ptr; DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); param_ptr = nb_ptr; status = DCE2_SmbTransactionReq(ssd, ttracker, data_ptr, dcnt, param_ptr, pcnt); if (status != DCE2_RET__SUCCESS) return status; } else { DCE2_Ret status = DCE2_SmbUpdateTransResponse(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; switch (ttracker->subcom) { case TRANS_TRANSACT_NMPIPE: case TRANS_READ_NMPIPE: if (!DCE2_BufferIsEmpty(ttracker->dbuf)) { const uint8_t *data_ptr = DCE2_BufferData(ttracker->dbuf); uint32_t data_len = DCE2_BufferLength(ttracker->dbuf); SFSnortPacket *rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS); if (rpkt == NULL) return DCE2_RET__ERROR; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Reassembled Transaction response\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); status = DCE2_SmbProcessResponseData(ssd, data_ptr, data_len); DCE2_SmbReturnRpkt(); if (status != DCE2_RET__SUCCESS) return status; } else { uint16_t dcnt = SmbTransactionRespDataCnt((SmbTransactionResp *)nb_ptr); uint16_t doff = SmbTransactionRespDataOff((SmbTransactionResp *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); if (DCE2_SmbProcessResponseData(ssd, nb_ptr, dcnt) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; } break; case TRANS_SET_NMPIPE_STATE: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting pipe " "to %s mode\n", ttracker->pipe_byte_mode ? "byte" : "message")); ssd->cur_rtracker->ftracker->fp_byte_mode = ttracker->pipe_byte_mode; break; case TRANS_WRITE_NMPIPE: break; default: return DCE2_RET__ERROR; } if (ttracker->disconnect_tid) DCE2_SmbRemoveTid(ssd, ssd->cur_rtracker->tid); } return DCE2_RET__SUCCESS; } // SMB_COM_TRANSACTION_SECONDARY static DCE2_Ret DCE2_SmbTransactionSecondary(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; DCE2_Ret status; SFSnortPacket *rpkt = NULL; if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; status = DCE2_SmbUpdateTransSecondary(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; switch (ttracker->subcom) { case TRANS_TRANSACT_NMPIPE: case TRANS_WRITE_NMPIPE: { const uint8_t *data_ptr = DCE2_BufferData(ttracker->dbuf); uint32_t data_len = DCE2_BufferLength(ttracker->dbuf); rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS); if (rpkt == NULL) return DCE2_RET__ERROR; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Reassembled Transaction request\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); status = DCE2_SmbTransactionReq(ssd, ttracker, data_ptr, data_len, DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf)); DCE2_SmbReturnRpkt(); } break; default: status = DCE2_SmbTransactionReq(ssd, ttracker, DCE2_BufferData(ttracker->dbuf), DCE2_BufferLength(ttracker->dbuf), DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf)); break; } return status; } // SMB_COM_WRITE_AND_CLOSE static DCE2_Ret DCE2_SmbWriteAndClose(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { // Have at least one byte based on byte count check done earlier uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint16_t dcnt = SmbWriteAndCloseReqCount((SmbWriteAndCloseReq *)nb_ptr); uint16_t fid = SmbWriteAndCloseReqFid((SmbWriteAndCloseReq *)nb_ptr); uint32_t offset = SmbWriteAndCloseReqOffset((SmbWriteAndCloseReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, (com_size + 1)); if (DCE2_SmbCheckData(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, byte_count, dcnt, (uint16_t)(sizeof(SmbNtHdr) + com_size + 1)) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (dcnt == 0) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DCNT_ZERO); return DCE2_RET__ERROR; } // WriteAndClose has a 1 byte pad after the byte count if ((uint32_t)(dcnt + 1) != (uint32_t)byte_count) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_DSIZE, (dcnt + 1), byte_count); if (dcnt > nb_len) dcnt = (uint16_t)nb_len; return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, offset); } else { DCE2_SmbRemoveFileTracker(ssd, ssd->cur_rtracker->ftracker); } return DCE2_RET__SUCCESS; } // SMB_COM_OPEN_ANDX static DCE2_Ret DCE2_SmbOpenAndX(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsResponse(com_info)) { const uint16_t fid = SmbOpenAndXRespFid((SmbOpenAndXResp *)nb_ptr); const uint16_t file_attrs = SmbOpenAndXRespFileAttrs((SmbOpenAndXResp *)nb_ptr); const uint16_t resource_type = SmbOpenAndXRespResourceType((SmbOpenAndXResp *)nb_ptr); DCE2_SmbFileTracker *ftracker = NULL; // Set request tracker's current file tracker in case of chained commands switch (SmbAndXCom2((SmbAndXCommon *)nb_ptr)) { // This is in case in the request a write was chained to an open // in which case the write will be to the newly opened file case SMB_COM_WRITE: case SMB_COM_WRITE_ANDX: case SMB_COM_TRANSACTION: case SMB_COM_READ_ANDX: ftracker = DCE2_SmbDequeueTmpFileTracker(ssd, ssd->cur_rtracker, fid); break; default: break; } if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid) && (SmbFileAttrsDirectory(file_attrs) || !SmbResourceTypeDisk(resource_type))) { if (ftracker != NULL) DCE2_SmbRemoveFileTracker(ssd, ftracker); return DCE2_RET__SUCCESS; } if (ftracker == NULL) { ftracker = DCE2_SmbNewFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, fid); if (ftracker == NULL) return DCE2_RET__ERROR; } DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker); if (!ftracker->is_ipc) { const uint16_t open_results = SmbOpenAndXRespOpenResults((SmbOpenAndXResp *)nb_ptr); if (SmbOpenResultRead(open_results)) { ftracker->ff_file_size = SmbOpenAndXRespFileSize((SmbOpenAndXResp *)nb_ptr); } else { ftracker->ff_file_size = ssd->cur_rtracker->file_size; ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; } } ssd->cur_rtracker->ftracker = ftracker; } else { uint32_t pad = 0; const bool unicode = SmbUnicode(smb_hdr); uint8_t null_bytes = unicode ? 2 : 1; if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { uint16_t file_attrs = SmbOpenAndXReqFileAttrs((SmbOpenAndXReq *)nb_ptr); if (SmbEvasiveFileAttrs(file_attrs)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS); ssd->cur_rtracker->file_size = SmbOpenAndXReqAllocSize((SmbOpenAndXReq *)nb_ptr); } DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); if (unicode) pad = (nb_ptr - (const uint8_t *)smb_hdr) & 1; if (nb_len < (pad + null_bytes)) return DCE2_RET__ERROR; DCE2_MOVE(nb_ptr, nb_len, pad); // Samba allows chaining OpenAndX/NtCreateAndX so might have // already been set. if (ssd->cur_rtracker->file_name == NULL) { ssd->cur_rtracker->file_name = DCE2_SmbGetString(nb_ptr, nb_len, unicode, &ssd->cur_rtracker->file_name_len); } } return DCE2_RET__SUCCESS; } // SMB_COM_READ_ANDX static DCE2_Ret DCE2_SmbReadAndX(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { DCE2_SmbFileTracker *ftracker = DCE2_SmbGetFileTracker(ssd, SmbReadAndXReqFid((SmbReadAndXReq *)nb_ptr)); // No sense in tracking response if (ftracker == NULL) return DCE2_RET__ERROR; if (!ftracker->is_ipc) ssd->cur_rtracker->file_offset = SmbReadAndXReqOffset((SmbReadAndXExtReq *)nb_ptr); // Set this for response ssd->cur_rtracker->ftracker = ftracker; } else { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint16_t doff = SmbReadAndXRespDataOff((SmbReadAndXResp *)nb_ptr); uint32_t dcnt = SmbReadAndXRespDataCnt((SmbReadAndXResp *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, com_size); if (DCE2_SmbCheckData(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, byte_count, dcnt, doff) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; // This may move backwards DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); if (dcnt > nb_len) dcnt = nb_len; return DCE2_SmbProcessResponseData(ssd, nb_ptr, dcnt); } return DCE2_RET__SUCCESS; } // SMB_COM_WRITE_ANDX static DCE2_Ret DCE2_SmbWriteAndX(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) { DCE2_SmbFileTracker *ftracker = ssd->cur_rtracker->ftracker; if ((ftracker != NULL) && ftracker->is_ipc && (ftracker->fp_writex_raw != NULL)) { ftracker->fp_writex_raw->remaining = 0; DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); } return DCE2_RET__ERROR; } if (DCE2_ComInfoIsRequest(com_info) && (SmbWriteAndXReqStartRaw((SmbWriteAndXReq *)nb_ptr) || SmbWriteAndXReqRaw((SmbWriteAndXReq *)nb_ptr))) { DCE2_SmbFileTracker *ftracker = DCE2_SmbGetFileTracker(ssd, SmbWriteAndXReqFid((SmbWriteAndXReq *)nb_ptr)); // Raw mode is only applicable to named pipes. if ((ftracker != NULL) && ftracker->is_ipc) return DCE2_SmbWriteAndXRawRequest(ssd, smb_hdr, com_info, nb_ptr, nb_len); } if (DCE2_ComInfoIsRequest(com_info)) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint16_t fid = SmbWriteAndXReqFid((SmbWriteAndXReq *)nb_ptr); uint16_t doff = SmbWriteAndXReqDataOff((SmbWriteAndXReq *)nb_ptr); uint32_t dcnt = SmbWriteAndXReqDataCnt((SmbWriteAndXReq *)nb_ptr); uint64_t offset = SmbWriteAndXReqOffset((SmbWriteAndXExtReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, com_size); if (DCE2_SmbCheckData(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, byte_count, dcnt, doff) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; // This may move backwards DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); if (dcnt > nb_len) { // Current Samba errors if data count is greater than data left if (DCE2_SsnGetPolicy(&ssd->sd) == DCE2_POLICY__SAMBA) return DCE2_RET__ERROR; // Windows and early Samba just use what's left dcnt = nb_len; } return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, offset); } return DCE2_RET__SUCCESS; } // SMB_COM_WRITE_ANDX - raw mode static DCE2_Ret DCE2_SmbWriteAndXRawRequest(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint16_t fid = SmbWriteAndXReqFid((SmbWriteAndXReq *)nb_ptr); uint16_t doff = SmbWriteAndXReqDataOff((SmbWriteAndXReq *)nb_ptr); uint32_t dcnt = SmbWriteAndXReqDataCnt((SmbWriteAndXReq *)nb_ptr); bool start_write_raw = SmbWriteAndXReqStartRaw((SmbWriteAndXReq *)nb_ptr); bool continue_write_raw = SmbWriteAndXReqRaw((SmbWriteAndXReq *)nb_ptr); uint16_t remaining = SmbWriteAndXReqRemaining((SmbWriteAndXReq *)nb_ptr); DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd); DCE2_SmbFileTracker *ftracker = DCE2_SmbGetFileTracker(ssd, fid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing WriteAndX with raw mode flags\n")); // Set this now for possible reassembled packet ssd->cur_rtracker->ftracker = ftracker; if (ftracker == NULL) return DCE2_RET__ERROR; // Got request to write in raw mode without having gotten the initial // raw mode request or got initial raw mode request and then another // without having finished the first. if ((start_write_raw && (ftracker->fp_writex_raw != NULL) && (ftracker->fp_writex_raw->remaining != 0)) || (continue_write_raw && ((ftracker->fp_writex_raw == NULL) || (ftracker->fp_writex_raw->remaining == 0)))) { switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: if (ftracker->fp_writex_raw != NULL) { ftracker->fp_writex_raw->remaining = 0; DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); } return DCE2_RET__ERROR; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: // Samba doesn't do anything special here except if the two // flags are set it walks past the two "length" bytes. // See below. break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } } DCE2_MOVE(nb_ptr, nb_len, com_size); if (DCE2_SmbCheckData(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, byte_count, dcnt, doff) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; // This may move backwards DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); // If a "raw" write is requested there will be two bytes after the // header/pad and before the data which is supposed to represent a // length but everyone ignores it. However we need to move past it. // This is the one situation where the remaining field matters and // should be equal to the total amount of data to be written. if (start_write_raw) { if (dcnt < 2) return DCE2_RET__ERROR; // From data size check above, nb_len >= dsize dcnt -= 2; DCE2_MOVE(nb_ptr, nb_len, 2); } if (dcnt > nb_len) dcnt = nb_len; // File tracker already validated switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: if (start_write_raw) { if (ftracker->fp_writex_raw == NULL) { ftracker->fp_writex_raw = (DCE2_SmbWriteAndXRaw *) DCE2_Alloc(sizeof(DCE2_SmbWriteAndXRaw), DCE2_MEM_TYPE__SMB_FID); if (ftracker->fp_writex_raw == NULL) return DCE2_RET__ERROR; ftracker->fp_writex_raw->remaining = (int)remaining; } } ftracker->fp_writex_raw->remaining -= (int)dcnt; if (ftracker->fp_writex_raw->remaining < 0) { ftracker->fp_writex_raw->remaining = 0; DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); return DCE2_RET__ERROR; } // If the "raw" write isn't finished in the first request // and haven't allocated a buffer yet. if (start_write_raw && (ftracker->fp_writex_raw->remaining != 0) && (ftracker->fp_writex_raw->buf == NULL)) { ftracker->fp_writex_raw->buf = DCE2_BufferNew(remaining, 0, DCE2_MEM_TYPE__SMB_FID); if (ftracker->fp_writex_raw->buf == NULL) { ftracker->fp_writex_raw->remaining = 0; return DCE2_RET__ERROR; } } // If data has to be added to buffer, i.e. not a start raw // or a start raw and more raw requests to come. if (!start_write_raw || (ftracker->fp_writex_raw->remaining != 0)) { if (DCE2_BufferAddData(ftracker->fp_writex_raw->buf, nb_ptr, dcnt, DCE2_BufferLength(ftracker->fp_writex_raw->buf), DCE2_BUFFER_MIN_ADD_FLAG__IGNORE) != DCE2_RET__SUCCESS) { ftracker->fp_writex_raw->remaining = 0; DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); return DCE2_RET__ERROR; } if (ftracker->fp_writex_raw->remaining == 0) { // Create reassembled packet const uint8_t *data_ptr = DCE2_BufferData(ftracker->fp_writex_raw->buf); uint32_t data_len = DCE2_BufferLength(ftracker->fp_writex_raw->buf); SFSnortPacket *rpkt = DCE2_SmbGetRpkt(ssd, &data_ptr, &data_len, DCE2_RPKT_TYPE__SMB_TRANS); if (rpkt == NULL) { DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); return DCE2_RET__ERROR; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Reassembled WriteAndX raw mode request\n")); DCE2_DEBUG_CODE(DCE2_DEBUG__MAIN, DCE2_PrintPktData(rpkt->payload, rpkt->payload_size);); (void)DCE2_SmbProcessRequestData(ssd, fid, data_ptr, data_len, 0); DCE2_SmbReturnRpkt(); DCE2_BufferEmpty(ftracker->fp_writex_raw->buf); } } else { (void)DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, 0); } // Windows doesn't process chained commands to raw WriteAndXs // so return error so it exits the loop. return DCE2_RET__ERROR; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: case DCE2_POLICY__SAMBA_3_0_22: case DCE2_POLICY__SAMBA_3_0_20: // All Samba cares about is skipping the 2 byte "length" // if both flags are set. break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } return DCE2_SmbProcessRequestData(ssd, fid, nb_ptr, dcnt, 0); } // TRANS2_OPEN2 static inline DCE2_Ret DCE2_SmbTrans2Open2Req(DCE2_SmbSsnData *ssd, const uint8_t *param_ptr, uint32_t param_len, bool unicode) { if (param_len < sizeof(SmbTrans2Open2ReqParams)) return DCE2_RET__ERROR; if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { uint16_t file_attrs = SmbTrans2Open2ReqFileAttrs((SmbTrans2Open2ReqParams *)param_ptr); if (SmbEvasiveFileAttrs(file_attrs)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS); ssd->cur_rtracker->file_size = SmbTrans2Open2ReqAllocSize((SmbTrans2Open2ReqParams *)param_ptr); } DCE2_MOVE(param_ptr, param_len, sizeof(SmbTrans2Open2ReqParams)); ssd->cur_rtracker->file_name = DCE2_SmbGetString(param_ptr, param_len, unicode, &ssd->cur_rtracker->file_name_len); return DCE2_RET__SUCCESS; } // TRANS2_QUERY_FILE_INFORMATION static inline DCE2_Ret DCE2_SmbTrans2QueryFileInfoReq(DCE2_SmbSsnData *ssd, const uint8_t *param_ptr, uint32_t param_len) { DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; DCE2_SmbFileTracker *ftracker; if (param_len < sizeof(SmbTrans2QueryFileInfoReqParams)) return DCE2_RET__ERROR; ftracker = DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbTrans2QueryFileInfoReqFid((SmbTrans2QueryFileInfoReqParams *)param_ptr)); if ((ftracker == NULL) || ftracker->is_ipc || DCE2_SmbFileUpload(ftracker->ff_file_direction)) return DCE2_RET__IGNORE; ttracker->info_level = SmbTrans2QueryFileInfoReqInfoLevel((SmbTrans2QueryFileInfoReqParams *)param_ptr); ssd->cur_rtracker->ftracker = ftracker; return DCE2_RET__SUCCESS; } // TRANS2_SET_FILE_INFORMATION static inline DCE2_Ret DCE2_SmbTrans2SetFileInfoReq(DCE2_SmbSsnData *ssd, const uint8_t *param_ptr, uint32_t param_len, const uint8_t *data_ptr, uint32_t data_len) { DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; DCE2_SmbFileTracker *ftracker; if ((param_len < sizeof(SmbTrans2SetFileInfoReqParams)) || (data_len < sizeof(uint64_t))) return DCE2_RET__ERROR; ttracker->info_level = SmbTrans2SetFileInfoReqInfoLevel((SmbTrans2SetFileInfoReqParams *)param_ptr); // Check to see if there is an attempt to set READONLY/HIDDEN/SYSTEM // attributes on a file if (SmbSetFileInfoSetFileBasicInfo(ttracker->info_level) && (data_len >= sizeof(SmbSetFileBasicInfo))) { uint32_t ext_file_attrs = SmbSetFileInfoExtFileAttrs((SmbSetFileBasicInfo *)data_ptr); if (SmbEvasiveFileAttrs(ext_file_attrs)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS); // Don't need to see the response return DCE2_RET__IGNORE; } // Only looking for end of file information for this subcommand if (!SmbSetFileInfoEndOfFile(ttracker->info_level)) return DCE2_RET__IGNORE; ftracker = DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbTrans2SetFileInfoReqFid((SmbTrans2SetFileInfoReqParams *)param_ptr)); if ((ftracker == NULL) || ftracker->is_ipc || DCE2_SmbFileDownload(ftracker->ff_file_direction) || (ftracker->ff_bytes_processed != 0)) return DCE2_RET__IGNORE; ssd->cur_rtracker->file_size = SmbNtohq((uint64_t *)data_ptr); ssd->cur_rtracker->ftracker = ftracker; return DCE2_RET__SUCCESS; } // SMB_COM_TRANSACTION2 static DCE2_Ret DCE2_SmbTransaction2(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; // Got a matching request for an in progress transaction - don't process it, // but don't want to remove tracker. if (DCE2_ComInfoIsRequest(com_info) && !DCE2_SmbIsTransactionComplete(ttracker)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Got new transaction request " "that matches an in progress transaction - not inspecting.\n")); return DCE2_RET__ERROR; } if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; // Interim response is sent if client didn't send all data / parameters // in initial Transaction2 request and will have to complete the request // with Transaction2Secondary commands. if (DCE2_ComInfoIsResponse(com_info) && (com_size == sizeof(SmbTransaction2InterimResp))) { return DCE2_RET__SUCCESS; } if (DCE2_ComInfoIsRequest(com_info)) { uint16_t pcnt = SmbTransaction2ReqParamCnt((SmbTransaction2Req *)nb_ptr); uint16_t poff = SmbTransaction2ReqParamOff((SmbTransaction2Req *)nb_ptr); uint16_t dcnt = SmbTransaction2ReqDataCnt((SmbTransaction2Req *)nb_ptr); uint16_t doff = SmbTransaction2ReqDataOff((SmbTransaction2Req *)nb_ptr); const uint8_t *data_ptr; DCE2_Ret status = DCE2_SmbUpdateTransRequest(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); switch (ttracker->subcom) { case TRANS2_OPEN2: if (DCE2_SmbTrans2Open2Req(ssd, nb_ptr, pcnt, SmbUnicode(smb_hdr)) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; break; case TRANS2_QUERY_FILE_INFORMATION: status = DCE2_SmbTrans2QueryFileInfoReq(ssd, nb_ptr, pcnt); if (status != DCE2_RET__SUCCESS) return status; break; case TRANS2_SET_FILE_INFORMATION: data_ptr = nb_ptr; DCE2_MOVE(data_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - data_ptr); status = DCE2_SmbTrans2SetFileInfoReq(ssd, nb_ptr, pcnt, data_ptr, dcnt); if (status != DCE2_RET__SUCCESS) return status; break; default: return DCE2_RET__IGNORE; } } else { const uint8_t *ptr; uint32_t len; DCE2_SmbFileTracker *ftracker = NULL; DCE2_Ret status = DCE2_SmbUpdateTransResponse(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; switch (ttracker->subcom) { case TRANS2_OPEN2: if (!DCE2_BufferIsEmpty(ttracker->pbuf)) { ptr = DCE2_BufferData(ttracker->pbuf); len = DCE2_BufferLength(ttracker->pbuf); } else { uint16_t poff = SmbTransaction2RespParamOff((SmbTransaction2Resp *)nb_ptr); uint16_t pcnt = SmbTransaction2RespParamCnt((SmbTransaction2Resp *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); ptr = nb_ptr; len = pcnt; } if (len < sizeof(SmbTrans2Open2RespParams)) return DCE2_RET__ERROR; if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid) && (SmbFileAttrsDirectory(SmbTrans2Open2RespFileAttrs( (SmbTrans2Open2RespParams *)ptr)) || !SmbResourceTypeDisk(SmbTrans2Open2RespResourceType( (SmbTrans2Open2RespParams *)ptr)))) { return DCE2_RET__SUCCESS; } ftracker = DCE2_SmbNewFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbTrans2Open2RespFid((SmbTrans2Open2RespParams *)ptr)); if (ftracker == NULL) return DCE2_RET__ERROR; DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker); if (!ftracker->is_ipc) { uint16_t open_results = SmbTrans2Open2RespActionTaken((SmbTrans2Open2RespParams *)ptr); if (SmbOpenResultRead(open_results)) { ftracker->ff_file_size = SmbTrans2Open2RespFileDataSize((SmbTrans2Open2RespParams *)ptr); } else { ftracker->ff_file_size = ssd->cur_rtracker->file_size; ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; } } break; case TRANS2_QUERY_FILE_INFORMATION: ftracker = ssd->cur_rtracker->ftracker; if (ftracker == NULL) return DCE2_RET__ERROR; if (!DCE2_BufferIsEmpty(ttracker->dbuf)) { ptr = DCE2_BufferData(ttracker->dbuf); len = DCE2_BufferLength(ttracker->dbuf); } else { uint16_t doff = SmbTransaction2RespDataOff((SmbTransaction2Resp *)nb_ptr); uint16_t dcnt = SmbTransaction2RespDataCnt((SmbTransaction2Resp *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); ptr = nb_ptr; len = dcnt; } switch (ttracker->info_level) { case SMB_INFO_STANDARD: if (len >= sizeof(SmbQueryInfoStandard)) { ftracker->ff_file_size = SmbQueryInfoStandardFileDataSize((SmbQueryInfoStandard *)ptr); } break; case SMB_INFO_QUERY_EA_SIZE: if (len >= sizeof(SmbQueryInfoQueryEaSize)) { ftracker->ff_file_size = SmbQueryInfoQueryEaSizeFileDataSize((SmbQueryInfoQueryEaSize *)ptr); } break; case SMB_QUERY_FILE_STANDARD_INFO: if (len >= sizeof(SmbQueryFileStandardInfo)) { ftracker->ff_file_size = SmbQueryFileStandardInfoEndOfFile((SmbQueryFileStandardInfo *)ptr); } break; case SMB_QUERY_FILE_ALL_INFO: if (len >= sizeof(SmbQueryFileAllInfo)) { ftracker->ff_file_size = SmbQueryFileAllInfoEndOfFile((SmbQueryFileAllInfo *)ptr); } break; case SMB_INFO_PT_FILE_STANDARD_INFO: if (len >= sizeof(SmbQueryPTFileStreamInfo)) { ftracker->ff_file_size = SmbQueryPTFileStreamInfoStreamSize((SmbQueryPTFileStreamInfo *)ptr); } break; case SMB_INFO_PT_FILE_STREAM_INFO: if (len >= sizeof(SmbQueryFileStandardInfo)) { ftracker->ff_file_size = SmbQueryFileStandardInfoEndOfFile((SmbQueryFileStandardInfo *)ptr); } break; case SMB_INFO_PT_FILE_ALL_INFO: if (len >= sizeof(SmbQueryPTFileAllInfo)) { ftracker->ff_file_size = SmbQueryPTFileAllInfoEndOfFile((SmbQueryPTFileAllInfo *)ptr); } break; case SMB_INFO_PT_NETWORK_OPEN_INFO: if (len >= sizeof(SmbQueryPTNetworkOpenInfo)) { ftracker->ff_file_size = SmbQueryPTNetworkOpenInfoEndOfFile((SmbQueryPTNetworkOpenInfo *)ptr); } break; default: break; } break; case TRANS2_SET_FILE_INFORMATION: ftracker = ssd->cur_rtracker->ftracker; if (ftracker == NULL) return DCE2_RET__ERROR; if (!DCE2_BufferIsEmpty(ttracker->pbuf)) { ptr = DCE2_BufferData(ttracker->pbuf); len = DCE2_BufferLength(ttracker->pbuf); } else { uint16_t poff = SmbTransaction2RespParamOff((SmbTransaction2Resp *)nb_ptr); uint16_t pcnt = SmbTransaction2RespParamCnt((SmbTransaction2Resp *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); ptr = nb_ptr; len = pcnt; } // *ptr will be non-zero if there was an error. if ((len >= 2) && (*ptr == 0)) ftracker->ff_file_size = ssd->cur_rtracker->file_size; break; default: break; } } return DCE2_RET__SUCCESS; } // SMB_COM_TRANSACTION2_SECONDARY static DCE2_Ret DCE2_SmbTransaction2Secondary(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { DCE2_Ret status; DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; status = DCE2_SmbUpdateTransSecondary(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; switch (ttracker->subcom) { case TRANS2_OPEN2: status = DCE2_SmbTrans2Open2Req(ssd, DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf), SmbUnicode(smb_hdr)); if (status != DCE2_RET__SUCCESS) return status; break; case TRANS2_QUERY_FILE_INFORMATION: status = DCE2_SmbTrans2QueryFileInfoReq(ssd, DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf)); if (status != DCE2_RET__SUCCESS) return status; break; case TRANS2_SET_FILE_INFORMATION: status = DCE2_SmbTrans2SetFileInfoReq(ssd, DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf), DCE2_BufferData(ttracker->dbuf), DCE2_BufferLength(ttracker->dbuf)); if (status != DCE2_RET__SUCCESS) return status; break; default: break; } return DCE2_RET__SUCCESS; } #define SHARE_0 (0) #define SHARE_FS (SHARE_0+5) #define SHARE_IPC (SHARE_FS+1) static DCE2_SmbFsm dce2_ipc_share_fsm[] = { {'I' , SHARE_0+1, SHARE_FS }, {'P' , SHARE_0+2, SHARE_FS }, {'C' , SHARE_0+3, SHARE_FS }, {'$' , SHARE_0+4, SHARE_FS }, {'\0', SHARE_IPC, SHARE_FS }, {0, SHARE_FS, SHARE_FS } }; // SMB_COM_TREE_CONNECT static DCE2_Ret DCE2_SmbTreeConnect(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); const uint8_t *bs = NULL; bool unicode = SmbUnicode(smb_hdr); uint8_t increment = unicode ? 2 : 1; int state = SHARE_0; bool is_ipc = false; // Have at least 4 bytes of data based on byte count check done earlier DCE2_MOVE(nb_ptr, nb_len, com_size); // If unicode flag is set, strings, except possibly the service string // are going to be unicode. The NT spec specifies that unicode strings // must be word aligned with respect to the beginning of the SMB and that for // type-prefixed strings (this case), the padding byte is found after the // type format byte. // This byte will realign things. if (*nb_ptr != SMB_FMT__ASCII) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, *nb_ptr); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, 1); // IPC$ does not need to be case sensitive. And the case sensitivity flag in // the SMB header doesn't seem to have any effect on this. while ((bs = memchr(nb_ptr, '\\', nb_len)) != NULL) DCE2_MOVE(nb_ptr, nb_len, (bs - nb_ptr) + 1); if (unicode && (nb_len > 0)) DCE2_MOVE(nb_ptr, nb_len, 1); // Check for invalid shares first if ((DCE2_ScSmbInvalidShares(ssd->sd.sconfig) != NULL) && (nb_len > 0)) DCE2_SmbInvalidShareCheck(ssd, smb_hdr, nb_ptr, nb_len); while ((nb_len >= increment) && (state < SHARE_FS)) { if (dce2_ipc_share_fsm[state].input == toupper((int)nb_ptr[0])) { if (unicode && (nb_ptr[1] != 0)) break; state = dce2_ipc_share_fsm[state].next_state; DCE2_MOVE(nb_ptr, nb_len, increment); } else { state = dce2_ipc_share_fsm[state].fail_state; } } switch (state) { case SHARE_IPC: is_ipc = true; break; case SHARE_FS: default: break; } ssd->cur_rtracker->is_ipc = is_ipc; } else { // XXX What if the TID in the SMB header differs from that returned // in the TreeConnect command response? uint16_t tid = SmbTid(smb_hdr); DCE2_SmbInsertTid(ssd, tid, ssd->cur_rtracker->is_ipc); DCE2_DEBUG_CODE(DCE2_DEBUG__SMB, if (ssd->cur_rtracker->is_ipc) printf("Tid (%u) is an IPC tree\n", tid); else printf("Tid (%u) not an IPC tree\n", tid);); } return DCE2_RET__SUCCESS; } // SMB_COM_TREE_DISCONNECT static DCE2_Ret DCE2_SmbTreeDisconnect(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsResponse(com_info)) DCE2_SmbRemoveTid(ssd, ssd->cur_rtracker->tid); return DCE2_RET__SUCCESS; } // SMB_COM_NEGOTIATE static DCE2_Ret DCE2_SmbNegotiate(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); PROFILE_VARS; if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; PREPROC_PROFILE_START(dce2_pstat_smb_negotiate); if (DCE2_ComInfoIsRequest(com_info)) { // Have at least 2 bytes based on byte count check done earlier uint8_t *term_ptr; int ntlm_index = 0; DCE2_MOVE(nb_ptr, nb_len, com_size); while ((term_ptr = memchr(nb_ptr, '\0', nb_len)) != NULL) { if (!SmbFmtDialect(*nb_ptr)) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_BAD_FORMAT, *nb_ptr); // Windows errors if bad format if (DCE2_SsnIsWindowsPolicy(&ssd->sd)) { PREPROC_PROFILE_END(dce2_pstat_smb_negotiate); return DCE2_RET__ERROR; } } // Move past format DCE2_MOVE(nb_ptr, nb_len, 1); if (nb_len == 0) break; // Just a NULL byte - acceptable by Samba and Windows if (term_ptr == nb_ptr) continue; if ((*nb_ptr == 'N') && (strncmp((const char *)nb_ptr, SMB_DIALECT_NT_LM_012, term_ptr - nb_ptr) == 0)) break; // Move past string and NULL byte DCE2_MOVE(nb_ptr, nb_len, (term_ptr - nb_ptr) + 1); ntlm_index++; } if (term_ptr != NULL) { ssd->dialect_index = ntlm_index; } else { ssd->dialect_index = DCE2_SENTINEL; DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DEPR_DIALECT_NEGOTIATED); } } else { uint16_t dialect_index = SmbNegotiateRespDialectIndex((SmbCore_NegotiateProtocolResp *)nb_ptr); if ((ssd->dialect_index != DCE2_SENTINEL) && (dialect_index != ssd->dialect_index)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DEPR_DIALECT_NEGOTIATED); ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__NEGOTIATED; if (DCE2_ComInfoWordCount(com_info) == 17) { ssd->max_outstanding_requests = SmbNt_NegotiateRespMaxMultiplex((SmbNt_NegotiateProtocolResp *)nb_ptr); } else if (DCE2_ComInfoWordCount(com_info) == 13) { ssd->max_outstanding_requests = SmbLm_NegotiateRespMaxMultiplex((SmbLm10_NegotiateProtocolResp *)nb_ptr); } else { ssd->max_outstanding_requests = 1; } } PREPROC_PROFILE_END(dce2_pstat_smb_negotiate); return DCE2_RET__SUCCESS; } #define OS_0 (0) // "Windows" start #define OS_1 (OS_0+ 8) // Windows 2000 and XP server #define OS_2 (OS_1+ 4) // Windows 2000 and XP client #define OS_3 (OS_2+ 5) // "Server", 2003, 2008R2, 2008 #define OS_4 (OS_3+20) // Windows Vista #define OS_5 (OS_4 +5) // Windows 7 #define OS_6 (OS_5 +1) // Windows NT #define OS_7 (OS_6 +2) // Windows 98 #define OS_FS (OS_7+ 3) // Failure state #define OS_WIN2000 (OS_FS+1) #define OS_WINXP (OS_FS+2) #define OS_WIN2003 (OS_FS+3) #define OS_WINVISTA (OS_FS+4) #define OS_WIN2008 (OS_FS+5) #define OS_WIN7 (OS_FS+6) static DCE2_SmbFsm dce2_smb_os_fsm[] = { // Windows start states { 'W', OS_0+1, OS_FS }, { 'i', OS_0+2, OS_FS }, { 'n', OS_0+3, OS_FS }, { 'd', OS_0+4, OS_FS }, { 'o', OS_0+5, OS_FS }, { 'w', OS_0+6, OS_FS }, { 's', OS_0+7, OS_FS }, { ' ', OS_0+8, OS_FS }, // Windows 2000 and XP server states { '5', OS_1+1, OS_2 }, { '.', OS_1+2, OS_FS }, { '1', OS_WINXP, OS_1+3 }, // Windows XP { '0', OS_WIN2000, OS_FS }, // Windows 2000 // Windows 2000 or XP client states { '2', OS_2+1, OS_3 }, { '0', OS_2+2, OS_FS }, { '0', OS_2+3, OS_FS }, { '2', OS_WINXP, OS_2+4 }, // Windows XP { '0', OS_WIN2000, OS_FS }, // Windows 2000 // "Server" string states { 'S', OS_3+ 1, OS_4 }, { 'e', OS_3+ 2, OS_FS }, { 'r', OS_3+ 3, OS_FS }, { 'v', OS_3+ 4, OS_FS }, { 'e', OS_3+ 5, OS_FS }, { 'r', OS_3+ 6, OS_FS }, { ' ', OS_3+ 7, OS_FS }, { '2', OS_3+ 8, OS_3+12 }, { '0', OS_3+ 9, OS_FS }, { '0', OS_3+10, OS_FS }, { '3', OS_WIN2003, OS_3+11 }, // Windows Server 2003 { '8', OS_WIN2008, OS_FS }, // Windows Server 2008R2 // Windows 2008 has this, 2008 R2 does not { '(', OS_3+13, OS_FS }, { 'R', OS_3+14, OS_FS }, { ')', OS_3+15, OS_FS }, { ' ', OS_3+16, OS_FS }, { '2', OS_3+17, OS_FS }, { '0', OS_3+18, OS_FS }, { '0', OS_3+19, OS_FS }, { '8', OS_WIN2008, OS_FS }, // Windows Vista states { 'V', OS_4+1, OS_5 }, { 'i', OS_4+2, OS_FS }, { 's', OS_4+3, OS_FS }, { 't', OS_4+4, OS_FS }, { 'a', OS_WINVISTA, OS_FS }, // Windows 7 state { '7', OS_WIN7, OS_6 }, // Windows NT { 'N', OS_6+1, OS_7 }, { 'T', OS_WIN2000, OS_FS }, // Windows NT, set policy to Windows 2000 // Windows 98 { '4', OS_7+1, OS_FS }, { '.', OS_7+2, OS_FS }, { '0', OS_WIN2000, OS_FS }, // Windows 98, set policy to Windows 2000 // Failure state { 0, OS_FS, OS_FS } // Match states shouldn't be accessed }; // SMB_COM_SESSION_SETUP_ANDX static DCE2_Ret DCE2_SmbSessionSetupAndX(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { uint16_t max_multiplex = SmbSessionSetupAndXReqMaxMultiplex((SmbLm10_SessionSetupAndXReq *)nb_ptr); if (max_multiplex < ssd->max_outstanding_requests) ssd->max_outstanding_requests = max_multiplex; if (!DCE2_SmbFingerprintedClient(ssd) && DCE2_GcSmbFingerprintClient()) { uint8_t increment = SmbUnicode(smb_hdr) ? 2 : 1; uint16_t word_count = DCE2_ComInfoWordCount(com_info); uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint32_t i; PROFILE_VARS; DCE2_SmbSetFingerprintedClient(ssd); // OS and Lanman strings won't be in request if ((word_count != 13) && (word_count != 12)) return DCE2_RET__SUCCESS; PREPROC_PROFILE_START(dce2_pstat_smb_fingerprint); if (word_count == 13) { uint16_t oem_pass_len = SmbNt10SessionSetupAndXReqOemPassLen((SmbNt10_SessionSetupAndXReq *)nb_ptr); uint16_t uni_pass_len = SmbNt10SessionSetupAndXReqUnicodePassLen((SmbNt10_SessionSetupAndXReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, com_size); if (((uint32_t)oem_pass_len + uni_pass_len) > nb_len) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, (oem_pass_len + uni_pass_len)); // If unicode there should be a padding byte if the password // lengths are even since the command length is odd if ((increment == 2) && (nb_len != 0) && !((oem_pass_len + uni_pass_len) & 1)) DCE2_MOVE(nb_ptr, nb_len, 1); } else // Extended security blob version, word count of 12 { uint16_t blob_len = SmbSessionSetupAndXReqBlobLen((SmbNt10_SessionSetupAndXExtReq *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, com_size); if (blob_len > nb_len) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, blob_len); // If unicode there should be a padding byte if the blob // length is even since the command length is odd if ((increment == 2) && (nb_len != 0) && !(blob_len & 1)) DCE2_MOVE(nb_ptr, nb_len, 1); } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Attempting to fingerprint " "Client Windows/Samba version ... \n")); // Move past Account and Domain strings // Blob version doesn't have these as they're in the blob if (DCE2_ComInfoWordCount(com_info) == 13) { int j; for (j = 0; j < 2; j++) { while ((nb_len >= increment) && (*nb_ptr != '\0')) DCE2_MOVE(nb_ptr, nb_len, increment); // Just return success if we run out of data if (nb_len < increment) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Move past NULL string terminator DCE2_MOVE(nb_ptr, nb_len, increment); } } if (nb_len < increment) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Note the below is quick and dirty. We're assuming the client // is kosher. It's policy will be used when the server is // sending data to it. #ifdef DEBUG { uint32_t k, l = 0; char buf[65535]; for (k = 0; (k < nb_len) && (nb_ptr[k] != 0); k += increment, l++) buf[l] = nb_ptr[k]; buf[l] = 0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, " Client OS: %s\n", buf)); k += increment; l = 0; for (; k < nb_len && nb_ptr[k] != 0; k += increment, l++) buf[l] = nb_ptr[k]; buf[l] = 0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, " Client Lanman: %s\n", buf)); } #endif // Windows Vista and above don't put anything here if (*nb_ptr == '\0') { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting client policy to Windows Vista\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINVISTA); PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Windows if (*nb_ptr == 'W') { int state = OS_0; int64_t rlen = (int64_t)nb_len; while ((rlen > 0) && (state < OS_FS)) { if (dce2_smb_os_fsm[state].input == (char)*nb_ptr) { state = dce2_smb_os_fsm[state].next_state; DCE2_MOVE(nb_ptr, rlen, increment); } else { state = dce2_smb_os_fsm[state].fail_state; } } switch (state) { case OS_WIN2000: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting client policy to Windows 2000\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2000); break; case OS_WINXP: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting client policy to Windows XP\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINXP); break; case OS_WIN2003: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting client policy to Windows 2003\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2003); break; default: break; } PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Samba puts "Unix" in the OS field if (*nb_ptr != 'U') { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Move past OS string for (i = 0; (i < nb_len) && (nb_ptr[i] != '\0'); i += increment); if ((i + increment) >= nb_len) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Move to LanMan string DCE2_MOVE(nb_ptr, nb_len, i + increment); // Samba if (*nb_ptr == 'S') { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting client policy to Samba\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA); } PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); } } else { uint16_t uid = SmbUid(smb_hdr); DCE2_SmbInsertUid(ssd, uid); ssd->cur_rtracker->uid = uid; // Set this in case there are chained commands if (!(ssd->ssn_state_flags & DCE2_SMB_SSN_STATE__NEGOTIATED)) ssd->ssn_state_flags |= DCE2_SMB_SSN_STATE__NEGOTIATED; if (!DCE2_SmbFingerprintedServer(ssd) && DCE2_GcSmbFingerprintServer()) { uint8_t increment = SmbUnicode(smb_hdr) ? 2 : 1; uint32_t i; PROFILE_VARS; DCE2_SmbSetFingerprintedServer(ssd); // Set the policy based on what the server reports in the OS field // for Windows and the LanManager field for Samba if (DCE2_ComInfoByteCount(com_info) == 0) return DCE2_RET__SUCCESS; PREPROC_PROFILE_START(dce2_pstat_smb_fingerprint); if (DCE2_ComInfoWordCount(com_info) == 3) { DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); // Word count 3 and Unicode has a one byte pad if ((increment == 2) && (nb_len != 0)) DCE2_MOVE(nb_ptr, nb_len, 1); } else // Only valid word counts are 3 and 4 { uint16_t blob_len = SmbSessionSetupAndXRespBlobLen((SmbNt10_SessionSetupAndXExtResp *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); if (blob_len > nb_len) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, blob_len); if ((increment == 2) && (nb_len != 0) && !(blob_len & 1)) DCE2_MOVE(nb_ptr, nb_len, 1); } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Attempting to fingerprint " "Server Windows/Samba version ... \n")); // Note the below is quick and dirty. We're assuming the server // is kosher. It's policy will be used when the client is // sending data to it. #ifdef DEBUG { uint32_t k, l = 0; char buf[65535]; for (k = 0; (k < nb_len) && (nb_ptr[k] != 0); k += increment, l++) buf[l] = nb_ptr[k]; buf[l] = 0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, " Server OS: %s\n", buf)); k += increment; l = 0; for (; k < nb_len && nb_ptr[k] != 0; k += increment, l++) buf[l] = nb_ptr[k]; buf[l] = 0; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, " Server Lanman: %s\n", buf)); } #endif if ((nb_len < increment) || (*nb_ptr == '\0')) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Next field should be OS string for (i = 0; (i < nb_len) && (nb_ptr[i] != '\0'); i += increment); i -= increment; // Windows if (*nb_ptr == 'W') { int state = OS_0; int64_t rlen = (int64_t)nb_len; while ((rlen > 0) && (state < OS_FS)) { if (dce2_smb_os_fsm[state].input == (char)*nb_ptr) { state = dce2_smb_os_fsm[state].next_state; DCE2_MOVE(nb_ptr, rlen, increment); } else { state = dce2_smb_os_fsm[state].fail_state; } } switch (state) { case OS_WIN2000: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Windows 2000\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2000); break; case OS_WINXP: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Windows XP\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINXP); break; case OS_WIN2003: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Windows 2003\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2003); break; case OS_WIN2008: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Windows 2008\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN2008); break; case OS_WINVISTA: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Windows Vista\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WINVISTA); break; case OS_WIN7: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Windows 7\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__WIN7); break; default: break; } PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Samba puts "Unix" in the OS field if (*nb_ptr != 'U') { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Move past OS string for (i = 0; (i < nb_len) && (nb_ptr[i] != '\0'); i += increment); if ((i + increment) >= nb_len) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Move to LanMan string DCE2_MOVE(nb_ptr, nb_len, i + increment); // Samba if (*nb_ptr == 'S') { uint8_t r1 = 0; // Release version first digit uint8_t r2 = 0; // Release version second digit // Get Major version for (i = 0; (i < nb_len) && (*nb_ptr != '\0'); i += increment) { if (isdigit((int)nb_ptr[i])) break; } if ((i == nb_len) || (*nb_ptr == '\0')) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // If less than 3 set policy to earliest Samba policy we use if ((nb_ptr[i] == '0') || (nb_ptr[i] == '1') || (nb_ptr[i] == '2')) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Samba 3.0.20\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_20); PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // Need ".\d.\d\d" or ".\d.\d\x00" if (i + increment*5 > nb_len) { PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } i += increment*2; // If it's not 0, then set to latest Samba policy we use if (nb_ptr[i] != '0') { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to current Samba\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA); PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } r1 = nb_ptr[i + increment*2]; r2 = nb_ptr[i + increment*3]; // First digit is 1 or no second digit or 20, Samba 3.0.20 if ((r1 == '1') || (r2 == '\0') || ((r1 == '2') && (r2 == '0'))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Samba 3.0.20\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_20); PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // 21 or 22, Samba 3.0.22 if ((r1 == '2') && (r2 <= '2')) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Samba 3.0.22\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_22); PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } // 23, 24 ... 30 ... 37, Samba 3.0.37 if ((r1 == '2') || ((r1 == '3') && (r2 <= '7'))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to Samba 3.0.37\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA_3_0_37); PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); return DCE2_RET__SUCCESS; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Setting server policy to current Samba\n")); DCE2_SsnSetPolicy(&ssd->sd, DCE2_POLICY__SAMBA); } PREPROC_PROFILE_END(dce2_pstat_smb_fingerprint); } } return DCE2_RET__SUCCESS; } // SMB_COM_LOGOFF_ANDX static DCE2_Ret DCE2_SmbLogoffAndX(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsResponse(com_info)) { DCE2_SmbRemoveUid(ssd, ssd->cur_rtracker->uid); switch (DCE2_SsnGetServerPolicy(&ssd->sd)) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: /* Windows responds to a chained LogoffAndX => SessionSetupAndX with a * word count 3 LogoffAndX without the chained SessionSetupAndX */ if (DCE2_ComInfoWordCount(com_info) == 3) { uint16_t uid = SmbUid(smb_hdr); DCE2_SmbInsertUid(ssd, uid); ssd->cur_rtracker->uid = uid; // Set this in case there are chained commands } break; default: break; } } return DCE2_RET__SUCCESS; } #define SERVICE_0 (0) // IPC start #define SERVICE_1 (SERVICE_0+4) // DISK start #define SERVICE_FS (SERVICE_1+3) // Failure #define SERVICE_IPC (SERVICE_FS+1) // IPC service #define SERVICE_DISK (SERVICE_FS+2) // DISK service static DCE2_SmbFsm dce2_smb_service_fsm[] = { // IPC { 'I', SERVICE_0+1, SERVICE_1 }, { 'P', SERVICE_0+2, SERVICE_FS }, { 'C', SERVICE_0+3, SERVICE_FS }, { '\0', SERVICE_IPC, SERVICE_FS }, // DISK { 'A', SERVICE_1+1, SERVICE_FS }, { ':', SERVICE_1+2, SERVICE_FS }, { '\0', SERVICE_DISK, SERVICE_FS }, { 0, SERVICE_FS, SERVICE_FS } }; // SMB_COM_TREE_CONNECT_ANDX static DCE2_Ret DCE2_SmbTreeConnectAndX(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsRequest(com_info)) { if (DCE2_ScSmbInvalidShares(ssd->sd.sconfig) != NULL) { uint16_t pass_len = SmbTreeConnectAndXReqPassLen((SmbTreeConnectAndXReq *)nb_ptr); const uint8_t *bs = NULL; DCE2_MOVE(nb_ptr, nb_len, com_size); if (pass_len >= nb_len) return DCE2_RET__ERROR; // Move past password length DCE2_MOVE(nb_ptr, nb_len, pass_len); // Move past path components while ((bs = memchr(nb_ptr, '\\', nb_len)) != NULL) DCE2_MOVE(nb_ptr, nb_len, (bs - nb_ptr) + 1); // Move past NULL byte if unicode if (SmbUnicode(smb_hdr) && (nb_len != 0)) DCE2_MOVE(nb_ptr, nb_len, 1); if (nb_len != 0) DCE2_SmbInvalidShareCheck(ssd, smb_hdr, nb_ptr, nb_len); } } else { uint16_t tid = SmbTid(smb_hdr); bool is_ipc = true; int state = SERVICE_0; DCE2_MOVE(nb_ptr, nb_len, com_size); while ((nb_len > 0) && (state < SERVICE_FS)) { if (dce2_smb_service_fsm[state].input == (char)*nb_ptr) { state = dce2_smb_service_fsm[state].next_state; DCE2_MOVE(nb_ptr, nb_len, 1); } else { state = dce2_smb_service_fsm[state].fail_state; } } switch (state) { case SERVICE_IPC: DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Tid (%u) is an IPC tree.\n", tid);); break; case SERVICE_DISK: is_ipc = false; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Tid (%u) is a DISK tree.\n", tid);); break; default: return DCE2_RET__IGNORE; } // Insert tid into list DCE2_SmbInsertTid(ssd, tid, is_ipc); ssd->cur_rtracker->tid = tid; // Set this in case there are chained commands } return DCE2_RET__SUCCESS; } // NT_TRANSACT_CREATE static inline DCE2_Ret DCE2_SmbNtTransactCreateReq(DCE2_SmbSsnData *ssd, const uint8_t *param_ptr, uint32_t param_len, bool unicode) { uint32_t pad = 0; uint32_t file_name_length; const uint8_t *param_start = param_ptr; if (param_len < sizeof(SmbNtTransactCreateReqParams)) return DCE2_RET__ERROR; if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { uint32_t ext_file_attrs = SmbNtTransactCreateReqFileAttrs((SmbNtTransactCreateReqParams *)param_ptr); if (SmbEvasiveFileAttrs(ext_file_attrs)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS); // If the file is going to be accessed sequentially, track it. if (SmbNtTransactCreateReqSequentialOnly((SmbNtTransactCreateReqParams *)param_ptr)) ssd->cur_rtracker->sequential_only = true; ssd->cur_rtracker->file_size = SmbNtTransactCreateReqAllocSize((SmbNtTransactCreateReqParams *)param_ptr); } file_name_length = SmbNtTransactCreateReqFileNameLength((SmbNtTransactCreateReqParams *)param_ptr); if (file_name_length > DCE2_SMB_MAX_PATH_LEN) return DCE2_RET__ERROR; DCE2_MOVE(param_ptr, param_len, sizeof(SmbNtTransactCreateReqParams)); if (unicode) pad = (param_ptr - param_start) & 1; if (param_len < (pad + file_name_length)) return DCE2_RET__ERROR; DCE2_MOVE(param_ptr, param_len, pad); ssd->cur_rtracker->file_name = DCE2_SmbGetString(param_ptr, file_name_length, unicode, &ssd->cur_rtracker->file_name_len); return DCE2_RET__SUCCESS; } // SMB_COM_NT_TRANSACT static DCE2_Ret DCE2_SmbNtTransact(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; // NOTE: Only looking at NT_TRANSACT_CREATE as another way to open a named pipe // Got a matching request for an in progress transaction - don't process it, // but don't want to remove tracker. if (DCE2_ComInfoIsRequest(com_info) && !DCE2_SmbIsTransactionComplete(ttracker)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Got new transaction request " "that matches an in progress transaction - not inspecting.\n")); return DCE2_RET__ERROR; } if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; // Interim response is sent if client didn't send all data / parameters // in initial NtTransact request and will have to complete the request // with NtTransactSecondary commands. if (DCE2_ComInfoIsResponse(com_info) && (com_size == sizeof(SmbNtTransactInterimResp))) { return DCE2_RET__SUCCESS; } if (DCE2_ComInfoIsRequest(com_info)) { uint32_t pcnt = SmbNtTransactReqParamCnt((SmbNtTransactReq *)nb_ptr); uint32_t poff = SmbNtTransactReqParamOff((SmbNtTransactReq *)nb_ptr); DCE2_Ret status = DCE2_SmbUpdateTransRequest(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); switch (ttracker->subcom) { case NT_TRANSACT_CREATE: status = DCE2_SmbNtTransactCreateReq(ssd, nb_ptr, pcnt, SmbUnicode(smb_hdr)); if (status != DCE2_RET__SUCCESS) return status; break; default: return DCE2_RET__IGNORE; } } else { const uint8_t *ptr; uint32_t len; DCE2_SmbFileTracker *ftracker = NULL; DCE2_Ret status = DCE2_SmbUpdateTransResponse(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; if (!DCE2_BufferIsEmpty(ttracker->pbuf)) { ptr = DCE2_BufferData(ttracker->pbuf); len = DCE2_BufferLength(ttracker->pbuf); } else { uint32_t poff = SmbNtTransactRespParamOff((SmbNtTransactResp *)nb_ptr); uint32_t pcnt = SmbNtTransactRespParamCnt((SmbNtTransactResp *)nb_ptr); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); ptr = nb_ptr; len = pcnt; } if (len < sizeof(SmbNtTransactCreateRespParams)) return DCE2_RET__ERROR; if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { const bool is_directory = SmbNtTransactCreateRespDirectory((SmbNtTransactCreateRespParams *)ptr); const uint16_t resource_type = SmbNtTransactCreateRespResourceType((SmbNtTransactCreateRespParams *)ptr); if (is_directory || !SmbResourceTypeDisk(resource_type)) return DCE2_RET__SUCCESS; // Give preference to files opened with the sequential only flag set if (((ssd->fapi_ftracker == NULL) || !ssd->fapi_ftracker->ff_sequential_only) && ssd->cur_rtracker->sequential_only) { DCE2_SmbAbortFileAPI(ssd); } } ftracker = DCE2_SmbNewFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, SmbNtTransactCreateRespFid((SmbNtTransactCreateRespParams *)ptr)); if (ftracker == NULL) return DCE2_RET__ERROR; DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker); if (!ftracker->is_ipc) { uint32_t create_disposition = SmbNtTransactCreateRespCreateAction((SmbNtTransactCreateRespParams *)ptr); if (SmbCreateDispositionRead(create_disposition)) { ftracker->ff_file_size = SmbNtTransactCreateRespEndOfFile((SmbNtTransactCreateRespParams *)ptr); } else { ftracker->ff_file_size = ssd->cur_rtracker->file_size; ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; } ftracker->ff_sequential_only = ssd->cur_rtracker->sequential_only; DCE2_DEBUG_CODE(DCE2_DEBUG__SMB, if (ftracker->ff_sequential_only) printf("File opened for sequential only access.\n");); } } return DCE2_RET__SUCCESS; } // SMB_COM_NT_TRANSACT_SECONDARY static DCE2_Ret DCE2_SmbNtTransactSecondary(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { DCE2_Ret status; DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; status = DCE2_SmbUpdateTransSecondary(ssd, smb_hdr, com_info, nb_ptr, nb_len); if (status != DCE2_RET__FULL) return status; switch (ttracker->subcom) { case NT_TRANSACT_CREATE: status = DCE2_SmbNtTransactCreateReq(ssd, DCE2_BufferData(ttracker->pbuf), DCE2_BufferLength(ttracker->pbuf), SmbUnicode(smb_hdr)); if (status != DCE2_RET__SUCCESS) return status; break; default: break; } return DCE2_RET__SUCCESS; } // SMB_COM_NT_CREATE_ANDX static DCE2_Ret DCE2_SmbNtCreateAndX(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { if (!DCE2_ComInfoCanProcessCommand(com_info)) return DCE2_RET__ERROR; if (DCE2_ComInfoIsResponse(com_info)) { const uint16_t fid = SmbNtCreateAndXRespFid((SmbNtCreateAndXResp *)nb_ptr); DCE2_SmbFileTracker *ftracker = NULL; // Set request tracker's current file tracker in case of chained commands switch (SmbAndXCom2((SmbAndXCommon *)nb_ptr)) { // This is in case in the request a write was chained to an open // in which case the write will be to the newly opened file case SMB_COM_WRITE: case SMB_COM_WRITE_ANDX: case SMB_COM_TRANSACTION: case SMB_COM_READ_ANDX: ftracker = DCE2_SmbDequeueTmpFileTracker(ssd, ssd->cur_rtracker, fid); break; default: break; } if (!DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid)) { const bool is_directory = SmbNtCreateAndXRespDirectory((SmbNtCreateAndXResp *)nb_ptr); const uint16_t resource_type = SmbNtCreateAndXRespResourceType((SmbNtCreateAndXResp *)nb_ptr); if (is_directory || !SmbResourceTypeDisk(resource_type)) { if (ftracker != NULL) DCE2_SmbRemoveFileTracker(ssd, ftracker); return DCE2_RET__SUCCESS; } // Give preference to files opened with the sequential only flag set if (((ssd->fapi_ftracker == NULL) || !ssd->fapi_ftracker->ff_sequential_only) && (ftracker == NULL) && ssd->cur_rtracker->sequential_only) { DCE2_SmbAbortFileAPI(ssd); } } if (ftracker == NULL) { ftracker = DCE2_SmbNewFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, fid); if (ftracker == NULL) return DCE2_RET__ERROR; } DCE2_Update_Ftracker_from_ReqTracker(ftracker, ssd->cur_rtracker); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "File name: %s\n", (ftracker->file_name == NULL) ? "NULL" : ftracker->file_name)); if (!ftracker->is_ipc) { const uint32_t create_disposition = SmbNtCreateAndXRespCreateDisposition((SmbNtCreateAndXResp *)nb_ptr); if (SmbCreateDispositionRead(create_disposition)) { ftracker->ff_file_size = SmbNtCreateAndXRespEndOfFile((SmbNtCreateAndXResp *)nb_ptr); } else { ftracker->ff_file_size = ssd->cur_rtracker->file_size; ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UPLOAD; } ftracker->ff_sequential_only = ssd->cur_rtracker->sequential_only; DCE2_DEBUG_CODE(DCE2_DEBUG__SMB, if (ftracker->ff_sequential_only) printf("File opened for sequential only access.\n");); } ssd->cur_rtracker->ftracker = ftracker; } else { uint32_t pad = 0; uint16_t file_name_length = SmbNtCreateAndXReqFileNameLen((SmbNtCreateAndXReq *)nb_ptr); const bool unicode = SmbUnicode(smb_hdr); bool is_ipc = DCE2_SmbIsTidIPC(ssd, ssd->cur_rtracker->tid); uint8_t smb_com2 = SmbAndXCom2((SmbAndXCommon *)nb_ptr); if (!is_ipc) { uint32_t ext_file_attrs = SmbNtCreateAndXReqFileAttrs((SmbNtCreateAndXReq *)nb_ptr); if (SmbEvasiveFileAttrs(ext_file_attrs)) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_EVASIVE_FILE_ATTRS); // If the file is going to be accessed sequentially, track it. if (SmbNtCreateAndXReqSequentialOnly((SmbNtCreateAndXReq *)nb_ptr)) ssd->cur_rtracker->sequential_only = true; ssd->cur_rtracker->file_size = SmbNtCreateAndXReqAllocSize((SmbNtCreateAndXReq *)nb_ptr); } DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); if (file_name_length > DCE2_SMB_MAX_PATH_LEN) return DCE2_RET__ERROR; if (unicode) pad = (nb_ptr - (const uint8_t *)smb_hdr) & 1; if (nb_len < (pad + file_name_length)) return DCE2_RET__ERROR; DCE2_MOVE(nb_ptr, nb_len, pad); // Samba allows chaining OpenAndX/NtCreateAndX so might have // already been set. if (ssd->cur_rtracker->file_name == NULL) { ssd->cur_rtracker->file_name = DCE2_SmbGetString(nb_ptr, file_name_length, unicode, &ssd->cur_rtracker->file_name_len); } if (is_ipc) { switch (smb_com2) { case SMB_COM_READ_ANDX: if (DCE2_SsnIsWindowsPolicy(&ssd->sd)) return DCE2_RET__ERROR; break; default: break; } } } return DCE2_RET__SUCCESS; } static inline DCE2_Ret DCE2_SmbProcessRequestData(DCE2_SmbSsnData *ssd, const uint16_t fid, const uint8_t *data_ptr, uint32_t data_len, uint64_t offset) { DCE2_SmbFileTracker *ftracker = DCE2_SmbGetFileTracker(ssd, fid); if (ftracker == NULL) return DCE2_RET__ERROR; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing request data with Fid: 0x%04X ~~~~~~~~~~~~~~~~~\n", ftracker->fid_v1)); // Set this in case of chained commands or reassembled packet ssd->cur_rtracker->ftracker = ftracker; DCE2_SmbSetFileName(ftracker->file_name, ftracker->file_name_len); if (ftracker->is_ipc) { // Maximum possible fragment length is 16 bit if (data_len > UINT16_MAX) data_len = UINT16_MAX; DCE2_CoProcess(&ssd->sd, ftracker->fp_co_tracker, data_ptr, (uint16_t)data_len); if (!ftracker->fp_used) ftracker->fp_used = true; } else { ftracker->ff_file_offset = offset; DCE2_SmbProcessFileData(ssd, ftracker, data_ptr, data_len, true); } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")); return DCE2_RET__SUCCESS; } static inline DCE2_Ret DCE2_SmbProcessResponseData(DCE2_SmbSsnData *ssd, const uint8_t *data_ptr, uint32_t data_len) { DCE2_SmbFileTracker *ftracker = ssd->cur_rtracker->ftracker; if (ftracker == NULL) return DCE2_RET__ERROR; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Processing response data with Fid: 0x%04X ~~~~~~~~~~~~~~~~\n", ftracker->fid_v1)); DCE2_SmbSetFileName(ftracker->file_name, ftracker->file_name_len); if (ftracker->is_ipc) { // Maximum possible fragment length is 16 bit if (data_len > UINT16_MAX) data_len = UINT16_MAX; DCE2_CoProcess(&ssd->sd, ftracker->fp_co_tracker, data_ptr, (uint16_t)data_len); } else { ftracker->ff_file_offset = ssd->cur_rtracker->file_offset; DCE2_SmbProcessFileData(ssd, ftracker, data_ptr, data_len, false); } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n")); return DCE2_RET__SUCCESS; } static inline DCE2_SmbRequestTracker * DCE2_SmbNewRequestTracker(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr) { DCE2_SmbRequestTracker *rtracker = NULL; DCE2_SmbRequestTracker *tmp_rtracker = NULL; uint16_t pid = SmbPid(smb_hdr); uint16_t mid = SmbMid(smb_hdr); uint16_t uid = SmbUid(smb_hdr); uint16_t tid = SmbTid(smb_hdr); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_req); if (ssd == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_req); return NULL; } if (ssd->outstanding_requests >= ssd->max_outstanding_requests) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_MAX_REQS_EXCEEDED, ssd->max_outstanding_requests); } // Check for outstanding requests with the same MID tmp_rtracker = &ssd->rtracker; while ((tmp_rtracker != NULL) && (tmp_rtracker->mid != DCE2_SENTINEL)) { if (tmp_rtracker->mid == (int)mid) { // Have yet to see an MID repeatedly used so shouldn't // be any outstanding requests with the same MID. DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_REQS_SAME_MID); break; } // Look at the next request in the queue if (tmp_rtracker == &ssd->rtracker) tmp_rtracker = DCE2_QueueFirst(ssd->rtrackers); else tmp_rtracker = DCE2_QueueNext(ssd->rtrackers); } if (ssd->rtracker.mid == DCE2_SENTINEL) { rtracker = &ssd->rtracker; } else { if (ssd->rtrackers == NULL) { ssd->rtrackers = DCE2_QueueNew(DCE2_SmbRequestTrackerDataFree, DCE2_MEM_TYPE__SMB_REQ); if (ssd->rtrackers == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_req); return NULL; } } rtracker = (DCE2_SmbRequestTracker *)DCE2_Alloc(sizeof(DCE2_SmbRequestTracker), DCE2_MEM_TYPE__SMB_REQ); if (rtracker == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_req); return NULL; } if (DCE2_QueueEnqueue(ssd->rtrackers, (void *)rtracker) != DCE2_RET__SUCCESS) { DCE2_Free((void *)rtracker, sizeof(DCE2_SmbRequestTracker), DCE2_MEM_TYPE__SMB_REQ); PREPROC_PROFILE_END(dce2_pstat_smb_req); return NULL; } } rtracker->smb_com = SmbCom(smb_hdr); rtracker->uid = uid; rtracker->tid = tid; rtracker->pid = pid; rtracker->mid = (int)mid; memset(&rtracker->ttracker, 0, sizeof(rtracker->ttracker)); rtracker->ftracker = NULL; rtracker->sequential_only = false; ssd->outstanding_requests++; if (ssd->outstanding_requests > dce2_stats.smb_max_outstanding_requests) dce2_stats.smb_max_outstanding_requests = ssd->outstanding_requests; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Added new request tracker => " "Uid: %u, Tid: %u, Pid: %u, Mid: %u\n", rtracker->uid, rtracker->tid, rtracker->pid, rtracker->mid)); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Current outstanding requests: %u\n", ssd->outstanding_requests)); PREPROC_PROFILE_END(dce2_pstat_smb_req); return rtracker; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DCE2_Ret DCE2_SmbBufferTransactionData(DCE2_SmbTransactionTracker *ttracker, const uint8_t *data_ptr, uint16_t dcnt, uint16_t ddisp) { PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_req); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Buffering transaction data.\n")); if (ttracker->dbuf == NULL) { /* Buf size should be the total data count we need */ ttracker->dbuf = DCE2_BufferNew(ttracker->tdcnt, 0, DCE2_MEM_TYPE__SMB_REQ); if (ttracker->dbuf == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Failed to allocate new " "buffer to for transaction data.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); return DCE2_RET__ERROR; } } if (DCE2_BufferAddData(ttracker->dbuf, data_ptr, dcnt, ddisp, DCE2_BUFFER_MIN_ADD_FLAG__IGNORE) != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Failed to buffer transaction data.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); return DCE2_RET__ERROR; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Successfully buffered transaction data.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); return DCE2_RET__SUCCESS; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DCE2_Ret DCE2_SmbBufferTransactionParameters(DCE2_SmbTransactionTracker *ttracker, const uint8_t *param_ptr, uint16_t pcnt, uint16_t pdisp) { PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_req); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Buffering transaction parameters.\n")); if (ttracker->pbuf == NULL) { /* Buf size should be the total data count we need */ ttracker->pbuf = DCE2_BufferNew(ttracker->tpcnt, 0, DCE2_MEM_TYPE__SMB_REQ); if (ttracker->pbuf == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Failed to allocate new " "buffer to for transaction parameter.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); return DCE2_RET__ERROR; } } if (DCE2_BufferAddData(ttracker->pbuf, param_ptr, pcnt, pdisp, DCE2_BUFFER_MIN_ADD_FLAG__IGNORE) != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Failed to buffer transaction parameter data.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); return DCE2_RET__ERROR; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Successfully buffered transaction parameter data.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); return DCE2_RET__SUCCESS; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DCE2_SmbRequestTracker * DCE2_SmbFindRequestTracker(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr) { DCE2_Policy policy = DCE2_SsnGetPolicy(&ssd->sd); DCE2_SmbRequestTracker *first_rtracker = NULL; DCE2_SmbRequestTracker *win_rtracker = NULL; DCE2_SmbRequestTracker *first_mid_rtracker = NULL; DCE2_SmbRequestTracker *tmp_rtracker = NULL; DCE2_SmbRequestTracker *ret_rtracker = NULL; int smb_com = SmbCom(smb_hdr); uint16_t uid = SmbUid(smb_hdr); uint16_t tid = SmbTid(smb_hdr); uint16_t pid = SmbPid(smb_hdr); uint16_t mid = SmbMid(smb_hdr); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_req); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Find request tracker => " "Uid: %u, Tid: %u, Pid: %u, Mid: %u ... ", uid, tid, pid, mid)); tmp_rtracker = &ssd->rtracker; switch (smb_com) { case SMB_COM_TRANSACTION_SECONDARY: smb_com = SMB_COM_TRANSACTION; break; case SMB_COM_TRANSACTION2_SECONDARY: smb_com = SMB_COM_TRANSACTION2; break; case SMB_COM_NT_TRANSACT_SECONDARY: smb_com = SMB_COM_NT_TRANSACT; break; case SMB_COM_WRITE_COMPLETE: smb_com = SMB_COM_WRITE_RAW; break; default: break; } while (tmp_rtracker != NULL) { if ((tmp_rtracker->mid == (int)mid) && (tmp_rtracker->smb_com == smb_com)) { // This is the normal case except for SessionSetupAndX and // TreeConnect/TreeConnectAndX which will fall into the // default case below. if ((tmp_rtracker->pid == pid) && (tmp_rtracker->uid == uid) && (tmp_rtracker->tid == tid)) { ret_rtracker = tmp_rtracker; } else { switch (smb_com) { case SMB_COM_TRANSACTION: case SMB_COM_TRANSACTION2: case SMB_COM_NT_TRANSACT: case SMB_COM_TRANSACTION_SECONDARY: case SMB_COM_TRANSACTION2_SECONDARY: case SMB_COM_NT_TRANSACT_SECONDARY: // These should conform to above break; default: if (tmp_rtracker->pid == pid) ret_rtracker = tmp_rtracker; break; } } if (ret_rtracker != NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Found.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); return ret_rtracker; } // Take the first one where the PIDs also match // in the case of the Transacts above if ((tmp_rtracker->pid == pid) && (win_rtracker == NULL)) win_rtracker = tmp_rtracker; // Set this to the first matching request in the queue // where the Mid matches. Don't set for Windows if from // client since PID/MID are necessary if (((DCE2_SmbType(ssd) == SMB_TYPE__RESPONSE) || !DCE2_SsnIsWindowsPolicy(&ssd->sd)) && first_mid_rtracker == NULL) { first_mid_rtracker = tmp_rtracker; } } // Set the first one we see for early Samba versions if ((first_rtracker == NULL) && (tmp_rtracker->mid != DCE2_SENTINEL) && (tmp_rtracker->smb_com == smb_com)) first_rtracker = tmp_rtracker; // Look at the next request in the queue if (tmp_rtracker == &ssd->rtracker) tmp_rtracker = DCE2_QueueFirst(ssd->rtrackers); else tmp_rtracker = DCE2_QueueNext(ssd->rtrackers); } switch (policy) { case DCE2_POLICY__SAMBA_3_0_20: case DCE2_POLICY__SAMBA_3_0_22: ret_rtracker = first_rtracker; break; case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: ret_rtracker = first_mid_rtracker; break; case DCE2_POLICY__WIN2000: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: if (win_rtracker != NULL) ret_rtracker = win_rtracker; else ret_rtracker = first_mid_rtracker; break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } DCE2_DEBUG_CODE(DCE2_DEBUG__SMB, if (ret_rtracker != NULL) printf("Found.\n"); else printf("Not found\n");); PREPROC_PROFILE_END(dce2_pstat_smb_req); return ret_rtracker; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_SmbRemoveRequestTracker(DCE2_SmbSsnData *ssd, DCE2_SmbRequestTracker *rtracker) { DCE2_SmbRequestTracker *tmp_node; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_req); if ((ssd == NULL) || (rtracker == NULL)) { PREPROC_PROFILE_END(dce2_pstat_smb_req); return; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Removing request tracker => " "Uid: %u, Tid: %u, Pid: %u, Mid: %u ... ", rtracker->uid, rtracker->tid, rtracker->pid, rtracker->mid)); if (rtracker == &ssd->rtracker) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Removed\n")); DCE2_SmbCleanRequestTracker(&ssd->rtracker); ssd->outstanding_requests--; PREPROC_PROFILE_END(dce2_pstat_smb_req); return; } for (tmp_node = DCE2_QueueFirst(ssd->rtrackers); tmp_node != NULL; tmp_node = DCE2_QueueNext(ssd->rtrackers)) { if (tmp_node == (void *)rtracker) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Removed.\n")); DCE2_QueueRemoveCurrent(ssd->rtrackers); ssd->outstanding_requests--; PREPROC_PROFILE_END(dce2_pstat_smb_req); return; } } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not removed.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_req); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbInsertUid(DCE2_SmbSsnData *ssd, const uint16_t uid) { PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_uid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Inserting Uid: %u\n", uid);); if (ssd->uid == DCE2_SENTINEL) { ssd->uid = (int)uid; } else { if (ssd->uids == NULL) { ssd->uids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare, NULL, NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__SMB_UID); if (ssd->uids == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_uid); return; } } DCE2_ListInsert(ssd->uids, (void *)(uintptr_t)uid, (void *)(uintptr_t)uid); } PREPROC_PROFILE_END(dce2_pstat_smb_uid); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static DCE2_Ret DCE2_SmbFindUid(DCE2_SmbSsnData *ssd, const uint16_t uid) { DCE2_Ret status; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_uid); if ((ssd->uid != DCE2_SENTINEL) && (ssd->uid == (int)uid)) status = DCE2_RET__SUCCESS; else status = DCE2_ListFindKey(ssd->uids, (void *)(uintptr_t)uid); PREPROC_PROFILE_END(dce2_pstat_smb_uid); return status; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbRemoveUid(DCE2_SmbSsnData *ssd, const uint16_t uid) { const DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_uid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Removing Uid: %u\n", uid);); if ((ssd->uid != DCE2_SENTINEL) && (ssd->uid == (int)uid)) ssd->uid = DCE2_SENTINEL; else DCE2_ListRemove(ssd->uids, (void *)(uintptr_t)uid); switch (policy) { case DCE2_POLICY__WIN2000: case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: // Removing uid invalidates any fid that was created with it */ if ((ssd->ftracker.fid_v1 != DCE2_SENTINEL) && (ssd->ftracker.uid_v1 == uid)) { DCE2_SmbRemoveFileTracker(ssd, &ssd->ftracker); } if (ssd->ftrackers != NULL) { DCE2_SmbFileTracker *ftracker; for (ftracker = DCE2_ListFirst(ssd->ftrackers); ftracker != NULL; ftracker = DCE2_ListNext(ssd->ftrackers)) { if (ftracker->uid_v1 == uid) { if (ssd->fapi_ftracker == ftracker) DCE2_SmbFinishFileAPI(ssd); #ifdef ACTIVE_RESPONSE if (ssd->fb_ftracker == ftracker) DCE2_SmbFinishFileBlockVerdict(ssd); #endif DCE2_ListRemoveCurrent(ssd->ftrackers); DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker); } } } break; case DCE2_POLICY__SAMBA_3_0_20: case DCE2_POLICY__SAMBA_3_0_22: // Removing Uid used to create file doesn't invalidate it. break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } PREPROC_PROFILE_END(dce2_pstat_smb_uid); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbInsertTid(DCE2_SmbSsnData *ssd, const uint16_t tid, const bool is_ipc) { int insert_tid = (int)tid; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_tid); if (!is_ipc && (!DCE2_ScSmbFileInspection(ssd->sd.sconfig) || ((ssd->max_file_depth == -1) && DCE2_ScSmbFileDepth(ssd->sd.sconfig) == -1))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not inserting TID (%u) " "because it's not IPC and not inspecting normal file " "data.", tid)); PREPROC_PROFILE_END(dce2_pstat_smb_tid); return; } if (is_ipc && DCE2_ScSmbFileInspectionOnly(ssd->sd.sconfig)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not inserting TID (%u) " "because it's IPC and only inspecting normal file " "data.", tid)); PREPROC_PROFILE_END(dce2_pstat_smb_tid); return; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Inserting Tid: %u\n", tid)); // Set a bit so as to distinguish between IPC and non-IPC TIDs if (!is_ipc) insert_tid |= (1 << 16); if (ssd->tid == DCE2_SENTINEL) { ssd->tid = insert_tid; } else { if (ssd->tids == NULL) { ssd->tids = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare, NULL, NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__SMB_TID); if (ssd->tids == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_tid); return; } } DCE2_ListInsert(ssd->tids, (void *)(uintptr_t)tid, (void *)(uintptr_t)insert_tid); } PREPROC_PROFILE_END(dce2_pstat_smb_tid); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static DCE2_Ret DCE2_SmbFindTid(DCE2_SmbSsnData *ssd, const uint16_t tid) { DCE2_Ret status; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_tid); if ((ssd->tid != DCE2_SENTINEL) && ((ssd->tid & 0x0000ffff) == (int)tid)) status = DCE2_RET__SUCCESS; else status = DCE2_ListFindKey(ssd->tids, (void *)(uintptr_t)tid); PREPROC_PROFILE_END(dce2_pstat_smb_tid); return status; } /******************************************************************** * Function: DCE2_SmbIsTidIPC() * * Purpose: Checks to see if the TID passed in was to IPC or not. * * Arguments: * DCE2_SmbSsnData * - pointer to session data * const uint16_t - the TID to check * * Returns: * bool - True if TID is IPC, false if not or if TID not found. * ********************************************************************/ static bool DCE2_SmbIsTidIPC(DCE2_SmbSsnData *ssd, const uint16_t tid) { if ((ssd->tid != DCE2_SENTINEL) && ((ssd->tid & 0x0000ffff) == (int)tid)) { if ((ssd->tid >> 16) == 0) return true; } else { int check_tid = (int)(uintptr_t)DCE2_ListFind(ssd->tids, (void *)(uintptr_t)tid); if (((check_tid & 0x0000ffff) == (int)tid) && ((check_tid >> 16) == 0)) return true; } return false; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbRemoveTid(DCE2_SmbSsnData *ssd, const uint16_t tid) { PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_tid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Removing Tid: %u\n", tid)); if ((ssd->tid != DCE2_SENTINEL) && ((ssd->tid & 0x0000ffff) == (int)tid)) ssd->tid = DCE2_SENTINEL; else DCE2_ListRemove(ssd->tids, (void *)(uintptr_t)tid); // Removing Tid invalidates files created with it if ((ssd->ftracker.fid_v1 != DCE2_SENTINEL) && (ssd->ftracker.tid_v1 == tid)) { DCE2_SmbRemoveFileTracker(ssd, &ssd->ftracker); } if (ssd->ftrackers != NULL) { DCE2_SmbFileTracker *ftracker; for (ftracker = DCE2_ListFirst(ssd->ftrackers); ftracker != NULL; ftracker = DCE2_ListNext(ssd->ftrackers)) { if (ftracker->tid_v1 == (int)tid) { if (ssd->fapi_ftracker == ftracker) DCE2_SmbFinishFileAPI(ssd); #ifdef ACTIVE_RESPONSE if (ssd->fb_ftracker == ftracker) DCE2_SmbFinishFileBlockVerdict(ssd); #endif DCE2_ListRemoveCurrent(ssd->ftrackers); DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker); } } } PREPROC_PROFILE_END(dce2_pstat_smb_tid); } static inline DCE2_Ret DCE2_SmbInitFileTracker(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker, const bool is_ipc, const uint16_t uid, const uint16_t tid, const int fid) { if (ftracker == NULL) return DCE2_RET__ERROR; ftracker->uid_v1 = uid; ftracker->tid_v1 = tid; ftracker->fid_v1 = fid; ftracker->is_ipc = is_ipc; ftracker->is_smb2 = false; ftracker->file_name = NULL; ftracker->file_name_len = 0; if (is_ipc) { DCE2_CoTracker *co_tracker = DCE2_Alloc(sizeof(DCE2_CoTracker), DCE2_MEM_TYPE__SMB_FID); if (co_tracker == NULL) return DCE2_RET__ERROR; DCE2_CoInitTracker(co_tracker); ftracker->fp_co_tracker = co_tracker; ftracker->fp_byte_mode = false; ftracker->fp_used = false; ftracker->fp_writex_raw = NULL; } else { ftracker->ff_file_size = 0; ftracker->ff_file_offset = 0; ftracker->ff_bytes_processed = 0; ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN; ftracker->ff_file_chunks = NULL; ftracker->ff_bytes_queued = 0; if ((ssd->fapi_ftracker == NULL) && (ssd->max_file_depth != -1)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Designating file tracker " "for file API processing: 0x%04X\n", (uint16_t)fid);); ssd->fapi_ftracker = ftracker; } } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static DCE2_SmbFileTracker * DCE2_SmbNewFileTracker(DCE2_SmbSsnData *ssd, const uint16_t uid, const uint16_t tid, const uint16_t fid) { DCE2_SmbFileTracker *ftracker = NULL; bool is_ipc = DCE2_SmbIsTidIPC(ssd, tid); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_fid); // Already have tracker for file API and not setting file data pointer // so don't create new file tracker. if (!is_ipc && (ssd->fapi_ftracker != NULL) && (DCE2_ScSmbFileDepth(ssd->sd.sconfig) == -1)) return NULL; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Creating new file tracker " "with Uid: %u, Tid: %u, Fid: 0x%04X\n", uid, tid, fid)); if (ssd->ftracker.fid_v1 == DCE2_SENTINEL) { ftracker = &ssd->ftracker; if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, (int)fid) != DCE2_RET__SUCCESS) { DCE2_SmbCleanFileTracker(ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } } else { ftracker = (DCE2_SmbFileTracker *) DCE2_Alloc(sizeof(DCE2_SmbFileTracker), DCE2_MEM_TYPE__SMB_FID); if (ftracker == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, (int)fid) != DCE2_RET__SUCCESS) { DCE2_SmbCleanFileTracker(ftracker); DCE2_Free((void *)ftracker, sizeof(DCE2_SmbFileTracker), DCE2_MEM_TYPE__SMB_FID); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } if (ssd->ftrackers == NULL) { ssd->ftrackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare, DCE2_SmbFileTrackerDataFree, NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__SMB_FID); if (ssd->ftrackers == NULL) { DCE2_SmbCleanSessionFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } } if (DCE2_ListInsert(ssd->ftrackers, (void *)(uintptr_t)fid, (void *)ftracker) != DCE2_RET__SUCCESS) { DCE2_SmbCleanSessionFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } } PREPROC_PROFILE_END(dce2_pstat_smb_fid); return ftracker; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbQueueTmpFileTracker(DCE2_SmbSsnData *ssd, DCE2_SmbRequestTracker *rtracker, const uint16_t uid, const uint16_t tid) { DCE2_SmbFileTracker *ftracker; bool is_ipc = DCE2_SmbIsTidIPC(ssd, tid); PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_fid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Queueing file tracker " "with Uid: %u, Tid: %u\n", uid, tid)); ftracker = (DCE2_SmbFileTracker *) DCE2_Alloc(sizeof(DCE2_SmbFileTracker), DCE2_MEM_TYPE__SMB_FID); if (ftracker == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_fid); return; } if (DCE2_SmbInitFileTracker(ssd, ftracker, is_ipc, uid, tid, DCE2_SENTINEL) != DCE2_RET__SUCCESS) { DCE2_SmbCleanFileTracker(ftracker); DCE2_Free((void *)ftracker, sizeof(DCE2_SmbFileTracker), DCE2_MEM_TYPE__SMB_FID); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return; } if (!is_ipc && (ssd->fapi_ftracker == ftracker)) ssd->fapi_ftracker = NULL; if (rtracker->ft_queue == NULL) { rtracker->ft_queue = DCE2_QueueNew(DCE2_SmbFileTrackerDataFree, DCE2_MEM_TYPE__SMB_FID); if (rtracker->ft_queue == NULL) { DCE2_SmbCleanSessionFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return; } } if (DCE2_QueueEnqueue(rtracker->ft_queue, (void *)ftracker) != DCE2_RET__SUCCESS) { DCE2_SmbCleanSessionFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return; } PREPROC_PROFILE_END(dce2_pstat_smb_fid); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: None * ********************************************************************/ static inline DCE2_SmbFileTracker * DCE2_SmbGetTmpFileTracker(DCE2_SmbRequestTracker *rtracker) { if (!DCE2_QueueIsEmpty(rtracker->ft_queue)) return (DCE2_SmbFileTracker *)DCE2_QueueLast(rtracker->ft_queue); return NULL; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: None * ********************************************************************/ static DCE2_SmbFileTracker * DCE2_SmbDequeueTmpFileTracker(DCE2_SmbSsnData *ssd, DCE2_SmbRequestTracker *rtracker, const uint16_t fid) { DCE2_SmbFileTracker *ftracker; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_fid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Dequeueing file tracker " "and binding to fid: 0x%04X\n", fid)); ftracker = (DCE2_SmbFileTracker *)DCE2_QueueDequeue(rtracker->ft_queue); if (ftracker == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } if (ssd->ftracker.fid_v1 == DCE2_SENTINEL) { memcpy(&ssd->ftracker, ftracker, sizeof(DCE2_SmbFileTracker)); DCE2_Free((void *)ftracker, sizeof(DCE2_SmbFileTracker), DCE2_MEM_TYPE__SMB_FID); if (ssd->fapi_ftracker == ftracker) ssd->fapi_ftracker = &ssd->ftracker; ftracker = &ssd->ftracker; } else { if (ssd->ftrackers == NULL) { ssd->ftrackers = DCE2_ListNew(DCE2_LIST_TYPE__SPLAYED, DCE2_SmbUidTidFidCompare, DCE2_SmbFileTrackerDataFree, NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__SMB_FID); if (ssd->ftrackers == NULL) { DCE2_SmbCleanSessionFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } } if (DCE2_ListInsert(ssd->ftrackers, (void *)(uintptr_t)fid, (void *)ftracker) != DCE2_RET__SUCCESS) { DCE2_SmbCleanSessionFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } } // Other values were intialized when queueing. ftracker->fid_v1 = (int)fid; PREPROC_PROFILE_END(dce2_pstat_smb_fid); return ftracker; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline DCE2_SmbFileTracker * DCE2_SmbGetFileTracker(DCE2_SmbSsnData *ssd, const uint16_t fid) { DCE2_SmbFileTracker *ftracker = ssd->cur_rtracker->ftracker; if (ftracker == NULL) { // Write could've been chained to an OpenAndX or NtCreateAndX so a // temporary file tracker would've been created until we get the // response with the Fid returned from the OpenAndX / NtCreateAndX ftracker = DCE2_SmbGetTmpFileTracker(ssd->cur_rtracker); if (ftracker == NULL) { // Otherwise find it with the passed in Fid ftracker = DCE2_SmbFindFileTracker(ssd, ssd->cur_rtracker->uid, ssd->cur_rtracker->tid, fid); } } return ftracker; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static DCE2_SmbFileTracker * DCE2_SmbFindFileTracker(DCE2_SmbSsnData *ssd, const uint16_t uid, const uint16_t tid, const uint16_t fid) { const DCE2_Policy policy = DCE2_SsnGetServerPolicy(&ssd->sd); DCE2_SmbFileTracker *ftracker; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_fid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Finding file tracker with " "Uid: %u, Tid: %u, Fid: 0x%04X ... ", uid, tid, fid)); if ((ssd->ftracker.fid_v1 != DCE2_SENTINEL) && (ssd->ftracker.fid_v1 == (int)fid)) { ftracker = &ssd->ftracker; } else { ftracker = (DCE2_SmbFileTracker *) DCE2_ListFind(ssd->ftrackers, (void *)(uintptr_t)fid); } if (ftracker == NULL) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not found.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } // Note IPC Tid has already been validated in initial processing switch (policy) { case DCE2_POLICY__SAMBA: case DCE2_POLICY__SAMBA_3_0_37: // Only Uid used to open file can be used to make a request if (ftracker->uid_v1 != uid) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not found.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } break; case DCE2_POLICY__WIN2000: case DCE2_POLICY__SAMBA_3_0_20: case DCE2_POLICY__SAMBA_3_0_22: // Any valid Uid can be used to make a request to a file ... // except for Windows 2000 on the first use. if ((policy != DCE2_POLICY__WIN2000) || (ftracker->is_ipc && ftracker->fp_used)) { // Check that the Uid exists if (DCE2_SmbFindUid(ssd, uid) != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not found.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } break; } // Fall through for Windows 2000 for first request to file case DCE2_POLICY__WIN2003: case DCE2_POLICY__WINXP: case DCE2_POLICY__WINVISTA: case DCE2_POLICY__WIN2008: case DCE2_POLICY__WIN7: // Both Uid and Tid used to create file must be used to make a request if ((ftracker->uid_v1 != uid) || (ftracker->tid_v1 != tid)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Not found.\n")); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return NULL; } break; default: DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Invalid policy: %d", __FILE__, __LINE__, policy); break; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Found with " "Uid: %u, Tid: %u, Fid: 0x%04X\n", ftracker->uid_v1, ftracker->tid_v1, ftracker->fid_v1)); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return ftracker; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static DCE2_Ret DCE2_SmbRemoveFileTracker(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker) { PROFILE_VARS; if (ftracker == NULL) return DCE2_RET__ERROR; PREPROC_PROFILE_START(dce2_pstat_smb_fid); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Removing file tracker with Fid: 0x%04X\n", ftracker->fid_v1)); if (ssd->fapi_ftracker == ftracker) { /* If the finish API returns pending set , we return from here * with not inspected and do not remove the file & request * trackers for upcoming retry packet. */ DCE2_SmbRetransmitPending flag = DCE2_SmbFinishFileAPI(ssd); if (flag == DCE2_SMB_RETRANSMIT_PENDING__SET) { PREPROC_PROFILE_END(dce2_pstat_smb_fid); return DCE2_RET__NOT_INSPECTED; } } #ifdef ACTIVE_RESPONSE if (ssd->fb_ftracker == ftracker) DCE2_SmbFinishFileBlockVerdict(ssd); #endif if (ftracker == &ssd->ftracker) DCE2_SmbCleanFileTracker(&ssd->ftracker); else if (ssd->ftrackers != NULL) DCE2_ListRemove(ssd->ftrackers, (void *)(uintptr_t)ftracker->fid_v1); DCE2_SmbRemoveFileTrackerFromRequestTrackers(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_fid); return DCE2_RET__SUCCESS; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_SmbCleanFileTracker(DCE2_SmbFileTracker *ftracker) { PROFILE_VARS; if (ftracker == NULL) return; PREPROC_PROFILE_START(dce2_pstat_smb_fid); ftracker->fid_v1 = DCE2_SENTINEL; if (ftracker->file_name != NULL) { DCE2_Free((void *)ftracker->file_name, ftracker->file_name_len, DCE2_MEM_TYPE__SMB_SSN); ftracker->file_name = NULL; ftracker->file_name_len = 0; } if (ftracker->is_ipc) { ftracker->fp_used = 0; ftracker->fp_byte_mode = 0; if (ftracker->fp_writex_raw != NULL) { DCE2_BufferDestroy(ftracker->fp_writex_raw->buf); DCE2_Free((void *)ftracker->fp_writex_raw, sizeof(DCE2_SmbWriteAndXRaw), DCE2_MEM_TYPE__SMB_FID); ftracker->fp_writex_raw = NULL; } if (ftracker->fp_co_tracker != NULL) { DCE2_CoCleanTracker(ftracker->fp_co_tracker); DCE2_Free((void *)ftracker->fp_co_tracker, sizeof(DCE2_CoTracker), DCE2_MEM_TYPE__SMB_FID); ftracker->fp_co_tracker = NULL; } } else { ftracker->ff_file_size = 0; ftracker->ff_file_offset = 0; ftracker->ff_bytes_processed = 0; ftracker->ff_file_direction = DCE2_SMB_FILE_DIRECTION__UNKNOWN; ftracker->ff_bytes_queued = 0; ftracker->ff_sequential_only = false; if (ftracker->ff_file_chunks != NULL) { DCE2_ListDestroy(ftracker->ff_file_chunks); ftracker->ff_file_chunks = NULL; } } PREPROC_PROFILE_END(dce2_pstat_smb_fid); } /******************************************************************** * * Remove file tracker and associated pointers in session * ********************************************************************/ static inline void DCE2_SmbCleanSessionFileTracker(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker) { DCE2_SmbCleanFileTracker(ftracker); DCE2_Free((void *)ftracker, sizeof(DCE2_SmbFileTracker), DCE2_MEM_TYPE__SMB_FID); if (ssd->fapi_ftracker == ftracker) ssd->fapi_ftracker = NULL; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: void * ********************************************************************/ static inline void DCE2_SmbCleanTransactionTracker(DCE2_SmbTransactionTracker *ttracker) { PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_req); if (ttracker == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_req); return; } if (ttracker->dbuf != NULL) DCE2_BufferDestroy(ttracker->dbuf); if (ttracker->pbuf != NULL) DCE2_BufferDestroy(ttracker->pbuf); memset(ttracker, 0, sizeof(*ttracker)); PREPROC_PROFILE_END(dce2_pstat_smb_req); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_SmbCleanRequestTracker(DCE2_SmbRequestTracker *rtracker) { PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_req); if (rtracker == NULL) { PREPROC_PROFILE_END(dce2_pstat_smb_req); return; } if (rtracker->mid == DCE2_SENTINEL) { PREPROC_PROFILE_END(dce2_pstat_smb_req); return; } rtracker->mid = DCE2_SENTINEL; rtracker->ftracker = NULL; rtracker->sequential_only = false; DCE2_SmbCleanTransactionTracker(&rtracker->ttracker); DCE2_QueueDestroy(rtracker->ft_queue); rtracker->ft_queue = NULL; if (rtracker->file_name != NULL) { DCE2_Free((void *)rtracker->file_name, rtracker->file_name_len, DCE2_MEM_TYPE__SMB_SSN); rtracker->file_name = NULL; } PREPROC_PROFILE_END(dce2_pstat_smb_req); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static int DCE2_SmbUidTidFidCompare(const void *a, const void *b) { int x = (int)(uintptr_t)a; int y = (int)(uintptr_t)b; if (x == y) return 0; /* Only care about equality for finding */ return -1; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_SmbDataFree(DCE2_SmbSsnData *ssd) { if (ssd == NULL) return; // XXX This tries to account for the situation where we never knew the file // size and the TCP session was shutdown before an SMB_COM_CLOSE on the file. // Possibly need to add callback to fileAPI since it may have already // released it's resources. //DCE2_SmbFinishFileAPI(ssd); if (ssd->uids != NULL) { DCE2_ListDestroy(ssd->uids); ssd->uids = NULL; } if (ssd->tids != NULL) { DCE2_ListDestroy(ssd->tids); ssd->tids = NULL; } DCE2_SmbCleanFileTracker(&ssd->ftracker); if (ssd->ftrackers != NULL) { DCE2_ListDestroy(ssd->ftrackers); ssd->ftrackers = NULL; } DCE2_SmbCleanRequestTracker(&ssd->rtracker); if (ssd->rtrackers != NULL) { DCE2_QueueDestroy(ssd->rtrackers); ssd->rtrackers = NULL; } if (ssd->cli_seg != NULL) { DCE2_BufferDestroy(ssd->cli_seg); ssd->cli_seg = NULL; } if (ssd->srv_seg != NULL) { DCE2_BufferDestroy(ssd->srv_seg); ssd->srv_seg = NULL; } if (ssd->smb2_requests != NULL) { DCE2_Smb2CleanRequests(ssd->smb2_requests); ssd->smb2_requests = NULL; } } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ void DCE2_SmbSsnFree(void *ssn) { DCE2_SmbSsnData *ssd = (DCE2_SmbSsnData *)ssn; if (ssd == NULL) return; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Removing Session: %p\n", ssd)); DCE2_SmbDataFree(ssd); DCE2_Free((void *)ssn, sizeof(DCE2_SmbSsnData), DCE2_MEM_TYPE__SMB_SSN); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbFileTrackerDataFree(void *data) { DCE2_SmbFileTracker *ftracker = (DCE2_SmbFileTracker *)data; if (ftracker == NULL) return; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Freeing file tracker: " "Uid: %u, Tid: %u, Fid: 0x%04X\n", ftracker->uid_v1, ftracker->tid_v1, ftracker->fid_v1)); DCE2_SmbCleanFileTracker(ftracker); DCE2_Free((void *)ftracker, sizeof(DCE2_SmbFileTracker), DCE2_MEM_TYPE__SMB_FID); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static void DCE2_SmbRequestTrackerDataFree(void *data) { DCE2_SmbRequestTracker *rtracker = (DCE2_SmbRequestTracker *)data; if (rtracker == NULL) return; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Freeing request tracker: " "Uid: %u, Tid: %u, Pid: %u, Mid: %u\n", rtracker->uid, rtracker->tid, rtracker->pid, rtracker->mid)); DCE2_SmbCleanRequestTracker(rtracker); DCE2_Free((void *)rtracker, sizeof(DCE2_SmbRequestTracker), DCE2_MEM_TYPE__SMB_REQ); } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline SFSnortPacket * DCE2_SmbGetRpkt(DCE2_SmbSsnData *ssd, const uint8_t **data, uint32_t *data_len, DCE2_RpktType rtype) { SFSnortPacket *rpkt; uint16_t header_len; if ((ssd == NULL) || (data == NULL) || (*data == NULL) || (data_len == NULL) || (*data_len == 0)) return NULL; rpkt = DCE2_GetRpkt(ssd->sd.wire_pkt, rtype, *data, *data_len); if (rpkt == NULL) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to create reassembly packet.", __FILE__, __LINE__); return NULL; } if (DCE2_PushPkt(rpkt) != DCE2_RET__SUCCESS) { DCE2_Log(DCE2_LOG_TYPE__ERROR, "%s(%d) Failed to push packet onto packet stack.", __FILE__, __LINE__); return NULL; } *data = rpkt->payload; *data_len = rpkt->payload_size; switch (rtype) { case DCE2_RPKT_TYPE__SMB_TRANS: if (DCE2_SmbType(ssd) == SMB_TYPE__REQUEST) header_len = DCE2_MOCK_HDR_LEN__SMB_CLI; else header_len = DCE2_MOCK_HDR_LEN__SMB_SRV; DCE2_SmbSetRdata(ssd, (uint8_t *)rpkt->payload, (uint16_t)(rpkt->payload_size - header_len)); DCE2_MOVE(*data, *data_len, header_len); break; case DCE2_RPKT_TYPE__SMB_SEG: default: break; } return rpkt; } /******************************************************************** * Function: * * Purpose: * * Arguments: * * Returns: * ********************************************************************/ static inline void DCE2_SmbReturnRpkt(void) { DCE2_PopPkt(); } #define DCE2_SMB_TRANS__NONE 0x00 #define DCE2_SMB_TRANS__DATA 0x01 #define DCE2_SMB_TRANS__PARAMS 0x02 #define DCE2_SMB_TRANS__BOTH (DCE2_SMB_TRANS__DATA|DCE2_SMB_TRANS__PARAMS) /******************************************************************** * Function: DCE2_SmbUpdateTransRequest() * * Purpose: * Handles common checks and updates of transaction requests - * SMB_COM_TRANSACTION, SMB_COM_TRANSACTION2 and SMB_COM_NT_TRANSACT * * Arguments: * DCE2_SmbSsnData * - pointer to SMB session data * const SmbNtHdr * - pointer to SMB header * const DCE2_SmbComInfo * - pointer to com info structure * const uint8_t * - pointer to data * uint32_t - data length * * Returns: * DCE2_Ret * DCE2_RET__IGNORE if we don't process the subcommand * DCE2_RET__FULL if the transaction is complete * DCE2_RET__ERROR if an error occurred. * DCE2_RET__SUCCESS if ok (but not complete). * ********************************************************************/ static DCE2_Ret DCE2_SmbUpdateTransRequest(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint32_t tpcnt, pcnt, poff; uint32_t tdcnt, dcnt, doff; uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint16_t fid; uint8_t setup_count; DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; uint16_t sub_com; int data_params = DCE2_SMB_TRANS__NONE; uint8_t smb_com = DCE2_ComInfoSmbCom(com_info); switch (smb_com) { case SMB_COM_TRANSACTION: sub_com = SmbTransactionReqSubCom((SmbTransactionReq *)nb_ptr); fid = SmbTransactionReqFid((SmbTransactionReq *)nb_ptr); setup_count = SmbTransactionReqSetupCnt((SmbTransactionReq *)nb_ptr); tdcnt = SmbTransactionReqTotalDataCnt((SmbTransactionReq *)nb_ptr); doff = SmbTransactionReqDataOff((SmbTransactionReq *)nb_ptr); dcnt = SmbTransactionReqDataCnt((SmbTransactionReq *)nb_ptr); tpcnt = SmbTransactionReqTotalParamCnt((SmbTransactionReq *)nb_ptr); pcnt = SmbTransactionReqParamCnt((SmbTransactionReq *)nb_ptr); poff = SmbTransactionReqParamOff((SmbTransactionReq *)nb_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Transaction subcommand: %s (0x%04X)\n", (sub_com < TRANS_SUBCOM_MAX) ? smb_transaction_sub_command_strings[sub_com] : "Unknown", sub_com)); if (sub_com < TRANS_SUBCOM_MAX) dce2_stats.smb_trans_subcom_stats[SMB_TYPE__REQUEST][sub_com]++; else dce2_stats.smb_trans_subcom_stats[SMB_TYPE__REQUEST][TRANS_SUBCOM_MAX]++; ssd->cur_rtracker->ftracker = DCE2_SmbGetFileTracker(ssd, fid); if (ssd->cur_rtracker->ftracker == NULL) return DCE2_RET__IGNORE; switch (sub_com) { case TRANS_TRANSACT_NMPIPE: if (DCE2_SsnIsWindowsPolicy(&ssd->sd) && ssd->cur_rtracker->ftracker->fp_byte_mode) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Pipe is in byte " "mode - TRANS_TRANSACT_NMPIPE won't work\n")); return DCE2_RET__ERROR; } data_params = DCE2_SMB_TRANS__DATA; break; case TRANS_READ_NMPIPE: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_UNUSUAL_COMMAND_USED, smb_transaction_sub_command_strings[sub_com]); break; case TRANS_SET_NMPIPE_STATE: data_params = DCE2_SMB_TRANS__PARAMS; break; case TRANS_WRITE_NMPIPE: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_UNUSUAL_COMMAND_USED, smb_transaction_sub_command_strings[sub_com]); data_params = DCE2_SMB_TRANS__DATA; break; // Not implemented according to MS-CIFS case TRANS_RAW_READ_NMPIPE: // Can only write 2 NULL bytes and subsequent writes return pipe disconnected case TRANS_RAW_WRITE_NMPIPE: // Can at most do a DCE/RPC bind case TRANS_CALL_NMPIPE: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_DEPR_COMMAND_USED, smb_transaction_sub_command_strings[sub_com]); // Aren't looking at these or the three above case TRANS_QUERY_NMPIPE_STATE: case TRANS_QUERY_NMPIPE_INFO: case TRANS_PEEK_NMPIPE: case TRANS_WAIT_NMPIPE: default: // Don't want to track the response return DCE2_RET__IGNORE; } // Servers return error if incorrect setup count if (setup_count != 2) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_SETUP_COUNT, smb_com_strings[SMB_COM_TRANSACTION], smb_transaction_sub_command_strings[sub_com], setup_count); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, com_size); // Samba validates the Name which should be \PIPE\ and errors // if not. Windows doesn't care. // And Samba uses the ByteCount to validate if (DCE2_SsnIsSambaPolicy(&ssd->sd) && (DCE2_SmbTransactionGetName(nb_ptr, nb_len, byte_count, SmbUnicode(smb_hdr)) != DCE2_RET__SUCCESS)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Failed to validate " "pipe name for Samba.\n")); return DCE2_RET__ERROR; } break; case SMB_COM_TRANSACTION2: sub_com = SmbTransaction2ReqSubCom((SmbTransaction2Req *)nb_ptr); setup_count = SmbTransaction2ReqSetupCnt((SmbTransaction2Req *)nb_ptr); tdcnt = SmbTransaction2ReqTotalDataCnt((SmbTransaction2Req *)nb_ptr); doff = SmbTransaction2ReqDataOff((SmbTransaction2Req *)nb_ptr); dcnt = SmbTransaction2ReqDataCnt((SmbTransaction2Req *)nb_ptr); tpcnt = SmbTransaction2ReqTotalParamCnt((SmbTransaction2Req *)nb_ptr); pcnt = SmbTransaction2ReqParamCnt((SmbTransaction2Req *)nb_ptr); poff = SmbTransaction2ReqParamOff((SmbTransaction2Req *)nb_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Transaction2 subcommand: %s (0x%04X)\n", (sub_com < TRANS2_SUBCOM_MAX) ? smb_transaction2_sub_command_strings[sub_com] : "Unknown", sub_com)); if (sub_com < TRANS2_SUBCOM_MAX) dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__REQUEST][sub_com]++; else dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__REQUEST][TRANS2_SUBCOM_MAX]++; switch (sub_com) { case TRANS2_OPEN2: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_UNUSUAL_COMMAND_USED, smb_transaction2_sub_command_strings[sub_com]); data_params = DCE2_SMB_TRANS__PARAMS; break; case TRANS2_QUERY_FILE_INFORMATION: data_params = DCE2_SMB_TRANS__PARAMS; break; case TRANS2_SET_FILE_INFORMATION: data_params = DCE2_SMB_TRANS__BOTH; break; case TRANS2_FIND_FIRST2: case TRANS2_FIND_NEXT2: case TRANS2_QUERY_FS_INFORMATION: case TRANS2_SET_FS_INFORMATION: case TRANS2_QUERY_PATH_INFORMATION: case TRANS2_SET_PATH_INFORMATION: case TRANS2_FSCTL: case TRANS2_IOCTL2: case TRANS2_FIND_NOTIFY_FIRST: case TRANS2_FIND_NOTIFY_NEXT: case TRANS2_CREATE_DIRECTORY: case TRANS2_SESSION_SETUP: case TRANS2_GET_DFS_REFERRAL: case TRANS2_REPORT_DFS_INCONSISTENCY: default: // Don't want to process this transaction any more return DCE2_RET__IGNORE; } if (setup_count != 1) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_SETUP_COUNT, smb_com_strings[SMB_COM_TRANSACTION2], smb_transaction2_sub_command_strings[sub_com], setup_count); return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, com_size); break; case SMB_COM_NT_TRANSACT: sub_com = SmbNtTransactReqSubCom((SmbNtTransactReq *)nb_ptr); setup_count = SmbNtTransactReqSetupCnt((SmbNtTransactReq *)nb_ptr); tdcnt = SmbNtTransactReqTotalDataCnt((SmbNtTransactReq *)nb_ptr); doff = SmbNtTransactReqDataOff((SmbNtTransactReq *)nb_ptr); dcnt = SmbNtTransactReqDataCnt((SmbNtTransactReq *)nb_ptr); tpcnt = SmbNtTransactReqTotalParamCnt((SmbNtTransactReq *)nb_ptr); pcnt = SmbNtTransactReqParamCnt((SmbNtTransactReq *)nb_ptr); poff = SmbNtTransactReqParamOff((SmbNtTransactReq *)nb_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Nt Transact subcommand: %s (0x%04X)\n", (sub_com < NT_TRANSACT_SUBCOM_MAX) ? smb_nt_transact_sub_command_strings[sub_com] : "Unknown", sub_com)); if (sub_com < NT_TRANSACT_SUBCOM_MAX) dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__REQUEST][sub_com]++; else dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__REQUEST][NT_TRANSACT_SUBCOM_MAX]++; switch (sub_com) { case NT_TRANSACT_CREATE: DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_UNUSUAL_COMMAND_USED, smb_nt_transact_sub_command_strings[sub_com]); if (setup_count != 0) { DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_INVALID_SETUP_COUNT, smb_com_strings[SMB_COM_NT_TRANSACT], smb_nt_transact_sub_command_strings[sub_com], setup_count); return DCE2_RET__ERROR; } data_params = DCE2_SMB_TRANS__PARAMS; break; case NT_TRANSACT_IOCTL: case NT_TRANSACT_SET_SECURITY_DESC: case NT_TRANSACT_NOTIFY_CHANGE: case NT_TRANSACT_RENAME: case NT_TRANSACT_QUERY_SECURITY_DESC: default: // Don't want to process this transaction any more return DCE2_RET__IGNORE; } DCE2_MOVE(nb_ptr, nb_len, com_size); break; default: return DCE2_RET__ERROR; } if (DCE2_SmbValidateTransactionFields(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, byte_count, tdcnt, tpcnt, dcnt, doff, 0, pcnt, poff, 0) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; ttracker->smb_type = SMB_TYPE__REQUEST; ttracker->subcom = (uint8_t)sub_com; ttracker->tdcnt = tdcnt; ttracker->dsent = dcnt; ttracker->tpcnt = tpcnt; ttracker->psent = pcnt; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Data count: %u, " "Total data count: %u, Param count: %u, " "Total param count: %u\n", dcnt, tdcnt, pcnt, tpcnt)); // Testing shows that Transacts aren't processed until // all of the data and parameters are received, so overlapping // writes to the same FID can occur as long as the pid/mid are // distinct (and that depends on policy). So we need to buffer // data up for each incomplete Transact so data doesn't get mangled // together with multiple ones intermixing at the same time. if (data_params & DCE2_SMB_TRANS__DATA) { if (tdcnt == 0) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_TDCNT_ZERO); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); // If all of the data and parameters weren't sent, buffer what was sent if (((dcnt != tdcnt) || (pcnt != tpcnt)) && (dcnt != 0) && (DCE2_SmbBufferTransactionData(ttracker, nb_ptr, dcnt, 0) != DCE2_RET__SUCCESS)) { return DCE2_RET__ERROR; } } if (data_params & DCE2_SMB_TRANS__PARAMS) { if (tpcnt == 0) DCE2_Alert(&ssd->sd, DCE2_EVENT__SMB_TDCNT_ZERO); DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); // If all of the data and parameters weren't sent, buffer what was sent if (((pcnt != tpcnt) || (dcnt != tdcnt)) && (pcnt != 0) && (DCE2_SmbBufferTransactionParameters(ttracker, nb_ptr, pcnt, 0) != DCE2_RET__SUCCESS)) { return DCE2_RET__ERROR; } } if ((dcnt == tdcnt) && (pcnt == tpcnt)) return DCE2_RET__FULL; return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbUpdateTransSecondary() * * Purpose: * Handles common checks and updates of transaction secondary * requests - SMB_COM_TRANSACTION_SECONDARY, * SMB_COM_TRANSACTION2_SECONDARY and * SMB_COM_NT_TRANSACT_SECONDARY * * Arguments: * DCE2_SmbSsnData * - pointer to SMB session data * const SmbNtHdr * - pointer to SMB header * const DCE2_SmbComInfo * - pointer to com info structure * const uint8_t * - pointer to data * uint32_t - data length * * Returns: * DCE2_Ret * DCE2_RET__IGNORE if we don't process the subcommand * DCE2_RET__FULL if the transaction is complete * DCE2_RET__ERROR if an error occurred. * DCE2_RET__SUCCESS if ok (but not complete). * ********************************************************************/ static DCE2_Ret DCE2_SmbUpdateTransSecondary(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint16_t com_size = DCE2_ComInfoCommandSize(com_info); uint16_t byte_count = DCE2_ComInfoByteCount(com_info); uint32_t tdcnt, doff, dcnt, ddisp; uint32_t tpcnt, poff, pcnt, pdisp; DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; uint16_t sub_com = ttracker->subcom; int data_params = DCE2_SMB_TRANS__NONE; uint8_t smb_com = DCE2_ComInfoSmbCom(com_info); switch (smb_com) { case SMB_COM_TRANSACTION_SECONDARY: tdcnt = SmbTransactionSecondaryReqTotalDataCnt((SmbTransactionSecondaryReq *)nb_ptr); doff = SmbTransactionSecondaryReqDataOff((SmbTransactionSecondaryReq *)nb_ptr); dcnt = SmbTransactionSecondaryReqDataCnt((SmbTransactionSecondaryReq *)nb_ptr); ddisp = SmbTransactionSecondaryReqDataDisp((SmbTransactionSecondaryReq *)nb_ptr); tpcnt = SmbTransactionSecondaryReqTotalParamCnt((SmbTransactionSecondaryReq *)nb_ptr); poff = SmbTransactionSecondaryReqParamOff((SmbTransactionSecondaryReq *)nb_ptr); pcnt = SmbTransactionSecondaryReqParamCnt((SmbTransactionSecondaryReq *)nb_ptr); pdisp = SmbTransactionSecondaryReqParamDisp((SmbTransactionSecondaryReq *)nb_ptr); switch (sub_com) { case TRANS_TRANSACT_NMPIPE: case TRANS_WRITE_NMPIPE: data_params = DCE2_SMB_TRANS__DATA; break; case TRANS_SET_NMPIPE_STATE: data_params = DCE2_SMB_TRANS__PARAMS; break; default: return DCE2_RET__IGNORE; } break; case SMB_COM_TRANSACTION2_SECONDARY: tdcnt = SmbTransaction2SecondaryReqTotalDataCnt((SmbTransaction2SecondaryReq *)nb_ptr); doff = SmbTransaction2SecondaryReqDataOff((SmbTransaction2SecondaryReq *)nb_ptr); dcnt = SmbTransaction2SecondaryReqDataCnt((SmbTransaction2SecondaryReq *)nb_ptr); ddisp = SmbTransaction2SecondaryReqDataDisp((SmbTransaction2SecondaryReq *)nb_ptr); tpcnt = SmbTransaction2SecondaryReqTotalParamCnt((SmbTransaction2SecondaryReq *)nb_ptr); poff = SmbTransaction2SecondaryReqParamOff((SmbTransaction2SecondaryReq *)nb_ptr); pcnt = SmbTransaction2SecondaryReqParamCnt((SmbTransaction2SecondaryReq *)nb_ptr); pdisp = SmbTransaction2SecondaryReqParamDisp((SmbTransaction2SecondaryReq *)nb_ptr); switch (sub_com) { case TRANS2_OPEN2: case TRANS2_QUERY_FILE_INFORMATION: data_params = DCE2_SMB_TRANS__PARAMS; break; case TRANS2_SET_FILE_INFORMATION: data_params = DCE2_SMB_TRANS__BOTH; break; default: return DCE2_RET__IGNORE; } break; case SMB_COM_NT_TRANSACT_SECONDARY: tdcnt = SmbNtTransactSecondaryReqTotalDataCnt((SmbNtTransactSecondaryReq *)nb_ptr); doff = SmbNtTransactSecondaryReqDataOff((SmbNtTransactSecondaryReq *)nb_ptr); dcnt = SmbNtTransactSecondaryReqDataCnt((SmbNtTransactSecondaryReq *)nb_ptr); ddisp = SmbNtTransactSecondaryReqDataDisp((SmbNtTransactSecondaryReq *)nb_ptr); tpcnt = SmbNtTransactSecondaryReqTotalParamCnt((SmbNtTransactSecondaryReq *)nb_ptr); poff = SmbNtTransactSecondaryReqParamOff((SmbNtTransactSecondaryReq *)nb_ptr); pcnt = SmbNtTransactSecondaryReqParamCnt((SmbNtTransactSecondaryReq *)nb_ptr); pdisp = SmbNtTransactSecondaryReqParamDisp((SmbNtTransactSecondaryReq *)nb_ptr); switch (sub_com) { case NT_TRANSACT_CREATE: data_params = DCE2_SMB_TRANS__PARAMS; break; default: return DCE2_RET__IGNORE; } break; default: return DCE2_RET__ERROR; } if (DCE2_SsnIsSambaPolicy(&ssd->sd)) { // If the total count decreases, Samba will reset this to the new // total count. if (tdcnt < ttracker->tdcnt) ttracker->tdcnt = tdcnt; if (tpcnt < ttracker->tpcnt) ttracker->tpcnt = tpcnt; } else { // Windows always uses the total data count from the first transaction. tdcnt = (uint16_t)ttracker->tdcnt; tpcnt = (uint16_t)ttracker->tpcnt; } DCE2_MOVE(nb_ptr, nb_len, com_size); if (DCE2_SmbValidateTransactionFields(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, byte_count, tdcnt, tpcnt, dcnt, doff, ddisp, pcnt, poff, pdisp) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_SmbValidateTransactionSent(ssd, ttracker->dsent, dcnt, ttracker->tdcnt, ttracker->psent, pcnt, ttracker->tpcnt) != DCE2_RET__SUCCESS) return DCE2_RET__IGNORE; ttracker->dsent += dcnt; ttracker->psent += pcnt; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Data displacement: %u, " "Data count: %u, Total data count: %u\n" "Parameter displacement: %u, " "Parameter count: %u, Total parameter count: %u\n", ddisp, dcnt, tdcnt, pdisp, pcnt, tpcnt)); if (data_params & DCE2_SMB_TRANS__DATA) { DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); if ((dcnt != 0) && (DCE2_SmbBufferTransactionData(ttracker, nb_ptr, dcnt, ddisp) != DCE2_RET__SUCCESS)) { return DCE2_RET__ERROR; } } if (data_params & DCE2_SMB_TRANS__PARAMS) { DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); if ((pcnt != 0) && (DCE2_SmbBufferTransactionParameters(ttracker, nb_ptr, pcnt, pdisp) != DCE2_RET__SUCCESS)) { return DCE2_RET__ERROR; } } if ((ttracker->dsent == ttracker->tdcnt) && (ttracker->psent == ttracker->tpcnt)) { return DCE2_RET__FULL; } return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbUpdateTransResponse() * * Purpose: * Handles common checks and updates of transaction responses - * SMB_COM_TRANSACTION, SMB_COM_TRANSACTION2 and SMB_COM_NT_TRANSACT * * Arguments: * DCE2_SmbSsnData * - pointer to SMB session data * const SmbNtHdr * - pointer to SMB header * const DCE2_SmbComInfo * - pointer to com info structure * const uint8_t * - pointer to data * uint32_t - data length * * Returns: * DCE2_Ret * DCE2_RET__FULL if the transaction is complete * DCE2_RET__ERROR if an error occurred. * DCE2_RET__SUCCESS if ok (but not complete). * ********************************************************************/ static DCE2_Ret DCE2_SmbUpdateTransResponse(DCE2_SmbSsnData *ssd, const SmbNtHdr *smb_hdr, const DCE2_SmbComInfo *com_info, const uint8_t *nb_ptr, uint32_t nb_len) { uint32_t tpcnt, pcnt, poff, pdisp; uint32_t tdcnt, dcnt, doff, ddisp; DCE2_SmbTransactionTracker *ttracker = &ssd->cur_rtracker->ttracker; uint16_t sub_com = ttracker->subcom; int data_params = DCE2_SMB_TRANS__NONE; uint8_t smb_com = DCE2_ComInfoSmbCom(com_info); switch (smb_com) { case SMB_COM_TRANSACTION: tdcnt = SmbTransactionRespTotalDataCnt((SmbTransactionResp *)nb_ptr); doff = SmbTransactionRespDataOff((SmbTransactionResp *)nb_ptr); dcnt = SmbTransactionRespDataCnt((SmbTransactionResp *)nb_ptr); ddisp = SmbTransactionRespDataDisp((SmbTransactionResp *)nb_ptr); tpcnt = SmbTransactionRespTotalParamCnt((SmbTransactionResp *)nb_ptr); pcnt = SmbTransactionRespParamCnt((SmbTransactionResp *)nb_ptr); poff = SmbTransactionRespParamOff((SmbTransactionResp *)nb_ptr); pdisp = SmbTransactionRespParamDisp((SmbTransactionResp *)nb_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Transaction subcommand: %s (0x%04X)\n", (sub_com < TRANS_SUBCOM_MAX) ? smb_transaction_sub_command_strings[sub_com] : "Unknown", sub_com)); if (sub_com < TRANS_SUBCOM_MAX) dce2_stats.smb_trans_subcom_stats[SMB_TYPE__RESPONSE][sub_com]++; else dce2_stats.smb_trans_subcom_stats[SMB_TYPE__RESPONSE][TRANS_SUBCOM_MAX]++; switch (sub_com) { case TRANS_TRANSACT_NMPIPE: case TRANS_READ_NMPIPE: data_params = DCE2_SMB_TRANS__DATA; break; case TRANS_SET_NMPIPE_STATE: case TRANS_WRITE_NMPIPE: data_params = DCE2_SMB_TRANS__PARAMS; break; default: return DCE2_RET__ERROR; } break; case SMB_COM_TRANSACTION2: tpcnt = SmbTransaction2RespTotalParamCnt((SmbTransaction2Resp *)nb_ptr); pcnt = SmbTransaction2RespParamCnt((SmbTransaction2Resp *)nb_ptr); poff = SmbTransaction2RespParamOff((SmbTransaction2Resp *)nb_ptr); pdisp = SmbTransaction2RespParamDisp((SmbTransaction2Resp *)nb_ptr); tdcnt = SmbTransaction2RespTotalDataCnt((SmbTransaction2Resp *)nb_ptr); dcnt = SmbTransaction2RespDataCnt((SmbTransaction2Resp *)nb_ptr); doff = SmbTransaction2RespDataOff((SmbTransaction2Resp *)nb_ptr); ddisp = SmbTransaction2RespDataDisp((SmbTransaction2Resp *)nb_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Transaction2 subcommand: %s (0x%04X)\n", (sub_com < TRANS2_SUBCOM_MAX) ? smb_transaction2_sub_command_strings[sub_com] : "Unknown", sub_com)); if (sub_com < TRANS2_SUBCOM_MAX) dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__RESPONSE][sub_com]++; else dce2_stats.smb_trans2_subcom_stats[SMB_TYPE__RESPONSE][TRANS2_SUBCOM_MAX]++; switch (sub_com) { case TRANS2_OPEN2: case TRANS2_SET_FILE_INFORMATION: data_params = DCE2_SMB_TRANS__PARAMS; break; case TRANS2_QUERY_FILE_INFORMATION: data_params = DCE2_SMB_TRANS__DATA; break; default: return DCE2_RET__ERROR; } break; case SMB_COM_NT_TRANSACT: tpcnt = SmbNtTransactRespTotalParamCnt((SmbNtTransactResp *)nb_ptr); pcnt = SmbNtTransactRespParamCnt((SmbNtTransactResp *)nb_ptr); poff = SmbNtTransactRespParamOff((SmbNtTransactResp *)nb_ptr); pdisp = SmbNtTransactRespParamDisp((SmbNtTransactResp *)nb_ptr); tdcnt = SmbNtTransactRespTotalDataCnt((SmbNtTransactResp *)nb_ptr); dcnt = SmbNtTransactRespDataCnt((SmbNtTransactResp *)nb_ptr); doff = SmbNtTransactRespDataOff((SmbNtTransactResp *)nb_ptr); ddisp = SmbNtTransactRespDataDisp((SmbNtTransactResp *)nb_ptr); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Nt Transact subcommand: %s (0x%04X)\n", (sub_com < NT_TRANSACT_SUBCOM_MAX) ? smb_nt_transact_sub_command_strings[sub_com] : "Unknown", sub_com)); if (sub_com < NT_TRANSACT_SUBCOM_MAX) dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__RESPONSE][sub_com]++; else dce2_stats.smb_nt_transact_subcom_stats[SMB_TYPE__RESPONSE][NT_TRANSACT_SUBCOM_MAX]++; switch (sub_com) { case NT_TRANSACT_CREATE: data_params = DCE2_SMB_TRANS__PARAMS; break; default: return DCE2_RET__ERROR; } break; default: return DCE2_RET__ERROR; } DCE2_MOVE(nb_ptr, nb_len, DCE2_ComInfoCommandSize(com_info)); // From client request if (ttracker->smb_type == SMB_TYPE__REQUEST) { ttracker->smb_type = SMB_TYPE__RESPONSE; ttracker->tdcnt = tdcnt; ttracker->tpcnt = tpcnt; ttracker->dsent = 0; ttracker->psent = 0; DCE2_BufferDestroy(ttracker->dbuf); ttracker->dbuf = NULL; DCE2_BufferDestroy(ttracker->pbuf); ttracker->pbuf = NULL; } else { if (tdcnt < ttracker->tdcnt) ttracker->tdcnt = tdcnt; if (tpcnt < ttracker->tpcnt) ttracker->tpcnt = pcnt; } if (DCE2_SmbValidateTransactionFields(ssd, (uint8_t *)smb_hdr, nb_ptr, nb_len, DCE2_ComInfoByteCount(com_info), tdcnt, tpcnt, dcnt, doff, ddisp, pcnt, poff, pdisp) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; if (DCE2_SmbValidateTransactionSent(ssd, ttracker->dsent, dcnt, ttracker->tdcnt, ttracker->psent, pcnt, ttracker->tpcnt) != DCE2_RET__SUCCESS) return DCE2_RET__ERROR; ttracker->dsent += dcnt; ttracker->psent += pcnt; DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Data displacement: %u, " "Data count: %u, Total data count: %u\n" "Parameter displacement: %u, " "Parameter count: %u, Total parameter count: %u\n", ddisp, dcnt, tdcnt, pdisp, pcnt, tpcnt)); if (data_params & DCE2_SMB_TRANS__DATA) { DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + doff) - nb_ptr); if ((ttracker->dsent < ttracker->tdcnt) || (ttracker->psent < ttracker->tpcnt) || !DCE2_BufferIsEmpty(ttracker->dbuf)) { if ((dcnt != 0) && (DCE2_SmbBufferTransactionData(ttracker, nb_ptr, dcnt, ddisp) != DCE2_RET__SUCCESS)) { return DCE2_RET__ERROR; } } } if (data_params & DCE2_SMB_TRANS__PARAMS) { DCE2_MOVE(nb_ptr, nb_len, ((uint8_t *)smb_hdr + poff) - nb_ptr); if ((ttracker->dsent < ttracker->tdcnt) || (ttracker->psent < ttracker->tpcnt) || !DCE2_BufferIsEmpty(ttracker->dbuf)) { if ((pcnt != 0) && (DCE2_SmbBufferTransactionParameters(ttracker, nb_ptr, pcnt, pdisp) != DCE2_RET__SUCCESS)) { return DCE2_RET__ERROR; } } } if ((ttracker->dsent == ttracker->tdcnt) && (ttracker->psent == ttracker->tpcnt)) { return DCE2_RET__FULL; } return DCE2_RET__SUCCESS; } static inline void DCE2_SmbRemoveFileTrackerFromRequestTrackers(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker) { DCE2_SmbRequestTracker *rtracker; if (ftracker == NULL) return; // NULL out file trackers of any outstanding requests // that reference this file tracker if (ssd->rtracker.ftracker == ftracker) ssd->rtracker.ftracker = NULL; if ((ssd->cur_rtracker != NULL) && (ssd->cur_rtracker->ftracker == ftracker)) ssd->cur_rtracker->ftracker = NULL; for (rtracker = DCE2_QueueFirst(ssd->rtrackers); rtracker != NULL; rtracker = DCE2_QueueNext(ssd->rtrackers)) { if (rtracker->ftracker == ftracker) rtracker->ftracker = NULL; } } static inline void DCE2_SmbSetNewFileAPIFileTracker(DCE2_SmbSsnData *ssd) { DCE2_SmbFileTracker *ftracker = &ssd->ftracker; while (ftracker != NULL) { if ((ftracker != ssd->fapi_ftracker) && (ftracker->fid_v1 != DCE2_SENTINEL) && !ftracker->is_ipc && ftracker->ff_sequential_only && (ftracker->ff_bytes_processed == 0)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Designating file tracker " "for file API processing: \"%s\" (0x%04X)\n", ftracker->file_name, (uint16_t)ftracker->fid_v1);); break; } if (ftracker == &ssd->ftracker) ftracker = DCE2_ListFirst(ssd->ftrackers); else ftracker = DCE2_ListNext(ssd->ftrackers); } ssd->fapi_ftracker = ftracker; } static inline void DCE2_SmbResetFileChunks(DCE2_SmbFileTracker *ftracker) { if (ftracker == NULL) return; DCE2_ListDestroy(ftracker->ff_file_chunks); ftracker->ff_file_chunks = NULL; ftracker->ff_bytes_queued = 0; } static inline void DCE2_SmbAbortFileAPI(DCE2_SmbSsnData *ssd) { DCE2_SmbResetFileChunks(ssd->fapi_ftracker); ssd->fapi_ftracker = NULL; } #ifdef ACTIVE_RESPONSE static uint8_t dce2_smb_delete_pdu[65535]; void DCE2_SmbInitDeletePdu(void) { NbssHdr *nb_hdr = (NbssHdr *)dce2_smb_delete_pdu; SmbNtHdr *smb_hdr = (SmbNtHdr *)((uint8_t *)nb_hdr + sizeof(*nb_hdr)); SmbDeleteReq *del_req = (SmbDeleteReq *)((uint8_t *)smb_hdr + sizeof(*smb_hdr)); uint8_t *del_req_fmt = (uint8_t *)del_req + sizeof(*del_req); uint16_t smb_flg2 = 0xc843; uint16_t search_attrs = 0x0006; memset(dce2_smb_delete_pdu, 0, sizeof(dce2_smb_delete_pdu)); nb_hdr->type = 0; nb_hdr->flags = 0; memcpy((void *)smb_hdr->smb_idf, (void *)"\xffSMB", sizeof(smb_hdr->smb_idf)); smb_hdr->smb_com = SMB_COM_DELETE; smb_hdr->smb_status.nt_status = 0; //smb_hdr->smb_flg = 0x18; smb_hdr->smb_flg = 0; smb_hdr->smb_flg2 = SmbHtons(&smb_flg2); smb_hdr->smb_tid = 0; // needs to be set before injected smb_hdr->smb_pid = 777; smb_hdr->smb_uid = 0; // needs to be set before injected smb_hdr->smb_mid = 777; del_req->smb_wct = 1; del_req->smb_search_attrs = SmbHtons(&search_attrs); *del_req_fmt = SMB_FMT__ASCII; } static void DCE2_SmbInjectDeletePdu(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker) { NbssHdr *nb_hdr = (NbssHdr *)dce2_smb_delete_pdu; SmbNtHdr *smb_hdr = (SmbNtHdr *)((uint8_t *)nb_hdr + sizeof(*nb_hdr)); SmbDeleteReq *del_req = (SmbDeleteReq *)((uint8_t *)smb_hdr + sizeof(*smb_hdr)); char *del_filename = (char *)((uint8_t *)del_req + sizeof(*del_req) + 1); uint32_t len; uint16_t file_name_len = ftracker->file_name_len; nb_hdr->length = htons(sizeof(*smb_hdr) + sizeof(*del_req) + 1 + file_name_len); len = ntohs(nb_hdr->length) + sizeof(*nb_hdr); smb_hdr->smb_tid = SmbHtons(&ftracker->tid_v1); smb_hdr->smb_uid = SmbHtons(&ftracker->uid_v1); del_req->smb_bcc = 1 + file_name_len; if (SmbUnicode(smb_hdr)) memcpy(del_filename, ftracker->file_name + UTF_16_LE_BOM_LEN, file_name_len - UTF_16_LE_BOM_LEN); else memcpy(del_filename, ftracker->file_name, file_name_len); _dpd.activeInjectData((void *)ssd->sd.wire_pkt, 0, (uint8_t *)nb_hdr, len); } static void DCE2_SmbFinishFileBlockVerdict(DCE2_SmbSsnData *ssd) { void *ssnptr = ssd->sd.wire_pkt->stream_session; void *p = (void *)ssd->sd.wire_pkt; File_Verdict verdict; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_file); verdict = DCE2_SmbGetFileVerdict(p, ssnptr); if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)) { DCE2_SmbInjectDeletePdu(ssd, ssd->fb_ftracker); PREPROC_PROFILE_START(dce2_pstat_smb_file_api); _dpd.fileAPI->render_block_verdict(NULL, p); PREPROC_PROFILE_END(dce2_pstat_smb_file_api); } ssd->fb_ftracker = NULL; ssd->block_pdus = false; PREPROC_PROFILE_END(dce2_pstat_smb_file); } static File_Verdict DCE2_SmbGetFileVerdict(void *p, void *ssnptr) { File_Verdict verdict; PROFILE_VARS; PREPROC_PROFILE_START(dce2_pstat_smb_file_api); verdict = _dpd.fileAPI->get_file_verdict(ssnptr); if (verdict == FILE_VERDICT_PENDING) { _dpd.fileAPI->file_signature_lookup(p, true); verdict = _dpd.fileAPI->get_file_verdict(ssnptr); } PREPROC_PROFILE_END(dce2_pstat_smb_file_api); return verdict; } #endif static inline DCE2_SmbRetransmitPending DCE2_SmbFinishFileAPI(DCE2_SmbSsnData *ssd) { void *ssnptr = ssd->sd.wire_pkt->stream_session; void *p = (void *)ssd->sd.wire_pkt; DCE2_SmbFileTracker *ftracker = ssd->fapi_ftracker; bool upload; PROFILE_VARS; if (ftracker == NULL) return DCE2_SMB_RETRANSMIT_PENDING__UNSET; PREPROC_PROFILE_START(dce2_pstat_smb_file); upload = _dpd.fileAPI->get_file_direction(ssnptr); /*This is a case of retrasmitted packet in upload sceanrio with Pending verdict*/ if ((ssd->smbretransmit)) { ssd->smbretransmit = false; _dpd.fileAPI->file_signature_lookup(p, true); File_Verdict verdict = _dpd.fileAPI->get_file_verdict(ssnptr); if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)) { ssd->fb_ftracker = ftracker; ssd->fapi_ftracker = NULL; PREPROC_PROFILE_END(dce2_pstat_smb_file); return DCE2_SMB_RETRANSMIT_PENDING__UNSET; } else if (verdict == FILE_VERDICT_PENDING) { PREPROC_PROFILE_END(dce2_pstat_smb_file_api); return DCE2_SMB_RETRANSMIT_PENDING__SET; } else /*if we get some other verdict , clean up*/ { ssd->fapi_ftracker = NULL; PREPROC_PROFILE_END(dce2_pstat_smb_file); return DCE2_SMB_RETRANSMIT_PENDING__UNSET; } } if (_dpd.fileAPI->get_file_processed_size(ssnptr) != 0) { // Never knew the size of the file so never knew when to tell the // fileAPI the upload/download was finished. if ((ftracker->ff_file_size == 0) && (ftracker->ff_bytes_processed != 0)) { DCE2_SmbSetFileName(ftracker->file_name, ftracker->file_name_len); PREPROC_PROFILE_START(dce2_pstat_smb_file_api); #ifdef ACTIVE_RESPONSE if (_dpd.fileAPI->file_process(p, NULL, 0, SNORT_FILE_END, upload, upload, false)) { if (upload) { File_Verdict verdict = _dpd.fileAPI->get_file_verdict(ssd->sd.wire_pkt->stream_session); if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)) ssd->fb_ftracker = ftracker; else if ((verdict == FILE_VERDICT_PENDING) && (smb_upload_ret_cb_id != 0)) { _dpd.streamAPI->set_event_handler(ssnptr, smb_upload_ret_cb_id, SE_REXMIT); PREPROC_PROFILE_END(dce2_pstat_smb_file_api); return DCE2_SMB_RETRANSMIT_PENDING__SET; } } } #else (void)_dpd.fileAPI->file_process(p, NULL, 0, SNORT_FILE_END, upload, false, false); #endif PREPROC_PROFILE_END(dce2_pstat_smb_file_api); dce2_stats.smb_files_processed++; } } ssd->fapi_ftracker = NULL; PREPROC_PROFILE_END(dce2_pstat_smb_file); return DCE2_SMB_RETRANSMIT_PENDING__UNSET; } static inline bool DCE2_SmbIsVerdictSuspend(bool upload, FilePosition position) { #ifdef ACTIVE_RESPONSE if (upload && ((position == SNORT_FILE_FULL) || (position == SNORT_FILE_END))) return true; #endif return false; } static DCE2_Ret DCE2_SmbFileAPIProcess(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker, const uint8_t *data_ptr, uint32_t data_len, bool upload) { FilePosition position; PROFILE_VARS; #ifdef ACTIVE_RESPONSE if (ssd->fb_ftracker && (ssd->fb_ftracker != ftracker)) return DCE2_RET__SUCCESS; #endif // Trim data length if it exceeds the maximum file depth if ((ssd->max_file_depth != 0) && (ftracker->ff_bytes_processed + data_len) > (uint64_t)ssd->max_file_depth) data_len = ssd->max_file_depth - ftracker->ff_bytes_processed; if (ftracker->ff_file_size == 0) { // Don't know the file size. if ((ftracker->ff_bytes_processed == 0) && (ssd->max_file_depth != 0) && (data_len == (uint64_t)ssd->max_file_depth)) position = SNORT_FILE_FULL; else if (ftracker->ff_bytes_processed == 0) position = SNORT_FILE_START; else if ((ssd->max_file_depth != 0) && ((ftracker->ff_bytes_processed + data_len) == (uint64_t)ssd->max_file_depth)) position = SNORT_FILE_END; else position = SNORT_FILE_MIDDLE; } else { if ((ftracker->ff_bytes_processed == 0) && ((data_len == ftracker->ff_file_size) || ((ssd->max_file_depth != 0) && (data_len == (uint64_t)ssd->max_file_depth)))) position = SNORT_FILE_FULL; else if (ftracker->ff_bytes_processed == 0) position = SNORT_FILE_START; else if (((ftracker->ff_bytes_processed + data_len) >= ftracker->ff_file_size) || ((ssd->max_file_depth != 0) && ((ftracker->ff_bytes_processed + data_len) == (uint64_t)ssd->max_file_depth))) position = SNORT_FILE_END; else position = SNORT_FILE_MIDDLE; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Sending SMB file data to file API ........\n")); PREPROC_PROFILE_START(dce2_pstat_smb_file_api); if (!_dpd.fileAPI->file_process((void *)ssd->sd.wire_pkt, (uint8_t *)data_ptr, (int)data_len, position, upload, DCE2_SmbIsVerdictSuspend(upload, position), false)) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "File API returned FAILURE " "for (0x%02X) %s\n", ftracker->fid_v1, upload ? "UPLOAD" : "DOWNLOAD")); PREPROC_PROFILE_END(dce2_pstat_smb_file_api); // Failure. Abort tracking this file under file API return DCE2_RET__ERROR; } else { PREPROC_PROFILE_END(dce2_pstat_smb_file_api); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "File API returned SUCCESS " "for (0x%02X) %s\n", ftracker->fid_v1, upload ? "UPLOAD" : "DOWNLOAD")); if (((position == SNORT_FILE_START) || (position == SNORT_FILE_FULL)) && (smb_file_name_len != 0)) { _dpd.fileAPI->set_file_name((void *)ssd->sd.wire_pkt->stream_session, (uint8_t *)smb_file_name, smb_file_name_len, false); } if ((position == SNORT_FILE_FULL) || (position == SNORT_FILE_END)) { #ifdef ACTIVE_RESPONSE if (upload) { File_Verdict verdict = _dpd.fileAPI->get_file_verdict(ssd->sd.wire_pkt->stream_session); if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT) || (verdict == FILE_VERDICT_PENDING)) { ssd->fb_ftracker = ftracker; } } #endif ftracker->ff_sequential_only = false; dce2_stats.smb_files_processed++; return DCE2_RET__FULL; } } return DCE2_RET__SUCCESS; } static int DCE2_SmbFileOffsetCompare(const void *a, const void *b) { const DCE2_SmbFileChunk *x = (DCE2_SmbFileChunk *)a; const DCE2_SmbFileChunk *y = (DCE2_SmbFileChunk *)b; if (x->offset > y->offset) return 1; if (x->offset < y->offset) return -1; return 0; } static void DCE2_SmbFileChunkFree(void *data) { DCE2_SmbFileChunk *fc = (DCE2_SmbFileChunk *)data; if (fc == NULL) return; if (fc->data != NULL) DCE2_Free((void *)fc->data, fc->length, DCE2_MEM_TYPE__SMB_FILE); DCE2_Free((void *)fc, sizeof(DCE2_SmbFileChunk), DCE2_MEM_TYPE__SMB_FILE); } static DCE2_Ret DCE2_SmbHandleOutOfOrderFileData(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker, const uint8_t *data_ptr, uint32_t data_len, bool upload) { if (ftracker->ff_file_offset == ftracker->ff_bytes_processed) { uint64_t initial_offset = ftracker->ff_file_offset; uint64_t next_offset = initial_offset + data_len; DCE2_SmbFileChunk *file_chunk = DCE2_ListFirst(ftracker->ff_file_chunks); DCE2_Ret ret = DCE2_SmbFileAPIProcess(ssd, ftracker, data_ptr, data_len, upload); ftracker->ff_bytes_processed += data_len; ftracker->ff_file_offset = ftracker->ff_bytes_processed; if (ret != DCE2_RET__SUCCESS) return ret; // Should already be chunks in here if we came into this function // with an in order chunk, but check just in case. if (file_chunk == NULL) return DCE2_RET__ERROR; while (file_chunk != NULL) { if (file_chunk->offset > next_offset) break; if (file_chunk->offset == next_offset) { ret = DCE2_SmbFileAPIProcess(ssd, ftracker, file_chunk->data, file_chunk->length, upload); ftracker->ff_bytes_processed += file_chunk->length; ftracker->ff_file_offset = ftracker->ff_bytes_processed; if (ret != DCE2_RET__SUCCESS) return ret; next_offset = file_chunk->offset + file_chunk->length; } ftracker->ff_bytes_queued -= file_chunk->length; DCE2_ListRemoveCurrent(ftracker->ff_file_chunks); file_chunk = DCE2_ListNext(ftracker->ff_file_chunks); } if (initial_offset == 0) DCE2_SmbResetFileChunks(ftracker); } else { DCE2_SmbFileChunk *file_chunk; DCE2_Ret ret; if (ftracker->ff_file_chunks == NULL) { ftracker->ff_file_chunks = DCE2_ListNew(DCE2_LIST_TYPE__SORTED, DCE2_SmbFileOffsetCompare, DCE2_SmbFileChunkFree, NULL, DCE2_LIST_FLAG__NO_DUPS, DCE2_MEM_TYPE__SMB_FILE); if (ftracker->ff_file_chunks == NULL) return DCE2_RET__ERROR; } if ((ftracker->ff_bytes_queued + data_len) > (DCE2_GcMemcap() >> 4)) return DCE2_RET__ERROR; file_chunk = DCE2_Alloc(sizeof(DCE2_SmbFileChunk), DCE2_MEM_TYPE__SMB_FILE); if (file_chunk == NULL) return DCE2_RET__ERROR; file_chunk->data = DCE2_Alloc(data_len, DCE2_MEM_TYPE__SMB_FILE); if (file_chunk->data == NULL) { DCE2_Free(file_chunk, sizeof(DCE2_SmbFileChunk), DCE2_MEM_TYPE__SMB_FILE); return DCE2_RET__ERROR; } file_chunk->offset = ftracker->ff_file_offset; file_chunk->length = data_len; memcpy(file_chunk->data, data_ptr, data_len); ftracker->ff_bytes_queued += data_len; if ((ret = DCE2_ListInsert(ftracker->ff_file_chunks, (void *)file_chunk, (void *)file_chunk)) != DCE2_RET__SUCCESS) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Insert file chunk failed: " "0x%02X.\n", ftracker->fid_v1);); DCE2_Free(file_chunk->data, data_len, DCE2_MEM_TYPE__SMB_FILE); DCE2_Free(file_chunk, sizeof(DCE2_SmbFileChunk), DCE2_MEM_TYPE__SMB_FILE); if (ret != DCE2_RET__DUPLICATE) return DCE2_RET__ERROR; } } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Currently buffering %u bytes " "of out of order file data.\n", ftracker->ff_bytes_queued);); return DCE2_RET__SUCCESS; } /******************************************************************** * Function: DCE2_SmbProcessFileData() * * Purpose: * Processes regular file data send via reads/writes. Sends * data to the file API for type id and signature and sets the * file data ptr for rule inspection. * * Arguments: * DCE2_SmbSsnData * - pointer to SMB session data * DCE2_SmbFileTracker * - pointer to file tracker * const uint8_t * - pointer to file data * uint32_t - length of file data * bool - whether it's an upload (true) or * download (false) * * Returns: None * ********************************************************************/ static void DCE2_SmbProcessFileData(DCE2_SmbSsnData *ssd, DCE2_SmbFileTracker *ftracker, const uint8_t *data_ptr, uint32_t data_len, bool upload) { bool cur_upload = DCE2_SmbFileUpload(ftracker->ff_file_direction) ? true : false; int64_t file_data_depth = DCE2_ScSmbFileDepth(ssd->sd.sconfig); PROFILE_VARS; if (data_len == 0) return; PREPROC_PROFILE_START(dce2_pstat_smb_file); DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "File size: "STDu64", File offset: "STDu64", Bytes processed: "STDu64", " "Data len: %u\n", ftracker->ff_file_size, ftracker->ff_file_offset, ftracker->ff_bytes_processed, data_len);); // Account for wrapping. Not likely but just in case. if ((ftracker->ff_bytes_processed + data_len) < ftracker->ff_bytes_processed) { DCE2_SmbRemoveFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } if ((ftracker->ff_bytes_processed == 0) && DCE2_SmbFileDirUnknown(ftracker->ff_file_direction)) { ftracker->ff_file_direction = upload ? DCE2_SMB_FILE_DIRECTION__UPLOAD : DCE2_SMB_FILE_DIRECTION__DOWNLOAD; } else if (cur_upload != upload) { if (cur_upload) { // Went from writing to reading. Ignore the read. DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Went from writing to " "reading - ignoring read.\n");); PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Went from reading to " "writing - consider transfer done.\n");); // Went from reading to writing. Consider the transfer done // and remove the file tracker. DCE2_SmbRemoveFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } if ((file_data_depth != -1) && ((ftracker->ff_file_offset == ftracker->ff_bytes_processed) && ((file_data_depth == 0) || (ftracker->ff_bytes_processed < (uint64_t)file_data_depth)))) { _dpd.setFileDataPtr((uint8_t *)data_ptr, (data_len > UINT16_MAX) ? UINT16_MAX : (uint16_t)data_len); DCE2_FileDetect(&ssd->sd); } if (ftracker == ssd->fapi_ftracker) { DCE2_Ret ret; if ((ftracker->ff_file_offset != ftracker->ff_bytes_processed) || !DCE2_ListIsEmpty(ftracker->ff_file_chunks)) { if ((ssd->max_file_depth != 0) && (ftracker->ff_file_offset >= (uint64_t)ssd->max_file_depth)) { // If the offset is beyond the max file depth, ignore it. PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } else if (upload && (data_len == 1) && (ftracker->ff_file_offset > ftracker->ff_bytes_processed)) { // Sometimes a write one byte is done at a high offset, I'm // guessing to make sure the system has sufficient disk // space to complete the full write. Ignore it because it // will likely be overwritten. PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } if ((ftracker->ff_file_offset == 0) && (ftracker->ff_bytes_processed != 0)) { // Sometimes initial reads/writes are out of order to get file info // such as an icon, then proceed to write in order. Usually the // first read/write is at offset 0, then the next ones are somewhere // off in the distance. Reset and continue on below. DCE2_SmbResetFileChunks(ftracker); ftracker->ff_bytes_processed = 0; } else if (ftracker->ff_file_offset < ftracker->ff_bytes_processed) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "File offset ("STDu64") is " "less than bytes processed ("STDu64") - aborting.\n", ftracker->ff_file_offset, ftracker->ff_bytes_processed);); DCE2_SmbAbortFileAPI(ssd); DCE2_SmbSetNewFileAPIFileTracker(ssd); PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } else { ret = DCE2_SmbHandleOutOfOrderFileData(ssd, ftracker, data_ptr, data_len, upload); if (ret != DCE2_RET__SUCCESS) { DCE2_SmbAbortFileAPI(ssd); DCE2_SmbSetNewFileAPIFileTracker(ssd); } PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } } ret = DCE2_SmbFileAPIProcess(ssd, ftracker, data_ptr, data_len, upload); ftracker->ff_bytes_processed += data_len; ftracker->ff_file_offset = ftracker->ff_bytes_processed; if (ret != DCE2_RET__SUCCESS) { DCE2_SmbAbortFileAPI(ssd); DCE2_SmbSetNewFileAPIFileTracker(ssd); } } else { if (ftracker->ff_file_offset == ftracker->ff_bytes_processed) { ftracker->ff_bytes_processed += data_len; ftracker->ff_file_offset = ftracker->ff_bytes_processed; } if ((file_data_depth == -1) || ((file_data_depth != 0) && (ftracker->ff_bytes_processed >= (uint64_t)file_data_depth))) { DEBUG_WRAP(DCE2_DebugMsg(DCE2_DEBUG__SMB, "Bytes processed ("STDu64") " "is at or beyond file data depth ("STDu64") - finished.\n", ftracker->ff_bytes_processed, file_data_depth);); DCE2_SmbRemoveFileTracker(ssd, ftracker); PREPROC_PROFILE_END(dce2_pstat_smb_file); return; } } PREPROC_PROFILE_END(dce2_pstat_smb_file); } /******************************************************************** * Function: DCE2_SmbSetFileName() * * Purpose: * Copies the NULL terminated file name passed in to the global * array for logging the file name for events. * * Arguments: * uint8_t* - NULL terminated file name(ASCII or UTF-16LE) * file_name returned by DCE2_SmbGetString can be max 2*DCE2_SMB_MAX_PATH_LEN + UTF_16_LE_BOM_LEN + 2 bytes * No need to check for overflow * uint16_t - file_name_len which includes NULL terminated bytes * * Returns: None * ********************************************************************/ static inline void DCE2_SmbSetFileName(uint8_t* file_name, uint16_t file_name_len) { if (file_name == NULL) return; smb_file_name_len = file_name_len; memcpy(smb_file_name, file_name, file_name_len); } /******************************************************************** * Function: DCE2_SmbGetString() * * Purpose: * Parses data passed in and returns a byte stream. * unicode stream is prepended with BOM * * Arguments: * const uint8_t * - pointer to data * uint32_t - data length * bool - true if the data is unicode (UTF-16LE) * uint16_t * - Returns the length of the output buffer including the NULL terminated bytes * * Returns: * uint8_t * - NULL terminated byte stream (ASCII or UTF-16LE with BOM) * ********************************************************************/ static uint8_t* DCE2_SmbGetString(const uint8_t *data, uint32_t data_len, bool unicode, uint16_t *file_name_len) { uint8_t *fname = NULL; uint32_t i = 0; uint8_t inc = unicode ? 2 : 1; *file_name_len = 0; if (data_len < inc) return NULL; for (i = 0; i < data_len; i += inc) { uint16_t uchar = unicode ? SmbNtohs((uint16_t *)(data + i)) : data[i]; if (uchar == 0) break; } if(i > inc*DCE2_SMB_MAX_PATH_LEN) return NULL; if(unicode) { fname = (uint8_t *)DCE2_Alloc(i + UTF_16_LE_BOM_LEN + 2, DCE2_MEM_TYPE__SMB_SSN); if (fname == NULL) return NULL; memcpy(fname, UTF_16_LE_BOM, UTF_16_LE_BOM_LEN);//Prepend with BOM memcpy(fname + UTF_16_LE_BOM_LEN, data, i); *file_name_len = i + UTF_16_LE_BOM_LEN + 2; } else { fname = (uint8_t *)DCE2_Alloc(i + 1, DCE2_MEM_TYPE__SMB_SSN); if (fname == NULL) return NULL; memcpy(fname, data, i); *file_name_len = i + 1; } return fname; } static inline void DCE2_Update_Ftracker_from_ReqTracker(DCE2_SmbFileTracker *ftracker, DCE2_SmbRequestTracker *cur_rtracker) { ftracker->file_name = cur_rtracker->file_name; ftracker->file_name_len = cur_rtracker->file_name_len; cur_rtracker->file_name = NULL; cur_rtracker->file_name_len = 0; return; } void DCE2_Process_Retransmitted(SFSnortPacket *p) { DCE2_SsnData *sd = (DCE2_SsnData *)DCE2_SsnGetAppData(p); if (sd != NULL) { sd->wire_pkt = p; DCE2_SmbSsnData *ssd = (DCE2_SmbSsnData *)sd; ssd->smbretransmit = true; DCE2_SmbRemoveFileTracker(ssd, &(ssd->ftracker)); } } snort-2.9.20/src/dynamic-preprocessors/dnp3/0000755000175000017500000000000014242725716017071 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_map.c0000644000175000017500000001002314241075710020721 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Tables for DNP3 function & indicator definitions * */ #include #include #include "dnp3_map.h" /* Name/value pair struct */ typedef struct _dnp3_map_t { char *name; uint16_t value; } dnp3_map_t; /* Mapping of name -> function code for "dnp3_func" option. */ static dnp3_map_t func_map[] = { {"confirm", 0}, {"read", 1}, {"write", 2}, {"select", 3}, {"operate", 4}, {"direct_operate", 5}, {"direct_operate_nr", 6}, {"immed_freeze", 7}, {"immed_freeze_nr", 8}, {"freeze_clear", 9}, {"freeze_clear_nr", 10}, {"freeze_at_time", 11}, {"freeze_at_time_nr", 12}, {"cold_restart", 13}, {"warm_restart", 14}, {"initialize_data", 15}, {"initialize_appl", 16}, {"start_appl", 17}, {"stop_appl", 18}, {"save_config", 19}, {"enable_unsolicited", 20}, {"disable_unsolicited", 21}, {"assign_class", 22}, {"delay_measure", 23}, {"record_current_time", 24}, {"open_file", 25}, {"close_file", 26}, {"delete_file", 27}, {"get_file_info", 28}, {"authenticate_file", 29}, {"abort_file", 30}, {"activate_config", 31}, {"authenticate_req", 32}, {"authenticate_err", 33}, {"response", 129}, {"unsolicited_response", 130}, {"authenticate_resp", 131} }; /* Mapping of name -> indication bit for "dnp3_ind" option. */ static dnp3_map_t indication_map[] = { /* The order is strange, but this is the order in which the spec lists them. */ {"all_stations", 0x0100}, {"class_1_events", 0x0200}, {"class_2_events", 0x0400}, {"class_3_events", 0x0800}, {"need_time", 0x1000}, {"local_control", 0x2000}, {"device_trouble", 0x4000}, {"device_restart", 0x8000}, {"no_func_code_support", 0x0001}, {"object_unknown", 0x0002}, {"parameter_error", 0x0004}, {"event_buffer_overflow", 0x0008}, {"already_executing", 0x0010}, {"config_corrupt", 0x0020}, {"reserved_2", 0x0040}, {"reserved_1", 0x0080}, }; int DNP3FuncIsDefined(uint16_t code) { size_t num_funcs = sizeof(func_map) / sizeof(func_map[0]); size_t i; int func_is_defined = 0; /* Check to see if code is higher than all codes in func map */ if (code > func_map[num_funcs-1].value) return func_is_defined; for (i = 0; i < num_funcs-1; i++) { /* This short-circuit check assumes that the function map remains in-order. */ if (code <= func_map[i].value) break; } if (code == func_map[i].value) func_is_defined = 1; return func_is_defined; } int DNP3FuncStrToCode(char *name) { size_t num_funcs = sizeof(func_map) / sizeof(func_map[0]); size_t i; for (i = 0; i < num_funcs; i++) { if (strcmp(name, func_map[i].name) == 0) return func_map[i].value; } return -1; } int DNP3IndStrToCode(char *name) { size_t num_indications = sizeof(indication_map) / sizeof(indication_map[0]); size_t i; for (i = 0; i < num_indications; i++) { if (strcmp(name, indication_map[i].name) == 0) return indication_map[i].value; } return -1; } snort-2.9.20/src/dynamic-preprocessors/dnp3/spp_dnp3.c0000644000175000017500000010444614241075720020764 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the DNP3 protocol * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "mempool.h" #include "preprocids.h" #include "spp_dnp3.h" #include "sf_preproc_info.h" #include "dnp3_paf.h" #include "dnp3_reassembly.h" #include "dnp3_roptions.h" #include "profiler.h" #ifdef PERF_PROFILING PreprocStats dnp3PerfStats; #endif #ifdef DUMP_BUFFER #include "dnp3_buffer_dump.h" void dumpBufferInit(void); #endif #ifdef SNORT_RELOAD #include "appdata_adjuster.h" static APPDATA_ADJUSTER *ada; #endif #ifdef REG_TEST #include "reg_test.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_DNP3"; #define SetupDNP3 DYNAMIC_PREPROC_SETUP /* Preprocessor config objects */ static tSfPolicyUserContextId dnp3_context_id = NULL; static dnp3_config_t *dnp3_eval_config = NULL; static MemPool *dnp3_mempool = NULL; /* Target-based app ID */ #ifdef TARGET_BASED int16_t dnp3_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif /* Prototypes */ static void DNP3Init(struct _SnortConfig *, char *); static inline void DNP3OneTimeInit(struct _SnortConfig *); static inline dnp3_config_t * DNP3PerPolicyInit(struct _SnortConfig *, tSfPolicyUserContextId); static void DNP3RegisterPerPolicyCallbacks(struct _SnortConfig *, dnp3_config_t *); static bool DNP3GlobalIsEnabled(tSfPolicyUserContextId context_id); static void DNP3InitializeMempool(tSfPolicyUserContextId context_id); static int DNP3IsEnabled(struct _SnortConfig *sc, tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data); static void ProcessDNP3(void *, void *); #ifdef SNORT_RELOAD static void DNP3Reload(struct _SnortConfig *, char *, void **); static int DNP3ReloadVerify(struct _SnortConfig *, void *); static void * DNP3ReloadSwap(struct _SnortConfig *, void *); static void DNP3ReloadSwapFree(void *); static bool DNP3ReloadAdjustFunc(bool idle, tSfPolicyId raPolicyId, void *userData); #endif static void _addPortsToStreamFilter(struct _SnortConfig *, dnp3_config_t *, tSfPolicyId); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static void DNP3FreeConfig(tSfPolicyUserContextId context_id); static void FreeDNP3Data(void *); static int DNP3CheckConfig(struct _SnortConfig *); static void DNP3CleanExit(int, void *); static void ParseDNP3Args(struct _SnortConfig *, dnp3_config_t *, char *); static void PrintDNP3Config(dnp3_config_t *config); static int DNP3PortCheck(dnp3_config_t *config, SFSnortPacket *packet); static MemBucket * DNP3CreateSessionData(SFSnortPacket *); static size_t DNP3MemInUse(); /* Default memcap is defined as MAX_TCP_SESSIONS * .05 * 20 bytes */ #define DNP3_DEFAULT_MEMCAP (256 * 1024) /* Register init callback */ void SetupDNP3(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc("dnp3", DNP3Init); #else _dpd.registerPreproc("dnp3", DNP3Init, DNP3Reload, DNP3ReloadVerify, DNP3ReloadSwap, DNP3ReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getDNP3Buffers, DNP3_BUFFER_DUMP_FUNC); #endif } static void DNP3RegisterPortsWithSession( struct _SnortConfig *sc, dnp3_config_t *policy ) { uint32_t port; for ( port = 0; port < MAX_PORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_DNP3, PROTO_BIT__TCP | PROTO_BIT__UDP, port ); } } #ifdef REG_TEST static inline void PrintDNP3Size(void) { _dpd.logMsg("\nDNP3 Session Size: %lu\n", (long unsigned int)sizeof(dnp3_session_data_t)); } #endif /* Allocate memory for preprocessor config, parse the args, set up callbacks */ static void DNP3Init(struct _SnortConfig *sc, char *argp) { dnp3_config_t *dnp3_policy = NULL; if (dnp3_context_id == NULL) DNP3OneTimeInit(sc); dnp3_policy = DNP3PerPolicyInit(sc, dnp3_context_id); ParseDNP3Args(sc, dnp3_policy, argp); #ifdef REG_TEST PrintDNP3Size(); #endif PrintDNP3Config(dnp3_policy); DNP3InitializeMempool(dnp3_context_id); DNP3RegisterPortsWithSession( sc, dnp3_policy ); DNP3RegisterPerPolicyCallbacks(sc, dnp3_policy); #ifdef DUMP_BUFFER dumpBufferInit(); #endif } static inline void DNP3OneTimeInit(struct _SnortConfig *sc) { /* context creation & error checking */ dnp3_context_id = sfPolicyConfigCreate(); if (dnp3_context_id == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory for " "DNP3 config.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupDNP3(): The Stream preprocessor " "must be enabled.\n"); } /* callback registration */ _dpd.addPreprocConfCheck(sc, DNP3CheckConfig); _dpd.addPreprocExit(DNP3CleanExit, NULL, PRIORITY_LAST, PP_DNP3); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("dnp3", (void *)&dnp3PerfStats, 0, _dpd.totalPerfStats, NULL); #endif /* Set up target-based app id */ #ifdef TARGET_BASED dnp3_app_id = _dpd.findProtocolReference("dnp3"); if (dnp3_app_id == SFTARGET_UNKNOWN_PROTOCOL) dnp3_app_id = _dpd.addProtocolReference("dnp3"); // register with session to handle application _dpd.sessionAPI->register_service_handler( PP_DNP3, dnp3_app_id ); #endif } /* Responsible for allocating a DNP3 policy. Never returns NULL. */ static inline dnp3_config_t * DNP3PerPolicyInit(struct _SnortConfig *sc, tSfPolicyUserContextId context_id) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); dnp3_config_t *dnp3_policy = NULL; /* Check for existing policy & bail if found */ sfPolicyUserPolicySet(context_id, policy_id); dnp3_policy = (dnp3_config_t *)sfPolicyUserDataGetCurrent(context_id); if (dnp3_policy != NULL) { DynamicPreprocessorFatalMessage("%s(%d): DNP3 preprocessor can only be " "configured once.\n", *_dpd.config_file, *_dpd.config_line); } /* Allocate new policy */ dnp3_policy = (dnp3_config_t *)calloc(1, sizeof(dnp3_config_t)); if (!dnp3_policy) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "dnp3 preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(context_id, dnp3_policy); return dnp3_policy; } static bool DNP3GlobalIsEnabled(tSfPolicyUserContextId context_id) { return sfPolicyUserDataIterate(NULL, context_id, DNP3IsEnabled) != 0; } static void DNP3InitializeMempool(tSfPolicyUserContextId context_id) { unsigned int max_sessions; dnp3_config_t *default_config = (dnp3_config_t*)sfPolicyUserDataGetDefault(context_id); if (default_config && DNP3GlobalIsEnabled(context_id)) { #ifdef SNORT_RELOAD #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload init-before: %p %p\n", dnp3_mempool, ada); } #endif #endif if (dnp3_mempool == NULL) { max_sessions = default_config->memcap / sizeof(dnp3_session_data_t); dnp3_mempool = (MemPool *)malloc(sizeof(MemPool)); if (!dnp3_mempool) { DynamicPreprocessorFatalMessage("DNP3InitializeMempool: " "Unable to allocate memory for dnp3 mempool\n"); } //mempool is set to 0 in init if (mempool_init(dnp3_mempool, max_sessions, sizeof(dnp3_session_data_t))) { DynamicPreprocessorFatalMessage("Unable to allocate DNP3 mempool.\n"); } } #ifdef SNORT_RELOAD if (ada == NULL) { ada = ada_init(DNP3MemInUse, PP_DNP3, (size_t) default_config->memcap); if (ada == NULL) DynamicPreprocessorFatalMessage("Unable to allocate DNP3 ada.\n"); } #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload init-after: %p %p\n", dnp3_mempool, ada); } #endif #endif } } static void DNP3RegisterPerPolicyCallbacks(struct _SnortConfig *sc, dnp3_config_t *dnp3_policy) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); /* Callbacks should be avoided if the preproc is disabled. */ if (dnp3_policy->disabled) return; _dpd.addPreproc(sc, ProcessDNP3, PRIORITY_APPLICATION, PP_DNP3, PROTO_BIT__TCP|PROTO_BIT__UDP); _addPortsToStreamFilter(sc, dnp3_policy, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); DNP3AddServiceToPaf(sc, dnp3_app_id, policy_id); #endif DNP3AddPortsToPaf(sc, dnp3_policy, policy_id); _dpd.preprocOptRegister(sc, DNP3_FUNC_NAME, DNP3FuncInit, DNP3FuncEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, DNP3_OBJ_NAME, DNP3ObjInit, DNP3ObjEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, DNP3_IND_NAME, DNP3IndInit, DNP3IndEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, DNP3_DATA_NAME, DNP3DataInit, DNP3DataEval, free, NULL, NULL, NULL, NULL); } static void ParseSinglePort(dnp3_config_t *config, char *token) { /* single port number */ char *endptr; unsigned long portnum = _dpd.SnortStrtoul(token, &endptr, 10); if ((*endptr != '\0') || (portnum >= MAX_PORTS)) { DynamicPreprocessorFatalMessage("%s(%d): Bad dnp3 port number: %s\n" "Port number must be an integer between 0 and 65535.\n", *_dpd.config_file, *_dpd.config_line, token); } /* Good port number! */ config->ports[PORT_INDEX(portnum)] |= CONV_PORT(portnum); } static void ParseDNP3Args(struct _SnortConfig *sc, dnp3_config_t *config, char *args) { char *saveptr; char *token; /* Set defaults */ config->memcap = DNP3_DEFAULT_MEMCAP; config->ports[PORT_INDEX(DNP3_PORT)] |= CONV_PORT(DNP3_PORT); config->check_crc = 0; /* No arguments? Stick with defaults. */ if (args == NULL) return; token = strtok_r(args, " ,", &saveptr); while (token != NULL) { if (strcmp(token, DNP3_PORTS_KEYWORD) == 0) { unsigned nPorts = 0; /* Un-set the default port */ config->ports[PORT_INDEX(DNP3_PORT)] = 0; /* Parse ports */ token = strtok_r(NULL, " ,", &saveptr); if (token == NULL) { DynamicPreprocessorFatalMessage("%s(%d): Missing argument for " "DNP3 preprocessor 'ports' option.\n", *_dpd.config_file, *_dpd.config_line); } if (isdigit(token[0])) { ParseSinglePort(config, token); nPorts++; } else if (*token == '{') { /* list of ports */ token = strtok_r(NULL, " ,", &saveptr); while (token != NULL && *token != '}') { ParseSinglePort(config, token); nPorts++; token = strtok_r(NULL, " ,", &saveptr); } } else { nPorts = 0; } if ( nPorts == 0 ) { DynamicPreprocessorFatalMessage("%s(%d): Bad DNP3 'ports' argument: '%s'\n" "Argument to DNP3 'ports' must be an integer, or a list " "enclosed in { } braces.\n", *_dpd.config_file, *_dpd.config_line, token); } } else if (strcmp(token, DNP3_MEMCAP_KEYWORD) == 0) { uint32_t memcap; char *endptr; /* Parse memcap */ token = strtok_r(NULL, " ", &saveptr); /* In a multiple policy scenario, the memcap from the default policy overrides the memcap in any targeted policies. */ if (_dpd.getParserPolicy(sc) != _dpd.getDefaultPolicy()) { dnp3_config_t *default_config = (dnp3_config_t *)sfPolicyUserDataGet(dnp3_context_id, _dpd.getDefaultPolicy()); if (!default_config || default_config->memcap == 0) { DynamicPreprocessorFatalMessage("%s(%d): DNP3 'memcap' must be " "configured in the default config.\n", *_dpd.config_file, *_dpd.config_line); } config->memcap = default_config->memcap; } else { if (token == NULL) { DynamicPreprocessorFatalMessage("%s(%d): Missing argument for DNP3 " "preprocessor 'memcap' option.\n", *_dpd.config_file, *_dpd.config_line); } memcap = _dpd.SnortStrtoul(token, &endptr, 10); if ((token[0] == '-') || (*endptr != '\0') || (memcap < MIN_DNP3_MEMCAP) || (memcap > MAX_DNP3_MEMCAP)) { DynamicPreprocessorFatalMessage("%s(%d): Bad DNP3 'memcap' argument: %s\n" "Argument to DNP3 'memcap' must be an integer between " "%d and %d.\n", *_dpd.config_file, *_dpd.config_line, token, MIN_DNP3_MEMCAP, MAX_DNP3_MEMCAP); } config->memcap = memcap; } } else if (strcmp(token, DNP3_CHECK_CRC_KEYWORD) == 0) { /* Parse check_crc */ config->check_crc = 1; } else if (strcmp(token, DNP3_DISABLED_KEYWORD) == 0) { /* TODO: if disabled, check that no other stuff is turned on except memcap */ config->disabled = 1; } else { DynamicPreprocessorFatalMessage("%s(%d): Failed to parse dnp3 argument: " "%s\n", *_dpd.config_file, *_dpd.config_line, token); } token = strtok_r(NULL, " ,", &saveptr); } } /* Print a DNP3 config */ static void PrintDNP3Config(dnp3_config_t *config) { int index, newline = 1; if (config == NULL) return; _dpd.logMsg("DNP3 config: \n"); if (config->disabled) _dpd.logMsg(" DNP3: INACTIVE\n"); _dpd.logMsg(" Memcap: %d\n", config->memcap); _dpd.logMsg(" Check Link-Layer CRCs: %s\n", config->check_crc ? "ENABLED":"DISABLED"); _dpd.logMsg(" Ports:\n"); /* Loop through port array & print, 5 ports per line */ for (index = 0; index < MAX_PORTS; index++) { if (config->ports[PORT_INDEX(index)] & CONV_PORT(index)) { _dpd.logMsg("\t%d", index); if ( !((newline++) % 5) ) { _dpd.logMsg("\n"); } } } _dpd.logMsg("\n"); } static int DNP3ProcessUDP(dnp3_config_t *dnp3_eval_config, dnp3_session_data_t *sessp, SFSnortPacket *packetp) { /* Possibly multiple PDUs in this UDP payload. Split up and process individually. */ uint16_t bytes_processed = 0; int truncated_pdu = 0; while (bytes_processed < packetp->payload_size) { uint8_t *pdu_start; uint16_t user_data, num_crcs, pdu_length; dnp3_link_header_t *link; pdu_start = (uint8_t *)(packetp->payload + bytes_processed); link = (dnp3_link_header_t *)pdu_start; /*Stop if the start bytes are not 0x0564 */ if ((packetp->payload_size < bytes_processed + 2) || (link->start != DNP3_START_BYTES)) break; /* Alert and stop if there's not enough data to read a length */ if ((packetp->payload_size - bytes_processed < (int)sizeof(dnp3_link_header_t)) || (link->len < DNP3_HEADER_REMAINDER_LEN)) { truncated_pdu = 1; break; } /* Calculate the actual length of data to inspect */ user_data = link->len - DNP3_HEADER_REMAINDER_LEN; num_crcs = 1 + (user_data/DNP3_CHUNK_SIZE) + (user_data % DNP3_CHUNK_SIZE? 1 : 0); pdu_length = DNP3_MIN_LEN + link->len + (DNP3_CRC_SIZE*num_crcs); if (bytes_processed + pdu_length > packetp->payload_size) { truncated_pdu = 1; break; } DNP3FullReassembly(dnp3_eval_config, sessp, packetp, pdu_start, pdu_length); bytes_processed += pdu_length; } if (truncated_pdu) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_DROPPED_FRAME, 1, 0, 3, DNP3_DROPPED_FRAME_STR, 0); } /* All detection was done when DNP3FullReassembly() called Detect() on the reassembled PDUs. Clear the flag to avoid double alerts on the last PDU. */ if (bytes_processed) _dpd.DetectReset((uint8_t *)packetp->payload, packetp->payload_size); return DNP3_OK; } /* Main runtime entry point */ static void ProcessDNP3(void *ipacketp, void *contextp) { SFSnortPacket *packetp = (SFSnortPacket *)ipacketp; MemBucket *tmp_bucket = NULL; dnp3_session_data_t *sessp = NULL; PROFILE_VARS; // preconditions - what we registered for assert((IsUDP(packetp) || IsTCP(packetp)) && packetp->payload && packetp->payload_size); /* If TCP, require that PAF flushes full PDUs first. */ if (packetp->tcp_header && !PacketHasFullPDU(packetp)) return; PREPROC_PROFILE_START(dnp3PerfStats); /* When pipelined DNP3 PDUs appear in a single TCP segment or UDP packet, the detection engine caches the results of the rule options after evaluating on the first PDU. Setting this flag stops the caching. */ packetp->flags |= FLAG_ALLOW_MULTIPLE_DETECT; /* Fetch me a preprocessor config to use with this VLAN/subnet/etc.! */ dnp3_eval_config = sfPolicyUserDataGetCurrent(dnp3_context_id); /* Look for a previously-allocated session data. */ tmp_bucket = _dpd.sessionAPI->get_application_data(packetp->stream_session, PP_DNP3); if (tmp_bucket == NULL) { /* No existing session. Check those ports. */ if (DNP3PortCheck(dnp3_eval_config, packetp) != DNP3_OK) { PREPROC_PROFILE_END(dnp3PerfStats); return; } /* Create session data and attach it to the Stream session */ tmp_bucket = DNP3CreateSessionData(packetp); if (tmp_bucket == NULL) { PREPROC_PROFILE_END(dnp3PerfStats); return; } } sessp = (dnp3_session_data_t *) tmp_bucket->data; /* Set reassembly direction */ if (packetp->flags & FLAG_FROM_CLIENT) sessp->direction = DNP3_CLIENT; else sessp->direction = DNP3_SERVER; /* Do preprocessor-specific detection stuff here */ if (packetp->tcp_header) { /* Single PDU. PAF already split them up into separate pseudo-packets. */ DNP3FullReassembly(dnp3_eval_config, sessp, packetp, (uint8_t *)packetp->payload, packetp->payload_size); } else if (packetp->udp_header) { DNP3ProcessUDP(dnp3_eval_config, sessp, packetp); } /* That's the end! */ PREPROC_PROFILE_END(dnp3PerfStats); } /* Check ports & services */ static int DNP3PortCheck(dnp3_config_t *config, SFSnortPacket *packet) { #ifdef TARGET_BASED int16_t app_id = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session); /* call to get_application_protocol_id gave an error */ if (app_id == SFTARGET_UNKNOWN_PROTOCOL) return DNP3_FAIL; /* this is positively identified as something non-dnp3 */ if (app_id && (app_id != dnp3_app_id)) return DNP3_FAIL; /* this is identified as dnp3 */ if (app_id == dnp3_app_id) return DNP3_OK; /* fall back to port check */ #endif if (config->ports[PORT_INDEX(packet->src_port)] & CONV_PORT(packet->src_port)) return DNP3_OK; if (config->ports[PORT_INDEX(packet->dst_port)] & CONV_PORT(packet->dst_port)) return DNP3_OK; return DNP3_FAIL; } static MemBucket * DNP3CreateSessionData(SFSnortPacket *packet) { MemBucket *tmp_bucket = NULL; dnp3_session_data_t *data = NULL; /* Sanity Check */ if (!packet || !packet->stream_session) return NULL; /* data = (dnp3_session_data_t *)calloc(1, sizeof(dnp3_session_data_t)); */ tmp_bucket = mempool_alloc(dnp3_mempool); if (!tmp_bucket) { /* Mempool was full, don't process this session. */ static unsigned int times_mempool_alloc_failed = 0; /* Print a message, but only every 1000 times. Don't want to flood the log if there's a lot of DNP3 traffic. */ if (times_mempool_alloc_failed % 1000 == 0) { _dpd.logMsg("WARNING: DNP3 memcap exceeded.\n"); } times_mempool_alloc_failed++; return NULL; } data = (dnp3_session_data_t *)tmp_bucket->data; if (!data) return NULL; /* Attach to Stream session */ _dpd.sessionAPI->set_application_data(packet->stream_session, PP_DNP3, tmp_bucket, FreeDNP3Data); #ifdef SNORT_RELOAD ada_add(ada, tmp_bucket, packet->stream_session); #endif /* Not sure when this reference counting stuff got added to the old preprocs */ data->policy_id = _dpd.getNapRuntimePolicy(); data->context_id = dnp3_context_id; ((dnp3_config_t *)sfPolicyUserDataGetCurrent(dnp3_context_id))->ref_count++; return tmp_bucket; } /* Reload functions */ #ifdef SNORT_RELOAD /* Almost like DNP3Init, but not quite. */ static void DNP3Reload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId dnp3_swap_context_id = (tSfPolicyUserContextId)*new_config; dnp3_config_t *dnp3_policy = NULL; if (dnp3_swap_context_id == NULL) { dnp3_swap_context_id = sfPolicyConfigCreate(); if (dnp3_swap_context_id == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for DNP3 config.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupDNP3(): The Stream preprocessor " "must be enabled.\n"); } *new_config = (void *)dnp3_swap_context_id; } dnp3_policy = DNP3PerPolicyInit(sc, dnp3_swap_context_id); ParseDNP3Args(sc, dnp3_policy, args); DNP3InitializeMempool(dnp3_swap_context_id); PrintDNP3Config(dnp3_policy); DNP3RegisterPortsWithSession( sc, dnp3_policy ); DNP3RegisterPerPolicyCallbacks(sc, dnp3_policy); } /* Check that Stream is still running, and that the memcap didn't change. */ static int DNP3ReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId dnp3_swap_context_id = (tSfPolicyUserContextId)swap_config; dnp3_config_t *current_default_config, *new_default_config; if ((dnp3_context_id == NULL) || (dnp3_swap_context_id == NULL)) return 0; current_default_config = (dnp3_config_t *)sfPolicyUserDataGet(dnp3_context_id, _dpd.getDefaultPolicy()); new_default_config = (dnp3_config_t *)sfPolicyUserDataGet(dnp3_swap_context_id, _dpd.getDefaultPolicy()); /* Sanity check. Shouldn't be possible. */ if (current_default_config == NULL) return 0; if (new_default_config == NULL) { _dpd.errMsg("DNP3 reload: Changing the DNP3 configuration " "requires a restart.\n"); return -1; } //is DNP3 enabled? bool wasEnabled = sfPolicyUserDataIterate(sc, dnp3_context_id, DNP3IsEnabled) != 0; bool isEnabled = sfPolicyUserDataIterate(sc, dnp3_swap_context_id, DNP3IsEnabled) != 0; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); if (wasEnabled && isEnabled) { if (new_default_config->memcap < current_default_config->memcap) { ada_set_new_cap(ada, (size_t) new_default_config->memcap); _dpd.reloadAdjustRegister(sc, "DNP3", policy_id, DNP3ReloadAdjustFunc, (void *) ada, NULL); } else if (new_default_config->memcap > current_default_config->memcap) { unsigned int max_sessions = new_default_config->memcap / sizeof(dnp3_session_data_t); #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload mempool-before: %zu %zu %zu\n", dnp3_mempool->max_memory, dnp3_mempool->used_memory, dnp3_mempool->free_memory); } #endif mempool_setObjectSize(dnp3_mempool, max_sessions, sizeof(dnp3_session_data_t)); #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload mempool-after: %zu %zu %zu\n", dnp3_mempool->max_memory, dnp3_mempool->used_memory, dnp3_mempool->free_memory); } #endif } } else if (wasEnabled) { ada_set_new_cap(ada, 0); _dpd.reloadAdjustRegister(sc, "DNP3", policy_id, DNP3ReloadAdjustFunc, (void *) ada, NULL); } /* Did stream5 get turned off? */ if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("SetupDNP3(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } static int DNP3FreeUnusedConfigPolicy( tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { dnp3_config_t *dnp3_config = (dnp3_config_t *)data; /* do any housekeeping before freeing dnp3 config */ if (dnp3_config->ref_count == 0) { sfPolicyUserDataClear(context_id, policy_id); free(dnp3_config); } return 0; } static void * DNP3ReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId dnp3_swap_context_id = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_context_id = dnp3_context_id; if (dnp3_swap_context_id == NULL) return NULL; dnp3_context_id = dnp3_swap_context_id; sfPolicyUserDataFreeIterate(old_context_id, DNP3FreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_context_id) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_context_id; } return NULL; } static void DNP3ReloadSwapFree(void *data) { if (data == NULL) return; DNP3FreeConfig( (tSfPolicyUserContextId)data ); } #endif /* Stream filter functions */ static void _addPortsToStreamFilter(struct _SnortConfig *sc, dnp3_config_t *config, tSfPolicyId policy_id) { if (config == NULL) return; if (_dpd.streamAPI) { uint32_t portNum; for (portNum = 0; portNum < MAX_PORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status( sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); _dpd.streamAPI->set_port_filter_status( sc, IPPROTO_UDP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); } } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, dnp3_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif static int DNP3FreeConfigPolicy( tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { dnp3_config_t *dnp3_config = (dnp3_config_t *)data; /* do any housekeeping before freeing dnp3_config */ sfPolicyUserDataClear(context_id, policy_id); free(dnp3_config); return 0; } static void DNP3FreeConfig(tSfPolicyUserContextId context_id) { if (context_id == NULL) return; sfPolicyUserDataFreeIterate(context_id, DNP3FreeConfigPolicy); sfPolicyConfigDelete(context_id); } static int DNP3IsEnabled(struct _SnortConfig *sc, tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data) { dnp3_config_t *config = (dnp3_config_t *)data; if ((data == NULL) || config->disabled) return 0; return 1; } /* Check an individual policy */ static int DNP3CheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { dnp3_config_t *config = (dnp3_config_t *)data; _dpd.setParserPolicy(sc, policy_id); /* In a multiple-policy setting, the preprocessor can be turned on in a "disabled" state. In this case, we don't require Stream. */ if (config->disabled) return 0; /* Otherwise, require Stream. */ if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("ERROR: DNP3CheckPolicyConfig(): " "The Stream preprocessor must be enabled.\n"); return -1; } return 0; } /* Check configs & set up mempool. Mempool stuff is in this function because we want to parse & check *ALL* of the configs before allocating a mempool. */ static int DNP3CheckConfig(struct _SnortConfig *sc) { int rval; /* Get default configuration */ dnp3_config_t *default_config = (dnp3_config_t *)sfPolicyUserDataGetDefault(dnp3_context_id); if ( !default_config ) { _dpd.errMsg( "ERROR: preprocessor dnp3 must be configured in the default policy.\n"); return -1; } /* Check all individual configurations */ if ((rval = sfPolicyUserDataIterate(sc, dnp3_context_id, DNP3CheckPolicyConfig))) return rval; return 0; } static void DNP3CleanExit(int signal, void *data) { if (dnp3_context_id != NULL) { DNP3FreeConfig(dnp3_context_id); dnp3_context_id = NULL; } if ((dnp3_mempool) && (mempool_destroy(dnp3_mempool) == 0)) { free(dnp3_mempool); dnp3_mempool = 0; } #ifdef SNORT_RELOAD ada_delete(ada); ada = NULL; #endif } static void FreeDNP3Data(void *bucket) { MemBucket *tmp_bucket = (MemBucket *)bucket; dnp3_session_data_t *session; dnp3_config_t *config = NULL; if ((tmp_bucket == NULL) || (tmp_bucket->data == NULL)) return; session = tmp_bucket->data; if (session->context_id != NULL) { config = (dnp3_config_t *)sfPolicyUserDataGet(session->context_id, session->policy_id); } if (config != NULL) { config->ref_count--; if ((config->ref_count == 0) && (session->context_id != dnp3_context_id)) { sfPolicyUserDataClear(session->context_id, session->policy_id); free(config); if (sfPolicyUserPolicyGetActive(session->context_id) == 0) { /* No more outstanding configs - free the config array */ DNP3FreeConfig(session->context_id); } } } #ifdef SNORT_RELOAD ada_appdata_freed(ada, bucket);//iff tmp_bucket/bucket is freed #endif mempool_free(dnp3_mempool, tmp_bucket); } static size_t DNP3MemInUse() { return dnp3_mempool->used_memory; } #ifdef SNORT_RELOAD static bool DNP3ReloadAdjustFunc(bool idle, tSfPolicyId raPolicyId, void *userData) { unsigned int max_sessions; unsigned maxwork = idle ? 512 : 32; if (ada_reload_adjust_func(idle, raPolicyId, userData)) { //check if mempool is being deleted or just resized if (DNP3GlobalIsEnabled(dnp3_context_id)) { #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload mempool-before: %zu %zu %zu\n", dnp3_mempool->max_memory, dnp3_mempool->used_memory, dnp3_mempool->free_memory); } #endif //if it is being resized then change the mepool size dnp3_config_t *default_config = (dnp3_config_t*)sfPolicyUserDataGetDefault(dnp3_context_id); if (default_config == NULL)//shouldn't be possible return false; max_sessions = default_config->memcap / sizeof(dnp3_session_data_t); maxwork = mempool_prune_freelist(dnp3_mempool, max_sessions * sizeof(dnp3_session_data_t), maxwork); if (maxwork) mempool_setObjectSize(dnp3_mempool, max_sessions, sizeof(dnp3_session_data_t)); #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload mempool-after: %zu %zu %zu\n", dnp3_mempool->max_memory, dnp3_mempool->used_memory, dnp3_mempool->free_memory); } #endif } else { //otherwise make sure that the mempool is empty and then delete the mempool maxwork = mempool_prune_freelist(dnp3_mempool, 0, maxwork); if (maxwork) { #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload before-destroy: %zu %zu %zu\n", dnp3_mempool->max_memory, dnp3_mempool->used_memory, dnp3_mempool->free_memory); } #endif #ifdef REG_TEST int retDestroy = #endif mempool_destroy(dnp3_mempool); dnp3_mempool = NULL; ada_delete(ada); ada = NULL; #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("DNP3-reload after-destroy: %d %p %p\n", retDestroy, dnp3_mempool, ada); } #endif } } return maxwork; } return false; } #endif snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_buffer_dump.c0000644000175000017500000000373614241075705022463 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file dnp3_buffer_dump.c ** ** @author Krishnakanth ** ** @brief This file contains structures and functions for ** DNP3 related dumping buffers. */ #include "dnp3_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_DNP3_BUFFER_DUMP] = {{"DNP3_SERVER_RESPONSE_DUMP","", 0}, {"DNP3_CLINET_REQUEST_DUMP","", 0}, {"DNP3_RESERVED_BAD_ADDR_DUMP","", 0}, {"DNP3_BAD_CRC_DUMP","", 0}}; void dumpBuffer(DNP3_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *) content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_DNP3_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getDNP3Buffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_paf.c0000644000175000017500000001415714241075712020730 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Protocol Aware Flushing (PAF) code for DNP3 preprocessor. * */ #include "spp_dnp3.h" #include "dnp3_paf.h" #include "sf_dynamic_preprocessor.h" /* Forward declarations */ static PAF_Status DNP3Paf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint64_t *flags, uint32_t *fp, uint32_t *fp_eoh); /* State-tracking structs */ typedef enum _dnp3_paf_state { DNP3_PAF_STATE__START_1 = 0, DNP3_PAF_STATE__START_2, DNP3_PAF_STATE__LENGTH, DNP3_PAF_STATE__SET_FLUSH } dnp3_paf_state_t; typedef struct _dnp3_paf_data { dnp3_paf_state_t state; uint8_t dnp3_length; uint16_t real_length; } dnp3_paf_data_t; static uint8_t dnp3_paf_id = 0; static int DNP3PafRegisterPort (struct _SnortConfig *sc, uint16_t port, tSfPolicyId policy_id) { if (!_dpd.isPafEnabled()) return 0; dnp3_paf_id = _dpd.streamAPI->register_paf_port(sc, policy_id, port, 0, DNP3Paf, true); dnp3_paf_id = _dpd.streamAPI->register_paf_port(sc, policy_id, port, 1, DNP3Paf, true); return 0; } #ifdef TARGET_BASED int DNP3AddServiceToPaf (struct _SnortConfig *sc, uint16_t service, tSfPolicyId policy_id) { if (!_dpd.isPafEnabled()) return 0; dnp3_paf_id = _dpd.streamAPI->register_paf_service(sc, policy_id, service, 0, DNP3Paf, true); dnp3_paf_id = _dpd.streamAPI->register_paf_service(sc, policy_id, service, 1, DNP3Paf, true); return 0; } #endif /* Function: DNP3Paf() Purpose: DNP3 PAF callback. Statefully inspects DNP3 traffic from the start of a session, Reads up until the length octet is found, then sets a flush point. The flushed PDU is a DNP3 Link Layer frame, the preprocessor handles reassembly of frames into Application Layer messages. Arguments: void * - stream5 session pointer void ** - DNP3 state tracking structure const uint8_t * - payload data to inspect uint32_t - length of payload data uint32_t - flags to check whether client or server uint32_t * - pointer to set flush point uint32_t * - pointer to set header flush point Returns: PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise */ static PAF_Status DNP3Paf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint64_t *flags, uint32_t *fp, uint32_t *fp_eoh) { dnp3_paf_data_t *pafdata = *(dnp3_paf_data_t **)user; uint32_t bytes_processed = 0; /* Allocate state object if it doesn't exist yet. */ if (pafdata == NULL) { pafdata = calloc(1, sizeof(dnp3_paf_data_t)); if (pafdata == NULL) return PAF_ABORT; *user = pafdata; } /* Process this packet 1 byte at a time */ while (bytes_processed < len) { uint16_t user_data = 0; uint16_t num_crcs = 0; switch (pafdata->state) { /* Check the Start bytes. If they are not \x05\x64, don't advance state. Could be out of sync, junk data between frames, mid-stream pickup, etc. */ case DNP3_PAF_STATE__START_1: if (((uint8_t) *(data + bytes_processed)) == DNP3_START_BYTE_1) pafdata->state++; else return PAF_ABORT; break; case DNP3_PAF_STATE__START_2: if (((uint8_t) *(data + bytes_processed)) == DNP3_START_BYTE_2) pafdata->state++; else return PAF_ABORT; break; /* Read the length. */ case DNP3_PAF_STATE__LENGTH: pafdata->dnp3_length = (uint8_t) *(data + bytes_processed); /* DNP3 length only counts non-CRC octets following the length field itself. Each CRC is two octets. One follows the headers, a CRC is inserted for every 16 octets of user data, plus a CRC for the last bit of user data (< 16 octets) */ if (pafdata->dnp3_length < DNP3_HEADER_REMAINDER_LEN) { /* XXX: Can we go about raising decoder alerts & dropping packets within PAF? */ return PAF_ABORT; } user_data = pafdata->dnp3_length - DNP3_HEADER_REMAINDER_LEN; num_crcs = 1 + (user_data/DNP3_CHUNK_SIZE) + (user_data % DNP3_CHUNK_SIZE? 1 : 0); pafdata->real_length = pafdata->dnp3_length + (DNP3_CRC_SIZE*num_crcs); pafdata->state++; break; /* Set the flush point. */ case DNP3_PAF_STATE__SET_FLUSH: *fp = pafdata->real_length + bytes_processed; pafdata->state = DNP3_PAF_STATE__START_1; return PAF_FLUSH; } bytes_processed++; } return PAF_SEARCH; } /* Take a DNP3 config + Snort policy, iterate through ports, register PAF callback. */ int DNP3AddPortsToPaf(struct _SnortConfig *sc, dnp3_config_t *config, tSfPolicyId policy_id) { unsigned int i; for (i = 0; i < MAX_PORTS; i++) { if (config->ports[PORT_INDEX(i)] & CONV_PORT(i)) { DNP3PafRegisterPort(sc, (uint16_t) i, policy_id); } } return DNP3_OK; } snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_buffer_dump.h0000644000175000017500000000322514241075706022462 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file dnp3_buffer_dump.h ** ** @author Krishnakanth ** ** @brief This file contains structures and functions for ** dumping buffers for DNP3 protocol */ #ifndef __DNP3_BUFFER_DUMP_H__ #define __DNP3_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { DNP3_SERVER_RESPONSE_DUMP, DNP3_CLINET_REQUEST_DUMP, DNP3_RESERVED_BAD_ADDR_DUMP, DNP3_BAD_CRC_DUMP } DNP3_BUFFER_DUMP; void dumpBuffer(DNP3_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getDNP3Buffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/dnp3/spp_dnp3.h0000644000175000017500000001256714241075721020774 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the DNP3 protocol * */ #ifndef SPP_DNP3_H #define SPP_DNP3_H #include "config.h" #include "sf_types.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" /* GIDs, SIDs, Messages */ #define GENERATOR_SPP_DNP3 145 #define DNP3_BAD_CRC 1 #define DNP3_DROPPED_FRAME 2 #define DNP3_DROPPED_SEGMENT 3 #define DNP3_REASSEMBLY_BUFFER_CLEARED 4 #define DNP3_RESERVED_ADDRESS 5 #define DNP3_RESERVED_FUNCTION 6 #define DNP3_BAD_CRC_STR "(spp_dnp3): DNP3 Link-Layer Frame contains bad CRC." #define DNP3_DROPPED_FRAME_STR "(spp_dnp3): DNP3 Link-Layer Frame was dropped." #define DNP3_DROPPED_SEGMENT_STR "(spp_dnp3): DNP3 Transport-Layer Segment was dropped during reassembly." #define DNP3_REASSEMBLY_BUFFER_CLEARED_STR "(spp_dnp3): DNP3 Reassembly Buffer was cleared without reassembling a complete message." #define DNP3_RESERVED_ADDRESS_STR "(spp_dnp3): DNP3 Link-Layer Frame uses a reserved address." #define DNP3_RESERVED_FUNCTION_STR "(spp_dnp3): DNP3 Application-Layer Fragment uses a reserved function code." #define MAX_PORTS 65536 /* Default DNP3 port */ #define DNP3_PORT 20000 /* Memcap limits. */ #define MIN_DNP3_MEMCAP 4144 #define MAX_DNP3_MEMCAP (100 * 1024 * 1024) /* Convert port value into an index for the dnp3_config->ports array */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) /* Packet directions */ #define DNP3_CLIENT 0 #define DNP3_SERVER 1 /* Session data flags */ #define DNP3_FUNC_RULE_FIRED 0x0001 #define DNP3_OBJ_RULE_FIRED 0x0002 #define DNP3_IND_RULE_FIRED 0x0004 #define DNP3_DATA_RULE_FIRED 0x0008 /* DNP3 minimum length: start (2 octets) + len (1 octet) */ #define DNP3_MIN_LEN 3 #define DNP3_LEN_OFFSET 2 /* Length of the rest of a DNP3 link-layer header: ctrl + src + dest */ #define DNP3_HEADER_REMAINDER_LEN 5 /* Reassembly data types moved here to avoid circular dependency with dnp3_sesion_data_t */ #define DNP3_BUFFER_SIZE 2048 typedef enum _dnp3_reassembly_state_t { DNP3_REASSEMBLY_STATE__IDLE = 0, DNP3_REASSEMBLY_STATE__ASSEMBLY, DNP3_REASSEMBLY_STATE__DONE } dnp3_reassembly_state_t; typedef struct _dnp3_reassembly_data_t { char buffer[DNP3_BUFFER_SIZE]; uint16_t buflen; dnp3_reassembly_state_t state; uint8_t last_seq; } dnp3_reassembly_data_t; /* DNP3 preprocessor configuration */ typedef struct _dnp3_config { uint32_t memcap; uint8_t ports[MAX_PORTS/8]; uint8_t check_crc; int disabled; int ref_count; } dnp3_config_t; /* DNP3 session data */ typedef struct _dnp3_session_data { /* Fields for rule option matching. */ uint8_t direction; uint8_t func; uint8_t obj_group; uint8_t obj_var; uint16_t indications; uint16_t flags; /* Reassembly stuff */ dnp3_reassembly_data_t client_rdata; dnp3_reassembly_data_t server_rdata; tSfPolicyId policy_id; tSfPolicyUserContextId context_id; } dnp3_session_data_t; /* DNP3 header structures */ typedef struct _dnp3_link_header_t { uint16_t start; uint8_t len; uint8_t ctrl; uint16_t dest; uint16_t src; } dnp3_link_header_t; #define DNP3_TRANSPORT_FIN(x) (x & 0x80) #define DNP3_TRANSPORT_FIR(x) (x & 0x40) #define DNP3_TRANSPORT_SEQ(x) (x & 0x3F) #define DNP3_MAX_TRANSPORT_LEN 250 typedef struct _dnp3_transport_header_t { uint8_t control; } dnp3_transport_header_t; /* Yep, the locations of FIR and FIN are switched at this layer... */ #define DNP3_APP_FIR(x) (x & 0x80) #define DNP3_APP_FIN(x) (x & 0x40) #define DNP3_APP_SEQ(x) (x & 0x0F) typedef struct _dnp3_app_request_header_t { uint8_t control; uint8_t function; } dnp3_app_request_header_t; typedef struct _dnp3_app_response_header_t { uint8_t control; uint8_t function; uint16_t indications; } dnp3_app_response_header_t; #define DNP3_CHECK_CRC_KEYWORD "check_crc" #define DNP3_PORTS_KEYWORD "ports" #define DNP3_MEMCAP_KEYWORD "memcap" #define DNP3_DISABLED_KEYWORD "disabled" #define DNP3_OK 1 #define DNP3_FAIL (-1) #ifdef WORDS_BIGENDIAN #define DNP3_MIN_RESERVED_ADDR 0xF0FF #define DNP3_MAX_RESERVED_ADDR 0xFBFF #define DNP3_START_BYTES 0x0564 #else #define DNP3_MIN_RESERVED_ADDR 0xFFF0 #define DNP3_MAX_RESERVED_ADDR 0xFFFB #define DNP3_START_BYTES 0x6405 #endif #define DNP3_START_BYTE_1 0x05 #define DNP3_START_BYTE_2 0x64 #define DNP3_CHUNK_SIZE 16 #define DNP3_CRC_SIZE 2 #endif /* SPP_DNP3_H */ snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_roptions.c0000644000175000017500000004301114241075716022032 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Rule options for the DNP3 preprocessor * */ #include #include "sf_types.h" #include "sf_snort_plugin_api.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "mempool.h" #include "spp_dnp3.h" #include "dnp3_map.h" #include "dnp3_roptions.h" /* Object decoding constants */ #define DNP3_OBJ_HDR_MIN_LEN 3 /* group, var, qualifier */ #define DNP3_OBJ_QUAL_PREFIX(x) ((x & 0x70) >> 4) #define DNP3_OBJ_QUAL_RANGE(x) (x & 0x0F) /* Object header prefix codes */ #define DNP3_PREFIX_NO_PREFIX 0x00 #define DNP3_PREFIX_1OCT_INDEX 0x01 #define DNP3_PREFIX_2OCT_INDEX 0x02 #define DNP3_PREFIX_4OCT_INDEX 0x03 #define DNP3_PREFIX_1OCT_SIZE 0x04 #define DNP3_PREFIX_2OCT_SIZE 0x05 #define DNP3_PREFIX_4OCT_SIZE 0x06 #define DNP3_PREFIX_RESERVED 0x07 /* Object header range specifiers -- 0x0A & 0x0C-0x0F are reserved */ #define DNP3_RANGE_1OCT_INDICES 0x00 #define DNP3_RANGE_2OCT_INDICES 0x01 #define DNP3_RANGE_4OCT_INDICES 0x02 #define DNP3_RANGE_1OCT_ADDRESSES 0x03 #define DNP3_RANGE_2OCT_ADDRESSES 0x04 #define DNP3_RANGE_4OCT_ADDRESSES 0x05 #define DNP3_RANGE_NO_RANGE 0x06 #define DNP3_RANGE_1OCT_COUNT 0x07 #define DNP3_RANGE_2OCT_COUNT 0x08 #define DNP3_RANGE_4OCT_COUNT 0x09 #define DNP3_RANGE_VARIABLE 0x0B typedef enum _dnp3_option_type_t { DNP3_FUNC = 0, DNP3_OBJ, DNP3_IND, DNP3_DATA } dnp3_option_type_t; typedef struct _dnp3_option_data_t { dnp3_option_type_t type; uint16_t arg; } dnp3_option_data_t; /* Parsing functions */ int DNP3FuncInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *endptr; dnp3_option_data_t *dnp3_data; long func_code; if (name == NULL || data == NULL) return 0; if (params == NULL) { DynamicPreprocessorFatalMessage("%s(%d): dnp3_func requires a " "number beween 0 and 255, or a valid function name.\n", *_dpd.config_file, *_dpd.config_line); } if (strcmp(name, DNP3_FUNC_NAME) != 0) return 0; dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t)); if (dnp3_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "dnp3_func data structure.\n", __FILE__, __LINE__); } /* Parsing time */ if (isdigit(params[0])) { /* Function code given as integer */ func_code = _dpd.SnortStrtol(params, &endptr, 10); if ((func_code > 255) || (func_code < 0) || (*endptr != '\0')) { DynamicPreprocessorFatalMessage("%s(%d): dnp3_func requires a " "number beween 0 and 255, or a valid function name.\n", *_dpd.config_file, *_dpd.config_line); } } else { func_code = DNP3FuncStrToCode(params); if (func_code == -1) { DynamicPreprocessorFatalMessage("%s(%d): dnp3_func requires a " "number beween 0 and 255, or a valid function name.\n", *_dpd.config_file, *_dpd.config_line); } } dnp3_data->type = DNP3_FUNC; dnp3_data->arg = (uint16_t) func_code; *data = (void *)dnp3_data; return 1; } NORETURN static inline void DNP3ObjError(void) { DynamicPreprocessorFatalMessage("%s(%d) dnp3_obj requires two arguments," "where each argument is a number between 0 and 255.\n", *_dpd.config_file, *_dpd.config_line); } int DNP3ObjInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *endptr, *token, *saveptr; dnp3_option_data_t *dnp3_data; unsigned int obj_group, obj_var; if (name == NULL || data == NULL) return 0; if (strcmp(name, DNP3_OBJ_NAME) != 0) return 0; if (params == NULL) { DynamicPreprocessorFatalMessage("%s(%d): No argument given for dnp3_obj. " "dnp3_obj requires two arguments, where each argument is a number " "between 0 and 255.\n", *_dpd.config_file, *_dpd.config_line); } dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t)); if (dnp3_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "dnp3_func data structure.\n", __FILE__, __LINE__); } token = strtok_r(params, ",", &saveptr); if (token == NULL) DNP3ObjError(); /* First token: object group */ obj_group = _dpd.SnortStrtoul(token, &endptr, 10); if ((obj_group > 255) || (*endptr != '\0')) DNP3ObjError(); token = strtok_r(NULL, ",", &saveptr); if (token == NULL) DNP3ObjError(); /* Second token: object var */ obj_var = _dpd.SnortStrtoul(token, &endptr, 10); if ((obj_var > 255) || (*endptr != '\0')) DNP3ObjError(); /* pack the two arguments into one uint16_t */ dnp3_data->type = DNP3_OBJ; dnp3_data->arg = ((obj_group << 8) | (obj_var)); *data = dnp3_data; return 1; } int DNP3IndInit(struct _SnortConfig *sc, char *name, char *params, void **data) { dnp3_option_data_t *dnp3_data; char *token, *saveptr; uint16_t flags = 0; if (name == NULL || data == NULL) return 0; if (params == NULL) { DynamicPreprocessorFatalMessage("%s(%d): dnp3_ind requires a " "number beween 0 and 255, or a valid function name.\n", *_dpd.config_file, *_dpd.config_line); } dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t)); if (dnp3_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "dnp3_func data structure.\n", __FILE__, __LINE__); } token = strtok_r(params, ",", &saveptr); while (token != NULL) { int flag = DNP3IndStrToCode(token); if (flag == -1) { DynamicPreprocessorFatalMessage("%s(%d): dnp3_ind requires a " "valid indication flag name. '%s' is invalid.\n", *_dpd.config_file, *_dpd.config_line, token); } flags |= (uint16_t) flag; token = strtok_r(NULL, ",", &saveptr); } if (flags == 0) { DynamicPreprocessorFatalMessage("%s(%d): dnp3_ind requires a " "valid indication flag name. No flags were given.\n", *_dpd.config_file, *_dpd.config_line); } dnp3_data->type = DNP3_IND; dnp3_data->arg = flags; *data = (void *)dnp3_data; return 1; } int DNP3DataInit(struct _SnortConfig *sc, char *name, char *params, void **data) { dnp3_option_data_t *dnp3_data; if (name == NULL || data == NULL) return 0; /* nothing to parse. */ if (params) { DynamicPreprocessorFatalMessage("%s(%d): dnp3_data does not take " "any arguments.\n", *_dpd.config_file, *_dpd.config_line); } dnp3_data = (dnp3_option_data_t *)calloc(1, sizeof(dnp3_option_data_t)); if (dnp3_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "dnp3_data data structure.\n", __FILE__, __LINE__); } dnp3_data->type = DNP3_DATA; dnp3_data->arg = 0; *data = (void *)dnp3_data; return 1; } /* Evaluation functions */ int DNP3FuncEval(void *raw_packet, const uint8_t **cursor, void *data) { SFSnortPacket *packet = (SFSnortPacket *)raw_packet; MemBucket *tmp_bucket; dnp3_option_data_t *rule_data = (dnp3_option_data_t *)data; dnp3_session_data_t *session_data; dnp3_reassembly_data_t *rdata; /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options don't check for this, they'll fire on stale session data when the original packet goes through before flushing. */ if (packet->tcp_header && !PacketHasFullPDU(packet)) return RULE_NOMATCH; /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */ if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE)) return RULE_NOMATCH; tmp_bucket = (MemBucket *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3); if ((packet->payload_size == 0) || (tmp_bucket == NULL)) { return RULE_NOMATCH; } session_data = (dnp3_session_data_t *)tmp_bucket->data; if (session_data->direction == DNP3_CLIENT) rdata = &(session_data->client_rdata); else rdata = &(session_data->server_rdata); /* Only evaluate rules against complete Application-layer fragments */ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE) return RULE_NOMATCH; if (session_data->func == rule_data->arg) return RULE_MATCH; return RULE_NOMATCH; } static int DNP3DecodeObject(uint8_t *buf, uint16_t buflen, uint8_t rule_group, uint8_t rule_var) { uint8_t group, var; /* XXX: uncomment these when fixing the below TODO regarding multiple objects uint8_t qualifier, prefix_size, prefix_code, range_specifier; uint32_t begin, end, num_objects; */ if (buf == NULL || buflen < DNP3_OBJ_HDR_MIN_LEN) return RULE_NOMATCH; /* Decode group */ group = *buf; buf++; buflen--; /* Decode var */ var = *buf; buf++; buflen--; /* Match the rule option here, quit decoding if we found the right header. */ if ((group == rule_group) && (var == rule_var)) return RULE_MATCH; /* TODO: Implement matching with multiple objects in a Request/Response. */ #if 0 /* Decode qualifier */ qualifier = *buf; prefix_code = DNP3_OBJ_QUAL_PREFIX(qualifier); range_specifier = DNP3_OBJ_QUAL_RANGE(qualifier); buf++; buflen--; /* The size of object prefixes depends on the prefix code */ switch (prefix_code) { case DNP3_PREFIX_NO_PREFIX: prefix_size = 0; break; case DNP3_PREFIX_1OCT_INDEX: case DNP3_PREFIX_1OCT_SIZE: prefix_size = 1; break; case DNP3_PREFIX_2OCT_INDEX: case DNP3_PREFIX_2OCT_SIZE: prefix_size = 2; break; case DNP3_PREFIX_4OCT_INDEX: case DNP3_PREFIX_4OCT_SIZE: prefix_size = 4; break; default: /* TODO: Preprocessor alert on reserved value */ return DNP3_FAIL; } /* Decoding of the range field depends on the Range Specifier */ switch (range_specifier) { case DNP3_RANGE_1OCT_INDICES: if (buflen < 2) return DNP3_FAIL; /* Decode 8-bit indices for object prefixes */ begin = *(uint8_t *)buf++; end = *(uint8_t *)buf++; buflen -= 2; /* Check that indices make sense */ if (begin > end) return DNP3_FAIL; /* TODO: Preprocessor alert */ num_objects = end - begin + 1; break; case DNP3_RANGE_2OCT_INDICES: if (buflen < 2) return DNP3_FAIL; /* Decode 8-bit indices for object prefixes */ begin = *(uint16_t *)buf++; end = *(uint16_t *)buf++; buflen -= 2; /* Check that indices make sense */ if (begin > end) return DNP3_FAIL; /* TODO: Preprocessor alert */ num_objects = end - begin + 1; break; case DNP3_RANGE_4OCT_INDICES: case DNP3_RANGE_1OCT_ADDRESSES: case DNP3_RANGE_2OCT_ADDRESSES: case DNP3_RANGE_4OCT_ADDRESSES: case DNP3_RANGE_NO_RANGE: case DNP3_RANGE_1OCT_COUNT: case DNP3_RANGE_2OCT_COUNT: case DNP3_RANGE_4OCT_COUNT: case DNP3_RANGE_VARIABLE: default: } #endif /* 0 */ return RULE_NOMATCH; } int DNP3ObjEval(void *raw_packet, const uint8_t **cursor, void *data) { SFSnortPacket *packet = (SFSnortPacket *)raw_packet; MemBucket *tmp_bucket; dnp3_option_data_t *rule_data = (dnp3_option_data_t *)data; dnp3_session_data_t *session_data; dnp3_reassembly_data_t *rdata; uint8_t group, var; uint8_t *obj_buffer; uint16_t obj_buflen; size_t header_size; int rval = RULE_NOMATCH; /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options don't check for this, they'll fire on stale session data when the original packet goes through before flushing. */ if (packet->tcp_header && !PacketHasFullPDU(packet)) return RULE_NOMATCH; /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */ if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE)) return RULE_NOMATCH; tmp_bucket = (MemBucket *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3); if ((packet->payload_size == 0) || (tmp_bucket == NULL)) { return RULE_NOMATCH; } session_data = (dnp3_session_data_t *)tmp_bucket->data; if (session_data->direction == DNP3_CLIENT) { rdata = &(session_data->client_rdata); header_size = sizeof(dnp3_app_request_header_t); } else { rdata = &(session_data->server_rdata); header_size = sizeof(dnp3_app_response_header_t); } /* Only evaluate rules against complete Application-layer fragments */ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE) return RULE_NOMATCH; /* Skip over the App request/response header. They are different sizes, depending on whether it is a request or response! */ if (rdata->buflen < header_size) return RULE_NOMATCH; obj_buffer = (uint8_t *)rdata->buffer + header_size; obj_buflen = rdata->buflen - header_size; /* Rule parsing code combined our two arguments into a single uint16_t */ group = (rule_data->arg >> 8); var = (rule_data->arg & 0x00FF); rval = DNP3DecodeObject(obj_buffer, obj_buflen, group, var); return rval; } int DNP3IndEval(void *raw_packet, const uint8_t **cursor, void *data) { SFSnortPacket *packet = (SFSnortPacket *)raw_packet; MemBucket *tmp_bucket; dnp3_option_data_t *rule_data = (dnp3_option_data_t *)data; dnp3_session_data_t *session_data; dnp3_reassembly_data_t *rdata; /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options don't check for this, they'll fire on stale session data when the original packet goes through before flushing. */ if (packet->tcp_header && !PacketHasFullPDU(packet)) return RULE_NOMATCH; /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */ if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE)) return RULE_NOMATCH; tmp_bucket = (MemBucket *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3); if ((packet->payload_size == 0) || (tmp_bucket == NULL)) { return RULE_NOMATCH; } session_data = (dnp3_session_data_t *)tmp_bucket->data; /* Internal Indications only apply to DNP3 responses, not requests. */ if (session_data->direction == DNP3_CLIENT) return RULE_NOMATCH; rdata = &(session_data->server_rdata); /* Only evaluate rules against complete Application-layer fragments */ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE) return RULE_NOMATCH; if (session_data->indications & rule_data->arg) return RULE_MATCH; return RULE_NOMATCH; } int DNP3DataEval(void *raw_packet, const uint8_t **cursor, void *data) { SFSnortPacket *packet = (SFSnortPacket *)raw_packet; MemBucket *tmp_bucket; dnp3_session_data_t *session_data; dnp3_reassembly_data_t *rdata; /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options don't check for this, they'll fire on stale session data when the original packet goes through before flushing. */ if (packet->tcp_header && !PacketHasFullPDU(packet)) return RULE_NOMATCH; /* For UDP packets, there is no PAF so we use the Alt Decode buffer. */ if (packet->udp_header && !_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE)) return RULE_NOMATCH; tmp_bucket = (MemBucket *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_DNP3); if ((packet->payload_size == 0) || (tmp_bucket == NULL)) { return RULE_NOMATCH; } session_data = (dnp3_session_data_t *)tmp_bucket->data; if (session_data->direction == DNP3_CLIENT) rdata = &(session_data->client_rdata); else rdata = &(session_data->server_rdata); /* Only evaluate rules against complete Application-layer fragments */ if (rdata->state != DNP3_REASSEMBLY_STATE__DONE) return RULE_NOMATCH; /* Set the cursor to the reassembled Application-layer buffer */ *cursor = (uint8_t *)rdata->buffer; _dpd.SetAltDetect((uint8_t *)rdata->buffer, rdata->buflen); return RULE_MATCH; } snort-2.9.20/src/dynamic-preprocessors/dnp3/Makefile.in0000644000175000017500000006246214242725546021151 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@am__append_1 = ../libsf_dynamic_utils.la @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@am__append_2 = ../include/appdata_adjuster.c ../include/sfxhash.c ../include/sfhashfcn.c ../include/sfmemcap.c ../include/sfprimetable.c ../include/reg_test.h ../include/reg_test.c @BUILD_BUFFER_DUMP_TRUE@am__append_3 = \ @BUILD_BUFFER_DUMP_TRUE@dnp3_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@dnp3_buffer_dump.h subdir = src/dynamic-preprocessors/dnp3 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_dnp3_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ $(am__append_1) am__libsf_dnp3_preproc_la_SOURCES_DIST = spp_dnp3.c spp_dnp3.h \ dnp3_paf.c dnp3_paf.h dnp3_reassembly.c dnp3_reassembly.h \ dnp3_roptions.c dnp3_roptions.h dnp3_map.c dnp3_map.h \ dnp3_buffer_dump.c dnp3_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = dnp3_buffer_dump.lo am_libsf_dnp3_preproc_la_OBJECTS = spp_dnp3.lo dnp3_paf.lo \ dnp3_reassembly.lo dnp3_roptions.lo dnp3_map.lo \ $(am__objects_1) @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@am__objects_2 = appdata_adjuster.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfxhash.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfhashfcn.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfmemcap.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfprimetable.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ reg_test.lo @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_dnp3_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo mempool.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_sdlist.lo $(am__objects_2) libsf_dnp3_preproc_la_OBJECTS = $(am_libsf_dnp3_preproc_la_OBJECTS) \ $(nodist_libsf_dnp3_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_dnp3_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_dnp3_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_dnp3_preproc_la_SOURCES) \ $(nodist_libsf_dnp3_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_dnp3_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ # $Id AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_dnp3_preproc.la libsf_dnp3_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_dnp3_preproc_la_LIBADD = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ $(am__append_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_dnp3_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/mempool.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sf_sdlist.c \ @SO_WITH_STATIC_LIB_FALSE@ $(am__append_2) libsf_dnp3_preproc_la_SOURCES = spp_dnp3.c spp_dnp3.h dnp3_paf.c \ dnp3_paf.h dnp3_reassembly.c dnp3_reassembly.h dnp3_roptions.c \ dnp3_roptions.h dnp3_map.c dnp3_map.h $(am__append_3) EXTRA_DIST = \ sf_dnp3.vcxproj \ sf_dnp3.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/dnp3/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/dnp3/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_dnp3_preproc.la: $(libsf_dnp3_preproc_la_OBJECTS) $(libsf_dnp3_preproc_la_DEPENDENCIES) $(EXTRA_libsf_dnp3_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_dnp3_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_dnp3_preproc_la_OBJECTS) $(libsf_dnp3_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mempool.lo: ../include/mempool.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mempool.lo `test -f '../include/mempool.c' || echo '$(srcdir)/'`../include/mempool.c sf_sdlist.lo: ../include/sf_sdlist.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_sdlist.lo `test -f '../include/sf_sdlist.c' || echo '$(srcdir)/'`../include/sf_sdlist.c appdata_adjuster.lo: ../include/appdata_adjuster.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o appdata_adjuster.lo `test -f '../include/appdata_adjuster.c' || echo '$(srcdir)/'`../include/appdata_adjuster.c sfxhash.lo: ../include/sfxhash.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfxhash.lo `test -f '../include/sfxhash.c' || echo '$(srcdir)/'`../include/sfxhash.c sfhashfcn.lo: ../include/sfhashfcn.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfhashfcn.lo `test -f '../include/sfhashfcn.c' || echo '$(srcdir)/'`../include/sfhashfcn.c sfmemcap.lo: ../include/sfmemcap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfmemcap.lo `test -f '../include/sfmemcap.c' || echo '$(srcdir)/'`../include/sfmemcap.c sfprimetable.lo: ../include/sfprimetable.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfprimetable.lo `test -f '../include/sfprimetable.c' || echo '$(srcdir)/'`../include/sfprimetable.c reg_test.lo: ../include/reg_test.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o reg_test.lo `test -f '../include/reg_test.c' || echo '$(srcdir)/'`../include/reg_test.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_map.h0000644000175000017500000000271014241075711020733 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Tables for DNP3 function & indicator definitions * */ #ifndef DNP3_MAP__H #define DNP3_MAP__H #include /* Check if "code" is in the function map. * * Returns: 1 on success, 0 on failure. */ int DNP3FuncIsDefined(uint16_t code); /* Return the DNP3 function code corresponding to "name". * * Returns: integer * -1 on failure */ int DNP3FuncStrToCode(char *name); /* Return the DNP3 indication code corresponding to "name". * * Returns: integer * -1 on failure */ int DNP3IndStrToCode(char *name); #endif snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_reassembly.c0000644000175000017500000004045014241075714022325 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the DNP3 protocol * */ #include #include #include "spp_dnp3.h" #include "sf_types.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_packet.h" #include "snort_bounds.h" #include "dnp3_map.h" #include "dnp3_reassembly.h" #include "dnp3_roptions.h" #ifdef DUMP_BUFFER #include "dnp3_buffer_dump.h" #endif /* Minimum length of DNP3 "len" field in order to get a transport header. */ #define DNP3_MIN_TRANSPORT_LEN 6 #define DNP3_TPDU_MAX 250 #define DNP3_LPDU_MAX 292 /* CRC look-up table, for computeCRC() below */ static uint16_t crcLookUpTable[256] = { 0x0000, 0x365E, 0x6CBC, 0x5AE2, 0xD978, 0xEF26, 0xB5C4, 0x839A, 0xFF89, 0xC9D7, 0x9335, 0xA56B, 0x26F1, 0x10AF, 0x4A4D, 0x7C13, 0xB26B, 0x8435, 0xDED7, 0xE889, 0x6B13, 0x5D4D, 0x07AF, 0x31F1, 0x4DE2, 0x7BBC, 0x215E, 0x1700, 0x949A, 0xA2C4, 0xF826, 0xCE78, 0x29AF, 0x1FF1, 0x4513, 0x734D, 0xF0D7, 0xC689, 0x9C6B, 0xAA35, 0xD626, 0xE078, 0xBA9A, 0x8CC4, 0x0F5E, 0x3900, 0x63E2, 0x55BC, 0x9BC4, 0xAD9A, 0xF778, 0xC126, 0x42BC, 0x74E2, 0x2E00, 0x185E, 0x644D, 0x5213, 0x08F1, 0x3EAF, 0xBD35, 0x8B6B, 0xD189, 0xE7D7, 0x535E, 0x6500, 0x3FE2, 0x09BC, 0x8A26, 0xBC78, 0xE69A, 0xD0C4, 0xACD7, 0x9A89, 0xC06B, 0xF635, 0x75AF, 0x43F1, 0x1913, 0x2F4D, 0xE135, 0xD76B, 0x8D89, 0xBBD7, 0x384D, 0x0E13, 0x54F1, 0x62AF, 0x1EBC, 0x28E2, 0x7200, 0x445E, 0xC7C4, 0xF19A, 0xAB78, 0x9D26, 0x7AF1, 0x4CAF, 0x164D, 0x2013, 0xA389, 0x95D7, 0xCF35, 0xF96B, 0x8578, 0xB326, 0xE9C4, 0xDF9A, 0x5C00, 0x6A5E, 0x30BC, 0x06E2, 0xC89A, 0xFEC4, 0xA426, 0x9278, 0x11E2, 0x27BC, 0x7D5E, 0x4B00, 0x3713, 0x014D, 0x5BAF, 0x6DF1, 0xEE6B, 0xD835, 0x82D7, 0xB489, 0xA6BC, 0x90E2, 0xCA00, 0xFC5E, 0x7FC4, 0x499A, 0x1378, 0x2526, 0x5935, 0x6F6B, 0x3589, 0x03D7, 0x804D, 0xB613, 0xECF1, 0xDAAF, 0x14D7, 0x2289, 0x786B, 0x4E35, 0xCDAF, 0xFBF1, 0xA113, 0x974D, 0xEB5E, 0xDD00, 0x87E2, 0xB1BC, 0x3226, 0x0478, 0x5E9A, 0x68C4, 0x8F13, 0xB94D, 0xE3AF, 0xD5F1, 0x566B, 0x6035, 0x3AD7, 0x0C89, 0x709A, 0x46C4, 0x1C26, 0x2A78, 0xA9E2, 0x9FBC, 0xC55E, 0xF300, 0x3D78, 0x0B26, 0x51C4, 0x679A, 0xE400, 0xD25E, 0x88BC, 0xBEE2, 0xC2F1, 0xF4AF, 0xAE4D, 0x9813, 0x1B89, 0x2DD7, 0x7735, 0x416B, 0xF5E2, 0xC3BC, 0x995E, 0xAF00, 0x2C9A, 0x1AC4, 0x4026, 0x7678, 0x0A6B, 0x3C35, 0x66D7, 0x5089, 0xD313, 0xE54D, 0xBFAF, 0x89F1, 0x4789, 0x71D7, 0x2B35, 0x1D6B, 0x9EF1, 0xA8AF, 0xF24D, 0xC413, 0xB800, 0x8E5E, 0xD4BC, 0xE2E2, 0x6178, 0x5726, 0x0DC4, 0x3B9A, 0xDC4D, 0xEA13, 0xB0F1, 0x86AF, 0x0535, 0x336B, 0x6989, 0x5FD7, 0x23C4, 0x159A, 0x4F78, 0x7926, 0xFABC, 0xCCE2, 0x9600, 0xA05E, 0x6E26, 0x5878, 0x029A, 0x34C4, 0xB75E, 0x8100, 0xDBE2, 0xEDBC, 0x91AF, 0xA7F1, 0xFD13, 0xCB4D, 0x48D7, 0x7E89, 0x246B, 0x1235 }; /* Append a DNP3 Transport segment to the reassembly buffer. Returns: DNP3_OK: Segment queued successfully. DNP3_FAIL: Data copy failed. Segment did not fit in reassembly buffer. */ static int DNP3QueueSegment(dnp3_reassembly_data_t *rdata, char *buf, uint16_t buflen) { if (rdata == NULL || buf == NULL) return DNP3_FAIL; /* At first I was afraid, but we checked for DNP3_MAX_TRANSPORT_LEN earlier. */ if (buflen + rdata->buflen > DNP3_BUFFER_SIZE) return DNP3_FAIL; memcpy((rdata->buffer + rdata->buflen), buf, (size_t) buflen); rdata->buflen += buflen; return DNP3_OK; } /* Reset a DNP3 reassembly buffer */ static void DNP3ReassemblyReset(dnp3_reassembly_data_t *rdata) { rdata->buflen = 0; rdata->state = DNP3_REASSEMBLY_STATE__IDLE; rdata->last_seq = 0; } /* DNP3 Transport-Layer reassembly state machine. Arguments: rdata: DNP3 reassembly state object. buf: DNP3 Transport Layer segment buflen: Length of Transport Layer segment. Returns: DNP3_FAIL: Segment was discarded. DNP3_OK: Segment was queued. */ static int DNP3ReassembleTransport(dnp3_reassembly_data_t *rdata, char *buf, uint16_t buflen) { dnp3_transport_header_t *trans_header; if (rdata == NULL || buf == NULL || buflen < sizeof(dnp3_transport_header_t) || (buflen > DNP3_MAX_TRANSPORT_LEN)) { return DNP3_FAIL; } /* Take the first byte as a transport header, cut it off of the buffer. */ trans_header = (dnp3_transport_header_t *)buf; buf += sizeof(dnp3_transport_header_t); buflen -= sizeof(dnp3_transport_header_t); /* If the previously-existing state was DONE, we need to reset it back to IDLE. */ if (rdata->state == DNP3_REASSEMBLY_STATE__DONE) DNP3ReassemblyReset(rdata); switch (rdata->state) { case DNP3_REASSEMBLY_STATE__IDLE: /* Discard any non-first segment. */ if ( DNP3_TRANSPORT_FIR(trans_header->control) == 0 ) return DNP3_FAIL; /* Reset the buffer & queue the first segment */ DNP3ReassemblyReset(rdata); DNP3QueueSegment(rdata, buf, buflen); rdata->last_seq = DNP3_TRANSPORT_SEQ(trans_header->control); if ( DNP3_TRANSPORT_FIN(trans_header->control) ) rdata->state = DNP3_REASSEMBLY_STATE__DONE; else rdata->state = DNP3_REASSEMBLY_STATE__ASSEMBLY; break; case DNP3_REASSEMBLY_STATE__ASSEMBLY: /* Reset if the FIR flag is set. */ if ( DNP3_TRANSPORT_FIR(trans_header->control) ) { DNP3ReassemblyReset(rdata); DNP3QueueSegment(rdata, buf, buflen); rdata->last_seq = DNP3_TRANSPORT_SEQ(trans_header->control); if (DNP3_TRANSPORT_FIN(trans_header->control)) rdata->state = DNP3_REASSEMBLY_STATE__DONE; /* Raise an alert so it's clear the buffer was reset. Could signify device trouble. */ _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_REASSEMBLY_BUFFER_CLEARED, 1, 0, 3, DNP3_REASSEMBLY_BUFFER_CLEARED_STR, 0); } else { /* Same seq but FIN is set. Discard segment, BUT finish reassembly. */ if ((DNP3_TRANSPORT_SEQ(trans_header->control) == rdata->last_seq) && (DNP3_TRANSPORT_FIN(trans_header->control))) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_DROPPED_SEGMENT, 1, 0, 3, DNP3_DROPPED_SEGMENT_STR, 0); rdata->state = DNP3_REASSEMBLY_STATE__DONE; return DNP3_FAIL; } /* Discard any other segments without the correct sequence. */ if (DNP3_TRANSPORT_SEQ(trans_header->control) != ((rdata->last_seq + 1) % 0x40 )) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_DROPPED_SEGMENT, 1, 0, 3, DNP3_DROPPED_SEGMENT_STR, 0); return DNP3_FAIL; } /* Otherwise, queue it up! */ DNP3QueueSegment(rdata, buf, buflen); rdata->last_seq = DNP3_TRANSPORT_SEQ(trans_header->control); if (DNP3_TRANSPORT_FIN(trans_header->control)) rdata->state = DNP3_REASSEMBLY_STATE__DONE; else rdata->state = DNP3_REASSEMBLY_STATE__ASSEMBLY; } break; case DNP3_REASSEMBLY_STATE__DONE: break; } /* Set the Alt Decode buffer. This must be done during preprocessing in order to stop the Fast Pattern matcher from using raw packet data to evaluate the longest content in a rule. */ if (rdata->state == DNP3_REASSEMBLY_STATE__DONE) { uint8_t *alt_buf = _dpd.altBuffer->data; uint16_t alt_len = sizeof(_dpd.altBuffer->data); int ret; ret = SafeMemcpy((void *)alt_buf, (const void *)rdata->buffer, (size_t)rdata->buflen, (const void *)alt_buf, (const void *)(alt_buf + alt_len)); if (ret == SAFEMEM_SUCCESS) _dpd.SetAltDecode(alt_len); } return DNP3_OK; } /* Check for reserved application-level function codes. */ static void DNP3CheckReservedFunction(dnp3_session_data_t *session) { if ( !(DNP3FuncIsDefined( (uint16_t)session->func)) ) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_RESERVED_FUNCTION, 1, 0, 3, DNP3_RESERVED_FUNCTION_STR, 0); } } /* Decode a DNP3 Application-layer Fragment, fill out the relevant session data for rule option evaluation. */ static int DNP3ProcessApplication(dnp3_session_data_t *session) { dnp3_reassembly_data_t *rdata = NULL; if (session == NULL) return DNP3_FAIL; /* Master and Outstation use slightly different Application-layer headers. Only the outstation sends Internal Indications. */ if (session->direction == DNP3_CLIENT) { dnp3_app_request_header_t *request = NULL; rdata = &(session->client_rdata); if (rdata->buflen < sizeof(dnp3_app_request_header_t)) return DNP3_FAIL; /* TODO: Preprocessor Alert */ request = (dnp3_app_request_header_t *)(rdata->buffer); #ifdef DUMP_BUFFER dumpBuffer(DNP3_CLINET_REQUEST_DUMP, (const uint8_t *) rdata->buffer,rdata->buflen); #endif session->func = request->function; } else if (session->direction == DNP3_SERVER) { dnp3_app_response_header_t *response = NULL; rdata = &(session->server_rdata); if (rdata->buflen < sizeof(dnp3_app_response_header_t)) return DNP3_FAIL; /* TODO: Preprocessor Alert */ response = (dnp3_app_response_header_t *)(rdata->buffer); #ifdef DUMP_BUFFER dumpBuffer(DNP3_SERVER_RESPONSE_DUMP, (const uint8_t *) rdata->buffer,rdata->buflen); #endif session->func = response->function; session->indications = ntohs(response->indications); } DNP3CheckReservedFunction(session); return DNP3_OK; } /* Check a CRC in a single block. */ /* This code is mostly lifted from the example in the DNP3 spec. */ static inline void computeCRC(unsigned char data, uint16_t *crcAccum) { *crcAccum = (*crcAccum >> 8) ^ crcLookUpTable[(*crcAccum ^ data) & 0xFF]; } static int DNP3CheckCRC(unsigned char *buf, uint16_t buflen) { uint16_t idx; uint16_t crc = 0; /* Compute check code for data in received block */ for (idx = 0; idx < buflen-2; idx++) computeCRC(buf[idx], &crc); crc = ~crc; /* Invert */ /* Check CRC at end of block */ if (buf[idx++] == (unsigned char)crc && buf[idx] == (unsigned char)(crc >> 8)) return DNP3_OK; else return DNP3_FAIL; } /* Check CRCs in a Link-Layer Frame, then fill a buffer containing just the user data */ static int DNP3CheckRemoveCRC(dnp3_config_t *config, uint8_t *pdu_start, uint16_t pdu_length, char *buf, uint16_t *buflen) { char *cursor; uint16_t bytes_left; uint16_t curlen = 0; /* Check Header CRC */ if ((config->check_crc) && (DNP3CheckCRC((unsigned char*)pdu_start, sizeof(dnp3_link_header_t)+2) == DNP3_FAIL)) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_BAD_CRC, 1, 0, 3, DNP3_BAD_CRC_STR, 0); return DNP3_FAIL; } cursor = (char *)pdu_start + sizeof(dnp3_link_header_t) + 2; bytes_left = pdu_length - sizeof(dnp3_link_header_t) - 2; /* Process whole 16-byte chunks (plus 2-byte CRC) */ while ( (bytes_left > (DNP3_CHUNK_SIZE + DNP3_CRC_SIZE)) && (curlen + DNP3_CHUNK_SIZE < *buflen) ) { if ((config->check_crc) && (DNP3CheckCRC((unsigned char*)cursor, (DNP3_CHUNK_SIZE+DNP3_CRC_SIZE)) == DNP3_FAIL)) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_BAD_CRC, 1, 0, 3, DNP3_BAD_CRC_STR, 0); return DNP3_FAIL; } memcpy((buf + curlen), cursor, DNP3_CHUNK_SIZE); curlen += DNP3_CHUNK_SIZE; cursor += (DNP3_CHUNK_SIZE+DNP3_CRC_SIZE); bytes_left -= (DNP3_CHUNK_SIZE+DNP3_CRC_SIZE); } /* Process leftover chunk, under 16 bytes */ if ( (bytes_left > DNP3_CRC_SIZE) && (curlen + bytes_left < *buflen) ) { if ((config->check_crc) && (DNP3CheckCRC((unsigned char*)cursor, bytes_left) == DNP3_FAIL)) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_BAD_CRC, 1, 0, 3, DNP3_BAD_CRC_STR, 0); return DNP3_FAIL; } memcpy((buf + curlen), cursor, (bytes_left - DNP3_CRC_SIZE)); curlen += (bytes_left - DNP3_CRC_SIZE); cursor += bytes_left; bytes_left = 0; } *buflen = curlen; return DNP3_OK; } static int DNP3CheckReservedAddrs(dnp3_link_header_t *link) { int bad_addr = 0; if ((link->src >= DNP3_MIN_RESERVED_ADDR) && (link->src <= DNP3_MAX_RESERVED_ADDR)) bad_addr = 1; else if ((link->dest >= DNP3_MIN_RESERVED_ADDR) && (link->dest <= DNP3_MAX_RESERVED_ADDR)) bad_addr = 1; if (bad_addr) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_RESERVED_ADDRESS, 1, 0, 3, DNP3_RESERVED_ADDRESS_STR, 0); return DNP3_FAIL; } return DNP3_OK; } /* Main DNP3 Reassembly function. Moved here to avoid circular dependency between spp_dnp3 and dnp3_reassembly. */ int DNP3FullReassembly(dnp3_config_t *config, dnp3_session_data_t *session, SFSnortPacket *packet, uint8_t *pdu_start, uint16_t pdu_length) { char buf[DNP3_TPDU_MAX]; uint16_t buflen = sizeof(buf); dnp3_link_header_t *link; dnp3_reassembly_data_t *rdata; if (pdu_length < (sizeof(dnp3_link_header_t) + sizeof(dnp3_transport_header_t) + 2)) return DNP3_FAIL; if ( pdu_length > DNP3_LPDU_MAX ) // this means PAF aborted - not DNP3 return DNP3_FAIL; /* Step 1: Decode header and skip to data */ link = (dnp3_link_header_t *) pdu_start; if (link->len < DNP3_MIN_TRANSPORT_LEN) { _dpd.alertAdd(GENERATOR_SPP_DNP3, DNP3_DROPPED_FRAME, 1, 0, 3, DNP3_DROPPED_FRAME_STR, 0); return DNP3_FAIL; } /* Check reserved addresses */ if ( DNP3CheckReservedAddrs(link) == DNP3_FAIL ) { #ifdef DUMP_BUFFER dumpBuffer(DNP3_RESERVED_BAD_ADDR_DUMP,packet->payload,packet->payload_size); #endif return DNP3_FAIL; } /* XXX: NEED TO TRACK SEPARATE DNP3 SESSIONS OVER SINGLE TCP SESSION */ /* Step 2: Remove CRCs */ if ( DNP3CheckRemoveCRC(config, pdu_start, pdu_length, buf, &buflen) == DNP3_FAIL ) { #ifdef DUMP_BUFFER dumpBuffer(DNP3_BAD_CRC_DUMP,packet->payload,packet->payload_size); #endif return DNP3_FAIL; } /* Step 3: Queue user data in frame for Transport-Layer reassembly */ if (session->direction == DNP3_CLIENT) rdata = &(session->client_rdata); else rdata = &(session->server_rdata); if (DNP3ReassembleTransport(rdata, buf, buflen) == DNP3_FAIL) return DNP3_FAIL; /* Step 4: Decode Application-Layer */ if (rdata->state == DNP3_REASSEMBLY_STATE__DONE) { int ret = DNP3ProcessApplication(session); /* To support multiple PDUs in UDP, we're going to call Detect() on each individual PDU. The AltDecode buffer was set earlier. */ if ((ret == DNP3_OK) && (packet->udp_header)) _dpd.detect(packet); else return ret; } return DNP3_OK; } snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_paf.h0000644000175000017500000000241714241075713020732 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Protocol Aware Flushing (PAF) code for DNP3 preprocessor. * */ #ifndef DNP3_PAF__H #define DNP3_PAF__H #include "spp_dnp3.h" #include "stream_api.h" int DNP3AddPortsToPaf(struct _SnortConfig *sc, dnp3_config_t *config, tSfPolicyId policy_id); int DNP3AddServiceToPaf(struct _SnortConfig *sc, uint16_t service, tSfPolicyId policy_id); #endif /* DNP3_PAF__H */ snort-2.9.20/src/dynamic-preprocessors/dnp3/sf_dnp3.dsp0000444000175000017500000001347314230012554021125 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_dnp3" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_dnp3 - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_dnp3.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_dnp3.mak" CFG="sf_dnp3 - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_dnp3 - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_dnp3 - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_dnp3 - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "SF_SNORT_PREPROC_DLL" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_dnp3 - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "SF_SNORT_PREPROC_DLL" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_dnp3 - Win32 Release" # Name "sf_dnp3 - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\dnp3_map.c # End Source File # Begin Source File SOURCE=.\dnp3_paf.c # End Source File # Begin Source File SOURCE=.\dnp3_reassembly.c # End Source File # Begin Source File SOURCE=.\dnp3_roptions.c # End Source File # Begin Source File SOURCE=..\include\mempool.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\spp_dnp3.c # End Source File # Begin Source File SOURCE=..\include\strtok_r.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\dnp3_map.h # End Source File # Begin Source File SOURCE=.\dnp3_paf.h # End Source File # Begin Source File SOURCE=.\dnp3_reassembly.h # End Source File # Begin Source File SOURCE=.\dnp3_roptions.h # End Source File # Begin Source File SOURCE=..\include\mempool.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.h # End Source File # Begin Source File SOURCE=..\include\sf_sdlist_types.h # End Source File # Begin Source File SOURCE=.\spp_dnp3.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/dnp3/sf_dnp3.vcxproj0000444000175000017500000004164414230012554022033 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {682D5F1B-D537-43F5-8D3E-90FD4926019E} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Debug\ .\Debug\ true true .\Release\ .\Release\ false false .\Release\ .\Release\ MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_dnp3.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_dnp3.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_dnp3.bsc true true true Console .\Debug\sf_dnp3.dll .\Debug\sf_dnp3.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_dnp3.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_dnp3.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_dnp3.bsc true true true Console .\Debug\sf_dnp3.dll .\Debug\sf_dnp3.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_dnp3.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_dnp3.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_dnp3.bsc true true Console .\Release\sf_dnp3.dll .\Release\sf_dnp3.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_dnp3.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_dnp3.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_dnp3.bsc true true Console .\Release\sf_dnp3.dll .\Release\sf_dnp3.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/dnp3/Makefile.am0000444000175000017500000000240414230012554021105 0ustar apoapo# $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_dnp3_preproc.la libsf_dnp3_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_dnp3_preproc_la_LIBADD = ../libsf_dynamic_preproc.la if BUILD_SNORT_RELOAD libsf_dnp3_preproc_la_LIBADD += ../libsf_dynamic_utils.la endif else nodist_libsf_dnp3_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c \ ../include/mempool.c \ ../include/sf_sdlist.c if BUILD_SNORT_RELOAD nodist_libsf_dnp3_preproc_la_SOURCES += ../include/appdata_adjuster.c ../include/sfxhash.c ../include/sfhashfcn.c ../include/sfmemcap.c ../include/sfprimetable.c ../include/reg_test.h ../include/reg_test.c endif endif libsf_dnp3_preproc_la_SOURCES = \ spp_dnp3.c \ spp_dnp3.h \ dnp3_paf.c \ dnp3_paf.h \ dnp3_reassembly.c \ dnp3_reassembly.h \ dnp3_roptions.c \ dnp3_roptions.h \ dnp3_map.c \ dnp3_map.h if BUILD_BUFFER_DUMP libsf_dnp3_preproc_la_SOURCES += \ dnp3_buffer_dump.c \ dnp3_buffer_dump.h endif EXTRA_DIST = \ sf_dnp3.vcxproj \ sf_dnp3.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_roptions.h0000644000175000017500000000357314241075717022051 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Rule options for the DNP3 preprocessor * */ #ifndef DNP3_ROPTIONS__H #define DNP3_ROPTIONS__H #include /* option names */ #define DNP3_FUNC_NAME "dnp3_func" #define DNP3_OBJ_NAME "dnp3_obj" #define DNP3_IND_NAME "dnp3_ind" #define DNP3_DATA_NAME "dnp3_data" /* Rule registration functions */ int DNP3FuncInit(struct _SnortConfig *sc, char *name, char *params, void **data); int DNP3ObjInit(struct _SnortConfig *sc, char *name, char *params, void **data); int DNP3IndInit(struct _SnortConfig *sc, char *name, char *params, void **data); int DNP3DataInit(struct _SnortConfig *sc, char *name, char *params, void **data); /* Rule evaluation functions */ int DNP3FuncEval(void *raw_packet, const uint8_t **cursor, void *data); int DNP3ObjEval(void *raw_packet, const uint8_t **cursor, void *data); int DNP3IndEval(void *raw_packet, const uint8_t **cursor, void *data); int DNP3DataEval(void *raw_packet, const uint8_t **cursor, void *data); #endif /* DNP3_ROPTIONS__H */ snort-2.9.20/src/dynamic-preprocessors/dnp3/dnp3_reassembly.h0000644000175000017500000000240514241075715022331 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the DNP3 protocol * */ #ifndef DNP3_REASSEMBLY__H #define DNP3_REASSEMBLY__H #include "sf_types.h" #include "sf_snort_packet.h" #include "spp_dnp3.h" int DNP3FullReassembly(dnp3_config_t *config, dnp3_session_data_t *session, SFSnortPacket *packet, uint8_t *pdu_start, uint16_t pdu_length); #endif /* DNP3_REASSEMBLY__H */ snort-2.9.20/src/dynamic-preprocessors/imap/0000755000175000017500000000000014242725715017152 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/imap/imap_buffer_dump.c0000644000175000017500000000374314241076027022623 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file imap_buffer_dump.c ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during IMAP inspection. */ #include "imap_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_IMAP_BUFFER_DUMP] = {{"IMAP_CLIENT_DUMP", "", 0}, {"IMAP_SERVER_DUMP", "", 0}, {"IMAP_CLIENT_CMD_DUMP", "", 0}, {"IMAP_SERVER_BODY_DATA_DUMP", "", 0}}; void dumpBuffer(IMAP_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *)content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_IMAP_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getIMAPBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/imap/imap_log.h0000644000175000017500000000464014241076035021107 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * imap_log.h * * Author: Bhagyashree Bantwal * **************************************************************************/ #ifndef __IMAP_LOG_H__ #define __IMAP_LOG_H__ #define GENERATOR_SPP_IMAP 141 /* Events for IMAP */ #define IMAP_UNKNOWN_CMD 1 #define IMAP_UNKNOWN_RESP 2 #define IMAP_MEMCAP_EXCEEDED 3 #define IMAP_B64_DECODING_FAILED 4 #define IMAP_QP_DECODING_FAILED 5 /* Do not delete or reuse this SID. Commenting this SID as this alert is no longer valid.* * #define IMAP_BITENC_DECODING_FAILED 6 */ #define IMAP_UU_DECODING_FAILED 7 #define IMAP_EVENT_MAX 8 /* Messages for each event */ #define IMAP_UNKNOWN_CMD_STR "(IMAP) Unknown IMAP4 command" #define IMAP_UNKNOWN_RESP_STR "(IMAP) Unknown IMAP4 response" #define IMAP_MEMCAP_EXCEEDED_STR "(IMAP) No memory available for decoding. Memcap exceeded" #define IMAP_B64_DECODING_FAILED_STR "(IMAP) Base64 Decoding failed." #define IMAP_QP_DECODING_FAILED_STR "(IMAP) Quoted-Printable Decoding failed." #define IMAP_UU_DECODING_FAILED_STR "(IMAP) Unix-to-Unix Decoding failed." #define EVENT_STR_LEN 256 /* Function prototypes */ void IMAP_GenerateAlert(int, char *, ...); void IMAP_DecodeAlert(void *ds); #endif snort-2.9.20/src/dynamic-preprocessors/imap/imap_config.c0000644000175000017500000002563114241076032021566 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*************************************************************************** * imap_config.c * * Author: Bhagyashree Bantwal * * Description: * * Handle configuration of the IMAP preprocessor * * Entry point functions: * * IMAP_ParseArgs() * ***************************************************************************/ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_imap.h" #include "imap_config.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" #include "sfPolicy.h" /* Global variable to hold configuration */ extern IMAPConfig **imap_config; extern const IMAPToken imap_known_cmds[]; /* Private functions */ static int ProcessPorts(IMAPConfig *, char *, int, char **); /* * Function: IMAP_ParseArgs(char *) * * Purpose: Process the preprocessor arguments from the rules file and * initialize the preprocessor's data struct. This function doesn't * have to exist if it makes sense to parse the args in the init * function. * * Arguments: args => argument list * * Returns: void function * */ void IMAP_ParseArgs(IMAPConfig *config, char *args) { int ret = 0; char *arg; char *saveptr; char errStr[ERRSTRLEN]; int errStrLen = ERRSTRLEN; if ((config == NULL) || (args == NULL)) return; enablePort( config->ports, IMAP_DEFAULT_SERVER_PORT ); config->memcap = DEFAULT_IMAP_MEMCAP; _dpd.fileAPI->set_mime_decode_config_defauts(&(config->decode_conf)); _dpd.fileAPI->set_mime_log_config_defauts(&(config->log_config)); *errStr = '\0'; arg = strtok_r(args, CONF_SEPARATORS, &saveptr); while ( arg != NULL ) { unsigned long value = 0; if ( !strcasecmp(CONF_PORTS, arg) ) { ret = ProcessPorts(config, errStr, errStrLen, &saveptr); } else if ( !strcasecmp(CONF_IMAP_MEMCAP, arg) ) { ret = _dpd.checkValueInRange(strtok_r(NULL, CONF_SEPARATORS, &saveptr), CONF_IMAP_MEMCAP, MIN_IMAP_MEMCAP, MAX_IMAP_MEMCAP, &value); config->memcap = (uint32_t)value; } else if ( !strcasecmp(CONF_MAX_MIME_MEM, arg) ) { ret = _dpd.checkValueInRange(strtok_r(NULL, CONF_SEPARATORS, &saveptr), CONF_MAX_MIME_MEM, MIN_MIME_MEM, MAX_MIME_MEM, &value); config->decode_conf.max_mime_mem = (int)value; } else if(!_dpd.fileAPI->parse_mime_decode_args(&(config->decode_conf), arg, "IMAP", &saveptr)) { ret = 0; } else if ( !strcasecmp(CONF_DISABLED, arg) ) { config->disabled = 1; } else { DynamicPreprocessorFatalMessage("%s(%d) => Unknown IMAP configuration option %s\n", *(_dpd.config_file), *(_dpd.config_line), arg); } if (ret == -1) { /* ** Fatal Error, log error and exit. */ if (*errStr) { DynamicPreprocessorFatalMessage("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), errStr); } else { DynamicPreprocessorFatalMessage("%s(%d) => Undefined Error.\n", *(_dpd.config_file), *(_dpd.config_line)); } } /* Get next token */ arg = strtok_r(NULL, CONF_SEPARATORS, &saveptr); } } void IMAP_CheckConfig(IMAPConfig *pPolicyConfig, tSfPolicyUserContextId context) { IMAPConfig *defaultConfig = (IMAPConfig *)sfPolicyUserDataGetDefault(context); if (pPolicyConfig == defaultConfig) { if (! _dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), &(defaultConfig->decode_conf), "IMAP")) return; if (!pPolicyConfig->memcap) pPolicyConfig->memcap = DEFAULT_IMAP_MEMCAP; } else if (defaultConfig == NULL) { _dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), NULL, "IMAP"); } else { pPolicyConfig->memcap = defaultConfig->memcap; if(pPolicyConfig->disabled) { pPolicyConfig->decode_conf = defaultConfig->decode_conf; return; } _dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), &(defaultConfig->decode_conf), "IMAP"); } } void IMAP_PrintConfig(IMAPConfig *config) { int i; int j = 0; char buf[8192]; if (config == NULL) return; memset(&buf[0], 0, sizeof(buf)); _dpd.logMsg("IMAP Config:\n"); if(config->disabled) _dpd.logMsg(" IMAP: INACTIVE\n"); snprintf(buf, sizeof(buf) - 1, " Ports: "); for(i = 0; i < 65536; i++) { if( isPortEnabled( config->ports, i ) ) { j++; _dpd.printfappend(buf, sizeof(buf) - 1, "%d ", i); if(!(j%10)) _dpd.printfappend(buf, sizeof(buf) - 1, "\n "); } } _dpd.logMsg("%s\n", buf); _dpd.logMsg(" IMAP Memcap: %u\n", config->memcap); _dpd.logMsg(" MIME Max Mem: %d\n", config->decode_conf.max_mime_mem); if(config->decode_conf.b64_depth > -1) { _dpd.logMsg(" Base64 Decoding: %s\n", "Enabled"); switch(config->decode_conf.b64_depth) { case 0: _dpd.logMsg(" Base64 Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Base64 Decoding Depth: %d\n", config->decode_conf.b64_depth); break; } } else _dpd.logMsg(" Base64 Decoding: %s\n", "Disabled"); if(config->decode_conf.qp_depth > -1) { _dpd.logMsg(" Quoted-Printable Decoding: %s\n","Enabled"); switch(config->decode_conf.qp_depth) { case 0: _dpd.logMsg(" Quoted-Printable Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Quoted-Printable Decoding Depth: %d\n", config->decode_conf.qp_depth); break; } } else _dpd.logMsg(" Quoted-Printable Decoding: %s\n", "Disabled"); if(config->decode_conf.uu_depth > -1) { _dpd.logMsg(" Unix-to-Unix Decoding: %s\n","Enabled"); switch(config->decode_conf.uu_depth) { case 0: _dpd.logMsg(" Unix-to-Unix Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Unix-to-Unix Decoding Depth: %d\n", config->decode_conf.uu_depth); break; } } else _dpd.logMsg(" Unix-to-Unix Decoding: %s\n", "Disabled"); if(config->decode_conf.bitenc_depth > -1) { _dpd.logMsg(" Non-Encoded MIME attachment Extraction: %s\n","Enabled"); switch(config->decode_conf.bitenc_depth) { case 0: _dpd.logMsg(" Non-Encoded MIME attachment Extraction Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Non-Encoded MIME attachment Extraction Depth: %d\n", config->decode_conf.bitenc_depth); break; } } else _dpd.logMsg(" Non-Encoded MIME attachment Extraction: %s\n", "Disabled"); } /* ** NAME ** ProcessPorts:: */ /** ** Process the port list. ** ** This configuration is a list of valid ports and is ended by a ** delimiter. ** ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessPorts(IMAPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int iPort; int iEndPorts = 0; int num_ports = 0; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "IMAP config is NULL.\n"); return -1; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid port list format."); return -1; } if(strcmp(CONF_START_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a port list with the '%s' token.", CONF_START_LIST); return -1; } /* Since ports are specified, clear default ports */ disablePort( config->ports, IMAP_DEFAULT_SERVER_PORT ); while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(CONF_END_LIST, pcToken)) { iEndPorts = 1; break; } iPort = strtol(pcToken, &pcEnd, 10); /* ** Validity check for port */ if(*pcEnd) { snprintf(ErrorString, ErrStrLen, "Invalid port number."); return -1; } if(iPort < 0 || iPort > MAXPORTS-1) { snprintf(ErrorString, ErrStrLen, "Invalid port number. Must be between 0 and 65535."); return -1; } enablePort( config->ports, iPort ); num_ports++; } if(!iEndPorts) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", CONF_PORTS, CONF_END_LIST); return -1; } else if(!num_ports) { snprintf(ErrorString, ErrStrLen, "IMAP: Empty port list not allowed."); return -1; } return 0; } snort-2.9.20/src/dynamic-preprocessors/imap/spp_imap.c0000644000175000017500000007227614241076045021136 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * spp_imap.c * * Author: Bhagyashree Bantwal * * Description: * * This file initializes IMAP as a Snort preprocessor. * * This file registers the IMAP initialization function, * adds the IMAP function into the preprocessor list. * * In general, this file is a wrapper to IMAP functionality, * by interfacing with the Snort preprocessor functions. The rest * of IMAP should be separate from the preprocessor hooks. * **************************************************************************/ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_imap.h" #include "sf_preproc_info.h" #include "snort_imap.h" #include "imap_util.h" #include "imap_config.h" #include "imap_log.h" #include "imap_paf.h" #include "preprocids.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "snort_debug.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "profiler.h" #ifdef PERF_PROFILING PreprocStats imapPerfStats; PreprocStats imapDetectPerfStats; int imapDetectCalled = 0; #endif #include "sf_types.h" #include "mempool.h" #include "snort_bounds.h" #include "file_api.h" #ifdef REG_TEST #include "reg_test.h" #endif #ifdef DUMP_BUFFER #include "imap_buffer_dump.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 0; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_IMAP"; const char *PROTOCOL_NAME = "IMAP"; #define SetupIMAP DYNAMIC_PREPROC_SETUP MemPool *imap_mempool = NULL; MemPool *imap_mime_mempool = NULL; IMAP_Stats imap_stats; tSfPolicyUserContextId imap_config = NULL; IMAPConfig *imap_eval_config = NULL; extern int16_t imap_proto_id; static void IMAPInit(struct _SnortConfig *, char *); static void IMAPDetect(void *, void *context); static void IMAPCleanExitFunction(int, void *); static void IMAPResetFunction(int, void *); static void IMAPResetStatsFunction(int, void *); static void registerPortsForDispatch( struct _SnortConfig *sc, IMAPConfig *policy ); static void registerPortsForReassembly( IMAPConfig *policy, int direction ); static void _addPortsToStreamFilter(struct _SnortConfig *, IMAPConfig *, tSfPolicyId); static void IMAP_PrintStats(int); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static int IMAPCheckConfig(struct _SnortConfig *); #ifdef SNORT_RELOAD static int IMAPMempoolFreeUsedBucket(MemPool *memory_pool); static unsigned IMAPReloadMimeMempoolAdjust(unsigned imapMaxWork); static unsigned IMAPReloadLogMempoolAdjust(unsigned imapMaxWork); static bool IMAPMimeReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData); static bool IMAPLogReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData); static void IMAPReload(struct _SnortConfig *, char *, void **); static int IMAPReloadVerify(struct _SnortConfig *, void *); static void * IMAPReloadSwap(struct _SnortConfig *, void *); static void IMAPReloadSwapFree(void *); #endif /* * Function: SetupIMAP() * * Purpose: Registers the preprocessor keyword and initialization * function into the preprocessor list. This is the function that * gets called from InitPreprocessors() in plugbase.c. * * Arguments: None. * * Returns: void function * */ void SetupIMAP(void) { /* link the preprocessor keyword to the init function in the preproc list */ #ifndef SNORT_RELOAD _dpd.registerPreproc("imap", IMAPInit); #else _dpd.registerPreproc("imap", IMAPInit, IMAPReload, IMAPReloadVerify, IMAPReloadSwap, IMAPReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getIMAPBuffers, IMAP_BUFFER_DUMP_FUNC); #endif } #ifdef REG_TEST static inline void PrintIMAPSize(void) { _dpd.logMsg("\nIMAP Session Size: %lu\n", (long unsigned int)sizeof(IMAP)); } #endif /* * Function: IMAPInit(char *) * * Purpose: Calls the argument parsing function, performs final setup on data * structs, links the preproc function into the function list. * * Arguments: args => ptr to argument string * * Returns: void function * */ static void IMAPInit(struct _SnortConfig *sc, char *args) { IMAPToken *tmp; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); IMAPConfig * pPolicyConfig = NULL; #ifdef REG_TEST PrintIMAPSize(); #endif _dpd.registerMemoryStatsFunc(PP_IMAP, IMAP_Print_Mem_Stats); if (imap_config == NULL) { //create a context imap_config = sfPolicyConfigCreate(); if (imap_config == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create IMAP " "configuration.\n"); } /* Initialize the searches not dependent on configuration. * headers, reponsed, data, mime boundary regular expression */ IMAP_SearchInit(); /* Put the preprocessor function into the function list */ /* _dpd.addPreproc(IMAPDetect, PRIORITY_APPLICATION, PP_IMAP, PROTO_BIT__TCP);*/ _dpd.addPreprocExit(IMAPCleanExitFunction, NULL, PRIORITY_LAST, PP_IMAP); _dpd.addPreprocReset(IMAPResetFunction, NULL, PRIORITY_LAST, PP_IMAP); _dpd.registerPreprocStats(IMAP_PROTO_REF_STR, IMAP_PrintStats); _dpd.addPreprocResetStats(IMAPResetStatsFunction, NULL, PRIORITY_LAST, PP_IMAP); _dpd.addPreprocConfCheck(sc, IMAPCheckConfig); #ifdef TARGET_BASED imap_proto_id = _dpd.findProtocolReference(IMAP_PROTO_REF_STR); if (imap_proto_id == SFTARGET_UNKNOWN_PROTOCOL) imap_proto_id = _dpd.addProtocolReference(IMAP_PROTO_REF_STR); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_IMAP, imap_proto_id ); DEBUG_WRAP(DebugMessage(DEBUG_IMAP,"IMAP: Target-based: Proto id for %s: %u.\n", IMAP_PROTO_REF_STR, imap_proto_id);); #endif #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("imap", (void*)&imapPerfStats, 0, _dpd.totalPerfStats, NULL); #endif } sfPolicyUserPolicySet (imap_config, policy_id); pPolicyConfig = (IMAPConfig *)sfPolicyUserDataGetCurrent(imap_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("Can only configure IMAP preprocessor once.\n"); } pPolicyConfig = (IMAPConfig *)_dpd.snortAlloc(1, sizeof(IMAPConfig), PP_IMAP, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create IMAP " "configuration.\n"); } sfPolicyUserDataSetCurrent(imap_config, pPolicyConfig); IMAP_InitCmds(pPolicyConfig); IMAP_ParseArgs(pPolicyConfig, args); IMAP_CheckConfig(pPolicyConfig, imap_config); IMAP_PrintConfig(pPolicyConfig); if(pPolicyConfig->disabled) return; _dpd.addPreproc(sc, IMAPDetect, PRIORITY_APPLICATION, PP_IMAP, PROTO_BIT__TCP); if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("Streaming & reassembly must be enabled " "for IMAP preprocessor\n"); } /* Command search - do this here because it's based on configuration */ pPolicyConfig->cmd_search_mpse = _dpd.searchAPI->search_instance_new(); if (pPolicyConfig->cmd_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate IMAP " "command search.\n"); } for (tmp = pPolicyConfig->cmds; tmp->name != NULL; tmp++) { pPolicyConfig->cmd_search[tmp->search_id].name = tmp->name; pPolicyConfig->cmd_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(pPolicyConfig->cmd_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(pPolicyConfig->cmd_search_mpse); // register ports with session and stream registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStreamFilter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } /* * Function: IMAPDetect(void *, void *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ static void IMAPDetect(void *pkt, void *context) { SFSnortPacket *p = (SFSnortPacket *)pkt; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); PROFILE_VARS; // preconditions - what we registered for assert(IsTCP(p) && p->payload && p->payload_size); PREPROC_PROFILE_START(imapPerfStats); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP Start (((((((((((((((((((((((((((((((((((((((\n");); sfPolicyUserPolicySet (imap_config, policy_id); SnortIMAP(p); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP End )))))))))))))))))))))))))))))))))))))))))\n\n");); PREPROC_PROFILE_END(imapPerfStats); #ifdef PERF_PROFILING if (PROFILING_PREPROCS && imapDetectCalled) { imapPerfStats.ticks -= imapDetectPerfStats.ticks; /* And Reset ticks to 0 */ imapDetectPerfStats.ticks = 0; imapDetectCalled = 0; } #endif } /* * Function: IMAPCleanExitFunction(int, void *) * * Purpose: This function gets called when Snort is exiting, if there's * any cleanup that needs to be performed (e.g. closing files) * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * function when it was registered, may be * needed to properly exit * * Returns: void function */ static void IMAPCleanExitFunction(int signal, void *data) { IMAP_Free(); if (mempool_destroy(imap_mime_mempool) == 0) { free(imap_mime_mempool); imap_mime_mempool = NULL; } if (mempool_destroy(imap_mempool) == 0) { free(imap_mempool); imap_mempool = NULL; } } static void IMAPResetFunction(int signal, void *data) { return; } static void IMAPResetStatsFunction(int signal, void *data) { return; } static void registerPortsForDispatch( struct _SnortConfig *sc, IMAPConfig *policy ) { int port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_IMAP, PROTO_BIT__TCP, port ); } } static void registerPortsForReassembly( IMAPConfig *policy, int direction ) { uint32_t port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.streamAPI->register_reassembly_port( NULL, port, direction ); } } static void _addPortsToStreamFilter(struct _SnortConfig *sc, IMAPConfig *config, tSfPolicyId policy_id) { unsigned int portNum; if (config == NULL) return; for (portNum = 0; portNum < MAXPORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); register_imap_paf_port(sc, portNum, policy_id); } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, imap_proto_id, PORT_MONITOR_SESSION, policy_id, 1); register_imap_paf_service(sc, imap_proto_id, policy_id); } #endif static int CheckFilePolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { IMAPConfig *context = (IMAPConfig *)pData; /* Use new Snort config to get the max file depth */ context->decode_conf.file_depth = _dpd.fileAPI->get_max_file_depth(sc, true); if (context->decode_conf.file_depth > -1) context->log_config.log_filename = 1; updateMaxDepth(context->decode_conf.file_depth, &context->decode_conf.max_depth); return 0; } static int IMAPEnableDecoding(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void *pData) { IMAPConfig *context = (IMAPConfig *)pData; if (pData == NULL) return 0; if(context->disabled) return 0; if(_dpd.fileAPI->is_decoding_enabled(&(context->decode_conf))) return 1; return 0; } static int IMAPCheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { IMAPConfig *context = (IMAPConfig *)pData; _dpd.setParserPolicy(sc, policyId); /* In a multiple-policy setting, the IMAP preproc can be turned on in a "disabled" state. In this case, we don't require Stream. */ if (context->disabled) return 0; if (_dpd.streamAPI == NULL) { _dpd.errMsg("Streaming & reassembly must be enabled for IMAP preprocessor\n"); return -1; } return 0; } static int IMAPLogExtraData(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void *pData) { IMAPConfig *context = (IMAPConfig *)pData; if (pData == NULL) return 0; if(context->disabled) return 0; if(context->log_config.log_filename) return 1; return 0; } static int IMAPCheckConfig(struct _SnortConfig *sc) { int rval; IMAPConfig *defaultConfig = (IMAPConfig *)sfPolicyUserDataGetDefault(imap_config); if ((rval = sfPolicyUserDataIterate (sc, imap_config, IMAPCheckPolicyConfig))) return rval; if ((rval = sfPolicyUserDataIterate (sc, imap_config, CheckFilePolicyConfig))) return rval; if (sfPolicyUserDataIterate(sc, imap_config, IMAPEnableDecoding) != 0) { if (defaultConfig == NULL) { /*error message */ _dpd.errMsg("IMAP: Must configure a default " "configuration if you want to imap decoding.\n"); return -1; } imap_mime_mempool = (MemPool *)_dpd.fileAPI->init_mime_mempool(defaultConfig->decode_conf.max_mime_mem, defaultConfig->decode_conf.max_depth, imap_mime_mempool, PROTOCOL_NAME); } if (sfPolicyUserDataIterate(sc, imap_config, IMAPLogExtraData) != 0) { if (defaultConfig == NULL) { /*error message */ _dpd.errMsg("IMAP: Must configure a default " "configuration if you want to log extra data.\n"); return -1; } imap_mempool = (MemPool *)_dpd.fileAPI->init_log_mempool(0, defaultConfig->memcap, imap_mempool, PROTOCOL_NAME); } return 0; } static void IMAP_PrintStats(int exiting) { _dpd.logMsg("IMAP Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions : " STDu64 "\n", imap_stats.sessions); _dpd.logMsg(" Max concurrent sessions : " STDu64 "\n", imap_stats.max_conc_sessions); if (imap_stats.sessions > 0) { _dpd.logMsg(" Base64 attachments decoded : " STDu64 "\n", imap_stats.mime_stats.attachments[DECODE_B64]); _dpd.logMsg(" Total Base64 decoded bytes : " STDu64 "\n", imap_stats.mime_stats.decoded_bytes[DECODE_B64]); _dpd.logMsg(" Quoted-Printable attachments decoded : " STDu64 "\n", imap_stats.mime_stats.attachments[DECODE_QP]); _dpd.logMsg(" Total Quoted decoded bytes : " STDu64 "\n", imap_stats.mime_stats.decoded_bytes[DECODE_QP]); _dpd.logMsg(" UU attachments decoded : " STDu64 "\n", imap_stats.mime_stats.attachments[DECODE_UU]); _dpd.logMsg(" Total UU decoded bytes : " STDu64 "\n", imap_stats.mime_stats.decoded_bytes[DECODE_UU]); _dpd.logMsg(" Non-Encoded MIME attachments extracted : " STDu64 "\n", imap_stats.mime_stats.attachments[DECODE_BITENC]); _dpd.logMsg(" Total Non-Encoded MIME bytes extracted : " STDu64 "\n", imap_stats.mime_stats.decoded_bytes[DECODE_BITENC]); if ( imap_stats.mime_stats.memcap_exceeded ) _dpd.logMsg(" Sessions not decoded due to memory unavailability : " STDu64 "\n", imap_stats.mime_stats.memcap_exceeded); if ( imap_stats.log_memcap_exceeded ) _dpd.logMsg(" IMAP sessions fastpathed due to memcap exceeded: " STDu64 "\n", imap_stats.log_memcap_exceeded); } } #ifdef SNORT_RELOAD static void IMAPReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId imap_swap_config = (tSfPolicyUserContextId)*new_config; IMAPToken *tmp; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); IMAPConfig *pPolicyConfig = NULL; if (imap_swap_config == NULL) { //create a context imap_swap_config = sfPolicyConfigCreate(); if (imap_swap_config == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create IMAP " "configuration.\n"); } *new_config = (void *)imap_swap_config; } sfPolicyUserPolicySet (imap_swap_config, policy_id); pPolicyConfig = (IMAPConfig *)sfPolicyUserDataGetCurrent(imap_swap_config); if (pPolicyConfig != NULL) DynamicPreprocessorFatalMessage("Can only configure IMAP preprocessor once.\n"); pPolicyConfig = (IMAPConfig *)_dpd.snortAlloc(1, sizeof(IMAPConfig), PP_IMAP, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create IMAP " "configuration.\n"); } sfPolicyUserDataSetCurrent(imap_swap_config, pPolicyConfig); IMAP_InitCmds(pPolicyConfig); IMAP_ParseArgs(pPolicyConfig, args); IMAP_CheckConfig(pPolicyConfig, imap_swap_config); IMAP_PrintConfig(pPolicyConfig); if( pPolicyConfig->disabled ) return; if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("Streaming & reassembly must be enabled " "for IMAP preprocessor\n"); } /* Command search - do this here because it's based on configuration */ pPolicyConfig->cmd_search_mpse = _dpd.searchAPI->search_instance_new(); if (pPolicyConfig->cmd_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate IMAP " "command search.\n"); } for (tmp = pPolicyConfig->cmds; tmp->name != NULL; tmp++) { pPolicyConfig->cmd_search[tmp->search_id].name = tmp->name; pPolicyConfig->cmd_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(pPolicyConfig->cmd_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(pPolicyConfig->cmd_search_mpse); _dpd.addPreproc(sc, IMAPDetect, PRIORITY_APPLICATION, PP_IMAP, PROTO_BIT__TCP); // register ports with session and stream registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStreamFilter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } static int IMAPMempoolFreeUsedBucket(MemPool *memory_pool) { MemBucket *lru_bucket = NULL; lru_bucket = mempool_get_lru_bucket(memory_pool); if(lru_bucket) { /* Deleting least recently used IMAP session data here to adjust to new max_memory */ _dpd.sessionAPI->set_application_data(lru_bucket->scbPtr, PP_IMAP, NULL, NULL); return 1; } return 0; } static unsigned IMAPReloadMimeMempoolAdjust(unsigned imapMaxWork) { int retVal; /* deleting MemBucket from free list in IMAP Mime Mempool */ imapMaxWork = mempool_prune_freelist(imap_mime_mempool, imap_mime_mempool->max_memory, imapMaxWork); if(!imapMaxWork) return 0; for( ; imapMaxWork && ((imap_mime_mempool->used_memory + imap_mime_mempool->free_memory) > imap_mime_mempool->max_memory); imapMaxWork--) { /* deleting least recently used MemBucket from Used list in IMAP Mime Mempool */ retVal = IMAPMempoolFreeUsedBucket(imap_mime_mempool); if(!retVal) break; } return imapMaxWork; } static unsigned IMAPReloadLogMempoolAdjust(unsigned imapMaxWork) { int retVal; /* deleting MemBucket from free list in IMAP Log Mempool */ imapMaxWork = mempool_prune_freelist(imap_mempool, imap_mempool->max_memory, imapMaxWork); if(!imapMaxWork) return 0; for( ; imapMaxWork && ((imap_mempool->used_memory + imap_mempool->free_memory) > imap_mempool->max_memory); imapMaxWork--) { /* deleting least recently used MemBucket from Used list in IMAP Log Mempool */ retVal = IMAPMempoolFreeUsedBucket(imap_mempool); if(!retVal) break; } return imapMaxWork; } static bool IMAPMimeReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned initialMaxWork = idle ? 512 : 5; unsigned maxWork; /* If new max_mime_mem is less than old configured max_mime_mem, need to adjust IMAP Mime Mempool. * In order to adjust to new max_memory of mime mempool, delete buckets from free list. * After deleting buckets from free list, still new max_memory is less than old value , delete buckets * (least recently used i.e head node of used list )from used list till total memory reaches to new max_memory. */ maxWork = IMAPReloadMimeMempoolAdjust(initialMaxWork); if(maxWork == initialMaxWork) { imap_stats.max_conc_sessions = imap_stats.conc_sessions; imap_stats.mime_stats.memcap_exceeded = 0; return true; } else return false; } static bool IMAPLogReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned initialMaxWork = idle ? 512 : 5; unsigned maxWork; /* If new memcap is less than old configured memcap, need to adjust IMAP Log Mempool. * In order to adjust to new max_memory of log mempool, delete buckets from free list. * After deleting buckets from free list, still new max_memory is less than old value , delete buckets * (least recently used i.e head node of used list )from used list till total memory reaches to new max_memory. */ maxWork = IMAPReloadLogMempoolAdjust(initialMaxWork); if (maxWork == initialMaxWork) { imap_stats.max_conc_sessions = imap_stats.conc_sessions; imap_stats.mime_stats.memcap_exceeded = 0; return true; } else return false; } static int IMAPReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId imap_swap_config = (tSfPolicyUserContextId)swap_config; IMAPConfig *config = NULL; IMAPConfig *configNext = NULL; tSfPolicyId policy_id = 0; int rval; if (imap_swap_config == NULL) return 0; if (imap_config != NULL) config = (IMAPConfig *)sfPolicyUserDataGet(imap_config, _dpd.getDefaultPolicy()); configNext = (IMAPConfig *)sfPolicyUserDataGet(imap_swap_config, _dpd.getDefaultPolicy()); if (config == NULL) return 0; if ((rval = sfPolicyUserDataIterate( sc, imap_swap_config, IMAPCheckPolicyConfig ))) return rval; if ((rval = sfPolicyUserDataIterate( sc, imap_swap_config, CheckFilePolicyConfig ))) return rval; policy_id = _dpd.getParserPolicy(sc); if (imap_mime_mempool != NULL) { /* If max_mime_mem changes, mime mempool need to be adjusted bcz mempool max_memory will be changed. Registering here to adjust Mime memory Pool when max_mime_mem changes. */ if( configNext->decode_conf.max_mime_mem < config->decode_conf.max_mime_mem ) _dpd.reloadAdjustRegister(sc, "IMAP-MIME-MEMPOOL", policy_id, &IMAPMimeReloadAdjust, NULL, NULL); } if (imap_mempool != NULL) { if(configNext) { /* If memcap cahnges, log mempool need to be adjusted bcz mempool max_mempory will be changed. Registering here to adjust Log memory Pool when memcap changes. */ if (configNext->memcap < config->memcap) _dpd.reloadAdjustRegister(sc, "IMAP-LOG-MEMPOOL", policy_id, &IMAPLogReloadAdjust, NULL, NULL); } } else if(configNext != NULL) { if (sfPolicyUserDataIterate(sc, imap_swap_config, IMAPEnableDecoding) != 0) { imap_mime_mempool = (MemPool *)_dpd.fileAPI->init_mime_mempool(configNext->decode_conf.max_mime_mem, configNext->decode_conf.max_depth, imap_mime_mempool, PROTOCOL_NAME); } if (sfPolicyUserDataIterate(sc, imap_swap_config, IMAPLogExtraData) != 0) { imap_mempool = (MemPool *)_dpd.fileAPI->init_log_mempool(0, configNext->memcap, imap_mempool, PROTOCOL_NAME); } if ( configNext->disabled ) return 0; } if (_dpd.streamAPI == NULL) { _dpd.errMsg("Streaming & reassembly must be enabled for IMAP preprocessor\n"); return -1; } return 0; } static int IMAPReloadSwapPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { IMAPConfig *pPolicyConfig = (IMAPConfig *)pData; if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); IMAP_FreeConfig(pPolicyConfig); } return 0; } static void * IMAPReloadSwap(struct _SnortConfig *sc, void *swap_config) { IMAPConfig *configNew = NULL, *configOld = NULL; tSfPolicyUserContextId imap_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = imap_config; if (imap_swap_config == NULL) return NULL; imap_config = imap_swap_config; configOld = (IMAPConfig *)sfPolicyUserDataGet(old_config, _dpd.getDefaultPolicy()); configNew = (IMAPConfig *)sfPolicyUserDataGet(imap_config, _dpd.getDefaultPolicy()); if(configNew) { if(imap_mime_mempool) { if((configOld->decode_conf.max_mime_mem != configNew->decode_conf.max_mime_mem) || (configOld->decode_conf.max_depth != configNew->decode_conf.max_depth) ) { #ifdef REG_TEST _dpd.fileAPI->displayMimeMempool(imap_mime_mempool,&(configOld->decode_conf), &(configNew->decode_conf)); #endif /* Update the imap_mime_mempool with new max_memmory and object size when max_mime_mem changes. */ _dpd.fileAPI->update_mime_mempool(imap_mime_mempool, configNew->decode_conf.max_mime_mem, configNew->decode_conf.max_depth); } } if(imap_mempool) { if(configOld->memcap != configNew->memcap) { #ifdef REG_TEST _dpd.fileAPI->displayLogMempool(imap_mempool, configOld->memcap, configNew->memcap); #endif /* Update the imap_mempool with new max_memory and objest size when memcap changes. */ _dpd.fileAPI->update_log_mempool(imap_mempool, configNew->memcap, 0); imap_stats.log_memcap_exceeded = 0; } } #ifdef REG_TEST _dpd.fileAPI->displayDecodeDepth(&(configOld->decode_conf), &(configNew->decode_conf)); #endif } sfPolicyUserDataFreeIterate (old_config, IMAPReloadSwapPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) IMAP_FreeConfigs(old_config); return NULL; } static void IMAPReloadSwapFree(void *data) { if (data == NULL) return; IMAP_FreeConfigs((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/imap/sf_imap.vcxproj0000444000175000017500000004432514230012554022176 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {4CC650AF-8110-4ED8-92A4-CF5A34BE99D6} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Debug\ .\Debug\ true true .\Release\ .\Release\ false false .\Release\ .\Release\ MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_imap.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_imap.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_imap.bsc true true true Console .\Debug\sf_imap.dll .\Debug\sf_imap.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_imap.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_imap.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_imap.bsc true true true Console .\Debug\sf_imap.dll .\Debug\sf_imap.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_imap.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_imap.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_imap.bsc true true Console .\Release\sf_imap.dll .\Release\sf_imap.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_imap.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_imap.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_imap.bsc true true Console .\Release\sf_imap.dll .\Release\sf_imap.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/imap/imap_paf.c0000644000175000017500000004305514241076036021073 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include #include "sf_types.h" #include "imap_paf.h" #include "spp_imap.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "snort_imap.h" #include "imap_config.h" #include "file_api.h" extern const IMAPToken imap_resps[]; static uint8_t imap_paf_id = 0; typedef struct _ImapDataInfo { int paren_cnt; /* The open parentheses count in fetch */ bool found_len; bool esc_nxt_char; /* true if the next charachter has been escaped */ char *next_letter; /* The current command in fetch */ uint32_t length; } ImapDataInfo; /* State tracker for SMTP PAF */ typedef enum _ImapPafState { IMAP_PAF_REG_STATE, /* default state. eat until LF */ IMAP_PAF_DATA_HEAD_STATE, /* parses the fetch header */ IMAP_PAF_DATA_LEN_STATE, /* parse the literal length */ IMAP_PAF_DATA_STATE, /* search for and flush on MIME boundaries */ IMAP_PAF_FLUSH_STATE, /* flush if a termination sequence is found */ IMAP_PAF_CMD_IDENTIFIER, /* determine the line identifier ('+', '*', tag) */ IMAP_PAF_CMD_TAG, /* currently analyzing tag . identifier */ IMAP_PAF_CMD_STATUS, /* currently parsing second argument */ IMAP_PAF_CMD_SEARCH /* currently searching data for a command */ } ImapPafState; typedef enum _ImapDataEnd { IMAP_PAF_DATA_END_UNKNOWN, IMAP_PAF_DATA_END_PAREN } ImapDataEnd; /* State tracker for POP PAF */ typedef struct _ImapPafData { MimeDataPafInfo mime_info; /* Mime response information */ ImapPafState imap_state; /* The current IMAP paf stat */ bool end_of_data; ImapDataInfo imap_data_info; /* Used for parsing data */ ImapDataEnd data_end_state; // uint32_t length; TODO -- parse and add literal length } ImapPafData; static inline void reset_data_states(ImapPafData *pfdata) { // reset MIME info _dpd.fileAPI->reset_mime_paf_state(&(pfdata->mime_info)); // reset server info pfdata->imap_state = IMAP_PAF_CMD_IDENTIFIER; // reset fetch data information information pfdata->imap_data_info.paren_cnt = 0; pfdata->imap_data_info.next_letter = 0; pfdata->imap_data_info.length = 0; } static inline bool is_untagged(const uint8_t ch) { return (ch == '*' || ch == '+'); } static bool parse_literal_length(const uint8_t ch, uint32_t *len) { uint32_t length = *len; if (isdigit(ch)) { uint64_t tmp_len = (10 * length) + (ch - '0'); if (tmp_len < UINT32_MAX) { *len = (uint32_t) tmp_len; return false; } else { *len = 0; } } else if (ch != '}') *len = 0; // ALERT!! charachter should be a digit or ''}'' return true; } static void parse_fetch_header(const uint8_t ch, ImapPafData *pfdata) { if (pfdata->imap_data_info.esc_nxt_char) { pfdata->imap_data_info.esc_nxt_char = false; } else { switch(ch) { case '{': pfdata->imap_state = IMAP_PAF_DATA_LEN_STATE; break; case '(': pfdata->imap_data_info.paren_cnt++; break; case ')': if (pfdata->imap_data_info.paren_cnt > 0) pfdata->imap_data_info.paren_cnt--; break; case '\n': if (pfdata->imap_data_info.paren_cnt) { pfdata->imap_state = IMAP_PAF_DATA_STATE; } else { reset_data_states(pfdata); } break; case '\\': pfdata->imap_data_info.esc_nxt_char = true; break; default: break; } } } /* * Statefully search for the single line termination sequence LF ("\n"). * * PARAMS: * const uint8_t ch - the next character to analyze. * ImapPafData *pfdata - the struct containing all imap paf information * * RETURNS: * false - if termination sequence not found * true - if termination sequence found */ static bool find_data_end_single_line(const uint8_t ch, ImapPafData *pfdata) { if (ch == '\n') { reset_data_states(pfdata); return true; } return false; } /* Flush based on data length*/ static inline bool literal_complete(ImapPafData *pfdata) { if (pfdata->imap_data_info.length) { pfdata->imap_data_info.length--; if (pfdata->imap_data_info.length) return false; } return true; } static bool check_imap_data_end(ImapDataEnd *data_end_state, uint8_t val) { switch(*data_end_state) { case IMAP_PAF_DATA_END_UNKNOWN: if (val == ')') *data_end_state = (ImapDataEnd) IMAP_PAF_DATA_END_PAREN; break; case IMAP_PAF_DATA_END_PAREN: if (val == '\n') { *data_end_state = (ImapDataEnd) PAF_DATA_END_UNKNOWN; return true; } else if (val != '\r') { *data_end_state = (ImapDataEnd) PAF_DATA_END_UNKNOWN; } break; default: break; } return false; } /* * Statefully search for the data termination sequence or a MIME boundary. * * PARAMS: * const uint8_t ch - the next character to analyze. * ImapPafData *pfdata - the struct containing all imap paf information * * RETURNS: * false - if termination sequence not found * true - if termination sequence found */ static bool find_data_end_mime_data(const uint8_t ch, ImapPafData *pfdata) { if (literal_complete(pfdata) && check_imap_data_end(&(pfdata->data_end_state), ch)) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: End of Data!\n");); reset_data_states(pfdata); return true; } // check for mime flush point if (_dpd.fileAPI->process_mime_paf_data(&(pfdata->mime_info), ch)) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: Mime Boundary found." " Flushing data!\n");); return true; } return false; } /* * Initial command processing function. Determine if this command * may be analyzed irregularly ( which currently means if emails * and email attachments need to be analyzed). * * PARAMS: * const uint8_t ch - the next character to analyze. * ImapPafData *pfdata - the struct containing all imap paf information */ static inline void init_command_search(const uint8_t ch, ImapPafData *pfdata) { switch(ch) { case 'F': case 'f': // may be a FETCH response pfdata->imap_data_info.next_letter = &(imap_resps[RESP_FETCH].name[1]); break; default: // this is not a data command. Search for regular end of line. pfdata->imap_state = IMAP_PAF_REG_STATE; } } /* * Confirms every character in the current sequence is part of the expected * command. After confirmation is complete, IMAP PAF will begin searching * for data. If any character is unexpected, searches for the default * termination sequence. * * PARAMS: * const uint8_t ch - the next character to analyze. * ImapPafData *pfdata - the struct containing all imap paf information */ static inline void parse_command(const uint8_t ch, ImapPafData *pfdata) { char val = *(pfdata->imap_data_info.next_letter); if (val == '\0' && isblank(ch)) pfdata->imap_state = IMAP_PAF_DATA_HEAD_STATE; else if (toupper(ch) == toupper(val)) pfdata->imap_data_info.next_letter++; else pfdata->imap_state = IMAP_PAF_REG_STATE; } /* * Wrapper function for the command parser. Determines whether this is the * first letter being processed and calls the appropriate processing * function. * * PARAMS: * const uint8_t ch - the next character to analyze. * ImapPafData *pfdata - the struct containing all imap paf information */ static inline void process_command(const uint8_t ch, ImapPafData *pfdata) { if (pfdata->imap_data_info.next_letter) parse_command(ch, pfdata); else init_command_search(ch, pfdata); } /* * This function only does something when the character is a blank or a CR/LF. * In those specific cases, this function will set the appropriate next * state information * * PARAMS: * const uint8_t ch - the next character to analyze. * ImapPafData *pfdata - the struct containing all imap paf information * ImapPafData base_state - if a space is not found, revert to this state * ImapPafData next_state - if a space is found, go to this state * RETURNS: * true - if the status has been eaten * false - if a CR or LF has been found */ static inline void eat_character(const uint8_t ch, ImapPafData *pfdata, ImapPafState base_state, ImapPafState next_state) { switch(ch) { case ' ': case '\t': pfdata->imap_state = next_state; break;; case '\r': case '\n': pfdata->imap_state = base_state; break; } } /* * defined above in the eat_character function * * Keeping the next two functions to ease any future development * where these cases will no longer be simple or identical */ static inline void eat_second_argument(const uint8_t ch, ImapPafData *pfdata) { eat_character(ch, pfdata, IMAP_PAF_REG_STATE, IMAP_PAF_CMD_SEARCH); } /* explanation in 'eat_second_argument' above */ static inline void eat_response_identifier(const uint8_t ch, ImapPafData *pfdata) { eat_character(ch, pfdata, IMAP_PAF_REG_STATE, IMAP_PAF_CMD_STATUS); } /* * Analyzes the current data for a correct flush point. Flushes when * a command is complete or a MIME boundary is found. * * PARAMS: * ImapPafData *pfdata - ImapPaf state tracking structure * const uint8_t *data - payload data to inspect * uint32_t len - length of payload data * uint32_t * fp- pointer to set flush point * * RETURNS: * PAF_Status - PAF_FLUSH if flush point found, * PAF_SEARCH otherwise */ static PAF_Status imap_paf_server(ImapPafData *pfdata, const uint8_t* data, uint32_t len, uint32_t* fp) { uint32_t i; uint32_t flush_len = 0; uint32_t boundary_start = 0; pfdata->end_of_data = false; for (i = 0; i < len; i++) { uint8_t ch = data[i]; switch(pfdata->imap_state) { case IMAP_PAF_CMD_IDENTIFIER: // can be '+', '*', or a tag if (is_untagged(ch)) { // continue checking for fetch command pfdata->imap_state = IMAP_PAF_CMD_TAG; } else { // end of a command. flush at end of line. pfdata->imap_state = IMAP_PAF_FLUSH_STATE; } break; case IMAP_PAF_CMD_TAG: eat_response_identifier(ch, pfdata); break; case IMAP_PAF_CMD_STATUS: // can be a command name, msg sequence number, msg count, etc... // since we are only interested in fetch, eat this argument eat_second_argument(ch, pfdata); break; case IMAP_PAF_CMD_SEARCH: process_command(ch, pfdata); find_data_end_single_line(ch, pfdata); break; case IMAP_PAF_REG_STATE: find_data_end_single_line(ch, pfdata); // data reset when end of line hit break; case IMAP_PAF_DATA_HEAD_STATE: parse_fetch_header(ch, pfdata); // function will change state break; case IMAP_PAF_DATA_LEN_STATE: if (parse_literal_length(ch, &(pfdata->imap_data_info.length))) { pfdata->imap_state = IMAP_PAF_DATA_HEAD_STATE; } break; case IMAP_PAF_DATA_STATE: if (find_data_end_mime_data(ch, pfdata)) { // if not a boundary, wait for end of // the server's response before flushing if (pfdata->imap_state == IMAP_PAF_DATA_STATE) { *fp = i + 1; return PAF_FLUSH; } } if (pfdata->mime_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN) boundary_start = i; break; case IMAP_PAF_FLUSH_STATE: if (find_data_end_single_line(ch, pfdata)) { flush_len = i +1; } break; } } if (flush_len) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: flushing data!\n");); // flush at the final termination sequence *fp = flush_len; return PAF_FLUSH; } if ( scanning_boundary(&pfdata->mime_info, boundary_start, fp) ) return PAF_LIMIT; return PAF_SEARCH; } /* * Searches through the current data for a LF. All client * commands end with this termination sequence * * PARAMS: * ImapPafData *pfdata - ImapPaf state tracking structure * const uint8_t *data - payload data to inspect * uint32_t len - length of payload data * uint32_t * fp- pointer to set flush point * * RETURNS: * PAF_Status - PAF_FLUSH if flush point found, * PAF_SEARCH otherwise */ static PAF_Status imap_paf_client(ImapPafData *pfdata, const uint8_t* data, uint32_t len, uint32_t* fp) { const char *pch; pch = memchr (data, '\n', len); if (pch != NULL) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: Flushing client" " data!\n");); *fp = (uint32_t)(pch - (const char*)data) + 1; return PAF_FLUSH; } return PAF_SEARCH; } /* Function: imap_paf() * * Purpose: IMAP PAF callback. * Inspects imap traffic. All client traffic will be flushed after * every single line termination sequence (LF == '\n'). When * analyzing server traffic, will flush after the final termination * sequence in the current PDU. However, if the server is returning * an email, a flush will occur after every MIME boundary and at the * end of the email. * * Arguments: * void *ssn - stream5 session pointer * void **ps - ImapPaf state tracking structure * const uint8_t *data - payload data to inspect * uint32_t len - length of payload data * uint32_t flags- flags to check whether client or server * uint32_t * fp- pointer to set flush point * uint32_t * fp_eoh - pointer to set header flush point * * Returns: * PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise */ static PAF_Status imap_paf(void* ssn, void** ps, const uint8_t* data, uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh) { ImapPafData *pfdata = *(ImapPafData **)ps; if (pfdata == NULL) { /* IMAP has long running sessions, avoid the paf check*/ //if (_dpd.fileAPI->check_paf_abort(ssn)) // return PAF_ABORT; pfdata = _dpd.snortAlloc(1, sizeof(*pfdata), PP_IMAP, PP_MEM_CATEGORY_SESSION); if (pfdata == NULL) { return PAF_ABORT; } reset_data_states(pfdata); *ps = pfdata; } if (*flags & FLAG_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: From server.\n");); return imap_paf_server(pfdata, data, len, fp); } else { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP PAF: From client.\n");); return imap_paf_client(pfdata, data, len, fp); } } bool is_data_end (void* ssn) { if ( ssn ) { ImapPafData** s = (ImapPafData **)_dpd.streamAPI->get_paf_user_data(ssn, 1, imap_paf_id); if ( s && (*s) ) return ((*s)->end_of_data); } return false; } void imap_paf_cleanup(void *pafData) { if(pafData) { _dpd.snortFree(pafData, sizeof(ImapPafData), PP_IMAP, PP_MEM_CATEGORY_SESSION); } } #ifdef TARGET_BASED void register_imap_paf_service (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { imap_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, imap_paf, true); imap_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false,imap_paf, true); _dpd.streamAPI->register_paf_free(imap_paf_id, imap_paf_cleanup); } } #endif void register_imap_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { imap_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, true, imap_paf, true); imap_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, false, imap_paf, true); _dpd.streamAPI->register_paf_free(imap_paf_id, imap_paf_cleanup); } } snort-2.9.20/src/dynamic-preprocessors/imap/Makefile.in0000644000175000017500000006265314242725546021235 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@imap_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@imap_buffer_dump.h subdir = src/dynamic-preprocessors/imap ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_imap_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_imap_preproc_la_SOURCES_DIST = imap_config.c imap_config.h \ imap_log.c imap_log.h imap_util.c imap_util.h imap_paf.c \ imap_paf.h snort_imap.c snort_imap.h spp_imap.c spp_imap.h \ imap_buffer_dump.c imap_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = imap_buffer_dump.lo am_libsf_imap_preproc_la_OBJECTS = imap_config.lo imap_log.lo \ imap_util.lo imap_paf.lo snort_imap.lo spp_imap.lo \ $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_imap_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo mempool.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_sdlist.lo sf_base64decode.lo \ @SO_WITH_STATIC_LIB_FALSE@ util_unfold.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_email_attach_decode.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo ssl.lo \ @SO_WITH_STATIC_LIB_FALSE@ ssl_config.lo ssl_inspect.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfparser.lo libsf_imap_preproc_la_OBJECTS = $(am_libsf_imap_preproc_la_OBJECTS) \ $(nodist_libsf_imap_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_imap_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_imap_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_imap_preproc_la_SOURCES) \ $(nodist_libsf_imap_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_imap_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_imap_preproc.la libsf_imap_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_imap_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_imap_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/mempool.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_sdlist.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_base64decode.c \ @SO_WITH_STATIC_LIB_FALSE@../include/util_unfold.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_email_attach_decode.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_config.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_inspect.c \ @SO_WITH_STATIC_LIB_FALSE@../libs/sfparser.c libsf_imap_preproc_la_SOURCES = imap_config.c imap_config.h imap_log.c \ imap_log.h imap_util.c imap_util.h imap_paf.c imap_paf.h \ snort_imap.c snort_imap.h spp_imap.c spp_imap.h \ $(am__append_1) EXTRA_DIST = \ sf_imap.vcxproj \ sf_imap.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/imap/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/imap/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_imap_preproc.la: $(libsf_imap_preproc_la_OBJECTS) $(libsf_imap_preproc_la_DEPENDENCIES) $(EXTRA_libsf_imap_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_imap_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_imap_preproc_la_OBJECTS) $(libsf_imap_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c mempool.lo: ../include/mempool.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mempool.lo `test -f '../include/mempool.c' || echo '$(srcdir)/'`../include/mempool.c sf_sdlist.lo: ../include/sf_sdlist.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_sdlist.lo `test -f '../include/sf_sdlist.c' || echo '$(srcdir)/'`../include/sf_sdlist.c sf_base64decode.lo: ../include/sf_base64decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_base64decode.lo `test -f '../include/sf_base64decode.c' || echo '$(srcdir)/'`../include/sf_base64decode.c util_unfold.lo: ../include/util_unfold.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o util_unfold.lo `test -f '../include/util_unfold.c' || echo '$(srcdir)/'`../include/util_unfold.c sf_email_attach_decode.lo: ../include/sf_email_attach_decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_email_attach_decode.lo `test -f '../include/sf_email_attach_decode.c' || echo '$(srcdir)/'`../include/sf_email_attach_decode.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c ssl.lo: ../ssl_common/ssl.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl.lo `test -f '../ssl_common/ssl.c' || echo '$(srcdir)/'`../ssl_common/ssl.c ssl_config.lo: ../ssl_common/ssl_config.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_config.lo `test -f '../ssl_common/ssl_config.c' || echo '$(srcdir)/'`../ssl_common/ssl_config.c ssl_inspect.lo: ../ssl_common/ssl_inspect.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_inspect.lo `test -f '../ssl_common/ssl_inspect.c' || echo '$(srcdir)/'`../ssl_common/ssl_inspect.c sfparser.lo: ../libs/sfparser.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfparser.lo `test -f '../libs/sfparser.c' || echo '$(srcdir)/'`../libs/sfparser.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/imap/imap_config.h0000644000175000017500000000713314241076033021571 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*************************************************************************** * * imap_config.h * * Author: Bhagyashree Bantwal * ***************************************************************************/ #ifndef __IMAP_CONFIG_H__ #define __IMAP_CONFIG_H__ #include "sfPolicyUserData.h" #include "file_mail_common.h" #include "sf_email_attach_decode.h" #define CONF_SEPARATORS " \t\n\r" #define CONF_PORTS "ports" #define CONF_IMAP_MEMCAP "memcap" #define CONF_MAX_MIME_MEM "max_mime_mem" #define CONF_B64_DECODE "b64_decode_depth" #define CONF_QP_DECODE "qp_decode_depth" #define CONF_BITENC_DECODE "bitenc_decode_depth" #define CONF_UU_DECODE "uu_decode_depth" #define CONF_DISABLED "disabled" #define CONF_START_LIST "{" #define CONF_END_LIST "}" /*These are temporary values*/ #define DEFAULT_MAX_MIME_MEM 838860 #define DEFAULT_IMAP_MEMCAP 838860 #define MAX_IMAP_MEMCAP 104857600 #define MIN_IMAP_MEMCAP 3276 #define MAX_MIME_MEM 104857600 #define MIN_MIME_MEM 3276 #define MAX_DEPTH 65535 #define MIN_DEPTH -1 #define IMAP_DEFAULT_SERVER_PORT 143 /* IMAP normally runs on port 143 */ #define ERRSTRLEN 512 typedef struct _IMAPSearch { char *name; int name_len; } IMAPSearch; typedef struct _IMAPToken { char *name; int name_len; int search_id; } IMAPToken; typedef struct _IMAPCmdConfig { char alert; /* 1 if alert when seen */ char normalize; /* 1 if we should normalize this command */ int max_line_len; /* Max length of this particular command */ } IMAPCmdConfig; typedef struct _IMAPConfig { uint8_t ports[8192]; uint32_t memcap; IMAPToken *cmds; IMAPSearch *cmd_search; void *cmd_search_mpse; int num_cmds; int disabled; MAIL_LogConfig log_config; DecodeConfig decode_conf; int ref_count; } IMAPConfig; typedef struct _IMAP_Stats { uint64_t sessions; uint64_t conc_sessions; uint64_t max_conc_sessions; uint64_t log_memcap_exceeded; uint64_t cur_sessions; MimeStats mime_stats; } IMAP_Stats; extern IMAP_Stats imap_stats; /* Function prototypes */ void IMAP_ParseArgs(IMAPConfig *, char *); void IMAP_PrintConfig(IMAPConfig *config); void IMAP_CheckConfig(IMAPConfig *, tSfPolicyUserContextId); #endif snort-2.9.20/src/dynamic-preprocessors/imap/sf_imap.dsp0000444000175000017500000001540014230012554021261 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_imap" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_imap - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_imap.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_imap.mak" CFG="sf_imap - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_imap - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_imap - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_imap - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_IMAP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 pcre.lib ws2_32.lib ../libs/Release/sfdynamic_preproc_libs.lib /nologo /dll /machine:I386 /libpath:"../../../src/win32/WIN32-Libraries" !ELSEIF "$(CFG)" == "sf_imap - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_IMAP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 pcre.lib ws2_32.lib ../libs/Debug/sfdynamic_preproc_libs.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"../../../src/win32/WIN32-Libraries" !ENDIF # Begin Target # Name "sf_imap - Win32 Release" # Name "sf_imap - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\imap_config.c # End Source File # Begin Source File SOURCE=.\imap_log.c # End Source File # Begin Source File SOURCE=.\imap_paf.c # End Source File # Begin Source File SOURCE=.\imap_util.c # End Source File # Begin Source File SOURCE=..\include\mempool.c # End Source File # Begin Source File SOURCE=..\include\sf_base64decode.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_email_attach_decode.c # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.c # End Source File # Begin Source File SOURCE=..\libs\sfparser.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\snort_imap.c # End Source File # Begin Source File SOURCE=.\spp_imap.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_ha.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_inspect.c # End Source File # Begin Source File SOURCE=..\include\util_unfold.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\ssl_common\ssl.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_include.h # End Source File # Begin Source File SOURCE=.\imap_config.h # End Source File # Begin Source File SOURCE=.\imap_log.h # End Source File # Begin Source File SOURCE=.\imap_util.h # End Source File # Begin Source File SOURCE=..\include\mempool.h # End Source File # Begin Source File SOURCE=..\include\sf_email_attach_decode.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.h # End Source File # Begin Source File SOURCE=.\snort_imap.h # End Source File # Begin Source File SOURCE=.\spp_imap.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/imap/imap_log.c0000644000175000017500000000645214241076034021104 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * imap_log.c * * Author: Bhagyashree Bantwal * * Description: * * This file handles IMAP alerts. * * Entry point functions: * * IMAP_GenerateAlert() * * **************************************************************************/ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "imap_config.h" #include "imap_log.h" #include "snort_imap.h" #include "sf_dynamic_preprocessor.h" extern IMAPConfig *imap_eval_config; extern IMAP *imap_ssn; char imap_event[IMAP_EVENT_MAX][EVENT_STR_LEN]; void IMAP_GenerateAlert(int event, char *format, ...) { va_list ap; /* Only log a specific alert once per session */ if (imap_ssn->alert_mask & (1 << event)) { #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Already alerted on: %s - " "ignoring event.\n", imap_event[event]);); #endif return; } /* set bit for this alert so we don't alert on again * in this session */ imap_ssn->alert_mask |= (1 << event); va_start(ap, format); imap_event[event][0] = '\0'; vsnprintf(&imap_event[event][0], EVENT_STR_LEN - 1, format, ap); imap_event[event][EVENT_STR_LEN - 1] = '\0'; _dpd.alertAdd(GENERATOR_SPP_IMAP, event, 1, 0, 3, &imap_event[event][0], 0); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP Alert generated: %s\n", imap_event[event]);); va_end(ap); } void IMAP_DecodeAlert(void *ds) { Email_DecodeState *decode_state = (Email_DecodeState *)ds; switch( decode_state->decode_type ) { case DECODE_B64: if (imap_eval_config->decode_conf.b64_depth > -1) IMAP_GenerateAlert(IMAP_B64_DECODING_FAILED, "%s", IMAP_B64_DECODING_FAILED_STR); break; case DECODE_QP: if (imap_eval_config->decode_conf.qp_depth > -1) IMAP_GenerateAlert(IMAP_QP_DECODING_FAILED, "%s", IMAP_QP_DECODING_FAILED_STR); break; case DECODE_UU: if (imap_eval_config->decode_conf.uu_depth > -1) IMAP_GenerateAlert(IMAP_UU_DECODING_FAILED, "%s", IMAP_UU_DECODING_FAILED_STR); break; default: break; } } snort-2.9.20/src/dynamic-preprocessors/imap/spp_imap.h0000644000175000017500000000222614241076046021130 0ustar apoapo /* * spp_imap.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Bhagyashree Bantwal * * Description: * * This file defines the publicly available functions for the IMAP * functionality for Snort. * */ #ifndef __SPP_IMAP_H__ #define __SPP_IMAP_H__ void SetupIMAP(void); #endif snort-2.9.20/src/dynamic-preprocessors/imap/snort_imap.h0000644000175000017500000001422214241076044021470 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * **************************************************************************/ /************************************************************************** * * snort_imap.h * * Author: Bhagyashree Bantwal * * Description: * * This file defines everything specific to the IMAP preprocessor. * **************************************************************************/ #ifndef __IMAP_H__ #define __IMAP_H__ /* Includes ***************************************************************/ #include #include "sf_snort_packet.h" #include "imap_config.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "mempool.h" #include "sf_email_attach_decode.h" #include "file_mail_common.h" #include "file_api.h" #ifdef DEBUG #include "sf_types.h" #endif /**************************************************************************/ /* Defines ****************************************************************/ /* Direction packet is coming from, if we can figure it out */ #define IMAP_PKT_FROM_UNKNOWN 0 #define IMAP_PKT_FROM_CLIENT 1 #define IMAP_PKT_FROM_SERVER 2 #define SEARCH_CMD 0 #define SEARCH_RESP 1 #define SEARCH_HDR 2 #define SEARCH_DATA_END 3 #define NUM_SEARCHES 4 #define BOUNDARY 0 #define STATE_DATA 0 /* Data state */ #define STATE_TLS_CLIENT_PEND 1 /* Got STARTTLS */ #define STATE_TLS_SERVER_PEND 2 /* Got STARTTLS */ #define STATE_TLS_DATA 3 /* Successful handshake, TLS encrypted data */ #define STATE_COMMAND 4 #define STATE_UNKNOWN 5 #define STATE_DATA_INIT 0 #define STATE_DATA_HEADER 1 /* Data header section of data state */ #define STATE_DATA_BODY 2 /* Data body section of data state */ #define STATE_MIME_HEADER 3 /* MIME header section within data section */ #define STATE_DATA_UNKNOWN 4 /* session flags */ #define IMAP_FLAG_NEXT_STATE_UNKNOWN 0x00000004 #define IMAP_FLAG_GOT_NON_REBUILT 0x00000008 #define IMAP_FLAG_CHECK_SSL 0x00000010 #define IMAP_SSL_ERROR_FLAGS (SSL_BOGUS_HS_DIR_FLAG | \ SSL_BAD_VER_FLAG | \ SSL_BAD_TYPE_FLAG | \ SSL_UNKNOWN_FLAG) /* Maximum length of header chars before colon, based on Exim 4.32 exploit */ #define MAX_HEADER_NAME_LEN 64 #define IMAP_PROTO_REF_STR "imap" /**************************************************************************/ /* Data structures ********************************************************/ typedef enum _IMAPCmdEnum { CMD_APPEND = 0, CMD_AUTHENTICATE, CMD_CAPABILITY, CMD_CHECK, CMD_CLOSE, CMD_COMPARATOR, CMD_COMPRESS, CMD_CONVERSIONS, CMD_COPY, CMD_CREATE, CMD_DELETE, CMD_DELETEACL, CMD_DONE, CMD_EXAMINE, CMD_EXPUNGE, CMD_FETCH, CMD_GETACL, CMD_GETMETADATA, CMD_GETQUOTA, CMD_GETQUOTAROOT, CMD_IDLE, CMD_LIST, CMD_LISTRIGHTS, CMD_LOGIN, CMD_LOGOUT, CMD_LSUB, CMD_MYRIGHTS, CMD_NOOP, CMD_NOTIFY, CMD_RENAME, CMD_SEARCH, CMD_SELECT, CMD_SETACL, CMD_SETMETADATA, CMD_SETQUOTA, CMD_SORT, CMD_STARTTLS, CMD_STATUS, CMD_STORE, CMD_SUBSCRIBE, CMD_THREAD, CMD_UID, CMD_UNSELECT, CMD_UNSUBSCRIBE, CMD_X, CMD_LAST } IMAPCmdEnum; typedef enum _IMAPRespEnum { RESP_CAPABILITY = 0, RESP_LIST, RESP_LSUB, RESP_STATUS, RESP_SEARCH, RESP_FLAGS, RESP_EXISTS, RESP_RECENT, RESP_EXPUNGE, RESP_FETCH, RESP_BAD, RESP_BYE, RESP_NO, RESP_OK, RESP_PREAUTH, RESP_ENVELOPE, RESP_UID, RESP_LAST } IMAPRespEnum; typedef enum _IMAPHdrEnum { HDR_CONTENT_TYPE = 0, HDR_CONT_TRANS_ENC, HDR_CONT_DISP, HDR_LAST } IMAPHdrEnum; typedef enum _IMAPDataEndEnum { DATA_END_1 = 0, DATA_END_2, DATA_END_3, DATA_END_4, DATA_END_LAST } IMAPDataEndEnum; typedef struct _IMAPSearchInfo { int id; int index; int length; } IMAPSearchInfo; typedef struct _IMAPMimeBoundary { int state; char boundary[2 + MAX_BOUNDARY_LEN + 1]; /* '--' + MIME boundary string + '\0' */ int boundary_len; void *boundary_search; } IMAPMimeBoundary; typedef struct _IMAPPcre { pcre *re; pcre_extra *pe; } IMAPPcre; typedef struct _IMAP { int state; int state_flags; int session_flags; int alert_mask; int reassembling; uint32_t body_len; uint32_t body_read; #ifdef DEBUG_MSGS uint64_t session_number; #endif tSfPolicyId policy_id; MimeState mime_ssn; tSfPolicyUserContextId config; uint32_t flow_id; } IMAP; /**************************************************************************/ /* Function prototypes ****************************************************/ void IMAP_InitCmds(IMAPConfig *config); void IMAP_SearchInit(void); void IMAP_Free(void); void SnortIMAP(SFSnortPacket *); int IMAP_IsServer(uint16_t); void IMAP_FreeConfig(IMAPConfig *); void IMAP_FreeConfigs(tSfPolicyUserContextId); int IMAP_GetFilename(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); /**************************************************************************/ #endif /* __IMAP_H__ */ snort-2.9.20/src/dynamic-preprocessors/imap/snort_imap.c0000644000175000017500000011040014241076043021455 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * snort_imap.c * * Author: Bhagyashree Bantwal * * Description: * * This file handles IMAP protocol checking and normalization. * * Entry point functions: * * SnortIMAP() * IMAP_Init() * IMAP_Free() * **************************************************************************/ /* Includes ***************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sf_types.h" #include "snort_imap.h" #include "imap_config.h" #include "imap_util.h" #include "imap_log.h" #include "sf_snort_packet.h" #include "stream_api.h" #include "snort_debug.h" #include "profiler.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" #include "ssl.h" #include "ssl_include.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "file_api.h" #ifdef DEBUG_MSGS #include "sf_types.h" #endif #ifdef DUMP_BUFFER #include "imap_buffer_dump.h" #endif #include "imap_paf.h" /**************************************************************************/ /* Externs ****************************************************************/ #ifdef PERF_PROFILING extern PreprocStats imapDetectPerfStats; extern int imapDetectCalled; #endif extern tSfPolicyUserContextId imap_config; extern IMAPConfig *imap_eval_config; extern MemPool *imap_mempool; extern MemPool *imap_mime_mempool; #ifdef DEBUG_MSGS extern char imap_print_buffer[]; #endif /**************************************************************************/ /* Globals ****************************************************************/ const IMAPToken imap_known_cmds[] = { {"APPEND", 6, CMD_APPEND}, {"AUTHENTICATE", 12, CMD_AUTHENTICATE}, {"CAPABILITY", 10, CMD_CAPABILITY}, {"CHECK", 5, CMD_CHECK}, {"CLOSE", 5, CMD_CLOSE}, {"COMPARATOR", 10, CMD_COMPARATOR}, {"COMPRESS", 8, CMD_COMPRESS}, {"CONVERSIONS", 11, CMD_CONVERSIONS}, {"COPY", 4, CMD_COPY}, {"CREATE", 6, CMD_CREATE}, {"DELETE", 6, CMD_DELETE}, {"DELETEACL", 9, CMD_DELETEACL}, {"DONE", 4, CMD_DONE}, {"EXAMINE", 7, CMD_EXAMINE}, {"EXPUNGE", 7, CMD_EXPUNGE}, {"FETCH", 5, CMD_FETCH}, {"GETACL", 6, CMD_GETACL}, {"GETMETADATA", 11, CMD_GETMETADATA}, {"GETQUOTA", 8, CMD_GETQUOTA}, {"GETQUOTAROOT", 12, CMD_GETQUOTAROOT}, {"IDLE", 4, CMD_IDLE}, {"LIST", 4, CMD_LIST}, {"LISTRIGHTS", 10, CMD_LISTRIGHTS}, {"LOGIN", 5, CMD_LOGIN}, {"LOGOUT", 6, CMD_LOGOUT}, {"LSUB", 4, CMD_LSUB}, {"MYRIGHTS", 8, CMD_MYRIGHTS}, {"NOOP", 4, CMD_NOOP}, {"NOTIFY", 6, CMD_NOTIFY}, {"RENAME", 6, CMD_RENAME}, {"SEARCH", 6, CMD_SEARCH}, {"SELECT", 6, CMD_SELECT}, {"SETACL", 6, CMD_SETACL}, {"SETMETADATA", 11, CMD_SETMETADATA}, {"SETQUOTA", 8, CMD_SETQUOTA}, {"SORT", 4, CMD_SORT}, {"STARTTLS", 8, CMD_STARTTLS}, {"STATUS", 6, CMD_STATUS}, {"STORE", 5, CMD_STORE}, {"SUBSCRIBE", 9, CMD_SUBSCRIBE}, {"THREAD", 6, CMD_THREAD}, {"UID", 3, CMD_UID}, {"UNSELECT", 8, CMD_UNSELECT}, {"UNSUBSCRIBE", 11, CMD_UNSUBSCRIBE}, {"X", 1, CMD_X}, {NULL, 0, 0} }; const IMAPToken imap_resps[] = { {"CAPABILITY", 10, RESP_CAPABILITY}, {"LIST", 4, RESP_LIST}, {"LSUB", 4, RESP_LSUB}, {"STATUS", 6, RESP_STATUS}, {"SEARCH", 6, RESP_SEARCH}, {"FLAGS", 5, RESP_FLAGS}, {"EXISTS", 6, RESP_EXISTS}, {"RECENT", 6, RESP_RECENT}, {"EXPUNGE", 7, RESP_EXPUNGE}, {"FETCH", 5, RESP_FETCH}, {"BAD", 3, RESP_BAD}, {"BYE", 3, RESP_BYE}, {"NO", 2, RESP_NO}, {"OK", 2, RESP_OK}, {"PREAUTH", 7, RESP_PREAUTH}, {"ENVELOPE", 8, RESP_ENVELOPE}, {"UID", 3, RESP_UID}, {NULL, 0, 0} }; IMAP *imap_ssn = NULL; IMAPSearchInfo imap_search_info; #ifdef DEBUG_MSGS uint64_t imap_session_counter = 0; #endif #ifdef TARGET_BASED int16_t imap_proto_id; #endif void *imap_resp_search_mpse = NULL; IMAPSearch imap_resp_search[RESP_LAST]; IMAPSearch *imap_current_search = NULL; /**************************************************************************/ /* Private functions ******************************************************/ static int IMAP_Setup(SFSnortPacket *p, IMAP *ssn); static void IMAP_ResetState(void); static void IMAP_SessionFree(void *); static int IMAP_GetPacketDirection(SFSnortPacket *, int); static void IMAP_ProcessClientPacket(SFSnortPacket *); static void IMAP_ProcessServerPacket(SFSnortPacket *); static void IMAP_DisableDetect(SFSnortPacket *); static const uint8_t * IMAP_HandleCommand(SFSnortPacket *, const uint8_t *, const uint8_t *); static int IMAP_SearchStrFound(void *, void *, int, void *, void *); static int IMAP_Inspect(SFSnortPacket *); void IMAP_Set_flow_id( void *app_data, uint32_t fid ); MimeMethods mime_methods = {NULL, NULL, IMAP_DecodeAlert, IMAP_ResetState, is_data_end}; /**************************************************************************/ void IMAP_InitCmds(IMAPConfig *config) { const IMAPToken *tmp; if (config == NULL) return; /* add one to CMD_LAST for NULL entry */ config->cmds = (IMAPToken *)_dpd.snortAlloc(CMD_LAST + 1, sizeof(IMAPToken), PP_IMAP, PP_MEM_CATEGORY_CONFIG); if (config->cmds == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for imap " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } for (tmp = &imap_known_cmds[0]; tmp->name != NULL; tmp++) { config->cmds[tmp->search_id].name_len = tmp->name_len; config->cmds[tmp->search_id].search_id = tmp->search_id; config->cmds[tmp->search_id].name = strdup(tmp->name); if (config->cmds[tmp->search_id].name == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for imap " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } } /* initialize memory for command searches */ config->cmd_search = (IMAPSearch *)_dpd.snortAlloc(CMD_LAST, sizeof(IMAPSearch), PP_IMAP, PP_MEM_CATEGORY_CONFIG); if (config->cmd_search == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for imap " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } config->num_cmds = CMD_LAST; } /* * Initialize IMAP searches * * @param none * * @return none */ void IMAP_SearchInit(void) { const IMAPToken *tmp; /* Response search */ imap_resp_search_mpse = _dpd.searchAPI->search_instance_new(); if (imap_resp_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate IMAP " "response search.\n"); } for (tmp = &imap_resps[0]; tmp->name != NULL; tmp++) { imap_resp_search[tmp->search_id].name = tmp->name; imap_resp_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(imap_resp_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(imap_resp_search_mpse); } /* * Reset IMAP session state * * @param none * * @return none */ static void IMAP_ResetState(void) { imap_ssn->state = STATE_COMMAND; imap_ssn->state_flags = 0; imap_ssn->body_read = imap_ssn->body_len = 0; } /* * Given a server configuration and a port number, we decide if the port is * in the IMAP server port list. * * @param port the port number to compare with the configuration * * @return integer * @retval 0 means that the port is not a server port * @retval !0 means that the port is a server port */ int IMAP_IsServer(uint16_t port) { if( isPortEnabled( imap_eval_config->ports, port ) ) return 1; return 0; } static IMAP * IMAP_GetNewSession(SFSnortPacket *p, tSfPolicyId policy_id) { IMAP *ssn; IMAPConfig *pPolicyConfig = NULL; int ret = 0; pPolicyConfig = (IMAPConfig *)sfPolicyUserDataGetCurrent(imap_config); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Creating new session data structure\n");); ssn = (IMAP *)_dpd.snortAlloc(1, sizeof(IMAP), PP_IMAP, PP_MEM_CATEGORY_SESSION); if (ssn == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate IMAP session data\n"); } imap_ssn = ssn; ssn->session_flags |= IMAP_FLAG_CHECK_SSL; imap_ssn->mime_ssn.log_config = &(imap_eval_config->log_config); imap_ssn->mime_ssn.decode_conf = &(imap_eval_config->decode_conf); imap_ssn->mime_ssn.mime_mempool = imap_mime_mempool; imap_ssn->mime_ssn.log_mempool = imap_mempool; imap_ssn->mime_ssn.mime_stats = &(imap_stats.mime_stats); imap_ssn->mime_ssn.methods = &(mime_methods); if (( ret = _dpd.fileAPI->set_log_buffers(&(imap_ssn->mime_ssn.log_state), &(pPolicyConfig->log_config),imap_mempool, p->stream_session, PP_IMAP)) < 0) { if( ret == -1 ) { if(imap_stats.log_memcap_exceeded % 10000 == 0) { _dpd.logMsg("WARNING: IMAP memcap exceeded.\n"); } imap_stats.log_memcap_exceeded++; } _dpd.snortFree(ssn, sizeof(*ssn), PP_IMAP, PP_MEM_CATEGORY_SESSION); return NULL; } _dpd.sessionAPI->set_application_data(p->stream_session, PP_IMAP, ssn, &IMAP_SessionFree); if (p->flags & SSNFLAG_MIDSTREAM) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Got midstream packet - " "setting state to unknown\n");); ssn->state = STATE_UNKNOWN; } #ifdef DEBUG_MSGS imap_session_counter++; ssn->session_number = imap_session_counter; #endif if (p->stream_session != NULL) { /* check to see if we're doing client reassembly in stream */ if (_dpd.streamAPI->get_reassembly_direction(p->stream_session) & SSN_DIR_TO_CLIENT) ssn->reassembling = 1; if(!ssn->reassembling) { _dpd.streamAPI->set_reassembly(p->stream_session, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_TO_CLIENT, STREAM_FLPOLICY_SET_ABSOLUTE); ssn->reassembling = 1; } } ssn->body_read = ssn->body_len = 0; ssn->policy_id = policy_id; ssn->config = imap_config; ssn->flow_id = 0; pPolicyConfig->ref_count++; imap_stats.sessions++; imap_stats.conc_sessions++; imap_stats.cur_sessions++; if(imap_stats.max_conc_sessions < imap_stats.conc_sessions) imap_stats.max_conc_sessions = imap_stats.conc_sessions; return ssn; } /* * Do first-packet setup * * @param p standard Packet structure * * @return none */ static int IMAP_Setup(SFSnortPacket *p, IMAP *ssn) { int flags = 0; int pkt_dir; if (p->stream_session != NULL) { /* set flags to session flags */ flags = _dpd.sessionAPI->get_session_flags(p->stream_session); } /* Figure out direction of packet */ pkt_dir = IMAP_GetPacketDirection(p, flags); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Session number: "STDu64"\n", ssn->session_number);); if (!(ssn->session_flags & IMAP_FLAG_CHECK_SSL)) ssn->session_flags |= IMAP_FLAG_CHECK_SSL; /* Check to see if there is a reassembly gap. If so, we won't know * what state we're in when we get the _next_ reassembled packet */ if ((pkt_dir != IMAP_PKT_FROM_SERVER) && (p->flags & FLAG_REBUILT_STREAM)) { int missing_in_rebuilt = _dpd.streamAPI->missing_in_reassembled(p->stream_session, SSN_DIR_TO_CLIENT); if (ssn->session_flags & IMAP_FLAG_NEXT_STATE_UNKNOWN) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Found gap in previous reassembly buffer - " "set state to unknown\n");); ssn->state = STATE_UNKNOWN; ssn->session_flags &= ~IMAP_FLAG_NEXT_STATE_UNKNOWN; } if (missing_in_rebuilt == SSN_MISSING_BEFORE) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Found missing packets before " "in reassembly buffer - set state to unknown\n");); ssn->state = STATE_UNKNOWN; } } return pkt_dir; } /* * Determine packet direction * * @param p standard Packet structure * * @return none */ static int IMAP_GetPacketDirection(SFSnortPacket *p, int flags) { int pkt_direction = IMAP_PKT_FROM_UNKNOWN; if (flags & SSNFLAG_MIDSTREAM) { if (IMAP_IsServer(p->src_port) && !IMAP_IsServer(p->dst_port)) { pkt_direction = IMAP_PKT_FROM_SERVER; } else if (!IMAP_IsServer(p->src_port) && IMAP_IsServer(p->dst_port)) { pkt_direction = IMAP_PKT_FROM_CLIENT; } } else { if (p->flags & FLAG_FROM_SERVER) { pkt_direction = IMAP_PKT_FROM_SERVER; } else if (p->flags & FLAG_FROM_CLIENT) { pkt_direction = IMAP_PKT_FROM_CLIENT; } /* if direction is still unknown ... */ if (pkt_direction == IMAP_PKT_FROM_UNKNOWN) { if (IMAP_IsServer(p->src_port) && !IMAP_IsServer(p->dst_port)) { pkt_direction = IMAP_PKT_FROM_SERVER; } else if (!IMAP_IsServer(p->src_port) && IMAP_IsServer(p->dst_port)) { pkt_direction = IMAP_PKT_FROM_CLIENT; } } } return pkt_direction; } /* * Free IMAP-specific related to this session * * @param v pointer to IMAP session structure * * * @return none */ static void IMAP_SessionFree(void *session_data) { IMAP *imap = (IMAP *)session_data; #ifdef SNORT_RELOAD IMAPConfig *pPolicyConfig = NULL; #endif ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if (imap == NULL) return; #ifdef SNORT_RELOAD pPolicyConfig = (IMAPConfig *)sfPolicyUserDataGet(imap->config, imap->policy_id); if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (imap->config != imap_config)) { sfPolicyUserDataClear (imap->config, imap->policy_id); IMAP_FreeConfig(pPolicyConfig); /* No more outstanding policies for this config */ if (sfPolicyUserPolicyGetActive(imap->config) == 0) IMAP_FreeConfigs(imap->config); } } #endif if(imap->mime_ssn.decode_state != NULL) { mempool_free(imap_mime_mempool, imap->mime_ssn.decode_bkt); _dpd.snortFree(imap->mime_ssn.decode_state, sizeof(Email_DecodeState), PP_IMAP, PP_MEM_CATEGORY_SESSION); } if(imap->mime_ssn.log_state != NULL) { mempool_free(imap_mempool, imap->mime_ssn.log_state->log_hdrs_bkt); _dpd.snortFree(imap->mime_ssn.log_state, sizeof(MAIL_LogState), PP_IMAP, PP_MEM_CATEGORY_SESSION); } if ( ssl_cb ) ssl_cb->session_free(imap->flow_id); _dpd.snortFree(imap, sizeof(*imap), PP_IMAP, PP_MEM_CATEGORY_SESSION); if(imap_stats.conc_sessions) imap_stats.conc_sessions--; if(imap_stats.cur_sessions) imap_stats.cur_sessions--; } static int IMAP_FreeConfigsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { IMAPConfig *pPolicyConfig = (IMAPConfig *)pData; //do any housekeeping before freeing IMAPConfig sfPolicyUserDataClear (config, policyId); IMAP_FreeConfig(pPolicyConfig); return 0; } void IMAP_FreeConfigs(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, IMAP_FreeConfigsPolicy); sfPolicyConfigDelete(config); } void IMAP_FreeConfig(IMAPConfig *config) { if (config == NULL) return; if (config->cmds != NULL) { IMAPToken *tmp = config->cmds; for (; tmp->name != NULL; tmp++) _dpd.snortFree(tmp->name, sizeof(*(tmp->name)), PP_IMAP, PP_MEM_CATEGORY_CONFIG); _dpd.snortFree(config->cmds, sizeof(*(config->cmds)), PP_IMAP, PP_MEM_CATEGORY_CONFIG); } if (config->cmd_search_mpse != NULL) _dpd.searchAPI->search_instance_free(config->cmd_search_mpse); if (config->cmd_search != NULL) _dpd.snortFree(config->cmd_search, sizeof(*(config->cmd_search)), PP_IMAP, PP_MEM_CATEGORY_CONFIG); _dpd.snortFree(config, sizeof(*config), PP_IMAP, PP_MEM_CATEGORY_CONFIG); } /* * Free anything that needs it before shutting down preprocessor * * @param none * * @return none */ void IMAP_Free(void) { IMAP_FreeConfigs(imap_config); imap_config = NULL; if (imap_resp_search_mpse != NULL) _dpd.searchAPI->search_instance_free(imap_resp_search_mpse); } /* * Callback function for string search * * @param id id in array of search strings from imap_config.cmds * @param index index in array of search strings from imap_config.cmds * @param data buffer passed in to search function * * @return response * @retval 1 commands caller to stop searching */ static int IMAP_SearchStrFound(void *id, void *unused, int index, void *data, void *unused2) { int search_id = (int)(uintptr_t)id; imap_search_info.id = search_id; imap_search_info.index = index; imap_search_info.length = imap_current_search[search_id].name_len; /* Returning non-zero stops search, which is okay since we only look for one at a time */ return 1; } /* * Handle COMMAND state * * @param p standard Packet structure * @param ptr pointer into p->payload buffer to start looking at data * @param end points to end of p->payload buffer * * @return pointer into p->payload where we stopped looking at data * will be end of line or end of packet */ static const uint8_t * IMAP_HandleCommand(SFSnortPacket *p, const uint8_t *ptr, const uint8_t *end) { const uint8_t *eol; /* end of line */ const uint8_t *eolm; /* end of line marker */ int cmd_found; /* get end of line and end of line marker */ IMAP_GetEOL(ptr, end, &eol, &eolm); /* TODO If the end of line marker coincides with the end of payload we can't be * sure that we got a command and not a substring which we could tell through * inspection of the next packet. Maybe a command pending state where the first * char in the next packet is checked for a space and end of line marker */ /* do not confine since there could be space chars before command */ imap_current_search = &imap_eval_config->cmd_search[0]; cmd_found = _dpd.searchAPI->search_instance_find (imap_eval_config->cmd_search_mpse, (const char *)ptr, eolm - ptr, 0, IMAP_SearchStrFound); /* if command not found, alert and move on */ if (!cmd_found) { if (imap_ssn->state == STATE_UNKNOWN) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Command not found, but state is " "unknown - checking for SSL\n");); /* check for encrypted */ if ((imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL) && (IsSSL(ptr, end - ptr, p->flags))) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Packet is SSL encrypted\n");); imap_ssn->state = STATE_TLS_DATA; /* Ignore data */ return end; } else { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Not SSL - try data state\n");); /* don't check for ssl again in this packet */ if (imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL) imap_ssn->session_flags &= ~IMAP_FLAG_CHECK_SSL; imap_ssn->state = STATE_DATA; //imap_ssn->data_state = STATE_DATA_UNKNOWN; return ptr; } } else { IMAP_GenerateAlert(IMAP_UNKNOWN_CMD, "%s", IMAP_UNKNOWN_CMD_STR); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "No known command found\n");); return eol; } } else { #ifdef DUMP_BUFFER dumpBuffer(IMAP_CLIENT_CMD_DUMP,ptr,eolm-ptr); #endif if (imap_ssn->state == STATE_UNKNOWN) imap_ssn->state = STATE_COMMAND; } /* At this point we have definitely found a legitimate command */ DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "%s\n", imap_eval_config->cmds[imap_search_info.id].name);); if(imap_search_info.id == CMD_STARTTLS) { if (eol == end) imap_ssn->state = STATE_TLS_CLIENT_PEND; } return eol; } /* * Process client packet * * @param packet standard Packet structure * * @return none */ static void IMAP_ProcessClientPacket(SFSnortPacket *p) { const uint8_t *ptr = p->payload; const uint8_t *end = p->payload + p->payload_size; #ifdef DUMP_BUFFER dumpBuffer(IMAP_CLIENT_DUMP,p->payload,p->payload_size); #endif ptr = IMAP_HandleCommand(p, ptr, end); } /* * Process server packet * * @param packet standard Packet structure * */ static void IMAP_ProcessServerPacket(SFSnortPacket *p) { int resp_found; const uint8_t *ptr; const uint8_t *end; const uint8_t *data_end; const uint8_t *eolm; const uint8_t *eol; int resp_line_len; const char *tmp = NULL; uint8_t *body_start, *body_end; char *eptr; uint32_t len = 0; body_start = body_end = NULL; #ifdef DUMP_BUFFER dumpBuffer(IMAP_SERVER_DUMP,p->payload,p->payload_size); #endif ptr = p->payload; end = p->payload + p->payload_size; while (ptr < end) { if(imap_ssn->state == STATE_DATA) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");); if( imap_ssn->body_len > imap_ssn->body_read) { len = imap_ssn->body_len - imap_ssn->body_read ; if( (uint32_t)(end - ptr) < len ) { data_end = end; len = data_end - ptr; } else data_end = ptr + len; #ifdef DUMP_BUFFER dumpBuffer(IMAP_SERVER_BODY_DATA_DUMP,ptr,len); #endif ptr = _dpd.fileAPI->process_mime_data(p, ptr, end, &(imap_ssn->mime_ssn), 0, true, "IMAP", PP_IMAP); if( ptr < data_end) len = len - (data_end - ptr); imap_ssn->body_read += len; continue; } else { imap_ssn->body_len = imap_ssn->body_read = 0; IMAP_ResetState(); } } IMAP_GetEOL(ptr, end, &eol, &eolm); resp_line_len = eol - ptr; /* Check for response code */ imap_current_search = &imap_resp_search[0]; resp_found = _dpd.searchAPI->search_instance_find (imap_resp_search_mpse, (const char *)ptr, resp_line_len, 0, IMAP_SearchStrFound); if (resp_found > 0) { const uint8_t *cmd_start = ptr + imap_search_info.index; switch (imap_search_info.id) { case RESP_FETCH: imap_ssn->body_len = imap_ssn->body_read = 0; imap_ssn->state = STATE_DATA; tmp = _dpd.SnortStrcasestr((const char *)cmd_start, (eol - cmd_start), "BODY"); if(tmp != NULL) imap_ssn->state = STATE_DATA; else { tmp = _dpd.SnortStrcasestr((const char *)cmd_start, (eol - cmd_start), "RFC822"); if(tmp != NULL) imap_ssn->state = STATE_DATA; else imap_ssn->state = STATE_UNKNOWN; } break; default: break; } if(imap_ssn->state == STATE_DATA) { body_start = (uint8_t *)memchr((char *)ptr, '{', (eol - ptr)); if( body_start == NULL ) { imap_ssn->state = STATE_UNKNOWN; } else { if( (body_start + 1) < (uint8_t *)eol ) { len = (uint32_t)_dpd.SnortStrtoul((const char *)(body_start + 1), &eptr, 10); if (*eptr != '}') { imap_ssn->state = STATE_UNKNOWN; } else imap_ssn->body_len = len; len = 0; } else imap_ssn->state = STATE_UNKNOWN; } } } else { if ((imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL) && (IsSSL(ptr, end - ptr, p->flags))) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Server response is an SSL packet\n");); imap_ssn->state = STATE_TLS_DATA; return; } else if (imap_ssn->session_flags & IMAP_FLAG_CHECK_SSL) { imap_ssn->session_flags &= ~IMAP_FLAG_CHECK_SSL; } if ( (*ptr != '*') && (*ptr !='+') && (*ptr != '\r') && (*ptr != '\n') ) { IMAP_GenerateAlert(IMAP_UNKNOWN_RESP, "%s", IMAP_UNKNOWN_RESP_STR); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Server response not found\n");); } } ptr = eol; } return; } /* For Target based * If a protocol for the session is already identified and not one IMAP is * interested in, IMAP should leave it alone and return without processing. * If a protocol for the session is already identified and is one that IMAP is * interested in, decode it. * If the protocol for the session is not already identified and the preprocessor * is configured to detect on one of the packet ports, detect. * Returns 0 if we should not inspect * 1 if we should continue to inspect */ static int IMAP_Inspect(SFSnortPacket *p) { #ifdef TARGET_BASED /* IMAP could be configured to be stateless. If stream isn't configured, assume app id * will never be set and just base inspection on configuration */ if (p->stream_session == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP: Target-based: No stream session.\n");); if ((IMAP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (IMAP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP: Target-based: Configured for this " "traffic, so let's inspect.\n");); return 1; } } else { int16_t app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if (app_id != 0) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP: Target-based: App id: %u.\n", app_id);); if (app_id == imap_proto_id) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP: Target-based: App id is " "set to \"%s\".\n", IMAP_PROTO_REF_STR);); return 1; } } else { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP: Target-based: Unknown protocol for " "this session. See if we're configured.\n");); if ((IMAP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (IMAP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP: Target-based: IMAP port is configured.");); return 1; } } } DEBUG_WRAP(DebugMessage(DEBUG_IMAP,"IMAP: Target-based: Not inspecting ...\n");); #else /* Make sure it's traffic we're interested in */ if ((IMAP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (IMAP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) return 1; #endif /* TARGET_BASED */ return 0; } /* * Entry point to snort preprocessor for each packet * * @param packet standard Packet structure * * @return none */ void SnortIMAP(SFSnortPacket *p) { int detected = 0; int pkt_dir; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); #ifdef DUMP_BUFFER dumpBufferInit(); #endif PROFILE_VARS; imap_ssn = (IMAP *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_IMAP); if (imap_ssn != NULL) imap_eval_config = (IMAPConfig *)sfPolicyUserDataGet(imap_ssn->config, imap_ssn->policy_id); else imap_eval_config = (IMAPConfig *)sfPolicyUserDataGetCurrent(imap_config); if (imap_eval_config == NULL) return; if (imap_ssn == NULL) { if (!IMAP_Inspect(p)) return; imap_ssn = IMAP_GetNewSession(p, policy_id); if (imap_ssn == NULL) return; } pkt_dir = IMAP_Setup(p, imap_ssn); if (pkt_dir == IMAP_PKT_FROM_CLIENT) { /* This packet should be a tls client hello */ if (imap_ssn->state == STATE_TLS_CLIENT_PEND) { if (IsTlsClientHello(p->payload, p->payload + p->payload_size)) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "TLS DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~\n");); imap_ssn->state = STATE_TLS_SERVER_PEND; if(ssl_cb) ssl_cb->session_initialize(p, imap_ssn, IMAP_Set_flow_id); return; } else { /* reset state - server may have rejected STARTTLS command */ imap_ssn->state = STATE_UNKNOWN; } } if ((imap_ssn->state == STATE_TLS_DATA) || (imap_ssn->state == STATE_TLS_SERVER_PEND)) { if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) { imap_ssn->state = STATE_COMMAND; } else return; } IMAP_ProcessClientPacket(p); DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP client packet\n");); } else { #ifdef DEBUG_MSGS if (pkt_dir == IMAP_PKT_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP server packet\n");); } else { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP packet NOT from client or server! " "Processing as a server packet\n");); } #endif if (imap_ssn->state == STATE_TLS_SERVER_PEND) { if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) { imap_ssn->state = STATE_COMMAND; } else if (IsTlsServerHello(p->payload, p->payload + p->payload_size)) { imap_ssn->state = STATE_TLS_DATA; } else if (!(_dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM) && !_dpd.streamAPI->missed_packets(p->stream_session, SSN_DIR_BOTH)) { /* revert back to command state - assume server didn't accept STARTTLS */ imap_ssn->state = STATE_UNKNOWN; } else return; } if (imap_ssn->state == STATE_TLS_DATA) { if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) { imap_ssn->state = STATE_COMMAND; } else return; } { if (!_dpd.readyForProcess(p)) { /* Packet will be rebuilt, so wait for it */ DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Client packet will be reassembled\n")); return; } else if (imap_ssn->reassembling && !(p->flags & FLAG_REBUILT_STREAM)) { /* If this isn't a reassembled packet and didn't get * inserted into reassembly buffer, there could be a * problem. If we miss syn or syn-ack that had window * scaling this packet might not have gotten inserted * into reassembly buffer because it fell outside of * window, because we aren't scaling it */ imap_ssn->session_flags |= IMAP_FLAG_GOT_NON_REBUILT; imap_ssn->state = STATE_UNKNOWN; } else if (imap_ssn->reassembling && (imap_ssn->session_flags & IMAP_FLAG_GOT_NON_REBUILT)) { /* This is a rebuilt packet. If we got previous packets * that were not rebuilt, state is going to be messed up * so set state to unknown. It's likely this was the * beginning of the conversation so reset state */ DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "Got non-rebuilt packets before " "this rebuilt packet\n");); imap_ssn->state = STATE_UNKNOWN; imap_ssn->session_flags &= ~IMAP_FLAG_GOT_NON_REBUILT; } /* Process as a server packet */ IMAP_ProcessServerPacket(p); } } PREPROC_PROFILE_START(imapDetectPerfStats); detected = _dpd.detect(p); #ifdef PERF_PROFILING imapDetectCalled = 1; #endif PREPROC_PROFILE_END(imapDetectPerfStats); /* Turn off detection since we've already done it. */ IMAP_DisableDetect(p); if (detected) { DEBUG_WRAP(DebugMessage(DEBUG_IMAP, "IMAP vulnerability detected\n");); } } static void IMAP_DisableDetect(SFSnortPacket *p) { _dpd.disableAllDetect(p); _dpd.enablePreprocessor(p, PP_SDF); } static inline IMAP *IMAP_GetSession(void *data) { if(data) return (IMAP *)_dpd.sessionAPI->get_application_data(data, PP_IMAP); return NULL; } /* Callback to return the MIME attachment filenames accumulated */ int IMAP_GetFilename(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { IMAP *ssn = IMAP_GetSession(data); if(ssn == NULL) return 0; *buf = ssn->mime_ssn.log_state->file_log.filenames; *len = ssn->mime_ssn.log_state->file_log.file_logged; return 1; } void IMAP_Set_flow_id( void *app_data, uint32_t fid ) { IMAP *ssn = (IMAP *)app_data; if( ssn ) ssn->flow_id = fid; } snort-2.9.20/src/dynamic-preprocessors/imap/imap_util.h0000644000175000017500000000325014241076042021275 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************* * * imap_util.h * * Author: Bhagyashree Bantwal * *************************************************************************/ #ifndef __IMAP_UTIL_H__ #define __IMAP_UTIL_H__ #include "sf_snort_packet.h" void IMAP_GetEOL(const uint8_t *, const uint8_t *, const uint8_t **, const uint8_t **); void IMAP_DecodeType(const char *start, int length, bool cnt_xf); int IMAP_Print_Mem_Stats(FILE *fd, char *buffer, PreprocMemInfo *meminfo); #ifdef DEBUG_MSGS const char * IMAP_PrintBuffer(SFSnortPacket *); #endif #endif /* __IMAP_UTIL_H__ */ snort-2.9.20/src/dynamic-preprocessors/imap/Makefile.am0000444000175000017500000000226414230012554021173 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_imap_preproc.la libsf_imap_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_imap_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_imap_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/mempool.c \ ../include/sf_sdlist.c \ ../include/sf_base64decode.c \ ../include/util_unfold.c \ ../include/sf_email_attach_decode.c \ ../include/sfPolicyUserData.c \ ../ssl_common/ssl.c \ ../ssl_common/ssl_config.c \ ../ssl_common/ssl_inspect.c \ ../libs/sfparser.c endif libsf_imap_preproc_la_SOURCES = \ imap_config.c \ imap_config.h \ imap_log.c \ imap_log.h \ imap_util.c \ imap_util.h \ imap_paf.c \ imap_paf.h \ snort_imap.c \ snort_imap.h \ spp_imap.c \ spp_imap.h if BUILD_BUFFER_DUMP libsf_imap_preproc_la_SOURCES += \ imap_buffer_dump.c \ imap_buffer_dump.h endif EXTRA_DIST = \ sf_imap.vcxproj \ sf_imap.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/imap/imap_paf.h0000644000175000017500000000257214241076037021100 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __IMAP_PAF_H__ #define __IMAP_PAF_H__ #include "sfPolicy.h" #include "sfPolicyUserData.h" #ifdef TARGET_BASED void register_imap_paf_service(struct _SnortConfig *sc, int16_t app, tSfPolicyId policy); #endif void register_imap_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy); bool is_data_end (void* ssn); #endif snort-2.9.20/src/dynamic-preprocessors/imap/imap_util.c0000644000175000017500000001620214241076040021267 0ustar apoapo/* * imap_util.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * * Author: Bhagyashree Bantwal * * Description: * * This file contains IMAP helper functions. * * Entry point functions: * * safe_strchr() * safe_strstr() * copy_to_space() * safe_sscanf() * * */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "snort_bounds.h" #include "snort_imap.h" #include "imap_util.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_packet.h" #include "Unified2_common.h" #include "memory_stats.h" extern IMAP *imap_ssn; extern MemPool *imap_mempool; extern MemPool *imap_mime_mempool; int IMAP_Print_Mem_Stats(FILE *fd, char *buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; if (fd) { len = fprintf(fd, ",%lu,%lu,%lu" ",%lu,%u,%u" ",%lu,%u,%u,%lu" , imap_stats.sessions , imap_stats.max_conc_sessions , imap_stats.cur_sessions , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); return len; } if (buffer) { /* * Old buffer output for control socket comm, * like via, "show snort preprocessor-memory-usage" * CLI preserved as is */ len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics of IMAP on: %s\n" "IMAP Session Statistics:\n" " Total Sessions seen: " STDu64 "\n" " Max concurrent sessions: " STDu64 "\n" " Current Active sessions: " STDu64 "\n" "\n Memory Pool:\n" " Free Memory:\n" " IMAP Mime Pool: %14zu bytes\n" " IMAP Pool: %14zu bytes\n" " Used Memory:\n" " IMAP Mime Pool: %14zu bytes\n" " IMAP Pool: %14zu bytes\n" " ------------------- ---------------\n" " Total Memory: %14zu bytes\n" , ctime(&curr_time) , imap_stats.sessions , imap_stats.max_conc_sessions , imap_stats.cur_sessions , (imap_mime_mempool) ? (imap_mime_mempool->max_memory - imap_mime_mempool->used_memory) : 0 , (imap_mempool) ? (imap_mempool->max_memory - imap_mempool->used_memory) : 0 , (imap_mime_mempool) ? imap_mime_mempool->used_memory : 0 , (imap_mempool) ? imap_mempool->used_memory : 0 , ((imap_mime_mempool) ? (imap_mime_mempool->max_memory) : 0) + ((imap_mempool) ? (imap_mempool->max_memory) : 0)); len += PopulateMemStatsBuffTrailer(buffer+len, len, meminfo); } else { _dpd.logMsg("IMAP Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions : %lu \n", imap_stats.sessions); _dpd.logMsg(" Max concurrent sessions : %lu \n", imap_stats.max_conc_sessions); _dpd.logMsg(" Current sessions : %lu \n", imap_stats.cur_sessions); _dpd.logMsg(" IMAP Session \n"); _dpd.logMsg(" Used Memory :%14lu\n", meminfo[PP_MEM_CATEGORY_SESSION].used_memory); _dpd.logMsg(" No of Allocs :%14u\n", meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc); _dpd.logMsg(" No of Frees :%14u\n", meminfo[PP_MEM_CATEGORY_SESSION].num_of_free); _dpd.logMsg(" IMAP Config \n"); _dpd.logMsg(" Used Memory :%14lu\n", meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); _dpd.logMsg(" No of Allocs :%14u\n", meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc); _dpd.logMsg(" No of Frees :%14u\n", meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free); _dpd.logMsg(" Total memory used :%14lu\n", meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); } return len; } void IMAP_GetEOL(const uint8_t *ptr, const uint8_t *end, const uint8_t **eol, const uint8_t **eolm) { const uint8_t *tmp_eol; const uint8_t *tmp_eolm; /* XXX maybe should fatal error here since none of these * pointers should be NULL */ if (ptr == NULL || end == NULL || eol == NULL || eolm == NULL) return; tmp_eol = (uint8_t *)memchr(ptr, '\n', end - ptr); if (tmp_eol == NULL) { tmp_eol = end; tmp_eolm = end; } else { /* end of line marker (eolm) should point to marker and * end of line (eol) should point to end of marker */ if ((tmp_eol > ptr) && (*(tmp_eol - 1) == '\r')) { tmp_eolm = tmp_eol - 1; } else { tmp_eolm = tmp_eol; } /* move past newline */ tmp_eol++; } *eol = tmp_eol; *eolm = tmp_eolm; } #ifdef DEBUG_MSGS char imap_print_buffer[65537]; const char * IMAP_PrintBuffer(SFSnortPacket *p) { const uint8_t *ptr = NULL; int len = 0; int iorig, inew; ptr = p->payload; len = p->payload_size; for (iorig = 0, inew = 0; iorig < len; iorig++, inew++) { if ((isascii((int)ptr[iorig]) && isprint((int)ptr[iorig])) || (ptr[iorig] == '\n')) { imap_print_buffer[inew] = ptr[iorig]; } else if (ptr[iorig] == '\r' && ((iorig + 1) < len) && (ptr[iorig + 1] == '\n')) { iorig++; imap_print_buffer[inew] = '\n'; } else if (isspace((int)ptr[iorig])) { imap_print_buffer[inew] = ' '; } else { imap_print_buffer[inew] = '.'; } } imap_print_buffer[inew] = '\0'; return &imap_print_buffer[0]; } #endif snort-2.9.20/src/dynamic-preprocessors/imap/imap_buffer_dump.h0000644000175000017500000000322114241076031022612 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file imap_buffer_dump.h ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during IMAP inspection. */ #ifndef __IMAP_BUFFER_DUMP_H__ #define __IMAP_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { IMAP_CLIENT_DUMP, IMAP_SERVER_DUMP, IMAP_CLIENT_CMD_DUMP, IMAP_SERVER_BODY_DATA_DUMP } IMAP_BUFFER_DUMP; void dumpBuffer(IMAP_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getIMAPBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/appid/0000755000175000017500000000000014242725717017323 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/appid/fw_appid.c0000644000175000017500000067136414241075440021267 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WIN32 #include #include #endif #include #include "appIdApi.h" #include "fw_appid.h" #include "profiler.h" #include "client_app_base.h" #include "httpCommon.h" #include "luaDetectorApi.h" #include "http_url_patterns.h" #include "fw_appid.h" #include "detector_http.h" #include "service_ssl.h" #include "detector_dns.h" #include "flow.h" #include "common_util.h" #include "spp_appid.h" #include "hostPortAppCache.h" #include "lengthAppCache.h" #include "appInfoTable.h" #include "appIdStats.h" #include "sf_mlmp.h" #include "ip_funcs.h" #include "app_forecast.h" #include "thirdparty_appid_types.h" #include "thirdparty_appid_utils.h" #include "appInfoTable.h" #include "service_base.h" //#define DEBUG_APP_ID_SESSIONS 1 //#define DEBUG_FW_APPID 1 //#define DEBUG_FW_APPID_PORT 80 #define MAX_ATTR_LEN 1024 #define HTTP_PREFIX "http://" #define HTTPS_PREFIX "https://" #define MULTI_BUF_SIZE 1024 #define MAX_HOSTNAME 255 #define HTTP_PREFIX_LEN 7 #define HTTPS_PREFIX_LEN 8 #define APP_MAPPING_FILE "appMapping.data" #ifdef RNA_DEBUG_PE static const char *MODULE_NAME = "fw_appid"; #endif static volatile int app_id_debug_flag; static FWDebugSessionConstraints app_id_debug_info; char app_id_debug_session[FW_DEBUG_SESSION_ID_SIZE]; bool app_id_debug_session_flag; #ifdef PERF_PROFILING PreprocStats tpPerfStats; PreprocStats tpLibPerfStats; PreprocStats httpPerfStats; PreprocStats clientMatchPerfStats; PreprocStats serviceMatchPerfStats; #endif #define HTTP_PATTERN_MAX_LEN 1024 #define PORT_MAX 65535 unsigned long app_id_ongoing_session = 0; unsigned long app_id_total_alloc = 0; unsigned long app_id_raw_packet_count = 0; unsigned long app_id_processed_packet_count = 0; unsigned long app_id_ignored_packet_count = 0; unsigned long app_id_flow_data_free_list_count = 0; unsigned long app_id_data_free_list_count = 0; unsigned long app_id_tmp_free_list_count = 0; unsigned long app_id_session_heap_alloc_count = 0; unsigned long app_id_session_freelist_alloc_count = 0; static tAppIdData *app_id_free_list; static tTmpAppIdData *tmp_app_id_free_list; static uint32_t snortInstance; int app_id_debug; static int ptype_scan_counts[NUMBER_OF_PTYPES]; static void ProcessThirdPartyResults(SFSnortPacket* p, APPID_SESSION_DIRECTION direction, tAppIdData* appIdSession, int confidence, tAppId* proto_list, ThirdPartyAppIDAttributeData* attribute_data); static void ExamineRtmpMetadata(SFSnortPacket* p, APPID_SESSION_DIRECTION direction, tAppIdData *appIdSession); AppIdDebugHostInfo_t AppIdDebugHostInfo; static inline void appSharedDataFree(tAppIdData * sharedData) { sharedData->next = app_id_free_list; app_id_free_list = sharedData; app_id_data_free_list_count++; } static inline void appTmpSharedDataFree(tTmpAppIdData * sharedData) { sharedData->next = tmp_app_id_free_list; tmp_app_id_free_list = sharedData; app_id_tmp_free_list_count++; } static inline void appHttpFieldClear (httpSession *hsession) { if (hsession == NULL) return; if (hsession->referer) { free(hsession->referer); hsession->referer = NULL; } if (hsession->cookie) { free(hsession->cookie); hsession->cookie = NULL; } if (hsession->url) { free(hsession->url); hsession->url = NULL; } if (hsession->useragent) { free(hsession->useragent); hsession->useragent = NULL; } if (hsession->host) { free(hsession->host); hsession->host = NULL; } if (hsession->uri) { free(hsession->uri); hsession->uri = NULL; } if (hsession->content_type) { free(hsession->content_type); hsession->content_type = NULL; } if (hsession->location) { free(hsession->location); hsession->location = NULL; } if (hsession->body) { free(hsession->body); hsession->body = NULL; } if (hsession->req_body) { free(hsession->req_body); hsession->req_body = NULL; } if (hsession->server) { free(hsession->server); hsession->server = NULL; } if (hsession->x_working_with) { free(hsession->x_working_with); hsession->x_working_with = NULL; } if (hsession->xffAddr) { sfaddr_free(hsession->xffAddr); hsession->xffAddr = NULL; } if (hsession->xffPrecedence) { int i; for (i = 0; i < hsession->numXffFields; i++) free(hsession->xffPrecedence[i]); _dpd.snortFree(hsession->xffPrecedence, hsession->numXffFields*sizeof(char*), PP_APP_ID, PP_MEM_CATEGORY_SESSION); hsession->xffPrecedence = NULL; } } static inline void appHttpSessionDataFree (httpSession *hsession) { int i; if (hsession == NULL) return; appHttpFieldClear(hsession); if (hsession->new_field_contents) { for (i = 0; i < NUMBER_OF_PTYPES; i++) { if (NULL != hsession->new_field[i]) { free(hsession->new_field[i]); hsession->new_field[i] = NULL; } } } if (hsession->fflow) { _dpd.snortFree(hsession->fflow, sizeof(*hsession->fflow), PP_APP_ID, PP_MEM_CATEGORY_SESSION); hsession->fflow = NULL; } if (hsession->via) { free(hsession->via); hsession->via = NULL; } if (hsession->content_type) { free(hsession->content_type); hsession->content_type = NULL; } if (hsession->response_code) { free(hsession->response_code); hsession->response_code = NULL; } if (hsession->tunDest) { free(hsession->tunDest); hsession->tunDest = NULL; } _dpd.snortFree(hsession, sizeof(*hsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION); } static inline void appDNSSessionDataFree(dnsSession *dsession) { if (dsession == NULL) return; if (dsession->host) { free(dsession->host); dsession->host = NULL; } _dpd.snortFree(dsession, sizeof(*dsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION); } static inline void appTlsSessionDataFree (tlsSession *tsession) { if (tsession == NULL) return; if (tsession->tls_host) free(tsession->tls_host); if (tsession->tls_cname) free(tsession->tls_cname); if (tsession->tls_orgUnit) free(tsession->tls_orgUnit); if (tsession->tls_first_san) free(tsession->tls_first_san); _dpd.snortFree(tsession, sizeof(*tsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION); } #ifdef REG_TEST void appIdRegTestDumpEndOfSession(tAppIdData *appid_session) { if (appid_session == NULL) return; _dpd.logMsg("AppID End of Session...\n"); _dpd.logMsg(" pickServiceAppId(appid_session) = %d\n", pickServiceAppId(appid_session)); _dpd.logMsg(" pickPayloadId(appid_session) = %d\n", pickPayloadId(appid_session)); _dpd.logMsg(" pickClientAppId(appid_session) = %d\n", pickClientAppId(appid_session)); _dpd.logMsg(" pickMiscAppId(appid_session) = %d\n", pickMiscAppId(appid_session)); if (appid_session->dsession != NULL) { _dpd.logMsg(" appid_session->dsession->host = %s\n", appid_session->dsession->host ?: "NULL"); _dpd.logMsg(" appid_session->dsession->options_offset = %u\n", appid_session->dsession->options_offset); } if (appid_session->tsession != NULL) { _dpd.logMsg(" appid_session->tsession->tls_host = %s\n", appid_session->tsession->tls_host ?: "NULL"); } _dpd.logMsg(" Flow is %s, ignore flag is %s\n", getAppIdFlag(appid_session, APPID_SESSION_OOO)? "out-of-order":"in-order", getAppIdFlag(appid_session, APPID_SESSION_IGNORE_FLOW)? "true":"false"); } #endif void appSharedDataDelete(tAppIdData * sharedData) { RNAServiceSubtype *subtype; if (sharedData) { #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (sharedData->service_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "Deleting session %p\n", sharedData); #endif app_id_ongoing_session--; #ifdef REG_TEST if (appidStaticConfig->appid_reg_test_mode) appIdRegTestDumpEndOfSession(sharedData); #endif /*check daq flag */ appIdStatsUpdate(sharedData); if (sharedData->ssn) FailInProcessService(sharedData, pAppidActiveConfig); AppIdFlowdataFree(sharedData); if (thirdparty_appid_module) { thirdparty_appid_module->session_delete(sharedData->tpsession, 0); // we're completely done with it sharedData->tpsession = NULL; } free(sharedData->clientVersion); free(sharedData->serviceVendor); free(sharedData->serviceVersion); free(sharedData->netbios_name); while ((subtype = sharedData->subtype)) { sharedData->subtype = subtype->next; free(*(void **)&subtype->service); free(*(void **)&subtype->vendor); free(*(void **)&subtype->version); free(subtype); } if (sharedData->candidate_service_list != NULL) { sflist_free(sharedData->candidate_service_list); sharedData->candidate_service_list = NULL; } if (sharedData->candidate_client_list != NULL) { sflist_free(sharedData->candidate_client_list); sharedData->candidate_client_list = NULL; } free(sharedData->username); free(sharedData->netbiosDomain); free(sharedData->payloadVersion); appHttpSessionDataFree(sharedData->hsession); appTlsSessionDataFree(sharedData->tsession); appDNSSessionDataFree(sharedData->dsession); sharedData->tsession = NULL; if (sharedData->multiPayloadList) sfghash_delete(sharedData->multiPayloadList); free(sharedData->firewallEarlyData); sharedData->firewallEarlyData = NULL; appSharedDataFree(sharedData); } } /* The snortId_for_unsynchronized value is to cheaply insure we get a unique value from snort's list that guarantees no other preprocessor has it in use. */ static int16_t snortId_for_unsynchronized; static int16_t snortId_for_ftp_data; static int16_t snortId_for_http2; tAppIdData* appSharedDataAlloc(uint8_t proto, const struct in6_addr *ip, uint16_t port) { static uint32_t gFlowId; tAppIdData *data; app_id_ongoing_session++; if (app_id_free_list) { data = app_id_free_list; app_id_free_list = data->next; memset(data, 0, sizeof(*data)); app_id_data_free_list_count--; app_id_session_freelist_alloc_count++; } else if (!(data = _dpd.snortAlloc(1, sizeof(*data), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate tAppIdData data"); else app_id_session_heap_alloc_count++; app_id_total_alloc++; data->flowId = ++gFlowId; data->common.fsf_type.flow_type = APPID_SESSION_TYPE_NORMAL; data->proto = proto; data->common.initiator_ip = *ip; data->common.initiator_port = port; data->snortId = snortId_for_unsynchronized; data->search_support_type = SEARCH_SUPPORT_TYPE_UNKNOWN; return data; } static inline tAppIdData* appSharedCreateData(const SFSnortPacket *p, uint8_t proto, APPID_SESSION_DIRECTION direction) { #ifdef DEBUG_FW_APPID static unsigned long packet_count; #endif tAppIdData *data; sfaddr_t *ip; ip = (direction == APP_ID_FROM_INITIATOR) ? GET_SRC_IP(p) : GET_DST_IP(p); data = appSharedDataAlloc(proto, (struct in6_addr*)sfaddr_get_ip6_ptr(ip) , 0 ); if ((proto == IPPROTO_TCP || proto == IPPROTO_UDP) && p->src_port != p->dst_port) data->common.initiator_port = (direction == APP_ID_FROM_INITIATOR) ? p->src_port : p->dst_port; data->ssn = p->stream_session; #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "pkt %lu : tAppIdData: Allocated %p\n", ++packet_count, data); #endif data->stats.firstPktsecond = p->pkt_header->ts.tv_sec; _dpd.sessionAPI->set_application_data(p->stream_session, PP_APP_ID, data, (void (*)(void *))appSharedDataDelete); return data; } static inline void appSharedReInitData(tAppIdData* session) { session->miscAppId = APP_ID_NONE; //payload if (isSslServiceAppId(session->tpAppId)) { session->payloadAppId = session->referredPayloadAppId = session->tpPayloadAppId = APP_ID_NONE; clearAppIdFlag(session, APPID_SESSION_CONTINUE); if (session->payloadVersion) { free(session->payloadVersion); session->payloadVersion = NULL; } if (session->hsession && session->hsession->url) { free(session->hsession->url); session->hsession->url = NULL; } } //service if (!getAppIdFlag(session, APPID_SESSION_STICKY_SERVICE)) { session->tpAppId = session->serviceAppId = session->portServiceAppId = APP_ID_NONE; if (session->serviceVendor) { free(session->serviceVendor); session->serviceVendor = NULL; } if (session->serviceVersion) { free(session->serviceVersion); session->serviceVersion = NULL; } IP_CLEAR(session->service_ip); session->service_port = 0; session->rnaServiceState = RNA_STATE_NONE; session->serviceData = NULL; AppIdFlowdataDeleteAllByMask(session, APPID_SESSION_DATA_SERVICE_MODSTATE_BIT); #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) session->serviceAsId = 0xFF; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) session->carrierId = 0; #endif } //client session->clientAppId = session->clientServiceAppId = APP_ID_NONE; if (session->clientVersion) { free(session->clientVersion); session->clientVersion = NULL; } session->rnaClientState = RNA_STATE_NONE; session->clientData = NULL; if (session->candidate_client_list) { sflist_free(session->candidate_client_list); session->candidate_client_list = NULL; } session->num_candidate_clients_tried = 0; AppIdFlowdataDeleteAllByMask(session, APPID_SESSION_DATA_CLIENT_MODSTATE_BIT); //3rd party cleaning if (thirdparty_appid_module) thirdparty_appid_module->session_delete(session->tpsession, 1); session->init_tpPackets = 0; session->resp_tpPackets = 0; session->scan_flags &= ~SCAN_HTTP_HOST_URL_FLAG; clearAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED|APPID_SESSION_CLIENT_DETECTED|APPID_SESSION_SSL_SESSION|APPID_SESSION_HTTP_SESSION|APPID_SESSION_APP_REINSPECT); } void fwAppIdFini(tAppIdConfig *pConfig) { #ifdef APPID_FULL_CLEANUP tAppIdData *app_id; tTmpAppIdData *tmp_app_id; while ((app_id = app_id_free_list)) { app_id_free_list = app_id->next; app_id_data_free_list_count--; _dpd.snortFree(app_id, sizeof(*app_id), PP_APP_ID, PP_MEM_CATEGORY_SESSION); } while ((tmp_app_id = tmp_app_id_free_list)) { tmp_app_id_free_list = tmp_app_id->next; app_id_tmp_free_list_count--; _dpd.snortFree(tmp_app_id, sizeof(*tmp_app_id), PP_APP_ID, PP_MEM_CATEGORY_SESSION); } AppIdFlowdataFini(); #endif appInfoTableFini(pConfig); } static inline int PENetworkMatch(const sfaddr_t *pktAddr, const PortExclusion *pe) { const uint32_t* pkt = sfaddr_get_ip6_ptr(pktAddr); const uint32_t* nm = pe->netmask.s6_addr32; const uint32_t* peIP = pe->ip.s6_addr32; return (((pkt[0] & nm[0]) == peIP[0]) && ((pkt[1] & nm[1]) == peIP[1]) && ((pkt[2] & nm[2]) == peIP[2]) && ((pkt[3] & nm[3]) == peIP[3])); } static inline int checkPortExclusion(const SFSnortPacket *pkt, int reversed) { SF_LIST * *src_port_exclusions; SF_LIST * *dst_port_exclusions; SF_LIST *pe_list; PortExclusion *pe; sfaddr_t *s_ip; uint16_t port; tAppIdConfig *pConfig = appIdActiveConfigGet(); if (IsTCP(pkt)) { src_port_exclusions = pConfig->tcp_port_exclusions_src; dst_port_exclusions = pConfig->tcp_port_exclusions_dst; } else if (IsUDP(pkt)) { src_port_exclusions = pConfig->udp_port_exclusions_src; dst_port_exclusions = pConfig->udp_port_exclusions_dst; } else return 0; /* check the source port */ port = reversed ? pkt->dst_port : pkt->src_port; if( port && (pe_list=src_port_exclusions[port]) != NULL ) { s_ip = reversed ? GET_DST_IP(pkt) : GET_SRC_IP(pkt); /* walk through the list of port exclusions for this port */ for (pe=(PortExclusion *)sflist_first(pe_list); pe; pe=(PortExclusion *)sflist_next(pe_list)) { if( PENetworkMatch(s_ip, pe)) { #ifdef RNA_DEBUG_PE char inetBuffer[INET6_ADDRSTRLEN]; inetBuffer[0] = 0; inet_ntop(sfaddr_family(s_ip), (void *)sfaddr_get_ptr(s_ip), inetBuffer, sizeof(inetBuffer)); SFDEBUG(MODULE_NAME, "excluding src port: %d",port); SFDEBUG(MODULE_NAME, "for addresses src: %s", inetBuffer); #endif return 1; } } } /* check the dest port */ port = reversed ? pkt->src_port : pkt->dst_port; if( port && (pe_list=dst_port_exclusions[port]) != NULL ) { s_ip = reversed ? GET_SRC_IP(pkt) : GET_DST_IP(pkt); /* walk through the list of port exclusions for this port */ for (pe=(PortExclusion *)sflist_first(pe_list); pe; pe=(PortExclusion *)sflist_next(pe_list)) { if( PENetworkMatch(s_ip, pe)) { #ifdef RNA_DEBUG_PE char inetBuffer[INET6_ADDRSTRLEN]; inetBuffer[0] = 0; inet_ntop(sfaddr_family(s_ip), (void *)sfaddr_get_ptr(s_ip), inetBuffer, sizeof(inetBuffer)); SFDEBUG(MODULE_NAME, "excluding dst port: %d",port); SFDEBUG(MODULE_NAME, "for addresses dst: %s", inetBuffer); #endif return 1; } } } return 0; } static inline bool fwAppIdDebugCheck(void *lwssn, tAppIdData *session, volatile int debug_flag, FWDebugSessionConstraints *info, char *debug_session, APPID_SESSION_DIRECTION direction) { if (debug_flag) { const StreamSessionKey *key; key = _dpd.sessionAPI->get_key_from_session_ptr(lwssn); if ((!info->protocol || info->protocol == key->protocol) && (((!info->sport || info->sport == key->port_l) && (!info->sip_flag || memcmp(&info->sip, key->ip_l, sizeof(info->sip)) == 0) && (!info->dport || info->dport == key->port_h) && (!info->dip_flag || memcmp(&info->dip, key->ip_h, sizeof(info->dip)) == 0)) || ((!info->sport || info->sport == key->port_h) && (!info->sip_flag || memcmp(&info->sip, key->ip_h, sizeof(info->sip)) == 0) && (!info->dport || info->dport == key->port_l) && (!info->dip_flag || memcmp(&info->dip, key->ip_l, sizeof(info->dip)) == 0)))) { int af; const struct in6_addr* sip; const struct in6_addr* dip; unsigned offset; uint16_t sport; uint16_t dport; char sipstr[INET6_ADDRSTRLEN]; char dipstr[INET6_ADDRSTRLEN]; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t sAsId; uint16_t dAsId; #endif if (session && session->common.fsf_type.flow_type != APPID_SESSION_TYPE_IGNORE) { if (session->common.initiator_port) { if (session->common.initiator_port == key->port_l) { sip = (const struct in6_addr*)key->ip_l; dip = (const struct in6_addr*)key->ip_h; sport = key->port_l; dport = key->port_h; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) sAsId = key->addressSpaceId_l; dAsId = key->addressSpaceId_h; #endif } else { sip = (const struct in6_addr*)key->ip_h; dip = (const struct in6_addr*)key->ip_l; sport = key->port_h; dport = key->port_l; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) sAsId = key->addressSpaceId_h; dAsId = key->addressSpaceId_l; #endif } } else if (memcmp(&session->common.initiator_ip, key->ip_l, sizeof(session->common.initiator_ip))==0) { sip = (const struct in6_addr*)key->ip_l; dip = (const struct in6_addr*)key->ip_h; sport = key->port_l; dport = key->port_h; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) sAsId = key->addressSpaceId_l; dAsId = key->addressSpaceId_h; #endif } else { sip = (const struct in6_addr*)key->ip_h; dip = (const struct in6_addr*)key->ip_l; sport = key->port_h; dport = key->port_l; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) sAsId = key->addressSpaceId_h; dAsId = key->addressSpaceId_l; #endif } } else { sip = (const struct in6_addr*)key->ip_l; dip = (const struct in6_addr*)key->ip_h; sport = key->port_l; dport = key->port_h; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) sAsId = key->addressSpaceId_l; dAsId = key->addressSpaceId_h; #endif } sipstr[0] = 0; if (sip->s6_addr32[0] || sip->s6_addr32[1] || sip->s6_addr16[4] || (sip->s6_addr16[5] && sip->s6_addr16[5] != 0xFFFF)) { af = AF_INET6; offset = 0; } else { af = AF_INET; offset = 12; } inet_ntop(af, &sip->s6_addr[offset], sipstr, sizeof(sipstr)); dipstr[0] = 0; if (dip->s6_addr32[0] || dip->s6_addr32[1] || dip->s6_addr16[4] || (dip->s6_addr16[5] && dip->s6_addr16[5] != 0xFFFF)) { af = AF_INET6; offset = 0; } else { af = AF_INET; offset = 12; } inet_ntop(af, &dip->s6_addr[offset], dipstr, sizeof(dipstr)); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = key->carrierId; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) snprintf(debug_session, FW_DEBUG_SESSION_ID_SIZE, "%s-%u -> %s-%u %u%s AS %u-%u I %u CID %u", sipstr, (unsigned)sport, dipstr, (unsigned)dport, (unsigned)key->protocol, (direction == APP_ID_FROM_INITIATOR) ? "":" R", sAsId, dAsId, (unsigned)snortInstance, (unsigned)cid); #else snprintf(debug_session, FW_DEBUG_SESSION_ID_SIZE, "%s-%u -> %s-%u %u%s AS %u I %u CID %u", sipstr, (unsigned)sport, dipstr, (unsigned)dport, (unsigned)key->protocol, (direction == APP_ID_FROM_INITIATOR) ? "":" R", (unsigned)key->addressSpaceId, (unsigned)snortInstance, (unsigned)cid); #endif #else snprintf(debug_session, FW_DEBUG_SESSION_ID_SIZE, "%s-%u -> %s-%u %u%s I %u CID %u", sipstr, (unsigned)sport, dipstr, (unsigned)dport, (unsigned)key->protocol, (direction == APP_ID_FROM_INITIATOR) ? "":" R", (unsigned)snortInstance, (unsigned)cid ); #endif #else /* No carrierid support */ #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) snprintf(debug_session, FW_DEBUG_SESSION_ID_SIZE, "%s-%u -> %s-%u %u%s AS %u-%u I %u", sipstr, (unsigned)sport, dipstr, (unsigned)dport, (unsigned)key->protocol, (direction == APP_ID_FROM_INITIATOR) ? "":" R", sAsId, dAsId, (unsigned)snortInstance); #else snprintf(debug_session, FW_DEBUG_SESSION_ID_SIZE, "%s-%u -> %s-%u %u%s AS %u I %u", sipstr, (unsigned)sport, dipstr, (unsigned)dport, (unsigned)key->protocol, (direction == APP_ID_FROM_INITIATOR) ? "":" R", (unsigned)key->addressSpaceId, (unsigned)snortInstance); #endif #else snprintf(debug_session, FW_DEBUG_SESSION_ID_SIZE, "%s-%u -> %s-%u %u%s I %u", sipstr, (unsigned)sport, dipstr, (unsigned)dport, (unsigned)key->protocol, (direction == APP_ID_FROM_INITIATOR) ? "":" R", (unsigned)snortInstance); #endif #endif return true; } } return false; } static inline void appIdDebugParse(const char *desc, const uint8_t *data, uint32_t length, volatile int *debug_flag, FWDebugSessionConstraints *info) { *debug_flag = 0; memset(info, 0, sizeof(*info)); do { if (length >= sizeof(info->protocol)) { info->protocol = *data; length -= sizeof(info->protocol); data += sizeof(info->protocol); } else break; if (length >= sizeof(info->sip)) { memcpy(&info->sip, data, sizeof(info->sip)); if (info->sip.s6_addr32[1] || info->sip.s6_addr32[2] || info->sip.s6_addr32[3]) info->sip_flag = 1; else if (info->sip.s6_addr32[0]) { info->sip.s6_addr32[3] = info->sip.s6_addr32[0]; info->sip.s6_addr32[0] = 0; info->sip.s6_addr16[5] = 0xFFFF; info->sip_flag = 1; } length -= sizeof(info->sip); data += sizeof(info->sip); } else break; if (length >= sizeof(info->sport)) { memcpy(&info->sport, data, sizeof(info->sport)); length -= sizeof(info->sport); data += sizeof(info->sport); } else break; if (length >= sizeof(info->dip)) { memcpy(&info->dip, data, sizeof(info->dip)); if (info->dip.s6_addr32[1] || info->dip.s6_addr32[2] || info->dip.s6_addr32[3]) info->dip_flag = 1; else if (info->dip.s6_addr32[0]) { info->dip.s6_addr32[3] = info->dip.s6_addr32[0]; info->dip.s6_addr32[0] = 0; info->dip.s6_addr16[5] = 0xFFFF; info->dip_flag = 1; } length -= sizeof(info->dip); data += sizeof(info->dip); } else break; if (length >= sizeof(info->dport)) { memcpy(&info->dport, data, sizeof(info->dport)); length -= sizeof(info->dport); data += sizeof(info->dport); } else break; } while (0); if (info->protocol || info->sip_flag || info->sport || info->dip_flag || info->dport) { int saf; int daf; char sipstr[INET6_ADDRSTRLEN]; char dipstr[INET6_ADDRSTRLEN]; if (!info->sip.s6_addr32[0] && !info->sip.s6_addr32[0] && !info->sip.s6_addr16[4] && info->sip.s6_addr16[5] == 0xFFFF) { saf = AF_INET; } else saf = AF_INET6; if (!info->dip.s6_addr32[0] && !info->dip.s6_addr32[0] && !info->dip.s6_addr16[4] && info->dip.s6_addr16[5] == 0xFFFF) { daf = AF_INET; } else daf = AF_INET6; if (!info->sip_flag) saf = daf; if (!info->dip_flag) daf = saf; sipstr[0] = 0; inet_ntop(saf, saf == AF_INET ? &info->sip.s6_addr32[3] : info->sip.s6_addr32, sipstr, sizeof(sipstr)); dipstr[0] = 0; inet_ntop(daf, daf == AF_INET ? &info->dip.s6_addr32[3] : info->dip.s6_addr32, dipstr, sizeof(dipstr)); _dpd.logMsg("Debugging %s with %s-%u and %s-%u %u\n", desc, sipstr, (unsigned)info->sport, dipstr, (unsigned)info->dport, (unsigned)info->protocol); *debug_flag = 1; } else _dpd.logMsg("Debugging %s disabled\n", desc); } int AppIdDebug(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char* statusBuf, int statusBuf_len) { appIdDebugParse("appId", data, length, &app_id_debug_flag, &app_id_debug_info); return 0; } unsigned isIPv4HostMonitored(uint32_t ip4, int32_t zone) { NetworkSet *net_list; unsigned flags; tAppIdConfig *pConfig = appIdActiveConfigGet(); if (zone >= 0 && zone < MAX_ZONES && pConfig->net_list_by_zone[zone]) net_list = pConfig->net_list_by_zone[zone]; else net_list = pConfig->net_list; NetworkSet_ContainsEx(net_list, ip4, &flags); return flags; } static inline unsigned isIPMonitored(const SFSnortPacket *p, int dst) { uint32_t ipAddr; sfaddr_t *sf_ip; struct in_addr ip; NetworkSet *net_list; unsigned flags; int32_t zone; NSIPv6Addr ip6; tAppIdConfig *pConfig = appIdActiveConfigGet(); if (!dst) { zone = p->pkt_header->ingress_group; sf_ip = GET_SRC_IP(p); } else { zone = (p->pkt_header->egress_index == DAQ_PKTHDR_UNKNOWN) ? p->pkt_header->ingress_group : p->pkt_header->egress_group; if (zone == DAQ_PKTHDR_FLOOD) return 0; sf_ip = GET_DST_IP(p); } if (zone >= 0 && zone < MAX_ZONES && pConfig->net_list_by_zone[zone]) net_list = pConfig->net_list_by_zone[zone]; else net_list = pConfig->net_list; if (sfaddr_family(sf_ip) == AF_INET) { ip.s_addr = sfaddr_get_ip4_value(sf_ip); if (ip.s_addr == 0xFFFFFFFF) return IPFUNCS_CHECKED; ipAddr = ntohl(ip.s_addr); NetworkSet_ContainsEx(net_list, ipAddr, &flags); } else { memcpy(&ip6, sfaddr_get_ptr(sf_ip), sizeof(ip6)); NSIPv6AddrNtoH(&ip6); NetworkSet_Contains6Ex(net_list, &ip6, &flags); } return flags | IPFUNCS_CHECKED; } static inline int isSpecialSessionMonitored(const SFSnortPacket *p) { sfaddr_t *srcAddr; srcAddr = GET_SRC_IP(p); if (sfaddr_family(srcAddr) == AF_INET) { if (IsUDP(p) && ((p->src_port == 68 && p->dst_port == 67) || (p->src_port == 67 && p->dst_port == 68))) { return 1; } } return 0; } static inline uint64_t isSessionMonitored(const SFSnortPacket *p, APPID_SESSION_DIRECTION dir, tAppIdData *session) { uint64_t flags; uint64_t flow_flags = 0; tAppIdConfig *pConfig = appIdActiveConfigGet(); if (pConfig == NULL) return flow_flags; if (pConfig->isAppIdAlwaysRequired == APPID_REQ_UNINITIALIZED) pConfig->isAppIdAlwaysRequired = _dpd.isAppIdRequired() ? APPID_REQ_YES : APPID_REQ_NO; if (pConfig->isAppIdAlwaysRequired == APPID_REQ_YES) flow_flags |= APPID_SESSION_DISCOVER_APP; flow_flags |= (dir == APP_ID_FROM_INITIATOR) ? APPID_SESSION_INITIATOR_SEEN : APPID_SESSION_RESPONDER_SEEN; if (session) { flow_flags |= session->common.flags; if (session->common.policyId != appIdPolicyId) { if (checkPortExclusion(p, dir == APP_ID_FROM_RESPONDER)) { flow_flags |= APPID_SESSION_INITIATOR_SEEN | APPID_SESSION_RESPONDER_SEEN | APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_RESPONDER_CHECKED; flow_flags &= ~(APPID_SESSION_INITIATOR_MONITORED | APPID_SESSION_RESPONDER_MONITORED); return flow_flags; } if (dir == APP_ID_FROM_INITIATOR) { if (getAppIdFlag(session, APPID_SESSION_INITIATOR_CHECKED)) { flags = isIPMonitored(p, 0); if (flags & IPFUNCS_HOSTS_IP) { flow_flags |= APPID_SESSION_INITIATOR_MONITORED; AppIdDebugHostInfo.monitorType = APPID_DEBUG_HOST_MONITOR0; } else flow_flags &= ~APPID_SESSION_INITIATOR_MONITORED; } if (getAppIdFlag(session, APPID_SESSION_RESPONDER_CHECKED)) { flags = isIPMonitored(p, 1); if (flags & IPFUNCS_HOSTS_IP) flow_flags |= APPID_SESSION_RESPONDER_MONITORED; else flow_flags &= ~APPID_SESSION_RESPONDER_MONITORED; } } else { if (getAppIdFlag(session, APPID_SESSION_RESPONDER_CHECKED)) { flags = isIPMonitored(p, 0); if (flags & IPFUNCS_HOSTS_IP) flow_flags |= APPID_SESSION_RESPONDER_MONITORED; else flow_flags &= ~APPID_SESSION_RESPONDER_MONITORED; } if (getAppIdFlag(session, APPID_SESSION_INITIATOR_CHECKED)) { flags = isIPMonitored(p, 1); if (flags & IPFUNCS_HOSTS_IP) { flow_flags |= APPID_SESSION_INITIATOR_MONITORED; AppIdDebugHostInfo.monitorType = APPID_DEBUG_HOST_MONITOR1; } else flow_flags &= ~APPID_SESSION_INITIATOR_MONITORED; } } } if (getAppIdFlag(session, APPID_SESSION_BIDIRECTIONAL_CHECKED) == APPID_SESSION_BIDIRECTIONAL_CHECKED) return flow_flags; if (dir == APP_ID_FROM_INITIATOR) { if (!getAppIdFlag(session, APPID_SESSION_INITIATOR_CHECKED)) { flags = isIPMonitored(p, 0); flow_flags |= APPID_SESSION_INITIATOR_CHECKED; if (flags & IPFUNCS_HOSTS_IP) { flow_flags |= APPID_SESSION_INITIATOR_MONITORED; AppIdDebugHostInfo.monitorType = APPID_DEBUG_HOST_MONITOR2; } if (flags & IPFUNCS_USER_IP) flow_flags |= APPID_SESSION_DISCOVER_USER; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; if (isSpecialSessionMonitored(p)) { flow_flags |= APPID_SESSION_SPECIAL_MONITORED; } } if (!(flow_flags & APPID_SESSION_DISCOVER_APP) && !getAppIdFlag(session, APPID_SESSION_RESPONDER_CHECKED)) { flags = isIPMonitored(p, 1); if (flags & IPFUNCS_CHECKED) flow_flags |= APPID_SESSION_RESPONDER_CHECKED; if (flags & IPFUNCS_HOSTS_IP) flow_flags |= APPID_SESSION_RESPONDER_MONITORED; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; if (isSpecialSessionMonitored(p)) { flow_flags |= APPID_SESSION_SPECIAL_MONITORED; } } } else { if (!getAppIdFlag(session, APPID_SESSION_RESPONDER_CHECKED)) { flags = isIPMonitored(p, 0); flow_flags |= APPID_SESSION_RESPONDER_CHECKED; if (flags & IPFUNCS_HOSTS_IP) flow_flags |= APPID_SESSION_RESPONDER_MONITORED; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; if (isSpecialSessionMonitored(p)) { flow_flags |= APPID_SESSION_SPECIAL_MONITORED; } } if (!(flow_flags & APPID_SESSION_DISCOVER_APP) && !getAppIdFlag(session, APPID_SESSION_INITIATOR_CHECKED)) { flags = isIPMonitored(p, 1); if (flags & IPFUNCS_CHECKED) flow_flags |= APPID_SESSION_INITIATOR_CHECKED; if (flags & IPFUNCS_HOSTS_IP) { flow_flags |= APPID_SESSION_INITIATOR_MONITORED; AppIdDebugHostInfo.monitorType = APPID_DEBUG_HOST_MONITOR3; } if (flags & IPFUNCS_USER_IP) flow_flags |= APPID_SESSION_DISCOVER_USER; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; if (isSpecialSessionMonitored(p)) { flow_flags |= APPID_SESSION_SPECIAL_MONITORED; } } } } else if (checkPortExclusion(p, 0)) { flow_flags |= APPID_SESSION_INITIATOR_SEEN | APPID_SESSION_RESPONDER_SEEN | APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_RESPONDER_CHECKED; } else if (dir == APP_ID_FROM_INITIATOR) { flags = isIPMonitored(p, 0); flow_flags |= APPID_SESSION_INITIATOR_CHECKED; if (flags & IPFUNCS_HOSTS_IP) { flow_flags |= APPID_SESSION_INITIATOR_MONITORED; AppIdDebugHostInfo.monitorType = APPID_DEBUG_HOST_MONITOR4; } if (flags & IPFUNCS_USER_IP) flow_flags |= APPID_SESSION_DISCOVER_USER; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; if (!(flow_flags & APPID_SESSION_DISCOVER_APP)) { flags = isIPMonitored(p, 1); if (flags & IPFUNCS_CHECKED) flow_flags |= APPID_SESSION_RESPONDER_CHECKED; if (flags & IPFUNCS_HOSTS_IP) flow_flags |= APPID_SESSION_RESPONDER_MONITORED; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; } if (isSpecialSessionMonitored(p)) { flow_flags |= APPID_SESSION_SPECIAL_MONITORED; } } else { flags = isIPMonitored(p, 0); flow_flags |= APPID_SESSION_RESPONDER_CHECKED; if (flags & IPFUNCS_HOSTS_IP) flow_flags |= APPID_SESSION_RESPONDER_MONITORED; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; if (!(flow_flags & APPID_SESSION_DISCOVER_APP)) { flags = isIPMonitored(p, 1); if (flags & IPFUNCS_CHECKED) flow_flags |= APPID_SESSION_INITIATOR_CHECKED; if (flags & IPFUNCS_HOSTS_IP) { flow_flags |= APPID_SESSION_INITIATOR_MONITORED; AppIdDebugHostInfo.monitorType = APPID_DEBUG_HOST_MONITOR5; } if (flags & IPFUNCS_USER_IP) flow_flags |= APPID_SESSION_DISCOVER_USER; if (flags & IPFUNCS_APPLICATION) flow_flags |= APPID_SESSION_DISCOVER_APP; } if (isSpecialSessionMonitored(p)) { flow_flags |= APPID_SESSION_SPECIAL_MONITORED; } } return flow_flags; } void CheckDetectorCallback(const SFSnortPacket *p, tAppIdData *session, APPID_SESSION_DIRECTION direction, tAppId appId, const tAppIdConfig *pConfig) { AppInfoTableEntry *entry; int ret; if(!p || !session) return; if ((entry = appInfoEntryGet(appId, pConfig))) { if (entry->flags & APPINFO_FLAG_CLIENT_DETECTOR_CALLBACK) { if (entry->clntValidator) { if (entry->clntValidator->detectorContext) return; entry->clntValidator->detectorContext = true; ret = entry->clntValidator->detectorCallback(p->payload, p->payload_size, direction, session, p, entry->clntValidator->userData, pConfig); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s %s client detector callback returned %d\n", app_id_debug_session, entry->clntValidator->name ? entry->clntValidator->name : "UNKNOWN", ret); entry->clntValidator->detectorContext = false; } } if (entry->flags & APPINFO_FLAG_SERVICE_DETECTOR_CALLBACK) { if (entry->svrValidator) { if (entry->svrValidator->detectorContext) return; entry->svrValidator->detectorContext = true; ret = entry->svrValidator->detectorCallback(p->payload, p->payload_size, direction, session, p, entry->svrValidator->userdata, pConfig); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s %s service detector callback returned %d\n", app_id_debug_session, entry->svrValidator->name ? entry->svrValidator->name : "UNKNOWN", ret); entry->svrValidator->detectorContext = false; } } } } static inline bool svcTakingTooMuchTime(tAppIdData* session) { return ((session->initiatorPcketCountWithoutReply > appidStaticConfig-> max_packet_service_fail_ignore_bytes) || (session->initiatorPcketCountWithoutReply > appidStaticConfig->max_packet_before_service_fail && session->initiatorBytesWithoutServerReply > appidStaticConfig->max_bytes_before_service_fail)); } static inline void setServiceAppIdData(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *session, tAppId serviceAppId, char *vendor, char **version) { if (serviceAppId <= APP_ID_NONE) return; //in drambuie, 3rd party is in INIT state after processing first GET requuest. if (serviceAppId == APP_ID_HTTP) { if (session->clientServiceAppId == APP_ID_NONE) { session->clientServiceAppId = serviceAppId; } return; } if (session->serviceAppId != serviceAppId) { session->serviceAppId = serviceAppId; CheckDetectorCallback(p, session, direction, serviceAppId, appIdActiveConfigGet()); if (appidStaticConfig->instance_id) checkSandboxDetection(serviceAppId); /* Clear out previous values of vendor & version */ if (session->serviceVendor) { free(session->serviceVendor); session->serviceVendor = NULL; } if (session->serviceVersion) { free(session->serviceVersion); session->serviceVersion = NULL; } if (vendor) session->serviceVendor = vendor; if (version && *version) { session->serviceVersion = *version; *version = NULL; } } else { if (vendor || version) { /* Clear previous values */ if (session->serviceVendor) free(session->serviceVendor); if (session->serviceVersion) free(session->serviceVersion); /* set vendor */ if (vendor) session->serviceVendor = vendor; else session->serviceVendor = NULL; /* set version */ if (version && *version) { session->serviceVersion = *version; *version = NULL; } else session->serviceVersion = NULL; } } } static inline void setClientAppIdData(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *session, tAppId clientAppId, char **version) { tAppIdConfig *pConfig = appIdActiveConfigGet(); if (clientAppId <= APP_ID_NONE || clientAppId == APP_ID_HTTP) { if (version && *version) { free(*version); *version = NULL; } return; } if (session->clientAppId != clientAppId) { unsigned prev_priority = appInfoEntryPriorityGet(session->clientAppId, pConfig); unsigned curr_priority = appInfoEntryPriorityGet(clientAppId, pConfig) ; if (appidStaticConfig->instance_id) checkSandboxDetection(clientAppId); if ((session->clientAppId) && (prev_priority > curr_priority )) { if (version && *version) { free(*version); *version = NULL; } return; } session->clientAppId = clientAppId; CheckDetectorCallback(p, session, direction, clientAppId, pConfig); if (session->clientVersion) free(session->clientVersion); if (version && *version) { session->clientVersion = *version; *version = NULL; } else session->clientVersion = NULL; } else if (version && *version) { if (session->clientVersion) free(session->clientVersion); session->clientVersion = *version; *version = NULL; } } static inline void setReferredPayloadAppIdData(tAppIdData *session, tAppId referredPayloadAppId) { if (referredPayloadAppId <= APP_ID_NONE) return; if (session->referredPayloadAppId != referredPayloadAppId) { if (appidStaticConfig->instance_id) checkSandboxDetection(referredPayloadAppId); session->referredPayloadAppId = referredPayloadAppId; } } static inline void setPayloadAppIdData(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *session, tAppId payloadAppId, char **version) { tAppIdConfig *pConfig = appIdActiveConfigGet(); if (payloadAppId <= APP_ID_NONE) return; if (session->payloadAppId != payloadAppId) { unsigned prev_priority = appInfoEntryPriorityGet(session->payloadAppId, pConfig); unsigned curr_priority = appInfoEntryPriorityGet(payloadAppId, pConfig); if (appidStaticConfig->instance_id) checkSandboxDetection(payloadAppId); if ((session->payloadAppId ) && (prev_priority > curr_priority )) return; session->payloadAppId = payloadAppId; CheckDetectorCallback(p, session, direction, payloadAppId, pConfig); if (session->payloadVersion) free(session->payloadVersion); if (version && *version) { session->payloadVersion = *version; *version = NULL; } else session->payloadVersion = NULL; } else if (version && *version) { if (session->payloadVersion) free(session->payloadVersion); session->payloadVersion = *version; *version = NULL; } } static inline void setTPAppIdData(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *session, tAppId tpAppId) { tAppIdConfig *pConfig = appIdActiveConfigGet(); if (tpAppId <= APP_ID_NONE || !session) return; if (session->tpAppId != tpAppId) { session->tpAppId = tpAppId; CheckDetectorCallback(p, session, direction, tpAppId, pConfig); } } static inline void setTPPayloadAppIdData(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *session, tAppId tpPayloadAppId) { tAppIdConfig *pConfig = appIdActiveConfigGet(); if (tpPayloadAppId <= APP_ID_NONE || !session) return; if (session->tpPayloadAppId != tpPayloadAppId) { session->tpPayloadAppId = tpPayloadAppId; CheckDetectorCallback(p, session, direction, tpPayloadAppId, pConfig); } } static inline void clearSessionAppIdData(tAppIdData *session) { session->payloadAppId = APP_ID_UNKNOWN; session->serviceAppId = APP_ID_UNKNOWN; session->tpPayloadAppId = APP_ID_UNKNOWN; session->tpAppId = APP_ID_UNKNOWN; if (session->payloadVersion) { free(session->payloadVersion); session->payloadVersion = NULL; } if (session->serviceVendor) { free(session->serviceVendor); session->serviceVendor = NULL; } if (session->serviceVersion) { free(session->serviceVersion); session->serviceVersion = NULL; } if (session->clientVersion) { free(session->clientVersion); session->clientVersion = NULL; } if (session->tsession) { appTlsSessionDataFree(session->tsession); session->tsession = NULL; } if (session->hsession) { appHttpSessionDataFree(session->hsession); session->hsession = NULL; } if (session->dsession) { appDNSSessionDataFree(session->dsession); session->dsession = NULL; } if (thirdparty_appid_module) thirdparty_appid_module->session_delete(session->tpsession, 1); } static inline int initial_CHP_sweep (char ** chp_buffers, uint16_t * chp_buffer_lengths, MatchedCHPAction **ppmatches, tAppIdData *session, const tDetectorHttpConfig *pHttpConfig) { CHPApp* cah = NULL; int longest = 0; int i; httpSession *hsession; int scanKeyFoundSomething=0; CHPMatchTally *pTally = NULL; // scanKeyCHP allocates a pointer, but we free it when ready hsession = session->hsession; for (i = 0; i <= MAX_KEY_PATTERN; i++) { ppmatches[i] = NULL; if (chp_buffers[i] && chp_buffer_lengths[i] && scanKeyCHP((PatternType)i, chp_buffers[i], chp_buffer_lengths[i], &pTally, &ppmatches[i], pHttpConfig)) scanKeyFoundSomething=1; } if (!scanKeyFoundSomething) { if (pTally) free(pTally); for (i = 0; i <= MAX_KEY_PATTERN; i++) { if (ppmatches[i]) { FreeMatchedCHPActions(ppmatches[i]); ppmatches[i] = NULL; } } return 0; } for (i = 0; i < pTally->in_use_elements; i++) { // Only those items which have had their key_pattern_countdown field reduced to zero are a full match if (pTally->item[i].key_pattern_countdown) continue; if (longest < pTally->item[i].key_pattern_length_sum) { // We've found a new longest pattern set longest = pTally->item[i].key_pattern_length_sum; cah = pTally->item[i].chpapp; } } // either we have a candidate or we don't so we can free the tally structure either way. free(pTally); if (cah == NULL) { // We were planning to pass along the content of ppmatches to the second phase and let // them be freed inside scanCHP, but we have no candidate so we free here for (i = 0; i <= MAX_KEY_PATTERN; i++) { if (ppmatches[i]) { FreeMatchedCHPActions(ppmatches[i]); ppmatches[i] = NULL; } } return 0; } /****************************************************************/ /* candidate has been chosen and it is pointed to by cah */ /* we will preserve any match sets until the calls to scanCHP() */ /****************************************************************/ for (i = 0; i < NUMBER_OF_PTYPES; i++) { ptype_scan_counts[i] = cah->ptype_scan_counts[i]; hsession->ptype_req_counts[i] = cah->ptype_req_counts[i]; if (i > 3 && !cah->ptype_scan_counts[i] && !getAppIdFlag(session, APPID_SESSION_SPDY_SESSION)) { clearAppIdFlag(session, APPID_SESSION_CHP_INSPECTING); if (thirdparty_appid_module) thirdparty_appid_module->session_attr_clear(session->tpsession, TP_ATTR_CONTINUE_MONITORING); } } hsession->chp_candidate = cah->appIdInstance; hsession->app_type_flags = cah->app_type_flags; hsession->num_matches = cah->num_matches; hsession->num_scans = cah->num_scans; if (thirdparty_appid_module) { if ((ptype_scan_counts[CONTENT_TYPE_PT])) thirdparty_appid_module->session_attr_set(session->tpsession, TP_ATTR_COPY_RESPONSE_CONTENT); else thirdparty_appid_module->session_attr_clear(session->tpsession, TP_ATTR_COPY_RESPONSE_CONTENT); if ((ptype_scan_counts[LOCATION_PT])) thirdparty_appid_module->session_attr_set(session->tpsession, TP_ATTR_COPY_RESPONSE_LOCATION); else thirdparty_appid_module->session_attr_clear(session->tpsession, TP_ATTR_COPY_RESPONSE_LOCATION); if ((ptype_scan_counts[BODY_PT])) thirdparty_appid_module->session_attr_set(session->tpsession, TP_ATTR_COPY_RESPONSE_BODY); else thirdparty_appid_module->session_attr_clear(session->tpsession, TP_ATTR_COPY_RESPONSE_BODY); } return 1; } static char *httpFieldName[ NUMBER_OF_PTYPES ] = // for use in debug messages { "useragent", "host", "referer", "uri", "cookie", "req_body", "content_type", "location", "body", }; static inline void processCHP(tAppIdData *session, char **version, SFSnortPacket *p, APPID_SESSION_DIRECTION direction, const tAppIdConfig *pConfig) { int i; int found_in_buffer = 0; char *user = NULL; tAppId chp_final; tAppId ret = 0; httpSession *http_session = session->hsession; char *chp_buffers[NUMBER_OF_PTYPES] = { http_session->useragent, http_session->host, http_session->referer, http_session->uri, http_session->cookie, http_session->req_body, http_session->content_type, http_session->location, http_session->body, }; uint16_t chp_buffer_lengths[NUMBER_OF_PTYPES] = { http_session->useragent_buflen, http_session->host_buflen, http_session->referer_buflen, http_session->uri_buflen, http_session->cookie_buflen, http_session->req_body_buflen, http_session->content_type_buflen, http_session->location_buflen, http_session->body_buflen, }; char *chp_rewritten[NUMBER_OF_PTYPES] = { NULL,NULL,NULL, NULL,NULL,NULL, NULL,NULL,NULL }; MatchedCHPAction *chp_matches[NUMBER_OF_PTYPES] = { NULL,NULL,NULL, NULL,NULL,NULL, NULL,NULL,NULL }; if (http_session->chp_hold_flow) http_session->chp_finished = 0; if (!http_session->chp_candidate) { // remove artifacts from previous matches before we start again. if (http_session->new_field_contents) { for (i = 0; i < NUMBER_OF_PTYPES; i++) { if (http_session->new_field[i]) { free(http_session->new_field[i]); http_session->new_field[i] = NULL; } } } if (!initial_CHP_sweep(chp_buffers, chp_buffer_lengths, chp_matches, session, &pConfig->detectorHttpConfig)) http_session->chp_finished = 1; // this is a failure case. } if (!http_session->chp_finished && http_session->chp_candidate) { for (i = 0; i < NUMBER_OF_PTYPES; i++) { if (!ptype_scan_counts[i]) continue; // Do scans and check results if (chp_buffers[i] && chp_buffer_lengths[i]) { found_in_buffer = 0; ret = scanCHP((PatternType)i, chp_buffers[i], chp_buffer_lengths[i], chp_matches[i], version, &user, &chp_rewritten[i], &found_in_buffer, http_session, p, &pConfig->detectorHttpConfig); chp_matches[i] = NULL; // freed by scanCHP() http_session->total_found += found_in_buffer; if (!ret || found_in_buffer < http_session->ptype_req_counts[i]) { // No match at all or the required matches for the field was NOT made if (!http_session->num_matches) { // num_matches == 0 means: all must succeed // give up early http_session->chp_candidate = 0; break; } } } else { // No buffer or empty if (!http_session->num_matches) { // We had a pattern(s) and no buffer to look in. // num_matches == 0 means: all must succeed // give up early http_session->chp_candidate = 0; break; } } // Decrement the expected scan count toward 0. ptype_scan_counts[i] = 0; http_session->num_scans--; // if we have reached the end of the list of scans (which have something to do), then num_scans == 0 if (http_session->num_scans == 0) { // we finished the last scan // either the num_matches value was zero and we failed early-on or we need to check for the min. if (http_session->num_matches && http_session->total_found < http_session->num_matches) { // There was a minimum scans match count (num_matches != 0) // And we did not reach that minimum http_session->chp_candidate = 0; break; } // All required matches were met. http_session->chp_finished = 1; break; } } for (i = 0; i < NUMBER_OF_PTYPES; i++) { if (chp_matches[i]) // free leftover matches { FreeMatchedCHPActions(chp_matches[i]); chp_matches[i] = NULL; } } if (!http_session->chp_candidate) { http_session->chp_finished = 1; if (*version) { free(*version); *version = NULL; } if (user) { free(user); user = NULL; } for (i = 0; i < NUMBER_OF_PTYPES; i++) { if (NULL != chp_rewritten[i]) { free(chp_rewritten[i]); chp_rewritten[i] = NULL; } } memset(ptype_scan_counts, 0, 7 * sizeof(ptype_scan_counts[0])); // Make it possible for other detectors to run. http_session->skip_simple_detect = false; return; } if (http_session->chp_candidate && http_session->chp_finished) { chp_final = http_session->chp_alt_candidate ? http_session->chp_alt_candidate : CHP_APPIDINSTANCE_TO_ID(http_session->chp_candidate); if (http_session->app_type_flags & APP_TYPE_SERVICE) { setServiceAppIdData(p, direction, session, chp_final, NULL, version); } if (http_session->app_type_flags & APP_TYPE_CLIENT) { setClientAppIdData(p, direction, session, chp_final, version); } if (http_session->app_type_flags & APP_TYPE_PAYLOAD) { setPayloadAppIdData(p, direction, session, chp_final, version); } if (http_session->fflow && http_session->fflow->flow_prepared) { finalizeFflow(http_session->fflow, http_session->app_type_flags, (http_session->fflow->appId ? http_session->fflow->appId : chp_final), p); _dpd.snortFree(http_session->fflow, sizeof(*http_session->fflow), PP_APP_ID, PP_MEM_CATEGORY_SESSION); http_session->fflow = NULL; } if (*version) *version = NULL; if (user) { session->username = user; user = NULL; if (http_session->app_type_flags & APP_TYPE_SERVICE) session->usernameService = chp_final; else session->usernameService = session->serviceAppId; setAppIdFlag(session, APPID_SESSION_LOGIN_SUCCEEDED); } for (i = 0; i < NUMBER_OF_PTYPES; i++) { if (NULL != chp_rewritten[i]) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s rewritten %s: %s\n", app_id_debug_session, httpFieldName[i], chp_rewritten[i]); if (http_session->new_field[i]) free(http_session->new_field[i]); http_session->new_field[i] = chp_rewritten[i]; http_session->new_field_contents = true; chp_rewritten[i] = NULL; } } http_session->chp_candidate = 0; //if we're doing safesearch rewrites, we want to continue to hold the flow if (!http_session->get_offsets_from_rebuilt) http_session->chp_hold_flow = 0; session->scan_flags &= ~SCAN_HTTP_VIA_FLAG; session->scan_flags &= ~SCAN_HTTP_USER_AGENT_FLAG; session->scan_flags &= ~SCAN_HTTP_HOST_URL_FLAG; memset(ptype_scan_counts, 0, 7 * sizeof(ptype_scan_counts[0])); } else /* if we have a candidate, but we're not finished */ { if (user) { free(user); user = NULL; } for (i = 0; i < NUMBER_OF_PTYPES; i++) { if (NULL != chp_rewritten[i]) { free(chp_rewritten[i]); chp_rewritten[i] = NULL; } } } } } static inline bool payloadAppIdIsSet(tAppIdData *session) { return ( session->payloadAppId || session->tpPayloadAppId ); } static inline void clearMiscHttpFlags(tAppIdData *session) { if (!getAppIdFlag(session, APPID_SESSION_SPDY_SESSION)) { clearAppIdFlag(session, APPID_SESSION_CHP_INSPECTING); if (thirdparty_appid_module) thirdparty_appid_module->session_attr_clear(session->tpsession, TP_ATTR_CONTINUE_MONITORING); } } static int getHttpHostFromUri(char** host, const char* uri) { char buff[MAX_HOSTNAME + 1]; int len = 0; int offset = 0; if (!uri) return 0; memset(buff, 0, sizeof(buff)); if (!strncmp(uri, HTTP_PREFIX, HTTP_PREFIX_LEN)) { offset = HTTP_PREFIX_LEN; } else if (!strncmp(uri, HTTPS_PREFIX, HTTPS_PREFIX_LEN)) { offset = HTTPS_PREFIX_LEN; } while(len < MAX_HOSTNAME && uri[offset] != '/' && uri[offset] != '\0') buff[len++] = uri[offset++]; if (len) *host = strdup(buff); return len; } STATIC INLINE int processHTTPPacket(SFSnortPacket *p, tAppIdData *session, APPID_SESSION_DIRECTION direction, HttpParsedHeaders *const headers, const tAppIdConfig *pConfig) { #define RESPONSE_CODE_LENGTH 3 HeaderMatchedPatterns hmp; httpSession *http_session; int start, end, size; char *version = NULL; char *vendorVersion = NULL; char *vendor = NULL; tAppId serviceAppId = 0; tAppId clientAppId = 0; tAppId payloadAppId = 0; tAppId referredPayloadAppId = 0; char *host; char *url; char *useragent; char *referer; char *via; AppInfoTableEntry *entry; PROFILE_VARS; PREPROC_PROFILE_START(httpPerfStats); http_session = session->hsession; if (!http_session) { clearSessionAppIdData(session); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s attempt to process HTTP packet with no HTTP data\n", app_id_debug_session); PREPROC_PROFILE_END(httpPerfStats); return 0; } // For fragmented HTTP headers, do not process if none of the fields are set. // These fields will get set when the HTTP header is reassembled. if ((!http_session->useragent) && (!http_session->host) && (!http_session->referer) && (!http_session->uri)) { if (!http_session->skip_simple_detect) clearMiscHttpFlags(session); PREPROC_PROFILE_END(httpPerfStats); return 0; } if (direction == APP_ID_FROM_RESPONDER && !getAppIdFlag(session, APPID_SESSION_RESPONSE_CODE_CHECKED)) { if (http_session->response_code) { setAppIdFlag(session, APPID_SESSION_RESPONSE_CODE_CHECKED); if (http_session->response_code_buflen != RESPONSE_CODE_LENGTH) { /* received bad response code. Stop processing this session */ clearSessionAppIdData(session); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s bad http response code\n", app_id_debug_session); PREPROC_PROFILE_END(httpPerfStats); return 0; } } #if RESPONSE_CODE_PACKET_THRESHHOLD else if (++(http_session->response_code_packets) == RESPONSE_CODE_PACKET_THRESHHOLD) { setAppIdFlag(session, APPID_SESSION_RESPONSE_CODE_CHECKED); /* didn't receive response code in first X packets. Stop processing this session */ clearSessionAppIdData(session); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s no response code received\n", app_id_debug_session); PREPROC_PROFILE_END(httpPerfStats); return 0; } #endif } host = http_session->host; url = http_session->url; via = http_session->via; useragent = http_session->useragent; referer = http_session->referer; memset(&hmp, 0, sizeof(hmp)); if (session->serviceAppId == APP_ID_NONE) { session->serviceAppId = APP_ID_HTTP; if (appidStaticConfig->instance_id) checkSandboxDetection(APP_ID_HTTP); } if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s chp_finished %d chp_hold_flow %d\n", app_id_debug_session, http_session->chp_finished, http_session->chp_hold_flow); if (!http_session->chp_finished || http_session->chp_hold_flow) processCHP(session, &version, p, direction, pConfig); if (!http_session->skip_simple_detect) // false unless a match happened with a call to processCHP(). { if (!getAppIdFlag(session, APPID_SESSION_APP_REINSPECT)) { // Scan Server Header for Vendor & Version if ((thirdparty_appid_module && (session->scan_flags & SCAN_HTTP_VENDOR_FLAG) && session->hsession->server) || (!thirdparty_appid_module && getHTTPHeaderLocation(p->payload, p->payload_size, HTTP_ID_SERVER, &start, &end, &hmp, &pConfig->detectorHttpConfig) == 1)) { if (session->serviceAppId == APP_ID_NONE || session->serviceAppId == APP_ID_HTTP) { RNAServiceSubtype *subtype = NULL; RNAServiceSubtype **tmpSubtype; if (thirdparty_appid_module) getServerVendorVersion((uint8_t*)session->hsession->server, strlen(session->hsession->server), &vendorVersion, &vendor, &subtype); else getServerVendorVersion(p->payload + start, end - start, &vendorVersion, &vendor, &subtype); if (vendor || vendorVersion) { if (session->serviceVendor) { free(session->serviceVendor); session->serviceVendor = NULL; } if (session->serviceVersion) { free(session->serviceVersion); session->serviceVersion = NULL; } if (vendor) session->serviceVendor = vendor; if (vendorVersion) session->serviceVersion = vendorVersion; session->scan_flags &= ~SCAN_HTTP_VENDOR_FLAG; } if (subtype) { for (tmpSubtype = &session->subtype; *tmpSubtype; tmpSubtype = &(*tmpSubtype)->next); *tmpSubtype = subtype; } } } if (webdav_found(&hmp)) { if (app_id_debug_session_flag && payloadAppId > APP_ID_NONE && session->payloadAppId != payloadAppId) _dpd.logMsg("AppIdDbg %s payload is webdav\n", app_id_debug_session); setPayloadAppIdData(p, direction, session, APP_ID_WEBDAV, NULL); } // Scan User-Agent for Browser types or Skype if ((session->scan_flags & SCAN_HTTP_USER_AGENT_FLAG) && session->clientAppId <= APP_ID_NONE && useragent && http_session->useragent_buflen) { if (version) { free(version); version = NULL; } identifyUserAgent((uint8_t *)useragent, http_session->useragent_buflen, &serviceAppId, &clientAppId, &version, &pConfig->detectorHttpConfig); if (app_id_debug_session_flag && serviceAppId > APP_ID_NONE && serviceAppId != APP_ID_HTTP && session->serviceAppId != serviceAppId) _dpd.logMsg("AppIdDbg %s User Agent is service %d\n", app_id_debug_session, serviceAppId); setServiceAppIdData(p, direction, session, serviceAppId, NULL, NULL); if (app_id_debug_session_flag && clientAppId > APP_ID_NONE && clientAppId != APP_ID_HTTP && session->clientAppId != clientAppId) _dpd.logMsg("AppIdDbg %s User Agent is client %d\n", app_id_debug_session, clientAppId); setClientAppIdData(p, direction, session, clientAppId, &version); session->scan_flags &= ~SCAN_HTTP_USER_AGENT_FLAG; } /* Scan Via Header for squid */ if (!payloadAppIdIsSet(session) && (session->scan_flags & SCAN_HTTP_VIA_FLAG) && via && (size = strlen(via)) > 0) { if (version) { free(version); version = NULL; } payloadAppId = getAppidByViaPattern((uint8_t *)via, size, &version, &pConfig->detectorHttpConfig); if (app_id_debug_session_flag && payloadAppId > APP_ID_NONE && session->payloadAppId != payloadAppId) _dpd.logMsg("AppIdDbg %s VIA is payload %d\n", app_id_debug_session, payloadAppId); setPayloadAppIdData(p, direction, session, payloadAppId, NULL); session->scan_flags &= ~SCAN_HTTP_VIA_FLAG; } } /* Scan X-Working-With HTTP header */ if ((thirdparty_appid_module && (session->scan_flags & SCAN_HTTP_XWORKINGWITH_FLAG) && session->hsession->x_working_with) || (!thirdparty_appid_module && getHTTPHeaderLocation(p->payload, p->payload_size, HTTP_ID_X_WORKING_WITH, &start, &end, &hmp, &pConfig->detectorHttpConfig) == 1)) { tAppId appId; if (thirdparty_appid_module) appId = scan_header_x_working_with((uint8_t*)session->hsession->x_working_with, strlen(session->hsession->x_working_with), &version); else appId = scan_header_x_working_with(p->payload + start, end - start, &version); if (appId) { if (direction == APP_ID_FROM_INITIATOR) { if (app_id_debug_session_flag && clientAppId > APP_ID_NONE && clientAppId != APP_ID_HTTP && session->clientAppId != clientAppId) _dpd.logMsg("AppIdDbg %s X is client %d\n", app_id_debug_session, appId); setClientAppIdData(p, direction, session, appId, &version); } else { if (app_id_debug_session_flag && serviceAppId > APP_ID_NONE && serviceAppId != APP_ID_HTTP && session->serviceAppId != serviceAppId) _dpd.logMsg("AppIdDbg %s X is service %d\n", app_id_debug_session, appId); setServiceAppIdData(p, direction, session, appId, NULL, &version); } session->scan_flags &= ~SCAN_HTTP_XWORKINGWITH_FLAG; } } // Scan Content-Type Header for multimedia types and scan contents if ((thirdparty_appid_module && (session->scan_flags & SCAN_HTTP_CONTENT_TYPE_FLAG) && session->hsession->content_type && !payloadAppIdIsSet(session)) || (!thirdparty_appid_module && !payloadAppIdIsSet(session) && getHTTPHeaderLocation(p->payload, p->payload_size, HTTP_ID_CONTENT_TYPE, &start, &end, &hmp, &pConfig->detectorHttpConfig) == 1)) { if (thirdparty_appid_module) payloadAppId = getAppidByContentType((uint8_t*)session->hsession->content_type, strlen(session->hsession->content_type), &pConfig->detectorHttpConfig); else payloadAppId = getAppidByContentType(p->payload + start, end - start, &pConfig->detectorHttpConfig); if (app_id_debug_session_flag && payloadAppId > APP_ID_NONE && session->payloadAppId != payloadAppId) _dpd.logMsg("AppIdDbg %s Content-Type is payload %d\n", app_id_debug_session, payloadAppId); setPayloadAppIdData(p, direction, session, payloadAppId, NULL); session->scan_flags &= ~SCAN_HTTP_CONTENT_TYPE_FLAG; } if (session->scan_flags & SCAN_HTTP_HOST_URL_FLAG) { if (version) { free(version); version = NULL; } if (getAppIdFromUrl(host, url, &version, referer, &clientAppId, &serviceAppId, &payloadAppId, &referredPayloadAppId, 0, &pConfig->detectorHttpConfig) == 1) { // do not overwrite a previously-set client or service if (session->clientAppId <= APP_ID_NONE) { if (app_id_debug_session_flag && clientAppId > APP_ID_NONE && clientAppId != APP_ID_HTTP && session->clientAppId != clientAppId) _dpd.logMsg("AppIdDbg %s URL is client %d\n", app_id_debug_session, clientAppId); setClientAppIdData(p, direction, session, clientAppId, NULL); } if (session->serviceAppId <= APP_ID_NONE) { if (app_id_debug_session_flag && serviceAppId > APP_ID_NONE && serviceAppId != APP_ID_HTTP && session->serviceAppId != serviceAppId) _dpd.logMsg("AppIdDbg %s URL is service %d\n", app_id_debug_session, serviceAppId); setServiceAppIdData(p, direction, session, serviceAppId, NULL, NULL); } // DO overwrite a previously-set payload if (app_id_debug_session_flag && payloadAppId > APP_ID_NONE && session->payloadAppId != payloadAppId) _dpd.logMsg("AppIdDbg %s URL is payload %d\n", app_id_debug_session, payloadAppId); setPayloadAppIdData(p, direction, session, payloadAppId, &version); setReferredPayloadAppIdData(session, referredPayloadAppId); } session->scan_flags &= ~SCAN_HTTP_HOST_URL_FLAG; } if (session->clientAppId == APP_ID_APPLE_CORE_MEDIA) { if (session->tpPayloadAppId > APP_ID_NONE) { entry = appInfoEntryGet(session->tpPayloadAppId, pConfig); // only move tpPayloadAppId to client if its got a clientAppId if (entry && (entry->clientId > APP_ID_NONE)) { session->miscAppId = session->clientAppId; session->clientAppId = session->tpPayloadAppId; } } else if (session->payloadAppId > APP_ID_NONE) { entry = appInfoEntryGet(session->payloadAppId, pConfig); // only move payloadAppId to client if it has a clientAppid if (entry && (entry->clientId > APP_ID_NONE)) { session->miscAppId = session->clientAppId; session->clientAppId = session->payloadAppId; } } } clearMiscHttpFlags(session); } // end DON'T skip_simple_detect if (version) // We allocated this, but nobody used! { free(version); version = NULL; } PREPROC_PROFILE_END(httpPerfStats); return 0; } static inline void stopRnaServiceInspection(SFSnortPacket *p, tAppIdData* session, APPID_SESSION_DIRECTION direction) { sfaddr_t *ip; if (direction == APP_ID_FROM_INITIATOR) { ip = GET_DST_IP(p); session->service_ip = *ip; session->service_port = p->dst_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) session->serviceAsId = p->pkt_header->address_space_id_dst; #endif } else { ip = GET_SRC_IP(p); session->service_ip = *ip; session->service_port = p->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) session->serviceAsId = p->pkt_header->address_space_id_src; #endif } session->rnaServiceState = RNA_STATE_FINISHED; #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) session->carrierId = GET_SFOUTER_IPH_PROTOID(p, pkt_header); #endif if ((TPIsAppIdAvailable(session->tpsession) || getAppIdFlag(session, APPID_SESSION_NO_TPI)) && session->payloadAppId == APP_ID_NONE) session->payloadAppId = APP_ID_UNKNOWN; setAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED); clearAppIdFlag(session, APPID_SESSION_CONTINUE); #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %d stopping RNA service inspection\n", (unsigned)p->src_port, (unsigned)p->dst_port, IsTCP(p)? IPPROTO_TCP:IPPROTO_UDP); #endif } // Mock (derived?) function for _dpd.streamAPI->is_session_decrytped(). // It gets set at the beginning of fwAppIdSearch() in this file. // Note that _dpd.streamAPI->is_session_decrypted() gets called multiple times // in sfrna/firewall/src/spp_fw_engine.c. // change UNIT_TESTING and UNIT_TEST_FIRST_DECRYPTED_PACKET in appIdApi.h #ifdef UNIT_TEST_FIRST_DECRYPTED_PACKET bool is_session_decrypted_unit_test(void * ssn) { tAppIdData * session=getAppIdData(ssn); if (session && (session->session_packet_count >= UNIT_TEST_FIRST_DECRYPTED_PACKET)) return 1; return 0; } #endif static inline bool isSslDecryptionEnabled(tAppIdData *session) { if (getAppIdFlag(session, APPID_SESSION_DECRYPTED)) return 1; return _dpd.streamAPI->is_session_decrypted(session->ssn); } static inline void checkRestartSSLDetection(tAppIdData *session) { if (getAppIdFlag(session, APPID_SESSION_DECRYPTED)) return; if (!isSslDecryptionEnabled(session)) return; tAppId serviceAppId = pickServiceAppId(session); bool isSsl = isSslServiceAppId(serviceAppId); // A session could either: // 1. Start of as SSL - captured with isSsl flag, OR // 2. It could start of as a non-SSL session and later change to SSL. For example, FTP->FTPS. // In this case APPID_SESSION_ENCRYPTED flag is set by the protocol state machine. if (getAppIdFlag(session, APPID_SESSION_ENCRYPTED) || isSsl) { #ifdef DEBUG_FW_APPID fprintf(SF_DEBUG_FILE, "SSL decryption is available, restarting app Detection\n"); #endif setAppIdFlag(session, APPID_SESSION_DECRYPTED); session->encrypted.serviceAppId = serviceAppId; session->encrypted.payloadAppId = pickPayloadId(session); session->encrypted.clientAppId = pickClientAppId(session); session->encrypted.miscAppId = pickMiscAppId(session); session->encrypted.referredAppId = pickReferredPayloadId(session); appSharedReInitData(session); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s SSL decryption is available, restarting app Detection\n", app_id_debug_session); // APPID_SESSION_ENCRYPTED is set upon receiving a command which upgrades the session to SSL. // Next packet after the command will have encrypted traffic. // In the case of a session which starts as SSL, current packet itself is encrypted. Set the special flag // APPID_SESSION_APP_REINSPECT_SSL which allows reinspection of this packet. if (isSsl) setAppIdFlag(session, APPID_SESSION_APP_REINSPECT_SSL); } } static inline void checkRestartTunnelDetection(tAppIdData *session) { if ((session->hsession && session->hsession->is_tunnel) || (session->tpPayloadAppId == APP_ID_HTTP_TUNNEL && !getAppIdFlag(session, APPID_SESSION_HTTP_TUNNEL))) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Found HTTP Tunnel, restarting app Detection\n", app_id_debug_session); // Service if (session->serviceAppId == session->portServiceAppId) session->serviceAppId = APP_ID_NONE; session->portServiceAppId = APP_ID_NONE; if (session->serviceVendor) { free(session->serviceVendor); session->serviceVendor = NULL; } if (session->serviceVersion) { free(session->serviceVersion); session->serviceVersion = NULL; } IP_CLEAR(session->service_ip); session->service_port = 0; session->rnaServiceState = RNA_STATE_NONE; session->serviceData = NULL; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) session->serviceAsId = 0xFF; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) session->carrierId = 0; #endif AppIdFlowdataDeleteAllByMask(session, APPID_SESSION_DATA_SERVICE_MODSTATE_BIT); // Client session->rnaClientState = RNA_STATE_NONE; session->clientData = NULL; if (session->candidate_client_list) { sflist_free(session->candidate_client_list); session->candidate_client_list = NULL; } session->num_candidate_clients_tried = 0; AppIdFlowdataDeleteAllByMask(session, APPID_SESSION_DATA_CLIENT_MODSTATE_BIT); session->init_tpPackets = 0; session->resp_tpPackets = 0; session->scan_flags &= ~SCAN_HTTP_HOST_URL_FLAG; clearAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_CLIENT_DETECTED | APPID_SESSION_HTTP_SESSION | APPID_SESSION_HTTP_CONNECT); if (session->hsession && session->hsession->is_tunnel) { session->hsession->is_tunnel = false; if (appidStaticConfig->http_tunnel_detect == HTTP_TUNNEL_DETECT_RESTART_AND_RESET && thirdparty_appid_module) { thirdparty_appid_module->session_delete(session->tpsession, 0); session->tpsession = NULL; } } setAppIdFlag(session, APPID_SESSION_HTTP_TUNNEL); } return; } static inline void checkRestartAppDetection(tAppIdData *session) { checkRestartSSLDetection(session); checkRestartTunnelDetection(session); } static inline void updateEncryptedAppId( tAppIdData *session, tAppId serviceAppId) { switch (serviceAppId) { case APP_ID_HTTP: if (session->miscAppId == APP_ID_NSIIOPS || session->miscAppId == APP_ID_DDM_SSL || session->miscAppId == APP_ID_MSFT_GC_SSL || session->miscAppId == APP_ID_SF_APPLIANCE_MGMT) { break; } session->miscAppId = APP_ID_HTTPS; break; case APP_ID_SMTP: session->miscAppId = APP_ID_SMTPS; break; case APP_ID_NNTP: session->miscAppId = APP_ID_NNTPS; break; case APP_ID_IMAP: session->miscAppId = APP_ID_IMAPS; break; case APP_ID_SHELL: session->miscAppId = APP_ID_SSHELL; break; case APP_ID_LDAP: session->miscAppId = APP_ID_LDAPS; break; case APP_ID_FTP_DATA: session->miscAppId = APP_ID_FTPSDATA; break; case APP_ID_FTP: case APP_ID_FTP_CONTROL: session->miscAppId = APP_ID_FTPS; break; case APP_ID_TELNET: session->miscAppId = APP_ID_TELNET; break; case APP_ID_IRC: session->miscAppId = APP_ID_IRCS; break; case APP_ID_POP3: session->miscAppId = APP_ID_POP3S; break; default: break; } } /* * Desc: This function does AppId detection corresponding to the SSL params * The order of processing is: * Valid SNI: SNI->first_SAN->CN->OU * No SNI/Mismatched SNI: first_SAN->CN->OU */ static int scanSslParamsLookupAppId(tAppIdData *session, const char *serverName, bool isSniMismatch, const char *subjectAltName, const char *commonName, const char *orgName, tAppId *clientAppId, tAppId *payloadAppId) { int ret = 0; if ((session->scan_flags & SCAN_SSL_HOST_FLAG) && serverName && !isSniMismatch) { ret = ssl_scan_hostname((const uint8_t *)serverName, strlen(serverName), clientAppId, payloadAppId, &pAppidActiveConfig->serviceSslConfig); session->tsession->matched_tls_type = MATCHED_TLS_HOST; session->scan_flags &= ~SCAN_SSL_HOST_FLAG; } if (subjectAltName && (APP_ID_NONE == *clientAppId) && (APP_ID_NONE == *payloadAppId)) { ret = ssl_scan_hostname((const uint8_t *)subjectAltName, strlen(subjectAltName), clientAppId, payloadAppId, &pAppidActiveConfig->serviceSslConfig); session->tsession->matched_tls_type = MATCHED_TLS_FIRST_SAN; } if ((session->scan_flags & SCAN_SSL_CERTIFICATE_FLAG) && commonName && (APP_ID_NONE == *clientAppId) && (APP_ID_NONE == *payloadAppId)) { ret = ssl_scan_cname((const uint8_t *)commonName, strlen(commonName), clientAppId, payloadAppId, &pAppidActiveConfig->serviceSslConfig); session->tsession->matched_tls_type = MATCHED_TLS_CNAME; session->scan_flags &= ~SCAN_SSL_CERTIFICATE_FLAG; } if (orgName && (APP_ID_NONE == *clientAppId) && (APP_ID_NONE == *payloadAppId)) { ret = ssl_scan_cname((const uint8_t *)orgName, strlen(orgName), clientAppId, payloadAppId, &pAppidActiveConfig->serviceSslConfig); session->tsession->matched_tls_type = MATCHED_TLS_ORG_UNIT; } if ((APP_ID_NONE == *clientAppId) && (APP_ID_NONE == *payloadAppId)) session->tsession->matched_tls_type = MATCHED_TLS_NONE; return ret; } static inline void ExamineSslMetadata(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *session, tAppIdConfig *pConfig) { int ret = 0; tAppId clientAppId = 0; tAppId payloadAppId = 0; /* TLS params already scanned, skip scanning again */ if ((session->scan_flags & SCAN_CERTVIZ_ENABLED_FLAG)) return; ret = scanSslParamsLookupAppId(session, (const char*)session->tsession->tls_host, false, NULL, (const char*)session->tsession->tls_cname, (const char*)session->tsession->tls_orgUnit, &clientAppId, &payloadAppId); if (session->clientAppId == APP_ID_NONE || session->clientAppId == APP_ID_SSL_CLIENT) setClientAppIdData(p, direction, session, clientAppId, NULL); setPayloadAppIdData(p, direction, session, payloadAppId, NULL); setSSLSquelch(p, ret, (ret == 1 ? payloadAppId : clientAppId)); if (session->tsession->tls_orgUnit) { free(session->tsession->tls_orgUnit); session->tsession->tls_orgUnit = NULL; } if (session->tsession->tls_handshake_done && session->payloadAppId == APP_ID_NONE) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s End of SSL/TLS handshake detected with no payloadAppId, so setting to unknown\n", app_id_debug_session); session->payloadAppId = APP_ID_UNKNOWN; } } static inline int RunClientDetectors(tAppIdData *session, SFSnortPacket *p, int direction, tAppIdConfig *pConfig) { int ret = CLIENT_APP_INPROCESS; const struct RNAClientAppModule *tmpClientData = session->clientData; if (tmpClientData != NULL) { ret = tmpClientData->validate(p->payload, p->payload_size, direction, session, p, tmpClientData->userData, pConfig); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s %s client detector returned %d\n", app_id_debug_session, tmpClientData->name ? tmpClientData->name:"UNKNOWN", ret); } else { SF_LIST * tmpCandidateClientList = session->candidate_client_list; if ( (tmpCandidateClientList != NULL) && (sflist_count(tmpCandidateClientList) > 0) ) { SF_LNODE *node; tRNAClientAppModule *client; ret = CLIENT_APP_INPROCESS; node = sflist_first_node(tmpCandidateClientList); while (node != NULL) { int validator_result; SF_LNODE *node_tmp; client = (tRNAClientAppModule*)SFLIST_NODE_TO_DATA(node); validator_result = client->validate(p->payload, p->payload_size, direction, session, p, client->userData, pConfig); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s %s client detector returned %d\n", app_id_debug_session, client->name ? client->name:"UNKNOWN", validator_result); if (validator_result == CLIENT_APP_SUCCESS) { ret = CLIENT_APP_SUCCESS; session->clientData = client; sflist_free(tmpCandidateClientList); session->candidate_client_list = NULL; break; /* done */ } node_tmp = node; node = sflist_next_node(tmpCandidateClientList); if (validator_result != CLIENT_APP_INPROCESS) /* fail */ { sflist_remove_node(tmpCandidateClientList, node_tmp); } } } } return ret; } static inline void synchAppIdWithSnortId(tAppId newAppId, SFSnortPacket *p, tAppIdData *session, tAppIdConfig *pConfig) { if (newAppId > APP_ID_NONE && newAppId < SF_APPID_MAX) { AppInfoTableEntry *entry; // Certain AppIds are not useful to identifying snort preprocessor choices switch (newAppId) { case APP_ID_FTPS: case APP_ID_FTPSDATA: // These all are variants of HTTPS case APP_ID_DDM_SSL: case APP_ID_MSFT_GC_SSL: case APP_ID_NSIIOPS: case APP_ID_SF_APPLIANCE_MGMT: case APP_ID_HTTPS: case APP_ID_IMAPS: case APP_ID_IRCS: case APP_ID_LDAPS: case APP_ID_NNTPS: case APP_ID_POP3S: case APP_ID_SMTPS: case APP_ID_SSHELL: case APP_ID_TELNETS: return; case APP_ID_HTTP: if (session->is_http2) newAppId = APP_ID_HTTP2; break; default: break; } if ((entry = pAppidActiveConfig->AppInfoTable[newAppId]) != NULL) { register int16_t tempSnortId = entry->snortId; // A particular APP_ID_xxx may not be assigned a service_snort_key: value // in the rna_app.yaml file entry; so ignore the tempSnortId == 0 case. // Then if the value is different call the api. if ((tempSnortId != 0 || (tempSnortId = (newAppId == APP_ID_HTTP2) ? snortId_for_http2 : 0)) && tempSnortId != session->snortId) { session->snortId = tempSnortId; // remember the most recent change // inform Snort so that other preprocessors can be turned on/off if (app_id_debug_session_flag) if (tempSnortId == snortId_for_http2) _dpd.logMsg("AppIdDbg %s Telling Snort that it's HTTP/2\n", app_id_debug_session); #ifdef TARGET_BASED _dpd.sessionAPI->set_application_protocol_id(p->stream_session, tempSnortId); #endif p->application_protocol_ordinal = tempSnortId; } } } } static inline void checkTerminateTpModule(uint16_t tpPktCount, tAppIdData *session) { if ((tpPktCount >= appidStaticConfig->max_tp_flow_depth) || (getAppIdFlag(session, APPID_SESSION_HTTP_SESSION | APPID_SESSION_APP_REINSPECT) == (APPID_SESSION_HTTP_SESSION | APPID_SESSION_APP_REINSPECT) && session->hsession && session->hsession->uri && (!session->hsession->chp_candidate || session->hsession->chp_finished))) { if (session->tpAppId == APP_ID_NONE) session->tpAppId = APP_ID_UNKNOWN; if (session->rnaServiceState == RNA_STATE_FINISHED && session->payloadAppId == APP_ID_NONE) session->payloadAppId = APP_ID_UNKNOWN; if (thirdparty_appid_module) thirdparty_appid_module->session_delete(session->tpsession, 1); } } //#define DEBUG_PACKETS #ifdef DEBUG_PACKETS #define printSnortPacket( SFSnortPacket_ptr ) debug_printSnortPacket(SFSnortPacket_ptr) #define CHAR_DUMP_WIDTH 60 static inline void debug_printSnortPacket (SFSnortPacket *p) { if (app_id_debug_flag) { char *tweakedPayload; char *hexPayload; _dpd.logMsg("AppIdDbg \n"); _dpd.logMsg("AppIdDbg ------------------------------------------------\n"); _dpd.logMsg("AppIdDbg \n"); if (p->payload != NULL && p->payload_size) { tweakedPayload= (char *)malloc((CHAR_DUMP_WIDTH*2)+1); // room for hex if (tweakedPayload) { int j; int i; _dpd.logMsg("AppIdDbg payload: (%d chars per line)\n",CHAR_DUMP_WIDTH); for (j=0; jpayload_size; j+=CHAR_DUMP_WIDTH) { for (i=j; ipayload_size && i<(j+CHAR_DUMP_WIDTH); i++) { if((int)p->payload[i] >= 32 && (int)p->payload[i] <=126) tweakedPayload[i-j] = p->payload[i]; else tweakedPayload[i-j] = '.'; } tweakedPayload[i-j] = '\0'; _dpd.logMsg("AppIdDbg %s\n", tweakedPayload); } //#define DUMP_IN_HEX #ifdef DUMP_IN_HEX _dpd.logMsg("AppIdDbg HEX payload: (%d chars per line)\n",CHAR_DUMP_WIDTH); for (j=0; jpayload_size; j+=CHAR_DUMP_WIDTH) { for (i=j; ipayload_size && i<(j+CHAR_DUMP_WIDTH); i++) { sprintf(&tweakedPayload[(i-j)*2], "%02x", (p->payload)[i]); } // terminating '\0' provided by sprintf() _dpd.logMsg("AppIdDbg %s\n", tweakedPayload); } #endif free(tweakedPayload); tweakedPayload = NULL; } else { DynamicPreprocessorFatalMessage("debug_printSnortPacket: " "failed to allocate memory for tweakedPayload\n"); } } if (p->stream_session) { _dpd.logMsg("AppIdDbg \nAppIdDbg for p->stream_session=%p is_session_decrypted=%d direction=%d\n", p->stream_session, _dpd.streamAPI->is_session_decrypted(p->stream_session), _dpd.sessionAPI->get_ignore_direction(p->stream_session)); } _dpd.logMsg("AppIdDbg src_port: %d\n", p->src_port); _dpd.logMsg("AppIdDbg dst_port: %d\n", p->dst_port); _dpd.logMsg("AppIdDbg orig_src_port: %d\n", p->orig_src_port); _dpd.logMsg("AppIdDbg rig_dst_port: %d\n", p->orig_dst_port); _dpd.logMsg("AppIdDbg payloadsize: %d\n", p->payload_size); if ((p->flags) & 0x00000080) { _dpd.logMsg("AppIdDbg direction: client\n"); } else if ((p->flags) & 0x00000040) { _dpd.logMsg("AppIdDbg direction: server\n"); } else { _dpd.logMsg("AppIdDbg direction: unknown\n"); } if ((p->flags) & 0x00000001) _dpd.logMsg("AppIdDbg A rebuilt fragment\n"); if ((p->flags) & 0x00000002) _dpd.logMsg("AppIdDbg A rebuilt stream\n"); if ((p->flags) & 0x00000004) _dpd.logMsg("AppIdDbg From an unestablished stream and we've only seen traffic in one direction\n"); if ((p->flags) & 0x00000008) _dpd.logMsg("AppIdDbg From an established stream\n"); if ((p->flags) & 0x00000010) _dpd.logMsg("AppIdDbg this packet has been queued for stream reassembly\n"); if ((p->flags) & 0x00000020) _dpd.logMsg("AppIdDbg packet completes the 3 way handshake\n"); if ((p->flags) & 0x00000040) _dpd.logMsg("AppIdDbg packet come from server side of a connection(tcp)\n"); if ((p->flags) & 0x00000080) _dpd.logMsg("AppIdDbg packet come from client side of a connection(tcp)\n"); if ((p->flags) & 0x00000100) _dpd.logMsg("AppIdDbg start of PDU\n"); if ((p->flags) & 0x00000200) _dpd.logMsg("AppIdDbg end of PDU\n"); if ((p->flags) & 0x00800000) _dpd.logMsg("AppIdDbg packet has new size\n"); } } #else #define printSnortPacket( SFSnortPacket_ptr ) #endif // ============================================ // Protocol and Direction Determination Section // ============================================ // The getIPn_direction() functions are intended to return the direction for // the protocols for the respective IP4 and IP6 headers... // Except IPPROTO_TCP and IPPROTO_UDP which are handled within a // Snort API call in appDetermineProtocol(), below. static inline APPID_SESSION_DIRECTION getIP4_direction(SFSnortPacket *p) { switch (p->ip4h->ip_proto) { case IPPROTO_ICMP: if (p->icmp_header) { switch (p->icmp_header->type) { case 4: case 5: case 8: case 13: case 15: case 17: // It is a request type return APP_ID_FROM_INITIATOR; default: break; } } break; default: break; } // no differentiation for this protocol; call it a responder // so that every packet will be checked. return APP_ID_FROM_RESPONDER; } static inline APPID_SESSION_DIRECTION getIP6_direction(SFSnortPacket *p) { switch (p->ip6h->next) { case IPPROTO_ICMPV6: if (p->icmp6h) { switch (p->icmp6h->type) { case 138: // only code 0 is a request. if (p->icmp6h->code == 0) return APP_ID_FROM_INITIATOR; break; case 128: case 130: case 133: case 135: // It is a request type return APP_ID_FROM_INITIATOR; default: break; } } break; default: break; } // no differentiation for this protocol; call it a responder // so that every packet will be checked. return APP_ID_FROM_RESPONDER; } static inline int appDetermineProtocol(SFSnortPacket *p, tAppIdData *session, uint8_t *protocolp, uint8_t *outer_protocol, APPID_SESSION_DIRECTION *directionp) { // 'session' pointer may be NULL. In which case the packet examination is required. // But if 'session' is not NULL we use values saved from the initial creation if (session) { sfaddr_t *ip; #ifdef DEBUG_APP_ID_SESSIONS #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (session->service_port == DEBUG_FW_APPID_PORT) #endif { char src_ip[INET6_ADDRSTRLEN]; char dst_ip[INET6_ADDRSTRLEN]; src_ip[0] = 0; ip = GET_SRC_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), src_ip, sizeof(src_ip)); dst_ip[0] = 0; ip = GET_DST_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), dst_ip, sizeof(dst_ip)); fprintf(SF_DEBUG_FILE, "AppId Session %p %p for %s-%u -> %s-%u %d\n", session, session->ssn, src_ip, (unsigned)p->src_port, dst_ip, (unsigned)p->dst_port, IsTCP(p) ? IPPROTO_TCP:IPPROTO_UDP); } #endif if (session->common.fsf_type.flow_type == APPID_SESSION_TYPE_IGNORE) return 0; // just ignore if (session->common.fsf_type.flow_type == APPID_SESSION_TYPE_NORMAL) { *protocolp = session->proto; session->ssn = p->stream_session; } else if (IsTCP(p)) *protocolp = IPPROTO_TCP; else *protocolp = IPPROTO_UDP; ip = GET_SRC_IP(p); if (session->common.initiator_port) *directionp = (session->common.initiator_port == p->src_port) ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; else *directionp = (memcmp(sfaddr_get_ip6_ptr(ip), &session->common.initiator_ip, sizeof(session->common.initiator_ip))) ? APP_ID_FROM_RESPONDER: APP_ID_FROM_INITIATOR ; } else { if (IsTCP(p)) { *protocolp = IPPROTO_TCP; *directionp = (_dpd.sessionAPI->get_packet_direction(p) & FLAG_FROM_CLIENT) ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; } else if (IsUDP(p)) { *protocolp = IPPROTO_UDP; *directionp = (_dpd.sessionAPI->get_packet_direction(p) & FLAG_FROM_CLIENT) ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; } else if (IsIP(p)) { *protocolp = GET_IPH_PROTO(p); if (p->outer_iph_api) { IP4Hdr *save_ip4h = p->ip4h; IP6Hdr *save_ip6h = p->ip6h; p->ip4h = &p->outer_ip4h; p->ip6h = &p->outer_ip6h; *outer_protocol = p->outer_iph_api->iph_ret_proto(p); p->ip4h = save_ip4h; p->ip6h = save_ip6h; } if (IS_IP4(p) && p->ip4h) *directionp = getIP4_direction(p); else if (p->ip6h) *directionp = getIP6_direction(p); else *directionp = APP_ID_FROM_RESPONDER; } else { // Neither IPv4 nor IPv6 - currently unsupported return 0; } } return 1; } static inline bool checkThirdPartyReinspect(const SFSnortPacket* p, tAppIdData* session) { return getAppIdFlag(session, APPID_SESSION_HTTP_SESSION) && !getAppIdFlag(session, APPID_SESSION_NO_TPI) && TPIsAppIdDone(session->tpsession) && p->payload_size; } static inline int getIpPortFromHttpTunnel(char *url, int url_len, tunnelDest **tunDest) { char *host = NULL, *host_start, *host_end, *url_end; char *portStr = NULL; uint16_t port = 0; int isIPv6 = 0, family; if (url_len <= 0 || !url || !tunDest) { return 1; } url_end = url + url_len - 1; host_start = url; if (url[0] == '[') { // IPv6 literal isIPv6 = 1; portStr = strchr(url, ']'); if (portStr && portStr < url_end) { if (*(++portStr) != ':') { portStr = NULL; } } } else if(isdigit(url[0])) // Checking the first character for a possible domain name. { portStr = strrchr(url, ':'); } else { return 1; } if (portStr && portStr < url_end ) { host_end = portStr; if (*(++portStr) != '\0') { char *end = NULL; long ret = strtol(portStr, &end, 10); if (end != portStr && *end == '\0' && ret >= 1 && ret <= PORT_MAX) { port = (uint16_t)ret; } } } if (port) { if (isIPv6) { // IPv6 is enclosed in square braces. Adjusting the pointers. host_start++; host_end--; } if (host_start <= host_end) { char tmp = *host_end; *host_end = '\0'; host = strdup(host_start); *host_end = tmp; } else return 1; } if (host) { tunnelDest *tDest = _dpd.snortAlloc(1, sizeof(*tDest), PP_APP_ID, PP_MEM_CATEGORY_SESSION); if (!tDest) { _dpd.errMsg("AppId: Unable to allocate memory for HTTP tunnel information\n"); free(host); return 1; } if (!isIPv6) { if (inet_pton(AF_INET, host, &(tDest->ip).ip.s6_addr32[3]) <= 0) { free(host); _dpd.snortFree(tDest, sizeof(*tDest), PP_APP_ID, PP_MEM_CATEGORY_SESSION); return 1; } (tDest->ip).ip.s6_addr32[0] = (tDest->ip).ip.s6_addr32[1] = 0; (tDest->ip).ip.s6_addr32[2] = ntohl(0x0000ffff); family = AF_INET; } else { if (inet_pton(AF_INET6, host, &(tDest->ip).ip) <= 0) { free(host); _dpd.snortFree(tDest, sizeof(*tDest), PP_APP_ID, PP_MEM_CATEGORY_SESSION); return 1; } family = AF_INET6; } (tDest->ip).family = family; tDest->port = port; *tunDest = tDest; free(host); } else { return 1; } return 0; } static int checkHostCache(SFSnortPacket *p, tAppIdData *session, sfaddr_t *ip, uint16_t port, uint8_t protocol, tAppIdConfig *pConfig) { bool checkStatic = false, checkDynamic = false; tHostPortVal *hv = NULL; if (!(session->scan_flags & SCAN_HOST_PORT_FLAG)) checkStatic = true; if (isHostCacheUpdated(session->hostCacheVersion)) { if ((session->session_packet_count % appidStaticConfig->host_port_app_cache_lookup_interval == 0) && session->session_packet_count <= appidStaticConfig->host_port_app_cache_lookup_range && appidStaticConfig->is_host_port_app_cache_runtime) checkDynamic = true; } if (!(checkStatic || checkDynamic)) return 0; if (checkStatic) { hv = hostPortAppCacheFind(ip, port, protocol, pConfig); session->scan_flags |= SCAN_HOST_PORT_FLAG; } if (!hv && checkDynamic) { hv = hostPortAppCacheDynamicFind(ip, port, protocol); updateHostCacheVersion(&(session->hostCacheVersion)); } if (hv) { switch (hv->type) { case APP_ID_TYPE_SERVICE: session->serviceAppId = hv->appId; synchAppIdWithSnortId(hv->appId, p, session, pConfig); session->rnaServiceState = RNA_STATE_FINISHED; session->rnaClientState = RNA_STATE_FINISHED; setAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED); if (thirdparty_appid_module) thirdparty_appid_module->session_delete(session->tpsession, 1); if (session->payloadAppId == APP_ID_NONE) session->payloadAppId = APP_ID_UNKNOWN; case APP_ID_TYPE_CLIENT: session->clientAppId = hv->appId; session->rnaClientState = RNA_STATE_FINISHED; break; case APP_ID_TYPE_PAYLOAD: session->payloadAppId = hv->appId; break; default: break; } setAppIdFlag(session, APPID_SESSION_HOST_CACHE_MATCHED); return 1; } return 0; } static inline bool isCheckHostCacheValid(tAppIdData* session, tAppId serviceAppId, tAppId clientAppId, tAppId payloadAppId, tAppId miscAppId) { bool isPayloadClientNone = (payloadAppId <= APP_ID_NONE && clientAppId <= APP_ID_NONE); bool isAppIdNone = isPayloadClientNone && (serviceAppId <= APP_ID_NONE || serviceAppId == APP_ID_UNKNOWN_UI || (appidStaticConfig->recheck_for_portservice_appid && serviceAppId == session->portServiceAppId)); bool isSslNone = appidStaticConfig->check_host_cache_unknown_ssl && getAppIdFlag(session, APPID_SESSION_SSL_SESSION) && !(session->tsession && session->tsession->tls_host && session->tsession->tls_cname); if(isAppIdNone || isSslNone || appidStaticConfig->check_host_port_app_cache) { return true; } return false; } void fwAppIdInit(void) { /* init globals for snortId compares etc. */ #ifdef TARGET_BASED snortId_for_unsynchronized = _dpd.addProtocolReference("unsynchronized"); snortId_for_ftp_data = _dpd.findProtocolReference("ftp-data"); snortId_for_http2 = _dpd.findProtocolReference("http2"); #endif snortInstance = _dpd.getSnortInstance(); } static inline tAppId processThirdParty(SFSnortPacket* p, tAppIdData* session, APPID_SESSION_DIRECTION direction, uint8_t protocol, bool* isTpAppidDiscoveryDone, tAppIdConfig *pConfig) { tAppId tpAppId = session->tpAppId; int tp_confidence; tAppId* tp_proto_list; ThirdPartyAppIDAttributeData* tp_attribute_data; sfaddr_t *ip; /*** Start of third-party processing. ***/ PROFILE_VARS; PREPROC_PROFILE_START(tpPerfStats); if (p->payload_size || appidStaticConfig->tp_allow_probes) { //restart inspection by 3rd party if (!session->tpReinspectByInitiator && (direction == APP_ID_FROM_INITIATOR) && checkThirdPartyReinspect(p, session)) { session->tpReinspectByInitiator = 1; //once per request setAppIdFlag(session, APPID_SESSION_APP_REINSPECT); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s 3rd party allow reinspect http\n", app_id_debug_session); session->init_tpPackets = 0; session->resp_tpPackets = 0; appHttpFieldClear(session->hsession); } if (!isTPProcessingDone(session)) { if (protocol != IPPROTO_TCP || (p->flags & FLAG_STREAM_ORDER_OK) || appidStaticConfig->tp_allow_probes) { PREPROC_PROFILE_START(tpLibPerfStats); if (!session->tpsession) { if (!(session->tpsession = thirdparty_appid_module->session_create())) DynamicPreprocessorFatalMessage("Could not allocate tAppIdData->tpsession data"); } printSnortPacket(p); // debug output of packet content thirdparty_appid_module->session_process(session->tpsession, p, direction, &tpAppId, &tp_confidence, &tp_proto_list, &tp_attribute_data); PREPROC_PROFILE_END(tpLibPerfStats); // First SSL decrypted packet is now being inspected. Reset the flag so that SSL decrypted traffic // gets processed like regular traffic from next packet onwards if (getAppIdFlag(session, APPID_SESSION_APP_REINSPECT_SSL)) clearAppIdFlag(session, APPID_SESSION_APP_REINSPECT_SSL); *isTpAppidDiscoveryDone = true; if (thirdparty_appid_module->session_state_get(session->tpsession) == TP_STATE_CLASSIFIED) clearAppIdFlag(session, APPID_SESSION_APP_REINSPECT); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s 3rd party returned %d\n", app_id_debug_session, tpAppId); #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u 3rd party returned %d\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol, tpAppId); #endif // For now, third party can detect HTTP/2 (w/o metadata) for // some cases. Treat it like HTTP w/ is_http2 flag set. if ((tpAppId == APP_ID_HTTP2) && (tp_confidence == 100)) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s 3rd party saw HTTP/2\n", app_id_debug_session); tpAppId = APP_ID_HTTP; session->is_http2 = true; } // if the third-party appId must be treated as a client, do it now uint32_t entryFlags = appInfoEntryFlags(tpAppId, pConfig); if (entryFlags & APPINFO_FLAG_TP_CLIENT) session->clientAppId = tpAppId; ProcessThirdPartyResults(p, direction, session, tp_confidence, tp_proto_list, tp_attribute_data); if ((entryFlags & APPINFO_FLAG_SSL_SQUELCH) && getAppIdFlag(session, APPID_SESSION_SSL_SESSION) && !(session->scan_flags & SCAN_SSL_HOST_FLAG)) { if (!(session->scan_flags & SCAN_SPOOFED_SNI_FLAG)) { setSSLSquelch(p, 1, tpAppId); } else { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Ignored 3rd party returned %d, SNI is spoofed\n", app_id_debug_session, tpAppId); } } if (entryFlags & APPINFO_FLAG_IGNORE) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s 3rd party ignored\n", app_id_debug_session); if (getAppIdFlag(session, APPID_SESSION_HTTP_SESSION)) tpAppId = APP_ID_HTTP; else if(getAppIdFlag(session, APPID_SESSION_SSL_SESSION)) tpAppId = APP_ID_SSL; else tpAppId = APP_ID_NONE; } } else { tpAppId = APP_ID_NONE; #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u Skipping ooo\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol); #endif } if (thirdparty_appid_module->session_state_get(session->tpsession) == TP_STATE_MONITORING) { thirdparty_appid_module->disable_flags(session->tpsession, TP_SESSION_FLAG_ATTRIBUTE | TP_SESSION_FLAG_TUNNELING | TP_SESSION_FLAG_FUTUREFLOW); } #ifdef TARGET_BASED if(tpAppId == APP_ID_SSL && (_dpd.sessionAPI->get_application_protocol_id(p->stream_session) == snortId_for_ftp_data)) { // If we see SSL on an FTP data channel set tpAppId back // to APP_ID_NONE so the FTP preprocessor picks up the flow. tpAppId = APP_ID_NONE; } #endif if (tpAppId > APP_ID_NONE && (!getAppIdFlag(session, APPID_SESSION_APP_REINSPECT) || session->payloadAppId > APP_ID_NONE)) { #ifdef TARGET_BASED tAppId snortAppId; #endif // if the packet is HTTP, then search for via pattern if (getAppIdFlag(session, APPID_SESSION_HTTP_SESSION) && session->hsession) { #ifdef TARGET_BASED snortAppId = APP_ID_HTTP; #endif //payload should never be APP_ID_HTTP if (tpAppId != APP_ID_HTTP) setTPPayloadAppIdData(p, direction, session, tpAppId); #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u tp identified http payload %d\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol, tpAppId); #endif session->tpAppId = APP_ID_HTTP; processHTTPPacket(p, session, direction, NULL, pConfig); if (TPIsAppIdAvailable(session->tpsession) && session->tpAppId == APP_ID_HTTP && !getAppIdFlag(session, APPID_SESSION_APP_REINSPECT)) { session->rnaClientState = RNA_STATE_FINISHED; setAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED | APPID_SESSION_SERVICE_DETECTED); session->rnaServiceState = RNA_STATE_FINISHED; clearAppIdFlag(session, APPID_SESSION_CONTINUE); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) session->carrierId = GET_SFOUTER_IPH_PROTOID(p, pkt_header); #endif if (direction == APP_ID_FROM_INITIATOR) { ip = GET_DST_IP(p); session->service_ip = *ip; session->service_port = p->dst_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) session->serviceAsId = p->pkt_header->address_space_id_dst; #endif } else { ip = GET_SRC_IP(p); session->service_ip = *ip; session->service_port = p->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) session->serviceAsId = p->pkt_header->address_space_id_src; #endif } } } else if (getAppIdFlag(session, APPID_SESSION_SSL_SESSION) && session->tsession) { ExamineSslMetadata(p, direction, session, pConfig); uint16_t serverPort; tAppId portAppId; serverPort = (direction == APP_ID_FROM_INITIATOR)? p->dst_port:p->src_port; portAppId = getSslServiceAppId(serverPort); if (tpAppId == APP_ID_SSL ) { tpAppId = portAppId; //SSL policy needs to determine IMAPS/POP3S etc before appId sees first server packet session->portServiceAppId = portAppId; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s SSL is service %d, portServiceAppId %d\n", app_id_debug_session, tpAppId, session->portServiceAppId); } else { if (!(session->scan_flags & SCAN_SPOOFED_SNI_FLAG)) { setTPPayloadAppIdData(p, direction, session, tpAppId); } else { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Ignoring 3rd party returned %d, SNI is spoofed\n", app_id_debug_session, tpAppId); } tpAppId = portAppId; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s SSL is %d\n", app_id_debug_session, tpAppId); } setTPAppIdData(p, direction, session, tpAppId); #ifdef TARGET_BASED snortAppId = APP_ID_SSL; #endif #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u tp identified ssl service %d\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol, tpAppId); #endif } else { //for non-http protocols, tp id is treated like serviceId #ifdef TARGET_BASED snortAppId = tpAppId; #endif #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u tp identified non-http service %d\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol, tpAppId); #endif setTPAppIdData(p, direction, session, tpAppId); } #ifdef TARGET_BASED synchAppIdWithSnortId(snortAppId, p, session, pConfig); #endif } else { if ((session->serviceAppId != APP_ID_ENIP && session->serviceAppId != APP_ID_CIP) && (protocol != IPPROTO_TCP || (p->flags & (FLAG_STREAM_ORDER_OK | FLAG_STREAM_ORDER_BAD)))) { if (direction == APP_ID_FROM_INITIATOR) { session->init_tpPackets++; checkTerminateTpModule(session->init_tpPackets, session); } else { session->resp_tpPackets++; checkTerminateTpModule(session->resp_tpPackets, session); } } } } else if (session->hsession && (direction == APP_ID_FROM_RESPONDER) && getAppIdFlag(session, APPID_SESSION_HTTP_CONNECT)) { if ((p->payload_size >= 13) && !strncasecmp((char *)p->payload, "HTTP/1.1 200 ", 13)) session->hsession->is_tunnel = true; } if (session->tpReinspectByInitiator && checkThirdPartyReinspect(p, session)) { if (*isTpAppidDiscoveryDone) clearAppIdFlag(session, APPID_SESSION_APP_REINSPECT); if (direction == APP_ID_FROM_RESPONDER) session->tpReinspectByInitiator = 0; //toggle at OK response } } PREPROC_PROFILE_END(tpPerfStats); /*** End of third-party processing. ***/ return tpAppId; } void fwAppIdSearch(SFSnortPacket *p) { tAppIdData *session; uint8_t protocol, outer_protocol = 0; APPID_SESSION_DIRECTION direction; tAppId tpAppId = 0; tAppId serviceAppId = 0; tAppId clientAppId = 0; tAppId payloadAppId = 0; tAppId miscAppId = 0; bool isTpAppidDiscoveryDone = false; uint64_t flow_flags; sfaddr_t *ip; uint16_t port; size_t size; #ifdef TARGET_BASED AppInfoTableEntry *entry; #endif tAppIdConfig *pConfig = appIdActiveConfigGet(); #ifdef UNIT_TEST_FIRST_DECRYPTED_PACKET if(app_id_raw_packet_count==0) _dpd.streamAPI->is_session_decrypted=is_session_decrypted_unit_test; #endif app_id_raw_packet_count++; if (!p->stream_session || (p->payload_size && !p->payload)) { app_id_ignored_packet_count++; return; } SetPacketRealTime(p->pkt_header->ts.tv_sec); session = appSharedGetData(p); if (!appDetermineProtocol(p, session, &protocol, &outer_protocol, &direction)) { // unsupported protocol or other ignore app_id_ignored_packet_count++; return; } if (pConfig->debugHostIp) { AppIdDebugHostInfo.session = session; AppIdDebugHostInfo.protocol = protocol; AppIdDebugHostInfo.direction = direction; if (session) { memcpy(&AppIdDebugHostInfo.initiatorIp, &session->common.initiator_ip, sizeof(session->common.initiator_ip)); AppIdDebugHostInfo.initiatorPort = session->common.initiator_port; AppIdDebugHostInfo.family = sfaddr_family(GET_SRC_IP(p)); } else { memset(&AppIdDebugHostInfo.initiatorIp, 0, sizeof(session->common.initiator_ip)); AppIdDebugHostInfo.initiatorPort = 0; AppIdDebugHostInfo.family = AF_INET; } AppIdDebugHostInfo.monitorType = APPID_DEBUG_HOST_NOT_MONITORED; } app_id_debug_session_flag = fwAppIdDebugCheck(p->stream_session, session, app_id_debug_flag, &app_id_debug_info, app_id_debug_session, direction); #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif { char sipstr[INET6_ADDRSTRLEN]; char dipstr[INET6_ADDRSTRLEN]; sipstr[0] = 0; ip = GET_SRC_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), sipstr, sizeof(sipstr)); dipstr[0] = 0; ip = GET_DST_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), dipstr, sizeof(dipstr)); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = GET_SFOUTER_IPH_PROTOID(p, pkt_header); #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t sAsId = p->pkt_header->address_space_id_src; uint16_t dAsId = p->pkt_header->address_space_id_dst; fprintf(SF_DEBUG_FILE, "%s-%u -> %s-%u AS %u-%u CID %u %u\n", sipstr, (unsigned)p->src_port, dipstr, (unsigned)p->dst_port, sAsId, dAsId, (unsigned)cid, (unsigned)protocol); #else fprintf(SF_DEBUG_FILE, "%s-%u -> %s-%u CID %u %u\n", sipstr, (unsigned)p->src_port, dipstr, (unsigned)p->dst_port, (unsigned)cid, (unsigned)protocol); #endif /* No Carrierid support*/ #else #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t sAsId = p->pkt_header->address_space_id_src; uint16_t dAsId = p->pkt_header->address_space_id_dst; fprintf(SF_DEBUG_FILE, "%s-%u -> %s-%u AS %u-%u %u\n", sipstr, (unsigned)p->src_port, dipstr, (unsigned)p->dst_port, sAsId, dAsId, (unsigned)protocol); #else fprintf(SF_DEBUG_FILE, "%s-%u -> %s-%u %u\n", sipstr, (unsigned)p->src_port, dipstr, (unsigned)p->dst_port, (unsigned)protocol); #endif #endif /*DumpHex(SF_DEBUG_FILE, p->payload, p->payload_size); */ } #endif if (protocol == IPPROTO_TCP) // HTTP is a subset of TCP { if (_dpd.streamAPI->is_session_http2(p->stream_session)) { if (session) session->is_http2 = true; if (!(p->flags & FLAG_REBUILT_STREAM)) { // For HTTP/2, we only want to look at the ones that are rebuilt from // Stream / HTTP Inspect as HTTP/1 packets. app_id_ignored_packet_count++; return; } } else // not HTTP/2 { if (p->flags & FLAG_REBUILT_STREAM && !_dpd.streamAPI->is_session_decrypted(p->stream_session)) { if (direction == APP_ID_FROM_INITIATOR && session && session->hsession && session->hsession->get_offsets_from_rebuilt) { httpGetNewOffsetsFromPacket(p, session->hsession, pConfig); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s offsets from rebuilt packet: uri: %u-%u cookie: %u-%u\n", app_id_debug_session, session->hsession->fieldOffset[REQ_URI_FID], session->hsession->fieldEndOffset[REQ_URI_FID], session->hsession->fieldOffset[REQ_COOKIE_FID], session->hsession->fieldEndOffset[REQ_COOKIE_FID]); } app_id_ignored_packet_count++; return; } } } // fwAppIdSearch() is a top-level function that is called by AppIdProcess(). // At this point, we know that we need to use the current active config - // pAppidActiveConfig. This function uses pAppidActiveConfig and passes it // to all the functions that need to look at AppId config. flow_flags = isSessionMonitored(p, direction, session); if (!(flow_flags & (APPID_SESSION_DISCOVER_APP | APPID_SESSION_SPECIAL_MONITORED))) { if (!session) { if ((flow_flags & APPID_SESSION_BIDIRECTIONAL_CHECKED) == APPID_SESSION_BIDIRECTIONAL_CHECKED) { static APPID_SESSION_STRUCT_FLAG ignore_fsf = {.flow_type = APPID_SESSION_TYPE_IGNORE}; _dpd.sessionAPI->set_application_data(p->stream_session, PP_APP_ID, &ignore_fsf, NULL); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s not monitored\n", app_id_debug_session); } else { tTmpAppIdData *tmp_session; if (tmp_app_id_free_list) { tmp_session = tmp_app_id_free_list; tmp_app_id_free_list = tmp_session->next; app_id_tmp_free_list_count--; } else if (!(tmp_session = _dpd.snortAlloc(1, sizeof(*tmp_session), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate tTmpAppIdData data"); tmp_session->common.fsf_type.flow_type = APPID_SESSION_TYPE_TMP; tmp_session->common.flags = flow_flags; ip = (direction == APP_ID_FROM_INITIATOR) ? GET_SRC_IP(p) : GET_DST_IP(p); sfaddr_copy_to_raw(&tmp_session->common.initiator_ip, ip); if ((protocol == IPPROTO_TCP || protocol == IPPROTO_UDP) && p->src_port != p->dst_port) tmp_session->common.initiator_port = (direction == APP_ID_FROM_INITIATOR) ? p->src_port : p->dst_port; else tmp_session->common.initiator_port = 0; tmp_session->common.policyId = appIdPolicyId; _dpd.sessionAPI->set_application_data(p->stream_session, PP_APP_ID, tmp_session, (void (*)(void*))appTmpSharedDataFree); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s unknown monitoring\n", app_id_debug_session); } } else { session->common.flags = flow_flags; if ((flow_flags & APPID_SESSION_BIDIRECTIONAL_CHECKED) == APPID_SESSION_BIDIRECTIONAL_CHECKED) session->common.fsf_type.flow_type = APPID_SESSION_TYPE_IGNORE; session->common.policyId = appIdPolicyId; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s not monitored\n", app_id_debug_session); } return; } if (!session || session->common.fsf_type.flow_type == APPID_SESSION_TYPE_TMP) { /* This call will free the existing temporary session, if there is one */ session = appSharedCreateData(p, protocol, direction); if (_dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM) { flow_flags |= APPID_SESSION_MID; // set once per flow if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s new mid-stream session\n", app_id_debug_session); } else if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s new session\n", app_id_debug_session); } app_id_processed_packet_count++; session->session_packet_count++; if (direction == APP_ID_FROM_INITIATOR) { session->stats.initiatorBytes += p->pkt_header->pktlen; if ( p->payload_size) { session->initiatorPcketCountWithoutReply ++; session->initiatorBytesWithoutServerReply += p->payload_size; } } else { session->stats.responderBytes += p->pkt_header->pktlen; if(p->payload_size) { session->initiatorPcketCountWithoutReply = 0; session->initiatorBytesWithoutServerReply = 0; } } session->common.flags = flow_flags; session->common.policyId = appIdPolicyId; tpAppId = session->tpAppId; session->common.policyId = appIdPolicyId; #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) { #endif fprintf(SF_DEBUG_FILE, "%u %u -> %u %u Begin %d %u - (%d %d %d %d %d) %u %" PRIx64 " %" PRIx64 " (%u %u %u)\n", (unsigned )session->session_packet_count, (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol, direction, (unsigned)p->payload_size, session->serviceAppId, session->clientAppId, session->payloadAppId, tpAppId, session->miscAppId, session->rnaServiceState, session->common.flags, p->flags, thirdparty_appid_module->session_state_get(session->tpsession), (unsigned)session->init_tpPackets, (unsigned)session->resp_tpPackets); /*DumpHex(SF_DEBUG_FILE, p->payload, p->payload_size); */ #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT } #endif #endif if (getAppIdFlag(session, APPID_SESSION_IGNORE_FLOW)) { if (app_id_debug_session_flag && !getAppIdFlag(session, APPID_SESSION_IGNORE_FLOW_LOGGED)) { setAppIdFlag(session, APPID_SESSION_IGNORE_FLOW_LOGGED); _dpd.logMsg("AppIdDbg %s Ignoring flow with service %d\n", app_id_debug_session, session->serviceAppId); } return; } if (p->tcp_header && !getAppIdFlag(session, APPID_SESSION_OOO)) { if ((p->flags & FLAG_STREAM_ORDER_BAD) || (p->payload_size && !(p->flags & (FLAG_STREAM_ORDER_OK | FLAG_RETRANSMIT | FLAG_REBUILT_STREAM)))) { setAppIdFlag(session, APPID_SESSION_OOO | APPID_SESSION_OOO_CHECK_TP); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Packet out-of-order, %s%sflow\n", app_id_debug_session, (p->flags & FLAG_STREAM_ORDER_BAD)? "bad ":"not-ok ", getAppIdFlag(session, APPID_SESSION_MID)? "mid-stream ":""); /* Shut off service/client discoveries, since they skip not-ok data packets and may keep failing on subsequent data packets causing performance degradation. */ if (!getAppIdFlag(session, APPID_SESSION_MID) || (p->src_port != 21 && p->dst_port != 21)) // exception for ftp-control { session->rnaServiceState = RNA_STATE_FINISHED; session->rnaClientState = RNA_STATE_FINISHED; if ((TPIsAppIdAvailable(session->tpsession) || getAppIdFlag(session, APPID_SESSION_NO_TPI)) && session->payloadAppId == APP_ID_NONE) session->payloadAppId = APP_ID_UNKNOWN; setAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_CLIENT_DETECTED); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s stopped service/client discovery\n", app_id_debug_session); } } else { if ((p->tcp_header->flags & TCPHEADER_RST) && session->previous_tcp_flags == TCPHEADER_SYN) { AppIdServiceIDState *id_state; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = 0; #endif setAppIdFlag(session, APPID_SESSION_SYN_RST); if (sfaddr_is_set(&session->service_ip)) { ip = &session->service_ip; port = session->service_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = session->serviceAsId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) cid = session->carrierId; #endif } else { ip = GET_SRC_IP(p); port = p->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = p->pkt_header->address_space_id_src; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) cid = GET_SFOUTER_IPH_PROTOID(p, pkt_header); #endif } #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session), asId, cid); #else id_state = AppIdGetServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session), cid); #endif #else /* No Carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session), asId); #else id_state = AppIdGetServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session)); #endif #endif if (id_state) { if (!id_state->reset_time) id_state->reset_time = GetPacketRealTime; else if ((GetPacketRealTime - id_state->reset_time) >= 60) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) AppIdRemoveServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session), asId, cid); #else AppIdRemoveServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session), cid); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) AppIdRemoveServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session), asId); #else AppIdRemoveServiceIDState(ip, IPPROTO_TCP, port, AppIdServiceDetectionLevel(session)); #endif #endif setAppIdFlag(session, APPID_SESSION_SERVICE_DELETED); } } } session->previous_tcp_flags = p->tcp_header->flags; } } checkRestartAppDetection(session); if (outer_protocol) { session->miscAppId = pConfig->ip_protocol[outer_protocol]; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s outer protocol service %d\n", app_id_debug_session, session->miscAppId); } if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) { if (!getAppIdFlag(session, APPID_SESSION_PORT_SERVICE_DONE)) { session->serviceAppId = session->portServiceAppId = getProtocolServiceId(protocol, pConfig); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s protocol service %d\n", app_id_debug_session, session->portServiceAppId); setAppIdFlag(session, APPID_SESSION_PORT_SERVICE_DONE); session->rnaServiceState = RNA_STATE_FINISHED; _dpd.streamAPI->set_application_id(p->stream_session, session->serviceAppId, clientAppId, payloadAppId, session->miscAppId); } return; } if (session->tpAppId == APP_ID_SSH && session->payloadAppId != APP_ID_SFTP && session->session_packet_count >= MIN_SFTP_PACKET_COUNT && session->session_packet_count < MAX_SFTP_PACKET_COUNT) { if (GET_IPH_TOS(p) == 8) { session->payloadAppId = APP_ID_SFTP; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s payload is SFTP\n", app_id_debug_session); } } tpAppId = processThirdParty(p, session, direction, protocol, &isTpAppidDiscoveryDone, pConfig); if (!getAppIdFlag(session, APPID_SESSION_PORT_SERVICE_DONE)) { switch (protocol) { case IPPROTO_TCP: // TCP-specific checks. No SYN/RST, and no SYN/ACK // we have to check for SYN/ACK here explicitly in case of TCP Fast Open if (getAppIdFlag(session, APPID_SESSION_SYN_RST) || (p->tcp_header && (p->tcp_header->flags & TCPHEADER_SYN) && (p->tcp_header->flags & TCPHEADER_ACK))) break; // fall through to next test case IPPROTO_UDP: // Both TCP and UDP need these tests to be made // packet must be from responder, and with a non-zero payload size // For all other cases the port parameter is never checked. if (p->payload_size < 1 || direction != APP_ID_FROM_RESPONDER) break; // fall through to all other cases default: { session->portServiceAppId = getPortServiceId(protocol, p->src_port, pConfig); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s port service %d\n", app_id_debug_session, session->portServiceAppId); setAppIdFlag(session, APPID_SESSION_PORT_SERVICE_DONE); } break; } } /* Length-based detectors. */ /* Only check if: * - Port service didn't find anything (and we haven't yet either). * - We haven't hit the max packets allowed for detector sequence matches. * - Packet has in-order data (we'll ignore 0-sized packets in sequencing). */ if ( (p->payload_size > 0) && (session->portServiceAppId <= APP_ID_NONE) && (session->length_sequence.sequence_cnt < LENGTH_SEQUENCE_CNT_MAX) && !getAppIdFlag(session, APPID_SESSION_OOO)) { uint8_t index = session->length_sequence.sequence_cnt; session->length_sequence.proto = protocol; session->length_sequence.sequence_cnt++; session->length_sequence.sequence[index].direction = direction; session->length_sequence.sequence[index].length = p->payload_size; session->portServiceAppId = lengthAppCacheFind(&session->length_sequence, pConfig); if (session->portServiceAppId > APP_ID_NONE) { setAppIdFlag(session, APPID_SESSION_PORT_SERVICE_DONE); } } /* exceptions for rexec and any other service detector that needs to see SYN and SYN/ACK */ if (getAppIdFlag(session, APPID_SESSION_REXEC_STDERR)) { AppIdDiscoverService(p, direction, session, pConfig); if (getAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_CONTINUE) == APPID_SESSION_SERVICE_DETECTED) { session->rnaServiceState = RNA_STATE_FINISHED; if (session->payloadAppId == APP_ID_NONE) session->payloadAppId = APP_ID_UNKNOWN; } } else if (protocol != IPPROTO_TCP || (p->flags & FLAG_STREAM_ORDER_OK) || getAppIdFlag(session, APPID_SESSION_MID)) { /*** Start of service discovery. ***/ if (session->rnaServiceState != RNA_STATE_FINISHED) { PROFILE_VARS; RNA_INSPECTION_STATE prevRnaServiceState; PREPROC_PROFILE_START(serviceMatchPerfStats); tpAppId = session->tpAppId; prevRnaServiceState = session->rnaServiceState; //decision to directly call validator or go through elaborate service_state tracking //is made once at the beginning of session. if (session->rnaServiceState == RNA_STATE_NONE && p->payload_size) { if (getAppIdFlag(session, APPID_SESSION_MID)) { // Unless it could be ftp control if (protocol == IPPROTO_TCP && (p->src_port == 21 || p->dst_port == 21) && !(p->tcp_header->flags & (TCPHEADER_FIN | TCPHEADER_RST))) { setAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED | APPID_SESSION_NOT_A_SERVICE | APPID_SESSION_SERVICE_DETECTED); if (!AddFTPServiceState(session)) { setAppIdFlag(session, APPID_SESSION_CONTINUE); if (p->dst_port != 21) setAppIdFlag(session, APPID_SESSION_RESPONDER_SEEN); } session->rnaServiceState = RNA_STATE_STATEFUL; } else { setAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED); session->rnaServiceState = RNA_STATE_FINISHED; if ((TPIsAppIdAvailable(session->tpsession) || getAppIdFlag(session, APPID_SESSION_NO_TPI)) && session->payloadAppId == APP_ID_NONE) session->payloadAppId = APP_ID_UNKNOWN; } } else if (TPIsAppIdAvailable(session->tpsession)) { if (tpAppId > APP_ID_NONE) { //tp has positively identified appId, Dig deeper only if sourcefire detector //identifies additional information or flow is UDP reveresed. #ifdef TARGET_BASED if ((entry = appInfoEntryGet(tpAppId, pConfig)) && entry->svrValidator && ((entry->flags & APPINFO_FLAG_SERVICE_ADDITIONAL) || ((entry->flags & APPINFO_FLAG_SERVICE_UDP_REVERSED) && protocol == IPPROTO_UDP && getAppIdFlag(session, APPID_SESSION_INITIATOR_MONITORED | APPID_SESSION_RESPONDER_MONITORED)))) { AppIdFlowdataDeleteAllByMask(session, APPID_SESSION_DATA_SERVICE_MODSTATE_BIT); #ifdef DEBUG_FW_APPID if (session->serviceData && compareServiceElements(session->serviceData, entry->svrValidator)) { fprintf(stderr, "Mismatched validator Original %s, new tp %s", session->serviceData->name, entry->svrValidator->name); } #endif session->serviceData = entry->svrValidator; session->rnaServiceState = RNA_STATE_STATEFUL; #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u RNA doing deeper inspection\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol); #endif } else { stopRnaServiceInspection(p, session, direction); } #endif } else session->rnaServiceState = RNA_STATE_STATEFUL; } else session->rnaServiceState = RNA_STATE_STATEFUL; } //stop rna inspection as soon as tp has classified a valid AppId later in the session if (session->rnaServiceState == RNA_STATE_STATEFUL && prevRnaServiceState == RNA_STATE_STATEFUL && !getAppIdFlag(session, APPID_SESSION_NO_TPI) && TPIsAppIdAvailable(session->tpsession) && tpAppId > APP_ID_NONE && tpAppId < SF_APPID_MAX) { #ifdef TARGET_BASED entry = appInfoEntryGet(tpAppId, pConfig); if (entry && entry->svrValidator && !(entry->flags & APPINFO_FLAG_SERVICE_ADDITIONAL)) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Stopping service detection\n", app_id_debug_session); stopRnaServiceInspection(p, session, direction); } #endif } // Check to see if we want to stop any detectors for SIP/RTP. if (session->rnaServiceState != RNA_STATE_FINISHED) { if (tpAppId == APP_ID_SIP) { // TP needs to see its own future flows and does a better // job of it than we do, so stay out of its way, and don't // waste time (but we will still get the Snort callbacks // for any of our own future flows). // - Shut down our detectors. session->serviceAppId = APP_ID_SIP; stopRnaServiceInspection(p, session, direction); session->rnaClientState = RNA_STATE_FINISHED; } else if ((tpAppId == APP_ID_RTP) || (tpAppId == APP_ID_RTP_AUDIO) || (tpAppId == APP_ID_RTP_VIDEO)) { // No need for anybody to keep wasting time once we've // found RTP. // - Shut down our detectors. session->serviceAppId = tpAppId; stopRnaServiceInspection(p, session, direction); session->rnaClientState = RNA_STATE_FINISHED; // - Shut down TP. thirdparty_appid_module->session_state_set(session->tpsession, TP_STATE_TERMINATED); // - Just ignore everything from now on. setAppIdFlag(session, APPID_SESSION_IGNORE_FLOW); } } if (session->rnaServiceState == RNA_STATE_STATEFUL) { #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u RNA identifying service\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol); #endif AppIdDiscoverService(p, direction, session, pConfig); isTpAppidDiscoveryDone = true; //to stop executing validator after service has been detected by RNA. if (getAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_CONTINUE) == APPID_SESSION_SERVICE_DETECTED) { session->rnaServiceState = RNA_STATE_FINISHED; if ((TPIsAppIdAvailable(session->tpsession) || getAppIdFlag(session, APPID_SESSION_NO_TPI)) && session->payloadAppId == APP_ID_NONE) session->payloadAppId = APP_ID_UNKNOWN; } if (session->rnaServiceState == RNA_STATE_STATEFUL && session->serviceAppId == APP_ID_NONE && svcTakingTooMuchTime(session)) { stopRnaServiceInspection(p, session, direction); session->serviceAppId = APP_ID_UNKNOWN; } else if(session->serviceAppId == APP_ID_DNS && appidStaticConfig->dns_host_reporting && session->dsession && session->dsession->host ) { size = session->dsession->host_len; dns_host_scan_hostname((const u_int8_t *)session->dsession->host , size, &clientAppId, &payloadAppId, &pConfig->serviceDnsConfig); setClientAppIdData(p, direction, session, clientAppId, NULL); } else if (session->serviceAppId == APP_ID_RTMP) ExamineRtmpMetadata(p, direction, session); else if (getAppIdFlag(session, APPID_SESSION_SSL_SESSION) && session->tsession) ExamineSslMetadata(p, direction, session, pConfig); #ifdef TARGET_BASED if (tpAppId <= APP_ID_NONE && getAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_NOT_A_SERVICE | APPID_SESSION_IGNORE_HOST) == APPID_SESSION_SERVICE_DETECTED) { synchAppIdWithSnortId(session->serviceAppId, p, session, pConfig); } #endif } PREPROC_PROFILE_END(serviceMatchPerfStats); } /*** End of service discovery. ***/ /*** Start of client discovery. ***/ if (session->rnaClientState != RNA_STATE_FINISHED) { PROFILE_VARS; PREPROC_PROFILE_START(clientMatchPerfStats); RNA_INSPECTION_STATE prevRnaClientState = session->rnaClientState; bool was_http2 = session->is_http2; #ifdef TARGET_BASED bool was_service = getAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED) ? true : false; #endif //decision to directly call validator or go through elaborate service_state tracking //is made once at the beginning of session. if (session->rnaClientState == RNA_STATE_NONE && p->payload_size && direction == APP_ID_FROM_INITIATOR) { if (getAppIdFlag(session, APPID_SESSION_MID)) session->rnaClientState = RNA_STATE_FINISHED; else if (TPIsAppIdAvailable(session->tpsession) && (tpAppId = session->tpAppId) > APP_ID_NONE && tpAppId < SF_APPID_MAX) { #ifdef TARGET_BASED if ((entry = appInfoEntryGet(tpAppId, pConfig)) && entry->clntValidator && ((entry->flags & APPINFO_FLAG_CLIENT_ADDITIONAL) || ((entry->flags & APPINFO_FLAG_CLIENT_USER) && getAppIdFlag(session, APPID_SESSION_DISCOVER_USER)))) { //tp has positively identified appId, Dig deeper only if sourcefire detector //identifies additional information session->clientData = entry->clntValidator; session->rnaClientState = RNA_STATE_DIRECT; } else { setAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED); session->rnaClientState = RNA_STATE_FINISHED; } #endif } else if (getAppIdFlag(session, APPID_SESSION_HTTP_SESSION)) session->rnaClientState = RNA_STATE_FINISHED; else session->rnaClientState = RNA_STATE_STATEFUL; } //stop rna inspection as soon as tp has classified a valid AppId later in the session if ((session->rnaClientState == RNA_STATE_STATEFUL || session->rnaClientState == RNA_STATE_DIRECT) && session->rnaClientState == prevRnaClientState && !getAppIdFlag(session, APPID_SESSION_NO_TPI) && TPIsAppIdAvailable(session->tpsession) && tpAppId > APP_ID_NONE && tpAppId < SF_APPID_MAX) { #ifdef TARGET_BASED entry = appInfoEntryGet(tpAppId, pConfig); if (!(entry && entry->clntValidator && entry->clntValidator == session->clientData && (entry->flags & (APPINFO_FLAG_CLIENT_ADDITIONAL|APPINFO_FLAG_CLIENT_USER)))) { session->rnaClientState = RNA_STATE_FINISHED; setAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED); } #endif } if (session->rnaClientState == RNA_STATE_DIRECT) { int ret = CLIENT_APP_INPROCESS; #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u RNA identifying additional client info\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol); #endif if (direction == APP_ID_FROM_INITIATOR) { /* get out if we've already tried to validate a client app */ if (!getAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED)) { ret = RunClientDetectors(session, p, direction, pConfig); } } else if (session->rnaServiceState != RNA_STATE_STATEFUL && getAppIdFlag(session, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS)) { ret = RunClientDetectors(session, p, direction, pConfig); } #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u direct client validate returned %d\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol, ret); #endif switch (ret) { case CLIENT_APP_INPROCESS: break; default: session->rnaClientState = RNA_STATE_FINISHED; break; } } else if (session->rnaClientState == RNA_STATE_STATEFUL) { #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "%u -> %u %u RNA identifying client\n", (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol); #endif AppIdDiscoverClientApp(p, direction, session, pConfig); isTpAppidDiscoveryDone = true; if (session->candidate_client_list != NULL) { if (sflist_count(session->candidate_client_list) > 0) { int ret = 0; if (direction == APP_ID_FROM_INITIATOR) { /* get out if we've already tried to validate a client app */ if (!getAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED)) { ret = RunClientDetectors(session, p, direction, pConfig); } } else if (session->rnaServiceState != RNA_STATE_STATEFUL && getAppIdFlag(session, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS)) { ret = RunClientDetectors(session, p, direction, pConfig); } if (ret < 0) { setAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED); session->rnaClientState = RNA_STATE_FINISHED; } } else { setAppIdFlag(session, APPID_SESSION_CLIENT_DETECTED); session->rnaClientState = RNA_STATE_FINISHED; } } } if (app_id_debug_session_flag) if (!was_http2 && session->is_http2) _dpd.logMsg("AppIdDbg %s Got a preface for HTTP/2\n", app_id_debug_session); #ifdef TARGET_BASED if (!was_service && getAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED)) synchAppIdWithSnortId(session->serviceAppId, p, session, pConfig); #endif PREPROC_PROFILE_END(clientMatchPerfStats); } /*** End of client discovery. ***/ setAppIdFlag(session, APPID_SESSION_ADDITIONAL_PACKET); } else { #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) #endif fprintf(SF_DEBUG_FILE, "Packet not okay\n"); #endif } serviceAppId = fwPickServiceAppId(session); payloadAppId = fwPickPayloadAppId(session); if (serviceAppId > APP_ID_NONE) { if (getAppIdFlag(session, APPID_SESSION_DECRYPTED)) { if (session->miscAppId == APP_ID_NONE) updateEncryptedAppId(session, serviceAppId); } else if (isTpAppidDiscoveryDone && isSslServiceAppId(serviceAppId) && _dpd.isSSLPolicyEnabled(NULL)) setAppIdFlag(session, APPID_SESSION_CONTINUE); } clientAppId = fwPickClientAppId(session); miscAppId = fwPickMiscAppId(session); if (!getAppIdFlag(session, APPID_SESSION_HOST_CACHE_MATCHED)) { bool isHttpTunnel = (payloadAppId == APP_ID_HTTP_TUNNEL || payloadAppId == APP_ID_HTTP_SSL_TUNNEL) ? true : false; if(isCheckHostCacheValid(session, serviceAppId, clientAppId, payloadAppId, miscAppId) || isHttpTunnel) { bool isCheckHostCache = true; sfaddr_t *srv_ip; uint16_t srv_port; if (isHttpTunnel) { if (session->hsession) { if (session->scan_flags & SCAN_HTTP_URI_FLAG) { if (session->hsession->tunDest) { _dpd.snortFree(session->hsession->tunDest, sizeof(tunnelDest), PP_APP_ID, PP_MEM_CATEGORY_SESSION); session->hsession->tunDest = NULL; } getIpPortFromHttpTunnel(session->hsession->uri, session->hsession->uri_buflen, &session->hsession->tunDest); session->scan_flags &= ~SCAN_HTTP_URI_FLAG; } if (session->hsession->tunDest) { srv_ip = &(session->hsession->tunDest->ip); srv_port = session->hsession->tunDest->port; } else { isCheckHostCache = false; } } else { isCheckHostCache = false; } } else { if (direction == APP_ID_FROM_INITIATOR) { srv_ip = GET_DST_IP(p); srv_port = p->dst_port; } else { srv_ip = GET_SRC_IP(p); srv_port = p->src_port; } } if (isCheckHostCache && checkHostCache(p, session, srv_ip, srv_port, protocol, pConfig)) { session->portServiceAppId = APP_ID_NONE; // overriding the portServiceAppId in case of a cache hit serviceAppId = pickServiceAppId(session); payloadAppId = pickPayloadId(session); clientAppId = pickClientAppId(session); } } } /* For OOO flow, disable third party if checkHostCache is done and appid is found. Not sure if it is safe to set APPID_SESSION_IGNORE_FLOW here. */ if ((getAppIdFlag(session, APPID_SESSION_OOO_CHECK_TP | APPID_SESSION_HOST_CACHE_MATCHED) == (APPID_SESSION_OOO_CHECK_TP | APPID_SESSION_HOST_CACHE_MATCHED)) && (serviceAppId || payloadAppId || clientAppId) && thirdparty_appid_module) { clearAppIdFlag(session, APPID_SESSION_OOO_CHECK_TP); // don't repeat this block if (!TPIsAppIdDone(session->tpsession)) { thirdparty_appid_module->session_state_set(session->tpsession, TP_STATE_TERMINATED); if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s stopped third party detection\n", app_id_debug_session); } } _dpd.streamAPI->set_application_id(p->stream_session, serviceAppId, clientAppId, payloadAppId, miscAppId); /* Set the field that the Firewall queries to see if we have a search engine. */ if (session->search_support_type == SEARCH_SUPPORT_TYPE_UNKNOWN && payloadAppId > APP_ID_NONE) { uint flags = appInfoEntryFlagGet(payloadAppId, APPINFO_FLAG_SEARCH_ENGINE | APPINFO_FLAG_SUPPORTED_SEARCH, pConfig); session->search_support_type = (flags & APPINFO_FLAG_SEARCH_ENGINE) ? ((flags & APPINFO_FLAG_SUPPORTED_SEARCH) ? SUPPORTED_SEARCH_ENGINE : UNSUPPORTED_SEARCH_ENGINE ) : NOT_A_SEARCH_ENGINE; if (app_id_debug_session_flag) { char *typeString; switch (session->search_support_type) { case NOT_A_SEARCH_ENGINE: typeString = "NOT_A_SEARCH_ENGINE"; break; case SUPPORTED_SEARCH_ENGINE: typeString = "SUPPORTED_SEARCH_ENGINE"; break; case UNSUPPORTED_SEARCH_ENGINE: typeString = "UNSUPPORTED_SEARCH_ENGINE"; break; default: break; } _dpd.logMsg("AppIdDbg %s appId: %u (safe)search_support_type=%s\n", app_id_debug_session, payloadAppId, typeString); } } if (serviceAppId > APP_ID_NONE) { if (session->pastIndicator != payloadAppId && payloadAppId > APP_ID_NONE) { session->pastIndicator = payloadAppId; checkSessionForAFIndicator(p, direction, pConfig, payloadAppId); } if (session->pastForecast != serviceAppId && session->payloadAppId == APP_ID_NONE && session->pastForecast != APP_ID_UNKNOWN) { session->pastForecast = checkSessionForAFForecast(session, p, direction, pConfig, serviceAppId); } } if (*_dpd.pkt_tracer_enabled) { const char *serviceName = appGetAppName(serviceAppId); const char *appName = appGetAppName(payloadAppId); _dpd.addPktTrace(VERDICT_REASON_APPID, snprintf(_dpd.trace, _dpd.traceMax, "AppID: service %s (%d), application %s (%d)%s\n", serviceName? serviceName : "unknown", serviceAppId, appName? appName : "unknown", payloadAppId, getAppIdFlag(session, APPID_SESSION_OOO)? ", out-of-order":"")); } #ifdef DEBUG_FW_APPID #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT if (p->dst_port == DEBUG_FW_APPID_PORT || p->src_port == DEBUG_FW_APPID_PORT) { #endif fprintf(SF_DEBUG_FILE, "%u %u -> %u %u End %d %u - (%d %d %d %d %d) %u %" PRIx64 " %u %u %u\n", (unsigned)session->session_packet_count, (unsigned)p->src_port, (unsigned)p->dst_port, (unsigned)protocol, direction, (unsigned)p->payload_size, session->serviceAppId, session->clientAppId, session->payloadAppId, session->tpAppId, session->miscAppId, session->rnaServiceState, session->common.flags, thirdparty_appid_module->session_state_get(session->tpsession), (unsigned)session->init_tpPackets, (unsigned)session->resp_tpPackets); //DumpHex(SF_DEBUG_FILE, p->payload, p->payload_size); #if defined(DEBUG_FW_APPID_PORT) && DEBUG_FW_APPID_PORT } #endif #endif } STATIC INLINE void pickHttpXffAddress(SFSnortPacket* p, tAppIdData* appIdSession, ThirdPartyAppIDAttributeData* attribute_data) { int i; static char* defaultXffPrecedence[] = {HTTP_XFF_FIELD_X_FORWARDED_FOR, HTTP_XFF_FIELD_TRUE_CLIENT_IP}; // XFF precedence configuration cannot change for a session. Do not get it again if we already got it. if (!appIdSession->hsession->xffPrecedence) { char** xffPrecedence = _dpd.sessionAPI->get_http_xff_precedence(p->stream_session, p->flags, &appIdSession->hsession->numXffFields); int j; if (!xffPrecedence) { xffPrecedence = defaultXffPrecedence; appIdSession->hsession->numXffFields = sizeof(defaultXffPrecedence) / sizeof(defaultXffPrecedence[0]); } appIdSession->hsession->xffPrecedence = _dpd.snortAlloc(appIdSession->hsession->numXffFields, sizeof(char*), PP_APP_ID, PP_MEM_CATEGORY_SESSION); if(!appIdSession->hsession->xffPrecedence) { DynamicPreprocessorFatalMessage("pickHttpXffAddress: " "failed to allocate memory for xffPrecedence in appIdSession\n"); } for (j = 0; j < appIdSession->hsession->numXffFields; j++) appIdSession->hsession->xffPrecedence[j] = strndup(xffPrecedence[j], UINT8_MAX); } if (app_id_debug_session_flag) { for (i = 0; i < attribute_data->numXffFields; i++) _dpd.logMsg("AppIdDbg %s %s : %s\n", app_id_debug_session, attribute_data->xffFieldValue[i].field, attribute_data->xffFieldValue[i].value ? attribute_data->xffFieldValue[i].value : "(empty)"); } // xffPrecedence array is sorted based on precedence for (i = 0; (i < appIdSession->hsession->numXffFields) && appIdSession->hsession->xffPrecedence[i]; i++) { int j; for (j = 0; j < attribute_data->numXffFields; j++) { if (appIdSession->hsession->xffAddr) { sfaddr_free(appIdSession->hsession->xffAddr); appIdSession->hsession->xffAddr = NULL; } if (strncasecmp(attribute_data->xffFieldValue[j].field, appIdSession->hsession->xffPrecedence[i], UINT8_MAX) == 0) { if (!attribute_data->xffFieldValue[j].value || (attribute_data->xffFieldValue[j].value[0] == '\0')) return; char* tmp = strrchr(attribute_data->xffFieldValue[j].value, ','); SFIP_RET status; if (!tmp) { appIdSession->hsession->xffAddr = sfaddr_alloc(attribute_data->xffFieldValue[j].value, &status); } // For a comma-separated list of addresses, pick the last address else { appIdSession->hsession->xffAddr = sfaddr_alloc(tmp + 1, &status); } break; } } if (appIdSession->hsession->xffAddr) break; } } static inline void ProcessThirdPartyResults(SFSnortPacket* p, APPID_SESSION_DIRECTION direction, tAppIdData* appIdSession, int confidence, tAppId* proto_list, ThirdPartyAppIDAttributeData* attribute_data) { int size; tAppId serviceAppId = 0; tAppId clientAppId = 0; tAppId payloadAppId = 0; tAppId referredPayloadAppId = 0; tAppIdConfig *pConfig = appIdActiveConfigGet(); #ifdef TARGET_BASED AppInfoTableEntry *entry = NULL; #endif if (!appIdSession->payloadAppId && ThirdPartyAppIDFoundProto(APP_ID_EXCHANGE, proto_list)) appIdSession->payloadAppId = APP_ID_EXCHANGE; if (ThirdPartyAppIDFoundProto(APP_ID_HTTP, proto_list)) { setAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION); } if (ThirdPartyAppIDFoundProto(APP_ID_SPDY, proto_list)) { setAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION | APPID_SESSION_SPDY_SESSION); } if (ThirdPartyAppIDFoundProto(APP_ID_SSL, proto_list)) { if (getAppIdFlag(appIdSession, APPID_SESSION_HTTP_TUNNEL)) { if (!appIdSession->serviceData) { #ifdef TARGET_BASED entry = appInfoEntryGet(APP_ID_SSL, pConfig); appIdSession->serviceData = entry->svrValidator; #endif } if (getAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION | APPID_SESSION_SPDY_SESSION)) clearAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION | APPID_SESSION_SPDY_SESSION); } setAppIdFlag(appIdSession, APPID_SESSION_SSL_SESSION); } if (getAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION)) { if (!appIdSession->hsession) { if (!(appIdSession->hsession = _dpd.snortAlloc(1, sizeof(*appIdSession->hsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate httpSession data"); memset(ptype_scan_counts, 0, 7 * sizeof(ptype_scan_counts[0])); } if (getAppIdFlag(appIdSession, APPID_SESSION_SPDY_SESSION)) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s flow is SPDY\n", app_id_debug_session); if (attribute_data->spdyRequestScheme && attribute_data->spdyRequestHost && attribute_data->spdyRequestPath) { static const char httpsScheme[] = "https"; static const char httpScheme[] = "http"; const char *scheme; if (appIdSession->hsession->url) { free(appIdSession->hsession->url); appIdSession->hsession->chp_finished = 0; } if (getAppIdFlag(appIdSession, APPID_SESSION_DECRYPTED) && memcmp(attribute_data->spdyRequestScheme, httpScheme, sizeof(httpScheme)-1) == 0) { scheme = httpsScheme; } else { scheme = attribute_data->spdyRequestScheme; } size = strlen(scheme) + strlen(attribute_data->spdyRequestHost) + strlen(attribute_data->spdyRequestPath) + sizeof("://"); // see sprintf() format if (NULL != (appIdSession->hsession->url = malloc(size))) { sprintf(appIdSession->hsession->url, "%s://%s%s", scheme, attribute_data->spdyRequestHost, attribute_data->spdyRequestPath); appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } free(attribute_data->spdyRequestScheme); attribute_data->spdyRequestScheme = NULL; } else if (attribute_data->spdyRequestScheme) { free(attribute_data->spdyRequestScheme); attribute_data->spdyRequestScheme = NULL; } if (attribute_data->spdyRequestHost) { if (appIdSession->hsession->host) { free(appIdSession->hsession->host); appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->host = attribute_data->spdyRequestHost; attribute_data->spdyRequestHost = NULL; appIdSession->hsession->fieldOffset[REQ_HOST_FID] = attribute_data->spdyRequestHostOffset; appIdSession->hsession->fieldEndOffset[REQ_HOST_FID] = attribute_data->spdyRequestHostEndOffset; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s SPDY Host (%u-%u) is %s\n", app_id_debug_session, appIdSession->hsession->fieldOffset[REQ_HOST_FID], appIdSession->hsession->fieldEndOffset[REQ_HOST_FID], appIdSession->hsession->host); appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } if (attribute_data->spdyRequestPath) { if (appIdSession->hsession->uri) { free(appIdSession->hsession->uri); appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->uri = attribute_data->spdyRequestPath; attribute_data->spdyRequestPath = NULL; appIdSession->hsession->fieldOffset[REQ_URI_FID] = attribute_data->spdyRequestPathOffset; appIdSession->hsession->fieldEndOffset[REQ_URI_FID] = attribute_data->spdyRequestPathEndOffset; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s SPDY URI (%u-%u) is %s\n", app_id_debug_session, appIdSession->hsession->fieldOffset[REQ_URI_FID], appIdSession->hsession->fieldEndOffset[REQ_URI_FID], appIdSession->hsession->uri); } } else { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s flow is HTTP\n", app_id_debug_session); if (attribute_data->httpRequestHost) { if (appIdSession->hsession->host) { free(appIdSession->hsession->host); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->host = attribute_data->httpRequestHost; appIdSession->hsession->host_buflen = attribute_data->httpRequestHostLen; appIdSession->hsession->fieldOffset[REQ_HOST_FID] = attribute_data->httpRequestHostOffset; appIdSession->hsession->fieldEndOffset[REQ_HOST_FID] = attribute_data->httpRequestHostEndOffset; attribute_data->httpRequestHost = NULL; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s HTTP Host (%u-%u) is %s\n", app_id_debug_session, appIdSession->hsession->fieldOffset[REQ_HOST_FID], appIdSession->hsession->fieldEndOffset[REQ_HOST_FID], appIdSession->hsession->host); appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } if (attribute_data->httpRequestUrl) { static const char httpScheme[] = "http://"; if (appIdSession->hsession->url) { free(appIdSession->hsession->url); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } //change http to https if session was decrypted. if (getAppIdFlag(appIdSession, APPID_SESSION_DECRYPTED) && memcmp(attribute_data->httpRequestUrl, httpScheme, sizeof(httpScheme)-1) == 0) { appIdSession->hsession->url = malloc(strlen(attribute_data->httpRequestUrl) + 2); if(!appIdSession->hsession->url) { DynamicPreprocessorFatalMessage("ProcessThirdPartyResults: " "Failed to allocate URL memory in AppID session\n"); } if (appIdSession->hsession->url) sprintf(appIdSession->hsession->url, "https://%s", attribute_data->httpRequestUrl + sizeof(httpScheme)-1); free(attribute_data->httpRequestUrl); attribute_data->httpRequestUrl = NULL; } else { appIdSession->hsession->url = attribute_data->httpRequestUrl; attribute_data->httpRequestUrl = NULL; } appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } if (attribute_data->httpRequestUri) { if (appIdSession->hsession->uri) { free(appIdSession->hsession->uri); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->uri = attribute_data->httpRequestUri; appIdSession->hsession->uri_buflen = attribute_data->httpRequestUriLen; appIdSession->hsession->fieldOffset[REQ_URI_FID] = attribute_data->httpRequestUriOffset; appIdSession->hsession->fieldEndOffset[REQ_URI_FID] = attribute_data->httpRequestUriEndOffset; appIdSession->scan_flags |= SCAN_HTTP_URI_FLAG; /* Extract host from URI if it is not available */ if (appIdSession->hsession->host == NULL) { appIdSession->hsession->host_buflen = getHttpHostFromUri(&appIdSession->hsession->host, appIdSession->hsession->uri); appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } attribute_data->httpRequestUri = NULL; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s HTTP URI (%u-%u) is %s\n", app_id_debug_session, appIdSession->hsession->fieldOffset[REQ_URI_FID], appIdSession->hsession->fieldEndOffset[REQ_URI_FID], appIdSession->hsession->uri); } } //======================================== // Begin common HTTP component field data //======================================== if (attribute_data->httpRequestMethod) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s HTTP request method is %s\n", app_id_debug_session, attribute_data->httpRequestMethod); if (!strcmp(attribute_data->httpRequestMethod, "CONNECT")) setAppIdFlag(appIdSession, APPID_SESSION_HTTP_CONNECT); } if (attribute_data->httpRequestVia) { if (appIdSession->hsession->via) { free(appIdSession->hsession->via); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->via = attribute_data->httpRequestVia; attribute_data->httpRequestVia = NULL; appIdSession->scan_flags |= SCAN_HTTP_VIA_FLAG; } else if (attribute_data->httpResponseVia) { if (appIdSession->hsession->via) { free(appIdSession->hsession->via); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->via = attribute_data->httpResponseVia; attribute_data->httpResponseVia = NULL; appIdSession->scan_flags |= SCAN_HTTP_VIA_FLAG; } if (attribute_data->httpRequestUserAgent) { if (appIdSession->hsession->useragent) { free(appIdSession->hsession->useragent); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->useragent = attribute_data->httpRequestUserAgent; appIdSession->hsession->useragent_buflen = attribute_data->httpRequestUserAgentLen; attribute_data->httpRequestUserAgent = NULL; appIdSession->hsession->fieldOffset[REQ_AGENT_FID] = attribute_data->httpRequestUserAgentOffset; appIdSession->hsession->fieldEndOffset[REQ_AGENT_FID] = attribute_data->httpRequestUserAgentEndOffset; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s User-Agent (%u-%u) is %s\n", app_id_debug_session, appIdSession->hsession->fieldOffset[REQ_AGENT_FID], appIdSession->hsession->fieldEndOffset[REQ_AGENT_FID], appIdSession->hsession->useragent); appIdSession->scan_flags |= SCAN_HTTP_USER_AGENT_FLAG; } // Check to see if third party discovered HTTP/2. // - once it supports it... if (attribute_data->httpResponseVersion) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s HTTP response version is %s\n", app_id_debug_session, attribute_data->httpResponseVersion); if (strncmp(attribute_data->httpResponseVersion, "HTTP/2", 6) == 0) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s 3rd party detected and parsed HTTP/2\n", app_id_debug_session); appIdSession->is_http2 = true; } free(attribute_data->httpResponseVersion); attribute_data->httpResponseVersion = NULL; } if (attribute_data->httpResponseCode) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s HTTP response code is %s\n", app_id_debug_session, attribute_data->httpResponseCode); if (appIdSession->hsession->response_code) { free(appIdSession->hsession->response_code); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->response_code = attribute_data->httpResponseCode; appIdSession->hsession->response_code_buflen = attribute_data->httpResponseCodeLen; attribute_data->httpResponseCode = NULL; } // Check to see if we've got an upgrade to HTTP/2 (if enabled). // - This covers the "without prior knowledge" case (i.e., the client // asks the server to upgrade to HTTP/2). if (attribute_data->httpResponseUpgrade) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s HTTP response upgrade is %s\n", app_id_debug_session, attribute_data->httpResponseUpgrade); if (appidStaticConfig->http2_detection_enabled) if (appIdSession->hsession->response_code && (strncmp(appIdSession->hsession->response_code, "101", 3) == 0)) if (strncmp(attribute_data->httpResponseUpgrade, "h2c", 3) == 0) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Got an upgrade to HTTP/2\n", app_id_debug_session); appIdSession->is_http2 = true; } free(attribute_data->httpResponseUpgrade); attribute_data->httpResponseUpgrade = NULL; } if (attribute_data->httpRequestReferer) { if (appIdSession->hsession->referer) { free(appIdSession->hsession->referer); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->referer = attribute_data->httpRequestReferer; appIdSession->hsession->referer_buflen = attribute_data->httpRequestRefererLen; attribute_data->httpRequestReferer = NULL; appIdSession->hsession->fieldOffset[REQ_REFERER_FID] = attribute_data->httpRequestRefererOffset; appIdSession->hsession->fieldEndOffset[REQ_REFERER_FID] = attribute_data->httpRequestRefererEndOffset; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Referer (%u-%u) is %s\n", app_id_debug_session, appIdSession->hsession->fieldOffset[REQ_REFERER_FID], appIdSession->hsession->fieldEndOffset[REQ_REFERER_FID], appIdSession->hsession->referer); } if (attribute_data->httpRequestCookie) { if (appIdSession->hsession->cookie) { free(appIdSession->hsession->cookie); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->cookie = attribute_data->httpRequestCookie; appIdSession->hsession->cookie_buflen = attribute_data->httpRequestCookieLen; attribute_data->httpRequestCookie = NULL; appIdSession->hsession->fieldOffset[REQ_COOKIE_FID] = attribute_data->httpRequestCookieOffset; appIdSession->hsession->fieldEndOffset[REQ_COOKIE_FID] = attribute_data->httpRequestCookieEndOffset; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s Cookie (%u-%u) is %s\n", app_id_debug_session, appIdSession->hsession->fieldOffset[REQ_COOKIE_FID], appIdSession->hsession->fieldEndOffset[REQ_COOKIE_FID], appIdSession->hsession->cookie); } if (attribute_data->httpResponseContent) { if (appIdSession->hsession->content_type) { free(appIdSession->hsession->content_type); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->content_type = attribute_data->httpResponseContent; appIdSession->hsession->content_type_buflen = attribute_data->httpResponseContentLen; attribute_data->httpResponseContent = NULL; appIdSession->scan_flags |= SCAN_HTTP_CONTENT_TYPE_FLAG; } if (ptype_scan_counts[LOCATION_PT] && attribute_data->httpResponseLocation) { if (appIdSession->hsession->location) { free(appIdSession->hsession->location); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->location = attribute_data->httpResponseLocation; appIdSession->hsession->location_buflen = attribute_data->httpResponseLocationLen; attribute_data->httpResponseLocation = NULL; } if (attribute_data->httpRequestBody) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s got a request body %s\n", app_id_debug_session, attribute_data->httpRequestBody); if (appIdSession->hsession->req_body) { free(appIdSession->hsession->req_body); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->req_body = attribute_data->httpRequestBody; appIdSession->hsession->req_body_buflen = attribute_data->httpRequestBodyLen; attribute_data->httpRequestBody = NULL; } if (ptype_scan_counts[BODY_PT] && attribute_data->httpResponseBody) { if (appIdSession->hsession->body) { free(appIdSession->hsession->body); if (!getAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT)) appIdSession->hsession->chp_finished = 0; } appIdSession->hsession->body = attribute_data->httpResponseBody; appIdSession->hsession->body_buflen = attribute_data->httpResponseBodyLen; attribute_data->httpResponseBody = NULL; } if (attribute_data->numXffFields) { pickHttpXffAddress(p, appIdSession, attribute_data); } if (!appIdSession->hsession->chp_finished || appIdSession->hsession->chp_hold_flow) { setAppIdFlag(appIdSession, APPID_SESSION_CHP_INSPECTING); if (thirdparty_appid_module) thirdparty_appid_module->session_attr_set(appIdSession->tpsession, TP_ATTR_CONTINUE_MONITORING); } if (attribute_data->httpResponseServer) { if (appIdSession->hsession->server) free(appIdSession->hsession->server); appIdSession->hsession->server = attribute_data->httpResponseServer; attribute_data->httpResponseServer = NULL; appIdSession->scan_flags |= SCAN_HTTP_VENDOR_FLAG; } if (attribute_data->httpRequestXWorkingWith) { if (appIdSession->hsession->x_working_with) free(appIdSession->hsession->x_working_with); appIdSession->hsession->x_working_with = attribute_data->httpRequestXWorkingWith; attribute_data->httpRequestXWorkingWith = NULL; appIdSession->scan_flags |= SCAN_HTTP_XWORKINGWITH_FLAG; } } else if (ThirdPartyAppIDFoundProto(APP_ID_RTMP, proto_list) || ThirdPartyAppIDFoundProto(APP_ID_RTSP, proto_list)) { if (!appIdSession->hsession) { if (!(appIdSession->hsession = _dpd.snortAlloc(1, sizeof(*appIdSession->hsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate httpSession data"); } if (!appIdSession->hsession->url) { if (attribute_data->httpRequestUrl) { appIdSession->hsession->url = attribute_data->httpRequestUrl; attribute_data->httpRequestUrl = NULL; appIdSession->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } } if (!appidStaticConfig->referred_appId_disabled && !appIdSession->hsession->referer) { if (attribute_data->httpRequestReferer) { appIdSession->hsession->referer = attribute_data->httpRequestReferer; attribute_data->httpRequestReferer = NULL; } } if (!appIdSession->hsession->useragent) { if (attribute_data->httpRequestUserAgent) { appIdSession->hsession->useragent = attribute_data->httpRequestUserAgent; appIdSession->hsession->useragent_buflen = attribute_data->httpRequestUserAgentLen; attribute_data->httpRequestUserAgent = NULL; appIdSession->scan_flags |= SCAN_HTTP_USER_AGENT_FLAG; } } if ((appIdSession->scan_flags & SCAN_HTTP_USER_AGENT_FLAG) && appIdSession->clientAppId <= APP_ID_NONE && appIdSession->hsession->useragent && (size = appIdSession->hsession->useragent_buflen) > 0) { identifyUserAgent((uint8_t *)appIdSession->hsession->useragent, size, &serviceAppId, &clientAppId, NULL, &pConfig->detectorHttpConfig); setClientAppIdData(p, direction, appIdSession, clientAppId, NULL); // do not overwrite a previously-set service if (appIdSession->serviceAppId <= APP_ID_NONE) setServiceAppIdData(p, direction, appIdSession, serviceAppId, NULL, NULL); appIdSession->scan_flags &= ~SCAN_HTTP_USER_AGENT_FLAG; } if (appIdSession->hsession->url || (confidence == 100 && appIdSession->session_packet_count > appidStaticConfig->rtmp_max_packets)) { if (appIdSession->hsession->url) { if (((getAppIdFromUrl(NULL, appIdSession->hsession->url, NULL, appIdSession->hsession->referer, &clientAppId, &serviceAppId, &payloadAppId, &referredPayloadAppId, 1, &pConfig->detectorHttpConfig)) || (getAppIdFromUrl(NULL, appIdSession->hsession->url, NULL, appIdSession->hsession->referer, &clientAppId, &serviceAppId, &payloadAppId, &referredPayloadAppId, 0, &pConfig->detectorHttpConfig))) == 1) { // do not overwrite a previously-set client or service if (appIdSession->clientAppId <= APP_ID_NONE) setClientAppIdData(p, direction, appIdSession, clientAppId, NULL); if (appIdSession->serviceAppId <= APP_ID_NONE) setServiceAppIdData(p, direction, appIdSession, serviceAppId, NULL, NULL); // DO overwrite a previously-set payload setPayloadAppIdData(p, direction, appIdSession, payloadAppId, NULL); setReferredPayloadAppIdData(appIdSession, referredPayloadAppId); } } if (thirdparty_appid_module) { thirdparty_appid_module->disable_flags(appIdSession->tpsession, TP_SESSION_FLAG_ATTRIBUTE | TP_SESSION_FLAG_TUNNELING | TP_SESSION_FLAG_FUTUREFLOW); thirdparty_appid_module->session_delete(appIdSession->tpsession, 1); } clearAppIdFlag(appIdSession, APPID_SESSION_APP_REINSPECT); } } else if (getAppIdFlag(appIdSession, APPID_SESSION_SSL_SESSION)) { int reInspectSSLAppId = 0; tAppId tmpAppId = APP_ID_NONE; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s flow is SSL\n", app_id_debug_session); if (thirdparty_appid_module && appIdSession->tpsession) tmpAppId = thirdparty_appid_module->session_appid_get(appIdSession->tpsession); if (!appIdSession->tsession) { if (!(appIdSession->tsession = _dpd.snortAlloc(1, sizeof(*appIdSession->tsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate tlsSession data"); } if (!appIdSession->clientAppId) setClientAppIdData(p, direction, appIdSession, APP_ID_SSL_CLIENT, NULL); reInspectSSLAppId = testSSLAppIdForReinspect(tmpAppId); if (attribute_data->tlsHost) { /* This flag is to avoid NAVL overwritting SSL provided SNI */ if (!(appIdSession->scan_flags & SCAN_CERTVIZ_ENABLED_FLAG)) { if (appIdSession->tsession->tls_host) free(appIdSession->tsession->tls_host); appIdSession->tsession->tls_host = attribute_data->tlsHost; attribute_data->tlsHost = NULL; if (reInspectSSLAppId) appIdSession->scan_flags |= SCAN_SSL_HOST_FLAG; } else { free(attribute_data->tlsHost); attribute_data->tlsHost = NULL; } } if (attribute_data->tlsCname) { /* This flag is to avoid NAVL overwritting SSL provided CN */ if (!(appIdSession->scan_flags & SCAN_CERTVIZ_ENABLED_FLAG)) { if (appIdSession->tsession->tls_cname) free(appIdSession->tsession->tls_cname); appIdSession->tsession->tls_cname = attribute_data->tlsCname; attribute_data->tlsCname = NULL; if (reInspectSSLAppId) appIdSession->scan_flags |= SCAN_SSL_CERTIFICATE_FLAG; } else { free(attribute_data->tlsCname); attribute_data->tlsCname = NULL; } } if (reInspectSSLAppId) { if (attribute_data->tlsOrgUnit) { /* This flag is to avoid NAVL overwritting SSL provided ORG */ if (!(appIdSession->scan_flags & SCAN_CERTVIZ_ENABLED_FLAG)) { if (appIdSession->tsession->tls_orgUnit) free(appIdSession->tsession->tls_orgUnit); appIdSession->tsession->tls_orgUnit = attribute_data->tlsOrgUnit; attribute_data->tlsOrgUnit = NULL; } else { free(attribute_data->tlsOrgUnit); attribute_data->tlsOrgUnit = NULL; } } } } else if (ThirdPartyAppIDFoundProto(APP_ID_FTP_CONTROL, proto_list)) { if (!appidStaticConfig->ftp_userid_disabled && attribute_data->ftpCommandUser) { if (appIdSession->username) free(appIdSession->username); appIdSession->username = attribute_data->ftpCommandUser; attribute_data->ftpCommandUser = NULL; appIdSession->usernameService = APP_ID_FTP_CONTROL; setAppIdFlag(appIdSession, APPID_SESSION_LOGIN_SUCCEEDED); } } } void appSetServiceDetectorCallback(RNAServiceCallbackFCN fcn, tAppId appId, struct _Detector *userdata, tAppIdConfig *pConfig) { #ifdef TARGET_BASED AppInfoTableEntry* entry; if ((entry = appInfoEntryGet(appId, pConfig))) { if (entry->svrValidator) { if (entry->flags & APPINFO_FLAG_SERVICE_DETECTOR_CALLBACK) { _dpd.errMsg("AppId: Service detector callback already registerted for appid %d\n", appId); return; } entry->svrValidator->userdata = userdata; entry->svrValidator->detectorCallback = fcn; entry->flags |= APPINFO_FLAG_SERVICE_DETECTOR_CALLBACK; } } #endif return; } void appSetClientDetectorCallback(RNAClientAppCallbackFCN fcn, tAppId appId, struct _Detector *userdata, tAppIdConfig *pConfig) { #ifdef TARGET_BASED AppInfoTableEntry* entry; if ((entry = appInfoEntryGet(appId, pConfig))) { if (entry->clntValidator) { if (entry->flags & APPINFO_FLAG_CLIENT_DETECTOR_CALLBACK) { _dpd.errMsg("AppId: Client detector callback already registerted for appid %d\n", appId); return; } entry->clntValidator->userData = userdata; entry->clntValidator->detectorCallback = fcn; entry->flags |= APPINFO_FLAG_CLIENT_DETECTOR_CALLBACK; } } #endif return; } void appSetServiceValidator(RNAServiceValidationFCN fcn, tAppId appId, unsigned extractsInfo, tAppIdConfig *pConfig) { AppInfoTableEntry* pEntry = appInfoEntryGet(appId, pConfig); if (!pEntry) { _dpd.errMsg("AppId", "Invalid direct service AppId, %d, for %p", appId, fcn); return; } extractsInfo &= (APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED); if (!extractsInfo) { _dpd.debugMsg(DEBUG_LOG, "Ignoring direct service without info for %p with AppId %d", fcn, appId); return; } pEntry->svrValidator = ServiceGetServiceElement(fcn, NULL, pConfig); if (pEntry->svrValidator) pEntry->flags |= extractsInfo; else _dpd.errMsg("AppId", "Failed to find a service element for %p with AppId %d", fcn, appId); } void appSetLuaServiceValidator(RNAServiceValidationFCN fcn, tAppId appId, unsigned extractsInfo, struct _Detector *data) { #ifdef TARGET_BASED AppInfoTableEntry *entry; tAppIdConfig *pConfig = appIdNewConfigGet(); if ((entry = appInfoEntryGet(appId, pConfig))) { entry->flags |= APPINFO_FLAG_ACTIVE; extractsInfo &= (APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED); if (!extractsInfo) { _dpd.debugMsg(DEBUG_LOG,"Ignoring direct service without info for %p %p with AppId %d\n",fcn, data, appId); return; } entry->svrValidator = ServiceGetServiceElement(fcn, data, pConfig); if (entry->svrValidator) entry->flags |= extractsInfo; else _dpd.errMsg("AppId: Failed to find a service element for %p %p with AppId %d", fcn, data, appId); } else { _dpd.errMsg("Invalid direct service AppId, %d, for %p %p\n",appId, fcn, data); } #endif } void appSetClientValidator(RNAClientAppFCN fcn, tAppId appId, unsigned extractsInfo, tAppIdConfig *pConfig) { AppInfoTableEntry* pEntry = appInfoEntryGet(appId, pConfig); if (!pEntry) { _dpd.errMsg("AppId", "Invalid direct client application AppId, %d, for %p", appId, fcn); return; } extractsInfo &= (APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER); if (!extractsInfo) { _dpd.debugMsg(DEBUG_LOG, "Ignoring direct client application without info for %p with AppId %d", fcn, appId); return; } pEntry->clntValidator = ClientAppGetClientAppModule(fcn, NULL, &pConfig->clientAppConfig); if (pEntry->clntValidator) pEntry->flags |= extractsInfo; else _dpd.errMsg("AppId", "Failed to find a client application module for %p with AppId %d", fcn, appId); } void appSetLuaClientValidator(RNAClientAppFCN fcn, tAppId appId, unsigned extractsInfo, struct _Detector *data) { AppInfoTableEntry* entry; tAppIdConfig *pConfig = appIdNewConfigGet(); if ((entry = appInfoEntryGet(appId, pConfig))) { entry->flags |= APPINFO_FLAG_ACTIVE; extractsInfo &= (APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER); if (!extractsInfo) { _dpd.debugMsg(DEBUG_LOG,"Ignoring direct client application without info for %p %p with AppId %d\n",fcn, data, appId); return; } entry->clntValidator = ClientAppGetClientAppModule(fcn, data, &pConfig->clientAppConfig); if (entry->clntValidator) entry->flags |= extractsInfo; else _dpd.errMsg("AppId: Failed to find a client application module for %p %p with AppId %d", fcn, data, appId); } else { _dpd.errMsg("Invalid direct client application AppId, %d, for %p %p\n",appId, fcn, data); return; } } void AppIdAddUser(tAppIdData *flowp, const char *username, tAppId appId, int success) { if (flowp->username) free(flowp->username); flowp->username = strdup(username); if (!flowp->username) DynamicPreprocessorFatalMessage("Could not allocate username data"); flowp->usernameService = appId; if (success) setAppIdFlag(flowp, APPID_SESSION_LOGIN_SUCCEEDED); else clearAppIdFlag(flowp, APPID_SESSION_LOGIN_SUCCEEDED); } void AppIdAddDnsQueryInfo(tAppIdData *flow, uint16_t id, const uint8_t *host, uint8_t host_len, uint16_t host_offset, uint16_t record_type, uint16_t options_offset, bool root_query) { if (!flow->dsession) { if (!(flow->dsession = _dpd.snortAlloc(1, sizeof(*flow->dsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate dnsSession data"); } else if ((flow->dsession->state != 0) && (flow->dsession->id != id)) { AppIdResetDnsInfo(flow); } if (flow->dsession->state & DNS_GOT_QUERY) return; flow->dsession->state = DNS_GOT_QUERY; // clears any response state flow->dsession->id = id; flow->dsession->record_type = record_type; if (!flow->dsession->host) { if (root_query && !host_len) { flow->dsession->host_len = 1; flow->dsession->host_offset = 0; flow->dsession->host = strdup("."); flow->dsession->options_offset = options_offset; } else if ((host != NULL) && (host_len > 0) && (host_offset > 0)) { flow->dsession->host_len = host_len; flow->dsession->host_offset = host_offset; flow->dsession->host = dns_parse_host(host, host_len); flow->dsession->options_offset = options_offset; } } } void AppIdAddDnsResponseInfo(tAppIdData *flow, uint16_t id, const uint8_t *host, uint8_t host_len, uint16_t host_offset, uint8_t response_type, uint32_t ttl) { if (!flow->dsession) { if (!(flow->dsession = _dpd.snortAlloc(1, sizeof(*flow->dsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate dnsSession data"); } else if ((flow->dsession->state != 0) && (flow->dsession->id != id)) { AppIdResetDnsInfo(flow); } if (flow->dsession->state & DNS_GOT_RESPONSE) return; flow->dsession->state |= DNS_GOT_RESPONSE; flow->dsession->id = id; flow->dsession->response_type = response_type; flow->dsession->ttl = ttl; if (!flow->dsession->host) { if ((host != NULL) && (host_len > 0) && (host_offset > 0)) { flow->dsession->host_len = host_len; flow->dsession->host_offset = host_offset; flow->dsession->host = dns_parse_host(host, host_len); } } } void AppIdResetDnsInfo(tAppIdData *flow) { if (flow->dsession) { free(flow->dsession->host); memset(flow->dsession, 0, sizeof(*(flow->dsession))); } } void AppIdAddPayload(tAppIdData *flow, tAppId payload_id) { if (appidStaticConfig->instance_id) checkSandboxDetection(payload_id); flow->payloadAppId = payload_id; } void AppIdAddMultiPayload(tAppIdData *flow, tAppId payload_id) { if (appidStaticConfig->instance_id) checkSandboxDetection(payload_id); flow->payloadAppId = payload_id; if (flow->multiPayloadList && sfghash_find_node(flow->multiPayloadList, (const void*)&payload_id)) return; if (!flow->multiPayloadList) flow->multiPayloadList = sfghash_new(4, sizeof(tAppId), 0, NULL); sfghash_add(flow->multiPayloadList, (const void *)&payload_id, (void *)NEW_PAYLOAD_STATE); if (app_id_debug_session_flag) { SFGHASH_NODE *n; int buf_index = 0; int size = 0; tAppId cur; char b[MULTI_BUF_SIZE]; for (n = sfghash_findfirst(flow->multiPayloadList); n != 0 && size != -1;) { cur = *((tAppId*)n->key); size = sprintf(b+buf_index, "%d ", cur); buf_index+=size; n = sfghash_findnext(flow->multiPayloadList); } _dpd.logMsg("AppIdDbg %s service %d; adding payload %d to multipayload on packet %d.\n Mulipayload includes: %s\n", app_id_debug_session, flow->serviceAppId, payload_id, flow->session_packet_count, b); } } tAppId getOpenAppId(void *ssnptr) { tAppIdData *session; tAppId payloadAppId = APP_ID_NONE; if (ssnptr && (session = getAppIdData(ssnptr))) { payloadAppId = session->payloadAppId; } return payloadAppId; } /** * @returns 1 if some appid is found, 0 otherwise. */ int sslAppGroupIdLookup(void *ssnptr, const char * serverName, const char * commonName, tAppId *serviceAppId, tAppId *clientAppId, tAppId *payloadAppId) { tAppIdData *session; *serviceAppId = *clientAppId = *payloadAppId = APP_ID_NONE; if (commonName) { ssl_scan_cname((const uint8_t *)commonName, strlen(commonName), clientAppId, payloadAppId, &pAppidActiveConfig->serviceSslConfig); } if (serverName) { ssl_scan_hostname((const uint8_t *)serverName, strlen(serverName), clientAppId, payloadAppId, &pAppidActiveConfig->serviceSslConfig); } if (ssnptr && (session = getAppIdData(ssnptr))) { *serviceAppId = pickServiceAppId(session); if(*clientAppId == APP_ID_NONE) { *clientAppId = pickClientAppId(session); } if(*payloadAppId == APP_ID_NONE) { *payloadAppId = pickPayloadId(session); } } if(*serviceAppId != APP_ID_NONE || *clientAppId != APP_ID_NONE || *payloadAppId != APP_ID_NONE) { return 1; } return 0; } /* * In TLS 1.3, certificates come encrypted, hence SSL module does the * certificate decryption and provides the SNI/first-SAN/CN/ON to AppId module. * This API does the following: * 1. AppIds detection corresponding to the SNI/first-SAN/CN/ON * 2. Store the AppIds and SNI/first-SAN/CN/ON in AppID session struct. * 3. To make sure the SSL provided SNI/first-SAN/CN/ON are not further overwritten * by NAVL, appropriate flags are set. * Note: * Valid SNI: SNI->first_SAN->CN->OU * No SNI/Mismatched SNI: first_SAN->CN->OU */ void setTlsHost(void *ssnptr, const char *serverName, const char *commonName, const char *orgName, const char *subjectAltName, bool isSniMismatch, tAppId *serviceAppId, tAppId *clientAppId, tAppId *payloadAppId) { tAppIdData *session; *serviceAppId = *clientAppId = *payloadAppId = APP_ID_NONE; if (app_id_debug_session_flag) _dpd.logMsg("Received serverName=%s, commonName=%s, orgName=%s, subjectAltName=%s, isSniMismatch=%s, from SSL\n", serverName, commonName, orgName, subjectAltName, isSniMismatch?"true":"false"); if (ssnptr && (session = getAppIdData(ssnptr))) { if (!session->tsession) session->tsession = _dpd.snortAlloc(1, sizeof(*session->tsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION); session->scan_flags |= SCAN_SSL_HOST_FLAG; session->scan_flags |= SCAN_SSL_CERTIFICATE_FLAG; session->scan_flags |= SCAN_CERTVIZ_ENABLED_FLAG; if (isSniMismatch) session->scan_flags |= SCAN_SPOOFED_SNI_FLAG; if (serverName && (*serverName != '\0') && !isSniMismatch) { if (session->tsession->tls_host) free(session->tsession->tls_host); session->tsession->tls_host = strdup(serverName); session->tsession->tls_host_strlen = strlen(serverName); } if (subjectAltName && (*subjectAltName != '\0')) { if (session->tsession->tls_first_san) free(session->tsession->tls_first_san); session->tsession->tls_first_san = strdup(subjectAltName); session->tsession->tls_first_san_strlen = strlen(subjectAltName); } if (commonName && (*commonName != '\0')) { if (session->tsession->tls_cname) free(session->tsession->tls_cname); session->tsession->tls_cname = strdup(commonName); session->tsession->tls_cname_strlen = strlen(commonName); } if (orgName && (*orgName != '\0')) { if (session->tsession->tls_orgUnit) free(session->tsession->tls_orgUnit); session->tsession->tls_orgUnit = strdup(orgName); session->tsession->tls_orgUnit_strlen = strlen(orgName); } (void)scanSslParamsLookupAppId(session, (const char*)session->tsession->tls_host, isSniMismatch, (const char*)session->tsession->tls_first_san, (const char*)session->tsession->tls_cname, (const char*)session->tsession->tls_orgUnit, clientAppId, payloadAppId); *serviceAppId = pickServiceAppId(session); if (APP_ID_NONE == *clientAppId) *clientAppId = pickClientAppId(session); if (APP_ID_NONE == *payloadAppId) *payloadAppId = pickPayloadId(session); session->serviceAppId = *serviceAppId; session->clientAppId = *clientAppId; session->payloadAppId = *payloadAppId; if (app_id_debug_session_flag) _dpd.logMsg("serviceAppId %d, clientAppId %d, payloadAppId %d\n", session->serviceAppId, session->clientAppId, session->payloadAppId); } } void httpHeaderCallback (SFSnortPacket *p, HttpParsedHeaders *const headers) { tAppIdData *session; APPID_SESSION_DIRECTION direction; tAppIdConfig *pConfig = appIdActiveConfigGet(); if (thirdparty_appid_module) return; if (!p || !(session = getAppIdData(p->stream_session))) return; direction = (_dpd.sessionAPI->get_packet_direction(p) & FLAG_FROM_CLIENT) ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; #ifdef DEBUG_APP_ID_SESSIONS { char src_ip[INET6_ADDRSTRLEN]; char dst_ip[INET6_ADDRSTRLEN]; sfaddr_t *ip; src_ip[0] = 0; ip = GET_SRC_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), src_ip, sizeof(src_ip)); dst_ip[0] = 0; ip = GET_DST_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), dst_ip, sizeof(dst_ip)); fprintf(SF_DEBUG_FILE, "AppId Http Callback Session %s-%u -> %s-%u %d\n", src_ip, (unsigned)p->src_port, dst_ip, (unsigned)p->dst_port, IsTCP(p) ? IPPROTO_TCP:IPPROTO_UDP); } #endif if (!session->hsession) { if (!(session->hsession = _dpd.snortAlloc(1, sizeof(*session->hsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate httpSession data"); } if (direction == APP_ID_FROM_INITIATOR) { if (headers->host.start) { free(session->hsession->host); session->hsession->host = strndup((char *)headers->host.start, headers->host.len); session->hsession->host_buflen = headers->host.len; session->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; if (headers->url.start) { free(session->hsession->url); session->hsession->url = malloc(sizeof(HTTP_PREFIX) + headers->host.len + headers->url.len); if (session->hsession->url) { strcpy(session->hsession->url, HTTP_PREFIX); strncat(session->hsession->url, (char *)headers->host.start, headers->host.len); strncat(session->hsession->url, (char *)headers->url.start, headers->url.len); session->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } else { DynamicPreprocessorFatalMessage("httpHeaderCallback: " "Failed to allocate memory for URL in APP_ID session header\n"); } } } if (headers->userAgent.start) { free(session->hsession->useragent); session->hsession->useragent = strndup((char *)headers->userAgent.start, headers->userAgent.len); session->hsession->useragent_buflen = headers->userAgent.len; session->scan_flags |= SCAN_HTTP_USER_AGENT_FLAG; } if (headers->referer.start) { free(session->hsession->referer); session->hsession->referer = strndup((char *)headers->referer.start, headers->referer.len); session->hsession->referer_buflen = headers->referer.len; } if (headers->via.start) { free(session->hsession->via); session->hsession->via = strndup((char *)headers->via.start, headers->via.len); session->scan_flags |= SCAN_HTTP_VIA_FLAG; } } else { if (headers->via.start) { free(session->hsession->via); session->hsession->via = strndup((char *)headers->via.start, headers->via.len); session->scan_flags |= SCAN_HTTP_VIA_FLAG; } if (headers->contentType.start) { free(session->hsession->content_type); session->hsession->content_type = strndup((char *)headers->contentType.start, headers->contentType.len); session->hsession->content_type_buflen = headers->contentType.len; } if (headers->responseCode.start) { long responseCodeNum; responseCodeNum = strtoul((char *)headers->responseCode.start, NULL, 10); if (responseCodeNum > 0 && responseCodeNum < 700) { free(session->hsession->response_code); session->hsession->response_code = strndup((char *)headers->responseCode.start, headers->responseCode.len); session->hsession->response_code_buflen = headers->responseCode.len; } } } processHTTPPacket(p, session, direction, headers, pConfig); setAppIdFlag(session, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_HTTP_SESSION); _dpd.streamAPI->set_application_id(p->stream_session, pickServiceAppId(session), pickClientAppId(session), pickPayloadId(session), pickMiscAppId(session)); } static inline void ExamineRtmpMetadata(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *appIdSession) { tAppId serviceAppId = 0; tAppId clientAppId = 0; tAppId payloadAppId = 0; tAppId referredPayloadAppId = 0; char *version = NULL; httpSession *hsession; tAppIdConfig *pConfig = appIdActiveConfigGet(); if (!appIdSession->hsession) { if (!(appIdSession->hsession = _dpd.snortAlloc(1, sizeof(*appIdSession->hsession), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) DynamicPreprocessorFatalMessage("Could not allocate httpSession data"); } hsession = appIdSession->hsession; if (hsession->url) { if (((getAppIdFromUrl(NULL, hsession->url, &version, hsession->referer, &clientAppId, &serviceAppId, &payloadAppId, &referredPayloadAppId, 1, &pConfig->detectorHttpConfig)) || (getAppIdFromUrl(NULL, hsession->url, &version, hsession->referer, &clientAppId, &serviceAppId, &payloadAppId, &referredPayloadAppId, 0, &pConfig->detectorHttpConfig))) == 1) { /* do not overwrite a previously-set client or service */ if (appIdSession->clientAppId <= APP_ID_NONE) setClientAppIdData(p, direction, appIdSession, clientAppId, NULL); if (appIdSession->serviceAppId <= APP_ID_NONE) setServiceAppIdData(p, direction, appIdSession, serviceAppId, NULL, NULL); /* DO overwrite a previously-set payload */ setPayloadAppIdData(p, direction, appIdSession, payloadAppId, NULL); setReferredPayloadAppIdData(appIdSession, referredPayloadAppId); } } } void checkSandboxDetection(tAppId appId) { AppInfoTableEntry *entry; tAppIdConfig *pConfig = appIdActiveConfigGet(); if (appidStaticConfig->instance_id && pConfig) { entry = appInfoEntryGet(appId, pConfig); if (entry && entry->flags & APPINFO_FLAG_ACTIVE) { fprintf(SF_DEBUG_FILE, "add service\n"); fprintf(SF_DEBUG_FILE, "Detected AppId %d\n", entry->appId); } } } snort-2.9.20/src/dynamic-preprocessors/appid/service_state.c0000644000175000017500000003532114241075570022325 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sfxhash.h" #include "sf_dynamic_preprocessor.h" #include "service_state.h" #include "service_api.h" #include "service_base.h" /*#define DEBUG_SERVICE_STATE 1 */ //#define DEBUG_APPID_MEMCAP_PRUNING 1 STATIC SFXHASH *serviceStateCache4; STATIC SFXHASH *serviceStateCache6; #define SERVICE_STATE_CACHE_ROWS 65536 bool AppIdServiceStateReloadAdjust(bool idle, unsigned long memcap) { unsigned max_work = idle ? 512 : 8; static bool adjustStart = true; static unsigned numIpv4Entries = 0; static unsigned numIpv6Entries = 0; static unsigned numIpv4EntriesPruned = 0; static unsigned numIpv6EntriesPruned = 0; static unsigned ipv4MemUsed = 0; static unsigned ipv6MemUsed = 0; unsigned target = max_work; memcap >>= 1; if (adjustStart) { adjustStart = false; numIpv4Entries = sfxhash_count(serviceStateCache4); numIpv4EntriesPruned = 0; ipv4MemUsed = serviceStateCache4->mc.memused; numIpv6Entries = sfxhash_count(serviceStateCache6); numIpv6EntriesPruned = 0; ipv6MemUsed = serviceStateCache6->mc.memused; } #ifdef DEBUG_APPID_MEMCAP_PRUNING if (memcap < serviceStateCache4->mc.memused) { unsigned count = sfxhash_count(serviceStateCache4); _dpd.logMsg("AppId: IPv4 cache mem used = %u, num entries = %u, pruning up to %u entries\n", serviceStateCache4->mc.memused, count, (max_work < count) ? max_work : count); } #endif // DEBUG_APPID_MEMCAP_PRUNING if (SFXHASH_OK != sfxhash_change_memcap(serviceStateCache4, memcap, &max_work)) { numIpv4EntriesPruned += (target - max_work); return false; } numIpv4EntriesPruned += (target - max_work); if (target != max_work) { _dpd.logMsg("AppId: IPv4 cache pruning done - initial mem used = %u, initial entries = %u, pruned %u entries, current mem used = %u\n", ipv4MemUsed, numIpv4Entries, numIpv4EntriesPruned, serviceStateCache4->mc.memused); target = max_work; } #ifdef DEBUG_APPID_MEMCAP_PRUNING if (memcap < serviceStateCache6->mc.memused) { unsigned count = sfxhash_count(serviceStateCache6); _dpd.logMsg("AppId: IPv6 cache mem used = %u, num entries = %u, pruning up to %u entries\n", serviceStateCache6->mc.memused, count, (max_work < count) ? max_work : count); } #endif // DEBUG_APPID_MEMCAP_PRUNING if (SFXHASH_OK != sfxhash_change_memcap(serviceStateCache6, memcap, &max_work)) { numIpv6EntriesPruned += (target - max_work); return false; } numIpv6EntriesPruned += (target - max_work); if (numIpv4EntriesPruned == 0) _dpd.logMsg("AppId: IPv4 cache pruning done - initial mem used = %u, initial entries = %u, pruned %u entries, current mem used = %u\n", ipv4MemUsed, numIpv4Entries, numIpv4EntriesPruned, serviceStateCache4->mc.memused); _dpd.logMsg("AppId: IPv6 cache pruning done - initial mem used = %u, initial entries = %u, pruned %u entries, current mem used = %u\n", ipv6MemUsed, numIpv6Entries, numIpv6EntriesPruned, serviceStateCache6->mc.memused); adjustStart = true; return true; } int AppIdServiceStateInit(unsigned long memcap) { serviceStateCache4 = sfxhash_new(SERVICE_STATE_CACHE_ROWS, sizeof(AppIdServiceStateKey4), sizeof(AppIdServiceIDState), memcap >> 1, 1, NULL, NULL, 1); if (!serviceStateCache4) { _dpd.errMsg( "Failed to allocate a hash table"); return -1; } serviceStateCache6 = sfxhash_new(SERVICE_STATE_CACHE_ROWS, sizeof(AppIdServiceStateKey6), sizeof(AppIdServiceIDState), memcap >> 1, 1, NULL, NULL, 1); if (!serviceStateCache6) { _dpd.errMsg( "Failed to allocate a hash table"); return -1; } return 0; } void AppIdServiceStateCleanup(void) { if (serviceStateCache4) { sfxhash_delete(serviceStateCache4); serviceStateCache4 = NULL; } if (serviceStateCache6) { sfxhash_delete(serviceStateCache6); serviceStateCache6 = NULL; } } #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId, uint32_t cid) #else void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint32_t cid) #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId) #else void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level) #endif #endif { AppIdServiceStateKey k; SFXHASH *cache; if (sfaddr_family(ip) == AF_INET6) { k.key6.proto = proto; k.key6.port = port; memcpy(k.key6.ip, sfaddr_get_ip6_ptr(ip), sizeof(k.key6.ip)); k.key6.level = level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) k.key6.asId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) k.key6.cid = cid; #endif cache = serviceStateCache6; } else { k.key4.proto = proto; k.key4.port = port; k.key4.ip = sfaddr_get_ip4_value(ip); k.key4.level = level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) k.key4.asId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) k.key4.cid = cid; #endif cache = serviceStateCache4; } if (sfxhash_remove(cache, &k) != SFXHASH_OK) { char ipstr[INET6_ADDRSTRLEN]; ipstr[0] = 0; inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), ipstr, sizeof(ipstr)); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) _dpd.errMsg("Failed to remove from hash: %s:%u:%u:%u:%u\n", ipstr, (unsigned)proto, (unsigned)port, asId, cid); #else _dpd.errMsg("Failed to remove from hash: %s:%u:%u:%u\n",ipstr, (unsigned)proto, (unsigned)port, cid); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) _dpd.errMsg("Failed to remove from hash: %s:%u:%u:%u\n", ipstr, (unsigned)proto, (unsigned)port, asId); #else _dpd.errMsg("Failed to remove from hash: %s:%u:%u\n",ipstr, (unsigned)proto, (unsigned)port); #endif #endif } } #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId, uint32_t cid) #else AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint32_t cid) #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId) #else AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level) #endif #endif { AppIdServiceStateKey k; SFXHASH *cache; AppIdServiceIDState* ss; if (sfaddr_family(ip) == AF_INET6) { k.key6.proto = proto; k.key6.port = port; memcpy(k.key6.ip, sfaddr_get_ip6_ptr(ip), sizeof(k.key6.ip)); k.key6.level = level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) k.key6.asId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) k.key6.cid = cid; #endif cache = serviceStateCache6; } else { k.key4.proto = proto; k.key4.port = port; k.key4.ip = sfaddr_get_ip4_value(ip); k.key4.level = level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) k.key4.asId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) k.key4.cid = cid; #endif cache = serviceStateCache4; } ss = sfxhash_find(cache, &k); #ifdef DEBUG_SERVICE_STATE char ipstr[INET6_ADDRSTRLEN]; ipstr[0] = 0; inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), ipstr, sizeof(ipstr)); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) _dpd.logMsg("ServiceState: Read from hash: %s:%u:%u:%u:%u:%u %p %u %p\n", ipstr, (unsigned)proto, (unsigned)port, level, asId, cid ss, ss ? ss->state:0, ss ? ss->svc:NULL); #else _dpd.logMsg("ServiceState: Read from hash: %s:%u:%u:%u:%u %p %u %p\n",ipstr, (unsigned)proto, (unsigned)port, level, cid, ss, ss ? ss->state:0, ss ? ss->svc:NULL); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) _dpd.logMsg("ServiceState: Read from hash: %s:%u:%u:%u:%u %p %u %p\n", ipstr, (unsigned)proto, (unsigned)port, level, asId, ss, ss ? ss->state:0, ss ? ss->svc:NULL); #else _dpd.logMsg("ServiceState: Read from hash: %s:%u:%u:%u %p %u %p\n",ipstr, (unsigned)proto, (unsigned)port, level, ss, ss ? ss->state:0, ss ? ss->svc:NULL); #endif #endif #endif if (ss && ss->svc && !ss->svc->ref_count) { ss->svc = NULL; ss->state = SERVICE_ID_NEW; } return ss; } #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId, uint32_t cid) #else AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint32_t cid) #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId) #else AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level) #endif #endif { AppIdServiceStateKey k; AppIdServiceIDState *ss = NULL; SFXHASH *cache; char ipstr[INET6_ADDRSTRLEN]; if (sfaddr_family(ip) == AF_INET6) { k.key6.proto = proto; k.key6.port = port; memcpy(k.key6.ip, sfaddr_get_ip6_ptr(ip), sizeof(k.key6.ip)); k.key6.level = level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) k.key6.asId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) k.key6.cid = cid; #endif cache = serviceStateCache6; } else { k.key4.proto = proto; k.key4.port = port; k.key4.ip = sfaddr_get_ip4_value(ip); k.key4.level = level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) k.key4.asId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) k.key4.cid = cid; #endif cache = serviceStateCache4; } #ifdef DEBUG_SERVICE_STATE ipstr[0] = 0; inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), ipstr, sizeof(ipstr)); #endif if ((sfxhash_add_return_data_ptr(cache, &k, (void **)&ss) < 0) || !ss) { ipstr[0] = 0; inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), ipstr, sizeof(ipstr)); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) _dpd.errMsg("ServiceState: Failed to add to hash: %s:%u:%u:%u:%u:%u\n",ipstr, (unsigned)proto, (unsigned)port, level, asId, cid); #else _dpd.errMsg("ServiceState: Failed to add to hash: %s:%u:%u:%u:%u\n",ipstr, (unsigned)proto, (unsigned)port, level, cid); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) _dpd.errMsg("ServiceState: Failed to add to hash: %s:%u:%u:%u:%u\n",ipstr, (unsigned)proto, (unsigned)port, level, asId); #else _dpd.errMsg("ServiceState: Failed to add to hash: %s:%u:%u:%u\n",ipstr, (unsigned)proto, (unsigned)port, level); #endif #endif return NULL; } #ifdef DEBUG_SERVICE_STATE #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) _dpd.logMsg("ServiceState: Added to hash: %s:%u:%u:%u:%u %p\n", ipstr, (unsigned)proto, (unsigned)port, level, ss, asId); #else _dpd.logMsg("ServiceState: Added to hash: %s:%u:%u:%u %p\n", ipstr, (unsigned)proto, (unsigned)port, level, ss); #endif #endif if (ss) memset(ss, 0, sizeof(*ss)); return ss; } void AppIdServiceStateDumpStats(void) { _dpd.logMsg("Service State:\n"); if (serviceStateCache4) { _dpd.logMsg(" IPv4 Count: %u\n", sfxhash_count(serviceStateCache4)); _dpd.logMsg(" IPv4 Memory Limit: %u\n", serviceStateCache4->mc.memcap); _dpd.logMsg(" IPv4 Memory Used: %u\n", serviceStateCache4->mc.memused); } if (serviceStateCache6) { _dpd.logMsg(" IPv6 Count: %u\n", sfxhash_count(serviceStateCache6)); _dpd.logMsg(" IPv6 Memory Limit: %u\n", serviceStateCache6->mc.memcap); _dpd.logMsg(" IPv6 Memory Used: %u\n", serviceStateCache6->mc.memused); } } snort-2.9.20/src/dynamic-preprocessors/appid/spp_appid.h0000644000175000017500000000172214241075574021453 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SPP_APPID_H #define _SPP_APPID_H void SetupAppId(void); #endif snort-2.9.20/src/dynamic-preprocessors/appid/appId_ss.c0000644000175000017500000002363414241075341021227 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2012-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * *****************************************************************************/ /************************************************************************** * * appId_ss.c * * Authors: Hareesh Vutla * * Description: * * AppId state sharing support. * **************************************************************************/ #ifdef SIDE_CHANNEL #include "appId_ss.h" #include "appIdConfig.h" #include "common_util.h" #include "hostPortAppCache.h" #include "profiler.h" #include #include #ifdef REG_TEST typedef struct _MsgHeader { uint32_t type; uint32_t data_length; } MsgHeader; #endif typedef struct { uint32_t messages_received; uint32_t messages_sent; } AppIdSSStats; static int runtime_output_fd = -1; static uint8_t file_io_buffer[UINT16_MAX]; static AppIdSSStats appId_ss_stats; void AppIdPrintSSStats(void) { _dpd.logMsg(" AppId State Sharing:\n"); _dpd.logMsg(" Messages Received: %u\n", appId_ss_stats.messages_received); _dpd.logMsg(" Messages Sent: %u\n", appId_ss_stats.messages_sent); } void AppIdResetSSStats(void) { memset(&appId_ss_stats, 0, sizeof(appId_ss_stats)); } void AppIdPrintSSConfig(AppIdSSConfig *appId_ss_config) { if (appId_ss_config == NULL) return; #ifdef REG_TEST _dpd.logMsg(" AppId SS config:\n"); if (appId_ss_config->startup_input_file) _dpd.logMsg(" Startup Input File: %s\n", appId_ss_config->startup_input_file); if (appId_ss_config->runtime_output_file) _dpd.logMsg(" Runtime Output File: %s\n", appId_ss_config->runtime_output_file); #endif } void AppIdSSConfigFree(AppIdSSConfig *appId_ss_config) { if (appId_ss_config == NULL) return; #ifdef REG_TEST if (appId_ss_config->startup_input_file) free(appId_ss_config->startup_input_file); if (appId_ss_config->runtime_output_file) free(appId_ss_config->runtime_output_file); #endif _dpd.snortFree(appId_ss_config, sizeof(*appId_ss_config), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); } static int ConsumeAppIdSSMsg(uint32_t type, const uint8_t *msg, uint32_t msglen) { int rval = 1; switch(type) { case SC_MSG_TYPE_APPID_SS_HOST_CACHE: rval = ConsumeSSHostCache(msg, msglen); break; default: break; } appId_ss_stats.messages_received++; return rval; } #ifdef REG_TEST /* * File I/O */ static inline ssize_t Read(int fd, void *buf, size_t count) { ssize_t n; errno = 0; while ((n = read(fd, buf, count)) <= (ssize_t) count) { if (n == (ssize_t) count) return 0; if (n > 0) { buf = (uint8_t *) buf + n; count -= n; } else if (n == 0) break; else if (errno != EINTR) { _dpd.errMsg("Error reading from AppId SS message file: %s (%d)\n", strerror(errno), errno); break; } } return -1; } static inline ssize_t Write(int fd, const void *buf, size_t count) { ssize_t n; errno = 0; while ((n = write(fd, buf, count)) <= (ssize_t) count) { if (n == (ssize_t) count) return 0; if (n > 0) count -= n; else if (errno != EINTR) { _dpd.errMsg("Error writing to AppId SS message file: %s (%d)\n", strerror(errno), errno); break; } } return -1; } static int ReadAppIdSSMessagesFromFile(const char *filename) { MsgHeader *msg_header; uint8_t *msg; int rval = 0, fd, offset = 0; fd = open(filename, O_RDONLY , 0664); if (fd < 0) { if (errno == ENOENT) return 0; DynamicPreprocessorFatalMessage("Could not open %s for reading AppId SS messages from: %s (%d)\n", filename, strerror(errno), errno); } _dpd.logMsg("Reading AppId SS messages from '%s'...\n", filename); msg = file_io_buffer; while ((rval = Read(fd, msg, sizeof(*msg_header))) == 0) { msg_header = (MsgHeader *) msg; offset = sizeof(*msg_header); if ((rval = Read(fd, msg + offset, msg_header->data_length)) != 0) { _dpd.errMsg("Error reading the remaining %zu bytes of AppId SS message from file: %s (%d)\n", msg_header->data_length, strerror(errno), errno); close(fd); return rval; } if ((rval = ConsumeAppIdSSMsg(msg_header->type, msg + offset, msg_header->data_length)) != 0) { close(fd); return rval; } offset += msg_header->data_length; msg += offset; } close(fd); return 0; } static uint32_t WriteAppIdSSMsgHeader(uint8_t *msg, uint32_t type, uint32_t data_len) { MsgHeader *msg_hdr; msg_hdr = (MsgHeader *) msg; msg_hdr->type = type; msg_hdr->data_length = data_len; return 0; } #endif static int AppIdSSSCMsgHandler(SCMsgHdr *hdr, const uint8_t *msg, uint32_t msglen) { int rval = 1; if(!hdr) return rval; rval = ConsumeAppIdSSMsg(hdr->type, msg, msglen); return rval; } /* Caller should proceed writing to the 'data_ptr' ONLY upon successful return i.e 0 */ int CreateAppIdSSUpdate(void **msg_handle, void **hdr_ptr, void **data_ptr, uint32_t type, uint32_t data_len) { #ifdef REG_TEST if (!data_ptr) return -1; if (runtime_output_fd >= 0) { WriteAppIdSSMsgHeader(file_io_buffer, type, data_len); *data_ptr = (void *)(&file_io_buffer[0] + sizeof(MsgHeader)); } #else if (!msg_handle || !hdr_ptr || !data_ptr) return -1; SCMsgHdr *schdr; uint8_t *msg; int rval; if (appidStaticConfig && appidStaticConfig->send_state_sharing_updates && appidStaticConfig->appId_ss_config && appidStaticConfig->appId_ss_config->use_side_channel) { /* Allocate space for the message. */ if ((rval = _dpd.scAllocMessageTX(data_len, &schdr, &msg, msg_handle)) != 0) { _dpd.errMsg("Unable to allocate memory for enqueing AppId SS message\n"); return -1; } *hdr_ptr = (void *)schdr; *data_ptr = (void *)msg; } #endif else { return -1; } return 0; } int SendAppIdSSUpdate(void *msg_handle, void *hdr_ptr, void *data_ptr, uint32_t type, uint32_t data_len) { #ifdef REG_TEST if (runtime_output_fd >= 0) { if (Write(runtime_output_fd, file_io_buffer, sizeof(MsgHeader) + data_len) == -1) { /* Already reported Error inside the 'Write' call */ } else { appId_ss_stats.messages_sent++; } } #else if (!msg_handle || !hdr_ptr || !data_ptr) return -1; SCMsgHdr *schdr; uint8_t *msg; if (appidStaticConfig && appidStaticConfig->appId_ss_config && appidStaticConfig->appId_ss_config->use_side_channel) { schdr = (SCMsgHdr *)hdr_ptr; msg = (uint8_t *)data_ptr; schdr->type = type; schdr->timestamp = _dpd.pktTime(); _dpd.scEnqueueMessageTX(schdr, msg, data_len, msg_handle, NULL); appId_ss_stats.messages_sent++; } #endif return 0; } void AppIdSSPostConfigInit(struct _SnortConfig *sc, int unused, void *arg) { int rval, i; #ifdef REG_TEST /* Probably need to do this through a different function for Reload. But we're okay for now. */ if (appidStaticConfig && appidStaticConfig->appId_ss_config) { if (appidStaticConfig->appId_ss_config->startup_input_file) { if ((rval = ReadAppIdSSMessagesFromFile(appidStaticConfig->appId_ss_config->startup_input_file)) != 0) { _dpd.errMsg("Errors were encountered while reading AppId SS messages from file '%s'\n", appidStaticConfig->appId_ss_config->startup_input_file); } } if (appidStaticConfig->appId_ss_config->runtime_output_file) { runtime_output_fd = open(appidStaticConfig->appId_ss_config->runtime_output_file, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (runtime_output_fd < 0) { DynamicPreprocessorFatalMessage("Could not open %s for writing AppId SS messages: %s (%d)\n", appidStaticConfig->appId_ss_config->runtime_output_file, strerror(errno), errno); } } } #endif if (!appidStaticConfig->appId_ss_config->use_side_channel) { _dpd.logMsg("Could not register AppId SS message handlers. Need Side channel configuration to do so.\n"); return; } for (i = SC_MSG_TYPE_APPID_SS_MIN + 1; i < SC_MSG_TYPE_APPID_SS_MAX; ++i) { if ((rval = _dpd.scRegisterRXHandler(i, AppIdSSSCMsgHandler, NULL)) != 0) { DynamicPreprocessorFatalMessage("Unable to register AppId SS message handler\n"); } } } void AppIdCleanSS(void) { if (runtime_output_fd >= 0) { close(runtime_output_fd); runtime_output_fd = -1; } } #endif /* SIDE_CHANNEL */ snort-2.9.20/src/dynamic-preprocessors/appid/luaDetectorApi.c0000644000175000017500000045277714241075450022411 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** @defgroup LuaDetectorBaseApi LuaDetectorBaseApi * This module supports basic API towards Lua detectors. *@{ */ #include #include #include #include #include #include #include #include "client_app_base.h" #include "service_base.h" #include "luaDetectorApi.h" #include "luaDetectorModule.h" #include "luaDetectorApi.h" #include "luaDetectorFlowApi.h" #include #include "httpCommon.h" #include "sf_multi_mpse.h" #include "fw_appid.h" #include "http_url_patterns.h" #include "service_ssl.h" #include "hostPortAppCache.h" #include "appInfoTable.h" #include "ip_funcs.h" #include "lengthAppCache.h" #include "detector_dns.h" #include "app_forecast.h" #include "detector_pattern.h" #include "detector_cip.h" #define DETECTOR "Detector" #define OVECCOUNT 30 /* should be a multiple of 3 */ #define URL_LIST_STEP_SIZE 5000 typedef enum { LUA_LOG_CRITICAL = 0, LUA_LOG_ERR = 1, LUA_LOG_WARN = 2, LUA_LOG_NOTICE = 3, LUA_LOG_INFO = 4, LUA_LOG_DEBUG = 5, } LUA_LOG_LEVELS; /*static const char * LuaLogLabel = "luaDetectorApi"; */ static ThrottleInfo error_throttleInfo = {0,30,0}; #ifdef PERF_PROFILING PreprocStats luaDetectorsPerfStats; PreprocStats luaCiscoPerfStats; PreprocStats luaCustomPerfStats; #endif static void FreeDetectorAppUrlPattern(DetectorAppUrlPattern *pattern); static DetectorUserData *toDetectorUserData (lua_State *L, int index) { DetectorUserData *bar = (DetectorUserData *)lua_touserdata(L, index); if (bar == NULL) luaL_typerror(L, index, DETECTOR); return bar; } DetectorUserData *checkDetectorUserData ( lua_State *L, int index ) { DetectorUserData *bar; luaL_checktype(L, index, LUA_TUSERDATA); bar = (DetectorUserData *)luaL_checkudata(L, index, DETECTOR); if (bar == NULL) { luaL_typerror(L, index, DETECTOR); } return bar; } static DetectorUserData *pushDetectorUserData(lua_State *L) { DetectorUserData *bar = (DetectorUserData *)lua_newuserdata(L, sizeof(DetectorUserData)); if (bar) { #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"DetectorUserData %p: allocated\n\n",bar); #endif memset(bar, 0, sizeof(*bar)); if ((bar->pDetector = (Detector *)calloc(1, sizeof(Detector))) == NULL) { lua_pop(L, -1); return NULL; } luaL_getmetatable(L, DETECTOR); lua_setmetatable(L, -2); #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"Detector %p: allocated\n\n",bar->pDetector); #endif } return bar; } Detector *createDetector( lua_State *L, const char *detectorName ) { DetectorUserData *pUserData; Detector *detector; pUserData = pushDetectorUserData(L); if (!pUserData || !pUserData->pDetector) { _dpd.errMsg( "Failed to allocate memory."); return NULL; } detector = pUserData->pDetector; lua_pushvalue( L, -1 ); /*create a copy of userData */ detector->detectorUserDataRef = luaL_ref( L, LUA_REGISTRYINDEX ); detector->name = strdup(detectorName); if (!detector->name) { free(pUserData->pDetector); return NULL; } detector->myLuaState = L; pthread_mutex_init(&detector->luaReloadMutex, NULL); return detector; } /**must be called only when RNA is exitting. */ void freeDetector(Detector *detector) { tDetectorPackageInfo *pkg = &detector->packageInfo; if (!detector) return; if (detector->server.pServiceElement) { free(detector->server.pServiceElement); } if (detector->server.serviceModule.name) { free((void *)(detector->server.serviceModule.name)); } if (pkg->name) { free(pkg->name); } if (pkg->client.initFunctionName) { free(pkg->client.initFunctionName); } if (pkg->client.cleanFunctionName) { free(pkg->client.cleanFunctionName); } if (pkg->client.validateFunctionName) { free(pkg->client.validateFunctionName); } if (pkg->server.initFunctionName) { free(pkg->server.initFunctionName); } if (pkg->server.cleanFunctionName) { free(pkg->server.cleanFunctionName); } if (pkg->server.validateFunctionName) { free(pkg->server.validateFunctionName); } /*The detectorUserData itself is a userdata and therefore be freed by Lua side. */ if (detector->detectorUserDataRef != LUA_REFNIL) { DetectorUserData *pUserData; lua_rawgeti(detector->myLuaState, LUA_REGISTRYINDEX, detector->detectorUserDataRef); pUserData = checkDetectorUserData(detector->myLuaState, -1); if (pUserData) { pUserData->pDetector = NULL; } luaL_unref (detector->myLuaState, LUA_REGISTRYINDEX, detector->detectorUserDataRef); detector->detectorUserDataRef = LUA_REFNIL; } if (detector->callbackFcnName) { free(detector->callbackFcnName); } free(detector->name); free(detector->validatorBuffer); #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"Detector %p: freed\n\n",detector); #endif free(detector); } int detector_Callback( const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, const SFSnortPacket *pkt, Detector *detector, const tAppIdConfig *pConfig ) { int retValue; lua_State *myLuaState; char *callbackFn; char *detectorName; PROFILE_VARS; #ifdef PERF_PROFILING PreprocStats *pPerfStats1; PreprocStats *pPerfStats2; #endif if (!data || !flowp || !pkt || !detector) { return -10; } #ifdef PERF_PROFILING if (detector->isCustom) pPerfStats1 = &luaCustomPerfStats; else pPerfStats1 = &luaCiscoPerfStats; pPerfStats2 = detector->pPerfStats; #endif PREPROC_PROFILE_START(luaDetectorsPerfStats); PREPROC_PROFILE_START((*pPerfStats1)); PREPROC_PROFILE_START((*pPerfStats2)); myLuaState = detector->myLuaState; detector->validateParams.data = data; detector->validateParams.size = size; detector->validateParams.dir = dir; detector->validateParams.flowp = flowp; detector->validateParams.pkt = (SFSnortPacket *)pkt; callbackFn = detector->callbackFcnName; detectorName = detector->name; /* Bail out if we cannot acquire the lock on the detector. To avoid bailing out, use recursive mutex instead. */ if( pthread_mutex_trylock(&detector->luaReloadMutex)) { detector->validateParams.pkt = NULL; PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return -11; } if ((!callbackFn) || !(lua_checkstack(myLuaState, 1))) { _dpd.errMsgThrottled(&error_throttleInfo, "Detector %s: invalid LUA %s\n", detectorName, lua_tostring(myLuaState, -1)); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return -10; } lua_getglobal(myLuaState, callbackFn); #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"Detector %s: Lua Memory usage %d\n", detectorName, lua_gc(myLuaState, LUA_GCCOUNT,0)); _dpd.debugMsg(DEBUG_LOG,"Detector %s: validating\n", detectorName); #endif if (lua_pcall(myLuaState, 0, 1, 0 )) { _dpd.errMsg("Detector %s: Error validating %s\n", detectorName, lua_tostring(myLuaState, -1)); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return -10; } /**detectorFlows must be destroyed after each packet is processed.*/ sflist_static_free_all(&allocatedFlowList, freeDetectorFlow); /* retrieve result */ if (!lua_isnumber(myLuaState, -1)) { _dpd.errMsg("Detector %s: Validator returned non-numeric value\n", detectorName); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); retValue = -10; } retValue = lua_tonumber(myLuaState, -1); lua_pop(myLuaState, 1); /* pop returned value */ /*lua_settop(myLuaState, 0); */ #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"Detector %s: Validator returned %d\n", detectorName, retValue); #endif detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return retValue; } static int Detector_registerClientCallback(lua_State *L) { tAppId appId; const char *callback; Detector *detector; int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); appId = lua_tonumber(L, index++); callback = lua_tostring(L, index++); if (!detectorUserData || !callback) { lua_pushnumber(L, -1); return 1; /*number of results */ } detector = detectorUserData->pDetector; if (!(detector->callbackFcnName = strdup(callback))) { lua_pushnumber(L, -1); return 1; } appSetClientDetectorCallback(detector_Callback, appId, detector, detector->pAppidNewConfig); lua_pushnumber(L, 0); return 1; } static int Detector_registerServiceCallback(lua_State *L) { tAppId appId; const char *callback; Detector *detector; int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); appId = lua_tonumber(L, index++); callback = lua_tostring(L, index++); if (!detectorUserData || !callback) { lua_pushnumber(L, -1); return 1; /*number of results */ } detector = detectorUserData->pDetector; if (!(detector->callbackFcnName = strdup(callback))) { lua_pushnumber(L, -1); return 1; } appSetServiceDetectorCallback(detector_Callback, appId, detector, detector->pAppidNewConfig); lua_pushnumber(L, 0); return 1; } /*converts Luastring to C compatible string. */ static int storeLuaString(const char *LuaString, char **CString) { char *string = *CString; if (!LuaString) { return 0; } *CString = strdup(LuaString); if (*CString == NULL) { *CString = string; return -1; } if (string) { free(string); } return 0; } /*check service element, Allocate if necessary */ int checkServiceElement( Detector *detector ) { if (!detector->server.pServiceElement) { detector->server.pServiceElement = calloc(1, sizeof(*detector->server.pServiceElement)); if (!detector->server.pServiceElement) { return 0; } detector->server.pServiceElement->name = detector->server.serviceModule.name; } return 1; } /**Creates a new detector instance. Creates a new detector instance and leaves the instance * on stack. This is the first call by a lua detector to create and instance. Later calls * provide the detector instance. * * @param Lua_State* - Lua state variable. * @param serviceName/stack - name of service * @param pValidator/stack - service validator function name * @param pFini/stack - service clean exit function name * @return int - Number of elements on stack, which should be 1 if success 0 otherwise. * @return detector - a detector instance on stack if successful */ static int service_init(lua_State *L) { const char *pValidator; const char *pServiceName; const char *pFini; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); pServiceName = lua_tostring(L, 2); pValidator = lua_tostring(L, 3); pFini = lua_tostring(L, 4); if ((!detectorUserData) || (!pServiceName) || (!pValidator) || (!pFini)) { return 0; } detector = detectorUserData->pDetector; lua_getglobal(L, pValidator); lua_getglobal(L, pFini); if (!(lua_isfunction(L, -1)) || !(lua_isfunction(L, -2))) { _dpd.errMsg("%s: attempted setting validator/fini to non-function\n",detector->server.serviceModule.name); lua_pop(L, 2); return 0; } lua_pop(L, 2); /*old value is preserved so no error checks needed. */ if (!detector->server.serviceModule.name) storeLuaString(pServiceName, (char **)&detector->server.serviceModule.name); storeLuaString(pValidator, &detector->packageInfo.server.validateFunctionName); storeLuaString(pFini, &detector->packageInfo.server.cleanFunctionName); /*create a ServiceElement */ if (checkServiceElement(detector)) { detector->server.pServiceElement->validate = validateAnyService; detector->server.pServiceElement->userdata = detector; detector->server.pServiceElement->detectorType = DETECTOR_TYPE_DECODER; } #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"allocated detector = %p\n",detector); #endif return 1; } /**Register a pattern for fast pattern matching. Lua detector calls this function to register a pattern * for fast pattern matching. This is similar to registerPattern in traditional C detector. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param protocol/stack - protocol type. Values can be {tcp=6, udp=17 } * @param pattern/stack - pattern string. * @param size/stack - number of bytes in pattern * @param position/stack - position offset where to start matching pattern. * @return int - Number of elements on stack, which is always 1. * @return status/stack - 0 if successful, -1 otherwise. */ static int service_registerPattern( lua_State *L ) { int protocol; size_t size; const char *pattern; unsigned int position; Detector *detector; int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); protocol = lua_tonumber(L, index++); pattern = lua_tostring(L, index++); size = lua_tonumber(L, index++); position = lua_tonumber(L, index++); if ((pattern == NULL) || (detectorUserData == NULL)) { lua_pushnumber(L, -1); return 1; /*number of results */ } detector = detectorUserData->pDetector; /*Note: we can not give callback into lua directly so we have to */ /*give a local callback function, which will do demuxing and */ /*then call lua callback function. */ /*mpse library does not hold reference to pattern therefore we dont need to allocate it. */ ServiceRegisterPatternDetector(validateAnyService, protocol, (uint8_t *)pattern, size, position, detector, detector->server.serviceModule.name); lua_pushnumber(L, 0); return 1; /*number of results */ } static int common_registerAppId( lua_State *L ) { unsigned int appId; Detector *detector; int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); appId = lua_tonumber(L, index++); if (detectorUserData == NULL) { lua_pushnumber(L, -1); return 1; /*number of results */ } detector = detectorUserData->pDetector; if (detector->packageInfo.server.initFunctionName) appSetLuaServiceValidator(validateAnyService, appId, APPINFO_FLAG_SERVICE_ADDITIONAL, (void *)detector); if (detector->packageInfo.client.initFunctionName) appSetLuaClientValidator(validateAnyClientApp, appId, APPINFO_FLAG_CLIENT_ADDITIONAL, (void *)detector); appInfoSetActive(appId, true); lua_pushnumber(L, 0); return 1; /*number of results */ } static int Detector_htons( lua_State *L ) { unsigned short aShort; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); aShort = lua_tonumber(L, 2); if (detectorUserData == NULL) { lua_pushnumber(L, -1); return 1; } lua_pushnumber(L, htons(aShort)); return 1; } static int Detector_htonl( lua_State *L ) { unsigned int anInt; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); anInt = lua_tonumber(L, 2); if (detectorUserData == NULL) { lua_pushnumber(L, -1); return 1; } lua_pushnumber(L, htonl(anInt)); return 1; } /**Logs messages from detectors into wherever /etc/syslog.conf directs them. * examples are: * detector:log(DC.logLevel.warning, 'a warning') * *@param level - level of message. See DetectorCommon for enumeration. *@param message - message to be logged. */ static int Detector_logMessage( lua_State *L ) { unsigned int level; const char *message; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); level = lua_tonumber(L, 2); message = lua_tostring(L, 3); if (detectorUserData == NULL) { return 0; } detector = detectorUserData->pDetector; switch (level) { case LUA_LOG_CRITICAL: _dpd.fatalMsg("%s:%s\n",detector->server.serviceModule.name, message); break; case LUA_LOG_ERR: _dpd.errMsg("%s:%s\n",detector->server.serviceModule.name, message); break; case LUA_LOG_WARN: _dpd.errMsg("%s:%s\n",detector->server.serviceModule.name, message); break; case LUA_LOG_NOTICE: _dpd.logMsg("%s:%s\n",detector->server.serviceModule.name, message); break; case LUA_LOG_INFO: _dpd.logMsg("%s:%s\n",detector->server.serviceModule.name, message); break; case LUA_LOG_DEBUG: DEBUG_WRAP(DebugMessage(DEBUG_APPID, "%s:%s\n",detector->server.serviceModule.name, message);); break; default: break; } return 0; } /** Analyze application payload * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param major/stack - major number of application * @param minor/stack - minor number of application * @param flags/stack - any flags * @return int - Number of elements on stack, which is always 0. */ static int service_analyzePayload( lua_State *L ) { unsigned int payloadId; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); payloadId = lua_tonumber(L, 2); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; detector->validateParams.flowp->payloadAppId = payloadId; lua_pushnumber(L, 0); return 1; } /**Design notes: Due to following two design limitations: * a. lua validate functions, known only at runtime, can not be wrapped inside unique * C functions at runtime and * b. core engine can not call lua functions directly. * * There must be a common validate function in C that in turn calls relevent Lua functions. * Right now there is only one detector so there is a one-to-one mapping, but the framework * will have to support multiple detectors in production environment. Core engine API will be * changed to take an additional void* that will be used to call only a unique detector. */ /*Common validate function that wraps lua based validate functions. */ int validateAnyService(ServiceValidationArgs *args) { int retValue; lua_State *myLuaState = NULL; const char *serverName; struct _Detector *detector = args->userdata; PROFILE_VARS; #ifdef PERF_PROFILING PreprocStats *pPerfStats1; PreprocStats *pPerfStats2; #endif if (!detector) { _dpd.errMsg( "invalid LUA parameters"); return SERVICE_ENULL; } #ifdef PERF_PROFILING if (detector->isCustom) pPerfStats1 = &luaCustomPerfStats; else pPerfStats1 = &luaCiscoPerfStats; pPerfStats2 = detector->pPerfStats; #endif PREPROC_PROFILE_START(luaDetectorsPerfStats); PREPROC_PROFILE_START((*pPerfStats1)); PREPROC_PROFILE_START((*pPerfStats2)); myLuaState = detector->myLuaState; detector->validateParams.data = args->data; detector->validateParams.size = args->size; detector->validateParams.dir = args->dir; detector->validateParams.flowp = args->flowp; detector->validateParams.pkt = args->pkt; serverName = detector->name; /*Note: Some frequently used header fields may be extracted and stored in detector for */ /*better performance. */ pthread_mutex_lock(&detector->luaReloadMutex); if ((!detector->packageInfo.server.validateFunctionName) || !(lua_checkstack(myLuaState, 1))) { _dpd.errMsgThrottled(&error_throttleInfo, "server %s: invalid LUA %s\n", serverName, lua_tostring(myLuaState, -1)); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END((luaDetectorsPerfStats)); return SERVICE_ENULL; } lua_getglobal(myLuaState, detector->packageInfo.server.validateFunctionName); #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"server %s: Lua Memory usage %d\n",serverName, lua_gc(myLuaState, LUA_GCCOUNT,0)); _dpd.debugMsg(DEBUG_LOG,"server %s: validating\n",serverName); #endif if (lua_pcall(myLuaState, 0, 1, 0 )) { /*Runtime Lua errors are suppressed in production code since detectors are written for efficiency */ /*and with defensive minimum checks. Errors are dealt as exceptions that dont impact processing */ /*by other detectors or future packets by the same detector. */ _dpd.errMsg("server %s: error validating %s\n",serverName, lua_tostring(myLuaState, -1)); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return SERVICE_ENULL; } /**detectorFlows must be destroyed after each packet is processed.*/ sflist_static_free_all(&allocatedFlowList, freeDetectorFlow); /* retrieve result */ if (!lua_isnumber(myLuaState, -1)) { _dpd.errMsg("server %s: validator returned non-numeric value\n",serverName); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return SERVICE_ENULL; } retValue = lua_tonumber(myLuaState, -1); lua_pop(myLuaState, 1); /* pop returned value */ /*lua_settop(myLuaState, 0); */ #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"server %s: Validator returned %d\n",serverName, retValue); #endif detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return retValue; } /**design: dont store serviceId in detector structure since a single detector * can get serviceId for multiple protocols. For example SIP which gets Id for RTP and * SIP services. */ /**Get service id from database, given service name. Lua detectors call this function at init time * get get a service Id (an integer) from database. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param serviceName/stack - Name of service * @return int - Number of elements on stack, which is always 1. * @return serviceId/stack - serviceId if successful, -1 otherwise. */ static int service_getServiceId( lua_State *L ) { DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { lua_pushnumber(L, 0); return 1; } lua_pushnumber(L, detectorUserData->pDetector->server.serviceId); return 1; } /** * Design Notes: In these APIs, three different AppID contexts - pAppidNewConfig, pAppidOldConfig * and pAppidActiveConfig are used. pAppidNewConfig is used in APIs related to the loading of the * detector such as service_addPorts(), client_registerPattern(), etc. A detector is loaded either * during reload or at initialization. Use of pAppidNewConfig will cause the data structures related * to the detector such as service ports, patterns, etc to be saved in the new AppID context. * * The new AppID context becomes active at the end of initialization or at reload swap. * FinalizeLuaModules() is called at this time, which changes all the detectors' pAppidActiveConfig * references to the new context. Also, pAppidOldConfig will be changed to point to the previous * AppID context. In the packet processing APIs such as service_addService(), client_addUser(), etc. * pAppidActiveConfig is used. * * In the cleanup APIs such as service_removePorts(), Detector_fini(), etc., data structures in the * old AppID conext need to be freed. Therefore, pAppidOldConfig is used in these APIs. */ /**Add port for a given service. Lua detectors call this function to register ports on which a * given service is expected to run. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param protocol/stack - protocol type. Values can be {tcp=6, udp=17 } * @param port/stack - port number to register. * @return int - Number of elements on stack, which is always 1. * @return status/stack - 0 if successful, -1 otherwise. */ static int service_addPorts( lua_State *L ) { RNAServiceValidationPort pp; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); pp.proto = lua_tonumber(L, 2); pp.port = lua_tonumber(L, 3); pp.reversed_validation = lua_tonumber(L, 5); pp.validate = &validateAnyService; if (!detectorUserData || ((pp.proto != IPPROTO_UDP) && (pp.proto != IPPROTO_TCP)) || !pp.port) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; if (ServiceAddPort(&pp, &detector->server.serviceModule, (void*)detector, detector->pAppidNewConfig)) { lua_pushnumber(L, -1); return 1; } detector->server.pServiceElement->ref_count++; lua_pushnumber(L, 0); return 1; } /**Remove all ports for a given service. Lua detectors call this function to remove ports for this service * when exiting. This function is not used currently by any detectors. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1. * @return status/stack - 0 if successful, -1 otherwise. */ static int service_removePorts( lua_State *L ) { /*RNAServiceValidationFCN validate */ /*from initAPI */ Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; detectorRemoveAllPorts(detector, detector->pAppidOldConfig); lua_pushnumber(L, 0); return 1; } /**Shared function between Lua API and RNA core. */ void detectorRemoveAllPorts ( Detector *detector, tAppIdConfig *pConfig ) { ServiceRemovePorts(&validateAnyService, (void*)detector, pConfig); } /**Set service name. Lua detectors call this function to set service name. It is preferred to set service name * when a detector is created. Afterwards there is rarely a need to change service name. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param serviceName/stack - Name of service * @return int - Number of elements on stack, which is always 1. * @return status/stack - 0 if successful, -1 otherwise. */ static int service_setServiceName( lua_State *L ) { #if 0 Detector *detector; char *serviceName; int retValue = -1; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); serviceName = (char *)lua_tostring(L, 2); if (!detectorUserData || !serviceName) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; retValue = storeLuaString(serviceName, (char **)&detector->server.serviceModule.name); lua_pushnumber(L, retValue); return 1; #else lua_pushnumber(L, 0); return 1; #endif } /**Get service name. Lua detectors call this function to get service name. There is * rarely a need to change service name. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1. * @return serviceName/stack - service name if successful, nil otherwise. */ static int service_getServiceName( lua_State *L ) { Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { return 0; } detector = detectorUserData->pDetector; lua_pushstring(L, detector->server.serviceModule.name); return 1; } /**Is this a customer defined detector. Lua detectors can call this function to verify if the detector * was created by Sourcefire or not. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1. * @return integer/stack - -1 if failed, 0 if sourcefire created, 1 otherwise. */ static int service_isCustomDetector( lua_State *L ) { Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; lua_pushnumber(L, detector->isCustom); return 1; } /**Set service validator Lua function name. Lua detectors use this function to set a lua function name * as service validator function. It is preferred to set validatorname when a detector is created. * Afterwards there is rarely a need to change service name. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param validatorName/stack - Name of service validator * @return int - Number of elements on stack, which is always 0. */ static int service_setValidator( lua_State *L ) { Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); const char *pValidator; if (detectorUserData == NULL) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; pValidator = lua_tostring(L, 2); lua_getglobal(L, pValidator); if (!lua_isfunction(L, -1)) { _dpd.errMsg("%s: attempted setting validator to non-function\n",detector->server.serviceModule.name); lua_pop(L, 1); lua_pushnumber(L, -1); return 1; } lua_pop(L, 1); if (storeLuaString(pValidator, &detector->packageInfo.server.validateFunctionName) == -1) { _dpd.errMsg( "memory allocation failure"); lua_pushnumber(L, -1); return 1; } lua_pushnumber(L, 0); return 1; } /** Add data (validator function name) to a flow. Detector use this function when confirming a flow * belongs to this service. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param sourcePort/stack - Source port number. * @return int - Number of elements on stack, which is always 0. */ static int service_addDataId( lua_State *L ) { Detector *detector; uint16_t sport; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); sport = lua_tonumber(L, 2); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !checkServiceElement(detectorUserData->pDetector) || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; AppIdFlowdataAddId(detector->validateParams.flowp, sport, detector->server.pServiceElement); lua_pushnumber(L, 0); return 1; } /** Add service id to a flow. Positive identification by a detector. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param serviceId/stack - id of service postively identified on this flow. * @param vendorName/stack - name of vendor of service. This is optional. * @param version/stack - version of service. This is optional. * @return int - Number of elements on stack, which is always 1. * @return int/stack - values from enum SERVICE_RETCODE */ static int service_addService( lua_State *L ) { char *vendor, *version; unsigned int serviceId, retValue = SERVICE_ENULL; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); serviceId = lua_tonumber(L, 2); vendor = (char *)luaL_optstring(L, 3, NULL); version = (char *)luaL_optstring(L, 4, NULL); /*check inputs (vendor and version may be null) and whether this function is */ /*called in context of a packet */ if (!detectorUserData || !checkServiceElement(detectorUserData->pDetector) || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, SERVICE_ENULL); return 1; } detector = detectorUserData->pDetector; /*Phase2 - discuss RNAServiceSubtype will be maintained on lua side therefore the last parameter on the following call is NULL. */ /*Subtype is not displayed on DC at present. */ retValue = AppIdServiceAddService(detector->validateParams.flowp, detector->validateParams.pkt, detector->validateParams.dir, detector->server.pServiceElement, appGetAppFromServiceId(serviceId, detector->pAppidActiveConfig), vendor, version, NULL, NULL); lua_pushnumber(L, retValue); return 1; } /**Function confirms the flow is not running this service. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1. * @return int/stack - values from enum SERVICE_RETCODE */ static int service_failService( lua_State *L ) { unsigned int retValue = SERVICE_ENULL; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); /*check inputs (vendor and version may be null) and whether this function is */ /*called in context of a packet */ if (!detectorUserData || !checkServiceElement(detectorUserData->pDetector) || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, SERVICE_ENULL); return 1; } detector = detectorUserData->pDetector; retValue = AppIdServiceFailService(detector->validateParams.flowp, detector->validateParams.pkt, detector->validateParams.dir, detector->server.pServiceElement, APPID_SESSION_DATA_NONE, detector->pAppidActiveConfig, NULL); lua_pushnumber(L, retValue); return 1; } /**Detector use this function to indicate the flow may belong to this flow. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1. * @return int/stack - values from enum SERVICE_RETCODE */ static int service_inProcessService( lua_State *L ) { unsigned int retValue = -1; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); /*check inputs (vendor and version may be null) and whether this function is */ /*called in context of a packet */ if (!detectorUserData || !checkServiceElement(detectorUserData->pDetector) || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, SERVICE_ENULL); return 1; } detector = detectorUserData->pDetector; retValue = AppIdServiceInProcess(detector->validateParams.flowp, detector->validateParams.pkt, detector->validateParams.dir, detector->server.pServiceElement, NULL); lua_pushnumber(L, retValue); return 1; } /**Detector use this function to indicate error in service identification. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1. * @return int/stack - values from enum SERVICE_RETCODE */ static int service_inCompatibleData( lua_State *L ) { unsigned int retValue = SERVICE_ENULL; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); /*check inputs (vendor and version may be null) and whether this function is */ /*called in context of a packet */ if (!detectorUserData || !checkServiceElement(detectorUserData->pDetector) || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, SERVICE_ENULL); return 1; } detector = detectorUserData->pDetector; retValue = AppIdServiceIncompatibleData(detector->validateParams.flowp, detector->validateParams.pkt, detector->validateParams.dir, detector->server.pServiceElement, APPID_SESSION_DATA_NONE, detector->pAppidActiveConfig, NULL); lua_pushnumber(L, retValue); return 1; } /** Get size of current packet. It should be noted that due to restrictions on sharing pointers * between C and Lua, packet data is maintained on C side. Lua side can get specific fields, run * memcmp and pattern matching on packet data. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1 if successful, 0 otherwise. * @return packetSize/stack - size of packet on stack, if successful. */ static int Detector_getPacketSize( lua_State *L ) { Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { return 0; } detector = detectorUserData->pDetector; lua_pushnumber(L, detector->validateParams.size); return 1; } /**Get packet direction. A flow/session maintains initiater and responder sides. A packet direction * is determined wrt to the original initiater. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is always 1 if successful, 0 otherwise. * @return packetDir/stack - direction of packet on stack, if successful. */ static int Detector_getPacketDir( lua_State *L ) { Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { /*can not return 0 in case of error, since 0 can be a valid value. */ return 0; } detector = detectorUserData->pDetector; lua_pushnumber(L, detector->validateParams.dir); return 1; } /**Perform a pcre match with grouping. A simple regular expression match with no grouping * can also be performed. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of group matches. May be 0 or more. * @return matchedStrings/stack - matched strings are pushed on stack starting with group 0. * There may be 0 or more strings. */ static int Detector_getPcreGroups( lua_State *L ) { Detector *detector; char *pattern; unsigned int offset; pcre *re; int ovector[OVECCOUNT]; const char *error; int erroffset; int rc, i; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); pattern = (char *)lua_tostring(L, 2); offset = lua_tonumber(L, 3); /*offset can be zero, no check necessary. */ if ((pattern == NULL) || (detectorUserData == NULL)) { return 0; } detector = detectorUserData->pDetector; { /*compile the regular expression pattern, and handle errors */ re = pcre_compile( pattern, /*the pattern */ PCRE_DOTALL, /*default options - dot matches everything including newline */ &error, /*for error message */ &erroffset, /*for error offset */ NULL); /*use default character tables */ if (re == NULL) { _dpd.errMsg("PCRE compilation failed at offset %d: %s\n",erroffset, error); return 0; } /*pattern match against the subject string. */ rc = pcre_exec( re, /*compiled pattern */ NULL, /*no extra data */ (char *)detector->validateParams.data, /*subject string */ detector->validateParams.size, /*length of the subject */ offset, /*offset 0 */ 0, /*default options */ ovector, /*output vector for substring information */ OVECCOUNT); /*number of elements in the output vector */ if (rc < 0) { /*Matching failed: clubbing PCRE_ERROR_NOMATCH with other errors. */ pcre_free(re); return 0; } /*Match succeded */ /*printf("\nMatch succeeded at offset %d", ovector[0]); */ pcre_free(re); if (rc == 0) { /*overflow of matches */ rc = OVECCOUNT/3; /*printf("ovector only has room for %d captured substrings", rc - 1); */ _dpd.errMsg("ovector only has room for %d captured substrings\n",rc - 1); } } lua_checkstack (L, rc); for (i = 0; i < rc; i++) { /*printf("%2d: %.*s\n", i, , substring_start); */ lua_pushlstring(L, (char *)detector->validateParams.data + ovector[2*i], ovector[2*i+1] - ovector[2*i]); } return rc; } /**Performs a simple memory comparison. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param pattern/stack - pattern to be matched. * @param patternLenght/stack - length of pattern * @param offset/stack - offset into packet payload where matching should start. * * @return int - Number of group matches. May be 1 if successful, and 0 if error is encountered. * @return memCmpResult/stack - returns -1,0,1 based on memcmp result. */ static int Detector_memcmp( lua_State *L ) { Detector *detector; char *pattern; unsigned int patternLen; unsigned int offset; int rc; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); pattern = (char *)lua_tostring(L, 2); patternLen = lua_tonumber(L, 3); offset = lua_tonumber(L, 4); /*offset can be zero, no check necessary. */ if ((detectorUserData == NULL) || (pattern == NULL)) { return 0; } detector = detectorUserData->pDetector; rc = memcmp((char *)detector->validateParams.data + offset, pattern, patternLen); lua_checkstack (L, 1); lua_pushnumber(L, rc); return 1; } /**Get Packet Protocol Type * * @param Lua_State* - Lua state variable. * @return int - Number of elements on stack, which is protocol type if successful, 0 otherwise. * @return protocol type TCP or UDP */ static int Detector_getProtocolType ( lua_State *L ) { Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt || !IPH_IS_VALID(detectorUserData->pDetector->validateParams.pkt)) { lua_checkstack (L, 1); lua_pushnumber(L, 0); return 1; } detector = detectorUserData->pDetector; lua_checkstack (L, 1); lua_pushnumber(L, GET_IPH_PROTO(detector->validateParams.pkt)); return 1; } /**Get source IP address from IP header. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return IPv4/stack - Source IPv4 addresss. */ static int Detector_getPktSrcIPAddr( lua_State *L ) { Detector *detector; sfaddr_t *ipAddr; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { return 0; } detector = detectorUserData->pDetector; ipAddr = GET_SRC_IP(detector->validateParams.pkt); lua_checkstack (L, 1); lua_pushnumber(L, sfaddr_get_ip4_value(ipAddr)); return 1; } /**Get source port number from IP header. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return portNumber/stack - source port number. */ static int Detector_getPktSrcPort( lua_State *L ) { Detector *detector; unsigned int port; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { return 0; } detector = detectorUserData->pDetector; port = detector->validateParams.pkt->src_port; lua_checkstack (L, 1); lua_pushnumber(L, port); return 1; } /**Get destination port number from IP header. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return portNumber/stack - destination Port number. */ static int Detector_getPktDstPort( lua_State *L ) { Detector *detector; unsigned int port; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { return 0; } detector = detectorUserData->pDetector; port = detector->validateParams.pkt->dst_port; lua_checkstack (L, 1); lua_pushnumber(L, port); return 1; } /**Get destination IP address from IP header. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return IPv4/stack - destination IPv4 addresss. */ static int Detector_getPktDstIPAddr( lua_State *L ) { Detector *detector; sfaddr_t *ipAddr; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { return 0; } detector = detectorUserData->pDetector; ipAddr = GET_DST_IP(detector->validateParams.pkt); lua_checkstack (L, 1); lua_pushnumber(L, sfaddr_get_ip4_value(ipAddr)); return 1; } /**Get packet count. This is used mostly for printing packet sequence * number when RNA is being tested with a pcap file. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return packetCount/stack - Total packet processed by RNA. */ static int Detector_getPktCount( lua_State *L ) { DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); if (detectorUserData == NULL) { return 0; } lua_checkstack (L, 1); lua_pushnumber(L, app_id_processed_packet_count); return 1; } int validateAnyClientApp( const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, Detector *detector, const tAppIdConfig *pConfig ) { int retValue; lua_State *myLuaState; char *validateFn; char *clientName; PROFILE_VARS; #ifdef PERF_PROFILING PreprocStats *pPerfStats1; PreprocStats *pPerfStats2; #endif if (!data || !flowp || !pkt || !detector) { return CLIENT_APP_ENULL; } #ifdef PERF_PROFILING if (detector->isCustom) pPerfStats1 = &luaCustomPerfStats; else pPerfStats1 = &luaCiscoPerfStats; pPerfStats2 = detector->pPerfStats; #endif PREPROC_PROFILE_START(luaDetectorsPerfStats); PREPROC_PROFILE_START((*pPerfStats1)); PREPROC_PROFILE_START((*pPerfStats2)); myLuaState = detector->myLuaState; detector->validateParams.data = data; detector->validateParams.size = size; detector->validateParams.dir = dir; detector->validateParams.flowp = flowp; detector->validateParams.pkt = (SFSnortPacket *)pkt; validateFn = detector->packageInfo.client.validateFunctionName; clientName = detector->name; pthread_mutex_lock(&detector->luaReloadMutex); if ((!validateFn) || !(lua_checkstack(myLuaState, 1))) { _dpd.errMsgThrottled(&error_throttleInfo, "client %s: invalid LUA %s\n", clientName, lua_tostring(myLuaState, -1)); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return CLIENT_APP_ENULL; } lua_getglobal(myLuaState, validateFn); #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"client %s: Lua Memory usage %d\n",clientName, lua_gc(myLuaState, LUA_GCCOUNT,0)); _dpd.debugMsg(DEBUG_LOG,"client %s: validating\n",clientName); #endif if (lua_pcall(myLuaState, 0, 1, 0 )) { _dpd.errMsg("client %s: error validating %s\n",clientName, lua_tostring(myLuaState, -1)); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return SERVICE_ENULL; } /**detectorFlows must be destroyed after each packet is processed.*/ sflist_static_free_all(&allocatedFlowList, freeDetectorFlow); /* retrieve result */ if (!lua_isnumber(myLuaState, -1)) { _dpd.errMsg("client %s: validator returned non-numeric value\n",clientName); detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); retValue = SERVICE_ENULL; } retValue = lua_tonumber(myLuaState, -1); lua_pop(myLuaState, 1); /* pop returned value */ /*lua_settop(myLuaState, 0); */ #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"client %s: Validator returned %d\n",clientName, retValue); #endif detector->validateParams.pkt = NULL; pthread_mutex_unlock(&detector->luaReloadMutex); PREPROC_PROFILE_END((*pPerfStats2)); PREPROC_PROFILE_END((*pPerfStats1)); PREPROC_PROFILE_END(luaDetectorsPerfStats); return retValue; } static int client_registerPattern(lua_State *L) { int protocol; size_t size; const char *pattern; unsigned int position; Detector *detector; int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); protocol = lua_tonumber(L, index++); pattern = lua_tostring(L, index++); size = lua_tonumber(L, index++); position = lua_tonumber(L, index++); if ((pattern == NULL) || (detectorUserData == NULL)) { lua_pushnumber(L, -1); return 1; /*number of results */ } detector = detectorUserData->pDetector; /*Note: we can not give callback into lua directly so we have to */ /*give a local callback function, which will do demuxing and */ /*then call lua callback function. */ /*mpse library does not hold reference to pattern therefore we dont need to allocate it. */ detector->client.appModule.userData = detector; clientAppLoadForConfigCallback((void *) &(detector->client.appModule), &detector->pAppidNewConfig->clientAppConfig); ClientAppRegisterPattern(validateAnyClientApp, protocol, (uint8_t*)pattern, size, position, 0, (void *)detector, &detector->pAppidNewConfig->clientAppConfig); lua_pushnumber(L, 0); return 1; /*number of results */ } /**Creates a new detector instance. Creates a new detector instance and leaves the instance * on stack. This is the first call by a lua detector to create and instance. Later calls * provide the detector instance. * * @param Lua_State* - Lua state variable. * @param serviceName/stack - name of service * @param pValidator/stack - service validator function name * @param pFini/stack - service clean exit function name * @return int - Number of elements on stack, which should be 1 if success 0 otherwise. * @return detector - a detector instance on stack if successful */ static int client_init (lua_State *L) { /*nothing to do */ return 0; } static int service_addClient (lua_State *L) { tAppId clientAppId, serviceId; const char *version; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); clientAppId = lua_tonumber(L, 2); serviceId = lua_tonumber(L, 3); version = lua_tostring(L, 4); if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt || !version) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; AppIdAddClientApp(detector->validateParams.pkt, detector->validateParams.dir, detector->pAppidActiveConfig, detector->validateParams.flowp, serviceId, clientAppId, version); lua_pushnumber(L, 0); return 1; } static int client_addApp( lua_State *L ) { unsigned int serviceId, productId; const char *version; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); serviceId = lua_tonumber(L, 2); productId = lua_tonumber(L, 4); version = lua_tostring(L, 5); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt || !version) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; if (!detector->client.appModule.api) { lua_pushnumber(L, -1); return 1; } detector->client.appModule.api->add_app(detector->validateParams.pkt, (APPID_SESSION_DIRECTION) detector->validateParams.dir, detector->pAppidActiveConfig, detector->validateParams.flowp, appGetAppFromServiceId(serviceId, detector->pAppidActiveConfig), appGetAppFromClientId(productId, detector->pAppidActiveConfig), version); lua_pushnumber(L, 0); return 1; } static int client_addInfo( lua_State *L ) { const char *info; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); info = lua_tostring(L, 2); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt || !info) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; if (!detector->client.appModule.api) { lua_pushnumber(L, -1); return 1; } detector->client.appModule.api->add_info(detector->validateParams.flowp, info); lua_pushnumber(L, 0); return 1; } static int client_addUser( lua_State *L ) { unsigned int serviceId; const char *userName; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); userName = lua_tostring(L, 2); serviceId = lua_tonumber(L, 3); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt || !userName) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; if (!detector->client.appModule.api) { lua_pushnumber(L, -1); return 1; } detector->client.appModule.api->add_user(detector->validateParams.flowp, userName, appGetAppFromServiceId(serviceId, detector->pAppidActiveConfig), 1); lua_pushnumber(L, 0); return 1; } static int client_addPayload( lua_State *L ) { unsigned int payloadId; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); payloadId = lua_tonumber(L, 2); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; if (!detector->client.appModule.api) { lua_pushnumber(L, -1); return 1; } detector->client.appModule.api->add_payload(detector->validateParams.flowp, appGetAppFromPayloadId(payloadId, detector->pAppidActiveConfig)); lua_pushnumber(L, 0); return 1; } /**Get flow object from a detector object. The flow object is then used with flowApi. * A new copy of flow object is provided with every call. This can be optimized by maintaining * a single copy. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return packetCount/stack - Total packet processed by RNA. * @todo maintain a single copy and return the same copy with every call to Detector_getFlow(). */ static int Detector_getFlow( lua_State *L ) { DetectorFlowUserData *detectorFlowUserData; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); /*check inputs and whether this function is called in context of a packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { return 0; } detector = detectorUserData->pDetector; detectorFlowUserData = pushDetectorFlowUserData(L); if (!detectorFlowUserData || !detectorFlowUserData->pDetectorFlow) { _dpd.errMsg( "Failed to allocate memory."); return 0; } detectorFlowUserData->pDetectorFlow->pFlow = detector->validateParams.flowp; #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG, "service %s, Detector_getFlow(): allocated DetectorFlow = %p", detector->server.serviceModule.name, detectorFlowUserData); #endif return 1; } int Detector_addHttpPattern(lua_State *L) { int index = 1; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg( "Invalid HTTP detector user data addHttpPattern."); return 0; } /* Verify valid pattern type */ enum httpPatternType pType = (enum httpPatternType) lua_tointeger(L, index++); if(pType < HTTP_PAYLOAD || pType > HTTP_URL) { _dpd.errMsg( "Invalid HTTP pattern type."); return 0; } /* Verify valid DHSequence */ DHPSequence seq = (DHPSequence) lua_tointeger(L, index++); if(seq < SINGLE || seq > USER_AGENT_HEADER) { _dpd.errMsg( "Invalid HTTP DHP Sequence."); return 0; } uint32_t service_id = lua_tointeger(L, index++); uint32_t client_app = lua_tointeger(L, index++); /*uint32_t client_app_type =*/ lua_tointeger(L, index++); uint32_t payload = lua_tointeger(L, index++); /*uint32_t payload_type =*/ lua_tointeger(L, index++); if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid detector context addHttpPattern: service_id %u; client_app %u; payload %u\n",service_id, client_app, payload); return 0; } /* Verify that pattern is a valid string */ size_t pattern_size = 0; uint8_t* pattern_str = (uint8_t*) strdup(lua_tolstring(L, index++, &pattern_size)); if(pattern_str == NULL || pattern_size == 0) { _dpd.errMsg( "Invalid HTTP pattern string."); free(pattern_str); return 0; } uint32_t appId = lua_tointeger(L, index++); HTTPListElement *element = calloc(1, sizeof(*element)); if (element == NULL) { _dpd.errMsg( "Failed to allocate HTTP list element memory."); free(pattern_str); return 0; } DetectorHTTPPattern *pattern = &element->detectorHTTPPattern; tAppIdConfig *pConfig = detectorUserData->pDetector->pAppidNewConfig; pattern->seq = seq; pattern->service_id = appGetAppFromServiceId(service_id, pConfig); pattern->client_app = appGetAppFromClientId(client_app, pConfig); pattern->payload = appGetAppFromPayloadId(payload, pConfig); pattern->pattern = pattern_str; pattern->pattern_size = (int) pattern_size; pattern->appId = appId; /* for apps that should not show up in 4.10 and ealier, we cannot include an entry in the legacy client app or payload tables. We will use the appId instead. This is only for user-agents that ID clients. if you want a user-agent to ID a payload, include it in the payload database. If you want a host pattern ID, use the other API. */ if (!service_id && !client_app && !payload && pType == 2) { pattern->client_app = appId; } switch(pType) { case HTTP_PAYLOAD: element->next = pConfig->httpPatternLists.hostPayloadPatternList; pConfig->httpPatternLists.hostPayloadPatternList = element; break; case HTTP_URL: element->next = pConfig->httpPatternLists.urlPatternList; pConfig->httpPatternLists.urlPatternList = element; break; case HTTP_USER_AGENT: element->next = pConfig->httpPatternLists.clientAgentPatternList; pConfig->httpPatternLists.clientAgentPatternList = element; break; } appInfoSetActive(pattern->service_id, true); appInfoSetActive(pattern->client_app, true); appInfoSetActive(pattern->payload, true); appInfoSetActive(appId, true); return 0; } /* On the lua side, this should look something like: addSSLCertPattern(, '' ) */ int Detector_addSSLCertPattern (lua_State *L) { uint8_t *pattern_str; size_t pattern_size; int index = 1; uint8_t type; tAppId app_id; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "Invalid SSL detector user data or context."); return 0; } type = lua_tointeger(L, index++); app_id = (tAppId) lua_tointeger(L, index++); pattern_size = 0; const char *tmpString = lua_tolstring(L, index++, &pattern_size); if (!tmpString || !pattern_size) { _dpd.errMsg( "Invalid SSL Host pattern string"); return 0; } pattern_str = (uint8_t *)strdup(tmpString); if (!pattern_str) { _dpd.errMsg( "Invalid SSL Host pattern string."); return 0; } if (!ssl_add_cert_pattern(pattern_str, pattern_size, type, app_id, &detectorUserData->pDetector->pAppidNewConfig->serviceSslConfig)) { free(pattern_str); _dpd.errMsg( "Failed to add an SSL pattern list member"); return 0; } appInfoSetActive(app_id, true); return 0; } /* On the lua side, this should look something like: addDNSHostPattern(, '' ) */ int Detector_addDNSHostPattern (lua_State *L) { uint8_t *pattern_str; size_t pattern_size; int index = 1; uint8_t type; tAppId app_id; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "LuaDetectorApi:Invalid DNS detector user data or context."); return 0; } type = lua_tointeger(L, index++); app_id = (tAppId) lua_tointeger(L, index++); pattern_size = 0; const char *tmpString = lua_tolstring(L, index++, &pattern_size); if (!tmpString || !pattern_size) { _dpd.errMsg( "LuaDetectorApi:Invalid DNS Host pattern string"); return 0; } pattern_str = (uint8_t *)strdup(tmpString); if (!pattern_str) { _dpd.errMsg( "LuaDetectorApi:Invalid DNS Host pattern string."); return 0; } if (!dns_add_host_pattern(pattern_str, pattern_size, type, app_id, &detectorUserData->pDetector->pAppidNewConfig->serviceDnsConfig)) { free(pattern_str); _dpd.errMsg( "LuaDetectorApi:Failed to add an SSL pattern list member"); } return 0; } static int Detector_addSSLCnamePattern (lua_State *L) { uint8_t *pattern_str; size_t pattern_size; int index = 1; uint8_t type; tAppId app_id; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "Invalid SSL detector user data or context."); return 0; } type = lua_tointeger(L, index++); app_id = (tAppId) lua_tointeger(L, index++); pattern_size = 0; const char *tmpString = lua_tolstring(L, index++, &pattern_size); if (!tmpString || !pattern_size) { _dpd.errMsg( "Invalid SSL Host pattern string"); return 0; } pattern_str = (uint8_t *)strdup(tmpString); if (!pattern_str) { _dpd.errMsg( "Invalid SSL Host pattern string."); return 0; } if (!ssl_add_cname_pattern(pattern_str, pattern_size, type, app_id, &detectorUserData->pDetector->pAppidNewConfig->serviceSslConfig)) { free(pattern_str); _dpd.errMsg( "Failed to add an SSL pattern list member"); return 0; } appInfoSetActive(app_id, true); return 0; } static int Detector_addHostPortApp (lua_State *L) { /*uint8_t *ipaddr_str; */ size_t ipaddr_size; int index = 1; uint8_t type; tAppId app_id; struct in6_addr ip6Addr; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("%s: Invalid detector user data or context.\n",__func__); return 0; } type = lua_tointeger(L, index++); app_id = (tAppId) lua_tointeger(L, index++); ipaddr_size = 0; const char *tmpString = lua_tolstring(L, index++, &ipaddr_size); if (!tmpString || !ipaddr_size) { _dpd.errMsg("%s:Invalid ipaddr string\n",__func__); return 0; } if (!strchr(tmpString, ':')) { if (inet_pton(AF_INET, tmpString, &ip6Addr.s6_addr32[3]) <= 0) { _dpd.errMsg("%s: Invalid IP address: %s\n",__func__, tmpString); return 0; } ip6Addr.s6_addr32[0] = ip6Addr.s6_addr32[1] = 0; ip6Addr.s6_addr32[2] = ntohl(0x0000ffff); } else { if (inet_pton(AF_INET6, tmpString, &ip6Addr) <= 0) { _dpd.errMsg("%s: Invalid IP address: %s\n",__func__, tmpString); return 0; } } unsigned port = lua_tointeger(L, index++); unsigned proto = lua_tointeger(L, index++); if (!hostPortAppCacheAdd(&ip6Addr, (uint16_t)port, (uint16_t)proto, type, app_id, detectorUserData->pDetector->pAppidNewConfig)) { _dpd.errMsg("%s:Failed to backend call\n",__func__); } return 0; } static int Detector_addHostPortAppDynamic (lua_State *L) { /*uint8_t *ipaddr_str; */ size_t ipaddr_size; int index = 1; uint8_t type; tAppId app_id; struct in6_addr ip6Addr; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg("%s: Invalid detector user data.\n",__func__); return 0; } type = lua_tointeger(L, index++); app_id = (tAppId) lua_tointeger(L, index++); ipaddr_size = 0; const char *tmpString = lua_tolstring(L, index++, &ipaddr_size); if (!tmpString || !ipaddr_size) { _dpd.errMsg("%s:Invalid ipaddr string\n",__func__); return 0; } if (!strchr(tmpString, ':')) { if (inet_pton(AF_INET, tmpString, &ip6Addr.s6_addr32[3]) <= 0) { _dpd.errMsg("%s: Invalid IP address: %s\n",__func__, tmpString); return 0; } ip6Addr.s6_addr32[0] = ip6Addr.s6_addr32[1] = 0; ip6Addr.s6_addr32[2] = ntohl(0x0000ffff); } else { if (inet_pton(AF_INET6, tmpString, &ip6Addr) <= 0) { _dpd.errMsg("%s: Invalid IP address: %s\n",__func__, tmpString); return 0; } } unsigned port = lua_tointeger(L, index++); unsigned proto = lua_tointeger(L, index++); if (!hostPortAppCacheDynamicAdd(&ip6Addr, (uint16_t)port, (uint16_t)proto, type, app_id, true)) { _dpd.errMsg("%s:Failed to backend call\n",__func__); } return 0; } static int Detector_addContentTypePattern(lua_State *L) { uint8_t *pattern; tAppId appId; int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg( "Invalid HTTP detector user data addContentTypePattern."); return 0; } size_t stringSize = 0; const char *tmpString = lua_tolstring(L, index++, &stringSize); if (!tmpString || !stringSize) { _dpd.errMsg( "Invalid HTTP Header string"); return 0; } pattern = (uint8_t *)strdup(tmpString); if (!pattern) { _dpd.errMsg( "Failed to allocate Content Type pattern string."); return 0; } appId = lua_tointeger(L, index++); if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid detector context addSipUserAgent: appId %d\n",appId); free(pattern); return 0; } HTTPListElement *element = calloc(1, sizeof(*element)); if (!element) { _dpd.errMsg( "Failed to allocate HTTP list element memory."); free(pattern); return 0; } DetectorHTTPPattern *detector = &element->detectorHTTPPattern; tAppIdConfig *pConfig = detectorUserData->pDetector->pAppidNewConfig; detector->pattern = pattern; detector->pattern_size = strlen((char *)pattern); detector->appId = appId; element->next = pConfig->httpPatternLists.contentTypePatternList; pConfig->httpPatternLists.contentTypePatternList = element; appInfoSetActive(appId, true); return 0; } static inline int GetDetectorUserData(lua_State *L, int index, DetectorUserData **detector_user_data, const char *errorString) { // Verify detector user data and that we are not in packet context *detector_user_data = checkDetectorUserData(L, index); if (!*detector_user_data || (*detector_user_data)->pDetector->validateParams.pkt) { _dpd.errMsg(errorString); return -1; } return 0; } static int detector_create_chp_app(DetectorUserData *detectorUserData, tAppId appIdInstance, unsigned app_type_flags, int num_matches) { CHPApp *new_app = (CHPApp *)calloc(1,sizeof(CHPApp)); if (!new_app) { _dpd.errMsg( "LuaDetectorApi:Failed to allocate CHP app memory."); return -1; } new_app->appIdInstance = appIdInstance; new_app->app_type_flags = app_type_flags; new_app->num_matches = num_matches; if (sfxhash_add(detectorUserData->pDetector->pAppidNewConfig->CHP_glossary, &(new_app->appIdInstance), new_app)) { _dpd.errMsg( "LuaDetectorApi:Failed to add CHP for appId %d, instance %d", CHP_APPIDINSTANCE_TO_ID(appIdInstance), CHP_APPIDINSTANCE_TO_INSTANCE(appIdInstance)); free(new_app); return -1; } return 0; } static int Detector_CHPCreateApp (lua_State *L) { DetectorUserData *detectorUserData; tAppId appId; unsigned app_type_flags; int num_matches; tAppId appIdInstance; int index = 1; if (GetDetectorUserData(L, index++, &detectorUserData, "LuaDetectorApi:Invalid HTTP detector user data in CHPCreateApp.")) return 0; appId = lua_tointeger(L, index++); appIdInstance = CHP_APPID_SINGLE_INSTANCE(appId); // Last instance for the old API app_type_flags = lua_tointeger(L, index++); num_matches = lua_tointeger(L, index++); // We only want one of these for each appId. if (sfxhash_find(detectorUserData->pDetector->pAppidNewConfig->CHP_glossary, &appIdInstance)) { _dpd.errMsg( "LuaDetectorApi:Attempt to add more than one CHP for appId %d - use CHPMultiCreateApp", appId); return 0; } detector_create_chp_app(detectorUserData, appIdInstance, app_type_flags, num_matches); return 0; } static inline int CHPGetKeyPatternBoolean(lua_State *L, int index) { return (0 != lua_tointeger(L, index)); } static inline int CHPGetPatternType(lua_State *L, int index, PatternType *pattern_type) { *pattern_type = (PatternType) lua_tointeger(L, index); if(*pattern_type < AGENT_PT || *pattern_type > MAX_PATTERN_TYPE) { _dpd.errMsg( "LuaDetectorApi:Invalid CHP Action pattern type."); return -1; } return 0; } static inline int CHPGetPatternDataAndSize(lua_State *L, int index, char **pattern_data, size_t *pattern_size) { const char *tmpString = NULL; // Lua owns this pointer *pattern_size = 0; *pattern_data = NULL; tmpString = lua_tolstring(L, index, &*pattern_size); if(!tmpString || !*pattern_size || !(*pattern_data = strdup(tmpString))) // non-empty pattern required { if (*pattern_size) // implies strdup() failed _dpd.errMsg( "LuaDetectorApi:CHP Action PATTERN string mem alloc failed."); else _dpd.errMsg( "LuaDetectorApi:Invalid CHP Action PATTERN string."); // empty string in Lua code - bad return -1; } return 0; } static inline int CHPGetActionType(lua_State *L, int index, ActionType *action_type) { *action_type = (ActionType) lua_tointeger(L, index); if (*action_type < NO_ACTION || *action_type > MAX_ACTION_TYPE) { _dpd.errMsg( "LuaDetectorApi:Incompatible CHP Action type, might be for a later version."); return -1; } return 0; } static inline int CHPGetActionData(lua_State *L, int index, char **action_data) { // An empty string is translated into a NULL pointer because the action data is optional const char *tmpString = NULL; // Lua owns this pointer size_t action_data_size = 0; *action_data = NULL; tmpString = lua_tolstring(L, index, &action_data_size); if (action_data_size) { if(!(*action_data = strdup(tmpString))) { _dpd.errMsg( "LuaDetectorApi:Action DATA string mem alloc failed."); return -1; } } return 0; } static int detector_add_chp_action(DetectorUserData *detectorUserData, tAppId appIdInstance, int isKeyPattern, PatternType patternType, size_t patternSize, char *patternData, ActionType actionType, char *optionalActionData) { uint precedence; CHPListElement *tmp_chpa, *prev_chpa, *chpa; CHPApp *chpapp; tAppIdConfig *pConfig = detectorUserData->pDetector->pAppidNewConfig; //find the CHP App for this if (!(chpapp = sfxhash_find(detectorUserData->pDetector->pAppidNewConfig->CHP_glossary, &appIdInstance))) { _dpd.errMsg( "LuaDetectorApi:Invalid attempt to add a CHP action for unknown appId %d, instance %d. - pattern:\"%s\" - action \"%s\"\n", CHP_APPIDINSTANCE_TO_ID(appIdInstance), CHP_APPIDINSTANCE_TO_INSTANCE(appIdInstance), patternData, optionalActionData ? optionalActionData : ""); free(patternData); if (optionalActionData) free(optionalActionData); return 0; } if (isKeyPattern) { chpapp->key_pattern_count++; chpapp->key_pattern_length_sum += patternSize; } if (chpapp->ptype_scan_counts[patternType] == 0) chpapp->num_scans++; precedence = chpapp->ptype_scan_counts[patternType]++; // The increment is for the sake of the precedence. ptype_scan_counts means "The scan DOES count toward the total required." // at runtime we'll want to know how many of each type of pattern we are looking for. if (actionType == REWRITE_FIELD || actionType == INSERT_FIELD) { if (!appInfoEntryFlagGet(CHP_APPIDINSTANCE_TO_ID(appIdInstance), APPINFO_FLAG_SUPPORTED_SEARCH, pConfig)) { _dpd.errMsg( "LuaDetectorApi: CHP action type, %d, requires previous use of action type, %d, (see appId %d, pattern=\"%s\").\n", actionType, GET_OFFSETS_FROM_REBUILT, CHP_APPIDINSTANCE_TO_ID(appIdInstance), patternData); free(patternData); if (optionalActionData) free(optionalActionData); return 0; } switch (patternType) { // permitted pattern type (modifiable HTTP/SPDY request field) case AGENT_PT: case HOST_PT: case REFERER_PT: case URI_PT: case COOKIE_PT: break; default: _dpd.errMsg( "LuaDetectorApi: CHP action type, %d, on unsupported pattern type, %d, (see appId %d, pattern=\"%s\").\n", actionType, patternType, CHP_APPIDINSTANCE_TO_ID(appIdInstance), patternData); free(patternData); if (optionalActionData) free(optionalActionData); return 0; } } else if (actionType != ALTERNATE_APPID && actionType != DEFER_TO_SIMPLE_DETECT) chpapp->ptype_req_counts[patternType]++; chpa = (CHPListElement*)calloc(1,sizeof(CHPListElement)); if (!chpa) { _dpd.errMsg( "LuaDetectorApi: Failed to allocate CHP action memory.\n"); free(patternData); if (optionalActionData) free(optionalActionData); return 0; } chpa->chp_action.appIdInstance = appIdInstance; chpa->chp_action.precedence = precedence; chpa->chp_action.key_pattern = isKeyPattern; chpa->chp_action.ptype = patternType; chpa->chp_action.psize = patternSize; chpa->chp_action.pattern = patternData; chpa->chp_action.action = actionType; chpa->chp_action.action_data = optionalActionData; chpa->chp_action.chpapp = chpapp; // link this struct to the Glossary entry tmp_chpa = pConfig->httpPatternLists.chpList; if (!tmp_chpa) pConfig->httpPatternLists.chpList = chpa; else { while (tmp_chpa->next) tmp_chpa = tmp_chpa->next; tmp_chpa->next = chpa; } /* Set the safe-search bits in the appId entry */ if (actionType == GET_OFFSETS_FROM_REBUILT) { /* This is a search engine and it is SUPPORTED for safe-search packet rewrite */ appInfoEntryFlagSet(CHP_APPIDINSTANCE_TO_ID(appIdInstance), APPINFO_FLAG_SEARCH_ENGINE | APPINFO_FLAG_SUPPORTED_SEARCH, pConfig); } else if (actionType == SEARCH_UNSUPPORTED) { /* This is a search engine and it is UNSUPPORTED for safe-search packet rewrite */ appInfoEntryFlagSet(CHP_APPIDINSTANCE_TO_ID(appIdInstance), APPINFO_FLAG_SEARCH_ENGINE, pConfig); } else if (actionType == DEFER_TO_SIMPLE_DETECT && strcmp(patternData,"") == 0) { // Walk the list of all the patterns we have inserted, searching for this appIdInstance and free them. // The purpose is for the 14 and 15 to be used together to only set the APPINFO_FLAG_SEARCH_ENGINE flag // If the reserved pattern is not used, it is a mixed use case and should just behave normally. prev_chpa = NULL; tmp_chpa = pConfig->httpPatternLists.chpList; while (tmp_chpa) { if (tmp_chpa->chp_action.appIdInstance == appIdInstance) { // advance the tmp_chpa pointer by removing the item pointed to. Keep prev_chpa unchanged. // 1) unlink the struct, 2) free strings and then 3) free the struct. chpa = tmp_chpa; // preserve this pointer to be freed at the end. if (prev_chpa == NULL) { // Remove from head pConfig->httpPatternLists.chpList = tmp_chpa->next; tmp_chpa = pConfig->httpPatternLists.chpList; } else { // Remove from middle of list. prev_chpa->next = tmp_chpa->next; tmp_chpa = prev_chpa->next; } free(chpa->chp_action.pattern); if (chpa->chp_action.action_data) free(chpa->chp_action.action_data); free(chpa); } else { // advance both pointers prev_chpa = tmp_chpa; tmp_chpa = tmp_chpa->next; } } } return 0; } static int Detector_CHPAddAction (lua_State *L) { DetectorUserData *detectorUserData; int key_pattern; PatternType ptype; size_t psize; char *pattern; ActionType action; char *action_data; tAppId appIdInstance; tAppId appId; int index = 1; if (GetDetectorUserData(L, index++, &detectorUserData, "LuaDetectorApi:Invalid HTTP detector user data in CHPAddAction.")) return 0; // Parameter 1 appId = lua_tointeger(L, index++); appIdInstance = CHP_APPID_SINGLE_INSTANCE(appId); // Last instance for the old API // Parameter 2 key_pattern = CHPGetKeyPatternBoolean(L, index++); // Parameter 3 if (CHPGetPatternType(L, index++, &ptype)) return 0; // Parameter 4 if (CHPGetPatternDataAndSize(L, index++, &pattern, &psize)) return 0; // Parameter 5 if (CHPGetActionType(L, index++, &action)) { free(pattern); return 0; } // Parameter 6 if (CHPGetActionData(L, index++, &action_data)) { free(pattern); return 0; } return detector_add_chp_action(detectorUserData, appIdInstance, key_pattern, ptype, psize, pattern, action, action_data); } static int Detector_CHPMultiCreateApp (lua_State *L) { DetectorUserData *detectorUserData; tAppId appId; unsigned app_type_flags; int num_matches; tAppId appIdInstance; int instance; int index = 1; if (GetDetectorUserData(L, index++, &detectorUserData, "LuaDetectorApi:Invalid HTTP detector user data in CHPMultiCreateApp.")) return 0; appId = lua_tointeger(L, index++); app_type_flags = lua_tointeger(L, index++); num_matches = lua_tointeger(L, index++); for (instance=0; instance < CHP_APPID_INSTANCE_MAX; instance++ ) { appIdInstance = (appId << CHP_APPID_BITS_FOR_INSTANCE) + instance; if (sfxhash_find(detectorUserData->pDetector->pAppidNewConfig->CHP_glossary, &appIdInstance)) continue; break; } // We only want a maximum of these for each appId. if (instance == CHP_APPID_INSTANCE_MAX) { _dpd.errMsg( "LuaDetectorApi:Attempt to create more than %d CHP for appId %d", CHP_APPID_INSTANCE_MAX, appId); return 0; } if (detector_create_chp_app(detectorUserData, appIdInstance, app_type_flags, num_matches)) return 0; lua_pushnumber(L, appIdInstance); return 1; } static int Detector_CHPMultiAddAction (lua_State *L) { DetectorUserData *detectorUserData; int key_pattern; PatternType ptype; size_t psize; char *pattern; ActionType action; char *action_data; tAppId appIdInstance; int index = 1; if (GetDetectorUserData(L, index++, &detectorUserData, "LuaDetectorApi:Invalid HTTP detector user data in CHPMultiAddAction.")) return 0; // Parameter 1 appIdInstance = lua_tointeger(L, index++); // Parameter 2 key_pattern = CHPGetKeyPatternBoolean(L, index++); // Parameter 3 if (CHPGetPatternType(L, index++, &ptype)) return 0; // Parameter 4 if (CHPGetPatternDataAndSize(L, index++, &pattern, &psize)) return 0; // Parameter 5 if (CHPGetActionType(L, index++, &action)) { free(pattern); return 0; } // Parameter 6 if (CHPGetActionData(L, index++, &action_data)) { free(pattern); return 0; } return detector_add_chp_action(detectorUserData, appIdInstance, key_pattern, ptype, psize, pattern, action, action_data); } static int Detector_portOnlyService (lua_State *L) { int index = 1; // Verify detector user data and that we are not in packet context DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "LuaDetectorApi:Invalid HTTP detector user data in addPortOnlyService."); return 0; } tAppId appId = lua_tointeger(L, index++); u_int16_t port = lua_tointeger(L, index++); u_int8_t protocol = lua_tointeger(L, index++); if (port == 0) detectorUserData->pDetector->pAppidNewConfig->ip_protocol[protocol] = appId; else if (protocol == 6) detectorUserData->pDetector->pAppidNewConfig->tcp_port_only[port] = appId; else if (protocol == 17) detectorUserData->pDetector->pAppidNewConfig->udp_port_only[port] = appId; return 0; } /* Add a length-based detector. This is done by adding a new length sequence * to the cache. Note that this does not require a validate and is only used * as a fallback identification. * * @param lua_State* - Lua state variable. * @param appId/stack - App ID to use for this detector. * @param proto/stack - Protocol (IPPROTO_TCP/DC.ipproto.tcp (6) or * IPPROTO_UDP/DC.ipproto.udp (17)). * @param sequence_cnt/stack - Number of elements in sequence below (max of * LENGTH_SEQUENCE_CNT_MAX). * @param sequence_str/stack - String that defines direction/length sequence. * - Example: "I/8,R/512,I/512,R/1024,I/1024" * - Direction: I(nitiator) or R(esponder). * - Length : Payload size (bytes) (> 0). * @return int - Number of elements on stack, which is always 1. * @return status/stack - 0 if successful, -1 otherwise. */ static int Detector_lengthAppCacheAdd(lua_State *L) { int i; const char *str_ptr; uint16_t length; tLengthKey length_sequence; int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (detectorUserData == NULL) { _dpd.errMsg( "LuaDetectorApi:Invalid detector user data!"); lua_pushnumber(L, -1); return 1; } tAppId appId = lua_tonumber(L, index++); uint8_t proto = lua_tonumber(L, index++); uint8_t sequence_cnt = lua_tonumber(L, index++); const char *sequence_str = lua_tostring(L, index++); if (((proto != IPPROTO_TCP) && (proto != IPPROTO_UDP)) || ((sequence_cnt == 0) || (sequence_cnt > LENGTH_SEQUENCE_CNT_MAX)) || ((sequence_str == NULL) || (strlen(sequence_str) == 0))) { _dpd.errMsg( "LuaDetectorApi:Invalid input (%d,%u,%u,\"%s\")!", appId, (unsigned)proto, (unsigned)sequence_cnt, sequence_str ?: ""); lua_pushnumber(L, -1); return 1; } memset(&length_sequence, 0, sizeof(length_sequence)); length_sequence.proto = proto; length_sequence.sequence_cnt = sequence_cnt; str_ptr = sequence_str; for (i = 0; i < sequence_cnt; i++) { int last_one; switch (*str_ptr) { case 'I': length_sequence.sequence[i].direction = APP_ID_FROM_INITIATOR; break; case 'R': length_sequence.sequence[i].direction = APP_ID_FROM_RESPONDER; break; default: _dpd.errMsg( "LuaDetectorApi:Invalid sequence string (\"%s\")!", sequence_str); lua_pushnumber(L, -1); return 1; } str_ptr++; if (*str_ptr != '/') { _dpd.errMsg( "LuaDetectorApi:Invalid sequence string (\"%s\")!", sequence_str); lua_pushnumber(L, -1); return 1; } str_ptr++; length = (uint16_t)atoi(str_ptr); if (length == 0) { _dpd.errMsg( "LuaDetectorApi:Invalid sequence string (\"%s\")!", sequence_str); lua_pushnumber(L, -1); return 1; } length_sequence.sequence[i].length = length; while ((*str_ptr != ',') && (*str_ptr != 0)) { str_ptr++; } last_one = (i == (sequence_cnt - 1)); if ( (!last_one && (*str_ptr != ',')) || (last_one && (*str_ptr != 0))) { _dpd.errMsg( "LuaDetectorApi:Invalid sequence string (\"%s\")!", sequence_str); lua_pushnumber(L, -1); return 1; } str_ptr++; } if (!lengthAppCacheAdd(&length_sequence, appId, detectorUserData->pDetector->pAppidNewConfig)) { _dpd.errMsg( "LuaDetectorApi:Could not add entry to cache!"); lua_pushnumber(L, -1); return 1; } lua_pushnumber(L, 0); return 1; } static int Detector_AFAddApp (lua_State *L) { int index = 1; AFElement val; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "LuaDetectorApi:Invalid HTTP detector user data in AFAddApp."); return 0; } tAppId indicator = lua_tointeger(L, index++); tAppId forecast = lua_tointeger(L, index++); tAppId target = lua_tointeger(L, index++); if (sfxhash_find(detectorUserData->pDetector->pAppidNewConfig->AF_indicators, &indicator)) { _dpd.errMsg( "LuaDetectorApi:Attempt to add more than one AFElement per appId %d", indicator); return 0; } val.indicator = indicator; val.forecast = forecast; val.target = target; if (sfxhash_add(detectorUserData->pDetector->pAppidNewConfig->AF_indicators, &indicator, &val)) { _dpd.errMsg( "LuaDetectorApi:Failed to add AFElement for appId %d", indicator); return 0; } return 0; } static int Detector_addAppUrl(lua_State *L) { int index = 1; DetectorAppUrlPattern **tmp; const char *tmpString; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "Invalid HTTP detector user data in addAppUrl."); return 0; } u_int32_t service_id = lua_tointeger(L, index++); u_int32_t client_id = lua_tointeger(L, index++); lua_tointeger(L, index++); // client_app_type u_int32_t payload_id = lua_tointeger(L, index++); lua_tointeger(L, index++); // payload_type if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid HTTP detector context addAppUrl: service_id %u; client_id %u; payload_id %u\n",service_id, client_id, payload_id); return 0; } /* Verify that host pattern is a valid string */ size_t hostPatternSize = 0; u_int8_t* hostPattern = NULL; tmpString = lua_tolstring(L, index++, &hostPatternSize); if(!tmpString || !hostPatternSize) { _dpd.errMsg( "Invalid host pattern string:service_id %u; client_id %u; payload_id %u\n.",service_id, client_id, payload_id); return 0; } else if (!(hostPattern = (u_int8_t *)strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate host pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); return 0; } /* Verify that path pattern is a valid string */ size_t pathPatternSize = 0; u_int8_t* pathPattern = NULL; tmpString = lua_tolstring(L, index++, &pathPatternSize); if(!tmpString || !pathPatternSize) { _dpd.errMsg( "Invalid path pattern string: service_id %u; client_id %u; payload_id %u\n.",service_id, client_id, payload_id); free(hostPattern); return 0; } else if (!(pathPattern = (u_int8_t *)strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate path pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); free(hostPattern); return 0; } /* Verify that scheme pattern is a valid string */ size_t schemePatternSize; u_int8_t* schemePattern = NULL; tmpString = lua_tolstring(L, index++, &schemePatternSize); if(!tmpString || !schemePatternSize) { _dpd.errMsg( "Invalid scheme pattern string: service_id %u; client_id %u; payload_id %u\n.",service_id, client_id, payload_id); free(pathPattern); free(hostPattern); return 0; } else if (!(schemePattern = (u_int8_t*) strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate scheme pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); free(pathPattern); free(hostPattern); return 0; } /* Verify that query pattern is a valid string */ size_t queryPatternSize; u_int8_t* queryPattern = NULL; tmpString = lua_tolstring(L, index++, &queryPatternSize); if(tmpString && queryPatternSize) { if (!(queryPattern = (u_int8_t*) strdup(tmpString))) { _dpd.errMsg( "Invalid query pattern string."); free(hostPattern); free(pathPattern); free(schemePattern); return 0; } } u_int32_t appId = lua_tointeger(L, index++); /* Allocate memory for data structures */ DetectorAppUrlPattern *pattern = malloc(sizeof(DetectorAppUrlPattern)); if (!pattern) { _dpd.errMsg( "Failed to allocate HTTP pattern memory."); free(hostPattern); free(pathPattern); free(schemePattern); if (queryPattern) free(queryPattern); return 0; } tAppIdConfig *pConfig = detectorUserData->pDetector->pAppidNewConfig; pattern->userData.service_id = appGetAppFromServiceId(service_id, pConfig); pattern->userData.client_app = appGetAppFromClientId(client_id, pConfig); pattern->userData.payload = appGetAppFromPayloadId(payload_id, pConfig); pattern->userData.appId = appId; pattern->userData.query.pattern = queryPattern; pattern->userData.query.patternSize = queryPatternSize; pattern->patterns.host.pattern = hostPattern; pattern->patterns.host.patternSize = (int) hostPatternSize; pattern->patterns.path.pattern = pathPattern; pattern->patterns.path.patternSize = (int) pathPatternSize; pattern->patterns.scheme.pattern = schemePattern; pattern->patterns.scheme.patternSize = (int) schemePatternSize; DetectorAppUrlList *urlList = &pConfig->httpPatternLists.appUrlList; /**first time usedCount and allocatedCount are both 0, urlPattern will be NULL. * This case is same as malloc. In case of error, realloc will return NULL, and * original urlPattern buffer is left untouched. */ if (urlList->usedCount == urlList->allocatedCount) { tmp = realloc(urlList->urlPattern, (urlList->allocatedCount+URL_LIST_STEP_SIZE)*sizeof(*tmp)); if (!tmp) { FreeDetectorAppUrlPattern(pattern); return 0; } urlList->urlPattern = tmp; urlList->allocatedCount += URL_LIST_STEP_SIZE; } urlList->urlPattern[urlList->usedCount++] = pattern; appInfoSetActive(pattern->userData.service_id, true); appInfoSetActive(pattern->userData.client_app, true); appInfoSetActive(pattern->userData.payload, true); appInfoSetActive(appId, true); return 0; } static int Detector_addRTMPUrl(lua_State *L) { int index = 1; DetectorAppUrlPattern **tmp; const char *tmpString; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "Invalid HTTP detector user data in addRTMPUrl."); return 0; } u_int32_t service_id = lua_tointeger(L, index++); u_int32_t client_id = lua_tointeger(L, index++); lua_tointeger(L, index++); // client_app_type u_int32_t payload_id = lua_tointeger(L, index++); lua_tointeger(L, index++); // payload_type if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid HTTP detector context addRTMPUrl: service_id %u; client_id %u; payload_id %u\n",service_id, client_id, payload_id); return 0; } /* Verify that host pattern is a valid string */ size_t hostPatternSize = 0; u_int8_t* hostPattern = NULL; tmpString = lua_tolstring(L, index++, &hostPatternSize); if(!tmpString || !hostPatternSize) { _dpd.errMsg( "Invalid host pattern string:service_id %u; client_id %u; payload_id %u\n",service_id, client_id, payload_id); return 0; } else if (!(hostPattern = (u_int8_t *)strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate host pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); return 0; } /* Verify that path pattern is a valid string */ size_t pathPatternSize = 0; u_int8_t* pathPattern = NULL; tmpString = lua_tolstring(L, index++, &pathPatternSize); if(!tmpString || !pathPatternSize) { _dpd.errMsg( "Invalid path pattern string: service_id %u; client_id %u; payload_id %u\n.",service_id, client_id, payload_id); free(hostPattern); return 0; } else if (!(pathPattern = (u_int8_t *)strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate path pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); free(hostPattern); return 0; } /* Verify that scheme pattern is a valid string */ size_t schemePatternSize; u_int8_t* schemePattern = NULL; tmpString = lua_tolstring(L, index++, &schemePatternSize); if(!tmpString || !schemePatternSize) { _dpd.errMsg( "Invalid scheme pattern string: service_id %u; client_id %u; payload_id %u\n",service_id, client_id, payload_id); free(pathPattern); free(hostPattern); return 0; } else if (!(schemePattern = (u_int8_t*) strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate scheme pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); free(pathPattern); free(hostPattern); return 0; } /* Verify that query pattern is a valid string */ size_t queryPatternSize; u_int8_t* queryPattern = NULL; tmpString = lua_tolstring(L, index++, &queryPatternSize); if(tmpString && queryPatternSize) { if (!(queryPattern = (u_int8_t*) strdup(tmpString))) { _dpd.errMsg( "Invalid query pattern string."); free(hostPattern); free(pathPattern); free(schemePattern); return 0; } } u_int32_t appId = lua_tointeger(L, index++); /* Allocate memory for data structures */ DetectorAppUrlPattern *pattern = malloc(sizeof(DetectorAppUrlPattern)); if (!pattern) { _dpd.errMsg( "Failed to allocate HTTP pattern memory."); free(hostPattern); free(pathPattern); free(schemePattern); if (queryPattern) free(queryPattern); return 0; } /* we want to put these patterns in just like for regular Urls, but we do NOT need legacy IDs for them. * so just use the appID for service, client, or payload ID */ pattern->userData.service_id = service_id; pattern->userData.client_app = client_id; pattern->userData.payload = payload_id; pattern->userData.appId = appId; pattern->userData.query.pattern = queryPattern; pattern->userData.query.patternSize = queryPatternSize; pattern->patterns.host.pattern = hostPattern; pattern->patterns.host.patternSize = (int) hostPatternSize; pattern->patterns.path.pattern = pathPattern; pattern->patterns.path.patternSize = (int) pathPatternSize; pattern->patterns.scheme.pattern = schemePattern; pattern->patterns.scheme.patternSize = (int) schemePatternSize; tAppIdConfig *pConfig = detectorUserData->pDetector->pAppidNewConfig; DetectorAppUrlList *urlList = &pConfig->httpPatternLists.RTMPUrlList; /**first time usedCount and allocatedCount are both 0, urlPattern will be NULL. * This case is same as malloc. In case of error, realloc will return NULL, and * original urlPattern buffer is left untouched. */ if (urlList->usedCount == urlList->allocatedCount) { tmp = realloc(urlList->urlPattern, (urlList->allocatedCount+URL_LIST_STEP_SIZE)*sizeof(*tmp)); if (!tmp) { FreeDetectorAppUrlPattern(pattern); return 0; } urlList->urlPattern = tmp; urlList->allocatedCount += URL_LIST_STEP_SIZE; } urlList->urlPattern[urlList->usedCount++] = pattern; appInfoSetActive(pattern->userData.service_id, true); appInfoSetActive(pattern->userData.client_app, true); appInfoSetActive(pattern->userData.payload, true); appInfoSetActive(appId, true); return 0; } /*Lua should inject patterns in format. */ static int Detector_addSipUserAgent(lua_State *L) { int index = 1; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg( "Invalid HTTP detector user data addSipUserAgent."); return 0; } u_int32_t client_app = lua_tointeger(L, index++); const char *clientVersion = lua_tostring(L, index++); if(!clientVersion ) { _dpd.errMsg( "Invalid sip client version string."); return 0; } if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid detector context addSipUserAgent: client_app %u\n",client_app); return 0; } /* Verify that ua pattern is a valid string */ const char* uaPattern = lua_tostring(L, index++); if(!uaPattern) { _dpd.errMsg( "Invalid sip ua pattern string."); return 0; } sipUaPatternAdd(client_app, clientVersion, uaPattern, &detectorUserData->pDetector->pAppidNewConfig->detectorSipConfig); appInfoSetActive(client_app, true); return 0; } static int openCreateApp(lua_State *L) { int index = 1; const char *tmpString; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "Invalid HTTP detector user data in addAppUrl."); return 0; } /* Verify that host pattern is a valid string */ size_t appNameLen = 0; tmpString = lua_tolstring(L, index++, &appNameLen); if(!tmpString || !appNameLen) { _dpd.errMsg( "Invalid appName string."); lua_pushnumber(L, APP_ID_NONE); return 1; /*number of results */ } AppInfoTableEntry *entry = appInfoEntryCreate(tmpString, detectorUserData->pDetector->pAppidNewConfig); if (entry) { lua_pushnumber(L, entry->appId); return 1; /*number of results */ } lua_pushnumber(L, APP_ID_NONE); return 1; /*number of results */ } static int openAddClientApp( lua_State *L ) { unsigned int serviceAppId, clientAppId; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); serviceAppId = lua_tonumber(L, 2); clientAppId = lua_tonumber(L, 3); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; if (!detector->client.appModule.api) { lua_pushnumber(L, -1); return 1; } detector->client.appModule.api->add_app(detector->validateParams.pkt, (APPID_SESSION_DIRECTION) detector->validateParams.dir, detector->pAppidActiveConfig, detector->validateParams.flowp, serviceAppId, clientAppId, ""); lua_pushnumber(L, 0); return 1; } /** Add service id to a flow. Positive identification by a detector. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param serviceId/stack - id of service postively identified on this flow. * @param vendorName/stack - name of vendor of service. This is optional. * @param version/stack - version of service. This is optional. * @return int - Number of elements on stack, which is always 1. * @return int/stack - values from enum SERVICE_RETCODE */ static int openAddServiceApp( lua_State *L ) { unsigned int serviceId, retValue = SERVICE_ENULL; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); serviceId = lua_tonumber(L, 2); if (!detectorUserData || !checkServiceElement(detectorUserData->pDetector) || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, SERVICE_ENULL); return 1; } detector = detectorUserData->pDetector; /*Phase2 - discuss RNAServiceSubtype will be maintained on lua side therefore the last parameter on the following call is NULL. */ /*Subtype is not displayed on DC at present. */ retValue = AppIdServiceAddService(detector->validateParams.flowp, detector->validateParams.pkt, detector->validateParams.dir, detector->server.pServiceElement, serviceId, NULL, NULL, NULL, NULL); lua_pushnumber(L, retValue); return 1; } static int openAddPayloadApp( lua_State *L ) { unsigned int payloadAppId; Detector *detector; DetectorUserData *detectorUserData = checkDetectorUserData(L, 1); payloadAppId = lua_tonumber(L, 2); /*check inputs and whether this function is called in context of a */ /*packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, -1); return 1; } detector = detectorUserData->pDetector; if (!detector->client.appModule.api) { lua_pushnumber(L, -1); return 1; } detector->client.appModule.api->add_payload(detector->validateParams.flowp, payloadAppId); lua_pushnumber(L, 0); return 1; } int openAddHttpPattern(lua_State *L) { int index = 1; tAppIdConfig *pConfig; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg( "Invalid HTTP detector user data addHttpPattern."); return 0; } pConfig = detectorUserData->pDetector->pAppidNewConfig; /* Verify valid pattern type */ enum httpPatternType pType = (enum httpPatternType) lua_tointeger(L, index++); if(pType < HTTP_PAYLOAD || pType > HTTP_URL) { _dpd.errMsg( "Invalid HTTP pattern type."); return 0; } /* Verify valid DHSequence */ DHPSequence seq = (DHPSequence) lua_tointeger(L, index++); if(seq < SINGLE || seq > USER_AGENT_HEADER) { _dpd.errMsg( "Invalid HTTP DHP Sequence."); return 0; } uint32_t serviceAppId = lua_tointeger(L, index++); uint32_t clientAppId = lua_tointeger(L, index++); uint32_t payloadAppId = lua_tointeger(L, index++); if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid detector context addHttpPattern: serviceAppId %u; clientAppId %u; payloadAppId %u\n",serviceAppId, clientAppId, payloadAppId); return 0; } /* Verify that pattern is a valid string */ size_t pattern_size = 0; uint8_t* pattern_str = (uint8_t*) strdup(lua_tolstring(L, index++, &pattern_size)); if(pattern_str == NULL || pattern_size == 0) { _dpd.errMsg( "Invalid HTTP pattern string."); free(pattern_str); return 0; } HTTPListElement *element = calloc(1, sizeof(*element)); if (element == NULL) { _dpd.errMsg( "Failed to allocate HTTP list element memory."); free(pattern_str); return 0; } DetectorHTTPPattern *pattern = &element->detectorHTTPPattern; pattern->seq = seq; pattern->service_id = serviceAppId; pattern->client_app = clientAppId; pattern->payload = payloadAppId; pattern->pattern = pattern_str; pattern->pattern_size = (int) pattern_size; pattern->appId = APP_ID_NONE; switch(pType) { case HTTP_PAYLOAD: element->next = pConfig->httpPatternLists.hostPayloadPatternList; pConfig->httpPatternLists.hostPayloadPatternList = element; break; case HTTP_URL: element->next = pConfig->httpPatternLists.urlPatternList; pConfig->httpPatternLists.urlPatternList = element; break; case HTTP_USER_AGENT: element->next = pConfig->httpPatternLists.clientAgentPatternList; pConfig->httpPatternLists.clientAgentPatternList = element; break; } appInfoSetActive(serviceAppId, true); appInfoSetActive(clientAppId, true); appInfoSetActive(payloadAppId, true); return 0; } static int openAddUrlPattern(lua_State *L) { int index = 1; DetectorAppUrlPattern **tmp; const char *tmpString; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg( "Invalid HTTP detector user data in addAppUrl."); return 0; } tAppIdConfig *pConfig = detectorUserData->pDetector->pAppidNewConfig; u_int32_t service_id = lua_tointeger(L, index++); u_int32_t client_id = lua_tointeger(L, index++); u_int32_t payload_id = lua_tointeger(L, index++); if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid HTTP detector context addAppUrl: service_id %u; client_id %u; payload_id %u\n",service_id, client_id, payload_id); return 0; } /* Verify that host pattern is a valid string */ size_t hostPatternSize = 0; u_int8_t* hostPattern = NULL; tmpString = lua_tolstring(L, index++, &hostPatternSize); if(!tmpString || !hostPatternSize) { _dpd.errMsg( "Invalid host pattern string: service_id %u; client_id %u; payload_id %u\n",service_id, client_id, payload_id); return 0; } else if (!(hostPattern = (u_int8_t *)strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate host pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); return 0; } /* Verify that path pattern is a valid string */ size_t pathPatternSize = 0; u_int8_t* pathPattern = NULL; tmpString = lua_tolstring(L, index++, &pathPatternSize); if(!tmpString || !pathPatternSize) { _dpd.errMsg( "Invalid path pattern string: service_id %u; client_id %u; payload %u\n.",service_id, client_id, payload_id); free(hostPattern); return 0; } else if (!(pathPattern = (u_int8_t *)strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate path pattern: %s, service_id %u; client_id %u; payload %u\n.",tmpString, service_id, client_id, payload_id); free(hostPattern); return 0; } /* Verify that scheme pattern is a valid string */ size_t schemePatternSize; u_int8_t* schemePattern = NULL; tmpString = lua_tolstring(L, index++, &schemePatternSize); if(!tmpString || !schemePatternSize) { _dpd.errMsg( "Invalid scheme pattern string: service_id %u; client_id %u; payload_id %u\n",service_id, client_id, payload_id); free(pathPattern); free(hostPattern); return 0; } else if (!(schemePattern = (u_int8_t*) strdup(tmpString))) { _dpd.errMsg( "Failed to duplicate scheme pattern: %s, service_id %u; client_id %u; payload_id %u\n.",tmpString, service_id, client_id, payload_id); free(pathPattern); free(hostPattern); return 0; } /* Allocate memory for data structures */ DetectorAppUrlPattern *pattern = malloc(sizeof(DetectorAppUrlPattern)); if (!pattern) { _dpd.errMsg( "Failed to allocate HTTP pattern memory."); free(hostPattern); free(pathPattern); free(schemePattern); return 0; } pattern->userData.service_id = service_id; pattern->userData.client_app = client_id; pattern->userData.payload = payload_id; pattern->userData.appId = APP_ID_NONE; pattern->userData.query.pattern = NULL; pattern->userData.query.patternSize = 0; pattern->patterns.host.pattern = hostPattern; pattern->patterns.host.patternSize = (int) hostPatternSize; pattern->patterns.path.pattern = pathPattern; pattern->patterns.path.patternSize = (int) pathPatternSize; pattern->patterns.scheme.pattern = schemePattern; pattern->patterns.scheme.patternSize = (int) schemePatternSize; DetectorAppUrlList *urlList = &pConfig->httpPatternLists.appUrlList; /**first time usedCount and allocatedCount are both 0, urlPattern will be NULL. * This case is same as malloc. In case of error, realloc will return NULL, and * original urlPattern buffer is left untouched. */ if (urlList->usedCount == urlList->allocatedCount) { tmp = realloc(urlList->urlPattern, (urlList->allocatedCount+URL_LIST_STEP_SIZE)*sizeof(*tmp)); if (!tmp) { FreeDetectorAppUrlPattern(pattern); return 0; } urlList->urlPattern = tmp; urlList->allocatedCount += URL_LIST_STEP_SIZE; } urlList->urlPattern[urlList->usedCount++] = pattern; appInfoSetActive(service_id, true); appInfoSetActive(client_id, true); appInfoSetActive(payload_id, true); return 0; } void CleanClientPortPatternList(tAppIdConfig *pConfig) { tPortPatternNode *tmp; if ( pConfig->clientPortPattern) { while ((tmp = pConfig->clientPortPattern->luaInjectedPatterns)) { pConfig->clientPortPattern->luaInjectedPatterns = tmp->next; free(tmp->pattern); free(tmp->detectorName); free(tmp); } free(pConfig->clientPortPattern); } } void CleanServicePortPatternList(tAppIdConfig *pConfig) { tPortPatternNode *tmp; if ( pConfig->servicePortPattern) { while ((tmp = pConfig->servicePortPattern->luaInjectedPatterns)) { pConfig->servicePortPattern->luaInjectedPatterns = tmp->next; free(tmp->pattern); free(tmp->detectorName); free(tmp); } free(pConfig->servicePortPattern); } } /* Add a port and pattern based detection for client application. Both port and pattern criteria * must be met before client application is deemed detected. * * @param lua_State* - Lua state variable. * @param proto/stack - Protocol (IPPROTO_TCP/DC.ipproto.tcp (6) or * IPPROTO_UDP/DC.ipproto.udp (17)). * @param port/stack - port number to register. * @param pattern/stack - pattern to be matched. * @param patternLenght/stack - length of pattern * @param offset/stack - offset into packet payload where matching should start. * @param appId/stack - App ID to use for this detector. * @return int - Number of elements on stack, which is always 0. */ static int addPortPatternClient(lua_State *L) { int index = 1; tAppIdConfig *pConfig; tPortPatternNode *pPattern; uint8_t protocol; uint16_t port; const char*pattern; size_t patternSize = 0; unsigned position; tAppId appId; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg( "addPortPatternClient(): Invalid detector user data"); return 0; } pConfig = detectorUserData->pDetector->pAppidNewConfig; protocol = lua_tonumber(L, index++); //port = lua_tonumber(L, index++); port = 0; pattern = lua_tolstring(L, index++, &patternSize); position = lua_tonumber(L, index++); appId = lua_tointeger(L, index++); if (!pConfig->clientPortPattern) { if (!(pConfig->clientPortPattern = calloc(1, sizeof(*pConfig->clientPortPattern)))) { _dpd.errMsg( "addPortPatternClient(): memory allocation failure"); return 0; } } if (appId <= APP_ID_NONE || !pattern || !patternSize || (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP)) { _dpd.errMsg("addPortPatternClient(): Invalid input in %s\n", detectorUserData->pDetector->name); return 0; } if (!(pPattern = calloc(1, sizeof(*pPattern)))) { _dpd.errMsg( "addPortPatternClient(): memory allocation failure"); return 0; } if (!(pPattern->pattern = malloc(patternSize))) { _dpd.errMsg( "addPortPatternClient(): memory allocation failure"); free(pPattern); return 0; } pPattern->appId = appId; pPattern->protocol = protocol; pPattern->port = port; memcpy(pPattern->pattern, pattern, patternSize); pPattern->length = patternSize; pPattern->offset = position; if (!(pPattern->detectorName = strdup(detectorUserData->pDetector->name))) { _dpd.errMsg( "addPortPatternClient(): memory allocation failure"); free(pPattern->pattern); free(pPattern); return 0; } //insert ports in order. { tPortPatternNode **prev; tPortPatternNode **curr; prev = NULL; for (curr = &pConfig->clientPortPattern->luaInjectedPatterns; *curr; prev = curr, curr = &((*curr)->next)) { if (strcmp(pPattern->detectorName, (*curr)->detectorName) || pPattern->protocol < (*curr)->protocol || pPattern->port < (*curr)->port) break; } if (prev) { pPattern->next = (*prev)->next; (*prev)->next = pPattern; } else { pPattern->next = *curr; *curr = pPattern; } } appInfoSetActive(appId, true); return 0; } /* Add a port and pattern based detection for service application. Both port and pattern criteria * must be met before service application is deemed detected. * * @param lua_State* - Lua state variable. * @param proto/stack - Protocol (IPPROTO_TCP/DC.ipproto.tcp (6) or * IPPROTO_UDP/DC.ipproto.udp (17)). * @param port/stack - port number to register. * @param pattern/stack - pattern to be matched. * @param patternLenght/stack - length of pattern * @param offset/stack - offset into packet payload where matching should start. * @param appId/stack - App ID to use for this detector. * @return int - Number of elements on stack, which is always 0. */ static int addPortPatternService(lua_State *L) { int index = 1; size_t patternSize = 0; tAppIdConfig *pConfig; tPortPatternNode *pPattern; uint8_t protocol; uint16_t port; const char *pattern; unsigned position; tAppId appId; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg( "addPortPatternService(): Invalid detector user data"); return 0; } pConfig = detectorUserData->pDetector->pAppidNewConfig; protocol = lua_tonumber(L, index++); port = lua_tonumber(L, index++); pattern = lua_tolstring(L, index++, &patternSize); position = lua_tonumber(L, index++); appId = lua_tointeger(L, index++); if (!pConfig->servicePortPattern) { if (!(pConfig->servicePortPattern = calloc(1, sizeof(*pConfig->servicePortPattern)))) { _dpd.errMsg( "addPortPatternService(): memory allocation failure"); return 0; } } if (!(pPattern = calloc(1, sizeof(*pPattern)))) { _dpd.errMsg( "addPortPatternService(): memory allocation failure"); return 0; } if (!(pPattern->pattern = malloc(patternSize))) { _dpd.errMsg( "addPortPatternService(): memory allocation failure"); free(pPattern); return 0; } pPattern->appId = appId; pPattern->protocol = protocol; pPattern->port = port; memcpy(pPattern->pattern, pattern, patternSize); pPattern->length = patternSize; pPattern->offset = position; if (!(pPattern->detectorName = strdup(detectorUserData->pDetector->name))) { _dpd.errMsg( "addPortPatternService(): memory allocation failure"); free(pPattern->pattern); free(pPattern); return 0; } //insert ports in order. { tPortPatternNode **prev; tPortPatternNode **curr; prev = NULL; for (curr = &pConfig->servicePortPattern->luaInjectedPatterns; *curr; prev = curr, curr = &((*curr)->next)) { if (strcmp(pPattern->detectorName, (*curr)->detectorName) || pPattern->protocol < (*curr)->protocol || pPattern->port < (*curr)->port) break; } if (prev) { pPattern->next = (*prev)->next; (*prev)->next = pPattern; } else { pPattern->next = *curr; *curr = pPattern; } } appInfoSetActive(appId, true); return 0; } /*Lua should inject patterns in format. */ static int Detector_addSipServer(lua_State *L) { int index = 1; /* Verify detector user data and that we are not in packet context */ DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData) { _dpd.errMsg( "Invalid HTTP detector user data addSipServer."); return 0; } u_int32_t client_app = lua_tointeger(L, index++); const char *clientVersion = lua_tostring(L, index++); if(!clientVersion ) { _dpd.errMsg( "Invalid sip client version string."); return 0; } if (detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("Invalid detector context addSipServer: client_app %u\n",client_app); return 0; } /* Verify that ua pattern is a valid string */ const char* uaPattern = lua_tostring(L, index++); if(!uaPattern) { _dpd.errMsg( "Invalid sip ua pattern string."); return 0; } sipServerPatternAdd(client_app, clientVersion, uaPattern, &detectorUserData->pDetector->pAppidNewConfig->detectorSipConfig); appInfoSetActive(client_app, true); return 0; } static inline int ConvertStringToAddress(const char * string, sfaddr_t * address) { int af; struct in6_addr buf; if (strchr(string, ':')) af = AF_INET6; else if (strchr(string, '.')) af = AF_INET; else return 0; if (inet_pton(af, string, &buf)) { if (sfip_set_raw(address, &buf, af) != SFIP_SUCCESS) return 0; } else return 0; return 1; // success } /**Creates a future flow based on the current flow. When the future flow is * seen, the app ID will simply be declared with the info given here. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object. * @param client_addr/stack - client address of the future flow * @param client_port/stack - client port of the the future flow (can use 0 for wildcard here) * @param server_addr/stack - server address of the future flow * @param server_port/stack - server port of the future flow * @param proto/stack - protocol type (see define IPPROTO_xxxx in /usr/include/netinet/in.h) * @param service_app_id/stack - service app ID to declare for future flow (can be 0 for none) * @param client_app_id/stack - client app ID to declare for future flow (can be 0 for none) * @param payload_app_id/stack - payload app ID to declare for future flow (can be 0 for none) * @param app_id_to_snort/stack - AppID's app ID entry to convert to Snort app ID (see note below) * @return int - number of elements on stack, which is 1 if successful, 0 otherwise. * * Notes: For app_id_to_snort, use the app ID that AppID knows about (it'll * probably be a repeat of one of the other 3 app IDs given here). For * example, for "FTP Data", use 166. Internally, this'll be converted to the * app ID that Snort recognizes ("ftp-data"). For this to really mean * anything, the app IDs entry in appMapping.data should have a Snort app ID * defined. * * Example: createFutureFlow("192.168.0.200", 0, "192.168.0.100", 20, 6, 166, 0, 0, 166) */ static int createFutureFlow (lua_State *L) { sfaddr_t client_addr; sfaddr_t server_addr; uint8_t proto; uint16_t client_port, server_port; DetectorUserData *detectorUserData = NULL; char *pattern; tAppId service_app_id, client_app_id, payload_app_id, app_id_to_snort; int16_t snort_app_id; tAppIdData *fp; detectorUserData = checkDetectorUserData(L, 1); /*check inputs and whether this function is called in context of a packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { return 0; } pattern = (char *)lua_tostring(L, 2); if (!ConvertStringToAddress(pattern, &client_addr)) return 0; client_port = lua_tonumber(L, 3); pattern = (char *)lua_tostring(L, 4); if (!ConvertStringToAddress(pattern, &server_addr)) return 0; server_port = lua_tonumber(L, 5); proto = lua_tonumber(L, 6); service_app_id = lua_tointeger(L, 7); client_app_id = lua_tointeger(L, 8); payload_app_id = lua_tointeger(L, 9); app_id_to_snort = lua_tointeger(L, 10); if (app_id_to_snort > APP_ID_NONE) { AppInfoTableEntry* entry = appInfoEntryGet(app_id_to_snort, appIdActiveConfigGet()); if (NULL == entry) return 0; snort_app_id = entry->snortId; } else { snort_app_id = 0; } fp = AppIdEarlySessionCreate(detectorUserData->pDetector->validateParams.flowp, detectorUserData->pDetector->validateParams.pkt, &client_addr, client_port, &server_addr, server_port, proto, snort_app_id, APPID_EARLY_SESSION_FLAG_FW_RULE); if (fp) { fp->serviceAppId = service_app_id; fp->clientAppId = client_app_id; fp->payloadAppId = payload_app_id; setAppIdFlag(fp, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_NOT_A_SERVICE | APPID_SESSION_PORT_SERVICE_DONE); fp->rnaServiceState = RNA_STATE_FINISHED; fp->rnaClientState = RNA_STATE_FINISHED; return 1; } else return 0; } static int isMidStreamSession(lua_State *L) { DetectorUserData *detectorUserData = NULL; detectorUserData = checkDetectorUserData(L, 1); /*check inputs and whether this function is called in context of a packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { lua_pushnumber(L, -1); return -1; } if (_dpd.sessionAPI->get_session_flags(detectorUserData->pDetector->validateParams.pkt->stream_session) & SSNFLAG_MIDSTREAM) { lua_pushnumber(L, 1); return 1; } lua_pushnumber(L, 0); return 0; } /* Check if traffic is going through an HTTP proxy */ static int isHttpTunnel(lua_State *L) { DetectorUserData *detectorUserData = NULL; detectorUserData = checkDetectorUserData(L, 1); /*check inputs and whether this function is called in context of a packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) return -1; httpSession *hsession = detectorUserData->pDetector->validateParams.flowp->hsession; if (hsession) { tunnelDest *tunDest = hsession->tunDest; if (tunDest) { lua_pushboolean(L, 1); return 1; } } lua_pushboolean(L, 0); return 0; } /* Get destination IP tunneled through a proxy */ static int getHttpTunneledIp(lua_State* L) { DetectorUserData *detectorUserData = NULL; detectorUserData = checkDetectorUserData(L, 1); /*check inputs and whether this function is called in context of a packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) return -1; httpSession *hsession = detectorUserData->pDetector->validateParams.flowp->hsession; if (hsession) { tunnelDest *tunDest = hsession->tunDest; if (!tunDest) lua_pushnumber(L, 0); else lua_pushnumber(L, sfaddr_get_ip4_value(&(tunDest->ip))); } return 1; } /* Get port tunneled through a proxy */ static int getHttpTunneledPort(lua_State* L) { DetectorUserData *detectorUserData = NULL; detectorUserData = checkDetectorUserData(L, 1); /*check inputs and whether this function is called in context of a packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) return -1; httpSession *hsession = detectorUserData->pDetector->validateParams.flowp->hsession; if (hsession) { tunnelDest *tunDest = hsession->tunDest; if (!tunDest) lua_pushnumber(L, 0); else lua_pushnumber(L, tunDest->port); } return 1; } /*Lua should inject patterns in format. */ static int Detector_addCipConnectionClass(lua_State *L) { int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("%s: Invalid detector user data or context.\n", __func__); return -1; } uint32_t appId = lua_tointeger(L, index++); uint32_t classId = lua_tointeger(L, index++); if (CipAddConnectionClass(appId, classId) == -1) { return -1; } appInfoSetActive(appId, true); return 0; } /*Lua should inject patterns in format. */ static int Detector_addCipPath(lua_State *L) { int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("%s: Invalid detector user data or context.\n", __func__); return -1; } uint32_t appId = lua_tointeger(L, index++); uint32_t classId = lua_tointeger(L, index++); uint8_t serviceId = lua_tointeger(L, index++); if (CipAddPath(appId, classId, serviceId) == -1) { return -1; } appInfoSetActive(appId, true); return 0; } /*Lua should inject patterns in format. */ static int Detector_addCipSetAttribute(lua_State *L) { int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("%s: Invalid detector user data or context.\n", __func__); return -1; } uint32_t appId = lua_tointeger(L, index++); uint32_t classId = lua_tointeger(L, index++); bool isClassInstance = lua_toboolean(L, index++); uint32_t attributeId = lua_tointeger(L, index++); if (CipAddSetAttribute(appId, classId, isClassInstance, attributeId) == -1) { return -1; } appInfoSetActive(appId, true); return 0; } /*Lua should inject patterns in format. */ static int Detector_addCipExtendedSymbolService(lua_State *L) { int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("%s: Invalid detector user data or context.\n", __func__); return -1; } uint32_t appId = lua_tointeger(L, index++); uint8_t serviceId = lua_tointeger(L, index++); if (CipAddExtendedSymbolService(appId, serviceId) == -1) { return -1; } appInfoSetActive(appId, true); return 0; } /*Lua should inject patterns in format. */ static int Detector_addCipService(lua_State *L) { int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("%s: Invalid detector user data or context.\n", __func__); return -1; } uint32_t appId = lua_tointeger(L, index++); uint16_t serviceId = lua_tointeger(L, index++); if (CipAddService(appId, serviceId) == -1) { return -1; } appInfoSetActive(appId, true); return 0; } /*Lua should inject patterns in format. */ static int Detector_addEnipCommand(lua_State *L) { int index = 1; DetectorUserData *detectorUserData = checkDetectorUserData(L, index++); if (!detectorUserData || detectorUserData->pDetector->validateParams.pkt) { _dpd.errMsg("%s: Invalid detector user data or context.\n", __func__); return -1; } uint32_t appId = lua_tointeger(L, index++); uint16_t commandId = lua_tointeger(L, index++); if (CipAddEnipCommand(appId, commandId) == -1) { return -1; } appInfoSetActive(appId, true); return 0; } static const luaL_Reg Detector_methods[] = { /* Obsolete API names. No longer use these! They are here for backward * compatibility and will eventually be removed. */ /* - "memcmp" is now "matchSimplePattern" (below) */ {"memcmp", Detector_memcmp}, /* - "getProtocolType" is now "getL4Protocol" (below) */ {"getProtocolType", Detector_getProtocolType}, /* - "inCompatibleData" is now "markIncompleteData" (below) */ {"inCompatibleData", service_inCompatibleData}, /* - "addDataId" is now "addAppIdDataToFlow" (below) */ {"addDataId", service_addDataId}, /* - "service_inCompatibleData" is now "service_markIncompleteData" (below) */ {"service_inCompatibleData", service_inCompatibleData}, /* - "service_addDataId" is now "service_addAppIdDataToFlow" (below) */ {"service_addDataId", service_addDataId}, {"getPacketSize", Detector_getPacketSize}, {"getPacketDir", Detector_getPacketDir}, {"matchSimplePattern", Detector_memcmp}, {"getPcreGroups", Detector_getPcreGroups}, {"getL4Protocol", Detector_getProtocolType}, {"getPktSrcAddr", Detector_getPktSrcIPAddr}, {"getPktDstAddr", Detector_getPktDstIPAddr}, {"getPktSrcPort", Detector_getPktSrcPort}, {"getPktDstPort", Detector_getPktDstPort}, {"getPktCount", Detector_getPktCount}, {"getFlow", Detector_getFlow}, {"htons", Detector_htons}, {"htonl", Detector_htonl}, {"log", Detector_logMessage}, {"addHttpPattern", Detector_addHttpPattern}, {"addAppUrl", Detector_addAppUrl}, {"addRTMPUrl", Detector_addRTMPUrl}, {"addContentTypePattern", Detector_addContentTypePattern}, {"addSSLCertPattern", Detector_addSSLCertPattern}, {"addSipUserAgent", Detector_addSipUserAgent}, {"addSipServer", Detector_addSipServer}, {"addSSLCnamePattern", Detector_addSSLCnamePattern}, {"addHostPortApp", Detector_addHostPortApp}, {"addHostPortAppDynamic", Detector_addHostPortAppDynamic}, {"addDNSHostPattern", Detector_addDNSHostPattern}, {"registerClientDetectorCallback", Detector_registerClientCallback}, {"registerServiceDetectorCallback", Detector_registerServiceCallback}, /* CIP registration */ {"addCipConnectionClass", Detector_addCipConnectionClass}, {"addCipPath", Detector_addCipPath}, {"addCipSetAttribute", Detector_addCipSetAttribute}, {"addCipExtendedSymbolService", Detector_addCipExtendedSymbolService}, {"addCipService", Detector_addCipService}, {"addEnipCommand", Detector_addEnipCommand}, /*Obsolete - new detectors should not use this API */ {"init", service_init}, {"registerPattern", service_registerPattern}, {"getServiceID", service_getServiceId}, {"addPort", service_addPorts}, {"removePort", service_removePorts}, {"setServiceName", service_setServiceName}, {"getServiceName", service_getServiceName}, {"isCustomDetector", service_isCustomDetector}, {"setValidator", service_setValidator}, {"addService", service_addService}, {"failService", service_failService}, {"inProcessService", service_inProcessService}, {"markIncompleteData", service_inCompatibleData}, {"analyzePayload", service_analyzePayload}, {"addAppIdDataToFlow", service_addDataId}, /*service API */ {"service_init", service_init}, {"service_registerPattern", service_registerPattern}, {"service_getServiceId", service_getServiceId}, {"service_addPort", service_addPorts}, {"service_removePort", service_removePorts}, {"service_setServiceName", service_setServiceName}, {"service_getServiceName", service_getServiceName}, {"service_isCustomDetector", service_isCustomDetector}, {"service_setValidator", service_setValidator}, {"service_addService", service_addService}, {"service_failService", service_failService}, {"service_inProcessService", service_inProcessService}, {"service_markIncompleteData", service_inCompatibleData}, {"service_analyzePayload", service_analyzePayload}, {"service_addAppIdDataToFlow", service_addDataId}, {"service_addClient", service_addClient}, /*client init API */ {"client_init", client_init}, {"client_registerPattern", client_registerPattern}, {"client_getServiceId", service_getServiceId}, /*client service API */ {"client_addApp", client_addApp}, {"client_addInfo", client_addInfo}, {"client_addUser", client_addUser}, {"client_addPayload", client_addPayload}, //HTTP Multi Pattern engine {"CHPCreateApp", Detector_CHPCreateApp}, {"CHPAddAction", Detector_CHPAddAction}, {"CHPMultiCreateApp", Detector_CHPMultiCreateApp}, // allows multiple detectors, same appId {"CHPMultiAddAction", Detector_CHPMultiAddAction}, //App Forecasting engine {"AFAddApp", Detector_AFAddApp}, {"portOnlyService", Detector_portOnlyService}, /* Length-based detectors. */ {"AddLengthBasedDetector", Detector_lengthAppCacheAdd}, {"registerAppId" , common_registerAppId}, {"open_createApp", openCreateApp}, {"open_addClientApp", openAddClientApp}, {"open_addServiceApp", openAddServiceApp}, {"open_addPayloadApp", openAddPayloadApp}, {"open_addHttpPattern", openAddHttpPattern}, {"open_addUrlPattern", openAddUrlPattern}, {"addPortPatternClient", addPortPatternClient}, {"addPortPatternService", addPortPatternService}, {"createFutureFlow", createFutureFlow}, {"isMidStreamSession", isMidStreamSession}, { "isHttpTunnel", isHttpTunnel }, { "getHttpTunneledIp", getHttpTunneledIp }, { "getHttpTunneledPort", getHttpTunneledPort }, {0, 0} }; /**This function performs a clean exit on an api instance. It is called when RNA is performing * a clean exit. */ void Detector_fini(void *data) { lua_State *myLuaState; Detector *detector = (Detector*) data; #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"Finishing detector %s\n",detector->server.serviceModule.name); #endif myLuaState = detector->myLuaState; if (detector->packageInfo.server.cleanFunctionName && lua_checkstack(myLuaState, 1)) { lua_getglobal(myLuaState, detector->packageInfo.server.cleanFunctionName); if (lua_pcall(myLuaState, 0, 0, 0 )) { /*See comment at first lua_pcall() */ #ifdef LUA_DETECTOR_DEBUG _dpd.errMsg( "%s: error running %s in lua: %s", detector->server.serviceModule.name, detector->packageInfo.server.cleanFunctionName, lua_tostring(myLuaState, -1)); #endif } } else if (detector->packageInfo.client.cleanFunctionName && lua_checkstack(myLuaState, 1)) { lua_getglobal(myLuaState, detector->packageInfo.client.cleanFunctionName); if (lua_pcall(myLuaState, 0, 0, 0 )) { /*See comment at first lua_pcall() */ #ifdef LUA_DETECTOR_DEBUG _dpd.errMsg( "%s: error running %s in lua: %s", detector->server.serviceModule.name, detector->packageInfo.client.cleanFunctionName, lua_tostring(myLuaState, -1)); #endif } } freeDetector(detector); /*lua_close will perform garbage collection after killing lua script. */ /**Design: Lua_state does not allow me to store user variables so detectors store lua_state. * There is one lua_state for each lua file, which can have only one * detectors. So if lua detector creates a detector, registers a pattern * and then loses reference then lua will garbage collect but we should not free the buffer. * */ lua_close(myLuaState); } /**Garbage collector hook function. Called when Lua side garbage collects detector api instance. Current design is to allocate * one of each luaState, detector and detectorUserData buffers, and hold these buffers till RNA exits. SigHups processing * reuses the buffers and calls DetectorInit to reinitialize. RNA ensures that DetectorUserData is not garbage collected, by * creating a reference in LUA_REGISTRY table. The reference is released only on RNA exit. * * If in future, one needs to free any of these buffers then one should consider references to detector buffer in RNAServiceElement * stored in flows and hostServices data structures. Other detectors at this time create one static instance for the lifetime of RNA, * and therefore we have adopted the same principle for Lua Detecotors. */ static int Detector_gc ( lua_State *L ) { DetectorUserData *detectorUserData = toDetectorUserData(L, -1); if (detectorUserData) { #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"DetectorUserData %p: freeing\n\n",detectorUserData); #endif } return 0; } /*convert detector to string for printing */ static int Detector_tostring ( lua_State *L ) { char buff[32]; snprintf(buff, sizeof(buff), "%p", toDetectorUserData(L, 1)); lua_pushfstring(L, "Detector (%s)", buff); return 1; } static const luaL_Reg Detector_meta[] = { {"__gc", Detector_gc}, {"__tostring", Detector_tostring}, {0, 0} }; /**Registers C functions as an API, enabling Lua detector to call these functions. This function * should be called once before loading any lua detectors. This function itself is not part of API * and therefore can not be called by a Lua detection. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return methodArray/stack - array of newly created methods */ int Detector_register ( lua_State *L ) { /* populates a new table with Detector_methods (method_table), add the table to the globals and stack*/ luaL_openlib(L, DETECTOR, Detector_methods, 0); /* create metatable for Foo, add it to the Lua registry, metatable on stack */ luaL_newmetatable(L, DETECTOR); /* populates table on stack with Detector_meta methods, puts the metatable on stack*/ luaL_openlib(L, NULL, Detector_meta, 0); lua_pushliteral(L, "__index"); lua_pushvalue(L, -3); /* dup methods table*/ lua_settable(L, -3); /* metatable.__index = methods */ lua_pushliteral(L, "__metatable"); lua_pushvalue(L, -3); /* dup methods table*/ lua_settable(L, -3); /* hide metatable: metatable.__metatable = methods */ lua_pop(L, 1); /* drop metatable */ return 1; /* return methods on the stack */ } /** @} */ /* end of LuaDetectorBaseApi */ static void FreeHTTPListElement(HTTPListElement *element) { if (element) { if (element->detectorHTTPPattern.pattern) free(element->detectorHTTPPattern.pattern); free(element); } } static void FreeCHPAppListElement (CHPListElement *element) { if (element) { if (element->chp_action.pattern) free(element->chp_action.pattern); if (element->chp_action.action_data) free(element->chp_action.action_data); free (element); } } static void FreeDetectorAppUrlPattern(DetectorAppUrlPattern *pattern) { if (pattern) { if (pattern->userData.query.pattern) free(*(void **)&pattern->userData.query.pattern); if (pattern->patterns.host.pattern) free(*(void **)&pattern->patterns.host.pattern); if (pattern->patterns.path.pattern) free(*(void **)&pattern->patterns.path.pattern); if (pattern->patterns.scheme.pattern) free(*(void **)&pattern->patterns.scheme.pattern); free(pattern); } } void CleanHttpPatternLists(tAppIdConfig *pConfig) { HTTPListElement *element; CHPListElement *chpe; size_t i; for (i = 0; i < pConfig->httpPatternLists.appUrlList.usedCount; i++) { FreeDetectorAppUrlPattern(pConfig->httpPatternLists.appUrlList.urlPattern[i]); pConfig->httpPatternLists.appUrlList.urlPattern[i] = NULL; } for (i = 0; i < pConfig->httpPatternLists.RTMPUrlList.usedCount; i++) { FreeDetectorAppUrlPattern(pConfig->httpPatternLists.RTMPUrlList.urlPattern[i]); pConfig->httpPatternLists.RTMPUrlList.urlPattern[i] = NULL; } if (pConfig->httpPatternLists.appUrlList.urlPattern) { free(pConfig->httpPatternLists.appUrlList.urlPattern); pConfig->httpPatternLists.appUrlList.urlPattern = NULL; } pConfig->httpPatternLists.appUrlList.allocatedCount = 0; if (pConfig->httpPatternLists.RTMPUrlList.urlPattern) { free(pConfig->httpPatternLists.RTMPUrlList.urlPattern); pConfig->httpPatternLists.RTMPUrlList.urlPattern = NULL; } pConfig->httpPatternLists.RTMPUrlList.allocatedCount = 0; pConfig->httpPatternLists.appUrlList.usedCount = 0; pConfig->httpPatternLists.RTMPUrlList.usedCount = 0; while ((element = pConfig->httpPatternLists.clientAgentPatternList)) { pConfig->httpPatternLists.clientAgentPatternList = element->next; FreeHTTPListElement(element); } while ((element = pConfig->httpPatternLists.hostPayloadPatternList)) { pConfig->httpPatternLists.hostPayloadPatternList = element->next; FreeHTTPListElement(element); } while ((element = pConfig->httpPatternLists.urlPatternList)) { pConfig->httpPatternLists.urlPatternList = element->next; FreeHTTPListElement(element); } while ((element = pConfig->httpPatternLists.contentTypePatternList)) { pConfig->httpPatternLists.contentTypePatternList = element->next; FreeHTTPListElement(element); } while ((chpe = pConfig->httpPatternLists.chpList)) { pConfig->httpPatternLists.chpList = chpe->next; FreeCHPAppListElement(chpe); } } snort-2.9.20/src/dynamic-preprocessors/appid/thirdparty_appid_api.h0000644000175000017500000001041014241075575023667 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _THIRDPARTY_APPID_API_H_ #define _THIRDPARTY_APPID_API_H_ #include #include "appId.h" #include "appIdApi.h" #include "sf_snort_packet.h" #include "thirdparty_appid_types.h" #define THIRD_PARTY_APP_ID_API_VERSION 1 #define TP_PATH_MAX 4096 struct ThirdPartyConfig { unsigned chp_body_collection_max; unsigned ftp_userid_disabled:1; unsigned chp_body_collection_disabled:1; unsigned tp_allow_probes:1; unsigned http_upgrade_reporting_enabled:1; char tp_config_path[TP_PATH_MAX]; int numXffFields; char** xffFields; int oldNumXffFields; char** oldXffFields; }; struct ThirdPartyUtils { void (*logMsg)(const char *, ...); uint32_t (*getSnortInstance)(void); }; typedef int (*ThirdPartyAppIDModInit)(struct ThirdPartyConfig* config, struct ThirdPartyUtils* utils); typedef int (*ThirdPartyAppIDModReconfigure)(struct ThirdPartyConfig* config); typedef int (*ThirdPartyAppIDModFini)(void); typedef void* (*ThirdPartyAppIDSessionCreate)(void); typedef int (*ThirdPartyAppIDSessionDelete)(void* tpsession, int just_reset_state); typedef int (*ThirdPartyAppIDSessionProcess)(void* tpsession, // in SFSnortPacket* pkt, // in int direction, // in tAppId* appId, // out int* confidence, // out tAppId** proto_list, // out ThirdPartyAppIDAttributeData** attribute_data); // out typedef int (*ThirdPartyAppIDPrintStats)(void); typedef int (*ThirdPartyAppIDResetStats)(void); typedef int (*ThirdPartyAppIDDisableFlags)(void* tpsession, uint32_t session_flags); typedef TPState (*ThirdPartyAppIDSessionStateGet)(void* tpsession); typedef void (*ThirdPartyAppIDSessionStateSet)(void* tpsession, TPState); typedef void (*ThirdPartyAppIDSessionAttrSet)(void* tpsession, TPSessionAttr attr); typedef void (*ThirdPartyAppIDSessionAttrClear)(void* tpsession, TPSessionAttr attr); typedef unsigned (*ThirdPartyAppIDSessionAttrGet)(void* tpsession, TPSessionAttr attr); typedef tAppId (*ThirdPartyAppIDSessionCurrentAppIdGet)(void* tpsession); // SF_SO_PUBLIC const ThirdPartyAppIDModule thirdparty_appid_impl_module typedef struct _ThirdPartyAppIDModule { const uint32_t api_version; const char* module_name; ThirdPartyAppIDModInit init; ThirdPartyAppIDModReconfigure reconfigure; ThirdPartyAppIDModFini fini; ThirdPartyAppIDSessionCreate session_create; ThirdPartyAppIDSessionDelete session_delete; ThirdPartyAppIDSessionProcess session_process; ThirdPartyAppIDPrintStats print_stats; ThirdPartyAppIDResetStats reset_stats; ThirdPartyAppIDDisableFlags disable_flags; ThirdPartyAppIDSessionStateGet session_state_get; ThirdPartyAppIDSessionStateSet session_state_set; ThirdPartyAppIDSessionAttrSet session_attr_set; ThirdPartyAppIDSessionAttrClear session_attr_clear; ThirdPartyAppIDSessionAttrGet session_attr_get; ThirdPartyAppIDSessionCurrentAppIdGet session_appid_get; } ThirdPartyAppIDModule; #endif snort-2.9.20/src/dynamic-preprocessors/appid/app_forecast.h0000644000175000017500000000371414241075336022141 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _APP_FORECAST_H #define _APP_FORECAST_H #include #include #include "commonAppMatcher.h" #include "appId.h" #include "flow.h" #include "fw_appid.h" #include "appIdConfig.h" /* indicator - the appId that indicates there may be subsequent flows to look for, from the same host forecast - the appId in the subsequent flow that we are looking for target - the appId we want to set in that subsequent flow for now, indicator and target are WEB APPLICATIONS. The forecast is APP PROTOCOL. We can change this later by adding app type info for each, if we find a use case. */ typedef struct _AF_IND_ELEMENT { tAppId indicator; tAppId forecast; tAppId target; } AFElement; typedef struct _AF_ACTIVE_KEY { uint32_t ip[4]; tAppId forecast; } AFActKey; typedef struct _AF_ACT_VALUE { tAppId target; time_t last; } AFActVal; void checkSessionForAFIndicator(SFSnortPacket *, int, const tAppIdConfig *, tAppId); tAppId checkSessionForAFForecast(tAppIdData *, SFSnortPacket *, int, const tAppIdConfig *, tAppId); #endif snort-2.9.20/src/dynamic-preprocessors/appid/appInfoTable.h0000644000175000017500000001016614241075352022034 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __APP_INFO_TABLE_H__ #define __APP_INFO_TABLE_H__ #include "appId.h" #include "client_app_api.h" #include "detector_api.h" #include "service_api.h" #define APP_PRIORITY_DEFAULT 2 #define HTTP_TUNNEL_DETECT_RESTART 0 #define HTTP_TUNNEL_DETECT_RESTART_AND_RESET 1 typedef enum { APPINFO_FLAG_SERVICE_ADDITIONAL = (1<<0), APPINFO_FLAG_SERVICE_UDP_REVERSED = (1<<1), APPINFO_FLAG_CLIENT_ADDITIONAL = (1<<2), APPINFO_FLAG_CLIENT_USER = (1<<3), APPINFO_FLAG_ACTIVE = (1<<4), APPINFO_FLAG_SSL_INSPECT = (1<<5), APPINFO_FLAG_REFERRED = (1<<6), APPINFO_FLAG_DEFER = (1<<7), APPINFO_FLAG_IGNORE = (1<<8), APPINFO_FLAG_SSL_SQUELCH = (1<<9), APPINFO_FLAG_PERSISTENT = (1<<10), APPINFO_FLAG_TP_CLIENT = (1<<11), APPINFO_FLAG_DEFER_PAYLOAD = (1<<12), APPINFO_FLAG_SEARCH_ENGINE = (1<<13), APPINFO_FLAG_SUPPORTED_SEARCH = (1<<14), APPINFO_FLAG_CLIENT_DETECTOR_CALLBACK = (1<<15), APPINFO_FLAG_SERVICE_DETECTOR_CALLBACK = (1<<16) } tAppInfoFlags; struct _AppInfoTableEntry { struct _AppInfoTableEntry *next; tAppId appId; uint32_t serviceId; uint32_t clientId; uint32_t payloadId; int16_t snortId; uint32_t flags; tRNAClientAppModule *clntValidator; tRNAServiceElement *svrValidator; uint32_t priority; char *appName; }; typedef struct _AppInfoTableEntry AppInfoTableEntry; void appInfoTableInit(tAppidStaticConfig* appidSC, tAppIdConfig* pConfig); void appInfoTableFini(tAppIdConfig *pConfig); AppInfoTableEntry* appInfoEntryGet(tAppId appId, const tAppIdConfig *pConfig); AppInfoTableEntry* appInfoEntryCreate(const char *appName, tAppIdConfig *pConfig); tAppId appGetSnortIdFromAppId(tAppId appId); void AppIdDumpStats(int exit_flag); void appInfoTableDump(tAppIdConfig *pConfig); void appInfoSetActive(tAppId appId, bool active); const char * appGetAppName(int32_t appId); int32_t appGetAppId(const char *appName); static inline void appInfoEntryFlagSet (tAppId appId, unsigned flags, const tAppIdConfig *pConfig) { AppInfoTableEntry* entry = appInfoEntryGet(appId, pConfig); if (entry) entry->flags |= flags; } static inline void appInfoEntryFlagClear (tAppId appId, unsigned flags, const tAppIdConfig *pConfig) { AppInfoTableEntry* entry = appInfoEntryGet(appId, pConfig); if (entry) entry->flags &= (~flags); } static inline unsigned appInfoEntryFlagGet (tAppId app_id, unsigned flags, const tAppIdConfig *pConfig) { AppInfoTableEntry* entry = appInfoEntryGet(app_id, pConfig); if (entry) return (entry->flags & flags); return 0; } static inline uint32_t appInfoEntryFlags (tAppId app_id, const tAppIdConfig *pConfig) { AppInfoTableEntry* entry = appInfoEntryGet(app_id, pConfig); if (entry) return entry->flags; return 0; } static inline void appInfoEntryPrioritySet (tAppId appId, unsigned priority, const tAppIdConfig *pConfig) { AppInfoTableEntry* entry = appInfoEntryGet(appId, pConfig); if (entry) entry->priority |= priority; } static inline unsigned appInfoEntryPriorityGet (tAppId app_id, const tAppIdConfig *pConfig) { AppInfoTableEntry* entry = appInfoEntryGet(app_id, pConfig); if (entry) return (entry->priority); return 0; } #endif snort-2.9.20/src/dynamic-preprocessors/appid/dns_defs.h0000644000175000017500000000507614241075434021262 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DNS_DEFS_H__ #define __DNS_DEFS_H__ #include #define MAX_OPCODE 5 #define INVALID_OPCODE 3 #define MAX_RCODE 10 #define RCODE_NXDOMAIN 3 #define DNS_LENGTH_FLAGS 0xC0 #define PATTERN_A_REC 1 #define PATTERN_AAAA_REC 28 #define PATTERN_CNAME_REC 5 #define PATTERN_SRV_REC 33 #define PATTERN_TXT_REC 16 #define PATTERN_MX_REC 15 #define PATTERN_SOA_REC 6 #define PATTERN_NS_REC 2 #define PATTERN_PTR_REC 12 #define PATTERN_ANY_REC 255 #pragma pack(1) typedef struct _DNS_HEADER { uint16_t id; #if defined(SF_BIGENDIAN) uint8_t QR:1, Opcode:4, AA:1, TC:1, RD:1; uint8_t RA:1, Z:1, AD:1, CD:1, RCODE:4; #else uint8_t RD:1, TC:1, AA:1, Opcode:4, QR:1; uint8_t RCODE:4, CD:1, AD:1, Z:1, RA:1; #endif uint16_t QDCount; uint16_t ANCount; uint16_t NSCount; uint16_t ARCount; } DNSHeader; typedef struct _DNS_TCP_HEADER { uint16_t length; } DNSTCPHeader; typedef struct _DNS_LABEL { uint8_t len; uint8_t name; } DNSLabel; typedef struct _DNS_LABEL_POINTER { uint16_t position; uint8_t data; } DNSLabelPtr; typedef struct _DNS_LABEL_BITFIELD { uint8_t id; uint8_t len; uint8_t data; } DNSLabelBitfield; typedef struct _DNS_QUERY_FIXED { uint16_t QType; uint16_t QClass; } DNSQueryFixed; typedef struct _DNS_ANSWER_DATA { uint16_t type; uint16_t class; uint32_t ttl; uint16_t r_len; } DNSAnswerData; #pragma pack() #endif /* __DNS_DEFS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/appId.c0000644000175000017500000000170314241075337020520 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include "appId.h" snort-2.9.20/src/dynamic-preprocessors/appid/Makefile_defs0000444000175000017500000001710114230012554021743 0ustar apoapoAPPID_SRC_DIR = ${top_srcdir}/src/dynamic-preprocessors/appid INCLUDES = -I${top_builddir}/src/dynamic-preprocessors/include \ -I$(APPID_SRC_DIR)/../libs \ -I$(APPID_SRC_DIR)/util \ -I$(APPID_SRC_DIR)/service_plugins \ -I$(APPID_SRC_DIR)/client_plugins \ -I$(APPID_SRC_DIR)/detector_plugins APPID_SOURCES = \ $(APPID_SRC_DIR)/commonAppMatcher.c \ $(APPID_SRC_DIR)/flow.c \ $(APPID_SRC_DIR)/fw_appid.c \ $(APPID_SRC_DIR)/hostPortAppCache.c \ $(APPID_SRC_DIR)/luaDetectorApi.c \ $(APPID_SRC_DIR)/luaDetectorApi.h \ $(APPID_SRC_DIR)/luaDetectorFlowApi.c \ $(APPID_SRC_DIR)/luaDetectorModule.c \ $(APPID_SRC_DIR)/luaDetectorModule.h \ $(APPID_SRC_DIR)/service_state.c \ $(APPID_SRC_DIR)/spp_appid.c \ $(APPID_SRC_DIR)/appId.c \ $(APPID_SRC_DIR)/appId.h \ $(APPID_SRC_DIR)/appId_ss.h \ $(APPID_SRC_DIR)/appId_ss.c \ $(APPID_SRC_DIR)/appIdApi.c \ $(APPID_SRC_DIR)/commonAppMatcher.h \ $(APPID_SRC_DIR)/flow.h \ $(APPID_SRC_DIR)/hostPortAppCache.h \ $(APPID_SRC_DIR)/httpCommon.h \ $(APPID_SRC_DIR)/luaDetectorFlowApi.h \ $(APPID_SRC_DIR)/service_state.h \ $(APPID_SRC_DIR)/spp_appid.h \ $(APPID_SRC_DIR)/attribute.h \ $(APPID_SRC_DIR)/flow_error.h \ $(APPID_SRC_DIR)/fw_appid.h \ $(APPID_SRC_DIR)/appInfoTable.c \ $(APPID_SRC_DIR)/appInfoTable.h \ $(APPID_SRC_DIR)/appIdStats.c \ $(APPID_SRC_DIR)/appIdStats.h \ $(APPID_SRC_DIR)/appIdConfig.c \ $(APPID_SRC_DIR)/appIdConfig.h \ $(APPID_SRC_DIR)/app_forecast.c \ $(APPID_SRC_DIR)/app_forecast.h \ $(APPID_SRC_DIR)/lengthAppCache.c \ $(APPID_SRC_DIR)/lengthAppCache.h \ $(APPID_SRC_DIR)/thirdparty_appid_api.h \ $(APPID_SRC_DIR)/thirdparty_appid_types.h \ $(APPID_SRC_DIR)/thirdparty_appid_utils.c \ $(APPID_SRC_DIR)/thirdparty_appid_utils.h \ $(APPID_SRC_DIR)/dns_defs.h APPID_SOURCES += \ $(APPID_SRC_DIR)/client_plugins/client_app_base.c \ $(APPID_SRC_DIR)/client_plugins/client_app_ym.c \ $(APPID_SRC_DIR)/client_plugins/client_app_msn.c \ $(APPID_SRC_DIR)/client_plugins/client_app_aim.c \ $(APPID_SRC_DIR)/client_plugins/client_app_msn.h \ $(APPID_SRC_DIR)/client_plugins/client_app_aim.h \ $(APPID_SRC_DIR)/client_plugins/client_app_ym.h \ $(APPID_SRC_DIR)/client_plugins/client_app_api.h \ $(APPID_SRC_DIR)/client_plugins/client_app_base.h \ $(APPID_SRC_DIR)/client_plugins/client_app_bit.c \ $(APPID_SRC_DIR)/client_plugins/client_app_bit_tracker.c \ $(APPID_SRC_DIR)/client_plugins/client_app_rtp.c \ $(APPID_SRC_DIR)/client_plugins/client_app_ssh.c \ $(APPID_SRC_DIR)/client_plugins/client_app_timbuktu.c \ $(APPID_SRC_DIR)/client_plugins/client_app_tns.c \ $(APPID_SRC_DIR)/client_plugins/client_app_vnc.c \ $(APPID_SRC_DIR)/client_plugins/clientAppConfig.h \ $(APPID_SRC_DIR)/client_plugins/client_app_rtp.h APPID_SOURCES += \ $(APPID_SRC_DIR)/service_plugins/service_util.h \ $(APPID_SRC_DIR)/service_plugins/service_base.h \ $(APPID_SRC_DIR)/service_plugins/service_base.c \ $(APPID_SRC_DIR)/service_plugins/service_api.h \ $(APPID_SRC_DIR)/service_plugins/dcerpc.h \ $(APPID_SRC_DIR)/service_plugins/dcerpc.c \ $(APPID_SRC_DIR)/service_plugins/service_mysql.c \ $(APPID_SRC_DIR)/service_plugins/service_ssh.h \ $(APPID_SRC_DIR)/service_plugins/service_radius.c \ $(APPID_SRC_DIR)/service_plugins/service_telnet.c \ $(APPID_SRC_DIR)/service_plugins/service_rexec.h \ $(APPID_SRC_DIR)/service_plugins/service_nntp.h \ $(APPID_SRC_DIR)/service_plugins/service_bootp.c \ $(APPID_SRC_DIR)/service_plugins/service_ntp.c \ $(APPID_SRC_DIR)/service_plugins/service_rsync.h \ $(APPID_SRC_DIR)/service_plugins/service_flap.c \ $(APPID_SRC_DIR)/service_plugins/service_battle_field.h \ $(APPID_SRC_DIR)/service_plugins/service_rshell.c \ $(APPID_SRC_DIR)/service_plugins/service_netbios.c \ $(APPID_SRC_DIR)/service_plugins/service_ftp.h \ $(APPID_SRC_DIR)/service_plugins/service_snmp.c \ $(APPID_SRC_DIR)/service_plugins/service_radius.h \ $(APPID_SRC_DIR)/service_plugins/service_ntp.h \ $(APPID_SRC_DIR)/service_plugins/service_battle_field.c \ $(APPID_SRC_DIR)/service_plugins/service_rshell.h \ $(APPID_SRC_DIR)/service_plugins/service_direct_connect.h \ $(APPID_SRC_DIR)/service_plugins/service_bootp.h \ $(APPID_SRC_DIR)/service_plugins/service_mysql.h \ $(APPID_SRC_DIR)/service_plugins/service_rexec.c \ $(APPID_SRC_DIR)/service_plugins/service_rfb.h \ $(APPID_SRC_DIR)/service_plugins/service_rfb.c \ $(APPID_SRC_DIR)/service_plugins/service_dcerpc.h \ $(APPID_SRC_DIR)/service_plugins/service_lpr.h \ $(APPID_SRC_DIR)/service_plugins/service_ssl.h \ $(APPID_SRC_DIR)/service_plugins/service_MDNS.h \ $(APPID_SRC_DIR)/service_plugins/service_rpc.c \ $(APPID_SRC_DIR)/service_plugins/service_flap.h \ $(APPID_SRC_DIR)/service_plugins/service_telnet.h \ $(APPID_SRC_DIR)/service_plugins/service_bgp.c \ $(APPID_SRC_DIR)/service_plugins/service_direct_connect.c \ $(APPID_SRC_DIR)/service_plugins/service_irc.c \ $(APPID_SRC_DIR)/service_plugins/service_rlogin.h \ $(APPID_SRC_DIR)/service_plugins/service_rpc.h \ $(APPID_SRC_DIR)/service_plugins/service_ssh.c \ $(APPID_SRC_DIR)/service_plugins/service_tftp.c \ $(APPID_SRC_DIR)/service_plugins/service_ftp.c \ $(APPID_SRC_DIR)/service_plugins/service_rsync.c \ $(APPID_SRC_DIR)/service_plugins/service_rlogin.c \ $(APPID_SRC_DIR)/service_plugins/service_tftp.h \ $(APPID_SRC_DIR)/service_plugins/service_netbios.h \ $(APPID_SRC_DIR)/service_plugins/service_lpr.c \ $(APPID_SRC_DIR)/service_plugins/service_nntp.c \ $(APPID_SRC_DIR)/service_plugins/service_snmp.h \ $(APPID_SRC_DIR)/service_plugins/service_bgp.h \ $(APPID_SRC_DIR)/service_plugins/service_MDNS.c \ $(APPID_SRC_DIR)/service_plugins/service_irc.h \ $(APPID_SRC_DIR)/service_plugins/service_dcerpc.c \ $(APPID_SRC_DIR)/service_plugins/service_ssl.c \ $(APPID_SRC_DIR)/service_plugins/service_bit.c \ $(APPID_SRC_DIR)/service_plugins/service_timbuktu.c \ $(APPID_SRC_DIR)/service_plugins/service_tns.c \ $(APPID_SRC_DIR)/service_plugins/service_rtmp.h \ $(APPID_SRC_DIR)/service_plugins/service_rtmp.c \ $(APPID_SRC_DIR)/service_plugins/serviceConfig.h APPID_SOURCES += \ $(APPID_SRC_DIR)/detector_plugins/detector_api.h \ $(APPID_SRC_DIR)/detector_plugins/detector_base.h \ $(APPID_SRC_DIR)/detector_plugins/detector_base.c \ $(APPID_SRC_DIR)/detector_plugins/http_url_patterns.c \ $(APPID_SRC_DIR)/detector_plugins/detector_http.c \ $(APPID_SRC_DIR)/detector_plugins/detector_http.h \ $(APPID_SRC_DIR)/detector_plugins/http_url_patterns.h \ $(APPID_SRC_DIR)/detector_plugins/detector_imap.c \ $(APPID_SRC_DIR)/detector_plugins/detector_kerberos.c \ $(APPID_SRC_DIR)/detector_plugins/detector_pop3.c \ $(APPID_SRC_DIR)/detector_plugins/detector_smtp.c \ $(APPID_SRC_DIR)/detector_plugins/detector_sip.c \ $(APPID_SRC_DIR)/detector_plugins/detector_sip.h \ $(APPID_SRC_DIR)/detector_plugins/detector_pattern.c \ $(APPID_SRC_DIR)/detector_plugins/detector_pattern.h \ $(APPID_SRC_DIR)/detector_plugins/detector_dns.c \ $(APPID_SRC_DIR)/detector_plugins/detector_dns.h \ $(APPID_SRC_DIR)/detector_plugins/detector_cip.c \ $(APPID_SRC_DIR)/detector_plugins/detector_cip.h APPID_SOURCES += \ $(APPID_SRC_DIR)/util/common_util.h \ $(APPID_SRC_DIR)/util/common_util.c \ $(APPID_SRC_DIR)/util/sf_multi_mpse.c \ $(APPID_SRC_DIR)/util/sf_mlmp.h \ $(APPID_SRC_DIR)/util/sf_multi_mpse.h \ $(APPID_SRC_DIR)/util/sf_mlmp.c \ $(APPID_SRC_DIR)/util/fw_avltree.c \ $(APPID_SRC_DIR)/util/fw_avltree.h \ $(APPID_SRC_DIR)/util/OutputFile.c \ $(APPID_SRC_DIR)/util/OutputFile.h \ $(APPID_SRC_DIR)/util/NetworkSet.c \ $(APPID_SRC_DIR)/util/NetworkSet.h \ $(APPID_SRC_DIR)/util/ip_funcs.c \ $(APPID_SRC_DIR)/util/ip_funcs.h \ $(APPID_SRC_DIR)/util/sfutil.c \ $(APPID_SRC_DIR)/util/sfutil.h snort-2.9.20/src/dynamic-preprocessors/appid/service_state.h0000644000175000017500000001471314241075571022335 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SERVICE_STATE_H_ #define _SERVICE_STATE_H_ #include #include #include #include /**Service state stored in hosttracker for maintaining service matching states. */ typedef enum { /**first search of service. The matching criteria is coded in ProtocolID funtion. */ SERVICE_ID_NEW = 0, /**service is already detected and valid. */ SERVICE_ID_VALID, /**match based on round-robin through tcpServiceList or UdpServiceList. RNA walks * the list from first element to last. In a detector declares a flow incompatible * or the flow closes earlier than expected by detector, then the next detector is * tried. This can obviously delay detection under some scenarios. */ SERVICE_ID_BRUTE_FORCE, /**if brute-force failed after a complete walk of the list, * we stop searching and move into this state. */ SERVICE_ID_BRUTE_FORCE_FAILED, } SERVICE_ID_STATE; /* Service state stored per flow, which acts based on global SERVICE_ID_STATE * at the beginning of the flow, then independently do service discovery, and * synchronize findings at the end of service discovery by the flow. */ typedef enum { /* First attempt in search of service. */ SERVICE_ID_START = 0, /* Match based on source or destination port in first packet in flow. */ SERVICE_ID_PORT, /* Match based on pattern in first response from server or * client in case of client_services. */ SERVICE_ID_PATTERN, /* Flow is in this state after we retrieve all port/pattern candidates. */ SERVICE_ID_PENDING, } FLOW_SERVICE_ID_STATE; #define DETECTOR_TYPE_PASSIVE 0 #define DETECTOR_TYPE_DECODER 0 #define DETECTOR_TYPE_NETFLOW 1 #define DETECTOR_TYPE_PORT 2 #define DETECTOR_TYPE_DERIVED 3 #define DETECTOR_TYPE_CONFLICT 4 #define DETECTOR_TYPE_PATTERN 5 /**We will NOT hold onto pointers to host tracker entries (because they could be pruned). * When a session starts discovery, it retrieves the last-known id_state at the beginning of the session, * does independent service discovery, and * reconciles any changes back to the entry once a service determination is made. */ struct RNAServiceElement; struct _SERVICE_MATCH; typedef struct _APP_ID_SERVICE_ID_STATE { const struct RNAServiceElement *svc; /**State of service identification.*/ SERVICE_ID_STATE state; unsigned valid_count; unsigned detract_count; sfaddr_t last_detract; /**Number of consequetive flows that were declared incompatible by detectors. Incompatibility * means client packet did not match. */ unsigned invalid_client_count; /**IP address of client in last flow that was declared incompatible. If client IP address is * different everytime, then consequetive incompatible status indicate that flow is not using * specific service. */ sfaddr_t last_invalid_client; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t asId; #endif /** Count for number of unknown sessions saved */ unsigned unknowns_logged; time_t reset_time; } AppIdServiceIDState; typedef struct { uint16_t port; uint16_t proto; uint32_t ip; uint32_t level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid; #endif } AppIdServiceStateKey4; typedef struct { uint16_t port; uint16_t proto; uint8_t ip[16]; uint32_t level; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid; #endif } AppIdServiceStateKey6; typedef union { AppIdServiceStateKey4 key4; AppIdServiceStateKey6 key6; } AppIdServiceStateKey; bool AppIdServiceStateReloadAdjust(bool idle, unsigned long memcap); int AppIdServiceStateInit(unsigned long memcap); void AppIdServiceStateCleanup(void); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId, uint32_t cid); AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId, uint32_t cid); AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId, uint32_t cid); #else void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint32_t cid); AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint32_t cid); AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint32_t cid); #endif #else /* No carrier id */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId); AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId); AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level, uint16_t asId); #else void AppIdRemoveServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level); AppIdServiceIDState* AppIdGetServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level); AppIdServiceIDState* AppIdAddServiceIDState(sfaddr_t *ip, uint16_t proto, uint16_t port, uint32_t level); #endif #endif void AppIdServiceStateDumpStats(void); #endif snort-2.9.20/src/dynamic-preprocessors/appid/httpCommon.h0000644000175000017500000001672114241075444021625 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __HTTP_COMMON_H__ #define __HTTP_COMMON_H__ #include #include #include "sf_multi_mpse.h" #include "sf_mlmp.h" #include "flow.h" #define MAX_USERNAME_SIZE 64 #define MAX_URL_SIZE 65535 // These values are used in Lua code as raw numbers. Do NOT reassign new values. typedef enum { SINGLE = 0, SKYPE_URL = 1, SKYPE_VERSION = 2, BT_ANNOUNCE = 3, BT_OTHER = 4, USER_AGENT_HEADER = 5 /* HOST_HEADER, CONTENT_TYPE_HEADER, SERVER_HEADER */ } DHPSequence; typedef struct { DHPSequence seq; tAppId service_id; tAppId client_app; tAppId payload; int pattern_size; uint8_t *pattern; tAppId appId; } DetectorHTTPPattern; typedef struct HTTPListElementStruct { DetectorHTTPPattern detectorHTTPPattern; struct HTTPListElementStruct* next; } HTTPListElement; #define APPL_VERSION_LENGTH 40 typedef struct { uint32_t service_id; uint32_t client_app; uint32_t payload; tAppId appId; tMlpPattern query; } tUrlUserData; typedef struct { struct { tMlpPattern host; tMlpPattern path; tMlpPattern scheme; } patterns; tUrlUserData userData; } DetectorAppUrlPattern; typedef struct DetectorAppUrlListStruct { DetectorAppUrlPattern **urlPattern; size_t usedCount; size_t allocatedCount; } DetectorAppUrlList; // These values are used in Lua code as raw numbers. Do NOT reassign new values. #define APP_TYPE_SERVICE 0x1 #define APP_TYPE_CLIENT 0x2 #define APP_TYPE_PAYLOAD 0x4 // These values are used in Lua code as raw numbers. Do NOT reassign new values. typedef enum { NO_ACTION, //0 COLLECT_VERSION, //1 EXTRACT_USER, //2 REWRITE_FIELD, //3 INSERT_FIELD, //4 ALTERNATE_APPID, //5 FUTURE_APPID_SESSION_SIP, //6 FUTURE_APPID_SESSION_DIP, //7 FUTURE_APPID_SESSION_SPORT, //8 FUTURE_APPID_SESSION_DPORT, //9 FUTURE_APPID_SESSION_PROTOCOL, //10 FUTURE_APPID_SESSION_CREATE, //11 HOLD_FLOW, //12 GET_OFFSETS_FROM_REBUILT, //13 SEARCH_UNSUPPORTED, //14 DEFER_TO_SIMPLE_DETECT, //15 MAX_ACTION_TYPE = DEFER_TO_SIMPLE_DETECT, } ActionType; // These values are used in Lua code as raw numbers. Do NOT reassign new values. typedef enum { // Request-side headers AGENT_PT, // 0 HOST_PT, // 1 REFERER_PT, // 2 URI_PT, // 3 COOKIE_PT, // 4 REQ_BODY_PT, // 5 // Response-side headers CONTENT_TYPE_PT, // 6 LOCATION_PT, // 7 BODY_PT, // 8 MAX_PATTERN_TYPE = BODY_PT, MAX_KEY_PATTERN = URI_PT, } PatternType; #define CHP_APPID_BITS_FOR_INSTANCE 7 #define CHP_APPID_INSTANCE_MAX (1 << CHP_APPID_BITS_FOR_INSTANCE) #define CHP_APPIDINSTANCE_TO_ID(_appIdInstance) \ (_appIdInstance >> CHP_APPID_BITS_FOR_INSTANCE) #define CHP_APPIDINSTANCE_TO_INSTANCE(_appIdInstance) \ (_appIdInstance & (CHP_APPID_INSTANCE_MAX-1)) /* NOTE: The following structures have a field called appIdInstance. The low-order CHP_APPID_BITS_FOR_INSTANCE bits of appIdInstance field are used for the instance value while the remaining bits are used for the appId, shifted left that same number of bits. The legacy value for older apis is generated with the macro below. */ #define CHP_APPID_SINGLE_INSTANCE(_appId) \ ((_appId << CHP_APPID_BITS_FOR_INSTANCE) + (CHP_APPID_INSTANCE_MAX-1)) typedef struct _CHPApp { tAppId appIdInstance; // * see note above unsigned app_type_flags; int num_matches; int num_scans; int key_pattern_count; int key_pattern_length_sum; int ptype_scan_counts[NUMBER_OF_PTYPES]; int ptype_req_counts[NUMBER_OF_PTYPES]; } CHPApp; typedef struct _CHPAction { tAppId appIdInstance; // * see note above uint precedence; // order of creation int key_pattern; PatternType ptype; int psize; char *pattern; ActionType action; char *action_data; CHPApp *chpapp; } CHPAction; typedef struct _CHPListElement { CHPAction chp_action; struct _CHPListElement *next; } CHPListElement; typedef struct _HttpPatternLists { HTTPListElement* hostPayloadPatternList; HTTPListElement* urlPatternList; HTTPListElement* clientAgentPatternList; HTTPListElement* contentTypePatternList; CHPListElement* chpList; DetectorAppUrlList appUrlList; DetectorAppUrlList RTMPUrlList; } HttpPatternLists; /**url parts extracted from http headers. * "http" */ typedef struct { tMlpPattern host; /*from host header */ tMlpPattern path; /*from GET/POST request */ tMlpPattern scheme; /*hardcoded to "http:" */ tMlpPattern query; /*query match for version number */ } tUrlStruct; typedef struct _HostUrlDetectorPattern { tMlpPattern host; tMlpPattern path; tMlpPattern query; uint32_t payload_id; uint32_t service_id; uint32_t client_id; tAppId appId; DHPSequence seq; struct _HostUrlDetectorPattern *next; } HostUrlDetectorPattern; typedef struct _HostUrlPatternsList { HostUrlDetectorPattern *head; HostUrlDetectorPattern *tail; } HostUrlPatternsList; struct DetectorHttpConfig { void *url_matcher; void *client_agent_matcher; void *via_matcher; void *hostUrlMatcher; void *RTMPHostUrlMatcher; void *header_matcher; void *content_type_matcher; void *field_matcher; // CHP matchers // TODO: Is there a need for these variables? They just point to the pointers in the // array chp_matchers[]. They are used only in the function http_detector_clean(). But // there we could easily traverse through the members of chp_matchers instead of using // these variables. void *chp_user_agent_matcher; void *chp_host_matcher; void *chp_referer_matcher; void *chp_uri_matcher; void *chp_cookie_matcher; void *chp_content_type_matcher; void *chp_location_matcher; void *chp_body_matcher; // TODO: chp_req_body_matcher is not being used anywhere in the code, should it be removed? void *chp_req_body_matcher; void *chp_matchers[MAX_PATTERN_TYPE+1]; HostUrlPatternsList *hostUrlPatternsList; }; typedef struct DetectorHttpConfig tDetectorHttpConfig; extern tAppId getAppIdByHttpUrl( tUrlStruct *url, tUrlUserData **rnaData); #endif snort-2.9.20/src/dynamic-preprocessors/appid/thirdparty_appid_utils.h0000644000175000017500000000243214241075600024250 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _THIRDPARTY_APPID_UTILS_H_ #define _THIRDPARTY_APPID_UTILS_H_ #include "appIdConfig.h" #include "thirdparty_appid_api.h" #include "appIdConfig.h" extern ThirdPartyAppIDModule* thirdparty_appid_module; //NULL means no 3rd party AppID module void ThirdPartyAppIDInit(struct AppidStaticConfig *appidStaticConfig); void ThirdPartyAppIDReconfigure(void); void ThirdPartyAppIDFini(void); #endif snort-2.9.20/src/dynamic-preprocessors/appid/attribute.h0000644000175000017500000000776714241075353021511 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef ATTRIBUTE_H #define ATTRIBUTE_H #include #ifdef __cplusplus extern "C" { #endif #define _IOR_SIZE_MASK 0xFF000000 #define _IOR_TYPE_MASK 0x00FFFFFF #define _IOR(x, y, z, t) \ ((((sizeof(t)) & 0xFF) << 24) | (((x) & 0xFF) << 16) | (((y) & 0xFF) << 8) | ((z) & 0xFF)) #define ATTR_SIZE_MASK _IOR_SIZE_MASK #define ATTR_TYPE_MASK _IOR_TYPE_MASK typedef void *pointer_t; /* Ethernet */ #define _ATTR_ETH(a, id, t) _IOR('e', a, id, t) #define _ATTR_ETH_HDR(id, t) _ATTR_ETH(1, id, t) #define ATTR_ETH_HDR_RAW _ATTR_ETH_HDR(0, pointer_t) #define ATTR_ETH_HDR_DST _ATTR_ETH_HDR(1, uint8_t[6]) #define ATTR_ETH_HDR_SRC _ATTR_ETH_HDR(2, uint8_t[6]) #define ATTR_ETH_HDR_8021Q _ATTR_ETH_HDR(3, uint32_t) #define ATTR_ETH_HDR_TYPE _ATTR_ETH_HDR(4, uint16_t) /* IPv4 */ #define _ATTR_IP(a, id, t) _IOR('i', a, id, t) #define _ATTR_IP_HDR(id, t) _ATTR_IP(1, id, t) #define _ATTR_IP_SYS(id, t) _ATTR_IP(2, id, t) /* IPv4 Header Attributes */ #define ATTR_IP_HDR_RAW _ATTR_IP_HDR(0, pointer_t) #define ATTR_IP_HDR_IHL _ATTR_IP_HDR(1, uint16_t) #define ATTR_IP_HDR_LEN _ATTR_IP_HDR(2, uint16_t) #define ATTR_IP_HDR_TOS _ATTR_IP_HDR(3, uint8_t) #define ATTR_IP_HDR_ID _ATTR_IP_HDR(4, uint16_t) #define ATTR_IP_HDR_TTL _ATTR_IP_HDR(5, uint8_t) #define ATTR_IP_HDR_PTCL _ATTR_IP_HDR(6, uint8_t) #define ATTR_IP_HDR_CSUM _ATTR_IP_HDR(7, uint16_t) #define ATTR_IP_HDR_SRC _ATTR_IP_HDR(8, uint32_t) #define ATTR_IP_HDR_DST _ATTR_IP_HDR(9, uint32_t) /* IPv4 System Attributes */ #define ATTR_IP_SYS_CID _ATTR_IP_SYS(1, uint32_t) /* TCP */ #define _ATTR_TCP(a, id, t) _IOR('t', a, id, t) #define _ATTR_TCP_HDR(id, t) _ATTR_TCP(1, id, t) #define _ATTR_TCP_SYS(id, t) _ATTR_TCP(2, id, t) #define _ATTR_TCP_APP(id, t) _ATTR_TCP(3, id, t) /* TCP Header Attributes */ #define ATTR_TCP_HDR_RAW _ATTR_TCP_HDR(0, pointer_t) #define ATTR_TCP_HDR_SRC _ATTR_TCP_HDR(1, uint16_t) #define ATTR_TCP_HDR_DST _ATTR_TCP_HDR(2, uint16_t) /* TCP System Attributes */ #define ATTR_TCP_SYS_RETX _ATTR_TCP_SYS(1, uint8_t) /* TCP Port-based Application Attribute */ #define ATTR_TCP_APP_ID _ATTR_TCP_APP(1, uint16_t) /* UDP */ #define _ATTR_UDP(a, id, t) _IOR('u', a, id, t) #define _ATTR_UDP_HDR(id, t) _ATTR_UDP(1, id, t) #define _ATTR_UDP_APP(id, t) _ATTR_UDP(2, id, t) /* UDP Header Attributes */ #define ATTR_UDP_HDR_RAW _ATTR_UDP_HDR(0, pointer_t) #define ATTR_UDP_HDR_SRC _ATTR_UDP_HDR(1, uint16_t) #define ATTR_UDP_HDR_DST _ATTR_UDP_HDR(2, uint16_t) /* UDP Port-based Application Attribute */ #define ATTR_UDP_APP_ID _ATTR_UDP_APP(1, uint16_t) /* HTTP */ #define _ATTR_HTTP(a, t) _IOR('h', a, 0, t) #define ATTR_HTTP_HOST _ATTR_HTTP(0, pointer_t) #define ATTR_HTTP_CTYPE _ATTR_HTTP(1, pointer_t) #define ATTR_HTTP_URL _ATTR_HTTP(2, pointer_t) /* FACEBOOK */ #define _ATTR_FACEBOOK(a, t) _IOR('f', a, 0, t) #define ATTR_FACEBOOK_APP _ATTR_FACEBOOK(0, pointer_t) /* CITRIX */ #define _ATTR_CITRIX(a, t) _IOR('c', a, 0, t) #define ATTR_CITRIX_PRIORITY _ATTR_CITRIX(0, uint8_t) /* YAHOO */ #define _ATTR_YMSGFILE(a, t) _IOR('y', a, 0, t) #define ATTR_YMSGFILE_NAME _ATTR_YMSGFILE(0, pointer_t) #ifdef __cplusplus } #endif #endif snort-2.9.20/src/dynamic-preprocessors/appid/appId_ss.h0000644000175000017500000000370514241075342021232 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2012-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ /************************************************************************** * * appId_ss.h * * Authors: Hareesh Vutla * * Description: * * AppID state sharing exported functionality. * **************************************************************************/ #ifndef __APPID_SS_H__ #define __APPID_SS_H__ #ifdef SIDE_CHANNEL #include "appIdConfig.h" void AppIdPrintSSConfig(AppIdSSConfig *appId_ss_config); void AppIdSSConfigFree(AppIdSSConfig *appId_ss_config); void AppIdPrintSSStats(void); void AppIdResetSSStats(void); void AppIdSSPostConfigInit(struct _SnortConfig *sc, int unused, void *arg); void AppIdCleanSS(void); int CreateAppIdSSUpdate(void **msg_handle, void **hdr_ptr, void **data_ptr, uint32_t type, uint32_t data_len); int SendAppIdSSUpdate(void *msg_handle, void *hdr_ptr, void *data_ptr, uint32_t type, uint32_t data_len); #endif /* SIDE_CHANNEL */ #endif /* __APPID_SS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/appIdConfig.c0000644000175000017500000003336314241075344021653 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include "sf_dynamic_preprocessor.h" #include "appIdConfig.h" #include "common_util.h" #ifdef SIDE_CHANNEL #include "appId_ss.h" #endif #define APP_ID_MEMCAP_DEFAULT (256*1024*1024ULL) #define APP_ID_MEMCAP_UPPER_BOUND (3*1024*1024*1024ULL) #define APP_ID_MEMCAP_LOWER_BOUND (32*1024*1024ULL) #define APP_ID_MEMCAP_ABSOLUTE_LOWER_BOUND (1*1024*1024UL + 16UL) #define DEFAULT_APPID_DETECTOR_PATH "/usr/local/etc/appid" static void appIdConfigDump(tAppidStaticConfig* appidSC) { _dpd.logMsg("AppId Configuration\n"); _dpd.logMsg(" Detector Path: %s\n", appidSC->app_id_detector_path ? appidSC->app_id_detector_path : "NULL"); _dpd.logMsg(" appStats Files: %s\n", appidSC->app_stats_filename ? appidSC->app_stats_filename : "NULL"); _dpd.logMsg(" appStats Period: %d secs\n", appidSC->app_stats_period); _dpd.logMsg(" appStats Rollover Size: %d bytes\n", appidSC->app_stats_rollover_size); _dpd.logMsg(" appStats Rollover time: %d secs\n", appidSC->app_stats_rollover_time); #ifdef SIDE_CHANNEL AppIdPrintSSConfig(appidSC->appId_ss_config); #endif #ifdef REG_TEST _dpd.logMsg(" AppID Reg Test Mode: %s\n", appidSC->appid_reg_test_mode ? "true" : "false"); #endif _dpd.logMsg("\n"); } void appIdConfigParse(tAppidStaticConfig* appidSC, char *args) { char **toks; int num_toks; int i; char **stoks; int s_toks; char *endPtr; char *ro_app_detector_dir; memset (appidSC, 0, sizeof(*appidSC)); #ifdef SIDE_CHANNEL if (NULL == (appidSC->appId_ss_config = (AppIdSSConfig *)_dpd.snortAlloc(1, sizeof(*appidSC->appId_ss_config), PP_APP_ID, PP_MEM_CATEGORY_CONFIG))) { _dpd.fatalMsg("Appid failed to allocate memory for state sharing configuration\n"); } if (_dpd.isSCEnabled()) { appidSC->appId_ss_config->use_side_channel = true; } #endif if ((args == NULL) || (strlen(args) == 0)) return; ro_app_detector_dir = getenv("APPID_DETECTOR_DIR"); if (ro_app_detector_dir) { if (NULL == (appidSC->app_id_detector_path = strdup(ro_app_detector_dir))) { _dpd.fatalMsg("Appid failed to allocate RO detector path\n"); } } toks = _dpd.tokenSplit(args, ",", 0, &num_toks, 0); i = 0; for (i = 0; i < num_toks; i++) { stoks = _dpd.tokenSplit(toks[i], " ", 2, &s_toks, 0); if (s_toks == 0) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Missing AppId configuration"); } if(!strcasecmp(stoks[0], "conf")) { if ((s_toks != 2) || !*stoks[1]) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid rna_conf"); } if (NULL == (appidSC->conf_file = strdup(stoks[1]))) { _dpd.fatalMsg("Appid failed to allocate configuration file name\n"); } } else if(!strcasecmp(stoks[0], "debug")) { if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid debug"); } if (!strcasecmp(stoks[1], "yes")) appidSC->app_id_debug = 1; } else if(!strcasecmp(stoks[0], "dump_ports")) { if (s_toks > 1) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid dump ports specified"); } appidSC->app_id_dump_ports = 1; } else if(!strcasecmp(stoks[0], "memcap")) { if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid memcap"); } appidSC->memcap = strtoul(stoks[1], &endPtr, 10); if (!*stoks[1] || *endPtr) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid memcap"); } if (appidSC->memcap == 0) appidSC->memcap = APP_ID_MEMCAP_LOWER_BOUND; if (appidSC->memcap > APP_ID_MEMCAP_UPPER_BOUND) appidSC->memcap = APP_ID_MEMCAP_UPPER_BOUND; if (APP_ID_MEMCAP_ABSOLUTE_LOWER_BOUND > appidSC->memcap) { _dpd.errMsg("AppId invalid memory cap, %lu, overridden with %lu", appidSC->memcap, APP_ID_MEMCAP_ABSOLUTE_LOWER_BOUND); appidSC->memcap = APP_ID_MEMCAP_ABSOLUTE_LOWER_BOUND; } } else if(!strcasecmp(stoks[0], "app_stats_filename")) { if ((s_toks != 2) || !*stoks[1]) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid stats_filename"); } if (NULL == (appidSC->app_stats_filename = strdup(stoks[1]))) { _dpd.fatalMsg("Appid failed to allocate stats file name\n"); } } else if(!strcasecmp(stoks[0], "app_stats_period")) { if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid app_stats_period"); } appidSC->app_stats_period = strtoul(stoks[1], &endPtr, 10); if (!*stoks[1] || *endPtr) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid app_stats_period"); } } else if(!strcasecmp(stoks[0], "app_stats_rollover_size")) { if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid app_stats_rollover_size"); } appidSC->app_stats_rollover_size = strtoul(stoks[1], &endPtr, 10); if (!*stoks[1] || *endPtr) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid app_stats_rollover_size"); } } else if(!strcasecmp(stoks[0], "app_stats_rollover_time")) { if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid app_stats_rollover_time"); } appidSC->app_stats_rollover_time = strtoul(stoks[1], &endPtr, 10); if (!*stoks[1] || *endPtr) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid app_stats_rollover_time"); } } else if(!strcasecmp(stoks[0], "app_detector_dir")) { if (!ro_app_detector_dir) { if ((s_toks != 2) || !*stoks[1]) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid app_detector_dir"); } if (NULL == (appidSC->app_id_detector_path = strdup(stoks[1]))) { _dpd.fatalMsg("Appid failed to allocate detector path\n"); } } } else if(!strcasecmp(stoks[0], "instance_id")) { if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid instance id"); } appidSC->instance_id = strtoul(stoks[1], &endPtr, 10); if (!*stoks[1] || *endPtr) { _dpd.fatalMsg("Invalid instance id specified"); } } else if(!strcasecmp(stoks[0], "thirdparty_appid_dir")) { if (appidSC->appid_thirdparty_dir) { free((void *)appidSC->appid_thirdparty_dir); appidSC->appid_thirdparty_dir = NULL; } if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid ThirdpartyDirectory"); } if (!(appidSC->appid_thirdparty_dir = strdup(stoks[1]))) { _dpd.errMsg("Failed to allocate a module directory"); return; } } else if(!strcasecmp(stoks[0], "tp_config_path")) { if (appidSC->tp_config_path) { free((void *)appidSC->tp_config_path); appidSC->tp_config_path = NULL; } if (s_toks != 2) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid TP configuration"); } if (!(appidSC->tp_config_path = strdup(stoks[1]))) { _dpd.errMsg("Failed to allocate a module file"); return; } } #ifdef REG_TEST #ifdef SIDE_CHANNEL else if(!strcasecmp(stoks[0], "ss_startup_input_file")) { if ((s_toks != 2) || !*stoks[1]) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid AppId state sharing startup input file"); } if (NULL == (appidSC->appId_ss_config->startup_input_file = strdup(stoks[1]))) { _dpd.fatalMsg("Appid failed to allocate memory for state sharing startup input file\n"); } } else if(!strcasecmp(stoks[0], "ss_runtime_output_file")) { if ((s_toks != 2) || !*stoks[1]) { _dpd.fatalMsg("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), "Invalid AppId state sharing runtime output file"); } if (NULL == (appidSC->appId_ss_config->runtime_output_file = strdup(stoks[1]))) { _dpd.fatalMsg("Appid failed to allocate memory for state sharing runtime output file\n"); } } #endif else if(!strcasecmp(stoks[0], "appid_reg_test_mode")) { appidSC->appid_reg_test_mode = true; } #endif else { DynamicPreprocessorFatalMessage("%s(%d) => Unknown AppId configuration option \"%s\"\n", *(_dpd.config_file), *(_dpd.config_line), toks[i]); } _dpd.tokenFree(&stoks, s_toks); } if (!appidSC->memcap) appidSC->memcap = APP_ID_MEMCAP_DEFAULT; if (!appidSC->app_stats_period) appidSC->app_stats_period = 5*60; if (!appidSC->app_stats_rollover_size) appidSC->app_stats_rollover_size = 20 * 1024 * 1024; if (!appidSC->app_stats_rollover_time) appidSC->app_stats_rollover_time = 24*60*60; if (!appidSC->app_id_detector_path) { if (NULL == (appidSC->app_id_detector_path = strdup(DEFAULT_APPID_DETECTOR_PATH))) { _dpd.fatalMsg("Appid failed to allocate detector path\n"); } } _dpd.tokenFree(&toks, num_toks); appIdConfigDump(appidSC); } void AppIdAddGenericConfigItem(tAppIdConfig *pConfig, const char *name, void *pData) { tAppidGenericConfigItem *pConfigItem; if (!(pConfigItem = (tAppidGenericConfigItem*)_dpd.snortAlloc(1, sizeof(*pConfigItem), PP_APP_ID, PP_MEM_CATEGORY_CONFIG)) || !(pConfigItem->name = strdup(name))) { if (pConfigItem) _dpd.snortFree(pConfigItem, sizeof(*pConfigItem), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); _dpd.errMsg("Failed to allocate a config item."); return; } pConfigItem->pData = pData; sflist_add_tail(&pConfig->genericConfigList, pConfigItem); } void *AppIdFindGenericConfigItem(const tAppIdConfig *pConfig, const char *name) { tAppidGenericConfigItem *pConfigItem; // Search a module's configuration by its name for (pConfigItem = (tAppidGenericConfigItem *) sflist_first((SF_LIST*)&pConfig->genericConfigList); pConfigItem; pConfigItem = (tAppidGenericConfigItem *) sflist_next((SF_LIST*)&pConfig->genericConfigList)) { if (strcmp(pConfigItem->name, name) == 0) { return pConfigItem->pData; } } return NULL; } void AppIdRemoveGenericConfigItem(tAppIdConfig *pConfig, const char *name) { SF_LNODE *pNode; // Search a module's configuration by its name for (pNode = sflist_first_node(&pConfig->genericConfigList); pNode; pNode = sflist_next_node(&pConfig->genericConfigList)) { tAppidGenericConfigItem *pConfigItem = SFLIST_NODE_TO_DATA(pNode); if (strcmp(pConfigItem->name, name) == 0) { free(pConfigItem->name); _dpd.snortFree(pConfigItem, sizeof(*pConfigItem), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); sflist_remove_node(&pConfig->genericConfigList, pNode); break; } } } snort-2.9.20/src/dynamic-preprocessors/appid/Makefile.in0000644000175000017500000021737514242725546021407 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/dynamic-preprocessors/appid ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) am__DEPENDENCIES_1 = @SO_WITH_STATIC_LIB_FALSE@libsf_appid_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_FALSE@ $(am__DEPENDENCIES_1) @SO_WITH_STATIC_LIB_TRUE@libsf_appid_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_utils.la \ @SO_WITH_STATIC_LIB_TRUE@ $(am__DEPENDENCIES_1) am__objects_1 = libsf_appid_preproc_la-commonAppMatcher.lo \ libsf_appid_preproc_la-flow.lo \ libsf_appid_preproc_la-fw_appid.lo \ libsf_appid_preproc_la-hostPortAppCache.lo \ libsf_appid_preproc_la-luaDetectorApi.lo \ libsf_appid_preproc_la-luaDetectorFlowApi.lo \ libsf_appid_preproc_la-luaDetectorModule.lo \ libsf_appid_preproc_la-service_state.lo \ libsf_appid_preproc_la-spp_appid.lo \ libsf_appid_preproc_la-appId.lo \ libsf_appid_preproc_la-appId_ss.lo \ libsf_appid_preproc_la-appIdApi.lo \ libsf_appid_preproc_la-appInfoTable.lo \ libsf_appid_preproc_la-appIdStats.lo \ libsf_appid_preproc_la-appIdConfig.lo \ libsf_appid_preproc_la-app_forecast.lo \ libsf_appid_preproc_la-lengthAppCache.lo \ libsf_appid_preproc_la-thirdparty_appid_utils.lo \ libsf_appid_preproc_la-client_app_base.lo \ libsf_appid_preproc_la-client_app_ym.lo \ libsf_appid_preproc_la-client_app_msn.lo \ libsf_appid_preproc_la-client_app_aim.lo \ libsf_appid_preproc_la-client_app_bit.lo \ libsf_appid_preproc_la-client_app_bit_tracker.lo \ libsf_appid_preproc_la-client_app_rtp.lo \ libsf_appid_preproc_la-client_app_ssh.lo \ libsf_appid_preproc_la-client_app_timbuktu.lo \ libsf_appid_preproc_la-client_app_tns.lo \ libsf_appid_preproc_la-client_app_vnc.lo \ libsf_appid_preproc_la-service_base.lo \ libsf_appid_preproc_la-dcerpc.lo \ libsf_appid_preproc_la-service_mysql.lo \ libsf_appid_preproc_la-service_radius.lo \ libsf_appid_preproc_la-service_telnet.lo \ libsf_appid_preproc_la-service_bootp.lo \ libsf_appid_preproc_la-service_ntp.lo \ libsf_appid_preproc_la-service_flap.lo \ libsf_appid_preproc_la-service_rshell.lo \ libsf_appid_preproc_la-service_netbios.lo \ libsf_appid_preproc_la-service_snmp.lo \ libsf_appid_preproc_la-service_battle_field.lo \ libsf_appid_preproc_la-service_rexec.lo \ libsf_appid_preproc_la-service_rfb.lo \ libsf_appid_preproc_la-service_rpc.lo \ libsf_appid_preproc_la-service_bgp.lo \ libsf_appid_preproc_la-service_direct_connect.lo \ libsf_appid_preproc_la-service_irc.lo \ libsf_appid_preproc_la-service_ssh.lo \ libsf_appid_preproc_la-service_tftp.lo \ libsf_appid_preproc_la-service_ftp.lo \ libsf_appid_preproc_la-service_rsync.lo \ libsf_appid_preproc_la-service_rlogin.lo \ libsf_appid_preproc_la-service_lpr.lo \ libsf_appid_preproc_la-service_nntp.lo \ libsf_appid_preproc_la-service_MDNS.lo \ libsf_appid_preproc_la-service_dcerpc.lo \ libsf_appid_preproc_la-service_ssl.lo \ libsf_appid_preproc_la-service_bit.lo \ libsf_appid_preproc_la-service_timbuktu.lo \ libsf_appid_preproc_la-service_tns.lo \ libsf_appid_preproc_la-service_rtmp.lo \ libsf_appid_preproc_la-detector_base.lo \ libsf_appid_preproc_la-http_url_patterns.lo \ libsf_appid_preproc_la-detector_http.lo \ libsf_appid_preproc_la-detector_imap.lo \ libsf_appid_preproc_la-detector_kerberos.lo \ libsf_appid_preproc_la-detector_pop3.lo \ libsf_appid_preproc_la-detector_smtp.lo \ libsf_appid_preproc_la-detector_sip.lo \ libsf_appid_preproc_la-detector_pattern.lo \ libsf_appid_preproc_la-detector_dns.lo \ libsf_appid_preproc_la-detector_cip.lo \ libsf_appid_preproc_la-common_util.lo \ libsf_appid_preproc_la-sf_multi_mpse.lo \ libsf_appid_preproc_la-sf_mlmp.lo \ libsf_appid_preproc_la-fw_avltree.lo \ libsf_appid_preproc_la-OutputFile.lo \ libsf_appid_preproc_la-NetworkSet.lo \ libsf_appid_preproc_la-ip_funcs.lo \ libsf_appid_preproc_la-sfutil.lo am_libsf_appid_preproc_la_OBJECTS = $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_appid_preproc_la_OBJECTS = libsf_appid_preproc_la-sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sf_ip.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sfPolicyUserData.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sfxhash.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sfghash.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sflsq.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sfhashfcn.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sfmemcap.lo \ @SO_WITH_STATIC_LIB_FALSE@ libsf_appid_preproc_la-sfprimetable.lo libsf_appid_preproc_la_OBJECTS = $(am_libsf_appid_preproc_la_OBJECTS) \ $(nodist_libsf_appid_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_appid_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) \ $(libsf_appid_preproc_la_LDFLAGS) $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_appid_preproc_la_SOURCES) \ $(nodist_libsf_appid_preproc_la_SOURCES) DIST_SOURCES = $(libsf_appid_preproc_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile_defs DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I${top_builddir}/src/dynamic-preprocessors/include \ -I$(APPID_SRC_DIR)/../libs \ -I$(APPID_SRC_DIR)/util \ -I$(APPID_SRC_DIR)/service_plugins \ -I$(APPID_SRC_DIR)/client_plugins \ -I$(APPID_SRC_DIR)/detector_plugins INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies APPID_SRC_DIR = ${top_srcdir}/src/dynamic-preprocessors/appid APPID_SOURCES = $(APPID_SRC_DIR)/commonAppMatcher.c \ $(APPID_SRC_DIR)/flow.c $(APPID_SRC_DIR)/fw_appid.c \ $(APPID_SRC_DIR)/hostPortAppCache.c \ $(APPID_SRC_DIR)/luaDetectorApi.c \ $(APPID_SRC_DIR)/luaDetectorApi.h \ $(APPID_SRC_DIR)/luaDetectorFlowApi.c \ $(APPID_SRC_DIR)/luaDetectorModule.c \ $(APPID_SRC_DIR)/luaDetectorModule.h \ $(APPID_SRC_DIR)/service_state.c $(APPID_SRC_DIR)/spp_appid.c \ $(APPID_SRC_DIR)/appId.c $(APPID_SRC_DIR)/appId.h \ $(APPID_SRC_DIR)/appId_ss.h $(APPID_SRC_DIR)/appId_ss.c \ $(APPID_SRC_DIR)/appIdApi.c \ $(APPID_SRC_DIR)/commonAppMatcher.h $(APPID_SRC_DIR)/flow.h \ $(APPID_SRC_DIR)/hostPortAppCache.h \ $(APPID_SRC_DIR)/httpCommon.h \ $(APPID_SRC_DIR)/luaDetectorFlowApi.h \ $(APPID_SRC_DIR)/service_state.h $(APPID_SRC_DIR)/spp_appid.h \ $(APPID_SRC_DIR)/attribute.h $(APPID_SRC_DIR)/flow_error.h \ $(APPID_SRC_DIR)/fw_appid.h $(APPID_SRC_DIR)/appInfoTable.c \ $(APPID_SRC_DIR)/appInfoTable.h $(APPID_SRC_DIR)/appIdStats.c \ $(APPID_SRC_DIR)/appIdStats.h $(APPID_SRC_DIR)/appIdConfig.c \ $(APPID_SRC_DIR)/appIdConfig.h $(APPID_SRC_DIR)/app_forecast.c \ $(APPID_SRC_DIR)/app_forecast.h \ $(APPID_SRC_DIR)/lengthAppCache.c \ $(APPID_SRC_DIR)/lengthAppCache.h \ $(APPID_SRC_DIR)/thirdparty_appid_api.h \ $(APPID_SRC_DIR)/thirdparty_appid_types.h \ $(APPID_SRC_DIR)/thirdparty_appid_utils.c \ $(APPID_SRC_DIR)/thirdparty_appid_utils.h \ $(APPID_SRC_DIR)/dns_defs.h \ $(APPID_SRC_DIR)/client_plugins/client_app_base.c \ $(APPID_SRC_DIR)/client_plugins/client_app_ym.c \ $(APPID_SRC_DIR)/client_plugins/client_app_msn.c \ $(APPID_SRC_DIR)/client_plugins/client_app_aim.c \ $(APPID_SRC_DIR)/client_plugins/client_app_msn.h \ $(APPID_SRC_DIR)/client_plugins/client_app_aim.h \ $(APPID_SRC_DIR)/client_plugins/client_app_ym.h \ $(APPID_SRC_DIR)/client_plugins/client_app_api.h \ $(APPID_SRC_DIR)/client_plugins/client_app_base.h \ $(APPID_SRC_DIR)/client_plugins/client_app_bit.c \ $(APPID_SRC_DIR)/client_plugins/client_app_bit_tracker.c \ $(APPID_SRC_DIR)/client_plugins/client_app_rtp.c \ $(APPID_SRC_DIR)/client_plugins/client_app_ssh.c \ $(APPID_SRC_DIR)/client_plugins/client_app_timbuktu.c \ $(APPID_SRC_DIR)/client_plugins/client_app_tns.c \ $(APPID_SRC_DIR)/client_plugins/client_app_vnc.c \ $(APPID_SRC_DIR)/client_plugins/clientAppConfig.h \ $(APPID_SRC_DIR)/client_plugins/client_app_rtp.h \ $(APPID_SRC_DIR)/service_plugins/service_util.h \ $(APPID_SRC_DIR)/service_plugins/service_base.h \ $(APPID_SRC_DIR)/service_plugins/service_base.c \ $(APPID_SRC_DIR)/service_plugins/service_api.h \ $(APPID_SRC_DIR)/service_plugins/dcerpc.h \ $(APPID_SRC_DIR)/service_plugins/dcerpc.c \ $(APPID_SRC_DIR)/service_plugins/service_mysql.c \ $(APPID_SRC_DIR)/service_plugins/service_ssh.h \ $(APPID_SRC_DIR)/service_plugins/service_radius.c \ $(APPID_SRC_DIR)/service_plugins/service_telnet.c \ $(APPID_SRC_DIR)/service_plugins/service_rexec.h \ $(APPID_SRC_DIR)/service_plugins/service_nntp.h \ $(APPID_SRC_DIR)/service_plugins/service_bootp.c \ $(APPID_SRC_DIR)/service_plugins/service_ntp.c \ $(APPID_SRC_DIR)/service_plugins/service_rsync.h \ $(APPID_SRC_DIR)/service_plugins/service_flap.c \ $(APPID_SRC_DIR)/service_plugins/service_battle_field.h \ $(APPID_SRC_DIR)/service_plugins/service_rshell.c \ $(APPID_SRC_DIR)/service_plugins/service_netbios.c \ $(APPID_SRC_DIR)/service_plugins/service_ftp.h \ $(APPID_SRC_DIR)/service_plugins/service_snmp.c \ $(APPID_SRC_DIR)/service_plugins/service_radius.h \ $(APPID_SRC_DIR)/service_plugins/service_ntp.h \ $(APPID_SRC_DIR)/service_plugins/service_battle_field.c \ $(APPID_SRC_DIR)/service_plugins/service_rshell.h \ $(APPID_SRC_DIR)/service_plugins/service_direct_connect.h \ $(APPID_SRC_DIR)/service_plugins/service_bootp.h \ $(APPID_SRC_DIR)/service_plugins/service_mysql.h \ $(APPID_SRC_DIR)/service_plugins/service_rexec.c \ $(APPID_SRC_DIR)/service_plugins/service_rfb.h \ $(APPID_SRC_DIR)/service_plugins/service_rfb.c \ $(APPID_SRC_DIR)/service_plugins/service_dcerpc.h \ $(APPID_SRC_DIR)/service_plugins/service_lpr.h \ $(APPID_SRC_DIR)/service_plugins/service_ssl.h \ $(APPID_SRC_DIR)/service_plugins/service_MDNS.h \ $(APPID_SRC_DIR)/service_plugins/service_rpc.c \ $(APPID_SRC_DIR)/service_plugins/service_flap.h \ $(APPID_SRC_DIR)/service_plugins/service_telnet.h \ $(APPID_SRC_DIR)/service_plugins/service_bgp.c \ $(APPID_SRC_DIR)/service_plugins/service_direct_connect.c \ $(APPID_SRC_DIR)/service_plugins/service_irc.c \ $(APPID_SRC_DIR)/service_plugins/service_rlogin.h \ $(APPID_SRC_DIR)/service_plugins/service_rpc.h \ $(APPID_SRC_DIR)/service_plugins/service_ssh.c \ $(APPID_SRC_DIR)/service_plugins/service_tftp.c \ $(APPID_SRC_DIR)/service_plugins/service_ftp.c \ $(APPID_SRC_DIR)/service_plugins/service_rsync.c \ $(APPID_SRC_DIR)/service_plugins/service_rlogin.c \ $(APPID_SRC_DIR)/service_plugins/service_tftp.h \ $(APPID_SRC_DIR)/service_plugins/service_netbios.h \ $(APPID_SRC_DIR)/service_plugins/service_lpr.c \ $(APPID_SRC_DIR)/service_plugins/service_nntp.c \ $(APPID_SRC_DIR)/service_plugins/service_snmp.h \ $(APPID_SRC_DIR)/service_plugins/service_bgp.h \ $(APPID_SRC_DIR)/service_plugins/service_MDNS.c \ $(APPID_SRC_DIR)/service_plugins/service_irc.h \ $(APPID_SRC_DIR)/service_plugins/service_dcerpc.c \ $(APPID_SRC_DIR)/service_plugins/service_ssl.c \ $(APPID_SRC_DIR)/service_plugins/service_bit.c \ $(APPID_SRC_DIR)/service_plugins/service_timbuktu.c \ $(APPID_SRC_DIR)/service_plugins/service_tns.c \ $(APPID_SRC_DIR)/service_plugins/service_rtmp.h \ $(APPID_SRC_DIR)/service_plugins/service_rtmp.c \ $(APPID_SRC_DIR)/service_plugins/serviceConfig.h \ $(APPID_SRC_DIR)/detector_plugins/detector_api.h \ $(APPID_SRC_DIR)/detector_plugins/detector_base.h \ $(APPID_SRC_DIR)/detector_plugins/detector_base.c \ $(APPID_SRC_DIR)/detector_plugins/http_url_patterns.c \ $(APPID_SRC_DIR)/detector_plugins/detector_http.c \ $(APPID_SRC_DIR)/detector_plugins/detector_http.h \ $(APPID_SRC_DIR)/detector_plugins/http_url_patterns.h \ $(APPID_SRC_DIR)/detector_plugins/detector_imap.c \ $(APPID_SRC_DIR)/detector_plugins/detector_kerberos.c \ $(APPID_SRC_DIR)/detector_plugins/detector_pop3.c \ $(APPID_SRC_DIR)/detector_plugins/detector_smtp.c \ $(APPID_SRC_DIR)/detector_plugins/detector_sip.c \ $(APPID_SRC_DIR)/detector_plugins/detector_sip.h \ $(APPID_SRC_DIR)/detector_plugins/detector_pattern.c \ $(APPID_SRC_DIR)/detector_plugins/detector_pattern.h \ $(APPID_SRC_DIR)/detector_plugins/detector_dns.c \ $(APPID_SRC_DIR)/detector_plugins/detector_dns.h \ $(APPID_SRC_DIR)/detector_plugins/detector_cip.c \ $(APPID_SRC_DIR)/detector_plugins/detector_cip.h \ $(APPID_SRC_DIR)/util/common_util.h \ $(APPID_SRC_DIR)/util/common_util.c \ $(APPID_SRC_DIR)/util/sf_multi_mpse.c \ $(APPID_SRC_DIR)/util/sf_mlmp.h \ $(APPID_SRC_DIR)/util/sf_multi_mpse.h \ $(APPID_SRC_DIR)/util/sf_mlmp.c \ $(APPID_SRC_DIR)/util/fw_avltree.c \ $(APPID_SRC_DIR)/util/fw_avltree.h \ $(APPID_SRC_DIR)/util/OutputFile.c \ $(APPID_SRC_DIR)/util/OutputFile.h \ $(APPID_SRC_DIR)/util/NetworkSet.c \ $(APPID_SRC_DIR)/util/NetworkSet.h \ $(APPID_SRC_DIR)/util/ip_funcs.c \ $(APPID_SRC_DIR)/util/ip_funcs.h \ $(APPID_SRC_DIR)/util/sfutil.c $(APPID_SRC_DIR)/util/sfutil.h dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_appid_preproc.la libsf_appid_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_FALSE@libsf_appid_preproc_la_LIBADD = $(LUA_LIBS) @SO_WITH_STATIC_LIB_TRUE@libsf_appid_preproc_la_LIBADD = ../libsf_dynamic_preproc.la ../libsf_dynamic_utils.la $(LUA_LIBS) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_appid_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_ip.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfxhash.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfghash.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sflsq.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfhashfcn.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfmemcap.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfprimetable.c libsf_appid_preproc_la_CFLAGS = -DDYNAMIC_PREPROC_CONTEXT -DSTATIC=static -DINLINE=inline $(LUA_CFLAGS) libsf_appid_preproc_la_SOURCES = $(APPID_SOURCES) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(srcdir)/Makefile_defs $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/appid/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/appid/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(srcdir)/Makefile_defs $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_appid_preproc.la: $(libsf_appid_preproc_la_OBJECTS) $(libsf_appid_preproc_la_DEPENDENCIES) $(EXTRA_libsf_appid_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_appid_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_appid_preproc_la_OBJECTS) $(libsf_appid_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< libsf_appid_preproc_la-commonAppMatcher.lo: $(APPID_SRC_DIR)/commonAppMatcher.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-commonAppMatcher.lo `test -f '$(APPID_SRC_DIR)/commonAppMatcher.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/commonAppMatcher.c libsf_appid_preproc_la-flow.lo: $(APPID_SRC_DIR)/flow.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-flow.lo `test -f '$(APPID_SRC_DIR)/flow.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/flow.c libsf_appid_preproc_la-fw_appid.lo: $(APPID_SRC_DIR)/fw_appid.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-fw_appid.lo `test -f '$(APPID_SRC_DIR)/fw_appid.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/fw_appid.c libsf_appid_preproc_la-hostPortAppCache.lo: $(APPID_SRC_DIR)/hostPortAppCache.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-hostPortAppCache.lo `test -f '$(APPID_SRC_DIR)/hostPortAppCache.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/hostPortAppCache.c libsf_appid_preproc_la-luaDetectorApi.lo: $(APPID_SRC_DIR)/luaDetectorApi.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-luaDetectorApi.lo `test -f '$(APPID_SRC_DIR)/luaDetectorApi.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/luaDetectorApi.c libsf_appid_preproc_la-luaDetectorFlowApi.lo: $(APPID_SRC_DIR)/luaDetectorFlowApi.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-luaDetectorFlowApi.lo `test -f '$(APPID_SRC_DIR)/luaDetectorFlowApi.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/luaDetectorFlowApi.c libsf_appid_preproc_la-luaDetectorModule.lo: $(APPID_SRC_DIR)/luaDetectorModule.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-luaDetectorModule.lo `test -f '$(APPID_SRC_DIR)/luaDetectorModule.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/luaDetectorModule.c libsf_appid_preproc_la-service_state.lo: $(APPID_SRC_DIR)/service_state.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_state.lo `test -f '$(APPID_SRC_DIR)/service_state.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_state.c libsf_appid_preproc_la-spp_appid.lo: $(APPID_SRC_DIR)/spp_appid.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-spp_appid.lo `test -f '$(APPID_SRC_DIR)/spp_appid.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/spp_appid.c libsf_appid_preproc_la-appId.lo: $(APPID_SRC_DIR)/appId.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-appId.lo `test -f '$(APPID_SRC_DIR)/appId.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/appId.c libsf_appid_preproc_la-appId_ss.lo: $(APPID_SRC_DIR)/appId_ss.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-appId_ss.lo `test -f '$(APPID_SRC_DIR)/appId_ss.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/appId_ss.c libsf_appid_preproc_la-appIdApi.lo: $(APPID_SRC_DIR)/appIdApi.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-appIdApi.lo `test -f '$(APPID_SRC_DIR)/appIdApi.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/appIdApi.c libsf_appid_preproc_la-appInfoTable.lo: $(APPID_SRC_DIR)/appInfoTable.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-appInfoTable.lo `test -f '$(APPID_SRC_DIR)/appInfoTable.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/appInfoTable.c libsf_appid_preproc_la-appIdStats.lo: $(APPID_SRC_DIR)/appIdStats.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-appIdStats.lo `test -f '$(APPID_SRC_DIR)/appIdStats.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/appIdStats.c libsf_appid_preproc_la-appIdConfig.lo: $(APPID_SRC_DIR)/appIdConfig.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-appIdConfig.lo `test -f '$(APPID_SRC_DIR)/appIdConfig.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/appIdConfig.c libsf_appid_preproc_la-app_forecast.lo: $(APPID_SRC_DIR)/app_forecast.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-app_forecast.lo `test -f '$(APPID_SRC_DIR)/app_forecast.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/app_forecast.c libsf_appid_preproc_la-lengthAppCache.lo: $(APPID_SRC_DIR)/lengthAppCache.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-lengthAppCache.lo `test -f '$(APPID_SRC_DIR)/lengthAppCache.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/lengthAppCache.c libsf_appid_preproc_la-thirdparty_appid_utils.lo: $(APPID_SRC_DIR)/thirdparty_appid_utils.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-thirdparty_appid_utils.lo `test -f '$(APPID_SRC_DIR)/thirdparty_appid_utils.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/thirdparty_appid_utils.c libsf_appid_preproc_la-client_app_base.lo: $(APPID_SRC_DIR)/client_plugins/client_app_base.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_base.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_base.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_base.c libsf_appid_preproc_la-client_app_ym.lo: $(APPID_SRC_DIR)/client_plugins/client_app_ym.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_ym.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_ym.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_ym.c libsf_appid_preproc_la-client_app_msn.lo: $(APPID_SRC_DIR)/client_plugins/client_app_msn.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_msn.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_msn.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_msn.c libsf_appid_preproc_la-client_app_aim.lo: $(APPID_SRC_DIR)/client_plugins/client_app_aim.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_aim.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_aim.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_aim.c libsf_appid_preproc_la-client_app_bit.lo: $(APPID_SRC_DIR)/client_plugins/client_app_bit.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_bit.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_bit.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_bit.c libsf_appid_preproc_la-client_app_bit_tracker.lo: $(APPID_SRC_DIR)/client_plugins/client_app_bit_tracker.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_bit_tracker.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_bit_tracker.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_bit_tracker.c libsf_appid_preproc_la-client_app_rtp.lo: $(APPID_SRC_DIR)/client_plugins/client_app_rtp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_rtp.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_rtp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_rtp.c libsf_appid_preproc_la-client_app_ssh.lo: $(APPID_SRC_DIR)/client_plugins/client_app_ssh.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_ssh.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_ssh.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_ssh.c libsf_appid_preproc_la-client_app_timbuktu.lo: $(APPID_SRC_DIR)/client_plugins/client_app_timbuktu.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_timbuktu.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_timbuktu.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_timbuktu.c libsf_appid_preproc_la-client_app_tns.lo: $(APPID_SRC_DIR)/client_plugins/client_app_tns.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_tns.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_tns.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_tns.c libsf_appid_preproc_la-client_app_vnc.lo: $(APPID_SRC_DIR)/client_plugins/client_app_vnc.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-client_app_vnc.lo `test -f '$(APPID_SRC_DIR)/client_plugins/client_app_vnc.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/client_plugins/client_app_vnc.c libsf_appid_preproc_la-service_base.lo: $(APPID_SRC_DIR)/service_plugins/service_base.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_base.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_base.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_base.c libsf_appid_preproc_la-dcerpc.lo: $(APPID_SRC_DIR)/service_plugins/dcerpc.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-dcerpc.lo `test -f '$(APPID_SRC_DIR)/service_plugins/dcerpc.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/dcerpc.c libsf_appid_preproc_la-service_mysql.lo: $(APPID_SRC_DIR)/service_plugins/service_mysql.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_mysql.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_mysql.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_mysql.c libsf_appid_preproc_la-service_radius.lo: $(APPID_SRC_DIR)/service_plugins/service_radius.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_radius.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_radius.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_radius.c libsf_appid_preproc_la-service_telnet.lo: $(APPID_SRC_DIR)/service_plugins/service_telnet.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_telnet.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_telnet.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_telnet.c libsf_appid_preproc_la-service_bootp.lo: $(APPID_SRC_DIR)/service_plugins/service_bootp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_bootp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_bootp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_bootp.c libsf_appid_preproc_la-service_ntp.lo: $(APPID_SRC_DIR)/service_plugins/service_ntp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_ntp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_ntp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_ntp.c libsf_appid_preproc_la-service_flap.lo: $(APPID_SRC_DIR)/service_plugins/service_flap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_flap.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_flap.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_flap.c libsf_appid_preproc_la-service_rshell.lo: $(APPID_SRC_DIR)/service_plugins/service_rshell.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_rshell.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_rshell.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_rshell.c libsf_appid_preproc_la-service_netbios.lo: $(APPID_SRC_DIR)/service_plugins/service_netbios.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_netbios.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_netbios.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_netbios.c libsf_appid_preproc_la-service_snmp.lo: $(APPID_SRC_DIR)/service_plugins/service_snmp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_snmp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_snmp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_snmp.c libsf_appid_preproc_la-service_battle_field.lo: $(APPID_SRC_DIR)/service_plugins/service_battle_field.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_battle_field.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_battle_field.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_battle_field.c libsf_appid_preproc_la-service_rexec.lo: $(APPID_SRC_DIR)/service_plugins/service_rexec.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_rexec.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_rexec.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_rexec.c libsf_appid_preproc_la-service_rfb.lo: $(APPID_SRC_DIR)/service_plugins/service_rfb.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_rfb.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_rfb.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_rfb.c libsf_appid_preproc_la-service_rpc.lo: $(APPID_SRC_DIR)/service_plugins/service_rpc.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_rpc.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_rpc.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_rpc.c libsf_appid_preproc_la-service_bgp.lo: $(APPID_SRC_DIR)/service_plugins/service_bgp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_bgp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_bgp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_bgp.c libsf_appid_preproc_la-service_direct_connect.lo: $(APPID_SRC_DIR)/service_plugins/service_direct_connect.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_direct_connect.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_direct_connect.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_direct_connect.c libsf_appid_preproc_la-service_irc.lo: $(APPID_SRC_DIR)/service_plugins/service_irc.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_irc.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_irc.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_irc.c libsf_appid_preproc_la-service_ssh.lo: $(APPID_SRC_DIR)/service_plugins/service_ssh.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_ssh.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_ssh.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_ssh.c libsf_appid_preproc_la-service_tftp.lo: $(APPID_SRC_DIR)/service_plugins/service_tftp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_tftp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_tftp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_tftp.c libsf_appid_preproc_la-service_ftp.lo: $(APPID_SRC_DIR)/service_plugins/service_ftp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_ftp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_ftp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_ftp.c libsf_appid_preproc_la-service_rsync.lo: $(APPID_SRC_DIR)/service_plugins/service_rsync.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_rsync.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_rsync.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_rsync.c libsf_appid_preproc_la-service_rlogin.lo: $(APPID_SRC_DIR)/service_plugins/service_rlogin.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_rlogin.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_rlogin.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_rlogin.c libsf_appid_preproc_la-service_lpr.lo: $(APPID_SRC_DIR)/service_plugins/service_lpr.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_lpr.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_lpr.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_lpr.c libsf_appid_preproc_la-service_nntp.lo: $(APPID_SRC_DIR)/service_plugins/service_nntp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_nntp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_nntp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_nntp.c libsf_appid_preproc_la-service_MDNS.lo: $(APPID_SRC_DIR)/service_plugins/service_MDNS.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_MDNS.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_MDNS.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_MDNS.c libsf_appid_preproc_la-service_dcerpc.lo: $(APPID_SRC_DIR)/service_plugins/service_dcerpc.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_dcerpc.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_dcerpc.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_dcerpc.c libsf_appid_preproc_la-service_ssl.lo: $(APPID_SRC_DIR)/service_plugins/service_ssl.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_ssl.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_ssl.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_ssl.c libsf_appid_preproc_la-service_bit.lo: $(APPID_SRC_DIR)/service_plugins/service_bit.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_bit.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_bit.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_bit.c libsf_appid_preproc_la-service_timbuktu.lo: $(APPID_SRC_DIR)/service_plugins/service_timbuktu.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_timbuktu.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_timbuktu.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_timbuktu.c libsf_appid_preproc_la-service_tns.lo: $(APPID_SRC_DIR)/service_plugins/service_tns.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_tns.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_tns.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_tns.c libsf_appid_preproc_la-service_rtmp.lo: $(APPID_SRC_DIR)/service_plugins/service_rtmp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-service_rtmp.lo `test -f '$(APPID_SRC_DIR)/service_plugins/service_rtmp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/service_plugins/service_rtmp.c libsf_appid_preproc_la-detector_base.lo: $(APPID_SRC_DIR)/detector_plugins/detector_base.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_base.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_base.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_base.c libsf_appid_preproc_la-http_url_patterns.lo: $(APPID_SRC_DIR)/detector_plugins/http_url_patterns.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-http_url_patterns.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/http_url_patterns.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/http_url_patterns.c libsf_appid_preproc_la-detector_http.lo: $(APPID_SRC_DIR)/detector_plugins/detector_http.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_http.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_http.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_http.c libsf_appid_preproc_la-detector_imap.lo: $(APPID_SRC_DIR)/detector_plugins/detector_imap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_imap.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_imap.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_imap.c libsf_appid_preproc_la-detector_kerberos.lo: $(APPID_SRC_DIR)/detector_plugins/detector_kerberos.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_kerberos.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_kerberos.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_kerberos.c libsf_appid_preproc_la-detector_pop3.lo: $(APPID_SRC_DIR)/detector_plugins/detector_pop3.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_pop3.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_pop3.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_pop3.c libsf_appid_preproc_la-detector_smtp.lo: $(APPID_SRC_DIR)/detector_plugins/detector_smtp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_smtp.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_smtp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_smtp.c libsf_appid_preproc_la-detector_sip.lo: $(APPID_SRC_DIR)/detector_plugins/detector_sip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_sip.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_sip.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_sip.c libsf_appid_preproc_la-detector_pattern.lo: $(APPID_SRC_DIR)/detector_plugins/detector_pattern.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_pattern.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_pattern.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_pattern.c libsf_appid_preproc_la-detector_dns.lo: $(APPID_SRC_DIR)/detector_plugins/detector_dns.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_dns.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_dns.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_dns.c libsf_appid_preproc_la-detector_cip.lo: $(APPID_SRC_DIR)/detector_plugins/detector_cip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-detector_cip.lo `test -f '$(APPID_SRC_DIR)/detector_plugins/detector_cip.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/detector_plugins/detector_cip.c libsf_appid_preproc_la-common_util.lo: $(APPID_SRC_DIR)/util/common_util.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-common_util.lo `test -f '$(APPID_SRC_DIR)/util/common_util.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/common_util.c libsf_appid_preproc_la-sf_multi_mpse.lo: $(APPID_SRC_DIR)/util/sf_multi_mpse.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sf_multi_mpse.lo `test -f '$(APPID_SRC_DIR)/util/sf_multi_mpse.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/sf_multi_mpse.c libsf_appid_preproc_la-sf_mlmp.lo: $(APPID_SRC_DIR)/util/sf_mlmp.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sf_mlmp.lo `test -f '$(APPID_SRC_DIR)/util/sf_mlmp.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/sf_mlmp.c libsf_appid_preproc_la-fw_avltree.lo: $(APPID_SRC_DIR)/util/fw_avltree.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-fw_avltree.lo `test -f '$(APPID_SRC_DIR)/util/fw_avltree.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/fw_avltree.c libsf_appid_preproc_la-OutputFile.lo: $(APPID_SRC_DIR)/util/OutputFile.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-OutputFile.lo `test -f '$(APPID_SRC_DIR)/util/OutputFile.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/OutputFile.c libsf_appid_preproc_la-NetworkSet.lo: $(APPID_SRC_DIR)/util/NetworkSet.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-NetworkSet.lo `test -f '$(APPID_SRC_DIR)/util/NetworkSet.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/NetworkSet.c libsf_appid_preproc_la-ip_funcs.lo: $(APPID_SRC_DIR)/util/ip_funcs.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-ip_funcs.lo `test -f '$(APPID_SRC_DIR)/util/ip_funcs.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/ip_funcs.c libsf_appid_preproc_la-sfutil.lo: $(APPID_SRC_DIR)/util/sfutil.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sfutil.lo `test -f '$(APPID_SRC_DIR)/util/sfutil.c' || echo '$(srcdir)/'`$(APPID_SRC_DIR)/util/sfutil.c libsf_appid_preproc_la-sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c libsf_appid_preproc_la-sf_ip.lo: ../include/sf_ip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sf_ip.lo `test -f '../include/sf_ip.c' || echo '$(srcdir)/'`../include/sf_ip.c libsf_appid_preproc_la-sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c libsf_appid_preproc_la-sfxhash.lo: ../include/sfxhash.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sfxhash.lo `test -f '../include/sfxhash.c' || echo '$(srcdir)/'`../include/sfxhash.c libsf_appid_preproc_la-sfghash.lo: ../include/sfghash.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sfghash.lo `test -f '../include/sfghash.c' || echo '$(srcdir)/'`../include/sfghash.c libsf_appid_preproc_la-sflsq.lo: ../include/sflsq.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sflsq.lo `test -f '../include/sflsq.c' || echo '$(srcdir)/'`../include/sflsq.c libsf_appid_preproc_la-sfhashfcn.lo: ../include/sfhashfcn.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sfhashfcn.lo `test -f '../include/sfhashfcn.c' || echo '$(srcdir)/'`../include/sfhashfcn.c libsf_appid_preproc_la-sfmemcap.lo: ../include/sfmemcap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sfmemcap.lo `test -f '../include/sfmemcap.c' || echo '$(srcdir)/'`../include/sfmemcap.c libsf_appid_preproc_la-sfprimetable.lo: ../include/sfprimetable.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_appid_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_appid_preproc_la-sfprimetable.lo `test -f '../include/sfprimetable.c' || echo '$(srcdir)/'`../include/sfprimetable.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/appid/appIdStats.h0000644000175000017500000000235014241075347021544 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _FW_STATS2_H_ #define _FW_STATS2_H_ #include #include #include void appIdStatsUpdate(tAppIdData* session); void appIdStatsInit(char* appFileName, time_t statsPeriod, size_t rolloverSize, time_t rolloverPeriod); void appIdStatsReinit(void); void appIdStatsIdleFlush(void); void appIdStatsFini(void); #endif snort-2.9.20/src/dynamic-preprocessors/appid/spp_appid.c0000644000175000017500000005166114241075573021454 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "common_util.h" #include "sf_preproc_info.h" #include "spp_appid.h" #include "fw_appid.h" #include "flow.h" #include "service_base.h" #include "luaDetectorModule.h" #include "appIdConfig.h" #include "appIdStats.h" #ifdef SIDE_CHANNEL #include "appId_ss.h" #endif #include "appInfoTable.h" #include "thirdparty_appid_utils.h" #include "cip_common.h" #include "detector_cip.h" #include "memory_stats.h" #ifdef PERF_PROFILING PreprocStats appMatchPerfStats; #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 5; static uint16_t appid_preproc_status_bit = 0; SF_SO_PUBLIC const char *PREPROC_NAME = "appid"; static pthread_mutex_t appIdReloadMutex = PTHREAD_MUTEX_INITIALIZER; static bool appIdReloadInProgress = false; extern void appIdApiInit(struct AppIdApi*); static void AppIdProcess(SFSnortPacket *p, void *context) { PROFILE_VARS; PREPROC_PROFILE_START(appMatchPerfStats); /* Trust */ if (p->stream_session && _dpd.sessionAPI->get_ignore_direction(p->stream_session) == SSN_DIR_BOTH) { _dpd.sessionAPI->disable_preproc_for_session( p->stream_session, PP_NETWORK_DISCOVERY ); PREPROC_PROFILE_END(appMatchPerfStats); return; } fwAppIdSearch(p); PREPROC_PROFILE_END(appMatchPerfStats); } static void AppIdAddPortsToStream5Filter(struct _SnortConfig *sc, tSfPolicyId policy_id) { unsigned portNum; for (portNum = 0; portNum < 65536; portNum++) { /*Add port the port */ _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_TCP, (uint16_t)portNum, appid_preproc_status_bit, policy_id, 1); _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_UDP, (uint16_t)portNum, appid_preproc_status_bit, policy_id, 1); } } static void initializeAppIDForDispatch(struct _SnortConfig *sc) { _dpd.sessionAPI->enable_preproc_all_ports_all_policies(sc, PP_APP_ID, PROTO_BIT__IP); _dpd.addPreprocAllPolicies(sc, (void (*)(void *, void *))AppIdProcess, PRIORITY_TRANSPORT + 1, PP_APP_ID, PROTO_BIT__IP); } static int AppIDCheckConfig(struct _SnortConfig *sc) { initializeAppIDForDispatch(sc); return 0; } static void AppIdStaticConfigFree(tAppidStaticConfig* appidSC) { if (appidSC) { free((char *)(appidSC->appid_thirdparty_dir)); free(appidSC->tp_config_path); free(appidSC->app_id_detector_path); free(appidSC->conf_file); free(appidSC->app_stats_filename); #ifdef SIDE_CHANNEL if (appidSC->appId_ss_config) AppIdSSConfigFree(appidSC->appId_ss_config); #endif if (appidSC->newAppIdConfig) AppIdCommonUnload(appidSC->newAppIdConfig); _dpd.snortFree(appidSC, sizeof(*appidSC), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); } } #ifdef SNORT_RELOAD /********** AppId Reload Functions **********/ static void reloadWait(void) { const struct timespec reloadPollTime = {0, 1000000}; // 1 msec wait time to poll reload status for (;;) { pthread_mutex_lock(&appIdReloadMutex); if (!appIdReloadInProgress) { appIdReloadInProgress = true; pthread_mutex_unlock(&appIdReloadMutex); return; } pthread_mutex_unlock(&appIdReloadMutex); nanosleep(&reloadPollTime, NULL); } } static void reloadUnlock(void) { pthread_mutex_lock(&appIdReloadMutex); appIdReloadInProgress = false; pthread_mutex_unlock(&appIdReloadMutex); } STATIC bool AppIdReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { return AppIdServiceStateReloadAdjust(idle, appidStaticConfig->memcap); } static int AppIdReloadReloadVerify(struct _SnortConfig *sc, void *swap_config) { initializeAppIDForDispatch(sc); if (swap_config) { tAppidStaticConfig* newConfig = (tAppidStaticConfig*)swap_config; if (newConfig->memcap != appidStaticConfig->memcap) { _dpd.logMsg("AppId: old memcap %lu, new memcap %lu\n", appidStaticConfig->memcap, newConfig->memcap); _dpd.reloadAdjustRegister(sc, "AppID", 0, AppIdReloadAdjust, NULL, NULL); } } return 0; } /** * \brief Callback function that handles AppId reload * * This function gets called on Snort reload in a separate thread. It starts * loading AppId configuration in new_config. * * @param sc * @param args * @param new_config return parameter to hold AppId configuration * @return void */ STATIC void AppIdReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyId policy_id; reloadWait(); policy_id = _dpd.getParserPolicy(sc); if (policy_id == _dpd.getDefaultPolicy()) AppIdAddPortsToStream5Filter(sc, policy_id); if (*new_config == NULL) { tAppidStaticConfig* newConfig; if (NULL == (newConfig = (tAppidStaticConfig*)_dpd.snortAlloc(1, sizeof(*newConfig), PP_APP_ID, PP_MEM_CATEGORY_CONFIG))) { _dpd.fatalMsg("AppID failed to allocate memory for new configuration\n"); } appIdConfigParse(newConfig, args); // Start loading AppId configuration into new_config AppIdCommonReload(newConfig, (void**)&newConfig->newAppIdConfig); *new_config = (void*)newConfig; } } /** * \brief Callback function that handles configuration swap on reload * * This function gets called after AppIdReload() returns. At this point, * AppIdReload() is done with loading the configuration into swap_config * and swap_config is ready to use. * * @param sc * @param swap_config pointer to data structure containing new configuration. * This data structure was populated by AppIdReload(). * @return pointer to old configuration */ STATIC void *AppIdReloadSwap(struct _SnortConfig *sc, void *swap_config) { struct timeval startTime; struct timeval endTime; double elapsedTime; tAppidStaticConfig* tmpConfig = NULL; if (swap_config) { gettimeofday(&startTime, NULL); tmpConfig = appidStaticConfig; appidStaticConfig = (tAppidStaticConfig*)swap_config; tmpConfig->newAppIdConfig = AppIdCommonReloadSwap(appidStaticConfig->newAppIdConfig); appidStaticConfig->newAppIdConfig = NULL; ThirdPartyAppIDReconfigure(); gettimeofday(&endTime, NULL); elapsedTime = (endTime.tv_sec*1000.0) + (endTime.tv_usec/1000.0) - (startTime.tv_sec*1000.0) - (startTime.tv_usec/1000.0); _dpd.logMsg("AppId reload swap time = %.3f msec\n", elapsedTime); } // Return old configuration data structure return (void*)tmpConfig; } /** * \brief Callback function that handles freeing of old configuration after * configuration is swapped on a reload * * This function gets called after AppIdReloadSwap() is done. It frees the data * structure that contains the old configuration. * * @param old_context pointer to old configuration * @return void */ STATIC void AppIdReloadFree(void *old_context) { AppIdStaticConfigFree((tAppidStaticConfig*)old_context); reloadUnlock(); } #endif // SNORT_RELOAD /******** AppId Reconfigure Functions ********/ /** * \brief Callback function that handles AppId reconfiguration * * This function gets called on AppId reconfiguration in a separate thread. It * starts loading AppId configuration into new_context. * * @param type * @param data * @param length * @param new_context return parameter to hold AppId configuration * @param statusBuf * @param statusBuf_Len * @return 0 on success */ STATIC int AppIdReconfigure(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char* statusBuf, int statusBuf_len) { reloadWait(); if (*new_context == NULL) { AppIdCommonReload(appidStaticConfig, new_context); } return 0; } /** * \brief Callback function that handles AppId reconfiguration swap * * This function gets called adter AppIdReconfigure() returns. At this point, * AppIdReconfigure() is done with loading the configuration into new_context * and new_context is ready to use. * * @param type * @param new_context pointer to data structure that contains AppId's new * configuration. This data structure was populated by AppIdReconfigure(). * @param old_context return parameter that points to old configuration * @return 0 on success */ STATIC int AppIdReconfigureSwap(uint16_t type, void *new_context, void **old_context) { struct timeval startTime; struct timeval endTime; double elapsedTime; gettimeofday(&startTime, NULL); if (new_context) { if (*old_context == NULL) { // Return current configuration in old_context *old_context = AppIdCommonReloadSwap(new_context); ThirdPartyAppIDReconfigure(); } } _dpd.logMsg("AppId", "Reconfigured"); gettimeofday(&endTime, NULL); elapsedTime = (endTime.tv_sec*1000.0) + (endTime.tv_usec/1000.0) - (startTime.tv_sec*1000.0) - (startTime.tv_usec/1000.0); _dpd.logMsg("AppId reconfigure swap time = %.3f msec\n", elapsedTime); return 0; } /** * \brief Callback function that handles freeing of old AppId configuration * * This function gets called after AppIdReconfigureSwap() returns. It frees the * data strcuture that contains the old configuration. * * @param type * @param old_context pointer to data structure that contains AppId's old * configuration. This pointer was returned by AppIdReconfigureSwap(). * @param te * @param f * @return void */ STATIC void AppIdReconfigureFree(uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f) { if (old_context) { AppIdCommonUnload(old_context); } reloadUnlock(); } void AppIdDumpStats(int exit_flag) { _dpd.logMsg("Application Identification Preprocessor:\n"); _dpd.logMsg(" Total packets received : %lu\n", app_id_raw_packet_count); _dpd.logMsg(" Total packets processed : %lu\n", app_id_processed_packet_count); _dpd.logMsg(" Total packets ignored : %lu\n", app_id_ignored_packet_count); _dpd.logMsg(" Total ongoing AppId sessions : %lu\n", app_id_ongoing_session); _dpd.logMsg(" Total AppId sessions allocated : %lu\n", app_id_total_alloc); _dpd.logMsg(" AppId session size : %lu\n", sizeof(tAppIdData)); _dpd.logMsg(" Total AppId sessions allocated from heap : %lu\n", app_id_session_heap_alloc_count); _dpd.logMsg("Total AppId sessions allocated from AppID Memory Pool : %lu\n", app_id_session_freelist_alloc_count); _dpd.logMsg(" AppID session Memory Pool free count : %lu\n", app_id_flow_data_free_list_count); _dpd.logMsg(" Flow-data Memory Pool free count : %lu\n", app_id_data_free_list_count); _dpd.logMsg(" Tmp Memory Pool free count : %lu\n", app_id_tmp_free_list_count); if (exit_flag == 0) // Snort's SigDumpStatsHandler dumping stats intentionally { if (thirdparty_appid_module) thirdparty_appid_module->print_stats(); AppIdServiceStateDumpStats(); RNAPndDumpLuaStats(); #ifdef SIDE_CHANNEL AppIdPrintSSStats(); #endif } } static void appIdIdleProcessing(void) { appIdStatsIdleFlush(); } static void AppIdResetStats(int signal, void *data) { app_id_raw_packet_count = 0; app_id_processed_packet_count = 0; app_id_ignored_packet_count = 0; app_id_ongoing_session = 0; app_id_total_alloc = 0; app_id_session_heap_alloc_count = 0; app_id_session_freelist_alloc_count = 0; app_id_flow_data_free_list_count = 0; app_id_data_free_list_count = 0; app_id_tmp_free_list_count = 0; if (thirdparty_appid_module) thirdparty_appid_module->reset_stats(); #ifdef SIDE_CHANNEL AppIdResetSSStats(); #endif } static void AppIdCleanExit(int signal, void *unused) { AppIdCommonFini(); #ifdef SIDE_CHANNEL AppIdCleanSS(); #endif AppIdStaticConfigFree(appidStaticConfig); } static int ThirdPartyReload(uint16_t type, void *new_context, void **old_context) { if (thirdparty_appid_module != NULL) { thirdparty_appid_module->print_stats(); } ThirdPartyAppIDFini(); ThirdPartyAppIDInit(appidStaticConfig); return 0; } int AppId_Print_Mem_Stats(FILE *fd, char *buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; if (fd) { len = fprintf(fd, ",%lu,%lu,%lu,%lu" ",%lu,%lu,%lu" ",%lu,%lu,%lu" ",%lu,%lu,%lu" ",%lu,%u,%u" ",%lu,%u,%u" ",%lu,%u,%u" ",%lu,%u,%u" , app_id_total_alloc, app_id_ongoing_session, app_id_session_freelist_alloc_count, app_id_session_heap_alloc_count , app_id_data_free_list_count, app_id_data_free_list_count*sizeof(tAppIdData) , app_id_flow_data_free_list_count, app_id_flow_data_free_list_count*sizeof(AppIdFlowData) , app_id_tmp_free_list_count, app_id_tmp_free_list_count*sizeof(tTmpAppIdData) , app_id_raw_packet_count, app_id_processed_packet_count, app_id_ignored_packet_count , meminfo[PP_MEM_CATEGORY_SESSION].used_memory, meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc, meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory, meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc, meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_MISC].used_memory, meminfo[PP_MEM_CATEGORY_MISC].num_of_alloc, meminfo[PP_MEM_CATEGORY_MISC].num_of_free , meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory + meminfo[PP_MEM_CATEGORY_MISC].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc + meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc + meminfo[PP_MEM_CATEGORY_MISC].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free + meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free + meminfo[PP_MEM_CATEGORY_MISC].num_of_free); } else if (buffer) { /* * Old buffer output for control socket comm, * like via, "show snort preprocessor-memory-usage" * There are no support for the preproc APPID here, * Hence keeping this block as empty */ } else { _dpd.logMsg("\n"); _dpd.logMsg("\n"); _dpd.logMsg("Memory Statistics of AppID on: %s\n", ctime(&curr_time)); _dpd.logMsg(" AppID Session Statistics:\n"); _dpd.logMsg(" Total Sessions seen: %14lu\n", app_id_total_alloc); _dpd.logMsg(" Current Active sessions: %14lu\n", app_id_ongoing_session); _dpd.logMsg("Total allocs from MemPool-AppID session: %14lu\n", app_id_session_freelist_alloc_count); _dpd.logMsg(" Total allocs from Heap: %14lu\n", app_id_session_heap_alloc_count); _dpd.logMsg(" AppID Memory Pool Statistics:\n"); _dpd.logMsg(" Memory Pool-AppID session:\n"); _dpd.logMsg(" Free count: %14lu\n", app_id_data_free_list_count); _dpd.logMsg(" Free size: %14lu bytes\n", app_id_data_free_list_count*sizeof(tAppIdData)); _dpd.logMsg(" Memory Pool-Flow data:\n"); _dpd.logMsg(" Free count: %14lu\n", app_id_flow_data_free_list_count); _dpd.logMsg(" Free size: %14lu bytes\n", app_id_flow_data_free_list_count*sizeof(AppIdFlowData)); _dpd.logMsg(" Memory Pool-Tmp:\n"); _dpd.logMsg(" Free count: %14lu \n", app_id_tmp_free_list_count); _dpd.logMsg(" Free size: %14lu bytes\n", app_id_tmp_free_list_count*sizeof(tTmpAppIdData)); _dpd.logMsg(" AppID Packet Statistics:\n"); _dpd.logMsg(" Total packets received: %14lu\n", app_id_raw_packet_count); _dpd.logMsg(" Total packets processed: %14lu\n", app_id_processed_packet_count); _dpd.logMsg(" Total packets ignored: %14lu\n", app_id_ignored_packet_count); } return len; } static void AppIdInit(struct _SnortConfig *sc, char *args) { static int once = 0; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); _dpd.registerMemoryStatsFunc(PP_APP_ID, AppId_Print_Mem_Stats); if (!once) { _dpd.addPreprocExit(AppIdCleanExit, NULL, PRIORITY_LAST, PP_APP_ID); # ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("fwApp", &appMatchPerfStats, 0, _dpd.totalPerfStats, NULL); _dpd.addPreprocProfileFunc("fwAppTP", &tpPerfStats, 1, &appMatchPerfStats, NULL); _dpd.addPreprocProfileFunc("fwLibAppTP", &tpLibPerfStats, 2, &tpPerfStats, NULL); _dpd.addPreprocProfileFunc("fwHTTP", &httpPerfStats, 2, &tpPerfStats, NULL); _dpd.addPreprocProfileFunc("fwClientPat", &clientMatchPerfStats, 1, &appMatchPerfStats, NULL); _dpd.addPreprocProfileFunc("fwServicePat", &serviceMatchPerfStats, 1, &appMatchPerfStats, NULL); _dpd.addPreprocProfileFunc("luaDetectors", &luaDetectorsPerfStats, 1, &appMatchPerfStats, NULL); _dpd.addPreprocProfileFunc("cisco", &luaCiscoPerfStats, 2, &luaDetectorsPerfStats, NULL); _dpd.addPreprocProfileFunc("custom", &luaCustomPerfStats, 2, &luaDetectorsPerfStats, NULL); # endif appid_preproc_status_bit = _dpd.sessionAPI->get_preprocessor_status_bit(); if (NULL == (appidStaticConfig = (tAppidStaticConfig*)_dpd.snortAlloc(1, sizeof(*appidStaticConfig), PP_APP_ID, PP_MEM_CATEGORY_CONFIG))) { _dpd.fatalMsg("AppID failed to allocate memory for the configuration\n"); } appIdConfigParse(appidStaticConfig, args); AppIdCommonInit(appidStaticConfig); ThirdPartyAppIDInit(appidStaticConfig); if (appidStaticConfig->app_id_dump_ports) { dumpPorts(stdout, pAppidActiveConfig); appInfoTableDump(pAppidActiveConfig); exit(0); } _dpd.addPreprocResetStats(AppIdResetStats, NULL, PRIORITY_LAST, PP_APP_ID); _dpd.registerPreprocStats(PREPROC_NAME, AppIdDumpStats); /* Hook into control socket to handle reload */ _dpd.controlSocketRegisterHandler(73, AppIdReconfigure, AppIdReconfigureSwap, AppIdReconfigureFree); _dpd.controlSocketRegisterHandler(74, AppIdDebug, NULL, NULL); _dpd.controlSocketRegisterHandler(56, NULL, ThirdPartyReload, NULL); _dpd.registerIdleHandler(appIdIdleProcessing); _dpd.registerGetAppId(getOpenAppId); if (!thirdparty_appid_module) _dpd.streamAPI->register_http_header_callback(httpHeaderCallback); _dpd.registerSslAppIdLookup(sslAppGroupIdLookup); if (_dpd.streamAPI->service_event_subscribe(PP_SIP, SIP_EVENT_TYPE_SIP_DIALOG, SipSessionSnortCallback) == false) DynamicPreprocessorFatalMessage("failed to subscribe to SIP_DIALOG\n"); if (_dpd.streamAPI->service_event_subscribe(PP_CIP, CIP_EVENT_TYPE_CIP_DATA, CipSessionSnortCallback) == false) DynamicPreprocessorFatalMessage("failed to subscribe to CIP_EVENT_TYPE_CIP_DATA\n"); _dpd.registerSetTlsHostAppId(setTlsHost); appIdApiInit(_dpd.appIdApi); #ifdef SIDE_CHANNEL _dpd.addFuncToPostConfigList(sc, AppIdSSPostConfigInit, NULL); #endif once = 1; } _dpd.addPreprocConfCheck(sc, AppIDCheckConfig); if (policy_id == _dpd.getDefaultPolicy()) AppIdAddPortsToStream5Filter(sc, policy_id); } #define SetupApplicationPreproc DYNAMIC_PREPROC_SETUP SF_SO_PUBLIC void SetupApplicationPreproc(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc(PREPROC_NAME, AppIdInit); #else _dpd.registerPreproc(PREPROC_NAME, AppIdInit, AppIdReload, AppIdReloadReloadVerify, AppIdReloadSwap, AppIdReloadFree); #endif } snort-2.9.20/src/dynamic-preprocessors/appid/thirdparty_appid_utils.c0000644000175000017500000001673114241075577024267 0ustar apoapo/**************************************************************************** * * Copyright (C) 2015-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2011 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ #include "thirdparty_appid_utils.h" #include #include #include "sf_dynamic_preprocessor.h" #include "commonAppMatcher.h" #define MODULE_SYMBOL "thirdparty_appid_impl_module" static _PluginHandle module_handle = NULL; static struct ThirdPartyConfig thirdpartyConfig; ThirdPartyAppIDModule* thirdparty_appid_module = NULL; static int LoadCallback(struct _SnortConfig *sc, const char * const path, int indent) { _PluginHandle handle_tmp; ThirdPartyAppIDModule* module_tmp; DynamicPluginMeta meta; if (thirdparty_appid_module != NULL) { _dpd.errMsg("Ignoring additional 3rd party AppID module (%s)!\n", path ? : ""); return 0; } handle_tmp = _dpd.openDynamicLibrary(path, 0); if (handle_tmp == NULL) { _dpd.errMsg("Could not load 3rd party AppID module (%s)!\n", path ? : ""); return 0; } meta.libraryPath = (char *)path; module_tmp = (ThirdPartyAppIDModule*)_dpd.getSymbol(handle_tmp, MODULE_SYMBOL, &meta, 1); if (module_tmp == NULL) { _dpd.errMsg("Ignoring invalid 3rd party AppID module (%s)!\n", path ? : ""); _dpd.closeDynamicLibrary(handle_tmp); return 0; } if ( (module_tmp->api_version != THIRD_PARTY_APP_ID_API_VERSION) || ((module_tmp->module_name == NULL) || (module_tmp->module_name[0] == 0)) || (module_tmp->init == NULL) || (module_tmp->fini == NULL) || (module_tmp->session_create == NULL) || (module_tmp->session_delete == NULL) || (module_tmp->session_process == NULL) || (module_tmp->print_stats == NULL) || (module_tmp->reset_stats == NULL) || (module_tmp->disable_flags == NULL) ) { _dpd.errMsg("Ignoring incomplete 3rd party AppID module (%s)!\n", path ? : ""); _dpd.closeDynamicLibrary(handle_tmp); return 0; } DEBUG_WRAP(DebugMessage(DEBUG_APPID, "Found 3rd party AppID module (%s).\n", module_tmp->module_name ? : "");); module_handle = handle_tmp; thirdparty_appid_module = module_tmp; return 0; } static void getXffFields(void) { static char* defaultXffFields[] = {HTTP_XFF_FIELD_X_FORWARDED_FOR, HTTP_XFF_FIELD_TRUE_CLIENT_IP}; char** xffFields; int i; xffFields = _dpd.getHttpXffFields(&thirdpartyConfig.numXffFields); if (!xffFields) { xffFields = defaultXffFields; thirdpartyConfig.numXffFields = sizeof(defaultXffFields) / sizeof(defaultXffFields[0]); } thirdpartyConfig.xffFields = malloc(thirdpartyConfig.numXffFields * sizeof(char*)); if(!thirdpartyConfig.xffFields) { _dpd.errMsg("getXffFields: Failed to allocate memory for xffFields in thirdpartyConfig\n"); } for (i = 0; i < thirdpartyConfig.numXffFields; i++) thirdpartyConfig.xffFields[i] = strndup(xffFields[i], UINT8_MAX); } void ThirdPartyAppIDInit(struct AppidStaticConfig *appidStaticConfig) { const char* thirdparty_appid_dir = appidStaticConfig->appid_thirdparty_dir; int ret; const char* dir = NULL; struct ThirdPartyUtils thirdpartyUtils; if (thirdparty_appid_module != NULL) { return; } if ((thirdparty_appid_dir == NULL) || (thirdparty_appid_dir[0] == 0)) { return; } else { dir = thirdparty_appid_dir; } _dpd.loadAllLibs(NULL, dir, LoadCallback); if (thirdparty_appid_module == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "No 3rd party AppID module loaded.\n");); return; } memset(&thirdpartyConfig, 0, sizeof(thirdpartyConfig)); thirdpartyConfig.chp_body_collection_max = appidStaticConfig->chp_body_collection_max; thirdpartyConfig.ftp_userid_disabled = appidStaticConfig->ftp_userid_disabled; thirdpartyConfig.chp_body_collection_disabled = appidStaticConfig->chp_body_collection_disabled; thirdpartyConfig.tp_allow_probes = appidStaticConfig->tp_allow_probes; if (appidStaticConfig->http2_detection_enabled) thirdpartyConfig.http_upgrade_reporting_enabled = 1; else thirdpartyConfig.http_upgrade_reporting_enabled = 0; if (appidStaticConfig->tp_config_path) { strncpy(thirdpartyConfig.tp_config_path, appidStaticConfig->tp_config_path, TP_PATH_MAX); thirdpartyConfig.tp_config_path[TP_PATH_MAX-1] = '\0'; } else thirdpartyConfig.tp_config_path[0] = '\0'; // use default path thirdpartyUtils.logMsg = _dpd.logMsg; thirdpartyUtils.getSnortInstance = _dpd.getSnortInstance; getXffFields(); ret = thirdparty_appid_module->init(&thirdpartyConfig, &thirdpartyUtils); if (ret != 0) { _dpd.errMsg("Unable to initialize 3rd party AppID module (%d)!\n", ret); _dpd.closeDynamicLibrary(module_handle); module_handle = NULL; thirdparty_appid_module = NULL; return; } DEBUG_WRAP(DebugMessage(DEBUG_APPID, "3rd party AppID module loaded and initialized OK (%s).\n", thirdparty_appid_module->module_name ? : "");); } void ThirdPartyAppIDReconfigure(void) { int ret; int i; if (thirdparty_appid_module == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "No 3rd party AppID module loaded.\n");); return; } thirdpartyConfig.oldNumXffFields = thirdpartyConfig.numXffFields; thirdpartyConfig.oldXffFields = thirdpartyConfig.xffFields; getXffFields(); ret = thirdparty_appid_module->reconfigure(&thirdpartyConfig); for (i = 0; i < thirdpartyConfig.oldNumXffFields; i++) free(thirdpartyConfig.oldXffFields[i]); free(thirdpartyConfig.oldXffFields); if (ret != 0) { _dpd.errMsg("Unable to reconfigure 3rd party AppID module (%d)!\n", ret); return; } DEBUG_WRAP(DebugMessage(DEBUG_APPID, "3rd party AppID module reconfigured OK (%s).\n", thirdparty_appid_module->module_name ? : "");); } void ThirdPartyAppIDFini(void) { int ret; int i; if (thirdparty_appid_module != NULL) { ret = thirdparty_appid_module->fini(); for (i = 0; i < thirdpartyConfig.numXffFields; i++) free(thirdpartyConfig.xffFields[i]); free(thirdpartyConfig.xffFields); if (ret != 0) { _dpd.errMsg("Could not finalize 3rd party AppID module (%d)!\n", ret); } _dpd.closeDynamicLibrary(module_handle); module_handle = NULL; thirdparty_appid_module = NULL; DEBUG_WRAP(DebugMessage(DEBUG_APPID, "3rd party AppID module finalized and unloaded OK.\n");); } } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/0000755000175000017500000000000014242725717022342 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_bit_tracker.c0000644000175000017500000001772414241075362027201 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "client_app_api.h" static const char UDP_BIT_QUERY[] = "d1:a"; static const char UDP_BIT_RESPONSE[] = "d1:r"; static const char UDP_BIT_ERROR[] = "d1:e"; static const char UDP_BIT_FIRST[] = "d1:"; static const char UDP_BIT_COMMON_END[] = "1:y1:"; #define UDP_BIT_FIRST_LEN (sizeof(UDP_BIT_FIRST)-1) #define UDP_BIT_COMMON_END_LEN (sizeof(UDP_BIT_COMMON_END)-1) #define UDP_BIT_END_LEN (UDP_BIT_COMMON_END_LEN+2) typedef enum { BIT_STATE_BANNER = 0, BIT_STATE_TYPES, BIT_STATE_DC, BIT_STATE_CHECK_END, BIT_STATE_CHECK_END_TYPES, BIT_STATE_CHECK_LAST } BITState; typedef enum { BIT_TYPE_REQUEST = 1, BIT_TYPE_RESPONSE, BIT_TYPE_ERROR } BITType; typedef struct _CLIENT_BIT_DATA { BITState state; BITType type; unsigned pos; } ClientBITData; typedef struct _BIT_CLIENT_APP_CONFIG { int enabled; } BIT_CLIENT_APP_CONFIG; static BIT_CLIENT_APP_CONFIG bit_config; static CLIENT_APP_RETCODE udp_bit_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE udp_bit_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); SF_SO_PUBLIC tRNAClientAppModule bit_tracker_client_mod = { .name = "BIT-UDP", .proto = IPPROTO_UDP, .init = &udp_bit_init, .validate = &udp_bit_validate, .minimum_matches = 1 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern udp_patterns[] = { {(const uint8_t *)UDP_BIT_QUERY, sizeof(UDP_BIT_QUERY), 0, APP_ID_BITTRACKER_CLIENT}, {(const uint8_t *)UDP_BIT_RESPONSE, sizeof(UDP_BIT_RESPONSE), 0, APP_ID_BITTRACKER_CLIENT}, {(const uint8_t *)UDP_BIT_ERROR, sizeof(UDP_BIT_ERROR), 0, APP_ID_BITTRACKER_CLIENT}, }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_BITTRACKER_CLIENT, 0}}; static CLIENT_APP_RETCODE udp_bit_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; bit_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { bit_config.enabled = atoi(item->value); } } } if (bit_config.enabled) { for (i=0; i < sizeof(udp_patterns)/sizeof(*udp_patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)udp_patterns[i].pattern, udp_patterns[i].index); init_api->RegisterPattern(&udp_bit_validate, IPPROTO_UDP, udp_patterns[i].pattern, udp_patterns[i].length, udp_patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&udp_bit_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE udp_bit_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { ClientBITData *fd; uint16_t offset; if (size < (UDP_BIT_FIRST_LEN + UDP_BIT_END_LEN + 3)) return CLIENT_APP_EINVALID; fd = bit_tracker_client_mod.api->data_get(flowp, bit_tracker_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (bit_tracker_client_mod.api->data_add(flowp, fd, bit_tracker_client_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } fd->state = BIT_STATE_BANNER; } offset = 0; while(offset < size) { switch (fd->state) { case BIT_STATE_BANNER: if(data[offset] != UDP_BIT_FIRST[fd->pos]) return CLIENT_APP_EINVALID; if(fd->pos == UDP_BIT_FIRST_LEN-1) fd->state = BIT_STATE_TYPES; fd->pos++; break; case BIT_STATE_TYPES: switch(data[offset]) { case 'a': fd->type = BIT_TYPE_REQUEST; fd->state = BIT_STATE_DC; break; case 'r': fd->type = BIT_TYPE_RESPONSE; fd->state = BIT_STATE_DC; break; case 'e': fd->type = BIT_TYPE_ERROR; fd->state = BIT_STATE_DC; break; default: return CLIENT_APP_EINVALID; } break; case BIT_STATE_DC: if(offset < (size - UDP_BIT_END_LEN)) break; else if(offset == (size - UDP_BIT_END_LEN) && data[offset] == UDP_BIT_COMMON_END[0]) { fd->state = BIT_STATE_CHECK_END; fd->pos = 0; } else return CLIENT_APP_EINVALID; /*fall through */ case BIT_STATE_CHECK_END: if(data[offset] != UDP_BIT_COMMON_END[fd->pos]) return CLIENT_APP_EINVALID; if(fd->pos == UDP_BIT_COMMON_END_LEN-1) fd->state = BIT_STATE_CHECK_END_TYPES; fd->pos++; break; case BIT_STATE_CHECK_END_TYPES: switch(data[offset]) { case 'q': if(fd->type != BIT_TYPE_REQUEST) return CLIENT_APP_EINVALID; fd->state = BIT_STATE_CHECK_LAST; break; case 'r': if(fd->type != BIT_TYPE_RESPONSE) return CLIENT_APP_EINVALID; fd->state = BIT_STATE_CHECK_LAST; break; case 'e': if(fd->type != BIT_TYPE_ERROR) return CLIENT_APP_EINVALID; fd->state = BIT_STATE_CHECK_LAST; break; default: return CLIENT_APP_EINVALID; } break; case BIT_STATE_CHECK_LAST: switch(data[offset]) { case 'e': goto done; default: return CLIENT_APP_EINVALID; } break; default: goto inprocess; } offset++; } inprocess: return CLIENT_APP_INPROCESS; done: bit_tracker_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_BITTORRENT, APP_ID_BITTRACKER_CLIENT, NULL); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/clientAppConfig.h0000644000175000017500000000373414241075377025566 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef CLIENT_APP_CONFIG_H_ #define CLIENT_APP_CONFIG_H_ /****************************** INCLUDES **************************************/ #include "client_app_api.h" /********************************* TYPES **************************************/ struct RNAClientAppModule; struct RNAClientAppRecord; typedef struct ClientPatternData { struct ClientPatternData *next; int position; const struct RNAClientAppModule *ca; } tClientPatternData; //typedef struct ClientPatternData tClientPatternData; typedef struct ClientAppConfig { struct RNAClientAppRecord_ *tcp_client_app_list; ///< List of all TCP client apps (C and Lua) struct RNAClientAppRecord_ *udp_client_app_list; ///< List of all UDP client apps (C and Lua) int enabled; SF_LIST module_configs; tClientPatternData *pattern_data_list; void *tcp_patterns; int tcp_pattern_count; void *udp_patterns; int udp_pattern_count; } tClientAppConfig; #endif // CLIENT_APP_CONFIG_H_ snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_bit.c0000644000175000017500000001456514241075361025465 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "client_app_api.h" static const char BIT_BANNER[] = "\023BitTorrent protocol"; #define BIT_BANNER_LEN (sizeof(BIT_BANNER)-1) #define RES_LEN 8 #define SHA_LEN 20 #define MAX_STR_LEN 20 #define PEER_ID_LEN 20 #define MAX_VER_LEN 4 #define LAST_BANNER_OFFSET (BIT_BANNER_LEN+RES_LEN+SHA_LEN+PEER_ID_LEN - 1) typedef enum { BIT_STATE_BANNER = 0, BIT_STATE_BANNER_DC, BIT_STATE_MESSAGE_LEN, BIT_STATE_MESSAGE_DATA } BITState; typedef struct _CLIENT_BIT_DATA { BITState state; unsigned stringlen; unsigned pos; union { uint32_t len; uint8_t raw_len[4]; }l; } ClientBITData; #pragma pack(1) typedef struct _CLIENT_BIT_MSG { uint32_t len; uint8_t code; } ClientBITMsg; #pragma pack() typedef struct _BIT_CLIENT_APP_CONFIG { int enabled; } BIT_CLIENT_APP_CONFIG; static BIT_CLIENT_APP_CONFIG bit_config; static CLIENT_APP_RETCODE bit_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE bit_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); SF_SO_PUBLIC tRNAClientAppModule bit_client_mod = { .name = "BIT", .proto = IPPROTO_TCP, .init = &bit_init, .validate = &bit_validate, .minimum_matches = 1 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)BIT_BANNER, sizeof(BIT_BANNER)-1, 0, APP_ID_BITTORRENT}, }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_BITTORRENT, 0}}; static CLIENT_APP_RETCODE bit_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; bit_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { bit_config.enabled = atoi(item->value); } } } if (bit_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&bit_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&bit_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE bit_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { ClientBITData *fd; uint16_t offset; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; fd = bit_client_mod.api->data_get(flowp, bit_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (bit_client_mod.api->data_add(flowp, fd, bit_client_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } fd->state = BIT_STATE_BANNER; } offset = 0; while(offset < size) { switch (fd->state) { case BIT_STATE_BANNER: if(data[offset] != BIT_BANNER[fd->pos]) return CLIENT_APP_EINVALID; if(fd->pos == BIT_BANNER_LEN-1) fd->state = BIT_STATE_BANNER_DC; fd->pos++; break; case BIT_STATE_BANNER_DC: if(fd->pos == LAST_BANNER_OFFSET) { fd->pos = 0; fd->state = BIT_STATE_MESSAGE_LEN; break; } fd->pos++; break; case BIT_STATE_MESSAGE_LEN: fd->l.raw_len[fd->pos] = data[offset]; fd->pos++; if(fd->pos >= offsetof(ClientBITMsg , code)) { fd->stringlen = ntohl(fd->l.len) ; fd->state = BIT_STATE_MESSAGE_DATA; if(!fd->stringlen) { if(offset == size-1) goto done; return CLIENT_APP_EINVALID; } fd->pos = 0; } break; case BIT_STATE_MESSAGE_DATA: fd->pos++; if(fd->pos == fd->stringlen) goto done; break; default: goto inprocess; } offset++; } inprocess: return CLIENT_APP_INPROCESS; done: bit_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_BITTORRENT, APP_ID_BITTORRENT, NULL); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_rtp.c0000644000175000017500000003113214241075365025505 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" /* for WORDS_BIGENDIAN */ #endif #include "client_app_api.h" #include "client_app_rtp.h" typedef enum { RTP_STATE_CONNECTION, RTP_STATE_CONTINUE } RTPState; #define MAX_REMOTE_SIZE 128 #define NUMBER_OF_PACKETS 3 #define MAX_SSRC_SWITCHES 2 typedef struct { RTPState state; uint16_t seq; uint8_t count; uint32_t timestamp; uint32_t ssrc; uint8_t numSsrcSwitches; } ClientRTPDirData; typedef struct _CLIENT_RTP_DATA { ClientRTPDirData initiatorData; ClientRTPDirData responderData; } ClientRTPData; typedef struct _RTP_CLIENT_APP_CONFIG { int enabled; } RTP_CLIENT_APP_CONFIG; static RTP_CLIENT_APP_CONFIG rtp_config; static CLIENT_APP_RETCODE rtp_init(const InitClientAppAPI * const init_api, SF_LIST *config); STATIC CLIENT_APP_RETCODE rtp_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); SF_SO_PUBLIC tRNAClientAppModule rtp_client_mod = { .name = "RTP", .proto = IPPROTO_UDP, .init = &rtp_init, .validate = &rtp_validate, .minimum_matches = 1 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)"\x000\x000", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x001", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x002", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x003", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x004", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x005", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x006", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x007", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x008", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x009", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x00a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x00b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x00c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x00d", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x00e", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x00f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x010", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x011", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x012", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x013", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x019", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x01a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x01b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x01c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x01f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x020", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x021", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x022", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x080", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x081", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x082", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x083", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x084", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x085", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x086", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x087", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x088", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x089", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x08a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x08b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x08c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x08d", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x08e", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x08f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x090", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x091", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x092", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x093", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x099", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x09a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x09b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x09c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x09f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x0a0", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x0a1", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x000\x0a2", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x000", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x001", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x002", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x003", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x004", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x005", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x006", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x007", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x008", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x009", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x00a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x00b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x00c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x00d", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x00e", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x00f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x010", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x011", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x012", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x013", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x019", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x01a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x01b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x01c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x01f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x020", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x021", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x022", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x080", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x081", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x082", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x083", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x084", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x085", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x086", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x087", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x088", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x089", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x08a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x08b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x08c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x08d", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x08e", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x08f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x090", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x091", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x092", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x093", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x099", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x09a", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x09b", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x09c", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x09f", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x0a0", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x0a1", 2, 0, APP_ID_RTP}, {(const uint8_t *)"\x080\x0a2", 2, 0, APP_ID_RTP}, }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_RTP, 0}}; static CLIENT_APP_RETCODE rtp_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; rtp_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { rtp_config.enabled = atoi(item->value); } } } if (rtp_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&rtp_validate, IPPROTO_UDP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&rtp_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static inline void rtpInitDirData(ClientRTPDirData* dirData, ClientRTPMsg* hdr) { dirData->seq = ntohs(hdr->seq); dirData->timestamp = ntohl(hdr->timestamp); dirData->ssrc = ntohl(hdr->ssrc); dirData->count = 1; } static inline CLIENT_APP_RETCODE rtpValidateDirData(ClientRTPDirData* dirData, ClientRTPMsg* hdr) { if ((ntohs(hdr->seq) != ++dirData->seq) || (ntohl(hdr->timestamp) < dirData->timestamp)) return CLIENT_APP_EINVALID; if (ntohl(hdr->ssrc) != dirData->ssrc) { if (++dirData->numSsrcSwitches > MAX_SSRC_SWITCHES) return CLIENT_APP_EINVALID; rtpInitDirData(dirData, hdr); return CLIENT_APP_INPROCESS; } dirData->timestamp = ntohl(hdr->timestamp); if (++dirData->count < NUMBER_OF_PACKETS) return CLIENT_APP_INPROCESS; return CLIENT_APP_SUCCESS; } STATIC CLIENT_APP_RETCODE rtp_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { ClientRTPData *fd; ClientRTPMsg *hdr; RTPState *state; if (!size) return CLIENT_APP_INPROCESS; if (size < sizeof(ClientRTPMsg)) return CLIENT_APP_EINVALID; hdr = (ClientRTPMsg *)data; if (hdr->vers > 2 || hdr->payloadtype > 34) return CLIENT_APP_EINVALID; fd = rtp_client_mod.api->data_get(flowp, rtp_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (rtp_client_mod.api->data_add(flowp, fd, rtp_client_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } fd->initiatorData.state = RTP_STATE_CONNECTION; fd->responderData.state = RTP_STATE_CONNECTION; } state = (dir == APP_ID_FROM_INITIATOR ? &fd->initiatorData.state : &fd->responderData.state); switch (*state) { CLIENT_APP_RETCODE retVal; case RTP_STATE_CONNECTION: if (dir == APP_ID_FROM_INITIATOR) rtpInitDirData(&fd->initiatorData, hdr); else rtpInitDirData(&fd->responderData, hdr); *state = RTP_STATE_CONTINUE; return CLIENT_APP_INPROCESS; case RTP_STATE_CONTINUE: if (dir == APP_ID_FROM_INITIATOR) retVal = rtpValidateDirData(&fd->initiatorData, hdr); else retVal = rtpValidateDirData(&fd->responderData, hdr); if (retVal != CLIENT_APP_SUCCESS) return retVal; break; default: return CLIENT_APP_INPROCESS; } rtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_RTP, APP_ID_RTP, NULL); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_rtp.h0000644000175000017500000000105314241075366025512 0ustar apoapo#ifndef CLIENT_APP_RTP_H_ #define CLIENT_APP_RTP_H_ #include #pragma pack(1) typedef struct { #if defined(WORDS_BIGENDIAN) uint8_t vers:2, padding:1, extension:1, count:4; uint8_t marker:1, payloadtype:7; #else uint8_t count:4, extension:1, padding:1, vers:2; uint8_t payloadtype:7, marker:1; #endif uint16_t seq; uint32_t timestamp; uint32_t ssrc; } ClientRTPMsg; #pragma pack() #endif // !CLIENT_APP_RTP_H_ snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_tns.c0000644000175000017500000003111714241075372025505 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "client_app_api.h" static const char TNS_BANNER[] = "\000\000"; #define TNS_BANNER_LEN (sizeof(TNS_BANNER)-1) #define TNS_TYPE_CONNECT 1 #define TNS_TYPE_ACCEPT 2 #define TNS_TYPE_ACK 3 #define TNS_TYPE_REFUSE 4 #define TNS_TYPE_REDIRECT 5 #define TNS_TYPE_DATA 6 #define TNS_TYPE_NULL 7 #define TNS_TYPE_ABORT 9 #define TNS_TYPE_RESEND 11 #define TNS_TYPE_MARKER 12 #define TNS_TYPE_ATTENTION 13 #define TNS_TYPE_CONTROL 14 #define TNS_TYPE_MAX 19 #define CONNECT_VERSION_OFFSET 8 #define CONNECT_DATA_OFFSET 26 #define USER_STRING "user=" #define MAX_USER_POS ((int)sizeof(USER_STRING) - 2) typedef enum { TNS_STATE_MESSAGE_LEN = 0, TNS_STATE_MESSAGE_CHECKSUM, TNS_STATE_MESSAGE, TNS_STATE_MESSAGE_RES, TNS_STATE_MESSAGE_HD_CHECKSUM, TNS_STATE_MESSAGE_DATA, TNS_STATE_MESSAGE_CONNECT, TNS_STATE_MESSAGE_CONNECT_OFFSET_DC, TNS_STATE_MESSAGE_CONNECT_OFFSET, TNS_STATE_MESSAGE_CONNECT_PREDATA, TNS_STATE_MESSAGE_CONNECT_DATA, TNS_STATE_COLLECT_USER } TNSState; #define MAX_VERSION_SIZE 12 typedef struct _CLIENT_TNS_DATA { TNSState state; unsigned stringlen; unsigned offsetlen; unsigned pos; unsigned message; union { uint16_t len; uint8_t raw_len[2]; }l; const char * version; uint8_t * data; } ClientTNSData; #pragma pack(1) typedef struct _CLIENT_TNS_MSG { uint16_t len; uint16_t checksum; uint8_t msg; uint8_t res; uint16_t hdchecksum; uint8_t data; } ClientTNSMsg; #pragma pack() typedef struct _TNS_CLIENT_APP_CONFIG { int enabled; } TNS_CLIENT_APP_CONFIG; #if 0 static const char * msg_type[] = { NULL, "Connect", "Accept", "Acknowledge", "Refuse", "Redirect", "Data" , "Null" , NULL, "Abort" , NULL, "Resend", "Marker", "Attention", "Control", NULL, NULL, NULL, NULL, NULL }; #endif static TNS_CLIENT_APP_CONFIG tns_config; static CLIENT_APP_RETCODE tns_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE tns_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); SF_SO_PUBLIC tRNAClientAppModule tns_client_mod = { .name = "TNS", .proto = IPPROTO_TCP, .init = &tns_init, .validate = &tns_validate, .minimum_matches = 1, .provides_user = 1, }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)TNS_BANNER, sizeof(TNS_BANNER)-1, 2, APP_ID_ORACLE_DATABASE}, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_ORACLE_DATABASE, APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER} }; static CLIENT_APP_RETCODE tns_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { tns_config.enabled = atoi(item->value); } } } tns_config.enabled = 1; if (tns_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&tns_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&tns_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } #define TNS_MAX_INFO_SIZE 63 static CLIENT_APP_RETCODE tns_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { char username[TNS_MAX_INFO_SIZE+1]; ClientTNSData *fd; uint16_t offset; int user_pos = 0; int user_size = 0; uint16_t user_start = 0; uint16_t user_end = 0; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; fd = tns_client_mod.api->data_get(flowp, tns_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (tns_client_mod.api->data_add(flowp, fd, tns_client_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } fd->state = TNS_STATE_MESSAGE_LEN; } offset = 0; while(offset < size) { switch (fd->state) { case TNS_STATE_MESSAGE_LEN: fd->l.raw_len[fd->pos++] = data[offset]; if(fd->pos >= offsetof(ClientTNSMsg, checksum)) { fd->stringlen = ntohs(fd->l.len); if(fd->stringlen == 2) { if(offset == size - 1) goto done; return CLIENT_APP_EINVALID; } else if(fd->stringlen < 2) return CLIENT_APP_EINVALID; else if(fd->stringlen > size) return CLIENT_APP_EINVALID; else fd->state = TNS_STATE_MESSAGE_CHECKSUM; } break; case TNS_STATE_MESSAGE_CHECKSUM: if(data[offset] != 0) return CLIENT_APP_EINVALID; fd->pos++; if(fd->pos >= offsetof(ClientTNSMsg, msg)) fd->state = TNS_STATE_MESSAGE; break; case TNS_STATE_MESSAGE: fd->message = data[offset]; if(fd->message < TNS_TYPE_CONNECT || fd->message > TNS_TYPE_MAX) return CLIENT_APP_EINVALID; fd->pos++; fd->state = TNS_STATE_MESSAGE_RES; break; case TNS_STATE_MESSAGE_RES: fd->state = TNS_STATE_MESSAGE_HD_CHECKSUM; fd->pos++; break; case TNS_STATE_MESSAGE_HD_CHECKSUM: fd->pos++; if(fd->pos >= offsetof(ClientTNSMsg, data)) { switch(fd->message) { case TNS_TYPE_CONNECT: fd->state = TNS_STATE_MESSAGE_CONNECT; break; case TNS_TYPE_ACK: case TNS_TYPE_REFUSE: case TNS_TYPE_DATA: case TNS_TYPE_NULL: case TNS_TYPE_ABORT: case TNS_TYPE_RESEND: case TNS_TYPE_MARKER: case TNS_TYPE_ATTENTION: case TNS_TYPE_CONTROL: if(fd->pos >= fd->stringlen) { if(offset == (size - 1)) goto done; return CLIENT_APP_EINVALID; } fd->state = TNS_STATE_MESSAGE_DATA; break; case TNS_TYPE_ACCEPT: case TNS_TYPE_REDIRECT: default: return CLIENT_APP_EINVALID; } } break; case TNS_STATE_MESSAGE_CONNECT: fd->l.raw_len[fd->pos - CONNECT_VERSION_OFFSET] = data[offset]; fd->pos++; if(fd->pos >= (CONNECT_VERSION_OFFSET + 2)) { { switch(ntohs(fd->l.len)) { case 0x136: fd->version = "8"; break; case 0x137: fd->version = "9i R1"; break; case 0x138: fd->version = "9i R2"; break; case 0x139: fd->version = "10g R1/R2"; break; case 0x13A: fd->version = "11g R1"; break; default: break; } } fd->l.len = 0; fd->state = TNS_STATE_MESSAGE_CONNECT_OFFSET_DC; } break; case TNS_STATE_MESSAGE_CONNECT_OFFSET_DC: fd->pos++; if(fd->pos >= CONNECT_DATA_OFFSET) fd->state = TNS_STATE_MESSAGE_CONNECT_OFFSET; break; case TNS_STATE_MESSAGE_CONNECT_OFFSET: fd->l.raw_len[fd->pos - CONNECT_DATA_OFFSET] = data[offset]; fd->pos++; if(fd->pos >= (CONNECT_DATA_OFFSET + 2)) { fd->offsetlen = ntohs(fd->l.len); if (fd->offsetlen > size) { return CLIENT_APP_EINVALID; } fd->state = TNS_STATE_MESSAGE_CONNECT_PREDATA; } break; case TNS_STATE_MESSAGE_CONNECT_PREDATA: fd->pos++; if(fd->pos >= fd->offsetlen) { fd->state = TNS_STATE_MESSAGE_CONNECT_DATA; } break; case TNS_STATE_MESSAGE_CONNECT_DATA: if (tolower(data[offset]) != USER_STRING[user_pos]) { user_pos = 0; if (tolower(data[offset]) == USER_STRING[user_pos]) user_pos++; } else if (++user_pos > MAX_USER_POS) { user_start = offset+1; fd->state = TNS_STATE_COLLECT_USER; } fd->pos++; if(fd->pos >= fd->stringlen) { if(offset == (size - 1)) goto done; return CLIENT_APP_EINVALID; } break; case TNS_STATE_COLLECT_USER: if (user_end == 0 && data[offset] == ')') { user_end = offset; } fd->pos++; if(fd->pos >= fd->stringlen) { if(offset == (size - 1)) goto done; return CLIENT_APP_EINVALID; } break; case TNS_STATE_MESSAGE_DATA: fd->pos++; if(fd->pos >= fd->stringlen) { if(offset == (size - 1)) goto done; return CLIENT_APP_EINVALID; } break; default: goto inprocess; } offset++; } inprocess: return CLIENT_APP_INPROCESS; done: tns_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_ORACLE_TNS, APP_ID_ORACLE_DATABASE, fd->version); if(user_start && user_end && ((user_size = user_end - user_start) > 0)) { /* we truncate extra long usernames */ if (user_size > TNS_MAX_INFO_SIZE) user_size = TNS_MAX_INFO_SIZE; memcpy(username, &data[user_start], user_size); username[user_size] = 0; tns_client_mod.api->add_user(flowp, username, APP_ID_ORACLE_DATABASE, 1); } setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_aim.c0000644000175000017500000002400714241075354025447 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include "appInfoTable.h" #include "client_app_api.h" #include "client_app_aim.h" #pragma pack(1) typedef struct _FLAP_FNAC_SIGNON { uint16_t len; } FLAPFNACSignOn; typedef struct _FLAP_FNAC { uint16_t family; uint16_t subtype; uint16_t flags; uint32_t id; } FLAPFNAC; typedef struct _FLAP_TLV { uint16_t subtype; uint16_t len; } FLAPTLV; typedef struct _FLAP_HEADER { uint8_t start; uint8_t channel; uint16_t seq; uint16_t len; } FLAPHeader; #pragma pack() typedef struct _AIM_CLIENT_APP_CONFIG { int enabled; } AIM_CLIENT_APP_CONFIG; static AIM_CLIENT_APP_CONFIG aim_config; #define MAX_VERSION_SIZE 64 static CLIENT_APP_RETCODE aim_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE aim_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); tRNAClientAppModule aim_client_mod = { .name = "AIM", .proto = IPPROTO_TCP, .init = &aim_init, .validate = &aim_validate, .minimum_matches = 2, .provides_user = 1, }; typedef struct { const uint8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static const uint8_t NEW_CONNECTION[] = "\x02a\x001"; static const uint8_t AIM_PROTOCOL_VERSION[] = "\x000\x004\x000\x000\x000\x001"; static const uint8_t OLDER_AOL[] = "AOL Instant Messenger"; static const uint8_t AOL[] = "imApp"; static const uint8_t NETSCAPE_AOL[] = "Netscape 2000 an approved user of AOL Instant Messenger"; static Client_App_Pattern patterns[] = { {NEW_CONNECTION, sizeof(NEW_CONNECTION)-1, 0}, {AIM_PROTOCOL_VERSION, sizeof(AIM_PROTOCOL_VERSION)-1, 4}, {OLDER_AOL, sizeof(OLDER_AOL)-1, -1, APP_ID_AOL_INSTANT_MESSENGER}, {AOL, sizeof(AOL)-1, -1, APP_ID_AOL_INSTANT_MESSENGER}, {NETSCAPE_AOL, sizeof(NETSCAPE_AOL), -1, APP_ID_AOL_NETSCAPE}, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_AOL_NETSCAPE, APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER}, {APP_ID_AOL_INSTANT_MESSENGER, APPINFO_FLAG_CLIENT_ADDITIONAL | APPINFO_FLAG_CLIENT_USER}, }; static CLIENT_APP_RETCODE aim_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; aim_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { aim_config.enabled = atoi(item->value); } } } if (aim_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering pattern length %u at %d\n",patterns[i].length, patterns[i].index); init_api->RegisterPattern(&aim_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&aim_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE aim_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { const uint8_t *end; const uint8_t *frame_end; uint16_t len; FLAPHeader *fh; FLAPFNAC *fnac; FLAPTLV *tlv; uint16_t tlvtype; uint16_t tlvlen; int check_user_name; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; end = data + size; while (data < end) { if (size < sizeof(*fh)) goto bail; fh = (FLAPHeader *)data; data += sizeof(*fh); if (fh->start != 0x2a || fh->channel < 1 || fh->channel > 5) goto bail; len = ntohs(fh->len); if (len > (end - data)) goto bail; check_user_name = 0; if (fh->channel == 0x02) { if (len < sizeof(*fnac)) goto bail; fnac = (FLAPFNAC *)data; if (fnac->family == htons(0x0017) && fnac->subtype == htons(0x0006)) check_user_name = 1; data += sizeof(*fnac); len -= sizeof(*fnac); } else if (fh->channel == 0x01) { if (len < 4 || memcmp(data, &AIM_PROTOCOL_VERSION[2], 4) != 0) goto bail; len -= 4; data += 4; } if (len) { int got_id = 0; uint32_t product_id = APP_ID_AOL_INSTANT_MESSENGER; uint16_t major = 0; uint16_t minor = 0; uint16_t lesser = 0; char username[256]; frame_end = data + len; while (data < frame_end) { if ((size_t)(frame_end - data) < sizeof(*tlv)) goto bail; tlv = (FLAPTLV *)data; data += sizeof(*tlv); tlvtype = ntohs(tlv->subtype); tlvlen = ntohs(tlv->len); if (frame_end - data < tlvlen) goto bail; switch (tlvtype) { case 0x0001: if (check_user_name) { char *u; const char *u_end; const char *p; const char *p_end; p = (const char *)data; p_end = p + tlvlen; u = username; u_end = u + sizeof(username) - 1; for (; p < p_end; p++) { if (isalnum(*p) || *p == '.' || *p == '@' || *p == '-' || *p == '_') { if (u < u_end) { *u = *p; u++; } } else { u = username; break; } } if (u != username) { *u = 0; aim_client_mod.api->add_user(flowp, username, APP_ID_AOL_INSTANT_MESSENGER, 1); } } break; case 0x0003: got_id = 1; if (tlvlen >= sizeof(AOL)-1 && memcmp(data, AOL, sizeof(AOL)-1) == 0) { product_id = APP_ID_AOL_INSTANT_MESSENGER; } else if (tlvlen >= sizeof(NETSCAPE_AOL)-1 && memcmp(data, NETSCAPE_AOL, sizeof(NETSCAPE_AOL)-1) == 0) { product_id = APP_ID_AOL_INSTANT_MESSENGER; } else if (tlvlen >= sizeof(OLDER_AOL)-1 && memcmp(data, OLDER_AOL, sizeof(OLDER_AOL)-1) == 0 ) { product_id = APP_ID_AOL_INSTANT_MESSENGER; } break; case 0x0017: got_id = 1; major = ntohs(*(uint16_t *)data); break; case 0x0018: got_id = 1; minor = ntohs(*(uint16_t *)data); break; case 0x0019: got_id = 1; lesser = ntohs(*(uint16_t *)data); break; default: break; } data += tlvlen; } if (got_id) { char version[MAX_VERSION_SIZE]; snprintf(version, sizeof(version), "%d.%d.%d", major, minor, lesser); aim_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_AOL_INSTANT_MESSENGER, product_id, version); } } } return CLIENT_APP_INPROCESS; bail: setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_aim.h0000644000175000017500000000205714241075355025456 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __CLIENT_APP_AIM_H__ #define __CLIENT_APP_AIM_H__ #include "client_app_api.h" extern tRNAClientAppModule aim_client_mod; #endif /* __CLIENT_APP_AIM_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_base.c0000644000175000017500000007434014241075357025623 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** * @file client_app_base.c * @author Ron Dempster * */ #include #include #include #include #include #include #include #include "profiler.h" #include "appIdApi.h" #include "client_app_base.h" #include "str_search.h" #include "common_util.h" #include "client_app_api.h" #include "client_app_base.h" #include "client_app_msn.h" #include "client_app_aim.h" #include "client_app_ym.h" #include "detector_cip.h" #include "detector_sip.h" #include "luaDetectorModule.h" #include "luaDetectorApi.h" #include "httpCommon.h" #include "fw_appid.h" #include "service_ssl.h" #include "appIdConfig.h" #include "detector_dns.h" #include "detector_pattern.h" /*#define CLIENT_APP_DEBUG 1 */ #define BUFSIZE 512 /* If this is greater than 1, more than 1 client detector can be searched for * and tried per flow based on pattern (if a valid detector doesn't * already exist). */ #define MAX_CANDIDATE_CLIENTS 10 static void *client_app_flowdata_get(tAppIdData *flowp, unsigned client_id); static int client_app_flowdata_add(tAppIdData *flowp, void *data, unsigned client_id, AppIdFreeFCN fcn); static void AppIdAddClientAppInfo(tAppIdData *flowp, const char *info); static const ClientAppApi client_app_api = { .data_get = &client_app_flowdata_get, .data_add = &client_app_flowdata_add, .add_app= &AppIdAddClientApp, .add_info = &AppIdAddClientAppInfo, .add_user = &AppIdAddUser, .add_payload = &AppIdAddPayload }; static void LuaClientAppRegisterPattern(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, struct _Detector *userData); static void CClientAppRegisterPattern(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, tAppIdConfig *pConfig); static void CClientAppRegisterPatternNoCase(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, tAppIdConfig *pConfig); static InitClientAppAPI client_init_api = { .RegisterPattern = &CClientAppRegisterPattern, .RegisterPatternEx = &LuaClientAppRegisterPattern, .RegisterPatternNoCase = &CClientAppRegisterPatternNoCase, .RegisterAppId = &appSetClientValidator, .RegisterDetectorCallback = &appSetClientDetectorCallback, }; static CleanClientAppAPI clean_api = { }; static FinalizeClientAppAPI finalize_api = { }; extern tRNAClientAppModule bit_client_mod; extern tRNAClientAppModule bit_tracker_client_mod; extern tRNAClientAppModule rtp_client_mod; extern tRNAClientAppModule ssh_client_mod; extern tRNAClientAppModule timbuktu_client_mod; extern tRNAClientAppModule tns_client_mod; extern tRNAClientAppModule vnc_client_mod; extern tRNAClientAppModule pattern_udp_client_mod; extern tRNAClientAppModule pattern_tcp_client_mod; extern tRNAClientAppModule http_client_mod; static tRNAClientAppModule *static_client_list[] = { &msn_client_mod, &aim_client_mod, &ym_client_mod, &sip_udp_client_mod, &sip_tcp_client_mod, &bit_client_mod, &bit_tracker_client_mod, &rtp_client_mod, &ssh_client_mod, &timbuktu_client_mod, &tns_client_mod, &vnc_client_mod, &pattern_udp_client_mod, &pattern_tcp_client_mod, &dns_udp_client_mod, &dns_tcp_client_mod, &http_client_mod, &cip_client_mod, &enip_client_mod }; /*static const char * const MODULE_NAME = "ClientApp"; */ const ClientAppApi *getClientApi(void) { return &client_app_api; } RNAClientAppModuleConfig *getClientAppModuleConfig(const char *moduleName, tClientAppConfig *pClientAppConfig) { RNAClientAppModuleConfig *mod_config; for (mod_config = (RNAClientAppModuleConfig *)sflist_first(&pClientAppConfig->module_configs); mod_config; mod_config = (RNAClientAppModuleConfig *)sflist_next(&pClientAppConfig->module_configs)) { if (strcasecmp(mod_config->name, moduleName) == 0) break; } return mod_config; } tRNAClientAppModule *ClientAppGetClientAppModule(RNAClientAppFCN fcn, struct _Detector *userdata, tClientAppConfig *pClientAppConfig) { RNAClientAppRecord *li; for (li=pClientAppConfig->tcp_client_app_list; li; li=li->next) { if ((li->module->validate == fcn) && (li->module->userData == userdata)) return li->module; } for (li=pClientAppConfig->udp_client_app_list; li; li=li->next) { if ((li->module->validate == fcn) && (li->module->userData == userdata)) return li->module; } return NULL; } static void clientCreatePattern(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, unsigned nocase, struct _Detector *userData, const tRNAClientAppModule *li, tClientAppConfig *pClientAppConfig) { void **patterns; int *count; tClientPatternData *pd; if (!li) { _dpd.errMsg( "Invalid client app when registering a pattern"); return; } if (proto == IPPROTO_TCP) { patterns = &pClientAppConfig->tcp_patterns; count = &pClientAppConfig->tcp_pattern_count; } else if (proto == IPPROTO_UDP) { patterns = &pClientAppConfig->udp_patterns; count = &pClientAppConfig->udp_pattern_count; } else { _dpd.errMsg("Invalid protocol when registering a pattern: %u\n",(unsigned)proto); return; } if (!(*patterns)) { *patterns = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF); if (!(*patterns)) { _dpd.errMsg("Error initializing the pattern table for protocol %u\n",(unsigned)proto); return; } } pd = malloc(sizeof(*pd)); if (pd) { pd->ca = li; pd->position = position; (*count)++; pd->next = pClientAppConfig->pattern_data_list; pClientAppConfig->pattern_data_list = pd; _dpd.searchAPI->search_instance_add_ex(*patterns, (const char *)pattern, size, pd, nocase); } else _dpd.errMsg( "Error allocating pattern data"); } static void CClientAppRegisterPattern(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, tAppIdConfig *pConfig) { ClientAppRegisterPattern(fcn, proto, pattern, size, position, 0, NULL, &pConfig->clientAppConfig); } static void CClientAppRegisterPatternNoCase(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, tAppIdConfig *pConfig) { ClientAppRegisterPattern(fcn, proto, pattern, size, position, 1, NULL, &pConfig->clientAppConfig); } static void LuaClientAppRegisterPattern(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, struct _Detector *userData) { ClientAppRegisterPattern(fcn, proto, pattern, size, position, 0, userData, &userData->pAppidNewConfig->clientAppConfig); } void ClientAppRegisterPattern(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, unsigned nocase, struct _Detector *userData, tClientAppConfig *pClientAppConfig) { RNAClientAppRecord *list; RNAClientAppRecord *li; if (proto == IPPROTO_TCP) list = pClientAppConfig->tcp_client_app_list; else if (proto == IPPROTO_UDP) list = pClientAppConfig->udp_client_app_list; else { _dpd.errMsg("Invalid protocol when registering a pattern: %u\n",(unsigned)proto); return; } for (li=list; li; li=li->next) { if ((li->module->validate == fcn) && (li->module->userData == userData)) { clientCreatePattern(fcn, proto, pattern, size, position, nocase, userData, li->module, pClientAppConfig); break; } } } int clientAppLoadForConfigCallback(void *symbol, tClientAppConfig *pClientAppConfig) { static unsigned client_module_index = 0; tRNAClientAppModule *cam = (tRNAClientAppModule *)symbol; RNAClientAppRecord * *list = NULL; RNAClientAppRecord *li; _dpd.debugMsg(DEBUG_LOG,"Adding client %s for protocol %u\n",cam->name, (unsigned)cam->proto); if (client_module_index >= 65536) { _dpd.errMsg( "Maximum number of client modules exceeded"); return -1; } if (cam->proto == IPPROTO_TCP) { list = &pClientAppConfig->tcp_client_app_list; } else if (cam->proto == IPPROTO_UDP) { list = &pClientAppConfig->udp_client_app_list; } else { _dpd.errMsg( "Client %s did not have a valid protocol (%u)", cam->name, (unsigned)cam->proto); return -1; } for (li=*list; li; li=li->next) { if (li->module == cam) break; } if (!li) { if (!(li = calloc(1, sizeof(*li)))) { _dpd.errMsg( "Could not allocate a client app list element"); return -1; } li->next = *list; *list = li; li->module = cam; cam->api = &client_app_api; cam->flow_data_index = client_module_index | APPID_SESSION_DATA_CLIENT_MODSTATE_BIT; client_module_index++; } /*Can't set cam->userData to NULL because Lua detectors use it although C detectors don't */ /*cam->userData = NULL; */ return 0; } int clientAppLoadCallback(void *symbol) { return clientAppLoadForConfigCallback(symbol, &pAppidActiveConfig->clientAppConfig); } int LoadClientAppModules(const char **dir_list, tAppIdConfig *pConfig) { unsigned i; for (i=0; iclientAppConfig)) return -1; } return 0; } static void AddModuleConfigItem(char *module_name, char *item_name, char *item_value, tClientAppConfig *config) { RNAClientAppModuleConfig *mod_config; RNAClientAppModuleConfigItem *item; for (mod_config = (RNAClientAppModuleConfig *)sflist_first(&config->module_configs); mod_config; mod_config = (RNAClientAppModuleConfig *)sflist_next(&config->module_configs)) { if (strcasecmp(mod_config->name, module_name) == 0) break; } if (!mod_config) { mod_config = calloc(1, sizeof(*mod_config)); if (!mod_config) { _dpd.fatalMsg( "Failed to allocate a module configuration"); exit(-1); } mod_config->name = strdup(module_name); if (!mod_config->name) { _dpd.fatalMsg( "Failed to allocate a module configuration name"); exit(-1); } sflist_init(&mod_config->items); if (sflist_add_tail(&config->module_configs, mod_config)) { _dpd.fatalMsg( "Failed to add a module configuration"); exit(-1); } } for (item = (RNAClientAppModuleConfigItem *)sflist_first(&mod_config->items); item; item = (RNAClientAppModuleConfigItem *)sflist_next(&mod_config->items)) { if (strcasecmp(item->name, item_name) == 0) { break; } } if (!item) { item = calloc(1, sizeof(*item)); if (!item) { _dpd.fatalMsg( "Failed to allocate a module configuration item"); exit(-1); } item->name = strdup(item_name); if (!item->name) { _dpd.fatalMsg( "Failed to allocate a module configuration item name"); exit(-1); } if (sflist_add_tail(&mod_config->items, item)) { _dpd.fatalMsg( "Failed to add a module configuration item"); exit(-1); } } if (item->value) { free((void *)item->value); item->value = NULL; } item->value = strdup(item_value); if (!item->value) { _dpd.fatalMsg( "Failed to add a module configuration item value"); exit(-1); } } static void ClientAppParseOption(tClientAppConfig *config, char *key, char *value) { char *p; if(!strcasecmp(key, "enable")) { config->enabled = atoi(value); } else if ((p = strchr(key, ':')) && p[1]) { *p = 0; AddModuleConfigItem(key, &p[1], value, config); *p = ':'; } else { _dpd.debugMsg(DEBUG_LOG, "Unknown client app argument ignored: key(%s) value(%s)", key, value); } } static int ClientAppParseArgs(tClientAppConfig *config, SF_LIST *args) { ConfigItem *ci; for (ci=(ConfigItem *)sflist_first(args); ci; ci=(ConfigItem *)sflist_next(args)) { ClientAppParseOption(config, ci->name, ci->value); } return 0; } #define MAX_DISPLAY_SIZE 65536 static void DisplayClientAppConfig(tClientAppConfig *config) { static char buffer[MAX_DISPLAY_SIZE]; int position = 0; int tmp; RNAClientAppModuleConfig *mod_config; RNAClientAppModuleConfigItem *item; tmp = snprintf(&buffer[position], MAX_DISPLAY_SIZE-position, "\n----------------------------------------------\nRNA Client App Config\n"); if (tmp >= MAX_DISPLAY_SIZE-position) position = MAX_DISPLAY_SIZE; else if (tmp > 0) position += tmp; tmp = snprintf(&buffer[position], MAX_DISPLAY_SIZE-position, "Enabled: %s\n", config->enabled?"Yes":"No"); if (tmp >= MAX_DISPLAY_SIZE-position) position = MAX_DISPLAY_SIZE; else if (tmp > 0) position += tmp; for (mod_config = (RNAClientAppModuleConfig *)sflist_first(&config->module_configs); mod_config; mod_config = (RNAClientAppModuleConfig *)sflist_next(&config->module_configs)) { tmp = snprintf(&buffer[position], MAX_DISPLAY_SIZE-position, "%s\n", mod_config->name); if (tmp >= MAX_DISPLAY_SIZE-position) position = MAX_DISPLAY_SIZE; else if (tmp > 0) position += tmp; for (item = (RNAClientAppModuleConfigItem *)sflist_first(&mod_config->items); item; item = (RNAClientAppModuleConfigItem *)sflist_next(&mod_config->items)) { tmp = snprintf(&buffer[position], MAX_DISPLAY_SIZE-position, " %s: %s\n", item->name, item->value); if (tmp >= MAX_DISPLAY_SIZE-position) position = MAX_DISPLAY_SIZE; else if (tmp > 0) position += tmp; } } tmp = snprintf(&buffer[position], MAX_DISPLAY_SIZE-position, "----------------------------------------------\n"); if (tmp >= MAX_DISPLAY_SIZE-position) position = MAX_DISPLAY_SIZE; else if (tmp > 0) position += tmp; _dpd.debugMsg(DEBUG_LOG,"%s\n",buffer); } static void free_module_config_item(void *module_config_item) { RNAClientAppModuleConfigItem *item = (RNAClientAppModuleConfigItem *)module_config_item; if (item) { if (item->name) free((void *)item->name); if (item->value) free((void *)item->value); free(item); } } static void free_module_config(void *module_config) { RNAClientAppModuleConfig *config = (RNAClientAppModuleConfig *)module_config; if (config) { if (config->name) free((void *)config->name); sflist_static_free_all(&config->items, &free_module_config_item); free(config); } } static void initialize_module(RNAClientAppRecord *li, tClientAppConfig *pClientAppConfig) { RNAClientAppModuleConfig *mod_config; int rval; for (mod_config = (RNAClientAppModuleConfig *)sflist_first(&pClientAppConfig->module_configs); mod_config; mod_config = (RNAClientAppModuleConfig *)sflist_next(&pClientAppConfig->module_configs)) { if (strcasecmp(mod_config->name, li->module->name) == 0) break; } if (li->module->init && (rval=li->module->init(&client_init_api, mod_config ? &mod_config->items:NULL)) != CLIENT_APP_SUCCESS) { _dpd.fatalMsg("Could not initialize the %s client app element: %d\n",li->module->name, rval); exit(-1); } } static void finalize_module(RNAClientAppRecord *li) { int rval; if (li->module->finalize && (rval=li->module->finalize(&finalize_api)) != CLIENT_APP_SUCCESS) { _dpd.fatalMsg("Could not finlize the %s client app element: %d\n",li->module->name, rval); exit(-1); } } static void clean_module(RNAClientAppRecord *li) { if (li->module->clean) li->module->clean(&clean_api); } void UnconfigureClientApp(tAppIdConfig *pConfig) { tClientPatternData *pd; RNAClientAppRecord *li; clean_api.pAppidConfig = pConfig; for (li = pConfig->clientAppConfig.tcp_client_app_list; li; li = li->next) clean_module(li); for (li = pConfig->clientAppConfig.udp_client_app_list; li; li = li->next) clean_module(li); if (pConfig->clientAppConfig.tcp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->clientAppConfig.tcp_patterns); pConfig->clientAppConfig.tcp_patterns = NULL; } if (pConfig->clientAppConfig.udp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->clientAppConfig.udp_patterns); pConfig->clientAppConfig.udp_patterns = NULL; } while (pConfig->clientAppConfig.pattern_data_list) { pd = pConfig->clientAppConfig.pattern_data_list; pConfig->clientAppConfig.pattern_data_list = pd->next; free((void *)pd); } CleanHttpPatternLists(pConfig); ssl_detector_free_patterns(&pConfig->serviceSslConfig); dns_detector_free_patterns(&pConfig->serviceDnsConfig); CleanClientPortPatternList(pConfig); sflist_static_free_all(&pConfig->clientAppConfig.module_configs, &free_module_config); } /** * Initialize the configuration of the client app module * * @param args */ void ClientAppInit(tAppidStaticConfig* appidSC, tAppIdConfig *pConfig) { RNAClientAppRecord *li; sflist_init(&pConfig->clientAppConfig.module_configs); pConfig->clientAppConfig.enabled = 1; ClientAppParseArgs(&pConfig->clientAppConfig, &pConfig->client_app_args); DisplayClientAppConfig(&pConfig->clientAppConfig); if (pConfig->clientAppConfig.enabled) { client_init_api.debug = app_id_debug; client_init_api.pAppidConfig = pConfig; client_init_api.instance_id = appidSC->instance_id; for (li = pConfig->clientAppConfig.tcp_client_app_list; li; li = li->next) initialize_module(li, &pConfig->clientAppConfig); for (li = pConfig->clientAppConfig.udp_client_app_list; li; li = li->next) initialize_module(li, &pConfig->clientAppConfig); luaModuleInitAllClients(); for (li = pConfig->clientAppConfig.tcp_client_app_list; li; li = li->next) finalize_module(li); for (li = pConfig->clientAppConfig.udp_client_app_list; li; li = li->next) finalize_module(li); } } void ClientAppFinalize(tAppIdConfig *pConfig) { if (pConfig->clientAppConfig.enabled) { if (pConfig->clientAppConfig.tcp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->clientAppConfig.tcp_patterns); } if (pConfig->clientAppConfig.udp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->clientAppConfig.udp_patterns); } } } typedef struct _CLIENT_APP_MATCH { struct _CLIENT_APP_MATCH *next; unsigned count; const tRNAClientAppModule *ca; } ClientAppMatch; static ClientAppMatch *match_free_list; /** * Clean up the configuration of the client app module */ void CleanupClientApp(tAppIdConfig *pConfig) { #ifdef APPID_FULL_CLEANUP ClientAppMatch *match; tClientPatternData *pd; RNAClientAppRecord *li; clean_api.pAppidConfig = pConfig; if (pConfig->clientAppConfig.tcp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->clientAppConfig.tcp_patterns); pConfig->clientAppConfig.tcp_patterns = NULL; } if (pConfig->clientAppConfig.udp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->clientAppConfig.udp_patterns); pConfig->clientAppConfig.udp_patterns = NULL; } while ((pd = pConfig->clientAppConfig.pattern_data_list) != NULL) { pConfig->clientAppConfig.pattern_data_list = pd->next; free(pd); } while ((li=pConfig->clientAppConfig.tcp_client_app_list) != NULL) { pConfig->clientAppConfig.tcp_client_app_list = li->next; if (li->module->clean) li->module->clean(&clean_api); free(li); } while ((li=pConfig->clientAppConfig.udp_client_app_list) != NULL) { pConfig->clientAppConfig.udp_client_app_list = li->next; if (li->module->clean) li->module->clean(&clean_api); free(li); } luaModuleCleanAllClients(); CleanHttpPatternLists(pConfig); ssl_detector_free_patterns(&pConfig->serviceSslConfig); dns_detector_free_patterns(&pConfig->serviceDnsConfig); CleanClientPortPatternList(pConfig); sflist_static_free_all(&pConfig->clientAppConfig.module_configs, &free_module_config); while ((match=match_free_list) != NULL) { match_free_list = match->next; free(match); } #endif } /* * Callback function for string search * * @param id id in array of search strings from pop_config.cmds * @param index index in array of search strings from pop_config.cmds * @param data buffer passed in to search function * * @return response * @retval 1 commands caller to stop searching */ static int pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { ClientAppMatch * *matches = (ClientAppMatch **)data; tClientPatternData *pd = (tClientPatternData *)id; ClientAppMatch *cam; if (pd->position >= 0 && pd->position != index) return 0; for (cam=*matches; cam; cam=cam->next) { if (cam->ca == pd->ca) break; } if (cam) cam->count++; else { if (match_free_list) { cam = match_free_list; match_free_list = cam->next; memset(cam, 0, sizeof(*cam)); } else cam = calloc(1, sizeof(*cam)); if (cam) { cam->count = 1; cam->ca = pd->ca; cam->next = *matches; *matches = cam; } else { _dpd.errMsg( "Error allocating a client app match structure"); } } return 0; } void AppIdAddClientApp(SFSnortPacket *p, int direction, const tAppIdConfig *pConfig, tAppIdData *flowp, tAppId service_id, tAppId id, const char *version) { tAppId tmpAppId = flowp->clientAppId; tAppId tmpServiceAppId = flowp->clientServiceAppId; if (version) { if (flowp->clientVersion) { if (strcmp(version, flowp->clientVersion)) { free(flowp->clientVersion); flowp->clientVersion = strdup(version); if (!flowp->clientVersion) _dpd.errMsg("failed to allocate client version name"); } } else { flowp->clientVersion = strdup(version); if (!flowp->clientVersion) _dpd.errMsg("failed to allocate client version name"); } } setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); flowp->clientServiceAppId = service_id; flowp->clientAppId = id; checkSandboxDetection(id); if (id > APP_ID_NONE && tmpAppId != id) CheckDetectorCallback(p, flowp, (APPID_SESSION_DIRECTION) direction, id, pConfig); if (service_id > APP_ID_NONE && tmpServiceAppId != service_id) CheckDetectorCallback(p, flowp, (APPID_SESSION_DIRECTION) direction, service_id, pConfig); } static void AppIdAddClientAppInfo(tAppIdData *flowp, const char *info) { if (flowp->hsession && !flowp->hsession->url) { flowp->hsession->url = strdup(info); if (!flowp->hsession->url) _dpd.errMsg("failed to allocate url"); } } static ClientAppMatch *BuildClientPatternList(const SFSnortPacket *pkt, uint32_t protocol, const tClientAppConfig *pClientAppConfig) { ClientAppMatch *match_list = NULL; void *patterns; if (protocol == IPPROTO_TCP) patterns = pClientAppConfig->tcp_patterns; else patterns = pClientAppConfig->udp_patterns; if (!patterns) return NULL; _dpd.searchAPI->search_instance_find_all(patterns, (char *)pkt->payload, pkt->payload_size, 0, &pattern_match, (void*)&match_list ); return match_list; } static const tRNAClientAppModule *GetNextFromClientPatternList(ClientAppMatch **match_list) { ClientAppMatch *curr = NULL; ClientAppMatch *prev = NULL; ClientAppMatch *max_curr = NULL; ClientAppMatch *max_prev = NULL; unsigned max_count; unsigned max_precedence; curr = *match_list; max_count = 0; max_precedence = 0; while (curr) { if (curr->count >= curr->ca->minimum_matches && ((curr->count > max_count) || (curr->count == max_count && curr->ca->precedence > max_precedence))) { max_count = curr->count; max_precedence = curr->ca->precedence; max_curr = curr; max_prev = prev; } prev = curr; curr = curr->next; } if (max_curr != NULL) { if (max_prev == NULL) { *match_list = (*match_list)->next; } else { max_prev->next = max_curr->next; } max_curr->next = match_free_list; match_free_list = max_curr; return max_curr->ca; } else { return NULL; } } static void FreeClientPatternList(ClientAppMatch **match_list) { ClientAppMatch *cam; ClientAppMatch *tmp; cam = *match_list; while (cam) { tmp = cam; cam = tmp->next; tmp->next = match_free_list; match_free_list = tmp; } *match_list = NULL; } /** * The process to determine the running client app given the packet data. * * @param p packet to process */ static void ClientAppID(SFSnortPacket *p, const int dir, tAppIdData *flowp, const tAppIdConfig *pConfig) { const tRNAClientAppModule *client = NULL; ClientAppMatch *match_list; #ifdef CLIENT_APP_DEBUG _dpd.logMsg( "Client"); #endif if (!p->payload_size) return; if (flowp->clientData != NULL) return; if (flowp->candidate_client_list != NULL) { if (flowp->num_candidate_clients_tried > 0) return; } else { if (!(flowp->candidate_client_list = malloc(sizeof(SF_LIST)))) { _dpd.errMsg("Could not allocate a candidate client list."); return; } sflist_init(flowp->candidate_client_list); flowp->num_candidate_clients_tried = 0; } match_list = BuildClientPatternList(p, flowp->proto, &pConfig->clientAppConfig); while (flowp->num_candidate_clients_tried < MAX_CANDIDATE_CLIENTS) { const tRNAClientAppModule *tmp = GetNextFromClientPatternList(&match_list); if (tmp != NULL) { client = sflist_first(flowp->candidate_client_list); while (client && (client != tmp)) client = sflist_next(flowp->candidate_client_list); if (client == NULL) { sflist_add_tail(flowp->candidate_client_list, (void*)tmp); flowp->num_candidate_clients_tried++; #ifdef CLIENT_APP_DEBUG _dpd.logMsg("Using %s from pattern match", tmp ? tmp->name:\n",ULL"); #endif } } else { break; } } FreeClientPatternList(&match_list); } int AppIdDiscoverClientApp(SFSnortPacket *p, int direction, tAppIdData *rnaData, const tAppIdConfig *pConfig) { if (!pConfig->clientAppConfig.enabled) return APPID_SESSION_SUCCESS; if (direction == APP_ID_FROM_INITIATOR) { /* get out if we've already tried to validate a client app */ if (!getAppIdFlag(rnaData, APPID_SESSION_CLIENT_DETECTED)) ClientAppID(p, direction, rnaData, pConfig); } else if (rnaData->rnaServiceState != RNA_STATE_STATEFUL && getAppIdFlag(rnaData, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS)) ClientAppID(p, direction, rnaData, pConfig); return APPID_SESSION_SUCCESS; } DetectorAppUrlList *getAppUrlList(tAppIdConfig *pConfig) { HttpPatternLists* patternLists = &pConfig->httpPatternLists; return (&patternLists->appUrlList); } static void *client_app_flowdata_get(tAppIdData *flowp, unsigned client_id) { return AppIdFlowdataGet(flowp, client_id); } static int client_app_flowdata_add(tAppIdData *flowp, void *data, unsigned client_id, AppIdFreeFCN fcn) { return AppIdFlowdataAdd(flowp, data, client_id, fcn); } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_ym.h0000644000175000017500000000205314241075375025333 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __CLIENT_APP_YM_H__ #define __CLIENT_APP_YM_H__ #include "client_app_api.h" extern tRNAClientAppModule ym_client_mod; #endif /* __CLIENT_APP_YM_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_msn.h0000644000175000017500000000205714241075364025505 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __CLIENT_APP_MSN_H__ #define __CLIENT_APP_MSN_H__ #include "client_app_api.h" extern tRNAClientAppModule msn_client_mod; #endif /* __CLIENT_APP_MSN_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_api.h0000644000175000017500000001213214241075356025455 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __CLIENT_APP_API_H__ #define __CLIENT_APP_API_H__ #include "flow.h" #include "commonAppMatcher.h" struct _Detector; // Forward declaration for AppId config. Cannot include appIdConfig.h because of // circular dependency struct appIdConfig_; typedef enum { CLIENT_APP_SUCCESS = 0, CLIENT_APP_INPROCESS = 10, CLIENT_APP_ENULL = -10, CLIENT_APP_EINVALID = -11, CLIENT_APP_ENOMEM = -12 } CLIENT_APP_RETCODE; typedef struct { const char *name; SF_LIST items; } RNAClientAppModuleConfig; typedef struct { const char *name; const char *value; } RNAClientAppModuleConfigItem; typedef CLIENT_APP_RETCODE (*RNAClientAppFCN)(const uint8_t *, uint16_t, const int, tAppIdData *session, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); typedef int (*RNAClientAppCallbackFCN)(const uint8_t *, uint16_t, const int, tAppIdData *session, const SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); typedef struct _INIT_CLIENT_API { void (*RegisterPattern)(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, struct appIdConfig_ *pConfig); void (*RegisterPatternEx)(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, struct _Detector *userdata); void (*RegisterPatternNoCase)(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, struct appIdConfig_ *pConfig); void (*RegisterAppId)(RNAClientAppFCN fcn, tAppId appId, uint32_t additionalInfo, struct appIdConfig_ *pConfig); void (*RegisterDetectorCallback)(RNAClientAppCallbackFCN fcn, tAppId appId, struct _Detector *userdata, struct appIdConfig_ *pConfig); int debug; uint32_t instance_id; struct appIdConfig_ *pAppidConfig; ///< AppId context for which this API should be used } InitClientAppAPI; typedef struct _CLEAN_CLIENT_API { struct appIdConfig_ *pAppidConfig; ///< AppId context for which this API should be used } CleanClientAppAPI; typedef struct _FINALIZE_CLIENT_API { void* data; } FinalizeClientAppAPI; typedef CLIENT_APP_RETCODE (*RNAClientAppInitFCN)(const InitClientAppAPI * const, SF_LIST *config); typedef CLIENT_APP_RETCODE (*RNAClientAppFinalizeFCN)(const FinalizeClientAppAPI * const); typedef void (*RNAClientAppCleanFCN)(const CleanClientAppAPI * const); typedef void *(*ClientAppFlowdataGet)(tAppIdData *, unsigned); typedef int (*ClientAppFlowdataAdd)(tAppIdData *, void *, unsigned, AppIdFreeFCN); typedef void (*ClientAppAddApp)(SFSnortPacket *p, int direction, const struct appIdConfig_ *pConfig, tAppIdData *, tAppId, tAppId, const char *); typedef void (*ClientAppAddInfo)(tAppIdData *, const char *); typedef void (*ClientAppAddUser)(tAppIdData *, const char *, tAppId, int); typedef void (*ClientAppAddPayload) (tAppIdData *, tAppId); typedef struct _CLIENT_APP_API { ClientAppFlowdataGet data_get; ClientAppFlowdataAdd data_add; ClientAppAddApp add_app; ClientAppAddInfo add_info; ClientAppAddUser add_user; ClientAppAddPayload add_payload; } ClientAppApi; typedef struct RNAClientAppRecord_ { struct RNAClientAppRecord_ *next; struct RNAClientAppModule *module; } RNAClientAppRecord; typedef struct RNAClientAppModule { const char *name; uint8_t proto; RNAClientAppInitFCN init; RNAClientAppCleanFCN clean; RNAClientAppFCN validate; RNAClientAppCallbackFCN detectorCallback; bool detectorContext; unsigned minimum_matches; const ClientAppApi *api; struct _Detector* userData; /**precedence of this detector.*/ unsigned int precedence; RNAClientAppFinalizeFCN finalize; int provides_user; unsigned flow_data_index; } tRNAClientAppModule; typedef struct _RNA_CLIENT_APP_APPID_SESSION_STATE { struct _RNA_CLIENT_APP_APPID_SESSION_STATE *next; const tRNAClientAppModule *ca; } RNAClientAppFlowState; #endif /* __CLIENT_APP_API_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_msn.c0000644000175000017500000001433414241075363025500 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "client_app_api.h" #include "client_app_msn.h" typedef struct _MSN_CLIENT_APP_CONFIG { int enabled; } MSN_CLIENT_APP_CONFIG; static MSN_CLIENT_APP_CONFIG msn_config; #define MAX_VERSION_SIZE 64 static CLIENT_APP_RETCODE msn_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE msn_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); tRNAClientAppModule msn_client_mod = { .name = "MSN", .proto = IPPROTO_TCP, .init = &msn_init, .validate = &msn_validate, .minimum_matches = 2 }; typedef struct { const uint8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static const uint8_t VER[] = "VER "; static const uint8_t CVRMAIN[] = "CVR0\x00d\x00a"; static const uint8_t CVR[] = "CVR"; static const uint8_t MSNMSGR[] = "MSNMSGR"; static const uint8_t MACMSGS[] = "macmsgs"; static const uint8_t MSMSGS[] = "MSMSGS"; static Client_App_Pattern patterns[] = { {VER, sizeof(VER)-1, 0, APP_ID_MSN}, {CVRMAIN, sizeof(CVRMAIN)-1, -1, APP_ID_MSN}, {MSNMSGR, sizeof(MSNMSGR)-1, -1, APP_ID_MSN_MESSENGER}, {MACMSGS, sizeof(MACMSGS)-1, -1, APP_ID_MSN_MESSENGER}, {MSMSGS, sizeof(MSMSGS)-1, -1, APP_ID_MICROSOFT_WINDOWS_MESSENGER} }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_MICROSOFT_WINDOWS_MESSENGER, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_MSN_MESSENGER, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_MSN, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_MSNP, APPINFO_FLAG_CLIENT_ADDITIONAL} }; static CLIENT_APP_RETCODE msn_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; msn_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { msn_config.enabled = atoi(item->value); } } } if (msn_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&msn_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&msn_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE msn_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { const u_int8_t *end; u_int8_t version[MAX_VERSION_SIZE]; u_int8_t *v; u_int8_t *v_end; uint32_t product_id; product_id = APP_ID_MSN_MESSENGER; memset(&version,0,sizeof(version)); if (!data || !msn_client_mod.api || !flowp || !pkt) return CLIENT_APP_ENULL; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; if (size >= sizeof(CVR) && memcmp(data, CVR, sizeof(CVR)-1) == 0) { int space_count = 0; end = data + size; while( data < end && space_count < 6 ) /* Skip to the product and version strings */ { if( *data == ' ' ) space_count++; data++; } /* Get the product */ if( end-data >= (int)sizeof(MSNMSGR) && memcmp(data, MSNMSGR, sizeof(MSNMSGR)-1) == 0 ) { product_id = APP_ID_MSN_MESSENGER; data += sizeof(MSNMSGR) - 1; data++; /* skip the space */ } else if( end-data >= (int)sizeof(MACMSGS) && memcmp(data, MACMSGS, sizeof(MACMSGS)-1) == 0 ) { product_id = APP_ID_MSN_MESSENGER; data += sizeof(MACMSGS) - 1; data++; /* skip the space */ } else if( end-data >= (int)sizeof(MSMSGS) && memcmp(data, MSMSGS, sizeof(MSMSGS)-1) == 0 ) { product_id = APP_ID_MICROSOFT_WINDOWS_MESSENGER; data += sizeof(MSMSGS) - 1; data++; /* skip the space */ } else /* advance past the unknown product name */ { while( data < end && *data != ' ') data++; data++; /* skip the space */ } v = version; v_end = v + (MAX_VERSION_SIZE - 1); /* Get the version */ while( data < end && *data != ' ' && v < v_end ) { *v = *data; v++; data++; } goto done; } return CLIENT_APP_INPROCESS; done: msn_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_MSN_MESSENGER, product_id, (char *)version); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_timbuktu.c0000644000175000017500000001554614241075371026554 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "client_app_api.h" static const char TIMBUKTU_BANNER[] = "\000\001"; #define TIMBUKTU_BANNER_LEN (sizeof(TIMBUKTU_BANNER)-1) #define MAX_ANY_SIZE 2 typedef enum { TIMBUKTU_STATE_BANNER = 0, TIMBUKTU_STATE_ANY_MESSAGE_LEN, TIMBUKTU_STATE_MESSAGE_LEN, TIMBUKTU_STATE_MESSAGE_DATA } TIMBUKTUState; typedef struct _CLIENT_TIMBUKTU_DATA { TIMBUKTUState state; uint16_t stringlen; unsigned pos; union { uint16_t len; uint8_t raw_len[2]; }l; } ClientTIMBUKTUData; #pragma pack(1) typedef struct _CLIENT_TIMBUKTU_MSG { uint16_t len; uint8_t message; } ClientTIMBUKTUMsg; #pragma pack() typedef struct _TIMBUKTU_CLIENT_APP_CONFIG { int enabled; } TIMBUKTU_CLIENT_APP_CONFIG; static TIMBUKTU_CLIENT_APP_CONFIG timbuktu_config; static CLIENT_APP_RETCODE timbuktu_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE timbuktu_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); SF_SO_PUBLIC tRNAClientAppModule timbuktu_client_mod = { .name = "TIMBUKTU", .proto = IPPROTO_TCP, .init = &timbuktu_init, .validate = &timbuktu_validate, .minimum_matches = 1 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)TIMBUKTU_BANNER, sizeof(TIMBUKTU_BANNER)-1, 0, APP_ID_TIMBUKTU}, }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_TIMBUKTU, 0}}; static CLIENT_APP_RETCODE timbuktu_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; timbuktu_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { timbuktu_config.enabled = atoi(item->value); } } } if (timbuktu_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&timbuktu_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&timbuktu_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE timbuktu_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { ClientTIMBUKTUData *fd; uint16_t offset; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; fd = timbuktu_client_mod.api->data_get(flowp, timbuktu_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (timbuktu_client_mod.api->data_add(flowp, fd, timbuktu_client_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } fd->state = TIMBUKTU_STATE_BANNER; } offset = 0; while(offset < size) { switch (fd->state) { case TIMBUKTU_STATE_BANNER: if(data[offset] != TIMBUKTU_BANNER[fd->pos]) return CLIENT_APP_EINVALID; if(fd->pos >= TIMBUKTU_BANNER_LEN-1) { fd->pos = 0; fd->state = TIMBUKTU_STATE_ANY_MESSAGE_LEN; break; } fd->pos++; break; /* cheeck any 2 bytes fisrt */ case TIMBUKTU_STATE_ANY_MESSAGE_LEN: fd->pos++; if(fd->pos >= MAX_ANY_SIZE) { fd->pos = 0; fd->state = TIMBUKTU_STATE_MESSAGE_LEN; break; } break; case TIMBUKTU_STATE_MESSAGE_LEN: if(fd->pos < offsetof(ClientTIMBUKTUMsg, message)) { fd->l.raw_len[fd->pos] = data[offset]; } fd->pos++; if(fd->pos >= offsetof(ClientTIMBUKTUMsg, message)) { fd->stringlen = ntohs(fd->l.len); if(!fd->stringlen) { if(offset == size - 1) goto done; return CLIENT_APP_EINVALID; } else if((fd->stringlen + TIMBUKTU_BANNER_LEN + MAX_ANY_SIZE + offsetof(ClientTIMBUKTUMsg, message)) > size) return CLIENT_APP_EINVALID; fd->state = TIMBUKTU_STATE_MESSAGE_DATA; fd->pos = 0; } break; case TIMBUKTU_STATE_MESSAGE_DATA: fd->pos++; if(fd->pos == fd->stringlen) { if(offset == size - 1) goto done; return CLIENT_APP_EINVALID; } break; default: goto inprocess; } offset++; } inprocess: return CLIENT_APP_INPROCESS; done: timbuktu_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_TIMBUKTU, APP_ID_TIMBUKTU, NULL); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_ym.c0000644000175000017500000001316114241075374025327 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "client_app_api.h" #include "client_app_ym.h" #include "appInfoTable.h" typedef struct _YM_CLIENT_APP_CONFIG { int enabled; } YM_CLIENT_APP_CONFIG; static YM_CLIENT_APP_CONFIG ym_config; #define MAX_VERSION_SIZE 64 static CLIENT_APP_RETCODE ym_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE ym_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); tRNAClientAppModule ym_client_mod = { .name = "YM", .proto = IPPROTO_TCP, .init = &ym_init, .validate = &ym_validate, .minimum_matches = 1 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static const uint8_t APP_YMSG[] = "YMSG"; static Client_App_Pattern patterns[] = { {APP_YMSG, sizeof(APP_YMSG)-1, 0, APP_ID_YAHOO_MSG}, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_YAHOO, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_YAHOO_MSG, APPINFO_FLAG_CLIENT_ADDITIONAL} }; static CLIENT_APP_RETCODE ym_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; ym_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { ym_config.enabled = atoi(item->value); } } } if (ym_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char*)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&ym_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&ym_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static const uint8_t * skip_separator(const uint8_t *data, const uint8_t *end) { while( data + 1 < end ) { if( data[0] == 0xc0 && data[1] == 0x80 ) break; data++; } data += 2; return data; } static CLIENT_APP_RETCODE ym_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { #define HEADERSIZE 20 #define VERSIONID "135" #define SEPARATOR 0xc080 const uint8_t *end; uint16_t len; uint8_t version[MAX_VERSION_SIZE]; uint8_t *v; uint8_t *v_end; uint32_t product_id; product_id = APP_ID_YAHOO; memset(&version,0,sizeof(version)); _dpd.debugMsg(DEBUG_LOG,"Found yahoo! client: %zu\n",sizeof(VERSIONID)); if (!data || !ym_client_mod.api || !flowp || !pkt) return CLIENT_APP_ENULL; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; /* Validate the packet using the length field, otherwise abort. */ if( size < 10 ) return CLIENT_APP_ENULL; len = *((uint16_t*) (data + 8)); len = ntohs(len); if( len != (size - HEADERSIZE) ) return CLIENT_APP_ENULL; end = data + size; if( size >= HEADERSIZE ) { data += HEADERSIZE; } while( data < end ) { if( end-data >= (int)sizeof(VERSIONID) && memcmp(data, VERSIONID, sizeof(VERSIONID)-1) == 0 ) { data += sizeof(VERSIONID)-1; if( data + 2 >= end ) /* Skip the separator */ goto done; else data += 2; product_id = APP_ID_YAHOO; v = version; v_end = v + (MAX_VERSION_SIZE - 1); /* Get the version */ while( data + 1 < end && v < v_end ) { if( data[0] == 0xc0 && data[1] == 0x80 ) break; *v = *data; v++; data++; } goto done; } data = skip_separator(data,end); /*skip to the command end separator */ data = skip_separator(data,end); /* skip to the command data end separator */ } return CLIENT_APP_INPROCESS; done: ym_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_YAHOO, product_id, (char *)version); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_base.h0000644000175000017500000000560114241075360025614 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __CLIENT_APP_BASE_H__ #define __CLIENT_APP_BASE_H__ #include "client_app_api.h" #include "commonAppMatcher.h" #include "httpCommon.h" #include "flow.h" #include "appIdConfig.h" #define GENERIC_APP_OFFSET 2000000000 void ClientAppInit(tAppidStaticConfig* appidSC, tAppIdConfig *pConfig); void ClientAppFinalize(tAppIdConfig *pConfig); void UnconfigureClientApp(tAppIdConfig *pConfig); void CleanupClientApp(tAppIdConfig *pConfig); int clientAppLoadCallback(void *symbol); int clientAppLoadForConfigCallback(void *symbol, tClientAppConfig *pConfig); int LoadClientAppModules(const char **dir_list, tAppIdConfig *pConfig); void ClientAppRegisterPattern(RNAClientAppFCN fcn, uint8_t proto, const uint8_t * const pattern, unsigned size, int position, unsigned nocase, struct _Detector *userData, tClientAppConfig *pConfig); const ClientAppApi *getClientApi(void); RNAClientAppModuleConfig * getClientAppModuleConfig(const char *moduleName, tClientAppConfig *pConfig); int AppIdDiscoverClientApp(SFSnortPacket *p, int direction, tAppIdData *rnaData, const tAppIdConfig *pConfig); void AppIdAddClientApp(SFSnortPacket *p, int direction, const tAppIdConfig *pConfig, tAppIdData *flowp, tAppId service_id, tAppId id, const char *version); DetectorAppUrlList *getAppUrlList(tAppIdConfig *pConfig); tRNAClientAppModule *ClientAppGetClientAppModule(RNAClientAppFCN fcn, struct _Detector *userdata, tClientAppConfig *pConfig); int sipUaPatternAdd( tAppId clientAppId, const char* clientVersion, const char* uaPattern, tDetectorSipConfig *pSipConfig ); int sipServerPatternAdd( tAppId clientAppId, const char* clientVersion, const char* uaPattern, tDetectorSipConfig *pSipConfig ); int sipUaFinalize(tDetectorSipConfig *pSipConfig); int sipServerFinalize(void); int portPatternFinalize(tAppIdConfig *pConfig); #endif /* __CLIENT_APP_BASE_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_ssh.c0000644000175000017500000004504614241075370025502 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "client_app_api.h" #include static const char SSH_CLIENT_BANNER[] = "SSH-"; #define SSH_CLIENT_BANNER_LEN (sizeof(SSH_CLIENT_BANNER)-1) #define SSH_CLIENT_BANNER_MAXPOS (sizeof(SSH_CLIENT_BANNER)-2) static const char DROPBEAR_BANNER[] = "dropbear"; #define DROPBEAR_BANNER_MAXPOS (sizeof(DROPBEAR_BANNER)-2) static const char LSH_BANNER[] = "lsh"; #define LSH_BANNER_MAXPOS (sizeof(LSH_BANNER)-2) static const char OPENSSH_BANNER[] = "OpenSSH"; #define OPENSSH_BANNER_MAXPOS (sizeof(OPENSSH_BANNER)-2) static const char PUTTY_BANNER[] = "PuTTY"; #define PUTTY_BANNER_MAXPOS (sizeof(PUTTY_BANNER)-2) #define SSH_MSG_KEYXINIT 20 #define SSH_MSG_IGNORE 2 #define SSH_MSG_SESSION_KEY 3 #define SSH_MAX_BANNER_LEN 255 #define SSH2 2 #define SSH1 1 typedef enum { SSH_CLIENT_STATE_BANNER = 0, SSH_CLIENT_STATE_ID_PROTO_VERSION, SSH_CLIENT_STATE_LOOKING_FOR_DASH, SSH_CLIENT_STATE_ID_CLIENT, SSH_CLIENT_STATE_CHECK_OPENSSH, SSH_CLIENT_STATE_CHECK_PUTTY, SSH_CLIENT_STATE_CHECK_LSH, SSH_CLIENT_STATE_CHECK_DROPBEAR, SSH_CLIENT_STATE_ID_SOFTWARE_VERSION, SSH_CLIENT_STATE_ID_REST_OF_LINE, SSH_CLIENT_STATE_KEY } SSHClientState; typedef enum { SSH2_HEADER_BEGIN, SSH2_HEADER_PLEN, SSH2_HEADER_CODE, SSH2_IGNORE, SSH2_PADDING, SSH2_KEYX_HEADER_FINISH, SSH2_FIELD_LEN_BEGIN, SSH2_FIELD_DATA_BEGIN, SSH2_PAYLOAD_BEGIN } SSH2HeaderState; typedef enum { SSH1_HEADER_BEGIN, SSH1_HEADER_PLEN, SSH1_HEADER_FIND_CODE, SSH1_HEADER_CODE, SSH1_SESSION_KEY } SSH1HeaderState; typedef struct _CLIENT_SSH_DATA { SSHClientState state; SSH2HeaderState hstate; SSH1HeaderState oldhstate; unsigned len; unsigned pos; unsigned field; unsigned field_len; unsigned read_data; union { uint32_t len; uint8_t raw_len[4]; } l; unsigned ssh_version; uint8_t version[SSH_MAX_BANNER_LEN]; uint8_t plen; uint8_t code; uint32_t client_id; } ClientSSHData; #pragma pack(1) typedef struct _CLIENT_SSH_KEY_STRING { uint32_t len; uint8_t data; } ClientSSHKeyString; typedef struct _CLIENT_SSH_MSG { uint32_t len; uint8_t plen; uint8_t code; } ClientSSHMsg; typedef struct _CLIENT_SSH2_KEY_EXCHANGE { ClientSSHMsg msg; uint8_t cookie[16]; } ClientSSH2KeyExchange; typedef struct _CLIENT_SSH1_KEY_EXCHANGE_V1 { uint32_t len; uint8_t code; } ClientSSH1KeyExchangeV1; typedef struct _CLIENT_SSH_KEY_EXCHANGE_FINAL { uint8_t kex_pkt; uint32_t future; } ClientSSHKeyExchangeFinal; #pragma pack() typedef struct _SSH_CLIENT_CONFIG { int enabled; } SSH_CLIENT_CONFIG; static SSH_CLIENT_CONFIG ssh_client_config; static CLIENT_APP_RETCODE ssh_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE ssh_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); SF_SO_PUBLIC tRNAClientAppModule ssh_client_mod = { .name = "SSH", .proto = IPPROTO_TCP, .init = &ssh_client_init, .validate = &ssh_client_validate, .minimum_matches = 1 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)SSH_CLIENT_BANNER, sizeof(SSH_CLIENT_BANNER)-1, 0, APP_ID_SSH}, {(const uint8_t *)OPENSSH_BANNER, sizeof(OPENSSH_BANNER)-1, 0, APP_ID_OPENSSH}, {(const uint8_t *)PUTTY_BANNER, sizeof(PUTTY_BANNER)-1, 0, APP_ID_PUTTY}, {(const uint8_t *)LSH_BANNER, sizeof(LSH_BANNER)-1, 0, APP_ID_LSH}, {(const uint8_t *)DROPBEAR_BANNER, sizeof(DROPBEAR_BANNER)-1, 0, APP_ID_DROPBEAR}, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_DROPBEAR, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_SSH, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_LSH, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_PUTTY, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_OPENSSH, APPINFO_FLAG_CLIENT_ADDITIONAL} }; static CLIENT_APP_RETCODE ssh_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; ssh_client_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { ssh_client_config.enabled = atoi(item->value); } } } if (ssh_client_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG, "registering patterns: %s: %d", (const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&ssh_client_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&ssh_client_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static inline CLIENT_APP_RETCODE ssh_client_validate_keyx(uint16_t offset, const uint8_t *data, uint16_t size, ClientSSHData *fd) { const ClientSSHMsg *ckx; const ClientSSHKeyString *cks; const ClientSSH2KeyExchange *ckex; while (offset < size) { switch (fd->hstate) { case SSH2_HEADER_BEGIN: fd->l.raw_len[fd->pos] = data[offset]; fd->pos++; if(fd->pos == sizeof(ckx->len)) { fd->len = ntohl(fd->l.len) ; fd->hstate = SSH2_HEADER_PLEN; } break; case SSH2_HEADER_PLEN: fd->plen = data[offset]; fd->hstate = SSH2_HEADER_CODE; fd->pos++; break; case SSH2_HEADER_CODE: fd->code = data[offset]; if (fd->code == SSH_MSG_KEYXINIT) { fd->pos = 0; fd->hstate = SSH2_KEYX_HEADER_FINISH; fd->read_data = fd->plen + sizeof(ckex->cookie) + sizeof(ckx->len); } else if (fd->code == SSH_MSG_IGNORE) { fd->pos = sizeof(ckx->len) + 2; fd->hstate = SSH2_IGNORE; } else return CLIENT_APP_EINVALID; fd->len = ntohl(fd->l.len) + sizeof(ckx->len); if (fd->len > 35000) return CLIENT_APP_EINVALID; break; case SSH2_IGNORE: fd->pos++; if (fd->pos >= fd->len) { fd->hstate = SSH2_HEADER_BEGIN; fd->pos = 0; } break; case SSH2_KEYX_HEADER_FINISH: fd->pos++; if (fd->pos >= sizeof(ckex->cookie)) { fd->hstate = SSH2_FIELD_LEN_BEGIN; fd->pos = 0; } break; case SSH2_FIELD_LEN_BEGIN: fd->l.raw_len[fd->pos] = data[offset]; fd->pos++; if (fd->pos >= sizeof(cks->len)) { fd->pos = 0; fd->field_len = ntohl(fd->l.len); fd->read_data += fd->field_len + sizeof(cks->len); if (fd->read_data > fd->len) return CLIENT_APP_EINVALID; if (fd->field_len) fd->hstate = SSH2_FIELD_DATA_BEGIN; else { fd->field++; if (fd->field >= 10) fd->hstate = SSH2_PAYLOAD_BEGIN; } } break; case SSH2_FIELD_DATA_BEGIN: fd->pos++; if (fd->pos >= fd->field_len) { fd->field++; if (fd->field >= 10) fd->hstate = SSH2_PAYLOAD_BEGIN; else fd->hstate = SSH2_FIELD_LEN_BEGIN; fd->pos = 0; } break; case SSH2_PAYLOAD_BEGIN: if (fd->pos >= offsetof(ClientSSHKeyExchangeFinal, future)) { fd->l.raw_len[fd->pos - offsetof(ClientSSHKeyExchangeFinal, future)] = data[offset]; } fd->pos++; if (fd->pos >= sizeof(ClientSSHKeyExchangeFinal)) { if (fd->l.len != 0) return CLIENT_APP_EINVALID; fd->hstate = SSH2_PADDING; fd->pos = 0; } break; case SSH2_PADDING: fd->pos++; if (fd->pos >= fd->plen) { offset++; if (offset == size) return CLIENT_APP_SUCCESS; return CLIENT_APP_EINVALID; } break; } offset++; } return CLIENT_APP_INPROCESS; } static inline CLIENT_APP_RETCODE ssh_client_validate_pubkey(uint16_t offset, const uint8_t *data, uint16_t size, ClientSSHData *fd) { const ClientSSHMsg *ckx; while (offset < size) { switch (fd->oldhstate) { case SSH1_HEADER_BEGIN: fd->l.raw_len[fd->pos] = data[offset]; fd->pos++; if(fd->pos == sizeof(ckx->len)) { fd->len = ntohl(fd->l.len) ; fd->oldhstate = SSH1_HEADER_PLEN; } break; case SSH1_HEADER_PLEN: if(size > (fd->len + sizeof(ckx->len))) fd->plen = size - (fd->len + sizeof(ckx->len)) ; else fd->plen = 0; fd->oldhstate = SSH1_HEADER_FIND_CODE; case SSH1_HEADER_FIND_CODE: if (fd->pos == fd->plen + sizeof(ckx->len)) { fd->oldhstate = SSH1_HEADER_CODE; fd->code = data[offset]; } fd->pos++; break; case SSH1_HEADER_CODE: if (fd->code == SSH_MSG_SESSION_KEY) { fd->oldhstate = SSH1_SESSION_KEY; fd->pos++; } else return CLIENT_APP_EINVALID; fd->len = fd->len + fd->plen + sizeof(ckx->len); if (fd->len > 35000) return CLIENT_APP_EINVALID; break; case SSH1_SESSION_KEY: fd->pos++; if (fd->pos >= fd->len) { offset++; if(offset == size) return CLIENT_APP_SUCCESS; return CLIENT_APP_EINVALID; } break; } offset++; } return CLIENT_APP_INPROCESS; } static inline CLIENT_APP_RETCODE ssh_client_sm(const uint8_t *data, uint16_t size, ClientSSHData *fd) { uint16_t offset = 0; uint8_t d; while(offset < size) { d = data[offset]; switch (fd->state) { case SSH_CLIENT_STATE_BANNER: if (d != SSH_CLIENT_BANNER[fd->pos]) return CLIENT_APP_EINVALID; if (fd->pos >= SSH_CLIENT_BANNER_MAXPOS) fd->state = SSH_CLIENT_STATE_ID_PROTO_VERSION; else fd->pos++; break; case SSH_CLIENT_STATE_ID_PROTO_VERSION: if (d == '1') fd->ssh_version = SSH1; else if (d == '2') fd->ssh_version = SSH2; else return CLIENT_APP_EINVALID; fd->state = SSH_CLIENT_STATE_LOOKING_FOR_DASH; break; case SSH_CLIENT_STATE_LOOKING_FOR_DASH: if (d == '-') { fd->state = SSH_CLIENT_STATE_ID_CLIENT; break; } break; case SSH_CLIENT_STATE_ID_CLIENT: switch (d) { case 'O': fd->state = SSH_CLIENT_STATE_CHECK_OPENSSH; break; case 'P': fd->state = SSH_CLIENT_STATE_CHECK_PUTTY; break; case 'l': fd->state = SSH_CLIENT_STATE_CHECK_LSH; break; case 'd': fd->state = SSH_CLIENT_STATE_CHECK_DROPBEAR; break; default: fd->state = SSH_CLIENT_STATE_ID_REST_OF_LINE; fd->client_id = APP_ID_SSH; } /*the next thing we want to see is the SECOND character... */ fd->pos = 1; break; case SSH_CLIENT_STATE_CHECK_OPENSSH: if (d != OPENSSH_BANNER[fd->pos]) { fd->client_id = APP_ID_SSH; fd->state = SSH_CLIENT_STATE_ID_REST_OF_LINE; } else if (fd->pos >= OPENSSH_BANNER_MAXPOS) { fd->client_id = APP_ID_OPENSSH; fd->state = SSH_CLIENT_STATE_ID_SOFTWARE_VERSION; fd->pos = 0; } else fd->pos++; break; case SSH_CLIENT_STATE_CHECK_PUTTY: if (d != PUTTY_BANNER[fd->pos]) { fd->client_id = APP_ID_SSH; fd->state = SSH_CLIENT_STATE_ID_REST_OF_LINE; } else if (fd->pos >= PUTTY_BANNER_MAXPOS) { fd->client_id = APP_ID_PUTTY; fd->state = SSH_CLIENT_STATE_ID_SOFTWARE_VERSION; fd->pos = 0; } else fd->pos++; break; case SSH_CLIENT_STATE_CHECK_LSH: if (d != LSH_BANNER[fd->pos]) { fd->client_id = APP_ID_SSH; fd->state = SSH_CLIENT_STATE_ID_REST_OF_LINE; } else if (fd->pos >= LSH_BANNER_MAXPOS) { fd->client_id = APP_ID_LSH; fd->state = SSH_CLIENT_STATE_ID_SOFTWARE_VERSION; fd->pos = 0; } else fd->pos++; break; case SSH_CLIENT_STATE_CHECK_DROPBEAR: if (d != DROPBEAR_BANNER[fd->pos]) { fd->client_id = APP_ID_SSH; fd->state = SSH_CLIENT_STATE_ID_REST_OF_LINE; } else if (fd->pos >= DROPBEAR_BANNER_MAXPOS) { fd->client_id = APP_ID_DROPBEAR; fd->state = SSH_CLIENT_STATE_ID_SOFTWARE_VERSION; fd->pos = 0; } else fd->pos++; break; case SSH_CLIENT_STATE_ID_SOFTWARE_VERSION: if (d == '\n') { fd->version[fd->pos] = 0; fd->pos = 0; fd->state = SSH_CLIENT_STATE_KEY; break; } if (d == ' ') { fd->version[fd->pos] = 0; fd->state = SSH_CLIENT_STATE_ID_REST_OF_LINE; break; } if (fd->pos < SSH_MAX_BANNER_LEN - 1 && d != '\r' && d != '-' && d != '_') { fd->version[fd->pos++] = d; } break; case SSH_CLIENT_STATE_ID_REST_OF_LINE: if (d == '\n') { fd->pos = 0; fd->state = SSH_CLIENT_STATE_KEY; break; } break; case SSH_CLIENT_STATE_KEY: switch (fd->ssh_version) { case SSH2: return ssh_client_validate_keyx(offset, data, size, fd); case SSH1: return ssh_client_validate_pubkey(offset, data, size, fd); default: return CLIENT_APP_EINVALID; } default: return CLIENT_APP_EINVALID; } offset++; } return CLIENT_APP_INPROCESS; } static CLIENT_APP_RETCODE ssh_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { ClientSSHData *fd; CLIENT_APP_RETCODE sm_ret; if (!size || dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; fd = ssh_client_mod.api->data_get(flowp, ssh_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (ssh_client_mod.api->data_add(flowp, fd, ssh_client_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } fd->state = SSH_CLIENT_STATE_BANNER; fd->hstate = SSH2_HEADER_BEGIN; fd->oldhstate = SSH1_HEADER_BEGIN; } sm_ret = ssh_client_sm(data, size, fd); if (sm_ret != CLIENT_APP_SUCCESS) return sm_ret; ssh_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SSH, fd->client_id, (const char *)fd->version); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/client_plugins/client_app_vnc.c0000644000175000017500000001344414241075373025473 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "client_app_api.h" static const char VNC_BANNER[] = "RFB "; static const char VNC_BANNER2[] = "."; #define VNC_BANNER_LEN (sizeof(VNC_BANNER)-1) typedef enum { VNC_STATE_BANNER = 0, VNC_STATE_VERSION } VNCState; #define MAX_VERSION_SIZE 8 typedef struct _CLIENT_VNC_DATA { VNCState state; unsigned pos; uint8_t version[MAX_VERSION_SIZE]; } ClientVNCData; typedef struct _VNC_CLIENT_APP_CONFIG { int enabled; } VNC_CLIENT_APP_CONFIG; static VNC_CLIENT_APP_CONFIG vnc_config; static CLIENT_APP_RETCODE vnc_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE vnc_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); SF_SO_PUBLIC tRNAClientAppModule vnc_client_mod = { .name = "RFB", .proto = IPPROTO_TCP, .init = &vnc_init, .validate = &vnc_validate, .minimum_matches = 2 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)VNC_BANNER, sizeof(VNC_BANNER)-1, 0, APP_ID_VNC}, {(const uint8_t *)VNC_BANNER2, sizeof(VNC_BANNER2)-1, 7, APP_ID_VNC}, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_VNC, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_VNC_RFB, APPINFO_FLAG_CLIENT_ADDITIONAL} }; static CLIENT_APP_RETCODE vnc_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; vnc_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { vnc_config.enabled = atoi(item->value); } } } if (vnc_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&vnc_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&vnc_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE vnc_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { ClientVNCData *fd; uint16_t offset; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; fd = vnc_client_mod.api->data_get(flowp, vnc_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (vnc_client_mod.api->data_add(flowp, fd, vnc_client_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } fd->state = VNC_STATE_BANNER; } offset = 0; while(offset < size) { switch (fd->state) { case VNC_STATE_BANNER: if(data[offset] != VNC_BANNER[fd->pos]) return CLIENT_APP_EINVALID; if(fd->pos >= VNC_BANNER_LEN-1) { fd->state = VNC_STATE_VERSION; fd->pos = 0; break; } fd->pos++; break; case VNC_STATE_VERSION: if((isdigit(data[offset]) || data[offset] == '.' || data[offset] == '\n') && fd->pos < MAX_VERSION_SIZE) { fd->version[fd->pos] = data[offset]; if(data[offset] == '\n' && fd->pos == 7) { fd->version[fd->pos] = 0; goto done; } } else return CLIENT_APP_EINVALID; fd->pos++; break; default: goto inprocess; } offset++; } inprocess: return CLIENT_APP_INPROCESS; done: vnc_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_VNC_RFB, APP_ID_VNC, (const char *) fd->version); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/commonAppMatcher.c0000644000175000017500000011774314241075400022723 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include "commonAppMatcher.h" #include "common_util.h" #include "httpCommon.h" #include "service_state.h" #include "service_base.h" #include "service_api.h" #include "client_app_base.h" #include "client_app_api.h" #include "detector_base.h" #include "detector_api.h" #include "detector_http.h" #include "detector_cip.h" #include "service_ssl.h" #include "luaDetectorApi.h" #include "luaDetectorModule.h" #include "fw_appid.h" #include "hostPortAppCache.h" #include "appInfoTable.h" #include "appIdStats.h" #include "appIdConfig.h" #include "ip_funcs.h" #include "sfutil.h" #include "app_forecast.h" #include "detector_dns.h" unsigned appIdPolicyId; tAppIdConfig appIdConfig; tAppIdConfig *pAppidActiveConfig; tAppIdConfig *pAppidPassiveConfig; tAppidStaticConfig* appidStaticConfig; uint32_t app_id_netmasks[33]; /*static const char * const MODULE_NAME = "AppMatcher"; */ #define MAX_DISPLAY_SIZE 65536 /*#define DEBUG_APP_COMMON*/ static void DisplayPortExclusionList(SF_LIST *pe_list, uint16_t port) { const char *p; const char *p2; char inet_buffer[INET6_ADDRSTRLEN]; char inet_buffer2[INET6_ADDRSTRLEN]; PortExclusion *pe; if (!pe_list) return; for (pe = (PortExclusion *)sflist_first(pe_list); pe; pe = (PortExclusion *)sflist_next(pe_list)) { p = inet_ntop(pe->family, &pe->ip, inet_buffer, sizeof(inet_buffer)); p2 = inet_ntop(pe->family, &pe->netmask, inet_buffer2, sizeof(inet_buffer2)); _dpd.logMsg(" %d on %s/%s\n", port, p ? p:"ERROR", p2 ? p2:"ERROR"); } } static void DisplayConfig(tAppidStaticConfig* appidSC, tAppIdConfig *aic) { unsigned i; int j; struct in_addr ia; char inet_buffer[INET6_ADDRSTRLEN]; char inet_buffer2[INET6_ADDRSTRLEN]; NSIPv6Addr six; const char *p; const char *p2; NetworkSet *net_list; if (appidSC->appid_thirdparty_dir) _dpd.logMsg(" 3rd Party Dir: %s\n", appidSC->appid_thirdparty_dir); if (appidSC->tp_config_path) _dpd.logMsg(" 3rd Party Conf: %s\n", appidSC->tp_config_path); net_list = aic->net_list; _dpd.logMsg(" Monitoring Networks for any zone:\n"); for (i = 0; i < net_list->count; i++) { ia.s_addr = htonl(net_list->pnetwork[i]->range_min); p = inet_ntop(AF_INET, &ia, inet_buffer, sizeof(inet_buffer)); ia.s_addr = htonl(net_list->pnetwork[i]->range_max); p2 = inet_ntop(AF_INET, &ia, inet_buffer2, sizeof(inet_buffer2)); _dpd.logMsg(" %s%s-%s %04X\n", (net_list->pnetwork[i]->info.ip_not) ? "!":"", p ? p:"ERROR", p2 ? p2:"ERROR", net_list->pnetwork[i]->info.type); } for (i = 0; i < net_list->count6; i++) { six = net_list->pnetwork6[i]->range_min; NSIPv6AddrHtoN(&six); p = inet_ntop(AF_INET6, (struct in6_addr *)&six, inet_buffer, sizeof(inet_buffer)); six = net_list->pnetwork6[i]->range_max; NSIPv6AddrHtoN(&six); p2 = inet_ntop(AF_INET6, (struct in6_addr *)&six, inet_buffer2, sizeof(inet_buffer2)); _dpd.logMsg(" %s%s-%s %04X\n", (net_list->pnetwork6[i]->info.ip_not) ? "!":"", p ? p:"ERROR", p2 ? p2:"ERROR", net_list->pnetwork6[i]->info.type); } for (j=0; j < MAX_ZONES; j++) { if (!(net_list = aic->net_list_by_zone[j])) continue; _dpd.logMsg(" Monitoring Networks for zone %d:\n", j); for (i = 0; i < net_list->count; i++) { ia.s_addr = htonl(net_list->pnetwork[i]->range_min); p = inet_ntop(AF_INET, &ia, inet_buffer, sizeof(inet_buffer)); ia.s_addr = htonl(net_list->pnetwork[i]->range_max); p2 = inet_ntop(AF_INET, &ia, inet_buffer2, sizeof(inet_buffer2)); _dpd.logMsg(" %s%s-%s %04X\n", (net_list->pnetwork[i]->info.ip_not) ? "!":"", p ? p:"ERROR", p2 ? p2:"ERROR", net_list->pnetwork[i]->info.type); } for (i = 0; i < net_list->count6; i++) { six = net_list->pnetwork6[i]->range_min; NSIPv6AddrHtoN(&six); p = inet_ntop(AF_INET6, (struct in6_addr *)&six, inet_buffer, sizeof(inet_buffer)); six = net_list->pnetwork6[i]->range_max; NSIPv6AddrHtoN(&six); p2 = inet_ntop(AF_INET6, (struct in6_addr *)&six, inet_buffer2, sizeof(inet_buffer2)); _dpd.logMsg(" %s%s-%s %04X\n", (net_list->pnetwork6[i]->info.ip_not) ? "!":"", p ? p:"ERROR", p2 ? p2:"ERROR", net_list->pnetwork6[i]->info.type); } } _dpd.logMsg(" Excluded TCP Ports for Src:\n"); for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++) DisplayPortExclusionList(aic->tcp_port_exclusions_src[i], i); _dpd.logMsg(" Excluded TCP Ports for Dst:\n"); for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++) DisplayPortExclusionList(aic->tcp_port_exclusions_dst[i], i); _dpd.logMsg(" Excluded UDP Ports Src:\n"); for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++) DisplayPortExclusionList(aic->udp_port_exclusions_src[i], i); _dpd.logMsg(" Excluded UDP Ports Dst:\n"); for (i = 0; i < APP_ID_PORT_ARRAY_SIZE; i++) DisplayPortExclusionList(aic->udp_port_exclusions_dst[i], i); } #ifdef DEBUG_APP_COMMON static void DisplayPortConfig(tAppIdConfig *aic) { unsigned i; int first; first = 1; for (i=0; itcp_port_only)/sizeof(*aic->tcp_port_only); i++) { if (aic->tcp_port_only[i]) { if (first) { _dpd.logMsg(" TCP Port-Only Services\n"); first = 0; } _dpd.logMsg(" %5u - %u\n", i, aic->tcp_port_only[i]); } } first = 1; for (i=0; iudp_port_only)/sizeof(*aic->udp_port_only); i++) { if (aic->udp_port_only[i]) { if (first) { _dpd.logMsg(" UDP Port-Only Services\n"); first = 0; } _dpd.logMsg(" %5u - %u\n", i, aic->udp_port_only[i]); } } } #endif typedef struct _PORT { struct _PORT *next; uint16_t port; } Port; static void ReadPortDetectors(tAppidStaticConfig* appidSC, tAppIdConfig *aic, const char *files) { int rval; glob_t globs; char pattern[PATH_MAX]; uint32_t n; snprintf(pattern, sizeof(pattern), "%s/%s", appidSC->app_id_detector_path, files); memset(&globs, 0, sizeof(globs)); rval = glob(pattern, 0, NULL, &globs); if (rval != 0 && rval != GLOB_NOMATCH) { _dpd.errMsg("Unable to read directory '%s'\n",pattern); return; } for(n = 0; n < globs.gl_pathc; n++) { FILE *file; unsigned proto = 0; tAppId appId = APP_ID_NONE; char line[1024]; Port *port = NULL; Port *tmp_port; if ((file = fopen(globs.gl_pathv[n], "r")) == NULL) { _dpd.errMsg("Unable to read port service '%s'\n",globs.gl_pathv[n]); continue; } while (fgets(line, sizeof(line), file)) { char *key, *value, *p; size_t len; len = strlen(line); for (; len && (line[len - 1] == '\n' || line[len - 1] == '\r'); len--) line[len - 1] = 0; /* find key/value for lines of the format "key: value\n" */ if ((value = strchr(line, ':'))) { key = line; *value = '\0'; value++; for (; *value && *value == ' '; value++); if (strcasecmp(key, "ports") == 0) { char *context = NULL; char *ptr; unsigned long tmp; for (ptr = strtok_r(value, ",", &context); ptr; ptr = strtok_r(NULL, ",", &context)) { for (; *ptr && *ptr == ' '; ptr++); len = strlen(ptr); for (; len && ptr[len - 1] == ' '; len--) ptr[len - 1] = 0; tmp = strtoul(ptr, &p, 10); if (!*ptr || *p || !tmp || tmp > 65535) { _dpd.errMsg("Invalid port, '%s', in lua detector '%s'\n",ptr, globs.gl_pathv[n]); goto next; } if ((tmp_port = calloc(1, sizeof(*tmp_port))) == NULL) { _dpd.errMsg( "Failed to allocate a port struct"); goto next; } tmp_port->port = (uint16_t)tmp; tmp_port->next = port; port = tmp_port; } } else if (strcasecmp(key, "protocol") == 0) { if (strcasecmp(value, "tcp") == 0) proto = 1; else if (strcasecmp(value, "udp") == 0) proto = 2; else if (strcasecmp(value, "tcp/udp") == 0) proto = 3; else { _dpd.errMsg("Invalid protocol, '%s', in port service '%s'\n",value, globs.gl_pathv[n]); goto next; } } else if (strcasecmp(key, "appId") == 0) { appId = (tAppId)strtoul(value, &p, 10); if (!*value || *p || appId <= APP_ID_NONE) { _dpd.errMsg("Invalid app ID, '%s', in port service '%s'\n",value, globs.gl_pathv[n]); goto next; } } } } if (port && proto && appId > APP_ID_NONE) { while ((tmp_port = port)) { port = tmp_port->next; if (proto & 1) aic->tcp_port_only[tmp_port->port] = appId; if (proto & 2) aic->udp_port_only[tmp_port->port] = appId; free(tmp_port); appInfoSetActive(appId, true); } appInfoSetActive(appId, true); } else _dpd.errMsg("Missing parameter(s) in port service '%s'\n",globs.gl_pathv[n]); next:; while ((tmp_port = port)) { port = tmp_port->next; free(tmp_port); } fclose(file); } globfree(&globs); } #define CISCO_PORT_DETECTORS "odp/port/*" #define CUSTOM_PORT_DETECTORS "custom/port/*" static void AppIdConfigureAnalyze(char *toklist[], uint32_t flag, tAppIdConfig *pConfig) { int zone; NetworkSet *net_list; RNAIpAddrSet *ias; RNAIpv6AddrSet *ias6; char *p; long tmp; if (toklist[0]) { if (strchr(toklist[0], ':')) { ias6 = ParseIpv6Cidr(toklist[0]); if (ias6) { NSIPv6Addr six; char min_ip[INET6_ADDRSTRLEN]; char max_ip[INET6_ADDRSTRLEN]; if (toklist[1]) { tmp = strtol(toklist[1], &p, 10); if (!*toklist[1] || *p != 0 || tmp >= MAX_ZONES || tmp < -1) { _dpd.errMsg("Invalid Analyze: %s '%s'", toklist[0], toklist[1]); zone = -1; } else zone = (int)tmp; } else zone = -1; ias6->addr_flags |= flag; six = ias6->range_min; NSIPv6AddrHtoN(&six); inet_ntop(AF_INET6, (struct in6_addr *)&six, min_ip, sizeof(min_ip)); six = ias6->range_max; NSIPv6AddrHtoN(&six); inet_ntop(AF_INET6, (struct in6_addr *)&six, max_ip, sizeof(max_ip)); _dpd.logMsg("Adding %s-%s (0x%08X) with zone %d\n", min_ip, max_ip, ias6->addr_flags, zone); if (zone >= 0) { if (!(net_list = pConfig->net_list_by_zone[zone])) { if (NetworkSet_New(&net_list)) _dpd.errMsg("%s", "Failed to create a network set"); else { net_list->next = pConfig->net_list_list; pConfig->net_list_list = net_list; } pConfig->net_list_by_zone[zone] = net_list; } } else net_list = pConfig->net_list; if (net_list && NetworkSet_AddCidrBlock6Ex(net_list, &ias6->range_min, ias6->netmask, ias6->addr_flags & IPFUNCS_EXCEPT_IP, 0, ias6->addr_flags & (~IPFUNCS_EXCEPT_IP))) { _dpd.errMsg("Failed to add an IP address set to the list of monitored networks"); } free(ias6); } else _dpd.errMsg("Invalid analysis parameter: %s", toklist[0]); } else { ias = ParseIpCidr(toklist[0], app_id_netmasks); if (ias) { if (toklist[1]) { tmp = strtol(toklist[1], &p, 10); if (!*toklist[1] || *p != 0 || tmp >= MAX_ZONES || tmp < -1) { _dpd.errMsg("Invalid Analyze: %s '%s'", toklist[0], toklist[1]); zone = -1; } else zone = (int)tmp; } else zone = -1; ias->addr_flags |= flag; _dpd.logMsg("Adding 0x%08X-0x%08X (0x%08X) with zone %d\n", ias->range_min, ias->range_max, ias->addr_flags, zone); if (zone >= 0) { if (!(net_list = pConfig->net_list_by_zone[zone])) { if (NetworkSet_New(&net_list)) _dpd.errMsg("%s", "Failed to create a network set"); else { net_list->next = pConfig->net_list_list; pConfig->net_list_list = net_list; } pConfig->net_list_by_zone[zone] = net_list; } } else net_list = pConfig->net_list; if (net_list && NetworkSet_AddCidrBlockEx(net_list, ias->range_min, ias->netmask, ias->addr_flags & IPFUNCS_EXCEPT_IP, 0, ias->addr_flags & (~IPFUNCS_EXCEPT_IP))) { _dpd.errMsg("Failed to add an IP address set to the list of monitored networks"); } free(ias); } else _dpd.errMsg("Invalid analysis parameter: %s", toklist[0]); } } } static sfaddr_t* AppIdConfigureDebug(char *toklist[]) { if (!toklist[0]) return NULL; struct in6_addr tmp; uint16_t family; if (!strchr(toklist[0], ':')) { if (inet_pton(AF_INET, toklist[0], &tmp) <= 0) { _dpd.errMsg("AppId Config: Failed to translate debug host %s", toklist[0]); return NULL; } family = AF_INET; } else { if (inet_pton(AF_INET6, toklist[0], &tmp) <= 0) { _dpd.errMsg("AppId Config: Failed to translate debug host %s", toklist[0]); return NULL; } family = AF_INET6; } sfaddr_t* ipAddr = malloc(sizeof(*ipAddr)); if (!ipAddr) { _dpd.errMsg("AppId Config: Failed to allocate memory"); return NULL; } ipAddr->family = family; if (ipAddr->family == AF_INET) copyIpv4ToIpv6Network(&ipAddr->ip, tmp.s6_addr32[0]); else memcpy(ipAddr, &tmp, sizeof(tmp)); _dpd.logMsg("\nAppIdDebugHost: Debugging host IP %s\n", toklist[0]); return ipAddr; } static inline int AddPortExclusion(SF_LIST *port_exclusions[], const struct in6_addr *ip, const struct in6_addr *netmask, int family, uint16_t port) { PortExclusion *port_ex; SF_LIST *pe_list; if (!(port_ex = calloc(1, sizeof(*port_ex)))) { _dpd.errMsg("Config: Failed to allocate memory for port exclusion entry"); return -1; } port_ex->ip = *ip; if (family == AF_INET) { port_ex->netmask.s6_addr32[0] = port_ex->netmask.s6_addr32[1] = port_ex->netmask.s6_addr32[2] = ~0; port_ex->netmask.s6_addr32[3] = netmask->s6_addr32[3]; } else port_ex->netmask = *netmask; if ((pe_list = port_exclusions[port]) == NULL) { pe_list = port_exclusions[port] = sflist_new(); if (pe_list == NULL) { free(port_ex); _dpd.errMsg("Config: Failed to allocate memory for port exclusion list"); return -1; } } /* add this PortExclusion to the sflist for this port */ if (sflist_add_tail(pe_list, port_ex) ) { free(port_ex); _dpd.errMsg("Config: Failed to add an port exclusion to the list"); return -1; } return 0; } static void ProcessPortExclusion(char *toklist[], tAppIdConfig *aic) { int i = 1; char *p; RNAIpAddrSet *ias; RNAIpv6AddrSet *ias6; SF_LIST **port_exclusions; unsigned proto; unsigned long dir; unsigned long port; struct in6_addr ip; struct in6_addr netmask; int family; if (!toklist[i]) { _dpd.errMsg("Config: Port exclusion direction omitted"); return; } if (strcasecmp(toklist[i], "dst") == 0) dir = 2; else if (strcasecmp(toklist[i], "src") == 0) dir = 1; else if (strcasecmp(toklist[i], "both") == 0) dir = 3; else { _dpd.errMsg("Config: Invalid port exclusion direction specified"); return; } i++; if (!toklist[i]) { _dpd.errMsg("Config: Port exclusion protocol omitted"); return; } if (strcasecmp(toklist[i], "tcp") == 0) proto = IPPROTO_TCP; else if (strcasecmp(toklist[i], "udp") == 0) proto = IPPROTO_UDP; else { _dpd.errMsg("Config: Invalid port exclusion protocol specified"); return; } i++; if (!toklist[i]) { _dpd.errMsg("Config: Port exclusion port omitted"); return; } port = strtoul(toklist[i], &p, 10); if (!*toklist[i] || *p || port >= APP_ID_PORT_ARRAY_SIZE) { _dpd.errMsg("Config: Invalid port exclusion port specified"); return; } i++; if (!toklist[i]) { _dpd.errMsg("Config: Port exclusion address omitted"); return; } if (strchr(toklist[i], ':')) { ias6 = ParseIpv6Cidr(toklist[i]); if (!ias6 || ias6->addr_flags) { if (ias6) free(ias6); _dpd.errMsg("Config: Invalid port exclusion address specified"); return; } NSIPv6AddrHtoNConv(&ias6->range_min, &ip); NSIPv6AddrHtoNConv(&ias6->netmask_mask, &netmask); family = AF_INET6; free(ias6); } else { ias = ParseIpCidr(toklist[i], app_id_netmasks); if (!ias || ias->addr_flags) { if (ias) free(ias); _dpd.errMsg("Config: Invalid port exclusion address specified"); return; } family = AF_INET; copyIpv4ToIpv6Network(&ip, htonl(ias->range_min)); copyIpv4ToIpv6Network(&netmask, htonl(ias->netmask_mask)); free(ias); } if (dir & 1) { if (proto == IPPROTO_TCP) port_exclusions = aic->tcp_port_exclusions_src; else port_exclusions = aic->udp_port_exclusions_src; AddPortExclusion(port_exclusions, &ip, &netmask, family, (uint16_t)port); } if (dir & 2) { if (proto == IPPROTO_TCP) port_exclusions = aic->tcp_port_exclusions_dst; else port_exclusions = aic->udp_port_exclusions_dst; AddPortExclusion(port_exclusions, &ip, &netmask, family, (uint16_t)port); } } static void ProcessConfigDirective(char *toklist[], tAppIdConfig *aic, int reload) { char *curtok; int i; /* the first tok is "config" or we wouldn't be here now */ i = 1; curtok = toklist[i]; i++; if (!strcasecmp(curtok, "Analyze")) { AppIdConfigureAnalyze(&toklist[i], IPFUNCS_HOSTS_IP | IPFUNCS_APPLICATION, aic); } else if (!strcasecmp(curtok, "AnalyzeHost")) { AppIdConfigureAnalyze(&toklist[i], IPFUNCS_HOSTS_IP | IPFUNCS_APPLICATION, aic); } else if (!strcasecmp(curtok, "AnalyzeUser")) { AppIdConfigureAnalyze(&toklist[i], IPFUNCS_USER_IP | IPFUNCS_APPLICATION, aic); } else if (!strcasecmp(curtok, "AnalyzeHostUser")) { AppIdConfigureAnalyze(&toklist[i], IPFUNCS_HOSTS_IP | IPFUNCS_USER_IP | IPFUNCS_APPLICATION, aic); } else if (!strcasecmp(curtok, "AnalyzeApplication")) { AppIdConfigureAnalyze(&toklist[i], IPFUNCS_APPLICATION, aic); } else if (!strcasecmp(curtok, "DebugHost")) { aic->debugHostIp = AppIdConfigureDebug(&toklist[i]); } } static int AppIdLoadConfigFile(tAppidStaticConfig* appidSC, int reload, int instance_id, tAppIdConfig *pConfig) { FILE *fp; char linebuffer[MAX_LINE]; char *cptr; char *toklist[MAX_TOKS]; int num_toks; unsigned line = 0; NetworkSet *net_list; if (NetworkSet_New(&pConfig->net_list)) { _dpd.fatalMsg("Failed to allocate a network set"); exit(1); } pConfig->net_list_list = pConfig->net_list; if (!appidSC->conf_file || (!appidSC->conf_file[0])) { char addrString[sizeof("0.0.0.0/0")]; _dpd.logMsg("Defaulting to monitoring all Snort traffic for AppID.\n"); toklist[1] = NULL; toklist[0] = addrString; strcpy(addrString,"0.0.0.0/0"); AppIdConfigureAnalyze(toklist, IPFUNCS_HOSTS_IP | IPFUNCS_USER_IP | IPFUNCS_APPLICATION, pConfig); strcpy(addrString,"::/0"); AppIdConfigureAnalyze(toklist, IPFUNCS_HOSTS_IP | IPFUNCS_USER_IP | IPFUNCS_APPLICATION, pConfig); toklist[0] = NULL; } else { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "Loading configuration file: %s", appidSC->conf_file);); if (!(fp = fopen(appidSC->conf_file, "r"))) { _dpd.errMsg("Unable to open %s", appidSC->conf_file); return -1; } while (fgets(linebuffer, MAX_LINE, fp) != NULL) { line++; strip(linebuffer); cptr = linebuffer; while (isspace((int)*cptr)) cptr++; if (*cptr && (*cptr != '#') && (*cptr != 0x0a)) { memset(toklist, 0, sizeof(toklist)); /* tokenize the line */ num_toks = Tokenize(cptr, toklist); if (num_toks < 2) { fclose(fp); _dpd.errMsg("Invalid configuration file line %u", line); return -1; } if (!(strcasecmp(toklist[0], "config"))) { ProcessConfigDirective(toklist, pConfig, reload); } else if (!(strcasecmp(toklist[0], "portexclusion"))) { ProcessPortExclusion(toklist, pConfig); } } } fclose(fp); } #define DEFAULT_THIRDPARTY_PATH "/usr/local/lib/thirdparty" if (!appidSC->appid_thirdparty_dir) { if (!(appidSC->appid_thirdparty_dir = strdup(DEFAULT_THIRDPARTY_PATH))) { _dpd.errMsg("Failed to allocate a module directory"); return -1; } } if (instance_id) { char *instance_toklist[2]; char addrString[sizeof("0.0.0.0/0")]; _dpd.logMsg("Defaulting to monitoring all Snort traffic for AppID.\n"); instance_toklist[0] = addrString; instance_toklist[1] = NULL; strcpy(addrString,"0.0.0.0/0"); AppIdConfigureAnalyze(instance_toklist, IPFUNCS_APPLICATION, pConfig); strcpy(addrString,"::/0"); AppIdConfigureAnalyze(instance_toklist, IPFUNCS_APPLICATION, pConfig); } for (net_list = pConfig->net_list_list; net_list; net_list = net_list->next) { if (net_list != pConfig->net_list) { if (NetworkSet_AddSet(net_list, pConfig->net_list)) _dpd.errMsg("Failed to add any network list to a zone network list"); } } pConfig->net_list_count = 0; for (net_list = pConfig->net_list_list; net_list; net_list = net_list->next) { if (NetworkSet_Reduce(net_list)) _dpd.errMsg("Failed to reduce the IP address sets"); pConfig->net_list_count += NetworkSet_CountEx(net_list) + NetworkSet_Count6Ex(net_list); } return 0; } static void ReadPorts(tAppidStaticConfig* appidSC, tAppIdConfig *aic) { ReadPortDetectors(appidSC, aic, CISCO_PORT_DETECTORS); ReadPortDetectors(appidSC, aic, CUSTOM_PORT_DETECTORS); } static void LoadModules(uint32_t instance_id, tAppIdConfig *pConfig) { if (LoadServiceModules(NULL, instance_id, pConfig)) exit(-1); if (LoadClientAppModules(NULL, pConfig)) exit(-1); if (LoadDetectorModules(NULL)) exit(-1); } static void FinalizePatternModules(tAppIdConfig *pConfig) { int i; tServicePatternData *curr; tServicePatternData *lists[] = { pConfig->serviceConfig.tcp_pattern_data, pConfig->serviceConfig.udp_pattern_data }; for (i = 0; i < (sizeof(lists) / sizeof(*lists)); i++) { curr = lists[i]; while (curr != NULL) { if (curr->svc != NULL) { bool isActive = true; if (curr->svc->userdata && !curr->svc->userdata->isActive) { /* C detectors don't have userdata here, but they're always * active. So, this check is really just for Lua * detectors. */ isActive = false; } if (isActive) { curr->svc->current_ref_count = curr->svc->ref_count; } } curr = curr->next; } } } /** * \brief Reload C modules - both static and dynamic. * * This function is called during reload/reconfiguration. Nothing needs to be done for client * app modules. In addition to the C service modules, \e ReloadServiceModules() also takes care * of loading services associated with detectors. * * @param pConfig - AppId context in which the modules need to be reloaded * @return None */ static void ReloadModules(tAppIdConfig *pConfig) { if (ReloadServiceModules(pConfig)) exit(-1); } typedef enum { RNA_FW_CONFIG_STATE_UNINIT, RNA_FW_CONFIG_STATE_INIT, RNA_FW_CONFIG_STATE_PENDING, } tRnaFwConfigState; static tRnaFwConfigState rnaFwConfigState = RNA_FW_CONFIG_STATE_UNINIT; static void ConfigItemFree(ConfigItem *ci) { if (ci) { if (ci->name) free(ci->name); if (ci->value) free(ci->value); free(ci); } } static void AppIdCleanupConfig(tAppIdConfig *pConfig) { NetworkSet *net_list; ///< list of network sets unsigned int i; while ((net_list = pConfig->net_list_list)) { pConfig->net_list_list = net_list->next; NetworkSet_Destroy(net_list); } /* clean up any port exclusions that have been allocated */ for( i=0; itcp_port_exclusions_src[i] != NULL ) { sflist_free_all(pConfig->tcp_port_exclusions_src[i], &free); pConfig->tcp_port_exclusions_src[i] = NULL; } if( pConfig->tcp_port_exclusions_dst[i] != NULL ) { sflist_free_all(pConfig->tcp_port_exclusions_dst[i], &free); pConfig->tcp_port_exclusions_dst[i] = NULL; } if( pConfig->udp_port_exclusions_src[i] != NULL ) { sflist_free_all(pConfig->udp_port_exclusions_src[i], &free); pConfig->udp_port_exclusions_src[i] = NULL; } if( pConfig->udp_port_exclusions_dst[i] != NULL ) { sflist_free_all(pConfig->udp_port_exclusions_dst[i], &free); pConfig->udp_port_exclusions_dst[i] = NULL; } } pConfig->net_list = NULL; if (pConfig->CHP_glossary) { sfxhash_delete(pConfig->CHP_glossary); pConfig->CHP_glossary = NULL; } if (pConfig->AF_indicators) { sfxhash_delete(pConfig->AF_indicators); pConfig->AF_indicators = NULL; } if (pConfig->AF_actives) { sfxhash_delete(pConfig->AF_actives); pConfig->AF_actives = NULL; } memset(pConfig->net_list_by_zone, 0, sizeof(pConfig->net_list_by_zone)); sflist_static_free_all(&pConfig->client_app_args, (void (*)(void*))ConfigItemFree); if (pConfig->debugHostIp) { free(pConfig->debugHostIp); pConfig->debugHostIp = NULL; } } static int initAFIndicators(tAppIdConfig *pConfig) { if (!(pConfig->AF_indicators = sfxhash_new(1024, sizeof(tAppId), sizeof(AFElement), 0, 0, NULL, NULL, 0))) { _dpd.errMsg("Config: failed to allocate memory for an sfxhash."); return 0; } else return 1; } static int initAFActives(tAppIdConfig *pConfig) { if (!(pConfig->AF_actives = sfxhash_new(1024, sizeof(AFActKey), sizeof(AFActVal), (sizeof(SFXHASH_NODE)*2048), 1, NULL, NULL, 1))) { _dpd.errMsg("Config: failed to allocate memory for an sfxhash."); return 0; } else return 1; } static int genericDataFree (void *key, void *data) { if (data) free(data); return 0; } static int initCHPGlossary (tAppIdConfig *pConfig) { if (!(pConfig->CHP_glossary = sfxhash_new(1024, sizeof(tAppId), 0, 0, 0, NULL, &genericDataFree, 0))) { _dpd.errMsg("Config: failed to allocate memory for an sfxhash."); return 0; } else return 1; } #if 0 if packet thread then active else if RNA_FW_CONFIG_STATE_UNINIT then active else passive #endif int AppIdCommonInit(tAppidStaticConfig *appidSC) { if (!(pAppidActiveConfig = (tAppIdConfig *)_dpd.snortAlloc(1, sizeof(*pAppidActiveConfig), PP_APP_ID, PP_MEM_CATEGORY_CONFIG))) { _dpd.errMsg("Config: Failed to allocate memory for AppIdConfig"); return -1; } fwAppIdInit(); if (rnaFwConfigState == RNA_FW_CONFIG_STATE_UNINIT) { appIdPolicyId = 53; pAppidPassiveConfig = pAppidActiveConfig; rnaFwConfigState = RNA_FW_CONFIG_STATE_PENDING; InitNetmasks(app_id_netmasks); sflist_init(&pAppidActiveConfig->client_app_args); AppIdLoadConfigFile(appidSC, 0, appidSC->instance_id, pAppidActiveConfig); if (!initCHPGlossary(pAppidActiveConfig)) return -1; if (!initAFIndicators(pAppidActiveConfig)) return -1; if (!initAFActives(pAppidActiveConfig)) return -1; luaModuleInit(); appInfoTableInit(appidSC, pAppidActiveConfig); ReadPorts(appidSC, pAppidActiveConfig); LoadModules(appidSC->instance_id, pAppidActiveConfig); hostPortAppCacheDynamicInit(); hostPortAppCacheInit(pAppidActiveConfig); lengthAppCacheInit(pAppidActiveConfig); LoadLuaModules(appidSC, pAppidActiveConfig); ClientAppInit(appidSC, pAppidActiveConfig); ServiceInit(pAppidActiveConfig); FinalizeLuaModules(pAppidActiveConfig); FinalizePatternModules(pAppidActiveConfig); http_detector_finalize(pAppidActiveConfig); sipUaFinalize(&pAppidActiveConfig->detectorSipConfig); ssl_detector_process_patterns(&pAppidActiveConfig->serviceSslConfig); dns_host_detector_process_patterns(&pAppidActiveConfig->serviceDnsConfig); portPatternFinalize(pAppidActiveConfig); ClientAppFinalize(pAppidActiveConfig); ServiceFinalize(pAppidActiveConfig); appIdStatsInit(appidSC->app_stats_filename, appidSC->app_stats_period, appidSC->app_stats_rollover_size, appidSC->app_stats_rollover_time); DisplayConfig(appidSC, pAppidActiveConfig); #ifdef DEBUG_APP_COMMON DisplayPortConfig(pAppidActiveConfig); #endif if (AppIdServiceStateInit(appidSC->memcap)) { _dpd.fatalMsg("AppID failed to create the service state cache with %lu memory\n", appidSC->memcap); } rnaFwConfigState = RNA_FW_CONFIG_STATE_INIT; return 0; } return -1; } int AppIdCommonFini(void) { if (rnaFwConfigState == RNA_FW_CONFIG_STATE_INIT) { rnaFwConfigState = RNA_FW_CONFIG_STATE_PENDING; pAppidPassiveConfig = pAppidActiveConfig; ThirdPartyAppIDFini(); AppIdCleanupConfig(pAppidActiveConfig); CleanupServices(pAppidActiveConfig); CleanupClientApp(pAppidActiveConfig); luaModuleFini(); hostPortAppCacheDynamicFini(); hostPortAppCacheFini(pAppidActiveConfig); AppIdServiceStateCleanup(); appIdStatsFini(); fwAppIdFini(pAppidActiveConfig); lengthAppCacheFini(pAppidActiveConfig); http_detector_clean(&pAppidActiveConfig->detectorHttpConfig); service_ssl_clean(&pAppidActiveConfig->serviceSslConfig); service_dns_host_clean(&pAppidActiveConfig->serviceDnsConfig); CipClean(); rnaFwConfigState = RNA_FW_CONFIG_STATE_UNINIT; _dpd.snortFree(pAppidActiveConfig, sizeof(*pAppidActiveConfig), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); pAppidActiveConfig = NULL; pAppidPassiveConfig = NULL; return 0; } return -1; } int AppIdCommonReload(tAppidStaticConfig* appidSC, void **new_context) { tAppIdConfig *pNewConfig = (tAppIdConfig *)_dpd.snortAlloc(1, sizeof(*pNewConfig), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); if (!pNewConfig) { _dpd.fatalMsg("AppID failed to allocate memory for reload AppIdConfig"); } pAppidPassiveConfig = pNewConfig; // During a reload, C modules are not reloaded. Also, existing Lua modules are not reloaded. // New Lua modules that did not exist before the reload are loaded. // Data structures related to them such as service ports, patterns, etc. are cleaned up and // reloaded. A Lua module that does not exist after the reload is made inactive by not reloading // its data structures. // Following lists are all linear lists and new items are inserted at the head. During a reload, // only new Lua modules can be added to these lists. No items are removed from the lists. Since // items are inserted at the head, we just point the lists in the new AppId context to the head // of the lists in the current AppId context. If a new item is added to a list, the list in the // current context will be unchanged. pNewConfig->clientAppConfig.tcp_client_app_list = pAppidActiveConfig->clientAppConfig.tcp_client_app_list; pNewConfig->clientAppConfig.udp_client_app_list = pAppidActiveConfig->clientAppConfig.udp_client_app_list; pNewConfig->serviceConfig.active_service_list = pAppidActiveConfig->serviceConfig.active_service_list; pNewConfig->serviceConfig.tcp_service_list = pAppidActiveConfig->serviceConfig.tcp_service_list; pNewConfig->serviceConfig.udp_service_list = pAppidActiveConfig->serviceConfig.udp_service_list; pNewConfig->serviceConfig.udp_reversed_service_list = pAppidActiveConfig->serviceConfig.udp_reversed_service_list; sflist_init(&pNewConfig->client_app_args); AppIdLoadConfigFile(appidSC, 1, 0, pNewConfig); if (!initCHPGlossary(pNewConfig)) return -1; if (!initAFIndicators(pNewConfig)) return -1; if (!initAFActives(pNewConfig)) return -1; sflist_init(&pNewConfig->genericConfigList); appInfoTableInit(appidSC, pNewConfig); ReadPorts(appidSC, pNewConfig); ReloadModules(pNewConfig); hostPortAppCacheInit(pNewConfig); lengthAppCacheInit(pNewConfig); LoadLuaModules(appidSC, pNewConfig); ClientAppInit(appidSC, pNewConfig); ReconfigureServices(pNewConfig); http_detector_finalize(pNewConfig); sipUaFinalize(&pNewConfig->detectorSipConfig); ssl_detector_process_patterns(&pNewConfig->serviceSslConfig); dns_host_detector_process_patterns(&pNewConfig->serviceDnsConfig); portPatternFinalize(pNewConfig); ClientAppFinalize(pNewConfig); ServiceFinalize(pNewConfig); appIdStatsReinit(); DisplayConfig(appidSC, pNewConfig); #ifdef DEBUG_APP_COMMON DisplayPortConfig(pNewConfig); #endif pAppidPassiveConfig = NULL; *new_context = pNewConfig; return 0; } void *AppIdCommonReloadSwap(void *swap_config) { tAppIdConfig *pAppidNewConfig = (tAppIdConfig *)swap_config; tAppIdConfig *pAppidOldConfig = NULL; pAppidPassiveConfig = pAppidNewConfig; FinalizeLuaModules(pAppidNewConfig); FinalizePatternModules(pAppidNewConfig); appIdPolicyId++; pAppidPassiveConfig = NULL; // Return old configuration data structure pAppidOldConfig = pAppidActiveConfig; // Make new configuration the active configuration pAppidActiveConfig = pAppidNewConfig; return pAppidOldConfig;; } void AppIdCommonUnload(void *old_context) { tAppIdConfig *pOldConfig = (tAppIdConfig *)old_context; pAppidPassiveConfig = pOldConfig; AppIdCleanupConfig(pOldConfig); UnconfigureServices(pOldConfig); UnconfigureClientApp(pOldConfig); UnloadLuaModules(pOldConfig); hostPortAppCacheFini(pOldConfig); lengthAppCacheFini(pOldConfig); appInfoTableFini(pOldConfig); http_detector_clean(&pOldConfig->detectorHttpConfig); service_ssl_clean(&pOldConfig->serviceSslConfig); service_dns_host_clean(&pOldConfig->serviceDnsConfig); _dpd.snortFree(pOldConfig, sizeof(*pOldConfig), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); pAppidPassiveConfig = NULL; } snort-2.9.20/src/dynamic-preprocessors/appid/thirdparty_appid_types.h0000644000175000017500000000652314241075576024275 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _THIRDPARTY_APPID_TYPES_H_ #define _THIRDPARTY_APPID_TYPES_H_ #include "session_api.h" #define TP_SESSION_FLAG_DPI 0x00000001 #define TP_SESSION_FLAG_MUTABLE 0x00000002 #define TP_SESSION_FLAG_FUTUREFLOW 0x00000004 #define TP_SESSION_FLAG_ATTRIBUTE 0x00000008 #define TP_SESSION_FLAG_TUNNELING 0x00000010 typedef enum { TP_STATE_INIT, TP_STATE_TERMINATED, TP_STATE_INSPECTING, TP_STATE_MONITORING, TP_STATE_CLASSIFIED, TP_STATE_HA = 21 } TPState; typedef enum { TP_ATTR_CONTINUE_MONITORING = (1 << 0), TP_ATTR_COPY_RESPONSE_CONTENT = (1 << 1), TP_ATTR_COPY_RESPONSE_LOCATION = (1 << 2), TP_ATTR_COPY_RESPONSE_BODY = (1 << 3), } TPSessionAttr; struct XffFieldValue { char* field; char* value; }; typedef struct { char *spdyRequestPath; char *spdyRequestScheme; char *spdyRequestHost; char *httpRequestUrl; char *httpRequestUri; uint16_t httpRequestUriLen; uint16_t httpRequestUriOffset; uint16_t httpRequestUriEndOffset; char *httpRequestHost; uint16_t httpRequestHostLen; char *httpRequestCookie; uint16_t httpRequestCookieLen; uint16_t httpRequestCookieOffset; uint16_t httpRequestCookieEndOffset; char *httpRequestMethod; char *httpRequestVia; char *httpResponseVia; char *httpResponseUpgrade; char *httpRequestUserAgent; uint16_t httpRequestUserAgentLen; char *httpResponseVersion; char *httpResponseCode; uint16_t httpResponseCodeLen; char *httpResponseContent; uint16_t httpResponseContentLen; char *httpResponseLocation; uint16_t httpResponseLocationLen; char *httpResponseBody; uint16_t httpResponseBodyLen; char *httpRequestBody; uint16_t httpRequestBodyLen; char *httpResponseServer; char *httpRequestXWorkingWith; char *tlsHost; char *tlsCname; char *tlsOrgUnit; char *httpRequestReferer; uint16_t httpRequestRefererLen; char *ftpCommandUser; struct XffFieldValue xffFieldValue[HTTP_MAX_XFF_FIELDS]; uint8_t numXffFields; uint16_t httpRequestUserAgentOffset; uint16_t httpRequestUserAgentEndOffset; uint16_t httpRequestHostOffset; uint16_t httpRequestHostEndOffset; uint16_t httpRequestRefererOffset; uint16_t httpRequestRefererEndOffset; uint16_t spdyRequestHostOffset; uint16_t spdyRequestHostEndOffset; uint16_t spdyRequestPathOffset; uint16_t spdyRequestPathEndOffset; } ThirdPartyAppIDAttributeData; #endif snort-2.9.20/src/dynamic-preprocessors/appid/appIdApi.c0000644000175000017500000005417014241075343021155 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "fw_appid.h" #include "thirdparty_appid_api.h" #include "appIdConfig.h" #define SSL_WHITELIST_PKT_LIMIT 20 tAppId getServiceAppId(struct AppIdData *appIdData) { if (appIdData) return pickServiceAppId(appIdData); return APP_ID_NONE; } tAppId getOnlyServiceAppId(struct AppIdData *appIdData) { if (appIdData) return pickOnlyServiceAppId(appIdData); return APP_ID_NONE; } tAppId getMiscAppId(struct AppIdData *appIdData) { if (appIdData) return pickMiscAppId(appIdData); return APP_ID_NONE; } tAppId getClientAppId(struct AppIdData *appIdData) { if (appIdData) return pickClientAppId(appIdData); return APP_ID_NONE; } tAppId getPayloadAppId(struct AppIdData *appIdData) { if (appIdData) return pickPayloadId(appIdData); return APP_ID_NONE; } tAppId getReferredAppId(struct AppIdData *appIdData) { if (appIdData) return pickReferredPayloadId(appIdData); return APP_ID_NONE; } tAppId getFwServiceAppId(struct AppIdData *appIdData) { if (appIdData) return fwPickServiceAppId(appIdData); return APP_ID_NONE; } tAppId getFwMiscAppId(struct AppIdData *appIdData) { if (appIdData) return fwPickMiscAppId(appIdData); return APP_ID_NONE; } tAppId getFwClientAppId(struct AppIdData *appIdData) { if (appIdData) return fwPickClientAppId(appIdData); return APP_ID_NONE; } tAppId getFwPayloadAppId(struct AppIdData *appIdData) { if (appIdData) return fwPickPayloadAppId(appIdData); return APP_ID_NONE; } tAppId getFwReferredAppId(struct AppIdData *appIdData) { if (appIdData) return fwPickReferredPayloadAppId(appIdData); return APP_ID_NONE; } char* getTlsHost(struct AppIdData *appIdData) { if (appIdData && appIdData->tsession) { switch (appIdData->tsession->matched_tls_type) { case MATCHED_TLS_HOST: return appIdData->tsession->tls_host; case MATCHED_TLS_FIRST_SAN: return appIdData->tsession->tls_first_san; case MATCHED_TLS_CNAME: return appIdData->tsession->tls_cname; default: /*tls_orgUnit is intentionally avoided from being returned as an URL here, even if its the matching one*/ if (appIdData->tsession->tls_host) return appIdData->tsession->tls_host; else if (appIdData->tsession->tls_first_san) return appIdData->tsession->tls_first_san; else if (appIdData->tsession->tls_cname) return appIdData->tsession->tls_cname; return NULL; } } return NULL; } SFGHASH* getFwMultiPayloadList(struct AppIdData *appIdData) { if (appIdData) return fwPickMultiPayloadList(appIdData); return NULL; } bool isSessionSslDecrypted(struct AppIdData *appIdData) { if (appIdData) return isFwSessionSslDecrypted(appIdData); return false; } struct AppIdData * getAppIdData(void* lwssn) { tAppIdData *appIdData = _dpd.sessionAPI->get_application_data(lwssn, PP_APP_ID); return (appIdData && appIdData->common.fsf_type.flow_type == APPID_SESSION_TYPE_NORMAL)? appIdData : NULL; } int getAppIdSessionPacketCount(struct AppIdData * appIdData) { return appIdData ? appIdData->session_packet_count : 0; } bool isHttpInspectionDone(struct AppIdData *appIdSession) { if (!appIdSession) return true; // No wait for http discovery if AppId data is unavailable if ((appIdSession->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) || (TPIsAppIdDone(appIdSession->tpsession) && !(getAppIdFlag(appIdSession, APPID_SESSION_SSL_SESSION) && !getTlsHost(appIdSession) && appIdSession->rnaServiceState != RNA_STATE_FINISHED))) return true; return false; } bool IsAppIdInspectingSession(struct AppIdData *appIdSession) { if (appIdSession && appIdSession->common.fsf_type.flow_type == APPID_SESSION_TYPE_NORMAL) { if (appIdSession->rnaServiceState != RNA_STATE_FINISHED || !TPIsAppIdDone(appIdSession->tpsession) || getAppIdFlag(appIdSession, APPID_SESSION_HTTP_SESSION | APPID_SESSION_CONTINUE) || (getAppIdFlag(appIdSession, APPID_SESSION_ENCRYPTED) && (getAppIdFlag(appIdSession, APPID_SESSION_DECRYPTED) || appIdSession->session_packet_count < SSL_WHITELIST_PKT_LIMIT))) { return true; } if (appIdSession->rnaClientState != RNA_STATE_FINISHED && (!getAppIdFlag(appIdSession, APPID_SESSION_CLIENT_DETECTED) || (appIdSession->rnaServiceState != RNA_STATE_STATEFUL && getAppIdFlag(appIdSession, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS)))) { return true; } if (appIdSession->tpAppId == APP_ID_SSH && appIdSession->payloadAppId != APP_ID_SFTP && appIdSession->session_packet_count < MAX_SFTP_PACKET_COUNT) { return true; } if (appidStaticConfig->recheck_for_unknown_appid) { if( (appIdSession->serviceAppId == APP_ID_UNKNOWN_UI || appIdSession->serviceAppId <= APP_ID_NONE) && appIdSession->clientAppId <= APP_ID_NONE && appIdSession->payloadAppId <= APP_ID_NONE && appIdSession->tpAppId <= APP_ID_NONE && (appIdSession->portServiceAppId <= APP_ID_NONE || appidStaticConfig->recheck_for_portservice_appid) && appIdSession->clientServiceAppId <= APP_ID_NONE && appIdSession->tpPayloadAppId <= APP_ID_NONE ) return true; if( appidStaticConfig->check_host_cache_unknown_ssl && getAppIdFlag(appIdSession, APPID_SESSION_SSL_SESSION) && !(appIdSession->tsession && appIdSession->tsession->tls_host && appIdSession->tsession->tls_cname)) return true; } if (appidStaticConfig->check_host_port_app_cache) { return true; } } return false; } char* getUserName(struct AppIdData *appIdData, tAppId *service, bool *isLoginSuccessful) { char *userName = NULL; if (appIdData) { userName = appIdData->username; *service = appIdData->usernameService; *isLoginSuccessful = getAppIdFlag(appIdData, APPID_SESSION_LOGIN_SUCCEEDED) ? true : false; appIdData->username = NULL; //transfer ownership to caller. return userName; } return NULL; } bool isAppIdAvailable(struct AppIdData *appIdData) { if (appIdData) { return (appIdData->serviceAppId != APP_ID_NONE || appIdData->payloadAppId != APP_ID_NONE) && (TPIsAppIdAvailable(appIdData->tpsession) || getAppIdFlag(appIdData, APPID_SESSION_NO_TPI)); } return false; } char* getClientVersion(struct AppIdData *appIdData) { return appIdData? appIdData->clientVersion: NULL; } uint64_t getAppIdSessionAttribute(struct AppIdData *appIdData, uint64_t flags) { return appIdData? getAppIdFlag(appIdData, flags): 0; } APPID_FLOW_TYPE getFlowType(struct AppIdData *appIdData) { return appIdData ? appIdData->common.fsf_type.flow_type: APPID_FLOW_TYPE_IGNORE; } void getServiceInfo(struct AppIdData *appIdData, char **serviceVendor, char **serviceVersion, RNAServiceSubtype **serviceSubtype) { if (appIdData) { *serviceVendor = appIdData->serviceVendor; *serviceVersion = appIdData->serviceVersion; *serviceSubtype = appIdData->subtype; } else { *serviceVendor = NULL; *serviceVersion = NULL; *serviceSubtype = NULL; } } short getServicePort(struct AppIdData *appIdData) { if (appIdData) return appIdData->service_port; return 0; } char* getHttpUserAgent(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->useragent; return NULL; } char* getHttpHost(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->host; return NULL; } char* getHttpUrl(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->url; return NULL; } char* getHttpReferer(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->referer; return NULL; } char* getHttpNewUrl(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->new_field[REQ_URI_FID]; return NULL; } char* getHttpUri(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->uri; return NULL; } char* getHttpResponseCode(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->response_code; return NULL; } char* getHttpCookie(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->cookie; return NULL; } char* getHttpNewCookie(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->new_field[REQ_COOKIE_FID]; return NULL; } char* getHttpNewField(struct AppIdData *appIdData, HTTP_FIELD_ID fieldId) { if (appIdData && appIdData->hsession && fieldId >= 0 && fieldId <= HTTP_FIELD_MAX) return appIdData->hsession->new_field[fieldId]; return NULL; } void freeHttpNewField(struct AppIdData *appIdData, HTTP_FIELD_ID fieldId) { if (appIdData && appIdData->hsession && fieldId >= 0 && fieldId <= HTTP_FIELD_MAX && NULL != appIdData->hsession->new_field[fieldId]) { free(appIdData->hsession->new_field[fieldId]); appIdData->hsession->new_field[fieldId] = NULL; } } char* getHttpContentType(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->content_type; return NULL; } char* getHttpLocation(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->location; return NULL; } char* getHttpBody(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->body; return NULL; } char* getHttpReqBody(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->req_body; return NULL; } uint16_t getHttpUriOffset(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->fieldOffset[REQ_URI_FID]; return 0; } uint16_t getHttpUriEndOffset(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->fieldEndOffset[REQ_URI_FID]; return 0; } uint16_t getHttpCookieOffset(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->fieldOffset[REQ_COOKIE_FID]; return 0; } uint16_t getHttpCookieEndOffset(struct AppIdData *appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->fieldEndOffset[REQ_COOKIE_FID]; return 0; } uint16_t getHttpFieldOffset(struct AppIdData *appIdData, HTTP_FIELD_ID fieldId) { if (appIdData && appIdData->hsession && fieldId >= 0 && fieldId <= HTTP_FIELD_MAX) return appIdData->hsession->fieldOffset[fieldId]; return 0; } uint16_t getHttpFieldEndOffset(struct AppIdData *appIdData, HTTP_FIELD_ID fieldId) { if (appIdData && appIdData->hsession && fieldId >= 0 && fieldId <= HTTP_FIELD_MAX) return appIdData->hsession->fieldEndOffset[fieldId]; return 0; } SEARCH_SUPPORT_TYPE getHttpSearch(struct AppIdData *appIdData) { if (appIdData) return (appIdData->search_support_type != SEARCH_SUPPORT_TYPE_UNKNOWN) ? appIdData->search_support_type : NOT_A_SEARCH_ENGINE; return NOT_A_SEARCH_ENGINE; } sfaddr_t* getHttpXffAddr(struct AppIdData* appIdData) { if (appIdData && appIdData->hsession) return appIdData->hsession->xffAddr; return NULL; } tAppId getPortServiceAppId(struct AppIdData *appIdData) { if (appIdData) return appIdData->portServiceAppId; return APP_ID_NONE; } sfaddr_t* getServiceIp(struct AppIdData *appIdData) { if (appIdData) return &appIdData->service_ip; return NULL; } struct in6_addr* getInitiatorIp(struct AppIdData *appIdData) { return appIdData ? &appIdData->common.initiator_ip : NULL; } DhcpFPData* getDhcpFpData(struct AppIdData *appIdData) { DhcpFPData *data; if (appIdData && getAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_FP)) { data = AppIdFlowdataRemove(appIdData, APPID_SESSION_DATA_DHCP_FP_DATA); return data; } return NULL; } void freeDhcpFpData(struct AppIdData *appIdData, DhcpFPData *data) { if (appIdData) { clearAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_FP); AppIdFreeDhcpData(data); } } DHCPInfo* getDhcpInfo(struct AppIdData *appIdData) { DHCPInfo *data; if (appIdData && getAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_INFO)) { data = AppIdFlowdataRemove(appIdData, APPID_SESSION_DATA_DHCP_INFO); return data; } return NULL; } void freeDhcpInfo(struct AppIdData *appIdData, DHCPInfo *data) { if (appIdData) { clearAppIdFlag(appIdData, APPID_SESSION_HAS_DHCP_INFO); AppIdFreeDhcpInfo(data); } } FpSMBData* getSmbFpData(struct AppIdData *appIdData) { FpSMBData *data; if (appIdData && getAppIdFlag(appIdData, APPID_SESSION_HAS_SMB_INFO)) { data = AppIdFlowdataRemove(appIdData, APPID_SESSION_DATA_SMB_DATA); return data; } return NULL; } void freeSmbFpData(struct AppIdData *appIdData, FpSMBData *data) { if (appIdData) { clearAppIdFlag(appIdData, APPID_SESSION_HAS_SMB_INFO); AppIdFreeSMBData(data); } } char* getNetbiosName(struct AppIdData *appIdData) { if (appIdData) { char *netbiosName = appIdData->netbios_name; appIdData->netbios_name = NULL; //transfer ownership to caller. return netbiosName; } return NULL; } uint32_t produceHAState(void *lwssn, uint8_t *buf) { AppIdSessionHA *appHA = (AppIdSessionHA *)buf; struct AppIdData *appIdData = _dpd.sessionAPI->get_application_data(lwssn, PP_APP_ID); if (appIdData && _dpd.appIdApi->getFlowType(appIdData) != APPID_FLOW_TYPE_NORMAL) appIdData = NULL; if (appIdData) { appHA->flags = APPID_HA_FLAGS_APP; if (TPIsAppIdAvailable(appIdData->tpsession)) appHA->flags |= APPID_HA_FLAGS_TP_DONE; if (getAppIdFlag(appIdData, APPID_SESSION_SERVICE_DETECTED)) appHA->flags |= APPID_HA_FLAGS_SVC_DONE; if (getAppIdFlag(appIdData, APPID_SESSION_HTTP_SESSION)) appHA->flags |= APPID_HA_FLAGS_HTTP; appHA->appId[0] = appIdData->tpAppId; appHA->appId[1] = appIdData->serviceAppId; appHA->appId[2] = appIdData->clientServiceAppId; appHA->appId[3] = appIdData->portServiceAppId; appHA->appId[4] = appIdData->payloadAppId; appHA->appId[5] = appIdData->tpPayloadAppId; appHA->appId[6] = appIdData->clientAppId; appHA->appId[7] = appIdData->miscAppId; } else { memset(appHA, 0, sizeof(*appHA)); } return sizeof(*appHA); } uint32_t consumeHAState(void *lwssn, const uint8_t *buf, uint8_t length, uint8_t proto, const struct in6_addr *ip, uint16_t initiatorPort) { AppIdSessionHA *appHA = (AppIdSessionHA *)buf; if (appHA->flags & APPID_HA_FLAGS_APP) { struct AppIdData *appIdData = (tAppIdData*)_dpd.sessionAPI->get_application_data(lwssn, PP_APP_ID); if (appIdData && _dpd.appIdApi->getFlowType(appIdData) != APPID_FLOW_TYPE_NORMAL) return sizeof(*appHA); if (!appIdData) { appIdData = appSharedDataAlloc(proto, ip, initiatorPort); _dpd.sessionAPI->set_application_data(lwssn, PP_APP_ID, appIdData, (void (*)(void *))appSharedDataDelete); appIdData->serviceAppId = appHA->appId[1]; if (appIdData->serviceAppId == APP_ID_FTP_CONTROL) { setAppIdFlag(appIdData, APPID_SESSION_CLIENT_DETECTED | APPID_SESSION_NOT_A_SERVICE | APPID_SESSION_SERVICE_DETECTED); if (!AddFTPServiceState(appIdData)) { setAppIdFlag(appIdData, APPID_SESSION_CONTINUE); } appIdData->rnaServiceState = RNA_STATE_STATEFUL; } else appIdData->rnaServiceState = RNA_STATE_FINISHED; appIdData->rnaClientState = RNA_STATE_FINISHED; if (thirdparty_appid_module) thirdparty_appid_module->session_state_set(appIdData->tpsession, TP_STATE_HA); } if (appHA->flags & APPID_HA_FLAGS_TP_DONE && thirdparty_appid_module) { thirdparty_appid_module->session_state_set(appIdData->tpsession, TP_STATE_TERMINATED); setAppIdFlag(appIdData, APPID_SESSION_NO_TPI); } if (appHA->flags & APPID_HA_FLAGS_SVC_DONE) setAppIdFlag(appIdData, APPID_SESSION_SERVICE_DETECTED); if (appHA->flags & APPID_HA_FLAGS_HTTP) setAppIdFlag(appIdData, APPID_SESSION_HTTP_SESSION); appIdData->tpAppId = appHA->appId[0]; appIdData->serviceAppId = appHA->appId[1]; appIdData->clientServiceAppId = appHA->appId[2]; appIdData->portServiceAppId = appHA->appId[3]; appIdData->payloadAppId = appHA->appId[4]; appIdData->tpPayloadAppId = appHA->appId[5]; appIdData->clientAppId = appHA->appId[6]; appIdData->miscAppId = appHA->appId[7]; } return sizeof(*appHA); } char* getDNSQuery(struct AppIdData *appIdData, uint8_t *query_len, bool *got_response) { if (appIdData && appIdData->dsession) { if (query_len) { if (appIdData->dsession->host) *query_len = appIdData->dsession->host_len; else *query_len = 0; } if (got_response) *got_response = (appIdData->dsession->state & DNS_GOT_RESPONSE) ? true : false; return appIdData->dsession->host; } if (query_len) *query_len = 0; if (got_response) *got_response = false; return NULL; } uint16_t getDNSQueryoffset(struct AppIdData *appIdData) { if (appIdData && appIdData->dsession) return appIdData->dsession->host_offset; return 0; } uint16_t getDNSRecordType(struct AppIdData *appIdData) { if (appIdData && appIdData->dsession) return appIdData->dsession->record_type; return 0; } uint8_t getDNSResponseType(struct AppIdData *appIdData) { if (appIdData && appIdData->dsession) return appIdData->dsession->response_type; return 0; } uint32_t getDNSTTL(struct AppIdData *appIdData) { if (appIdData && appIdData->dsession) return appIdData->dsession->ttl; return 0; } uint16_t getDNSOptionsOffset(struct AppIdData* appIdData) { if (appIdData && appIdData->dsession) return appIdData->dsession->options_offset; return 0; } static void dumpDebugHostInfo(void) { char ipStr[INET6_ADDRSTRLEN]; ipStr[0] = '\0'; if (AppIdDebugHostInfo.family == AF_INET) inet_ntop(AF_INET, (const struct in6_addr*) &AppIdDebugHostInfo.initiatorIp.s6_addr32[3], ipStr, sizeof(ipStr)); else inet_ntop(AF_INET6, &AppIdDebugHostInfo.initiatorIp, ipStr, sizeof(ipStr)); _dpd.logMsg("AppIdDebugHost: session %s, initiator %s:%u, direction %d, protocol %u, monitorType %d\n", AppIdDebugHostInfo.session ? "not null" : "null", ipStr, AppIdDebugHostInfo.initiatorPort, AppIdDebugHostInfo.direction, AppIdDebugHostInfo.protocol, AppIdDebugHostInfo.monitorType); } static struct AppIdApi appIdDispatchTable = { appGetAppName, appGetAppId, getServiceAppId, getPortServiceAppId, getOnlyServiceAppId, getMiscAppId, getClientAppId, getPayloadAppId, getReferredAppId, getFwServiceAppId, getFwMiscAppId, getFwClientAppId, getFwPayloadAppId, getFwReferredAppId, getFwMultiPayloadList, isSessionSslDecrypted, IsAppIdInspectingSession, isAppIdAvailable, getUserName, getClientVersion, getAppIdSessionAttribute, getFlowType, getServiceInfo, getServicePort, getServiceIp, getInitiatorIp, getHttpUserAgent, getHttpHost, getHttpUrl, getHttpReferer, getHttpNewUrl, getHttpUri, getHttpResponseCode, getHttpCookie, getHttpNewCookie, getHttpContentType, getHttpLocation, getHttpBody, getHttpReqBody, getHttpUriOffset, getHttpUriEndOffset, getHttpCookieOffset, getHttpCookieEndOffset, getHttpSearch, getHttpXffAddr, getTlsHost, getDhcpFpData, freeDhcpFpData, getDhcpInfo, freeDhcpInfo, getSmbFpData, freeSmbFpData, getNetbiosName, produceHAState, consumeHAState, getAppIdData, getAppIdSessionPacketCount, getDNSQuery, getDNSQueryoffset, getDNSRecordType, getDNSResponseType, getDNSTTL, getDNSOptionsOffset, getHttpNewField, freeHttpNewField, getHttpFieldOffset, getHttpFieldEndOffset, isHttpInspectionDone, dumpDebugHostInfo }; void appIdApiInit(struct AppIdApi *api) { *api = appIdDispatchTable; } snort-2.9.20/src/dynamic-preprocessors/appid/luaDetectorFlowApi.h0000644000175000017500000000317714241075453023233 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _LUA_DETECTOR_APPID_SESSION_API_H_ #define _LUA_DETECTOR_APPID_SESSION_API_H_ #include "flow.h" #include "luaDetectorApi.h" typedef struct { /**Lua_state. */ lua_State *myLuaState; /**Pointer to flow created by a validator. */ tAppIdData *pFlow; /**Reference to lua userdata. This is a key into LUA_REGISTRYINDEX */ int userDataRef; } DetectorFlow; /**Allocated on Lua side.*/ typedef struct { /**Pointer to DetectorFlow created on C side. */ DetectorFlow *pDetectorFlow; } DetectorFlowUserData; int DetectorFlow_register ( lua_State *L ); DetectorFlowUserData *pushDetectorFlowUserData ( lua_State *L ); void freeDetectorFlow( void *userdata ); #endif snort-2.9.20/src/dynamic-preprocessors/appid/luaDetectorFlowApi.c0000644000175000017500000004350014241075452023217 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** @defgroup LuaDetectorFlowApi LuaDetectorFlowApi * This module supports API towards Lua detectors for performing specific operations on a flow object. * The flow object on Lua side is a userData. *@{ */ #define DETECTORFLOW "DetectorFlow" #include "luaDetectorApi.h" #include "luaDetectorFlowApi.h" #include "luaDetectorModule.h" #include "common_util.h" /*static const char * LuaLogLabel = "luaDetectorFlowApi"; */ /* Lua flag bit/index to C flag value (0 for invalid). */ static const uint64_t FLAGS_TABLE_LUA_TO_C[32] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ 0, /* 3 */ 0, /* 4 */ 0, /* 5 */ 0, /* 6 */ 0, /* 7 */ 0, /* 8 */ 0, /* 9 */ 0, /* 10 */ 0, /* 11 */ 0, /* 12 */ 0, /* 13 */ 0, /* 14 */ 0, /* 15 */ 0, /* 16 */ 0, /* 17 */ 0, /* 18 */ 0, /* 19 */ APPID_SESSION_SSL_SESSION, /* 20: sslSession */ APPID_SESSION_HTTP_SESSION, /* 21: httpSession */ APPID_SESSION_UDP_REVERSED, /* 22: udpReversed */ APPID_SESSION_INCOMPATIBLE, /* 23: incompatible */ APPID_SESSION_IGNORE_HOST, /* 24: ignoreHost */ 0, /* 25: ignoreTcpSeq -- OBSOLETE */ APPID_SESSION_CLIENT_DETECTED, /* 26: clientAppDetected */ 0, /* 27: gotBanner -- OBSOLETE */ APPID_SESSION_NOT_A_SERVICE, /* 28: notAService */ 0, /* 29: logUnknown -- OBSOLETE */ APPID_SESSION_CONTINUE, /* 30: continue */ APPID_SESSION_SERVICE_DETECTED /* 31: serviceDetected */ }; /* C flag bit/index to Lua flag value (0 for invalid). */ static const uint64_t FLAGS_TABLE_C_TO_LUA[32] = { 0, /* 0 */ 0, /* 1 */ 0, /* 2 */ 0, /* 3 */ 0, /* 4 */ 0, /* 5 */ 0, /* 6 */ 0, /* 7 */ 0, /* 8 */ 0, /* 9 */ 0, /* 10 */ 0, /* 11 */ 0x00400000, /* 12: APPID_SESSION_UDP_REVERSED */ 0x00200000, /* 13: APPID_SESSION_HTTP_SESSION */ 0x80000000, /* 14: APPID_SESSION_SERVICE_DETECTED */ 0x04000000, /* 15: APPID_SESSION_CLIENT_DETECTED */ 0x10000000, /* 16: APPID_SESSION_NOT_A_SERVICE */ 0, /* 17 */ 0, /* 18 */ 0x40000000, /* 19: APPID_SESSION_CONTINUE */ 0x01000000, /* 20: APPID_SESSION_IGNORE_HOST */ 0x00800000, /* 21: APPID_SESSION_INCOMPATIBLE */ 0, /* 22 */ 0, /* 23 */ 0, /* 24 */ 0, /* 25 */ 0, /* 26 */ 0, /* 27 */ 0, /* 28 */ 0, /* 29 */ 0, /* 30 */ 0 /* 31 */ }; /* Convert flag bits used by the Lua code into what the C code uses. */ static inline uint64_t ConvertFlagsLuaToC(uint64_t in) { uint64_t out = 0; unsigned i; uint64_t msk; msk = 1; for (i = 0; i < 32; i++) { if (in & msk) out |= FLAGS_TABLE_LUA_TO_C[i]; msk <<= 1; } return out; } /* Convert flag bits used by the C code into what the Lua code uses. */ static inline uint64_t ConvertFlagsCToLua(uint64_t in) { uint64_t out = 0; unsigned i; uint64_t msk; msk = 1; for (i = 0; i < 32; i++) { if (in & msk) out |= FLAGS_TABLE_C_TO_LUA[i]; msk <<= 1; } return out; } static DetectorFlowUserData *toDetectorFlowUserData (lua_State *L, int index) { DetectorFlowUserData *pLuaData = (DetectorFlowUserData *)lua_touserdata(L, index); if (pLuaData == NULL) { luaL_typerror(L, index, DETECTORFLOW); } return pLuaData; } static DetectorFlowUserData *checkDetectorFlowUserData (lua_State *L, int index) { DetectorFlowUserData *pLuaData; luaL_checktype(L, index, LUA_TUSERDATA); pLuaData = (DetectorFlowUserData *)luaL_checkudata(L, index, DETECTORFLOW); if (pLuaData == NULL) { luaL_typerror(L, index, DETECTORFLOW); } return pLuaData; } /**Internal function to create user data for a flow. * * @param Lua_State* - Lua state variable. * @return pointer to allocated DetectorFlowUserData object. */ DetectorFlowUserData *pushDetectorFlowUserData (lua_State *L) { DetectorFlowUserData *pLuaData = (DetectorFlowUserData *)lua_newuserdata(L, sizeof(DetectorFlowUserData)); DetectorFlow *pDetectorFlow; if (pLuaData) { #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"DetectotFlowUserData %p: allocated\n\n",pLuaData); #endif memset(pLuaData, 0, sizeof(*pLuaData)); if ((pLuaData->pDetectorFlow = (DetectorFlow *)_dpd.snortAlloc(1, sizeof(*pDetectorFlow), PP_APP_ID, PP_MEM_CATEGORY_SESSION)) == NULL) { lua_pop(L, -1); return NULL; } luaL_getmetatable(L, DETECTORFLOW); lua_setmetatable(L, -2); pDetectorFlow = pLuaData->pDetectorFlow; pDetectorFlow->myLuaState = L; lua_pushvalue( L, -1 ); /*create a copy of userData */ pDetectorFlow->userDataRef = luaL_ref( L, LUA_REGISTRYINDEX ); #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"DetectorFlow %p: allocated\n\n",pDetectorFlow); #endif sflist_add_tail(&allocatedFlowList, pDetectorFlow); } return pLuaData; } /**Creates a user data for a flow. * * @param Lua_State* - Lua state variable. * @param detector/stack - detector object * @param srcAddress/stack - source address of the flow * @param srcPort/stack - source port of the the flow * @param dstAddress/stack - destination address of the flow. * @param dstPort/stack - detector port of the flow. * @param proto/stack - protocol type. See defined IPPROTO_xxxx in /usr/include/netinet/in.h * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return DetectorFlowUserData/stack - A userdata representing DetectorFlowUserData. */ static int DetectorFlow_new (lua_State *L) { DetectorFlowUserData *pLuaData = NULL; sfaddr_t saddr; sfaddr_t daddr; uint8_t proto; uint16_t sport, dport; DetectorUserData *detectorUserData = NULL; DetectorFlow *pDetectorFlow; char *pattern; size_t patternLen; detectorUserData = checkDetectorUserData(L, 1); /*check inputs and whether this function is called in context of a packet */ if (!detectorUserData || !detectorUserData->pDetector->validateParams.pkt) { return 0; /*number of results */ } pattern = (char *)lua_tostring(L, 2); patternLen = lua_strlen (L, 2); if (patternLen == 16) { if (sfip_set_raw(&saddr, pattern, AF_INET6) != SFIP_SUCCESS) return 0; } else if (patternLen == 4) { if (sfip_set_raw(&saddr, pattern, AF_INET) != SFIP_SUCCESS) return 0; } else { return 0; } pattern = (char *)lua_tostring(L, 3); patternLen = lua_strlen (L, 3); if (patternLen == 16) { if (sfip_set_raw(&daddr, pattern, AF_INET6) != SFIP_SUCCESS) return 0; } else if (patternLen == 4) { if (sfip_set_raw(&daddr, pattern, AF_INET) != SFIP_SUCCESS) return 0; } else { return 0; } sport = lua_tonumber(L, 4); dport = lua_tonumber(L, 5); proto = lua_tonumber(L, 6); pLuaData = pushDetectorFlowUserData(L); if (!pLuaData) { _dpd.errMsg( "Failed to allocate memory."); return 0; } pDetectorFlow = pLuaData->pDetectorFlow; pDetectorFlow->pFlow = AppIdEarlySessionCreate((tAppIdData*) pDetectorFlow, detectorUserData->pDetector->validateParams.pkt, &saddr, sport, &daddr, dport, proto, 0, 0); if (!pDetectorFlow->pFlow) { /*calloced buffer will be freed later after the current packet is processed. */ lua_pop(L, 1); return 0; } return 1; } /**free DetectorFlow and its corresponding user data. */ void freeDetectorFlow( void *userdata ) { DetectorFlow *pDetectorFlow = (DetectorFlow *)userdata; /*The detectorUserData itself is a userdata and therefore be freed by Lua side. */ if (pDetectorFlow->userDataRef != LUA_REFNIL) { DetectorFlowUserData *pLuaData; lua_rawgeti(pDetectorFlow->myLuaState, LUA_REGISTRYINDEX, pDetectorFlow->userDataRef); pLuaData = checkDetectorFlowUserData(pDetectorFlow->myLuaState, -1); if (pLuaData) { pLuaData->pDetectorFlow = NULL; } lua_pop(pDetectorFlow->myLuaState, 1); /*remove result of rawgeti */ luaL_unref (pDetectorFlow->myLuaState, LUA_REGISTRYINDEX, pDetectorFlow->userDataRef); pDetectorFlow->userDataRef = LUA_REFNIL; } /*free detectorFlow */ #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"DetectorFlow %p: freeing\n\n",pDetectorFlow); #endif _dpd.snortFree(pDetectorFlow, sizeof(*pDetectorFlow), PP_APP_ID, PP_MEM_CATEGORY_SESSION); } /**Sets a flow flag. * * @param Lua_State* - Lua state variable. * @param detectorFlow/stack - DetectorFlowUserData object * @param flags/stack - flags to be set. * @return int - Number of elements on stack, which is 0 */ static int DetectorFlow_setFlowFlag( lua_State *L ) { DetectorFlowUserData *pLuaData; uint64_t flags; pLuaData = checkDetectorFlowUserData(L, 1); if (!pLuaData || !pLuaData->pDetectorFlow) { return 0; } flags = lua_tonumber(L, 2); flags = ConvertFlagsLuaToC(flags); setAppIdFlag(pLuaData->pDetectorFlow->pFlow, flags); return 0; } /**Gets a flow flag value. * * @param Lua_State* - Lua state variable. * @param detectorFlow/stack - DetectorFlowUserData object * @param flags/stack - flags to get. * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return flagValue/stack - value of a given flag. */ static int DetectorFlow_getFlowFlag( lua_State *L ) { DetectorFlowUserData *pLuaData; uint64_t flags; uint64_t ret; pLuaData = checkDetectorFlowUserData(L, 1); if (!pLuaData || !pLuaData->pDetectorFlow) { _dpd.errMsg( "getFlowFlag called without detectorFlowUserData"); return 0; } flags = lua_tonumber(L, 2); flags = ConvertFlagsLuaToC(flags); ret = getAppIdFlag(pLuaData->pDetectorFlow->pFlow, flags); ret = ConvertFlagsCToLua(ret); lua_pushnumber(L, ret); return 1; } /**Clear a flow flag. * * @param Lua_State* - Lua state variable. * @param detectorFlow/stack - DetectorFlowUserData object * @param flags/stack - flags to be cleared. * @return int - Number of elements on stack, which is 0. */ static int DetectorFlow_clearFlowFlag( lua_State *L ) { DetectorFlowUserData *pLuaData; uint64_t flags; pLuaData = checkDetectorFlowUserData(L, 1); if (!pLuaData || !pLuaData->pDetectorFlow) { return 0; } flags = lua_tonumber(L, 2); flags = ConvertFlagsLuaToC(flags); clearAppIdFlag(pLuaData->pDetectorFlow->pFlow, flags); return 0; } /**Set service id on a flow. * * @param Lua_State* - Lua state variable. * @param detectorFlow/stack - DetectorFlowUserData object * @param serviceId/stack - service Id to be set on a flow. * @return int - Number of elements on stack, which is 0. */ static int DetectorFlow_setFlowServiceId( lua_State *L ) { return 0; } /**Set client application id on a flow. * * @param Lua_State* - Lua state variable. * @param detectorFlow/stack - DetectorFlowUserData object * @param applId/stack - client application Id to be set on a flow. * @return int - Number of elements on stack, which is 0. */ static int DetectorFlow_setFlowClntAppId( lua_State *L ) { return 0; } /**Set client application type id on a flow. * * @param Lua_State* - Lua state variable. * @param detectorFlow/stack - DetectorFlowUserData object * @param applTypeId/stack - client application type id to be set on a flow. * @return int - Number of elements on stack, which is 0. */ static int DetectorFlow_setFlowClntAppType( lua_State *L ) { return 0; } /**Design: For simplicity reason I am passing flowkey (20 bytes) to lua detectors. * The key is used to index into local lua table and get any flow specific data that a detector needs. * This approach avoids embedding lua detector data into core engine flow data structure. * * For optimization, I could have created an integer index on C side. This can be taken up in future. */ /**Get flow key from a DetectorFlowUserData object. * * @param Lua_State* - Lua state variable. * @param detectorflow/stack - DetectorFlowUserData object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return flowKey/stack - A 20 byte flow key */ static int DetectorFlow_getFlowKey( lua_State *L ) { DetectorFlowUserData *pLuaData; pLuaData = checkDetectorFlowUserData(L, 1); if (!pLuaData || !pLuaData->pDetectorFlow) { return 0; } lua_pushlstring(L, (char *)&pLuaData->pDetectorFlow->pFlow->flowId, sizeof(pLuaData->pDetectorFlow->pFlow->flowId)); return 1; } static const luaL_Reg DetectorFlow_methods[] = { /* Obsolete API names. No longer use these! They are here for backward * compatibility and will eventually be removed. */ /* - "new" is now "createFlow" (below) */ {"new", DetectorFlow_new}, {"createFlow", DetectorFlow_new}, {"setFlowFlag", DetectorFlow_setFlowFlag}, {"getFlowFlag", DetectorFlow_getFlowFlag}, {"clearFlowFlag", DetectorFlow_clearFlowFlag}, {"setFlowServiceId", DetectorFlow_setFlowServiceId}, {"setFlowClntAppId", DetectorFlow_setFlowClntAppId}, {"setFlowClntAppType", DetectorFlow_setFlowClntAppType}, {"getFlowKey", DetectorFlow_getFlowKey}, {0, 0} }; /** * lua_close will ensure that all detectors and flows get _gc called. */ static int DetectorFlow_gc ( lua_State *L ) { DetectorFlowUserData * pLuaData = toDetectorFlowUserData(L, 1); if (pLuaData) { #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG,"DetectorFlowUserData %p: freeing\n\n",pLuaData); #endif /*Lua frees DetectorFlowUserData buffer. */ } return 0; } static int DetectorFlow_tostring ( lua_State *L ) { char buff[32]; snprintf(buff, sizeof(buff), "%p", toDetectorFlowUserData(L, 1)); lua_pushfstring(L, "DetectorFlowUserData (%s)", buff); return 1; } static const luaL_Reg DetectorFlow_meta[] = { {"__gc", DetectorFlow_gc}, {"__tostring", DetectorFlow_tostring}, {0, 0} }; /**Registers C functions as an API, enabling Lua detector to call these functions. This function * should be called once before loading any lua detectors. This function itself is not part of API * and therefore can not be called by a Lua detection. * * @param Lua_State* - Lua state variable. * @param detectorFlow/stack - DetectorFlowUserData object * @return int - Number of elements on stack, which is 1 if successful, 0 otherwise. * @return methodArray/stack - array of newly created methods */ int DetectorFlow_register ( lua_State *L ) { /* populates a new table with Detector_methods (method_table), add the table to the globals and stack*/ luaL_openlib(L, DETECTORFLOW, DetectorFlow_methods, 0); /* create metatable for Foo, add it to the Lua registry, metatable on stack */ luaL_newmetatable(L, DETECTORFLOW); /* populates table on stack with Detector_meta methods, puts the metatable on stack*/ luaL_openlib(L, NULL, DetectorFlow_meta, 0); lua_pushliteral(L, "__index"); lua_pushvalue(L, -3); /* dup methods table*/ lua_settable(L, -3); /* metatable.__index = methods */ lua_pushliteral(L, "__metatable"); lua_pushvalue(L, -3); /* dup methods table*/ lua_settable(L, -3); /* hide metatable: metatable.__metatable = methods */ lua_pop(L, 1); /* drop metatable */ return 1; /* return methods on the stack */ } /** @} */ /* end of LuaDetectorFlowApi */ snort-2.9.20/src/dynamic-preprocessors/appid/lengthAppCache.h0000644000175000017500000000362514241075446022344 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _LENGTH_APP_CACHE_ #define _LENGTH_APP_CACHE_ #include "appId.h" #include "appIdApi.h" #define LENGTH_SEQUENCE_CNT_MAX (5) #pragma pack(1) // Forward declaration for AppId config. Cannot include appIdConfig.h because of // circular dependency struct appIdConfig_; typedef struct _tLengthSequenceEntry { uint8_t direction; /* APP_ID_FROM_INITIATOR or APP_ID_FROM_RESPONDER */ uint16_t length; /* payload size (bytes) */ } tLengthSequenceEntry; typedef struct _tLengthKey { uint8_t proto; /* IPPROTO_TCP or IPPROTO_UDP */ uint8_t sequence_cnt; /* num valid entries in sequence */ tLengthSequenceEntry sequence[LENGTH_SEQUENCE_CNT_MAX]; } tLengthKey; #pragma pack() void lengthAppCacheInit(struct appIdConfig_ *pConfig); void lengthAppCacheFini(struct appIdConfig_ *pConfig); tAppId lengthAppCacheFind(const tLengthKey *key, const struct appIdConfig_ *pConfig); int lengthAppCacheAdd(const tLengthKey *key, tAppId val, struct appIdConfig_ *pConfig); #endif snort-2.9.20/src/dynamic-preprocessors/appid/flow_error.h0000644000175000017500000000250014241075437021645 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _APPID_SESSION_ERROR_H #define _APPID_SESSION_ERROR_H #define APPID_SESSION_SUCCESS 0 #define APPID_SESSION_ENULL 1 #define APPID_SESSION_EINVALID 2 #define APPID_SESSION_ENOMEM 3 #define APPID_SESSION_NOTFOUND 4 #define APPID_SESSION_BADJUJU 5 #define APPID_SESSION_DISABLED 6 #define APPID_SESSION_EUNSUPPORTED 7 #define APPID_SESSION_STOP_PROCESSING 8 #define APPID_SESSION_EEXISTS 9 #endif /* _APPID_SESSION_ERROR_H */ snort-2.9.20/src/dynamic-preprocessors/appid/flow.h0000644000175000017500000002535114241075436020444 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _APPID_SESSION_H #define _APPID_SESSION_H #include #include #include "sf_snort_packet.h" #include "flow_error.h" #include "appId.h" #include "appIdApi.h" #include "service_state.h" #include "lengthAppCache.h" #include "thirdparty_appid_api.h" #include "thirdparty_appid_types.h" #include "sflsq.h" #include "sfghash.h" #define SF_DEBUG_FILE stdout #define NUMBER_OF_PTYPES 9 #define APPID_SESSION_DATA_NONE 0 #define APPID_SESSION_DATA_DHCP_FP_DATA 2 #define APPID_SESSION_DATA_SMB_DATA 4 #define APPID_SESSION_DATA_DHCP_INFO 5 #define APPID_SESSION_DATA_SERVICE_MODSTATE_BIT 0x20000000 #define APPID_SESSION_DATA_CLIENT_MODSTATE_BIT 0x40000000 #define APPID_SESSION_DATA_DETECTOR_MODSTATE_BIT 0x80000000 #define APPID_SESSION_BIDIRECTIONAL_CHECKED (APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_RESPONDER_CHECKED) #define APPID_SESSION_DO_RNA (APPID_SESSION_RESPONDER_MONITORED | APPID_SESSION_INITIATOR_MONITORED | APPID_SESSION_DISCOVER_USER | APPID_SESSION_SPECIAL_MONITORED) struct RNAServiceElement; typedef enum { RNA_STATE_NONE = 0, RNA_STATE_DIRECT, RNA_STATE_STATEFUL, RNA_STATE_FINISHED } RNA_INSPECTION_STATE; typedef void (*AppIdFreeFCN)(void *); #define FINGERPRINT_UDP_FLAGS_XENIX 0x00000800 #define FINGERPRINT_UDP_FLAGS_NT 0x00001000 #define FINGERPRINT_UDP_FLAGS_MASK (FINGERPRINT_UDP_FLAGS_XENIX | FINGERPRINT_UDP_FLAGS_NT) typedef struct _AppIdFlowData { struct _AppIdFlowData *next; unsigned fd_id; void *fd_data; AppIdFreeFCN fd_free; } AppIdFlowData; #define APPID_SESSION_TYPE_IGNORE APPID_FLOW_TYPE_IGNORE #define APPID_SESSION_TYPE_NORMAL APPID_FLOW_TYPE_NORMAL #define APPID_SESSION_TYPE_TMP APPID_FLOW_TYPE_TMP typedef struct _APPID_SESSION_STRUCT_FLAG { APPID_FLOW_TYPE flow_type; } APPID_SESSION_STRUCT_FLAG; typedef struct _tCommonAppIdData { APPID_SESSION_STRUCT_FLAG fsf_type; /* This must be first. */ unsigned policyId; //flags shared with other preprocessor via session attributes. uint64_t flags; struct in6_addr initiator_ip; uint16_t initiator_port; } tCommonAppIdData; typedef struct _tTmpAppIdData { tCommonAppIdData common; struct _tTmpAppIdData *next; } tTmpAppIdData; #define SCAN_HTTP_VIA_FLAG (1<<0) #define SCAN_HTTP_USER_AGENT_FLAG (1<<1) #define SCAN_HTTP_HOST_URL_FLAG (1<<2) #define SCAN_SSL_CERTIFICATE_FLAG (1<<3) #define SCAN_SSL_HOST_FLAG (1<<4) #define SCAN_HOST_PORT_FLAG (1<<5) #define SCAN_HTTP_VENDOR_FLAG (1<<6) #define SCAN_HTTP_XWORKINGWITH_FLAG (1<<7) #define SCAN_HTTP_CONTENT_TYPE_FLAG (1<<8) #define SCAN_HTTP_URI_FLAG (1<<9) #define SCAN_CERTVIZ_ENABLED_FLAG (1<<10) #define SCAN_SPOOFED_SNI_FLAG (1<<11) typedef struct _fflow_info { uint32_t sip; uint32_t dip; uint16_t sport; uint16_t dport; uint8_t protocol; tAppId appId; int flow_prepared; } fflow_info; typedef struct _httpFields { char *str; } HttpRewriteableFields; typedef struct _tunnelDest { sfaddr_t ip; uint16_t port; } tunnelDest; typedef struct _httpSession { char *host; char *url; char *uri; uint16_t host_buflen; uint16_t uri_buflen; uint16_t useragent_buflen; uint16_t response_code_buflen; char *via; char *useragent; char *response_code; char *referer; uint16_t referer_buflen; uint16_t cookie_buflen; uint16_t content_type_buflen; uint16_t location_buflen; char *cookie; char *content_type; char *location; char *body; uint16_t body_buflen; uint16_t req_body_buflen; int total_found; char *req_body; char *server; char *x_working_with; char *new_field[HTTP_FIELD_MAX+1]; uint16_t new_field_len[HTTP_FIELD_MAX+1]; uint16_t fieldOffset[HTTP_FIELD_MAX+1]; uint16_t fieldEndOffset[HTTP_FIELD_MAX+1]; bool new_field_contents; bool skip_simple_detect; // Flag to indicate if simple detection of client ID, payload ID, etc // should be skipped fflow_info *fflow; int chp_finished; tAppId chp_candidate; tAppId chp_alt_candidate; int chp_hold_flow; int ptype_req_counts[NUMBER_OF_PTYPES]; unsigned app_type_flags; int get_offsets_from_rebuilt; int num_matches; int num_scans; int numXffFields; sfaddr_t* xffAddr; char** xffPrecedence; tunnelDest *tunDest; bool is_tunnel; #if RESPONSE_CODE_PACKET_THRESHHOLD unsigned response_code_packets; #endif } httpSession; // For dnsSession.state: #define DNS_GOT_QUERY 0x01 #define DNS_GOT_RESPONSE 0x02 typedef struct _dnsSession { uint8_t state; // state uint8_t host_len; // for host uint8_t response_type; // response: RCODE uint16_t id; // DNS msg ID uint16_t host_offset; // for host uint16_t record_type; // query: QTYPE uint16_t options_offset; // offset at which DNS options such as EDNS begin in DNS query uint32_t ttl; // response: TTL char *host; // host (usually query, but could be response for reverse lookup) } dnsSession; struct _RNAServiceSubtype; typedef enum { MATCHED_TLS_NONE = 0, MATCHED_TLS_HOST, MATCHED_TLS_FIRST_SAN, MATCHED_TLS_CNAME, MATCHED_TLS_ORG_UNIT } MATCHED_TLS_TYPE; typedef struct _tlsSession { char *tls_host; int tls_host_strlen; int tls_cname_strlen; char *tls_cname; char *tls_orgUnit; int tls_orgUnit_strlen; int tls_first_san_strlen; char *tls_first_san; MATCHED_TLS_TYPE matched_tls_type; bool tls_handshake_done; } tlsSession; typedef struct AppIdData { tCommonAppIdData common; struct AppIdData *next; void *ssn; sfaddr_t service_ip; uint16_t service_port; uint8_t proto; uint8_t previous_tcp_flags; bool tried_reverse_service; uint8_t tpReinspectByInitiator; AppIdFlowData *flowData; /**AppId matching service side */ tAppId serviceAppId; tAppId portServiceAppId; /**RNAServiceElement for identifying detector*/ const struct RNAServiceElement *serviceData; RNA_INSPECTION_STATE rnaServiceState; FLOW_SERVICE_ID_STATE search_state; char *serviceVendor; char *serviceVersion; struct _RNAServiceSubtype *subtype; char *netbios_name; SF_LIST * candidate_service_list; int got_incompatible_services; /**AppId matching client side */ tAppId clientAppId; tAppId clientServiceAppId; RNA_INSPECTION_STATE rnaClientState; char *clientVersion; /**RNAClientAppModule for identifying client detector*/ const struct RNAClientAppModule *clientData; SF_LIST * candidate_client_list; unsigned int num_candidate_clients_tried; /**AppId matching payload*/ tAppId payloadAppId; tAppId referredPayloadAppId; tAppId miscAppId; //appId determined by 3rd party library tAppId tpAppId; tAppId tpPayloadAppId; char *username; tAppId usernameService; uint32_t flowId; char *netbiosDomain; httpSession *hsession; tlsSession *tsession; unsigned scan_flags; #if RESPONSE_CODE_PACKET_THRESHHOLD unsigned response_code_packets; #endif SFGHASH *multiPayloadList; tAppId referredAppId; tAppId tmpAppId; void *tpsession; uint16_t init_tpPackets; uint16_t resp_tpPackets; uint16_t session_packet_count; uint16_t initiatorPcketCountWithoutReply; char *payloadVersion; uint64_t initiatorBytesWithoutServerReply; int16_t snortId; /* Length-based detectors. */ tLengthKey length_sequence; bool is_http2; //appIds picked from encrypted session. struct { tAppId serviceAppId; tAppId clientAppId; tAppId payloadAppId; tAppId miscAppId; tAppId referredAppId; } encrypted; // New fields introduced for DNS Blacklisting struct { uint32_t firstPktsecond; uint32_t lastPktsecond; uint64_t initiatorBytes; uint64_t responderBytes; } stats; /* Policy and rule ID for related flows (e.g. ftp-data) */ struct AppIdData *expectedFlow; //struct FwEarlyData *fwData; dnsSession *dsession; void * firewallEarlyData; tAppId pastIndicator; tAppId pastForecast; SEARCH_SUPPORT_TYPE search_support_type; uint16_t hostCacheVersion; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t serviceAsId; //This is specific to VRF #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t carrierId; #endif } tAppIdData; /** * Mark a flow with a particular flag * * @param flow * @param flags */ static inline void setAppIdFlag(tAppIdData *flow, uint64_t flags) { flow->common.flags |= flags; } /** * Mark a flow with a particular flag * * @param flow * @param flags */ static inline void clearAppIdFlag(tAppIdData *flow, uint64_t flags) { flow->common.flags &= ~flags; } /** * Check to see if a particular flag exists * * @param flow * @param flags */ static inline uint64_t getAppIdFlag(tAppIdData *flow, uint64_t flags) { return (flow->common.flags & flags); } void AppIdFlowdataFree(tAppIdData *flowp); void AppIdFlowdataFini(void); void *AppIdFlowdataGet(tAppIdData *flowp, unsigned id); int AppIdFlowdataAdd(tAppIdData *flowp, void *data, unsigned id, AppIdFreeFCN fcn); void *AppIdFlowdataRemove(tAppIdData *flowp, unsigned id); void AppIdFlowdataDelete(tAppIdData *flowp, unsigned id); void AppIdFlowdataDeleteAllByMask(tAppIdData *flowp, unsigned mask); tAppIdData *AppIdEarlySessionCreate(tAppIdData *flowp, SFSnortPacket *ctrlPkt, sfaddr_t *cliIp, uint16_t cliPort, sfaddr_t *srvIp, uint16_t srvPort, uint8_t proto, int16_t app_id, int flags); struct RNAServiceElement; int AppIdFlowdataAddId(tAppIdData *flowp, uint16_t port, const struct RNAServiceElement *svc_element); #endif /* _APPID_SESSION_H */ snort-2.9.20/src/dynamic-preprocessors/appid/Makefile.am0000444000175000017500000000172014230012554021336 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies include $(srcdir)/Makefile_defs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_appid_preproc.la libsf_appid_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_appid_preproc_la_LIBADD = ../libsf_dynamic_preproc.la ../libsf_dynamic_utils.la $(LUA_LIBS) else nodist_libsf_appid_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sf_ip.c \ ../include/sfPolicyUserData.c \ ../include/sfxhash.c \ ../include/sfghash.c \ ../include/sflsq.c \ ../include/sfhashfcn.c \ ../include/sfmemcap.c \ ../include/sfprimetable.c libsf_appid_preproc_la_LIBADD = $(LUA_LIBS) endif libsf_appid_preproc_la_CFLAGS = -DDYNAMIC_PREPROC_CONTEXT -DSTATIC=static -DINLINE=inline $(LUA_CFLAGS) libsf_appid_preproc_la_SOURCES = $(APPID_SOURCES) all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/appid/luaDetectorModule.c0000644000175000017500000011105114241075454023102 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** @defgroup LuaDetectorCore LuaDetectorCore * Functions supporting Lua detectors in core engine. *@{ */ #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sfghash.h" #include "appIdConfig.h" #include "luaDetectorApi.h" #include "luaDetectorFlowApi.h" #include "luaDetectorModule.h" #include "fw_appid.h" #ifdef HAVE_OPENSSL_MD5 #include #define MD5CONTEXT MD5_CTX #define MD5INIT MD5_Init #define MD5UPDATE MD5_Update #define MD5FINAL MD5_Final #define MD5DIGEST MD5 #else #include "md5.h" #define MD5CONTEXT struct MD5Context #define MD5INIT MD5Init #define MD5UPDATE MD5Update #define MD5FINAL MD5Final #define MD5DIGEST MD5 #endif #define MAXPD 1024 #define LUA_DETECTOR_FILENAME_MAX 1024 typedef void (tFileProcessorFn)(char *filename); /*static const char * LuaLogLabel = "luaDetectorModule"; */ // This data structure is shared in the main and the reload threads. However, the detectors // in this list could be using different AppID contexts (pAppidOldConfig, pAppidActiveConfig // and pAppidActiveConfig) based on which context the detector is being used. For example, // a detector could simultaneously be loaded in the reload thread while the same detector // could be used in the packet processing thread. Since allocatedDetectorList is used only // during loading, we don't need to use synchronization measures to access it. static SFGHASH *allocatedDetectorList; ///< list of detectors allocated SF_LIST allocatedFlowList; /*list of flows allocated. */ static uint32_t gLuaTrackerSize = 0; static unsigned gNumDetectors = 0; static unsigned gNumActiveDetectors; //static void luaDetectorsCleanInactive(void); #ifdef LUA_DETECTOR_DEBUG void luaErrorHandler(lua_State *l, lua_Debug *ar) { /*fill up the debug structure with information from the lua stack */ lua_getinfo(l, "Sln", ar); if (ar->event == LUA_HOOKCALL) { _dpd.debugMsg(DEBUG_LOG,"Called: %s:%d: %s (%s)", ar->short_src, ar->linedefined, (ar->name == NULL ? "[UNKNOWN]\n",: ar->name), ar->namewhat); } else if (ar->event ==LUA_HOOKRET) { _dpd.debugMsg(DEBUG_LOG,"returned: %s:%d: %s (%s)", ar->short_src, ar->linedefined, (ar->name == NULL ? "[UNKNOWN]\n",: ar->name), ar->namewhat); } } #endif /**reads packageInfo defined inside lua detector. */ static void getDetectorPackageInfo( lua_State *L, Detector *detector, int fillDefaults ) { tDetectorPackageInfo *pkg = &detector->packageInfo; lua_getglobal (L, "DetectorPackageInfo"); if (!lua_istable(L, -1)) { lua_pop(L, 1); if (fillDefaults) { /*set default values first */ pkg->name = strdup("NoName"); pkg->server.initFunctionName = strdup("DetectorInit"); pkg->server.cleanFunctionName = strdup("DetectorClean"); pkg->server.validateFunctionName = strdup("DetectorValidate"); if (!pkg->name || !pkg->server.initFunctionName || !pkg->server.cleanFunctionName || !pkg->server.validateFunctionName) _dpd.errMsg("failed to allocate package"); } return; } /* Get all the variables */ lua_getfield(L, -1, "name"); /* string */ if (lua_isstring(L, -1)) { pkg->name = strdup(lua_tostring(L, -1)); if (!pkg->name) _dpd.errMsg("failed to allocate package name"); } else if (fillDefaults) { pkg->name = strdup("NoName"); if (!pkg->name) _dpd.errMsg("failed to allocate package name"); } lua_pop(L, 1); lua_getfield(L, -1, "proto"); /* integer? */ if (lua_isnumber(L, -1)) { pkg->proto = lua_tointeger(L, -1); } lua_pop(L, 1); lua_getfield(L, -1, "client"); if (lua_istable(L, -1)) { lua_getfield(L, -1, "init"); /* string*/ if (lua_isstring(L, -1)) { pkg->client.initFunctionName = strdup(lua_tostring(L, -1)); if (!pkg->client.initFunctionName) _dpd.errMsg("failed to allocate client init function name"); } lua_pop(L, 1); lua_getfield(L, -1, "clean"); /* string*/ if (lua_isstring(L, -1)) { pkg->client.cleanFunctionName = strdup(lua_tostring(L, -1)); if (!pkg->client.cleanFunctionName) _dpd.errMsg("failed to allocate client clean function name"); } lua_pop(L, 1); lua_getfield(L, -1, "validate"); /* string*/ if (lua_isstring(L, -1)) { pkg->client.validateFunctionName = strdup(lua_tostring(L, -1)); if (!pkg->client.validateFunctionName) _dpd.errMsg("failed to allocate client validate function name"); } lua_pop(L, 1); lua_getfield(L, -1, "minimum_matches"); /* integer*/ if (lua_isnumber(L, -1)) { pkg->client.minMatches = lua_tointeger(L, -1); } lua_pop(L, 1); } lua_pop(L, 1); /*pop client table */ lua_getfield(L, -1, "server"); if (lua_istable(L, -1)) { lua_getfield(L, -1, "init"); /* string*/ if (lua_isstring(L, -1)) { pkg->server.initFunctionName = strdup(lua_tostring(L, -1)); if (!pkg->server.initFunctionName) _dpd.errMsg("failed to allocate server init function name"); } else if (fillDefaults) { pkg->server.initFunctionName = strdup("DetectorInit"); if (!pkg->server.initFunctionName) _dpd.errMsg("failed to allocate server init function name"); } lua_pop(L, 1); lua_getfield(L, -1, "clean"); /* string*/ if (lua_isstring(L, -1)) { pkg->server.cleanFunctionName = strdup(lua_tostring(L, -1)); if (!pkg->server.cleanFunctionName) _dpd.errMsg("failed to allocate server clean function name"); } else if (fillDefaults) { pkg->server.cleanFunctionName = strdup("DetectorClean"); if (!pkg->server.cleanFunctionName) _dpd.errMsg("failed to allocate server clean function name"); } lua_pop(L, 1); lua_getfield(L, -1, "validate"); /* string*/ if (lua_isstring(L, -1)) { pkg->server.validateFunctionName = strdup(lua_tostring(L, -1)); if (!pkg->server.validateFunctionName) _dpd.errMsg("failed to allocate server validate function name"); } else if (fillDefaults) { pkg->server.validateFunctionName = strdup("DetectorValidate"); if (!pkg->server.validateFunctionName) _dpd.errMsg("failed to allocate server validate function name"); } lua_pop(L, 1); } lua_pop(L, 1); /*pop server table */ lua_pop(L, 1); /*pop DetectorPackageInfo table */ } /**Calls DetectorInit function inside lua detector. * Calls initialization function as defined in packageInfo, which reads either user defined name * or DetectorInit symbol. Pushes detectorUserData on stack as input parameter and the calls the * function. Notice * that on error, lua_state is not closed. This keeps faulty detectors around * without using it, but it keeps wrapping functions simpler. */ static void luaServerInit( lua_State *myLuaState, Detector *detector ) { if (!detector->packageInfo.server.initFunctionName) { _dpd.errMsg("Detector %s: DetectorInit() is not provided for server\n",detector->name); return; } lua_getglobal(myLuaState, detector->packageInfo.server.initFunctionName); if (!lua_isfunction(myLuaState, -1)) { _dpd.errMsg("Detector %s: does not contain DetectorInit() function\n",detector->name); /*lua_close(myLuaState); */ return; } /*first parameter is DetectorUserData */ lua_rawgeti(detector->myLuaState, LUA_REGISTRYINDEX, detector->detectorUserDataRef); if (lua_pcall(myLuaState, 1, 1, 0) != 0) { _dpd.errMsg("error loading lua Detector %s, error %s\n",detector->name, lua_tostring(myLuaState, -1)); /*lua_close(myLuaState); */ return; } else { if (detector->server.pServiceElement) detector->server.pServiceElement->ref_count = 1; _dpd.debugMsg(DEBUG_LOG,"Initialized %s\n",detector->name); } } /**Calls init function inside lua detector. * Calls initialization function as defined in packageInfo. Pushes detectorUserData on stack * as input parameter and the calls the function. Notice * that on error, lua_state is not * closed. This keeps faulty detectors around without using it, but it keeps wrapping functions * simpler. */ static void luaClientInit( tRNAClientAppModule *li) { Detector *detector = (Detector *)li->userData; lua_State *myLuaState = detector->myLuaState; if (!detector->packageInfo.client.initFunctionName) { _dpd.errMsg("Detector %s: DetectorInit() is not provided for client\n",detector->name); return; } /*printf ("readPackage beginning stack elements %d\n", lua_gettop(myLuaState)); */ lua_getglobal(myLuaState, detector->packageInfo.client.initFunctionName); if (!lua_isfunction(myLuaState, -1)) { _dpd.errMsg("Detector %s: does not contain DetectorInit() function\n",detector->name); /*lua_close(myLuaState); */ return; } /*first parameter is DetectorUserData */ lua_rawgeti(detector->myLuaState, LUA_REGISTRYINDEX, detector->detectorUserDataRef); /*second parameter is a table containing configuration stuff. */ lua_newtable(myLuaState); if (lua_pcall(myLuaState, 2, 1, 0) != 0) { _dpd.errMsg("Could not initialize the %s client app element: %s\n",li->name, lua_tostring(myLuaState, -1)); /*lua_close(myLuaState); */ return; } else { _dpd.debugMsg(DEBUG_LOG,"Initialized %s\n",detector->name); } } static void luaClientFini( Detector *detector ) { lua_State *myLuaState = detector->myLuaState; if (!detector->packageInfo.client.cleanFunctionName) { return; } lua_getglobal(myLuaState, detector->packageInfo.client.cleanFunctionName); if (!lua_isfunction(myLuaState, -1)) { _dpd.errMsg("Detector %s: does not contain DetectorFini() function\n",detector->name); /*lua_close(myLuaState); */ return; } /*first parameter is DetectorUserData */ lua_rawgeti(detector->myLuaState, LUA_REGISTRYINDEX, detector->detectorUserDataRef); if (lua_pcall(myLuaState, 1, 1, 0) != 0) { _dpd.errMsg("Could not cleanup the %s client app element: %s\n",detector->name, lua_tostring(myLuaState, -1)); /*lua_close(myLuaState); */ } } /**set tracker sizes on Lua detector sizes. Uses global module names to access functions. */ static inline void setLuaTrackerSize( lua_State *L, uint32_t numTrackers) { /*change flow tracker size according to available memory calculation */ lua_getglobal(L, "hostServiceTrackerModule"); if (lua_istable(L, -1)) { lua_getfield(L, -1, "setHostServiceTrackerSize"); if (lua_isfunction(L, -1)) { lua_pushinteger (L, numTrackers); if (lua_pcall(L, 1, 0, 0) != 0) { _dpd.errMsg( "error setting tracker size"); } } } else { #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG, "hostServiceTrackerModule.setHostServiceTrackerSize not found"); #endif } lua_pop(L, 1); /*change flow tracker size according to available memory calculation */ lua_getglobal(L, "flowTrackerModule"); if (lua_istable(L, -1)) { lua_getfield(L, -1, "setFlowTrackerSize"); if (lua_isfunction(L, -1)) { lua_pushinteger (L, numTrackers); if (lua_pcall(L, 1, 0, 0) != 0) { _dpd.errMsg( "error setting tracker size"); } } } else { #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG, "flowTrackerModule.setFlowTrackerSize not found"); #endif } lua_pop(L, 1); } /**Initializes Lua modules. Open lua and if available LuaJIT libraries, and registers all API modules. * * @return void */ void luaModuleInit(void) { sflist_init(&allocatedFlowList); allocatedDetectorList = sfghash_new(-1023, 0, 0, Detector_fini); if (!allocatedDetectorList) { _dpd.fatalMsg( "Failed to create the module hash"); exit(-1); } } /**calculates Number of flow and host tracker entries for Lua detectors, given amount * of memory allocated to RNA (fraction of total system memory) and number of detectors * loaded in database. Calculations are based on CAICCI detector and observing memory * consumption per tracker. * @param rnaMemory - total memory RNA is allowed to use. This is calculated as a fraction of * total system memory. * @param numDetectors - number of lua detectors present in database. */ #define LUA_TRACKERS_MAX 10000 #define LUA_TRACKER_AVG_MEM_BYTES 740 static inline uint32_t calculateLuaTrackerSize(u_int64_t rnaMemory, uint32_t numDetectors) { u_int64_t detectorMemory = (rnaMemory/8); unsigned numTrackers; if (!numDetectors) numDetectors = 1; numTrackers = (detectorMemory/LUA_TRACKER_AVG_MEM_BYTES)/numDetectors; return (numTrackers > LUA_TRACKERS_MAX)? LUA_TRACKERS_MAX: numTrackers; } static void loadCustomLuaModules(tAppidStaticConfig* appidSC, char *path, tAppIdConfig *pConfig, bool isCustom) { Detector *detector, *newDetector; int rval; glob_t globs; char pattern[PATH_MAX]; unsigned n; FILE *file; char *validatorBuffer; int validatorBufferLen; SFGHASH_NODE *node; tRNAClientAppModule *cam = NULL; lua_State *myLuaState; //snprintf(pattern, sizeof(pattern), "%s/????????-????-????-????-????????????", path); snprintf(pattern, sizeof(pattern), "%s/*", path); memset(&globs, 0, sizeof(globs)); rval = glob(pattern, 0, NULL, &globs); if (rval != 0 && rval != GLOB_NOMATCH) { _dpd.errMsg("Unable to read directory '%s'\n",pattern); return; } // Open each RNA detector file and gather detector information from it for (n = 0; n < globs.gl_pathc; n++) { unsigned char digest[16]; MD5CONTEXT context; char detectorName[LUA_DETECTOR_FILENAME_MAX]; char *basename; basename = strrchr(globs.gl_pathv[n], '/'); if (!basename) { basename = globs.gl_pathv[n]; } basename++; snprintf(detectorName, LUA_DETECTOR_FILENAME_MAX, "%s_%s", (isCustom? "custom": "cisco"), basename); if ((file = fopen(globs.gl_pathv[n], "r")) == NULL) { _dpd.errMsg("Unable to read lua detector '%s'\n",globs.gl_pathv[n]); continue; } /*Load lua file as a detector. */ if (fseek(file, 0, SEEK_END)) { _dpd.errMsg("Unable to seek lua detector '%s'\n",globs.gl_pathv[n]); fclose(file); continue; } validatorBufferLen = ftell(file); if (validatorBufferLen == -1) { _dpd.errMsg("Unable to return offset on lua detector '%s'\n",globs.gl_pathv[n]); fclose(file); continue; } if (fseek(file, 0, SEEK_SET)) { _dpd.errMsg("Unable to seek lua detector '%s'\n",globs.gl_pathv[n]); fclose(file); continue; } if ((validatorBuffer = malloc(validatorBufferLen + 1)) == NULL) { _dpd.errMsg("Failed to allocate the user lua detector %s\n",globs.gl_pathv[n]); goto next; } if (fread(validatorBuffer, validatorBufferLen, 1, file) == 0) { _dpd.errMsg("Failed to read lua detector %s\n",globs.gl_pathv[n]); free(validatorBuffer); fclose(file); continue; } validatorBuffer[validatorBufferLen] = 0; MD5INIT(&context); MD5UPDATE(&context, validatorBuffer, validatorBufferLen); MD5FINAL(digest, &context); /*calculate md5sum and mark the detector active if matched. */ detector = sfghash_find(allocatedDetectorList, detectorName); if (detector) { if (!memcmp(digest, detector->digest, sizeof(digest))) { detector->isActive = 1; detector->pAppidNewConfig = pConfig; free(validatorBuffer); goto next; } } myLuaState = NULL; /*Design: Whether to make all detectors share the lua_State or not? * Separate lua_State creates isolated environment for each detector. There * is no name collision or any side effects due to garbage collection. Runtime * memory overhead should be bearable. * * Shared lua_State saves on memory since each Lua library is loaded just once. */ myLuaState = lua_open(); /* opens Lua */ if (myLuaState == NULL) { _dpd.errMsg("Failed to open lua for lua detector %s\n",globs.gl_pathv[n]); free(validatorBuffer); globfree(&globs); fclose(file); return; } luaL_openlibs(myLuaState); #ifdef HAVE_LIBLUAJIT /*linked in during compilation */ luaopen_jit(myLuaState); { static unsigned once = 0; if (!once) { lua_getfield(myLuaState, LUA_REGISTRYINDEX, "_LOADED"); lua_getfield(myLuaState, -1, "jit"); /* Get jit.* module table. */ lua_getfield (myLuaState, -1, "version"); if (lua_isstring(myLuaState, -1)) DEBUG_WRAP(DebugMessage(DEBUG_APPID, "LuaJIT: Version %s\n", lua_tostring(myLuaState, -1));); lua_pop(myLuaState, 1); once = 1; } } #endif /*HAVE_LIBLUAJIT */ Detector_register(myLuaState); lua_pop(myLuaState, 1); /*After detector register the methods are still on the stack, remove them. */ DetectorFlow_register(myLuaState); lua_pop(myLuaState, 1); #if 0 #ifdef LUA_DETECTOR_DEBUG lua_sethook(myLuaState,&luaErrorHandler,LUA_MASKCALL | LUA_MASKRET,0); #endif #endif /*The garbage-collector pause controls how long the collector waits before */ /*starting a new cycle. Larger values make the collector less aggressive. */ /*Values smaller than 100 mean the collector will not wait to start a new */ /*cycle. A value of 200 means that the collector waits for the total memory */ /*in use to double before starting a new cycle. */ lua_gc(myLuaState, LUA_GCSETPAUSE,100); /*The step multiplier controls the relative speed of the collector relative */ /*to memory allocation. Larger values make the collector more aggressive */ /*but also increase the size of each incremental step. Values smaller than */ /*100 make the collector too slow and can result in the collector never */ /*finishing a cycle. The default, 200, means that the collector runs at */ /*"twice" the speed of memory allocation. */ lua_gc(myLuaState, LUA_GCSETSTEPMUL,200); /*set lua library paths */ { char newLuaPath[PATH_MAX]; lua_getglobal( myLuaState, "package" ); lua_getfield( myLuaState, -1, "path" ); const char * curLuaPath = lua_tostring(myLuaState, -1); if (curLuaPath && (strlen(curLuaPath))) { snprintf(newLuaPath, PATH_MAX-1, "%s;%s/odp/libs/?.lua;%s/custom/libs/?.lua", curLuaPath, appidSC->app_id_detector_path, appidSC->app_id_detector_path); } else { snprintf(newLuaPath, PATH_MAX-1, "%s/odp/libs/?.lua;%s/custom/libs/?.lua", appidSC->app_id_detector_path, appidSC->app_id_detector_path); } lua_pop( myLuaState, 1 ); lua_pushstring( myLuaState, newLuaPath); lua_setfield( myLuaState, -2, "path" ); lua_pop( myLuaState, 1 ); } /*Load function works the same with Lua and Luac file. */ if (luaL_loadbuffer(myLuaState, (const char *)validatorBuffer, validatorBufferLen, "") || lua_pcall(myLuaState, 0, 0, 0)) { _dpd.errMsg("cannot run validator %s, error: %s\n",detectorName, lua_tostring(myLuaState, -1)); lua_close(myLuaState); free(validatorBuffer); fclose(file); continue; } newDetector = createDetector(myLuaState, detectorName); if (!newDetector) { _dpd.errMsg("cannot allocate detector %s\n",detectorName); lua_close(myLuaState); free(validatorBuffer); globfree(&globs); fclose(file); return; } getDetectorPackageInfo(myLuaState, newDetector, 0); newDetector->validatorBuffer = validatorBuffer; /*newDetector->detector_version = version; */ newDetector->isActive = 1; newDetector->pAppidNewConfig = newDetector->pAppidActiveConfig = newDetector->pAppidOldConfig = pConfig; newDetector->isCustom = isCustom; if (newDetector->packageInfo.server.initFunctionName) { /*add to active service list */ newDetector->server.serviceModule.next = pConfig->serviceConfig.active_service_list; pConfig->serviceConfig.active_service_list = &newDetector->server.serviceModule; newDetector->server.serviceId = APP_ID_UNKNOWN; /*create a ServiceElement */ if (checkServiceElement(newDetector)) { newDetector->server.pServiceElement->validate = validateAnyService; newDetector->server.pServiceElement->userdata = newDetector; newDetector->server.pServiceElement->detectorType = DETECTOR_TYPE_DECODER; } } else { newDetector->client.appFpId = APP_ID_UNKNOWN; cam = &newDetector->client.appModule; cam->name = newDetector->packageInfo.name; cam->proto = newDetector->packageInfo.proto; cam->validate = validateAnyClientApp; cam->minimum_matches = newDetector->packageInfo.client.minMatches; cam->userData = newDetector; cam->api = getClientApi(); } node = sfghash_find_node(allocatedDetectorList, detectorName); if (node) { newDetector->next = node->data; node->data = newDetector; } else if (sfghash_add(allocatedDetectorList, detectorName, newDetector)) { _dpd.errMsg( "Failed to add to list."); freeDetector(newDetector); lua_close(myLuaState); globfree(&globs); fclose(file); return; } memcpy(newDetector->digest, digest, sizeof(newDetector->digest)); #ifdef PERF_PROFILING if (!(newDetector->pPerfStats = calloc(1, sizeof(*newDetector->pPerfStats)))) { _dpd.errMsg("cannot allocate perStats %s\n",detectorName); freeDetector(newDetector); lua_close(myLuaState); globfree(&globs); fclose(file); return; } if (isCustom) { _dpd.addPreprocProfileFunc(newDetector->name, newDetector->pPerfStats, 3, &luaCustomPerfStats, (PreprocStatsNodeFreeFunc)free); } else { _dpd.addPreprocProfileFunc(newDetector->name, newDetector->pPerfStats, 3, &luaCiscoPerfStats, (PreprocStatsNodeFreeFunc)free); } #endif /*PERF_PROFILING */ _dpd.debugMsg(DEBUG_LOG,"Loaded detector %s\n",detectorName); gNumDetectors++; next: fclose(file); } globfree(&globs); } void FinalizeLuaModules(tAppIdConfig *pConfig) { Detector *detector; Detector *detector_list; SFGHASH_NODE *node; gNumActiveDetectors = 0; for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { // Detector loading is done. Its data in the current context can be cleaned. // Its data in the new context is ready to use. Change pAppidOldConfig // and pAppidActiveConfig appropriately. detector->pAppidOldConfig = detector->pAppidActiveConfig; detector->pAppidActiveConfig = pConfig; if (detector->isActive) { gNumActiveDetectors++; if (detector->server.pServiceElement) detector->server.pServiceElement->current_ref_count = detector->server.pServiceElement->ref_count; } } } luaDetectorsSetTrackerSize(); } void LoadLuaModules(tAppidStaticConfig* appidSC, tAppIdConfig *pConfig) { char path[PATH_MAX]; Detector *detector; Detector *detector_list; SFGHASH_NODE *node; for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { detector->wasActive = detector->isActive; detector->isActive = 0; if (detector->server.pServiceElement) detector->server.pServiceElement->ref_count = 0; } } snprintf(path, sizeof(path), "%s/odp/lua", appidSC->app_id_detector_path); loadCustomLuaModules(appidSC, path, pConfig, 0); snprintf(path, sizeof(path), "%s/custom/lua", appidSC->app_id_detector_path); loadCustomLuaModules(appidSC, path, pConfig, 1); // luaDetectorsCleanInactive(); } #if 0 /** the inactive detectors are not deleted since some allocated values are used * in client and service lists.*/ static void luaDetectorsCleanInactive(void) { Detector *detector; Detector *detector_list; SFGHASH_NODE *node; for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { if (!detector->isActive) { tDetectorPackageInfo *pkg = &detector->packageInfo; if (detector->server.pServiceElement) detector->server.pServiceElement->ref_count = 0; if (pkg->name) { free(pkg->name); pkg->name = NULL; } if (pkg->client.initFunctionName) { free(pkg->client.initFunctionName); pkg->client.initFunctionName = NULL; } if (pkg->client.cleanFunctionName) { free(pkg->client.cleanFunctionName); pkg->client.cleanFunctionName = NULL; } if (pkg->client.validateFunctionName) { free(pkg->client.validateFunctionName); pkg->client.validateFunctionName = NULL; } if (pkg->server.initFunctionName) { free(pkg->server.initFunctionName); pkg->server.initFunctionName = NULL; } if (pkg->server.cleanFunctionName) { free(pkg->server.cleanFunctionName); pkg->server.cleanFunctionName = NULL; } if (pkg->server.validateFunctionName) { free(pkg->server.validateFunctionName); pkg->server.validateFunctionName = NULL; } /*The detectorUserData itself is a userdata and therefore be freed by Lua side. */ if (detector->detectorUserDataRef != LUA_REFNIL) { DetectorUserData *pUserData; lua_rawgeti(detector->myLuaState, LUA_REGISTRYINDEX, detector->detectorUserDataRef); pUserData = checkDetectorUserData(detector->myLuaState, -1); if (pUserData) { pUserData->pDetector = NULL; } luaL_unref (detector->myLuaState, LUA_REGISTRYINDEX, detector->detectorUserDataRef); detector->detectorUserDataRef = LUA_REFNIL; } if (detector->name) { free(detector->name); free(detector->validatorBuffer); detector->name = NULL; detector->validatorBuffer = NULL; } if (detector->myLuaState) { lua_close(detector->myLuaState); detector->myLuaState = NULL; } } } } } #endif void luaDetectorsUnload(tAppIdConfig *pConfig) { Detector *detector; Detector *detector_list; SFGHASH_NODE *node; for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { if (detector->isActive && detector->packageInfo.server.initFunctionName) detectorRemoveAllPorts(detector, pConfig); if (detector->isActive && detector->packageInfo.client.initFunctionName) luaClientFini(detector); detector->isActive = 0; if (detector->server.pServiceElement) detector->server.pServiceElement->ref_count = 0; } } gNumActiveDetectors = 0; } void luaDetectorsSetTrackerSize(void) { Detector *detector; Detector *detector_list; SFGHASH_NODE *node; gLuaTrackerSize = calculateLuaTrackerSize(512*1024*1024, gNumActiveDetectors); DEBUG_WRAP(DebugMessage(DEBUG_APPID, " Setting tracker size to %u\n", gLuaTrackerSize);); for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) if (detector->isActive) setLuaTrackerSize(detector->myLuaState, gLuaTrackerSize); } } void UnloadLuaModules(tAppIdConfig *pConfig) { Detector *detector; Detector *detector_list; SFGHASH_NODE *node; for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { if (detector->wasActive && detector->client.appFpId) { pthread_mutex_lock(&detector->luaReloadMutex); luaClientFini(detector); pthread_mutex_unlock(&detector->luaReloadMutex); } detector->wasActive = 0; // Detector cleanup is done. Move pAppidOldConfig to the current // AppID context. detector->pAppidOldConfig = detector->pAppidActiveConfig; } } } /**Reconfigure all Lua modules. * Iterates over all Lua detectors in system and reconfigures them. This * will however not read rna_csd_validator_map table again to check for * newly activated or deactivate detectors. Current design calls for restarting * RNA whenever detectors are activated/deactivated. */ void luaModuleInitAllServices(void) { Detector *detector_list; Detector *detector; SFGHASH_NODE *node; /*reconfiguring server elements only. */ for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { if (detector->isActive && detector->packageInfo.server.initFunctionName) { pthread_mutex_lock(&detector->luaReloadMutex); luaServerInit(detector->myLuaState, detector); pthread_mutex_unlock(&detector->luaReloadMutex); } } } } /**Reconfigure all Lua modules. * Iterates over all Lua detectors in system and reconfigures them. This * will however not read rna_csd_validator_map table again to check for * newly activated or deactivate detectors. Current design calls for restarting * RNA whenever detectors are activated/deactivated. */ void luaModuleInitAllClients(void) { Detector *detector_list; Detector *detector; SFGHASH_NODE *node; /*reconfiguring client elements. */ for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { if (detector->isActive && detector->packageInfo.client.initFunctionName) { pthread_mutex_lock(&detector->luaReloadMutex); luaClientInit(&detector->client.appModule); pthread_mutex_unlock(&detector->luaReloadMutex); } } } } void luaModuleCleanAllClients(void) { Detector *detector; SFGHASH_NODE *node; /*reconfiguring client elements. */ for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector = node->data; if (detector->packageInfo.client.initFunctionName) { luaClientFini(detector); } /*dont free detector. Lua side reclaims the memory. */ } } /**Finish routine for DetectorCore module. It release all Lua sessions and frees any memory. * @warn This function should be called once and that too when RNA is performing clean exit. * @return void. */ void luaModuleFini(void) { #ifdef LUA_DETECTOR_DEBUG _dpd.debugMsg(DEBUG_LOG, "luaModuleFini(): entered"); #endif /*flow can be freed during garbage collection */ sflist_static_free_all(&allocatedFlowList, freeDetectorFlow); sfghash_delete(allocatedDetectorList); allocatedDetectorList = NULL; } void RNAPndDumpLuaStats (void) { Detector *detector_list; Detector *detector; SFGHASH_NODE *node; unsigned long long totalMem = 0; unsigned long long mem; if (!allocatedDetectorList) return; _dpd.logMsg("Lua detector Stats"); for (node = sfghash_findfirst(allocatedDetectorList); node; node = sfghash_findnext(allocatedDetectorList)) { detector_list = node->data; for (detector = detector_list; detector; detector = detector->next) { if (detector->isActive) { mem = lua_gc(detector->myLuaState, LUA_GCCOUNT,0); totalMem += mem; _dpd.logMsg(" Detector %s: Lua Memory usage %d kb", detector->name, mem); } } } _dpd.logMsg("Lua Stats total memory usage %d kb", totalMem); } /** @} */ /* end of LuaDetectorCore */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/0000755000175000017500000000000014242725717022524 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_tftp.c0000644000175000017500000002545614241075562025374 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define TFTP_PORT 69 #define TFTP_COUNT_THRESHOLD 1 #define TFTP_MAX_PACKET_SIZE 512 typedef enum { TFTP_STATE_CONNECTION, TFTP_STATE_TRANSFER, TFTP_STATE_ACK, TFTP_STATE_DATA, TFTP_STATE_ERROR } TFTPState; typedef struct _SERVICE_TFTP_DATA { TFTPState state; unsigned count; int last; uint16_t block; } ServiceTFTPData; #pragma pack(1) typedef struct _SERVICE_TFTP_HEADER { uint16_t opcode; union { uint16_t block; uint16_t errorcode; } d; } ServiceTFTPHeader; #pragma pack() static int tftp_init(const InitServiceAPI * const api); static int tftp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &tftp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "tftp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&tftp_validate, 69, IPPROTO_UDP}, {NULL, 0, 0} }; tRNAServiceValidationModule tftp_service_mod = { "TFTP", &tftp_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_TFTP, APPINFO_FLAG_SERVICE_ADDITIONAL}}; static int16_t app_id; static int tftp_init(const InitServiceAPI * const init_api) { unsigned i; #ifdef TARGET_BASED app_id = init_api->dpd->addProtocolReference("tftp"); for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&tftp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } #endif return 0; } static int tftp_verify_header(const uint8_t *data, uint16_t size, uint16_t *block) { const ServiceTFTPHeader *hdr; if (size < sizeof(ServiceTFTPHeader)) return -1; hdr = (ServiceTFTPHeader *)data; switch (ntohs(hdr->opcode)) { case 3: if (size > sizeof(ServiceTFTPHeader) + TFTP_MAX_PACKET_SIZE) return -1; *block = ntohs(hdr->d.block); return TFTP_STATE_DATA; case 4: if (size != sizeof(ServiceTFTPHeader)) return -1; *block = ntohs(hdr->d.block); return TFTP_STATE_ACK; case 5: if (ntohs(hdr->d.errorcode) > 7) return -1; if (size <= sizeof(ServiceTFTPHeader)) return -1; if (data[size-1] != 0) return -1; return TFTP_STATE_ERROR; default: return -1; } } static int tftp_validate(ServiceValidationArgs* args) { ServiceTFTPData *td; ServiceTFTPData *tmp_td; int mode; uint16_t block = 0; uint16_t tmp; tAppIdData *pf; sfaddr_t *sip; sfaddr_t *dip; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; char* app_id_debug_session = args->app_id_debug_session; if (!size) goto inprocess; td = tftp_service_mod.api->data_get(flowp, tftp_service_mod.flow_data_index); if (!td) { td = calloc(1, sizeof(*td)); if (!td) return SERVICE_ENOMEM; if (tftp_service_mod.api->data_add(flowp, td, tftp_service_mod.flow_data_index, &free)) { free(td); return SERVICE_ENOMEM; } td->state = TFTP_STATE_CONNECTION; } if (args->app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s tftp state %d\n", app_id_debug_session, td->state); if (td->state == TFTP_STATE_CONNECTION && dir == APP_ID_FROM_RESPONDER) goto fail; if ((td->state == TFTP_STATE_TRANSFER || td->state == TFTP_STATE_DATA) && dir == APP_ID_FROM_INITIATOR) { goto inprocess; } switch (td->state) { case TFTP_STATE_CONNECTION: if (size < 6) goto bail; tmp = ntohs(*((uint16_t *)data)); if (tmp != 0x0001 && tmp != 0x0002) goto bail; data += sizeof(uint16_t); size -= sizeof(uint16_t); if (!(*data)) goto bail; for (; *data && size; data++, size--) { if (!isprint(*data)) goto bail; } if (!size) goto bail; size--; data++; if (!size || !(*data)) goto bail; if (data[size-1]) goto bail; if (strcasecmp((char *)data, "netascii") && strcasecmp((char *)data, "octet")) goto bail; tmp_td = calloc(1, sizeof(ServiceTFTPData)); if (tmp_td == NULL) return SERVICE_ENOMEM; tmp_td->state = TFTP_STATE_TRANSFER; dip = GET_DST_IP(pkt); sip = GET_SRC_IP(pkt); pf = tftp_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, pkt->src_port, flowp->proto, app_id, APPID_EARLY_SESSION_FLAG_FW_RULE); if (pf) { if (tftp_service_mod.api->data_add(pf, tmp_td, tftp_service_mod.flow_data_index, &free)) { free(tmp_td); return SERVICE_ENOMEM; } if (tftp_service_mod.api->data_add_id(pf, pkt->dst_port, &svc_element)) { setAppIdFlag(pf, APPID_SESSION_SERVICE_DETECTED); clearAppIdFlag(pf, APPID_SESSION_CONTINUE); tmp_td->state = TFTP_STATE_ERROR; return SERVICE_ENOMEM; } PopulateExpectedFlow(flowp, pf, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_FROM_RESPONDER); sfaddr_copy_to_raw(&pf->common.initiator_ip, sip); pf->rnaServiceState = RNA_STATE_STATEFUL; pf->scan_flags |= SCAN_HOST_PORT_FLAG; } else { free(tmp_td); goto inprocess; /* Assume that the flow already exists as in a retransmit situation */ } break; case TFTP_STATE_TRANSFER: if ((mode=tftp_verify_header(data, size, &block)) < 0) { if (args->app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s tftp failed to verify\n", app_id_debug_session); goto fail; } if (args->app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s tftp mode %d and block %u\n", app_id_debug_session, mode, (unsigned)block); if (mode == TFTP_STATE_ACK) { if (block != 0) { td->state = TFTP_STATE_ERROR; goto fail; } td->last = 0; td->block = 0; td->state = TFTP_STATE_ACK; } else if (mode == TFTP_STATE_DATA) { if (block != 1) { td->state = TFTP_STATE_ERROR; goto fail; } td->block = 1; td->state = TFTP_STATE_DATA; } else if (mode == TFTP_STATE_ERROR) break; else { td->state = TFTP_STATE_ERROR; goto fail; } break; case TFTP_STATE_ACK: if ((mode=tftp_verify_header(data, size, &block)) < 0) { if (dir == APP_ID_FROM_RESPONDER) goto fail; else { if (args->app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s tftp failed to verify\n", app_id_debug_session); goto bail; } } if (args->app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s tftp mode %d\n", app_id_debug_session, mode); if (mode == TFTP_STATE_ERROR) { td->state = TFTP_STATE_TRANSFER; break; } if (dir == APP_ID_FROM_INITIATOR && mode != TFTP_STATE_DATA) { if (args->app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s tftp bad mode\n", app_id_debug_session); goto bail; } if (dir == APP_ID_FROM_RESPONDER && mode != TFTP_STATE_ACK) goto fail; if (dir == APP_ID_FROM_INITIATOR) { if (size < sizeof(ServiceTFTPHeader) + TFTP_MAX_PACKET_SIZE) td->last = 1; break; } if (block == (uint16_t)(td->block + 1)) td->block++; else if (block != td->block) goto fail; td->count++; if (td->count >= TFTP_COUNT_THRESHOLD) goto success; if (td->last) td->state = TFTP_STATE_TRANSFER; break; case TFTP_STATE_DATA: if ((mode=tftp_verify_header(data, size, &block)) < 0) goto fail; if (mode == TFTP_STATE_ERROR) td->state = TFTP_STATE_TRANSFER; else if (mode != TFTP_STATE_DATA) goto fail; if (block == (uint16_t)(td->block + 1)) td->block++; else if (block != td->block) goto fail; td->count++; if (td->count >= TFTP_COUNT_THRESHOLD) goto success; if (size < sizeof(ServiceTFTPHeader) + TFTP_MAX_PACKET_SIZE) td->state = TFTP_STATE_TRANSFER; break; case TFTP_STATE_ERROR: default: goto fail; } inprocess: tftp_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: if (args->app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s tftp success\n", app_id_debug_session); tftp_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_TFTP, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; bail: tftp_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element, tftp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; fail: tftp_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, tftp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_api.h0000644000175000017500000002242114241075461025160 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_API_H__ #define __SERVICE_API_H__ #include #ifdef HAVE_CONFIG_H #include "config.h" /* for WORDS_BIGENDIAN */ #endif #include "sf_dynamic_preprocessor.h" #include "appIdApi.h" #include "service_util.h" #include "commonAppMatcher.h" #include "flow.h" // Forward declaration struct appIdConfig_; struct _Detector; typedef enum { SERVICE_SUCCESS = 0, SERVICE_INPROCESS = 10, SERVICE_NEED_REASSEMBLY = 11, SERVICE_NOT_COMPATIBLE = 12, SERVICE_INVALID_CLIENT = 13, SERVICE_REVERSED = 14, SERVICE_NOMATCH = 100, SERVICE_ENULL = -10, SERVICE_EINVALID = -11, SERVICE_ENOMEM = -12 } SERVICE_RETCODE; typedef struct _ServiceValidationArgs { const uint8_t *data; uint16_t size; int dir; tAppIdData *flowp; SFSnortPacket *pkt; struct _Detector *userdata; const struct appIdConfig_ *pConfig; bool app_id_debug_session_flag; char *app_id_debug_session; } ServiceValidationArgs; typedef int (*RNAServiceValidationFCN)(ServiceValidationArgs*); typedef int (*RNAServiceCallbackFCN)(const uint8_t *, uint16_t, const int, tAppIdData *session, const SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); #define MakeRNAServiceValidationPrototype(name) static int name(ServiceValidationArgs* args) struct _INIT_SERVICE_API; typedef struct { struct appIdConfig_ *pAppidConfig; ///< AppId context for which this API should be used } CleanServiceAPI; typedef int (*RNAServiceValidationInitFCN)(const struct _INIT_SERVICE_API * const); typedef void (*RNAServiceValidationCleanFCN)(const CleanServiceAPI *const); struct _RNA_SERVICE_VALIDATION_PP; struct RNAServiceValidationModule; typedef struct _INIT_SERVICE_API { void (*RegisterPattern)(RNAServiceValidationFCN fcn, uint8_t proto, const uint8_t *pattern, unsigned size, int position, const char *name, struct appIdConfig_ *pConfig); int (*AddPort)(struct _RNA_SERVICE_VALIDATION_PP *pp, struct RNAServiceValidationModule *svm, struct appIdConfig_ *pConfig); void (*RemovePorts)(RNAServiceValidationFCN validate, struct appIdConfig_ *pConfig); void (*RegisterPatternUser)(RNAServiceValidationFCN fcn, uint8_t proto, const uint8_t *pattern, unsigned size, int position, const char *name, struct appIdConfig_ *pConfig); void (*RegisterAppId)(RNAServiceValidationFCN fcn, tAppId appId, uint32_t additionalInfo, struct appIdConfig_ *pConfig); void (*RegisterDetectorCallback)(RNAServiceCallbackFCN fcn, tAppId appId, struct _Detector *userdata, struct appIdConfig_ *pConfig); int debug; uint32_t instance_id; DynamicPreprocessorData *dpd; struct appIdConfig_ *pAppidConfig; ///< AppId context for which this API should be used } InitServiceAPI; typedef struct _RNA_SERVICE_PERF { /*time to validate */ uint64_t totalValidateTime; } RNAServicePerf; struct RNAServiceElement { struct RNAServiceElement *next; RNAServiceValidationFCN validate; RNAServiceCallbackFCN detectorCallback; bool detectorContext; /**pointer to user data. Value of userdata pointer and validate pointer forms key for comparison. */ struct _Detector *userdata; /**type of detector - pattern based, Sourcefire (validator) or User (Validator). */ unsigned detectorType; /**Number of resources registered */ unsigned ref_count; unsigned current_ref_count; int provides_user; const char *name; }; typedef struct RNAServiceElement tRNAServiceElement; typedef void *(*ServiceFlowdataGet)(tAppIdData *, unsigned); typedef int (*ServiceFlowdataAdd)(tAppIdData *, void *, unsigned, AppIdFreeFCN); typedef int (*ServiceFlowdataAddId)(tAppIdData *, uint16_t, const tRNAServiceElement * const); typedef int (*ServiceFlowdataAddDHCP)(tAppIdData *, unsigned, const uint8_t *, unsigned, const uint8_t *, const uint8_t *); #define APPID_EARLY_SESSION_FLAG_FW_RULE 1 typedef tAppIdData *(*ServiceCreateNewFlow)( tAppIdData *flowp, SFSnortPacket *, sfaddr_t *, uint16_t, sfaddr_t *, uint16_t, uint8_t, int16_t, int flags); typedef void (*ServiceDhcpNewLease)(tAppIdData *flow, const uint8_t *mac, uint32_t ip, int32_t zone, uint32_t subnetmask, uint32_t leaseSecs, uint32_t router); typedef void (*ServiceAnalyzeFP)(tAppIdData *, unsigned, unsigned, uint32_t); typedef int (*AddService)(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, tAppId service, const char *vendor, const char *version, const RNAServiceSubtype *subtype, AppIdServiceIDState *id_state); typedef int (*AddServiceConsumeSubtype)(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, tAppId service, const char *vendor, const char *version, RNAServiceSubtype *subtype, AppIdServiceIDState *id_state); typedef int (*ServiceInProcess)(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, AppIdServiceIDState *id_state); typedef int (*FailService)(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, unsigned flow_data_index, const struct appIdConfig_ *pConfig, AppIdServiceIDState *id_state); typedef int (*IncompatibleData)(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, unsigned flow_data_index, const struct appIdConfig_ *pConfig, AppIdServiceIDState *id_state); typedef void (*AddHostInfo)(tAppIdData *flow, SERVICE_HOST_INFO_CODE code, const void *info); typedef void (*AddPayload)(tAppIdData *, tAppId); typedef void (*AddMultiPayload)(tAppIdData *, tAppId); typedef void (*AddUser)(tAppIdData *, const char *, tAppId, int); typedef void (*AddMisc)(tAppIdData *, tAppId); typedef void (*AddDnsQueryInfo)(tAppIdData *flow, uint16_t id, const uint8_t *host, uint8_t host_len, uint16_t host_offset, uint16_t record_type, uint16_t options_offset, bool root_query); typedef void (*AddDnsResponseInfo)(tAppIdData *flow, uint16_t id, const uint8_t *host, uint8_t host_len, uint16_t host_offset, uint8_t response_type, uint32_t ttl); typedef void (*ResetDnsInfo)(tAppIdData *flow); typedef struct _SERVICE_API { ServiceFlowdataGet data_get; ServiceFlowdataAdd data_add; ServiceCreateNewFlow flow_new; ServiceFlowdataAddId data_add_id; ServiceFlowdataAddDHCP data_add_dhcp; ServiceDhcpNewLease dhcpNewLease; ServiceAnalyzeFP analyzefp; AddService add_service; FailService fail_service; ServiceInProcess service_inprocess; IncompatibleData incompatible_data; AddHostInfo add_host_info; AddPayload add_payload; AddMultiPayload add_multipayload; AddUser add_user; AddServiceConsumeSubtype add_service_consume_subtype; AddMisc add_misc; AddDnsQueryInfo add_dns_query_info; AddDnsResponseInfo add_dns_response_info; ResetDnsInfo reset_dns_info; } ServiceApi; typedef struct _RNA_tAppIdData_STATE { struct _RNA_tAppIdData_STATE *next; const tRNAServiceElement *svc; uint16_t port; } RNAFlowState; typedef struct _RNA_SERVICE_VALIDATION_PP { RNAServiceValidationFCN validate; uint16_t port; uint8_t proto; uint8_t reversed_validation; } RNAServiceValidationPort; struct RNAServiceValidationModule { const char * name; RNAServiceValidationInitFCN init; RNAServiceValidationPort *pp; const ServiceApi *api; struct RNAServiceValidationModule *next; int provides_user; RNAServiceValidationCleanFCN clean; unsigned flow_data_index; }; typedef struct RNAServiceValidationModule tRNAServiceValidationModule; #if defined(WORDS_BIGENDIAN) #define LETOHS(p) BYTE_SWAP_16(*((uint16_t *)(p))) #define LETOHL(p) BYTE_SWAP_32(*((uint32_t *)(p))) #else #define LETOHS(p) (*((uint16_t *)(p))) #define LETOHL(p) (*((uint32_t *)(p))) #endif #endif /* __SERVICE_API_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rsync.c0000644000175000017500000001063414241075545025546 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define RSYNC_PORT 873 #define RSYNC_BANNER "@RSYNCD: " typedef enum { RSYNC_STATE_BANNER, RSYNC_STATE_MOTD, RSYNC_STATE_DONE } RSYNCState; typedef struct _SERVICE_RSYNC_DATA { RSYNCState state; } ServiceRSYNCData; static int rsync_init(const InitServiceAPI * const init_api); static int rsync_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &rsync_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "rsync", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&rsync_validate, RSYNC_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule rsync_service_mod = { "RSYNC", &rsync_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_RSYNC, APPINFO_FLAG_SERVICE_ADDITIONAL}}; static int rsync_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&rsync_validate, IPPROTO_TCP, (uint8_t *)RSYNC_BANNER, sizeof(RSYNC_BANNER)-1, 0, "rsync", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&rsync_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int rsync_validate(ServiceValidationArgs* args) { ServiceRSYNCData *rd; int i; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; rd = rsync_service_mod.api->data_get(flowp, rsync_service_mod.flow_data_index); if (!rd) { rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (rsync_service_mod.api->data_add(flowp, rd, rsync_service_mod.flow_data_index, &free)) { free(rd); return SERVICE_ENOMEM; } rd->state = RSYNC_STATE_BANNER; } switch (rd->state) { case RSYNC_STATE_BANNER: if (size < sizeof(RSYNC_BANNER)-1) goto fail; if (data[size-1] != 0x0A) goto fail; if (strncmp((char *)data, RSYNC_BANNER, sizeof(RSYNC_BANNER)-1)) goto fail; data += sizeof(RSYNC_BANNER) - 1; size -= sizeof(RSYNC_BANNER) - 1; for (i=0; istate = RSYNC_STATE_MOTD; break; case RSYNC_STATE_MOTD: if (data[size-1] != 0x0A) goto fail; for (i=0; istate = RSYNC_STATE_DONE; goto success; default: goto fail; } inprocess: rsync_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: rsync_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_RSYNC, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: rsync_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, rsync_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ntp.h0000644000175000017500000000205414241075527025213 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_NTP_H__ #define __SERVICE_NTP_H__ #include "service_api.h" extern tRNAServiceValidationModule ntp_service_mod; #endif /* __SERVICE_NTP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_snmp.c0000644000175000017500000004310414241075551025360 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define SNMP_PORT 161 #define SNMP_VERSION_1 0 #define SNMP_VERSION_2c 1 #define SNMP_VERSION_2u 2 #define SNMP_VERSION_3 3 #define SNMP_VENDOR_STR "SNMP" #define SNMP_VERSION_STR_1 "v1" #define SNMP_VERSION_STR_2c "v2c" #define SNMP_VERSION_STR_2u "v2u" #define SNMP_VERSION_STR_3 "v3" typedef enum { SNMP_STATE_CONNECTION, SNMP_STATE_RESPONSE, SNMP_STATE_REQUEST, SNMP_STATE_R_RESPONSE, SNMP_STATE_R_REQUEST, SNMP_STATE_ERROR } SNMPState; typedef struct _SERVICE_SNMP_DATA { SNMPState state; } ServiceSNMPData; typedef enum { SNMP_PDU_GET_REQUEST, SNMP_PDU_GET_NEXT_REQUEST, SNMP_PDU_GET_RESPONSE, SNMP_PDU_SET_REQUEST, SNMP_PDU_TRAP, SNMP_PDU_GET_BULK_REQUEST, SNMP_PDU_INFORM_REQUEST, SNMP_PDU_TRAPV2, SNMP_PDU_REPORT } SNMPPDUType; #pragma pack(1) typedef struct _SERVICE_SNMP_HEADER { uint16_t opcode; union { uint16_t block; uint16_t errorcode; } d; } ServiceSNMPHeader; #pragma pack() static int snmp_init(const InitServiceAPI * const init_api); static int snmp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &snmp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "snmp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&snmp_validate, SNMP_PORT, IPPROTO_TCP}, {&snmp_validate, SNMP_PORT, IPPROTO_UDP}, {&snmp_validate, 162, IPPROTO_UDP}, {NULL, 0, 0} }; tRNAServiceValidationModule snmp_service_mod = { "SNMP", &snmp_init, pp }; static uint8_t SNMP_PATTERN_2[] = {0x02, 0x01, 0x00, 0x04}; static uint8_t SNMP_PATTERN_3[] = {0x02, 0x01, 0x01, 0x04}; static uint8_t SNMP_PATTERN_4[] = {0x02, 0x01, 0x03, 0x30}; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_SNMP, APPINFO_FLAG_SERVICE_UDP_REVERSED|APPINFO_FLAG_SERVICE_ADDITIONAL} }; static int16_t app_id = 0; static int snmp_init(const InitServiceAPI * const init_api) { #ifdef TARGET_BASED app_id = init_api->dpd->addProtocolReference("snmp"); #endif init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_2, sizeof(SNMP_PATTERN_2), 2, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_3, sizeof(SNMP_PATTERN_3), 2, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_4, sizeof(SNMP_PATTERN_4), 2, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_2, sizeof(SNMP_PATTERN_2), 3, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_3, sizeof(SNMP_PATTERN_3), 3, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_4, sizeof(SNMP_PATTERN_4), 3, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_2, sizeof(SNMP_PATTERN_2), 4, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_3, sizeof(SNMP_PATTERN_3), 4, "snmp", init_api->pAppidConfig); init_api->RegisterPattern(&snmp_validate, IPPROTO_UDP, SNMP_PATTERN_4, sizeof(SNMP_PATTERN_4), 4, "snmp", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&snmp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int snmp_ans1_length(const uint8_t * * const data, const uint8_t * const end, uint32_t * const length) { *length = 0; if (**data == 0x80) return -1; if (**data < 0x80) { *length = (uint32_t)**data; (*data)++; } else { int cnt = (**data) & 0x7F; (*data)++; for (; *data= end) return -1; if (snmp_ans1_length(data, end, &overall_length)) return -1; if (overall_length < 3 || (int)overall_length > end-(*data)) return -1; if (**data != 0x02) return -1; (*data)++; if (**data != 0x01) return -1; (*data)++; version = **data; (*data)++; overall_length -= 3; if (!overall_length) return -1; switch (version) { case SNMP_VERSION_1: case SNMP_VERSION_2c: if (**data != 0x04) return -1; (*data)++; overall_length--; if (!overall_length) return -1; p = *data; if (snmp_ans1_length(data, *data+overall_length, &community_length)) return -1; overall_length -= *data - p; if (overall_length < community_length) return -1; for (; community_length; (*data)++, community_length--, overall_length--) { if (!isprint(**data)) return -1; } break; case SNMP_VERSION_2u: if (**data != 0x04) return -1; (*data)++; overall_length--; if (!overall_length) return -1; p = *data; if (snmp_ans1_length(data, *data+overall_length, &community_length)) return -1; overall_length -= *data - p; if (!community_length || overall_length < community_length) return -1; if (**data != 1) return -1; *data += community_length; overall_length -= community_length; break; case SNMP_VERSION_3: /* Global header */ if (**data != 0x30) return -1; (*data)++; overall_length--; if (!overall_length) return -1; p = *data; if (snmp_ans1_length(data, *data+overall_length, &global_length)) return -1; overall_length -= *data - p; if (global_length < 2 || overall_length < global_length) return -1; /* Message id */ if (**data != 0x02) return -1; (*data)++; global_length--; overall_length --; p = *data; if (snmp_ans1_length(data, *data+global_length, &length)) return -1; global_length -= *data - p; overall_length -= *data - p; if (global_length < length || length > sizeof(uint32_t)) return -1; *data += length; global_length -= length; overall_length -= length; /* Max message size */ if (global_length < 2) return -1; if (**data != 0x02) return -1; (*data)++; global_length--; overall_length --; p = *data; if (snmp_ans1_length(data, *data+global_length, &length)) return -1; global_length -= *data - p; overall_length -= *data - p; if (global_length < length || length > sizeof(uint32_t)) return -1; *data += length; global_length -= length; overall_length -= length; /* Message flags */ if (global_length < 2) return -1; if (**data != 0x04) return -1; (*data)++; global_length--; overall_length --; p = *data; if (snmp_ans1_length(data, *data+global_length, &length)) return -1; global_length -= *data - p; overall_length -= *data - p; if (length != 1 || global_length < length) return -1; (*data)++; global_length--; overall_length--; /* Security model */ if (global_length < 2) return -1; if (**data != 0x02) return -1; (*data)++; global_length--; overall_length --; p = *data; if (snmp_ans1_length(data, *data+global_length, &length)) return -1; global_length -= *data - p; overall_length -= *data - p; if (global_length < length || length > sizeof(uint32_t)) return -1; *data += length; global_length -= length; overall_length -= length; /* Security Parameters */ if (overall_length < 2) return -1; if (**data != 0x04) return -1; (*data)++; overall_length --; p = *data; if (snmp_ans1_length(data, *data+overall_length, &global_length)) return -1; overall_length -= *data - p; if (overall_length < global_length) return -1; *data += global_length; overall_length -= global_length; /* Message */ if (overall_length < 2) return -1; if (**data != 0x30) return -1; (*data)++; overall_length --; p = *data; if (snmp_ans1_length(data, *data+overall_length, &global_length)) return -1; overall_length -= *data - p; if (overall_length < global_length) return -1; /* Context Engine ID */ if (global_length < 2) return -1; if (**data != 0x04) return -1; (*data)++; global_length--; overall_length --; p = *data; if (snmp_ans1_length(data, *data+global_length, &length)) return -1; global_length -= *data - p; overall_length -= *data - p; if (global_length < length) return -1; *data += length; global_length -= length; overall_length -= length; /* Context Name */ if (global_length < 2) return -1; if (**data != 0x04) return -1; (*data)++; global_length--; overall_length --; p = *data; if (snmp_ans1_length(data, *data+global_length, &length)) return -1; global_length -= *data - p; overall_length -= *data - p; if (global_length < length) return -1; *data += length; global_length -= length; overall_length -= length; break; default: return -1; } if (!overall_length) return -1; cls = (**data) & 0xC0; if (cls != 0x80 && cls != 0x40) return -1; *pdu = (**data) & 0x1F; *version_ret = version; return 0; } static int snmp_validate(ServiceValidationArgs* args) { ServiceSNMPData *sd; ServiceSNMPData *tmp_sd; tAppIdData *pf; uint8_t pdu; sfaddr_t *sip; sfaddr_t *dip; uint8_t version; const char *version_str = NULL; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; bool app_id_debug_session_flag = args->app_id_debug_session_flag; char* app_id_debug_session = args->app_id_debug_session; if (!size) goto inprocess; sd = snmp_service_mod.api->data_get(flowp, snmp_service_mod.flow_data_index); if (!sd) { sd = calloc(1, sizeof(*sd)); if (!sd) return SERVICE_ENOMEM; if (snmp_service_mod.api->data_add(flowp, sd, snmp_service_mod.flow_data_index, &free)) { free(sd); return SERVICE_ENOMEM; } sd->state = SNMP_STATE_CONNECTION; } if (snmp_verify_packet(&data, data+size, &pdu, &version)) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s snmp payload verify failed\n", app_id_debug_session); if (getAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED)) { if (dir == APP_ID_FROM_RESPONDER) goto bail; else goto fail; } else { if (dir == APP_ID_FROM_RESPONDER) goto fail; else goto bail; } } if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s snmp state %d\n", app_id_debug_session, sd->state); switch (sd->state) { case SNMP_STATE_CONNECTION: if (pdu != SNMP_PDU_GET_RESPONSE && dir == APP_ID_FROM_RESPONDER) { sd->state = SNMP_STATE_R_RESPONSE; setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); break; } if (pdu == SNMP_PDU_GET_RESPONSE && dir == APP_ID_FROM_INITIATOR) { sd->state = SNMP_STATE_R_REQUEST; setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); break; } if (dir == APP_ID_FROM_RESPONDER) { sd->state = SNMP_STATE_REQUEST; break; } if (pdu == SNMP_PDU_TRAP || pdu == SNMP_PDU_TRAPV2) { setAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_NOT_A_SERVICE); clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); flowp->serviceAppId = APP_ID_SNMP; break; } sd->state = SNMP_STATE_RESPONSE; /*adding expected connection in case the server doesn't send from 161*/ dip = GET_DST_IP(pkt); sip = GET_SRC_IP(pkt); pf = snmp_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, pkt->src_port, flowp->proto, app_id, 0); if (pf) { tmp_sd = calloc(1, sizeof(ServiceSNMPData)); if (tmp_sd == NULL) return SERVICE_ENOMEM; tmp_sd->state = SNMP_STATE_RESPONSE; if (snmp_service_mod.api->data_add(pf, tmp_sd, snmp_service_mod.flow_data_index, &free)) { free(tmp_sd); return SERVICE_ENOMEM; } if (snmp_service_mod.api->data_add_id(pf, pkt->dst_port, &svc_element)) { setAppIdFlag(pf, APPID_SESSION_SERVICE_DETECTED); clearAppIdFlag(pf, APPID_SESSION_CONTINUE); tmp_sd->state = SNMP_STATE_ERROR; return SERVICE_ENULL; } PopulateExpectedFlow(flowp, pf, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_APPID_SESSION_DIRECTION_MAX); pf->rnaServiceState = RNA_STATE_STATEFUL; pf->scan_flags |= SCAN_HOST_PORT_FLAG; sfaddr_copy_to_raw(&pf->common.initiator_ip, sip); } break; case SNMP_STATE_RESPONSE: if (pdu == SNMP_PDU_GET_RESPONSE || pdu == SNMP_PDU_REPORT) { if (dir == APP_ID_FROM_RESPONDER) goto success; goto fail; } if (dir == APP_ID_FROM_RESPONDER) goto fail; break; case SNMP_STATE_REQUEST: if (pdu != SNMP_PDU_GET_RESPONSE) { if (dir == APP_ID_FROM_INITIATOR) goto success; goto fail; } if (dir == APP_ID_FROM_INITIATOR) goto fail; break; case SNMP_STATE_R_RESPONSE: if (pdu == SNMP_PDU_GET_RESPONSE || pdu == SNMP_PDU_REPORT) { if (dir == APP_ID_FROM_INITIATOR) goto success; goto fail; } if (dir == APP_ID_FROM_INITIATOR) goto fail; break; case SNMP_STATE_R_REQUEST: if (pdu != SNMP_PDU_GET_RESPONSE) { if (dir == APP_ID_FROM_RESPONDER) goto success; goto fail; } if (dir == APP_ID_FROM_RESPONDER) goto fail; break; default: if (dir == APP_ID_FROM_RESPONDER) goto fail; else goto bail; } inprocess: snmp_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: switch (version) { case SNMP_VERSION_1: version_str = SNMP_VERSION_STR_1; break; case SNMP_VERSION_2c: version_str = SNMP_VERSION_STR_2c; break; case SNMP_VERSION_2u: version_str = SNMP_VERSION_STR_2u; break; case SNMP_VERSION_3: version_str = SNMP_VERSION_STR_3; break; default: version_str = NULL; break; } snmp_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_SNMP, SNMP_VENDOR_STR, version_str, NULL, NULL); return SERVICE_SUCCESS; bail: snmp_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element, snmp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; fail: snmp_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, snmp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ssl.h0000644000175000017500000000325014241075557025215 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_SSL_H__ #define __SERVICE_SSL_H__ #include "service_api.h" extern struct RNAServiceValidationModule ssl_service_mod; tAppId getSslServiceAppId(short srcPort); bool isSslServiceAppId(tAppId appId); void service_ssl_clean(tServiceSslConfig *); int ssl_detector_process_patterns(tServiceSslConfig *); int ssl_scan_hostname(const u_int8_t*, size_t, tAppId*, tAppId*,tServiceSslConfig *); int ssl_scan_cname(const u_int8_t*, size_t, tAppId*, tAppId*,tServiceSslConfig *); int ssl_add_cert_pattern(uint8_t *, size_t, uint8_t, tAppId,tServiceSslConfig *); int ssl_add_cname_pattern(uint8_t *, size_t, uint8_t, tAppId,tServiceSslConfig *); void ssl_detector_free_patterns(tServiceSslConfig *); int setSSLSquelch(SFSnortPacket *p, int type, tAppId appId); #endif /* __SERVICE_SSL_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_util.h0000644000175000017500000000267414241075566025402 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_UTIL_H__ #define __SERVICE_UTIL_H__ #include #include static inline const uint8_t *service_strstr(const uint8_t *haystack, unsigned haystack_len, const uint8_t *needle, unsigned needle_len) { const uint8_t *p; const uint8_t *h_end = haystack + haystack_len; for (p = haystack; h_end-p >= (int)needle_len; p++) { if (memcmp(p, needle, needle_len) == 0) { return p; } } return NULL; } #endif /* __SERVICE_UTIL_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_direct_connect.h0000644000175000017500000000212414241075503027365 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_DIRECTCONNECT_H__ #define __SERVICE_DIRECTCONNECT_H__ #include "service_api.h" extern tRNAServiceValidationModule directconnect_service_mod; #endif /* __SERVICE_DIRECTCONNECT_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_netbios.h0000644000175000017500000000207414241075523026053 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_NETBIOS_H__ #define __SERVICE_NETBIOS_H__ #include "service_api.h" extern tRNAServiceValidationModule netbios_service_mod; #endif /* __SERVICE_NETBIOS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_battle_field.c0000644000175000017500000001671514241075464027034 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" typedef enum { CONN_STATE_INIT, CONN_STATE_HELLO_DETECTED, CONN_STATE_SERVICE_DETECTED, CONN_STATE_MESSAGE_DETECTED, CONN_STATE_MAX } CONNECTION_STATES; #define MAX_PACKET_INSPECTION_COUNT 10 typedef struct _SERVICE_DATA { uint32_t state; uint32_t messageId; uint32_t packetCount; } tServiceData; static int battle_field_init(const InitServiceAPI * const init_api); static int battle_field_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &battle_field_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "battle_field", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&battle_field_validate, 4711, IPPROTO_TCP}, {&battle_field_validate, 16567, IPPROTO_UDP}, {&battle_field_validate, 27900, IPPROTO_UDP}, {&battle_field_validate, 27900, IPPROTO_TCP}, {&battle_field_validate, 29900, IPPROTO_UDP}, {&battle_field_validate, 29900, IPPROTO_TCP}, {&battle_field_validate, 27901, IPPROTO_TCP}, {&battle_field_validate, 28910, IPPROTO_UDP}, {NULL, 0, 0} }; #define PATTERN_HELLO "battlefield2\x00" #define PATTERN_2 "\xfe\xfd" #define PATTERN_3 "\x11\x20\x00\x01\x00\x00\x50\xb9\x10\x11" #define PATTERN_4 "\x11\x20\x00\x01\x00\x00\x30\xb9\x10\x11" #define PATTERN_5 "\x11\x20\x00\x01\x00\x00\xa0\x98\x00\x11" #define PATTERN_6 "\xfe\xfd\x09\x00\x00\x00\x00" tRNAServiceValidationModule battlefield_service_mod = { "BattleField", &battle_field_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_BATTLEFIELD, 0}}; static int battle_field_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&battle_field_validate, IPPROTO_TCP, (uint8_t *)PATTERN_HELLO, sizeof(PATTERN_HELLO)-1, 5, "battle_field", init_api->pAppidConfig); init_api->RegisterPattern(&battle_field_validate, IPPROTO_TCP, (uint8_t *)PATTERN_2, sizeof(PATTERN_2)-1, 0, "battle_field", init_api->pAppidConfig); init_api->RegisterPattern(&battle_field_validate, IPPROTO_TCP, (uint8_t *)PATTERN_3, sizeof(PATTERN_3)-1, 0, "battle_field", init_api->pAppidConfig); init_api->RegisterPattern(&battle_field_validate, IPPROTO_TCP, (uint8_t *)PATTERN_4, sizeof(PATTERN_4)-1, 0, "battle_field", init_api->pAppidConfig); init_api->RegisterPattern(&battle_field_validate, IPPROTO_TCP, (uint8_t *)PATTERN_5, sizeof(PATTERN_5)-1, 0, "battle_field", init_api->pAppidConfig); init_api->RegisterPattern(&battle_field_validate, IPPROTO_TCP, (uint8_t *)PATTERN_6, sizeof(PATTERN_6)-1, 0, "battle_field", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&battle_field_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int battle_field_validate(ServiceValidationArgs* args) { tServiceData *fd; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; uint16_t size = args->size; if (!size) { goto inprocess_nofd; } fd = battlefield_service_mod.api->data_get(flowp, battlefield_service_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return SERVICE_ENOMEM; if (battlefield_service_mod.api->data_add(flowp, fd, battlefield_service_mod.flow_data_index, &free)) { free(fd); return SERVICE_ENOMEM; } } switch (fd->state) { case CONN_STATE_INIT: if ((pkt->src_port >= 27000 || pkt->dst_port >= 27000) && size >= 4) { if (data[0] == 0xfe && data[1] == 0xfd) { fd->messageId = (data[2]<<8) | data[3]; fd->state = CONN_STATE_MESSAGE_DETECTED; goto inprocess; } } if (size == 18 && memcmp(data+5, PATTERN_HELLO, sizeof(PATTERN_HELLO)-1) == 0) { fd->state = CONN_STATE_HELLO_DETECTED; goto inprocess; } break; case CONN_STATE_MESSAGE_DETECTED: if (size > 8) { if ((uint32_t)(data[0]<<8 | data[1]) == fd->messageId) { goto success; } if (data[0] == 0xfe && data[1] == 0xfd) { fd->messageId = (data[2]<<8) | data[3]; goto inprocess; } } fd->state = CONN_STATE_INIT; goto inprocess; break; case CONN_STATE_HELLO_DETECTED: if ((size == 7) && (memcmp(data, PATTERN_6, sizeof(PATTERN_6)-1) == 0)) { goto success; } if ((size > 10) && ((memcmp(data, PATTERN_3, sizeof(PATTERN_3)-1) == 0) || (memcmp(data, PATTERN_4, sizeof(PATTERN_4)-1) == 0) || (memcmp(data, PATTERN_5, sizeof(PATTERN_5)-1) == 0))) { goto success; } break; case CONN_STATE_SERVICE_DETECTED: goto success; } battlefield_service_mod.api->fail_service(flowp, pkt, args->dir, &svc_element, battlefield_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; inprocess: fd->packetCount++; if (fd->packetCount >= MAX_PACKET_INSPECTION_COUNT) goto fail; inprocess_nofd: battlefield_service_mod.api->service_inprocess(flowp, pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: if (args->dir != APP_ID_FROM_RESPONDER) { fd->state = CONN_STATE_SERVICE_DETECTED; goto inprocess; } battlefield_service_mod.api->add_service(flowp, pkt, args->dir, &svc_element, APP_ID_BATTLEFIELD, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: battlefield_service_mod.api->fail_service(flowp, pkt, args->dir, &svc_element, battlefield_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_base.h0000644000175000017500000001621314241075463025325 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_BASE_H__ #define __SERVICE_BASE_H__ #include "appIdApi.h" #include "service_api.h" #include "commonAppMatcher.h" #include "flow.h" #include "serviceConfig.h" #include "appIdConfig.h" #include "thirdparty_appid_utils.h" struct _SERVICE_MATCH; void CleanupServices(tAppIdConfig *pConfig); void ReconfigureServices(tAppIdConfig *pConfig); void UnconfigureServices(tAppIdConfig *pConfig); void ServiceInit(tAppIdConfig *pConfig); void ServiceFinalize(tAppIdConfig *pConfig); void FailInProcessService(tAppIdData *flowp, const tAppIdConfig *pConfig); int LoadServiceModules(const char **dir_list, uint32_t instance_id, tAppIdConfig *pConfig); /** * \brief Reload C service modules * * This function is called during reload/reconfiguration. It registers service ports in the given * AppId configuration. This function also takes care of services associated with detector modules. * * @param pConfig - AppId config in which services' ports get registered * @return 0 on success, -1 on failure */ int ReloadServiceModules(tAppIdConfig *pConfig); int serviceLoadCallback(void *symbol); int serviceLoadForConfigCallback(void *symbol, tAppIdConfig *pConfig); int ServiceAddPort(RNAServiceValidationPort *pp, tRNAServiceValidationModule *svm, struct _Detector* userdata, tAppIdConfig *pConfig); void ServiceRemovePorts(RNAServiceValidationFCN validate, struct _Detector* userdata, tAppIdConfig *pConfig); void ServiceRegisterPatternDetector(RNAServiceValidationFCN fcn, u_int8_t proto, const u_int8_t *pattern, unsigned size, int position, struct _Detector *userdata, const char *name); int AppIdDiscoverService(SFSnortPacket *p, APPID_SESSION_DIRECTION direction, tAppIdData *rnaData, const tAppIdConfig *pConfig); tAppId getPortServiceId(uint8_t proto, uint16_t port, const tAppIdConfig *pConfig); tAppId getProtocolServiceId(uint8_t proto, const tAppIdConfig *pConfig); void AppIdFreeServiceIDState(AppIdServiceIDState *id_state); int AppIdServiceAddService(tAppIdData*flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, tAppId appId, const char *vendor, const char *version, const RNAServiceSubtype *subtype, AppIdServiceIDState *id_state); int AppIdServiceAddServiceSubtype(tAppIdData*flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, tAppId appId, const char *vendor, const char *version, RNAServiceSubtype *subtype, AppIdServiceIDState *id_state); int AppIdServiceInProcess(tAppIdData*flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, AppIdServiceIDState *id_state); int AppIdServiceIncompatibleData(tAppIdData*flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, unsigned flow_data_index, const tAppIdConfig *pConfig, AppIdServiceIDState *id_state); int AppIdServiceFailService(tAppIdData*flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, unsigned flow_data_index, const tAppIdConfig *pConfig, AppIdServiceIDState *id_state); int AddFTPServiceState(tAppIdData *fp); void AppIdFreeDhcpInfo(DHCPInfo *dd); void AppIdFreeSMBData(FpSMBData *sd); void AppIdFreeDhcpData(DhcpFPData *dd); void dumpPorts(FILE *stream, const tAppIdConfig *pConfig); tRNAServiceElement *ServiceGetServiceElement(RNAServiceValidationFCN fcn, struct _Detector *userdata, tAppIdConfig *pConfig); extern tRNAServiceValidationModule *active_service_list; extern uint32_t app_id_instance_id; void cleanupFreeServiceMatch(void); void AppIdFreeServiceMatchList(struct _SERVICE_MATCH* sm); static inline bool compareServiceElements(const tRNAServiceElement *first, const tRNAServiceElement *second) { if (first == second) return 0; if (first == NULL || second == NULL) return 1; return (first->validate != second->validate || first->userdata != second->userdata); } // change UNIT_TESTING and UNIT_TEST_FIRST_DECRYPTED_PACKET in appIdApi.h static inline uint32_t AppIdServiceDetectionLevel(tAppIdData * session) { if (getAppIdFlag(session, APPID_SESSION_DECRYPTED)) return 1; #ifdef UNIT_TESTING if (session->session_packet_count >= UNIT_TEST_FIRST_DECRYPTED_PACKET) return 1; #endif return 0; } static inline void PopulateExpectedFlow(tAppIdData* parent, tAppIdData* expected, uint64_t flags, APPID_SESSION_DIRECTION dir) { if (dir == APP_ID_FROM_INITIATOR) { setAppIdFlag(expected, flags | getAppIdFlag(parent, APPID_SESSION_RESPONDER_MONITORED | APPID_SESSION_INITIATOR_MONITORED | APPID_SESSION_SPECIAL_MONITORED | APPID_SESSION_RESPONDER_CHECKED | APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_DISCOVER_APP | APPID_SESSION_DISCOVER_USER)); } else if (dir == APP_ID_FROM_RESPONDER) { if (getAppIdFlag(parent, APPID_SESSION_INITIATOR_MONITORED)) flags |= APPID_SESSION_RESPONDER_MONITORED; if (getAppIdFlag(parent, APPID_SESSION_INITIATOR_CHECKED)) flags |= APPID_SESSION_RESPONDER_CHECKED; if (getAppIdFlag(parent, APPID_SESSION_RESPONDER_MONITORED)) flags |= APPID_SESSION_INITIATOR_MONITORED; if (getAppIdFlag(parent, APPID_SESSION_RESPONDER_CHECKED)) flags |= APPID_SESSION_INITIATOR_CHECKED; setAppIdFlag(expected, flags | getAppIdFlag(parent, APPID_SESSION_SPECIAL_MONITORED | APPID_SESSION_DISCOVER_APP | APPID_SESSION_DISCOVER_USER)); } expected->rnaServiceState = RNA_STATE_FINISHED; expected->rnaClientState = RNA_STATE_FINISHED; if (thirdparty_appid_module) thirdparty_appid_module->session_state_set(expected->tpsession, TP_STATE_TERMINATED); } #endif /* __SERVICE_BASE_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ntp.c0000644000175000017500000001030414241075526025202 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" #pragma pack(1) typedef struct _SERVICE_NTP_TIMESTAMP { uint32_t sec; uint32_t frac; } ServiceNTPTimestamp; typedef struct _SERVICE_NTP_HEADER { uint8_t LVM; uint8_t stratum; uint8_t poll; int8_t precision; uint32_t delay; uint32_t dispersion; uint32_t id; ServiceNTPTimestamp ref; ServiceNTPTimestamp orig; ServiceNTPTimestamp recv; ServiceNTPTimestamp xmit; } ServiceNTPHeader; typedef struct _SERVICE_NTP_OPTIONAL { uint32_t keyid; uint32_t digest[4]; } ServiceNTPOptional; #pragma pack() static int ntp_init(const InitServiceAPI * const init_api); static int ntp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &ntp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "ntp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&ntp_validate, 123, IPPROTO_UDP}, {&ntp_validate, 123, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule ntp_service_mod = { "NTP", &ntp_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_NTP, 0}}; static int ntp_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&ntp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int ntp_validate(ServiceValidationArgs* args) { const ServiceNTPHeader *nh; uint8_t ver; uint8_t mode; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; nh = (ServiceNTPHeader *)data; mode = nh->LVM & 0x07; if (mode == 0 || mode == 7 || mode == 3) goto fail; ver = nh->LVM & 0x38; if (ver > 0x20 || ver < 0x08) goto fail; if (mode != 6) { if (ver < 0x18) { if (size != sizeof(ServiceNTPHeader)) goto fail; } else if (size < sizeof(ServiceNTPHeader) || size > sizeof(ServiceNTPHeader)+sizeof(ServiceNTPOptional)) { goto fail; } if (nh->stratum > 15) goto fail; if (nh->poll && (nh->poll < 4 || nh->poll > 14)) goto fail; if (nh->precision > -6 || nh->precision < -20) goto fail; } else { if (size < 2) goto fail; if (!(nh->stratum & 0x80)) goto fail; if (!(nh->stratum & 0x1F)) goto fail; } ntp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_NTP, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; inprocess: ntp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; fail: ntp_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, ntp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_lpr.c0000644000175000017500000001470114241075513025177 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" #define LPR_COUNT_THRESHOLD 5 typedef enum { LPR_STATE_COMMAND, LPR_STATE_RECEIVE, LPR_STATE_REPLY1, LPR_STATE_REPLY, LPR_STATE_IGNORE } LPRState; typedef enum { LPR_CMD_PRINT = 1, LPR_CMD_RECEIVE, LPR_CMD_SHORT_STATE, LPR_CMD_LONG_STATE, LPR_CMD_REMOVE } LPRCommand; typedef enum { LPR_SUBCMD_ABORT = 1, LPR_SUBCMD_CONTROL, LPR_SUBCMD_DATA } LPRSubCommand; typedef struct _SERVICE_LPR_DATA { LPRState state; unsigned no_data_count; unsigned count; } ServiceLPRData; static int lpr_init(const InitServiceAPI * const init_api); static int lpr_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &lpr_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "lpr", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&lpr_validate, 515, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule lpr_service_mod = { "LPR", &lpr_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_PRINTSRV, 0}}; static int lpr_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&lpr_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int lpr_validate(ServiceValidationArgs* args) { ServiceLPRData *ld; int i; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; ld = lpr_service_mod.api->data_get(flowp, lpr_service_mod.flow_data_index); if (!ld) { ld = calloc(1, sizeof(*ld)); if (!ld) return SERVICE_ENOMEM; if (lpr_service_mod.api->data_add(flowp, ld, lpr_service_mod.flow_data_index, &free)) { free(ld); return SERVICE_ENOMEM; } ld->state = LPR_STATE_COMMAND; } switch (ld->state) { case LPR_STATE_COMMAND: if (dir != APP_ID_FROM_INITIATOR) goto bail; if (size < 3) goto bail; switch (*data) { case LPR_CMD_RECEIVE: if (data[size-1] != 0x0A) goto bail; size--; for (i=1; istate = LPR_STATE_REPLY; break; case LPR_CMD_PRINT: ld->state = LPR_STATE_IGNORE; break; case LPR_CMD_SHORT_STATE: ld->state = LPR_STATE_IGNORE; break; case LPR_CMD_LONG_STATE: ld->state = LPR_STATE_IGNORE; break; case LPR_CMD_REMOVE: ld->state = LPR_STATE_IGNORE; break; default: goto bail; } break; case LPR_STATE_RECEIVE: if (dir != APP_ID_FROM_INITIATOR) goto inprocess; if (size < 2) goto bail; switch (*data) { case LPR_SUBCMD_ABORT: if (size != 2) goto bail; if (data[1] != 0x0A) goto bail; ld->state = LPR_STATE_REPLY; break; case LPR_SUBCMD_CONTROL: case LPR_SUBCMD_DATA: if (size < 5) goto bail; if (data[size-1] != 0x0A) goto bail; if (!isdigit(data[1])) goto bail; for (i=2; i= size) goto bail; for (; istate = LPR_STATE_REPLY1; break; default: goto bail; } break; case LPR_STATE_REPLY1: if (dir != APP_ID_FROM_RESPONDER) goto inprocess; if (size != 1) goto fail; ld->count++; if (ld->count >= LPR_COUNT_THRESHOLD) { ld->state = LPR_STATE_IGNORE; goto success; } ld->state = LPR_STATE_REPLY; break; case LPR_STATE_REPLY: if (dir != APP_ID_FROM_RESPONDER) goto inprocess; if (size != 1) goto fail; ld->count++; if (ld->count >= LPR_COUNT_THRESHOLD) { ld->state = LPR_STATE_IGNORE; goto success; } ld->state = LPR_STATE_RECEIVE; break; case LPR_STATE_IGNORE: break; default: goto bail; } inprocess: lpr_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: lpr_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element, APP_ID_PRINTSRV, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: lpr_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element, lpr_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; bail: lpr_service_mod.api->incompatible_data(flowp, args->pkt, dir, &svc_element, lpr_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ssh.h0000644000175000017500000000205414241075555025210 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_SSH_H__ #define __SERVICE_SSH_H__ #include "service_api.h" extern tRNAServiceValidationModule ssh_service_mod; #endif /* __SERVICE_SSH_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rlogin.c0000644000175000017500000001106014241075537025675 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" #define RLOGIN_PASSWORD "Password: " typedef enum { RLOGIN_STATE_HANDSHAKE, RLOGIN_STATE_PASSWORD, RLOGIN_STATE_CRLF, RLOGIN_STATE_DATA, RLOGIN_STATE_DONE } RLOGINState; typedef struct _SERVICE_RLOGIN_DATA { RLOGINState state; } ServiceRLOGINData; static int rlogin_init(const InitServiceAPI * const init_api); static int rlogin_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &rlogin_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "rlogin", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&rlogin_validate, 513, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule rlogin_service_mod = { "RLOGIN", &rlogin_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_RLOGIN, 0}}; static int rlogin_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&rlogin_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int rlogin_validate(ServiceValidationArgs* args) { ServiceRLOGINData *rd; tAppIdData *flowp = args->flowp; SFSnortPacket *pkt = args->pkt; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; rd = rlogin_service_mod.api->data_get(flowp, rlogin_service_mod.flow_data_index); if (!rd) { rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (rlogin_service_mod.api->data_add(flowp, rd, rlogin_service_mod.flow_data_index, &free)) { free(rd); return SERVICE_ENOMEM; } rd->state = RLOGIN_STATE_HANDSHAKE; } switch (rd->state) { case RLOGIN_STATE_HANDSHAKE: if (size != 1) goto fail; if (*data) goto fail; rd->state = RLOGIN_STATE_PASSWORD; break; case RLOGIN_STATE_PASSWORD: if ((pkt->tcp_header->flags & TCPHEADER_URG) && size >= ntohs(pkt->tcp_header->urgent_pointer)) { if (size != 1) goto fail; if (*data != 0x80) goto fail; rd->state = RLOGIN_STATE_DATA; } else { if (size != sizeof(RLOGIN_PASSWORD)-1) goto fail; if (strncmp((char *)data, RLOGIN_PASSWORD, sizeof(RLOGIN_PASSWORD)-1)) goto fail; rd->state = RLOGIN_STATE_CRLF; } break; case RLOGIN_STATE_CRLF: if (size != 2) goto fail; if (*data != 0x0A || *(data+1) != 0x0D) goto fail; rd->state = RLOGIN_STATE_DATA; break; case RLOGIN_STATE_DATA: rd->state = RLOGIN_STATE_DONE; goto success; default: goto fail; } inprocess: rlogin_service_mod.api->service_inprocess(flowp, pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: rlogin_service_mod.api->add_service(flowp, pkt, args->dir, &svc_element, APP_ID_RLOGIN, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: rlogin_service_mod.api->fail_service(flowp, pkt, args->dir, &svc_element, rlogin_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_bootp.c0000644000175000017500000002267614241075476025547 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define DHCP_MAGIC_COOKIE 0x63825363 #pragma pack(1) typedef struct _SERVICE_BOOTP_HEADER { uint8_t op; uint8_t htype; uint8_t hlen; uint8_t hops; uint32_t xid; uint16_t secs; uint16_t flags; uint32_t ciaddr; uint32_t yiaddr; uint32_t siaddr; uint32_t giaddr; uint8_t chaddr[16]; uint8_t sname[64]; uint8_t file[128]; } ServiceBOOTPHeader; typedef enum { DHCP_OPT_SUBNET_MASK = 1, DHCP_OPT_ROUTER = 3, DHCP_OPT_DOMAIN_NAME_SERVER = 6, DHCP_OPT_DOMAIN_NAME = 15, DHCP_OPT_IPADDR_LEASE_TIME = 51, DHCP_OPT_DHCP_MESSAGE_TYPE =53 } DHCP_OPTIONS; typedef struct _SERVICE_DHCP_HEADER { uint8_t option; uint8_t len; } ServiceDHCPOption; #pragma pack() static int bootp_init(const InitServiceAPI * const init_api); static int bootp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &bootp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "bootp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&bootp_validate, 67, IPPROTO_UDP}, {&bootp_validate, 67, IPPROTO_UDP, 1}, {NULL, 0, 0} }; tRNAServiceValidationModule bootp_service_mod = { "DHCP", &bootp_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_DHCP, APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED} }; static int bootp_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&bootp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int bootp_validate(ServiceValidationArgs* args) { const ServiceBOOTPHeader *bh; const ServiceDHCPOption *op; unsigned i; unsigned op55_len=0; unsigned op60_len=0; const uint8_t *op55=NULL; const uint8_t *op60=NULL; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; if (size < sizeof(ServiceBOOTPHeader)) goto fail; bh = (const ServiceBOOTPHeader *)data; if (bh->htype != 0x01) goto fail; if (bh->hlen != 0x06) goto fail; for (i=0; isname); i++) { if (!bh->sname[i]) break; } if (i >= sizeof(bh->sname)) goto fail; for (i=0; ifile); i++) { if (!bh->file[i]) break; } if (i >= sizeof(bh->file)) goto fail; if (bh->op == 0x01) { if (size > sizeof(ServiceBOOTPHeader) + 4) { if (ntohl(*((uint32_t *)(data + sizeof(ServiceBOOTPHeader)))) == DHCP_MAGIC_COOKIE) { int option53 = 0; for (i=sizeof(ServiceBOOTPHeader)+sizeof(uint32_t); ioption == 0xff) { if (!pkt->ether_header) goto fail; if (option53 && op55_len && (memcmp(pkt->ether_header->ether_source, bh->chaddr, 6) == 0)) { if(bootp_service_mod.api->data_add_dhcp(flowp, op55_len, op55, op60_len, op60, bh->chaddr)) { return SERVICE_ENOMEM; } } goto inprocess; } i += sizeof(ServiceDHCPOption); if (i >= size) goto not_compatible; if (op->option == 53 && op->len == 1 && i + 1 < size && data[i] == 3) { option53 = 1; } else if (op->option == 55 && op->len >= 1) { if(option53) { op55_len = op->len; op55 = &data[i]; } } else if (op->option == 60 && op->len >= 1) { if(option53) { op60_len = op->len; op60 = &data[i]; } } i += op->len; if (i >= size) goto not_compatible; } goto not_compatible; } } goto not_compatible; } if (bh->op != 0x02) goto fail; if (dir == APP_ID_FROM_INITIATOR) { setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); } else { clearAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); } if (size > sizeof(ServiceBOOTPHeader) + 4) { if (ntohl(*((uint32_t *)(data + sizeof(ServiceBOOTPHeader)))) == DHCP_MAGIC_COOKIE) { int option53 = 0; uint32_t subnet = 0; uint32_t router = 0; uint32_t leaseTime = 0; for (i=sizeof(ServiceBOOTPHeader)+sizeof(uint32_t); ioption == 0xff) { if (!pkt->ether_header) goto fail; if (option53 && (memcmp(pkt->ether_header->ether_destination, bh->chaddr, 6) == 0)) bootp_service_mod.api->dhcpNewLease(flowp, bh->chaddr, bh->yiaddr, pkt->pkt_header->ingress_group, ntohl(subnet), ntohl(leaseTime), router); goto success; } i += sizeof(ServiceDHCPOption); if (i + op->len > size) goto fail; switch (op->option) { case DHCP_OPT_DHCP_MESSAGE_TYPE: if (op->len == 1 && data[i] == 5) { option53 = 1; } break; case DHCP_OPT_SUBNET_MASK: if (op->len == 4) { memcpy(&subnet, &data[i], sizeof(subnet)); } break; case DHCP_OPT_ROUTER: if (op->len == 4) { memcpy(&router, &data[i], sizeof(router)); } break; case DHCP_OPT_IPADDR_LEASE_TIME: if (op->len == 4 ) { memcpy(&leaseTime, &data[i], sizeof(leaseTime)); } break; default: ; } i += op->len; if (i >= size) goto fail; } goto fail; } } success: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { setAppIdFlag(flowp, APPID_SESSION_CONTINUE); bootp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_DHCP, NULL, NULL, NULL, NULL); } return SERVICE_SUCCESS; inprocess: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { bootp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); } return SERVICE_INPROCESS; fail: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { bootp_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, bootp_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOMATCH; not_compatible: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { bootp_service_mod.api->incompatible_data(flowp, args->pkt, args->dir, &svc_element, bootp_service_mod.flow_data_index, args->pConfig, NULL); } return SERVICE_NOT_COMPATIBLE; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ftp.c0000644000175000017500000012126714241075506025203 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #include "service_util.h" #define FTP_PORT 21 /*#define RNA_FTP_EXPECTED_ON_PORT 1 */ typedef enum { FTP_STATE_CONNECTION, FTP_STATE_LOGIN, FTP_STATE_PASSWORD, FTP_STATE_ACCOUNT, FTP_STATE_CONNECTION_ERROR, FTP_STATE_MONITOR } FTPState; typedef enum { FTP_REPLY_BEGIN, FTP_REPLY_MULTI, FTP_REPLY_LONG, FTP_REPLY_MID } FTPReplyState; typedef enum { FTP_CMD_NONE, FTP_CMD_PORT_EPRT, FTP_CMD_PASV_EPSV } FTPCmd; #define MAX_STRING_SIZE 64 typedef struct _SERVICE_FTP_DATA { FTPState state; FTPReplyState rstate; int code; char vendor[MAX_STRING_SIZE]; char version[MAX_STRING_SIZE]; FTPCmd cmd; sfaddr_t address; uint16_t port; } ServiceFTPData; #pragma pack(1) typedef struct _SERVICE_FTP_CODE { uint8_t code[3]; uint8_t sp; } ServiceFTPCode; #pragma pack() static int ftp_init(const InitServiceAPI * const init_api); static int ftp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &ftp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "ftp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&ftp_validate, FTP_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule ftp_service_mod = { "FTP", &ftp_init, pp }; #define FTP_PATTERN1 "220 " #define FTP_PATTERN2 "220-" #define FTP_PATTERN3 "FTP" #define FTP_PATTERN4 "ftp" static tAppRegistryEntry appIdRegistry[] = { {APP_ID_FTP_CONTROL, APPINFO_FLAG_SERVICE_ADDITIONAL}, {APP_ID_FTP_ACTIVE, APPINFO_FLAG_SERVICE_ADDITIONAL}, {APP_ID_FTP_PASSIVE, APPINFO_FLAG_SERVICE_ADDITIONAL}, {APP_ID_FTPS, APPINFO_FLAG_SERVICE_ADDITIONAL} }; static int16_t ftp_data_app_id = 0; static int ftp_init(const InitServiceAPI * const init_api) { #ifdef TARGET_BASED ftp_data_app_id = init_api->dpd->addProtocolReference("ftp-data"); #endif init_api->RegisterPattern(&ftp_validate, IPPROTO_TCP, (uint8_t *)FTP_PATTERN1, sizeof(FTP_PATTERN1)-1, 0, "ftp", init_api->pAppidConfig); init_api->RegisterPattern(&ftp_validate, IPPROTO_TCP, (uint8_t *)FTP_PATTERN2, sizeof(FTP_PATTERN2)-1, 0, "ftp", init_api->pAppidConfig); init_api->RegisterPattern(&ftp_validate, IPPROTO_TCP, (uint8_t *)FTP_PATTERN3, sizeof(FTP_PATTERN3)-1, -1, "ftp", init_api->pAppidConfig); init_api->RegisterPattern(&ftp_validate, IPPROTO_TCP, (uint8_t *)FTP_PATTERN4, sizeof(FTP_PATTERN4)-1, -1, "ftp", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&ftp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static inline void CopyVendorString(ServiceFTPData *fd, const uint8_t *vendor, unsigned int vendorLen) { unsigned int copyLen = vendorLen < sizeof(fd->vendor)-1 ? vendorLen : sizeof(fd->vendor)-1; memcpy(fd->vendor, vendor, copyLen); fd->vendor[copyLen] = '\0'; } static inline void CopyVersionString(ServiceFTPData *fd, const uint8_t *version, unsigned int versionLen) { unsigned int copyLen = versionLen < sizeof(fd->version)-1 ? versionLen : sizeof(fd->version)-1; while (copyLen > 0 && !isalnum(version[copyLen-1])) { copyLen--; } memcpy(fd->version, version, copyLen); fd->version[copyLen] = '\0'; } typedef enum { VVP_PARSE_HP = 1, VVP_PARSE_FILEZILLA = 2, VVP_PARSE_MS = 3, VVP_PARSE_WU = 4, VVP_PARSE_PRO_FTPD = 5, VVP_PARSE_PURE_FTPD = 6, VVP_PARSE_NC_FTPD = 7 } VVP_PARSE_ENUM; static int VendorVersionParse(const uint8_t *data, uint16_t init_offset, uint16_t offset, ServiceFTPData *fd, const uint8_t *vendorCandidate, unsigned int vendorCandidateLen, const uint8_t *optionalVersion, unsigned int versionLen, VVP_PARSE_ENUM vvp_parse_type ) { const unsigned char *p; const unsigned char *end; const unsigned char *ver; unsigned int verlen; int ret = 0; // no match p = &data[init_offset]; end = &data[offset-1]; /* Search for the vendorCandidate string */ if (vvp_parse_type == VVP_PARSE_WU) { /* Search for the version string */ if ((p = service_strstr(p, end-p, optionalVersion, versionLen))) { /* If we like the version we will just assign the vendor */ CopyVendorString(fd, vendorCandidate, vendorCandidateLen); ret = 1; /* Found the version string. Move just past the version string */ ver = p + versionLen; p = ver; verlen = 0; while (p < end && *p && *p != ' ' ) { p++; verlen++; } CopyVersionString(fd, ver, verlen); } } else if ((p=service_strstr(p, end-p, vendorCandidate, vendorCandidateLen))) { /* Found vendorCandidate string */ CopyVendorString(fd, vendorCandidate, vendorCandidateLen); ret = 1; /* Move just past the vendor string */ p += vendorCandidateLen; if (optionalVersion) { /* Search for the version string */ if ((p = service_strstr(p, end-p, optionalVersion, versionLen))) { /* Found the version string. Move just past the version string */ ver = p + versionLen; p = ver; verlen = 0; switch (vvp_parse_type) { case VVP_PARSE_HP: while (p < end && *p && (isalnum(*p) || *p == '.')) { p++; verlen++; } break; case VVP_PARSE_FILEZILLA: while (p < end && *p && (isalnum(*p) || *p == '.' || *p == ' ')) { p++; verlen++; } break; case VVP_PARSE_MS: while (p < end && *p && *p != ')' ) { p++; verlen++; } break; case VVP_PARSE_PRO_FTPD: while (p < end && *p && *p != ' ' ) { p++; verlen++; } break; default: break; } CopyVersionString(fd, ver, verlen); } } } return ret; } static int CheckVendorVersion(const uint8_t *data, uint16_t init_offset, uint16_t offset, ServiceFTPData *fd, VVP_PARSE_ENUM vvp_parse_type ) { static const unsigned char ven_hp[] = "Hewlett-Packard FTP Print Server"; static const unsigned char ver_hp[] = "Version "; static const unsigned char ven_fzilla[] = "FileZilla Server"; static const unsigned char ver_fzilla[] = "version "; static const unsigned char ven_ms[] = "Microsoft FTP Service"; static const unsigned char ver_ms[] = "(Version "; static const unsigned char ven_wu[] = "wu"; static const unsigned char ver_wu[] = "(Version wu-"; static const unsigned char ven_proftpd[] = "ProFTPD"; static const unsigned char ven_pureftpd[] = "Pure-FTPd"; static const unsigned char ven_ncftpd[] = "NcFTPd"; if (!data || init_offset >= offset) return 0; switch (vvp_parse_type) { case VVP_PARSE_HP: return VendorVersionParse(data, init_offset, offset,fd, ven_hp, sizeof(ven_hp)-1, ver_hp, sizeof(ver_hp)-1, VVP_PARSE_HP); case VVP_PARSE_FILEZILLA: return VendorVersionParse(data, init_offset, offset,fd, ven_fzilla, sizeof(ven_fzilla)-1, ver_fzilla, sizeof(ver_fzilla)-1, VVP_PARSE_FILEZILLA); case VVP_PARSE_MS: return VendorVersionParse(data, init_offset, offset,fd, ven_ms, sizeof(ven_ms)-1, ver_ms, sizeof(ver_ms)-1, VVP_PARSE_MS); case VVP_PARSE_WU: return VendorVersionParse(data, init_offset, offset,fd, ven_wu, sizeof(ven_wu)-1, ver_wu, sizeof(ver_wu)-1, VVP_PARSE_WU); case VVP_PARSE_PRO_FTPD: return VendorVersionParse(data, init_offset, offset,fd, ven_proftpd, sizeof(ven_proftpd)-1, (unsigned char*)" ", sizeof(" ")-1, VVP_PARSE_PRO_FTPD); case VVP_PARSE_PURE_FTPD: return VendorVersionParse(data, init_offset, offset,fd, ven_pureftpd, sizeof(ven_pureftpd)-1, NULL, 0, VVP_PARSE_PURE_FTPD); case VVP_PARSE_NC_FTPD: return VendorVersionParse(data, init_offset, offset,fd, ven_ncftpd, sizeof(ven_ncftpd)-1, NULL, 0, VVP_PARSE_NC_FTPD); } return 0; } static int ftp_parse_response(const uint8_t *data, uint16_t *offset, uint16_t size, ServiceFTPData *fd, FTPReplyState rstate) { for (; *offset < size; (*offset)++) { if (data[*offset] == 0x0D) { (*offset)++; if (*offset >= size) return -1; if (data[*offset] == 0x0D) { (*offset)++; if (*offset >= size) return -1; } if (data[*offset] != 0x0A) return -1; fd->rstate = rstate; break; } if (data[*offset] == 0x0A) { fd->rstate = rstate; break; } } return 0; } static int ftp_validate_reply(const uint8_t *data, uint16_t *offset, uint16_t size, ServiceFTPData *fd) { const ServiceFTPCode *code_hdr; int tmp; FTPReplyState tmp_state; for (; *offset < size; (*offset)++) { /* Trim any blank lines (be a little tolerant) */ for (; *offsetrstate) { case FTP_REPLY_BEGIN: if (size - (*offset) < (int)sizeof(ServiceFTPCode)) return -1; code_hdr = (ServiceFTPCode *)(data + *offset); if (code_hdr->sp == '-') fd->rstate = FTP_REPLY_MULTI; else if (code_hdr->sp != ' ' && code_hdr->sp != 0x09) return -1; if (code_hdr->code[0] < '1' || code_hdr->code[0] > '5') return -1; fd->code = (code_hdr->code[0] - '0') * 100; if (code_hdr->code[1] < '0' || code_hdr->code[1] > '5') return -1; fd->code += (code_hdr->code[1] - '0') * 10; if (!isdigit(code_hdr->code[2])) return -1; fd->code += code_hdr->code[2] - '0'; *offset += sizeof(ServiceFTPCode); tmp_state = fd->rstate; if (!fd->vendor[0] && !fd->version[0]) { if (fd->code == 220) { // These vendor strings are present on the first "220" whether that is the "220-" or "220 " if (!CheckVendorVersion(data, *offset, size, fd, VVP_PARSE_MS ) && !CheckVendorVersion(data, *offset, size, fd, VVP_PARSE_WU ) && !CheckVendorVersion(data, *offset, size, fd, VVP_PARSE_PRO_FTPD ) && !CheckVendorVersion(data, *offset, size, fd, VVP_PARSE_PURE_FTPD ) && !CheckVendorVersion(data, *offset, size, fd, VVP_PARSE_NC_FTPD ) && !CheckVendorVersion(data, *offset, size, fd, VVP_PARSE_FILEZILLA) ) { /* Look for (Vendor Version: or (Vendor Version) */ const unsigned char *end; const unsigned char *p; const unsigned char *ven; const unsigned char *ver; end = &data[size-1]; for (p=&data[*offset]; p=end || !(*p)) { for (p=ver; pcode == 230) { // These vendor strings are present on the first "230" whether that is the "230-" or "230 " CheckVendorVersion(data, *offset, size, fd, VVP_PARSE_HP); } } fd->rstate = FTP_REPLY_MID; if (ftp_parse_response(data, offset, size, fd, tmp_state)) return -1; if (fd->rstate == FTP_REPLY_MID) fd->rstate = FTP_REPLY_LONG; break; case FTP_REPLY_MULTI: if (size - *offset < (int)sizeof(ServiceFTPCode)) { fd->rstate = FTP_REPLY_MID; if (ftp_parse_response(data, offset, size, fd, FTP_REPLY_MULTI)) return -1; if (fd->rstate == FTP_REPLY_MID) fd->rstate = FTP_REPLY_LONG; } else { code_hdr = (ServiceFTPCode *)(data + *offset); if ((code_hdr->sp == ' ' || code_hdr->sp == 0x09) && code_hdr->code[0] >= '1' && code_hdr->code[0] <= '5' && code_hdr->code[1] >= '1' && code_hdr->code[1] <= '5' && isdigit(code_hdr->code[2])) { tmp = (code_hdr->code[0] - '0') * 100; tmp += (code_hdr->code[1] - '0') * 10; tmp += code_hdr->code[2] - '0'; if (tmp == fd->code) { *offset += sizeof(ServiceFTPCode); fd->rstate = FTP_REPLY_BEGIN; } } tmp_state = fd->rstate; fd->rstate = FTP_REPLY_MID; if (ftp_parse_response(data, offset, size, fd, tmp_state)) return -1; if (fd->rstate == FTP_REPLY_MID) fd->rstate = FTP_REPLY_LONG; } break; case FTP_REPLY_LONG: fd->rstate = FTP_REPLY_MID; if (ftp_parse_response(data, offset, size, fd, FTP_REPLY_LONG)) return -1; if ((++(*offset)) >= size) { fd->rstate = FTP_REPLY_BEGIN; break; } if (fd->rstate == FTP_REPLY_MID) { fd->rstate = FTP_REPLY_LONG; break; } if (size - *offset < (int)sizeof(ServiceFTPCode)) { fd->rstate = FTP_REPLY_MID; if (ftp_parse_response(data, offset, size, fd, FTP_REPLY_LONG)) return -1; if (fd->rstate == FTP_REPLY_MID) fd->rstate = FTP_REPLY_LONG; } else { code_hdr = (ServiceFTPCode *)(data + *offset); if(code_hdr->code[0] >= '1' && code_hdr->code[0] <= '5' && code_hdr->code[1] >= '1' && code_hdr->code[1] <= '5' && isdigit(code_hdr->code[2])) { tmp = (code_hdr->code[0] - '0') * 100; tmp += (code_hdr->code[1] - '0') * 10; tmp += code_hdr->code[2] - '0'; if (tmp == fd->code) { *offset += sizeof(ServiceFTPCode); if (code_hdr->sp == ' ' || code_hdr->sp == 0x09) { fd->rstate = FTP_REPLY_MID; if (ftp_parse_response(data, offset, size, fd, FTP_REPLY_BEGIN)) return -1; } else if (code_hdr->sp == '-') { fd->rstate = FTP_REPLY_MID; if (ftp_parse_response(data, offset, size, fd, FTP_REPLY_MULTI)) return -1; } if (fd->rstate == FTP_REPLY_MID) fd->rstate = FTP_REPLY_LONG; } } } break; default: return -1; } if (fd->rstate == FTP_REPLY_BEGIN) { for (; *offset < size; (*offset)++) { if (data[*offset] == 0x0D) { (*offset)++; if (*offset >= size) return -1; if (data[*offset] != 0x0A) return -1; } else if (!isspace(data[*offset])) break; } return fd->code; } } return 0; } static inline int _ftp_decode_number32(const uint8_t * *data, const uint8_t *end, uint8_t delimiter, uint32_t *number) { const uint8_t *local_data; uint32_t local_number = 0; for (local_data = *data; local_data < end && *local_data == ' '; local_data++); if (local_data < end && *local_data == delimiter) { *number = 0; return -1; } while (local_data < end && *local_data != delimiter) { if (!isdigit(*local_data)) { *number = 0; return -1; } local_number *= 10; local_number += *local_data - '0'; local_data++; } if (local_data >= end || *local_data != delimiter) { *number = 0; return -1; } *number = local_number; *data = local_data+1; return 0; } static int ftp_decode_octet(const uint8_t * *data, const uint8_t *end, uint8_t delimiter, uint32_t *number) { if (_ftp_decode_number32(data, end, delimiter, number) == -1) return -1; if (*number > 255) { *number = 0; return -1; } return 0; } static int ftp_decode_port_number(const uint8_t * *data, const uint8_t *end, uint8_t delimiter, uint32_t *number) { if (_ftp_decode_number32(data, end, delimiter, number) == -1) return -1; if (*number > 65535) { *number = 0; return -1; } return 0; } static int ftp_validate_pasv(const uint8_t *data, uint16_t size, uint32_t *address, uint16_t *port) { const uint8_t *end; uint32_t tmp; *address = 0; *port = 0; end = data + size; data += sizeof(ServiceFTPCode); for (; data= end) return 1; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; *address = tmp << 24; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; *address += tmp << 16; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; *address += tmp << 8; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; *address += tmp; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; *port = (uint16_t)(tmp << 8); if (ftp_decode_octet(&data, end, ')', &tmp)) return -1; *port += tmp; return 0; } static int ftp_validate_epsv(const uint8_t *data, uint16_t size, uint16_t *port) { const uint8_t *end; uint8_t delimiter; *port = 0; end = data + size; data += sizeof(ServiceFTPCode); for (; data= end) return 1; delimiter = *data++; if (data >= end) return 1; for (; data= end) return 1; for (; data= end) return 1; while (data < end && *data != delimiter) { if (!isdigit(*data)) return -1; *port *= 10; *port += *data - '0'; data++; } return 0; } static int ftp_validate_port(const uint8_t *data, uint16_t size, sfaddr_t *address, uint16_t *port) { const uint8_t *end; const uint8_t *p; uint32_t tmp; uint32_t addr; uint32_t addr2; memset(address,0,sizeof(sfaddr_t)); *port = 0; end = data + size; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; addr = tmp << 24; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; addr += tmp << 16; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; addr += tmp << 8; if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; addr += tmp; addr2 = htonl(addr); // make it network order before calling sfip_set_raw() sfip_set_raw(address, &addr2, AF_INET); if (ftp_decode_octet(&data, end, ',', &tmp)) return -1; *port = (uint16_t)(tmp << 8); p = end - 1; if (p > data) { if (*p == 0x0a) { p--; if (*p == 0x0d) { if (ftp_decode_octet(&data, end, 0x0d, &tmp)) return -1; *port += tmp; return 0; } } } if (ftp_decode_octet(&data, end, 0x0a, &tmp)) return -1; *port += tmp; return 0; } /* RFC 2428 support */ typedef struct addr_family_map_t { uint16_t eprt_fam; uint16_t sfaddr_fam; } addr_family_map; static addr_family_map RFC2428_known_address_families[] = { { 1, AF_INET }, { 2, AF_INET6 }, { 0, 0 } }; static int ftp_validate_eprt(const uint8_t *data, uint16_t size, sfaddr_t *address, uint16_t *port) { int index; int addrFamilySupported = 0; uint8_t delimiter; const uint8_t *end; uint32_t tmp; char tmp_str[INET6_ADDRSTRLEN+1]; memset(address,0,sizeof(sfaddr_t)); *port = 0; end = data + size; delimiter = *data++; // all delimiters will match this one. if (ftp_decode_octet(&data, end, delimiter, &tmp)) return -1; // Look up the address family in the table. for (index = 0; !addrFamilySupported && RFC2428_known_address_families[index].eprt_fam != 0; index++) { if ( RFC2428_known_address_families[index].eprt_fam == (uint16_t)tmp ) { addrFamilySupported = RFC2428_known_address_families[index].sfaddr_fam; } } if (!addrFamilySupported) // not an ipv4 or ipv6 address being provided. return -1; for (index = 0; index < INET6_ADDRSTRLEN && data < end && *data != delimiter; index++, data++ ) { tmp_str[index] = *data; } tmp_str[index] = '\0'; // make the copied portion be nul terminated. if (sfip_convert_ip_text_to_binary( addrFamilySupported, tmp_str, &address->ip ) != SFIP_SUCCESS) return -1; address->family = addrFamilySupported; data++; // skip the delimiter at the end of the address substring. if (ftp_decode_port_number(&data, end, delimiter, &tmp)) // an error is returned if port was greater than 65535 return -1; *port = (uint16_t)tmp; return 0; } static inline void WatchForCommandResult(ServiceFTPData *fd, tAppIdData *flowp, FTPCmd command) { if (fd->state != FTP_STATE_MONITOR) { setAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_CONTINUE); fd->state = FTP_STATE_MONITOR; } fd->cmd = command; } static inline void CreateExpectedSession(tAppIdData *flowp, SFSnortPacket *ctrlPkt, sfaddr_t *cliIp, uint16_t cliPort, sfaddr_t *srvIp, uint16_t srvPort, uint8_t proto, int flags, APPID_SESSION_DIRECTION dir) { tAppIdData *fp; fp = ftp_service_mod.api->flow_new(flowp, ctrlPkt, cliIp, cliPort, srvIp, srvPort, proto, ftp_data_app_id, flags); if (fp) // initialize data session { unsigned encryptedFlag = getAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_DECRYPTED); if (encryptedFlag == APPID_SESSION_ENCRYPTED) { fp->serviceAppId = APP_ID_FTPSDATA; } else { encryptedFlag = 0; // change (APPID_SESSION_ENCRYPTED | APPID_SESSION_DECRYPTED) case to zeroes. fp->serviceAppId = APP_ID_FTP_DATA; } PopulateExpectedFlow(flowp, fp, APPID_SESSION_IGNORE_ID_FLAGS | encryptedFlag, dir); } } static int ftp_validate(ServiceValidationArgs* args) { static const char FTP_PASV_CMD[] = "PASV"; static const char FTP_EPSV_CMD[] = "EPSV"; static const char FTP_PORT_CMD[] = "PORT "; static const char FTP_EPRT_CMD[] = "EPRT "; ServiceFTPData *fd; uint16_t offset; uint16_t init_offset; int code; int code_index; uint32_t address; uint16_t port; int retval = SERVICE_INPROCESS; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; //ignore packets while encryption is on in explicit mode. In future, this will be changed //to direct traffic to SSL detector to extract payload from certs. This will require manintaining //two detector states at the same time. if (getAppIdFlag(flowp, APPID_SESSION_ENCRYPTED)) { if (!getAppIdFlag(flowp, APPID_SESSION_DECRYPTED)) { goto inprocess; } } fd = ftp_service_mod.api->data_get(flowp, ftp_service_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return SERVICE_ENOMEM; if (ftp_service_mod.api->data_add(flowp, fd, ftp_service_mod.flow_data_index, &free)) { free(fd); return SERVICE_ENOMEM; } fd->state = FTP_STATE_CONNECTION; fd->rstate = FTP_REPLY_BEGIN; fd->cmd = FTP_CMD_NONE; } if (dir != APP_ID_FROM_RESPONDER) { if (data[size-1] != 0x0a) goto inprocess; if (size > sizeof(FTP_PORT_CMD)-1 && strncasecmp((char *)data, FTP_PORT_CMD, sizeof(FTP_PORT_CMD)-1) == 0) { if (ftp_validate_port(data+(sizeof(FTP_PORT_CMD)-1), size-(sizeof(FTP_PORT_CMD)-1), &fd->address, &fd->port) == 0) { CreateExpectedSession(flowp, pkt, GET_DST_IP(pkt), 0, &fd->address, fd->port, flowp->proto, APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_RESPONDER); WatchForCommandResult(fd, flowp, FTP_CMD_PORT_EPRT); } } else if (size > sizeof(FTP_EPRT_CMD)-1 && strncasecmp((char *)data, FTP_EPRT_CMD, sizeof(FTP_EPRT_CMD)-1) == 0) { if (ftp_validate_eprt(data+(sizeof(FTP_EPRT_CMD)-1), size-(sizeof(FTP_EPRT_CMD)-1), &fd->address, &fd->port) == 0) { CreateExpectedSession(flowp, pkt, GET_DST_IP(pkt), 0, &fd->address, fd->port, flowp->proto, APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_RESPONDER); WatchForCommandResult(fd, flowp, FTP_CMD_PORT_EPRT); } } else if ( size > sizeof(FTP_PASV_CMD)-1 && ( strncasecmp((char *)data, FTP_PASV_CMD, sizeof(FTP_PASV_CMD)-1) == 0 || strncasecmp((char *)data, FTP_EPSV_CMD, sizeof(FTP_EPSV_CMD)-1) == 0 ) ) { WatchForCommandResult(fd, flowp, FTP_CMD_PASV_EPSV); } goto inprocess; } offset = 0; while (offset < size) { init_offset = offset; if ((code=ftp_validate_reply(data, &offset, size, fd)) < 0) goto fail; if (!code) goto inprocess; switch (fd->state) { case FTP_STATE_CONNECTION: switch (code) { case 120: /*system will be ready in nn minutes */ break; case 220: /*service ready for new user */ fd->state = FTP_STATE_LOGIN; break; case 110: /* restart mark reply */ case 125: /* connection is open start transferring file */ case 150: /* Opening command */ case 200: /*command ok */ case 202: /*command not implemented */ case 211: /* system status */ case 212: /* directory status */ case 213: /* file status */ case 214: /* help message */ case 215: /* name system type */ case 225: /* data connection open */ case 226: /* Transfer complete */ case 227: /*entering passive mode */ case 230: /*user loggined */ case 250: /* CWD command successful */ case 257: /* PATHNAME created */ case 331: /* login ok need password */ case 332: /*new account for login */ case 350: /*requested file action pending futher information */ case 450: /*requested file action not taken */ case 451: /*requested file action aborted */ case 452: /*requested file action not taken not enough space */ case 500: /*syntax error */ case 501: /*not recognozed */ case 502: /*not recognozed */ case 503: /*bad sequence of commands */ case 504: /*command not implemented */ case 530: /*login incorrect */ case 532: /*new account for storing file */ case 550: /*requested action not taken */ case 551: /*requested action aborted :page type unknown */ case 552: /*requested action aborted */ case 553: /*requested action not taken file name is not allowed */ setAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED | APPID_SESSION_CONTINUE); fd->state = FTP_STATE_MONITOR; break; case 221: /*good bye */ case 421: /*service not available closing connection */ fd->state = FTP_STATE_CONNECTION_ERROR; break; default: goto fail; } break; case FTP_STATE_LOGIN: code_index = code / 100; switch (code_index) { case 2: switch (code) { case 221: fd->state = FTP_STATE_CONNECTION_ERROR; break; case 230: setAppIdFlag(flowp, APPID_SESSION_CONTINUE); fd->state = FTP_STATE_MONITOR; retval = SERVICE_SUCCESS; break; case 234: { retval = SERVICE_SUCCESS; /* // we do not set the state to FTP_STATE_MONITOR here because we don't know // if there will be SSL decryption to allow us to see what we are interested in. // Let the WatchForCommandResult() usage elsewhere take care of it. */ setAppIdFlag(flowp, APPID_SESSION_CONTINUE | APPID_SESSION_ENCRYPTED | APPID_SESSION_STICKY_SERVICE); } break; default: break; } break; case 3: switch (code) { case 331: setAppIdFlag(flowp, APPID_SESSION_CONTINUE); fd->state = FTP_STATE_PASSWORD; retval = SERVICE_SUCCESS; break; case 332: fd->state = FTP_STATE_ACCOUNT; break; default: break; } break; case 4: switch (code) { case 421: fd->state = FTP_STATE_CONNECTION_ERROR; break; case 431: break; default: goto fail; } break; case 5: break; default: goto fail; } break; case FTP_STATE_PASSWORD: code_index = code / 100; switch (code_index) { case 2: switch (code) { case 221: fd->state = FTP_STATE_CONNECTION_ERROR; break; case 202: case 230: setAppIdFlag(flowp, APPID_SESSION_CONTINUE); fd->state = FTP_STATE_MONITOR; retval = SERVICE_SUCCESS; default: break; } break; case 3: switch (code) { case 332: fd->state = FTP_STATE_ACCOUNT; break; default: break; } break; case 4: switch (code) { case 421: fd->state = FTP_STATE_CONNECTION_ERROR; break; default: goto fail; } break; case 5: switch (code) { case 500: case 501: case 503: case 530: fd->state = FTP_STATE_LOGIN; break; default: goto fail; } break; default: goto fail; } break; case FTP_STATE_ACCOUNT: code_index = code / 100; switch (code_index) { case 2: switch (code) { case 202: case 230: setAppIdFlag(flowp, APPID_SESSION_CONTINUE); fd->state = FTP_STATE_MONITOR; retval = SERVICE_SUCCESS; default: break; } break; case 3: switch (code) { case 332: fd->state = FTP_STATE_ACCOUNT; break; default: break; } break; case 4: switch (code) { case 421: fd->state = FTP_STATE_CONNECTION_ERROR; break; default: goto fail; } break; case 5: switch (code) { case 500: case 501: case 503: case 530: fd->state = FTP_STATE_LOGIN; break; default: goto fail; } break; default: goto fail; } break; case FTP_STATE_MONITOR: // looking for the DATA channel info in the result switch (code) { case 227: { code = ftp_validate_pasv(data + init_offset, (uint16_t)(offset-init_offset), &address, &port); if (!code) { sfaddr_t ip; sfaddr_t *sip; sfaddr_t *dip; uint32_t addr; dip = GET_DST_IP(pkt); sip = GET_SRC_IP(pkt); addr = htonl(address); sfip_set_raw(&ip, &addr, AF_INET); CreateExpectedSession(flowp, pkt, dip, 0, &ip, port, flowp->proto, APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_INITIATOR); if (!sfip_fast_eq6(&ip, sip)) { CreateExpectedSession(flowp, pkt, dip, 0, sip, port, flowp->proto, APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_INITIATOR); } ftp_service_mod.api->add_payload(flowp, APP_ID_FTP_PASSIVE); // Passive mode FTP is reported as a payload id } else if (code < 0) { goto fail; } } break; case 229: { code = ftp_validate_epsv(data + init_offset, (uint16_t)(offset-init_offset), &port); if (!code) { sfaddr_t *sip; sfaddr_t *dip; dip = GET_DST_IP(pkt); sip = GET_SRC_IP(pkt); CreateExpectedSession(flowp, pkt, dip, 0, sip, port, flowp->proto, APPID_EARLY_SESSION_FLAG_FW_RULE, APP_ID_FROM_INITIATOR); ftp_service_mod.api->add_payload(flowp, APP_ID_FTP_PASSIVE); // Passive mode FTP is reported as a payload id } else if (code < 0) { goto fail; } } break; case 200: if (fd->cmd == FTP_CMD_PORT_EPRT) { // Expected session is created on PORT/EPRT command ftp_service_mod.api->add_payload(flowp, APP_ID_FTP_ACTIVE); // Active mode FTP is reported as a payload id } break; default: break; } fd->cmd = FTP_CMD_NONE; break; case FTP_STATE_CONNECTION_ERROR: default: goto fail; } } switch (retval) { default: case SERVICE_INPROCESS: inprocess: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { ftp_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); } return SERVICE_INPROCESS; case SERVICE_SUCCESS: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { uint64_t encryptedFlag = getAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_DECRYPTED); ftp_service_mod.api->add_service(flowp, pkt, dir, &svc_element, encryptedFlag == APPID_SESSION_ENCRYPTED ? // FTPS only when encrypted==1 decrypted==0 APP_ID_FTPS : APP_ID_FTP_CONTROL, fd->vendor[0] ? fd->vendor:NULL, fd->version[0] ? fd->version:NULL, NULL, NULL); } return SERVICE_SUCCESS; case SERVICE_NOMATCH: fail: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { ftp_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, ftp_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOMATCH; } } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rshell.h0000644000175000017500000000207014241075544025700 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_RSHELL_H__ #define __SERVICE_RSHELL_H__ #include "service_api.h" extern tRNAServiceValidationModule rshell_service_mod; #endif /* __SERVICE_RSHELL_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rpc.c0000644000175000017500000007652614241075541025204 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #if defined(FREEBSD) || defined(OPENBSD) #include "rpc/rpc.h" #endif /*#define RNA_DEBUG_RPC 1 */ typedef enum { RPC_STATE_CALL, RPC_STATE_REPLY, RPC_STATE_DONE } RPCState; typedef enum { RPC_TCP_STATE_FRAG, RPC_TCP_STATE_HEADER, RPC_TCP_STATE_CRED, RPC_TCP_STATE_CRED_DATA, RPC_TCP_STATE_VERIFY, RPC_TCP_STATE_VERIFY_DATA, RPC_TCP_STATE_REPLY_HEADER, RPC_TCP_STATE_PARTIAL, RPC_TCP_STATE_DONE } RPCTCPState; typedef enum { RPC_REPLY_BEGIN, RPC_REPLY_MULTI, RPC_REPLY_MID } RPCReplyState; #define min(x,y) ((x)<(y) ? (x):(y)) #define RPC_TYPE_CALL 0 #define RPC_TYPE_REPLY 1 #define RPC_PROGRAM_PORTMAP 100000 #define RPC_PORTMAP_GETPORT 3 #define RPC_REPLY_ACCEPTED 0 #define RPC_REPLY_DENIED 1 #define RPC_MAX_ACCEPTED 4 #define RPC_MAX_DENIED 5 #define RPC_TCP_FRAG_MASK 0x80000000 /* sizeof(ServiceRPCCall)+sizeof(_SERVICE_RPC_PORTMAP)==56 */ #define RPC_MAX_TCP_PACKET_SIZE 56 #pragma pack(1) typedef struct _SERVICE_RPC_FRAG { uint32_t length; } ServiceRPCFragment; typedef struct _SERVICE_RPC_AUTH { uint32_t flavor; uint32_t length; } ServiceRPCAuth; typedef struct _SERVICE_RPC_PORTMAP { uint32_t program; uint32_t version; uint32_t proto; uint32_t port; } ServiceRPCPortmap; typedef struct _SERVICE_RPC_PORTMAP_REPLY { uint32_t port; } ServiceRPCPortmapReply; typedef struct _SERVICE_RPC_HEADER { uint32_t xid; uint32_t type; } ServiceRPC; typedef struct _SERVICE_RPC_CALL_HEADER { ServiceRPC header; uint32_t version; uint32_t program; uint32_t program_version; uint32_t procedure; ServiceRPCAuth cred; ServiceRPCAuth verify; } ServiceRPCCall; typedef struct _SERVICE_RPC_REPLY_HEADER { ServiceRPC header; uint32_t reply_state; ServiceRPCAuth verify; uint32_t state; } ServiceRPCReply; #pragma pack() typedef struct _SERVICE_RPC_DATA { RPCState state; RPCTCPState tcpstate[APP_ID_APPID_SESSION_DIRECTION_MAX]; RPCTCPState tcpfragstate[APP_ID_APPID_SESSION_DIRECTION_MAX]; uint32_t program; uint32_t procedure; uint32_t xid; uint32_t proto; uint32_t tcpsize[APP_ID_APPID_SESSION_DIRECTION_MAX]; uint32_t tcpfragpos[APP_ID_APPID_SESSION_DIRECTION_MAX]; uint32_t tcpauthsize[APP_ID_APPID_SESSION_DIRECTION_MAX]; uint32_t tcppos[APP_ID_APPID_SESSION_DIRECTION_MAX]; uint8_t tcpdata[APP_ID_APPID_SESSION_DIRECTION_MAX][RPC_MAX_TCP_PACKET_SIZE]; int once; } ServiceRPCData; static int rpc_init(const InitServiceAPI * const init_api); static int rpc_validate(ServiceValidationArgs* args); static int rpc_tcp_validate(ServiceValidationArgs* args); static void rpc_clean(const CleanServiceAPI * const clean_api); static tRNAServiceElement svc_element = { .next = NULL, .validate = &rpc_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "rpc", .ref_count = 1, .current_ref_count = 1, }; static tRNAServiceElement tcp_svc_element = { .next = NULL, .validate = &rpc_tcp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "tcp rpc", .ref_count = 1, .current_ref_count = 1, }; #define RPC_PORT_PORTMAPPER 111 #define RPC_PORT_NFS 2049 #define RPC_PORT_MOUNTD 4046 #define RPC_PORT_NLOCKMGR 4045 static RNAServiceValidationPort pp[] = { {&rpc_validate, RPC_PORT_PORTMAPPER, IPPROTO_UDP}, {&rpc_validate, RPC_PORT_PORTMAPPER, IPPROTO_UDP, 1}, {&rpc_tcp_validate, RPC_PORT_PORTMAPPER, IPPROTO_TCP}, {&rpc_validate, RPC_PORT_NFS, IPPROTO_UDP}, {&rpc_validate, RPC_PORT_NFS, IPPROTO_UDP, 1}, {&rpc_tcp_validate, RPC_PORT_NFS, IPPROTO_TCP}, {&rpc_validate, RPC_PORT_MOUNTD, IPPROTO_UDP}, {&rpc_validate, RPC_PORT_MOUNTD, IPPROTO_UDP, 1}, {&rpc_tcp_validate, RPC_PORT_MOUNTD, IPPROTO_TCP}, {&rpc_validate, RPC_PORT_NLOCKMGR, IPPROTO_UDP}, {&rpc_validate, RPC_PORT_NLOCKMGR, IPPROTO_UDP, 1}, {&rpc_tcp_validate, RPC_PORT_NLOCKMGR, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule rpc_service_mod = { "RPC", &rpc_init, pp, .clean = rpc_clean }; typedef struct _RPC_PROGRAM { struct _RPC_PROGRAM *next; uint32_t program; char *name; } RPCProgram; static RPCProgram *rpc_programs = NULL; static uint8_t rpc_reply_accepted_pattern[8] = {0,0,0,1,0,0,0,0}; static uint8_t rpc_reply_denied_pattern[8] = {0,0,0,1,0,0,0,1}; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_SUN_RPC, APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_SERVICE_UDP_REVERSED} }; static int16_t app_id = 0; static int rpc_init(const InitServiceAPI * const init_api) { struct rpcent *rpc; RPCProgram *prog; #ifdef TARGET_BASED app_id = init_api->dpd->addProtocolReference("sunrpc"); #endif if (!rpc_programs) { while ((rpc = getrpcent())) { if (rpc->r_name) { prog = calloc(1, sizeof(RPCProgram)); if (prog) { prog->program = rpc->r_number; prog->next = rpc_programs; rpc_programs = prog; prog->name = strdup(rpc->r_name); if (!prog->name) _dpd.errMsg("failed to allocate rpc program name"); } } } endrpcent(); } init_api->RegisterPattern(&rpc_tcp_validate, IPPROTO_TCP, rpc_reply_accepted_pattern, sizeof(rpc_reply_accepted_pattern), 8, "rpc", init_api->pAppidConfig); init_api->RegisterPattern(&rpc_tcp_validate, IPPROTO_TCP, rpc_reply_denied_pattern, sizeof(rpc_reply_denied_pattern), 8, "rpc", init_api->pAppidConfig); init_api->RegisterPattern(&rpc_validate, IPPROTO_UDP, rpc_reply_accepted_pattern, sizeof(rpc_reply_accepted_pattern), 4, "rpc", init_api->pAppidConfig); init_api->RegisterPattern(&rpc_validate, IPPROTO_UDP, rpc_reply_denied_pattern, sizeof(rpc_reply_denied_pattern), 4, "rpc", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&rpc_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static const RPCProgram *FindRPCProgram(uint32_t program) { RPCProgram *rpc; for (rpc=rpc_programs; rpc; rpc=rpc->next) { if (program == rpc->program) break; } return rpc; } static int validate_packet(const uint8_t *data, uint16_t size, int dir, tAppIdData *flowp, SFSnortPacket *pkt, ServiceRPCData *rd, const char * *pname, uint32_t *program) { const ServiceRPCCall *call; const ServiceRPCReply *reply; const ServiceRPC *rpc; const ServiceRPCPortmap *pm; const ServiceRPCAuth *a; const ServiceRPCPortmapReply *pmr; uint32_t tmp; uint32_t val; const uint8_t *end; tAppIdData *pf; const RPCProgram *rprog; if (!size) return SERVICE_INPROCESS; end = data + size; if (flowp->proto == IPPROTO_UDP) { if (!rd->once) { rd->once = 1; if (size < sizeof(ServiceRPC)) return SERVICE_NOMATCH; rpc = (ServiceRPC *)data; if (ntohl(rpc->type) == RPC_TYPE_REPLY) { setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); rd->state = RPC_STATE_REPLY; dir = APP_ID_FROM_RESPONDER; } } else if (getAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED)) { dir = (dir == APP_ID_FROM_RESPONDER) ? APP_ID_FROM_INITIATOR:APP_ID_FROM_RESPONDER; } } switch (rd->state) { case RPC_STATE_CALL: if (dir != APP_ID_FROM_INITIATOR) return SERVICE_INPROCESS; rd->state = RPC_STATE_DONE; if (size < sizeof(ServiceRPCCall)) return SERVICE_NOT_COMPATIBLE; call = (ServiceRPCCall *)data; if (ntohl(call->header.type) != RPC_TYPE_CALL) return SERVICE_NOT_COMPATIBLE; if (ntohl(call->version) != 2) return SERVICE_NOT_COMPATIBLE; rd->program = ntohl(call->program); rd->procedure = ntohl(call->procedure); tmp = ntohl(call->cred.length); if (sizeof(ServiceRPCCall)+tmp > size) return SERVICE_NOT_COMPATIBLE; data += (sizeof(ServiceRPCCall) - sizeof(ServiceRPCAuth)) + tmp; a = (ServiceRPCAuth *)data; tmp = ntohl(a->length); if (tmp+sizeof(ServiceRPCAuth) > (unsigned)(end-data)) return SERVICE_NOT_COMPATIBLE; data += sizeof(ServiceRPCAuth) + tmp; if (rd->program >= 0x60000000) return SERVICE_NOT_COMPATIBLE; switch (rd->program) { case RPC_PROGRAM_PORTMAP: switch (rd->procedure) { case RPC_PORTMAP_GETPORT: if (end-data < (int)sizeof(ServiceRPCPortmap)) return SERVICE_NOT_COMPATIBLE; pm = (ServiceRPCPortmap *)data; rd->proto = pm->proto; break; default: break; } break; default: break; } rd->xid = call->header.xid; rd->state = RPC_STATE_REPLY; break; case RPC_STATE_REPLY: if (dir != APP_ID_FROM_RESPONDER) return SERVICE_INPROCESS; rd->state = RPC_STATE_DONE; if (size < sizeof(ServiceRPCReply)) return SERVICE_NOMATCH; reply = (ServiceRPCReply *)data; if (ntohl(reply->header.type) != RPC_TYPE_REPLY) return SERVICE_NOMATCH; if (rd->xid != reply->header.xid && rd->xid != 0xFFFFFFFF) return SERVICE_NOMATCH; tmp = ntohl(reply->verify.length); if (sizeof(ServiceRPCReply)+tmp > size) return SERVICE_NOMATCH; data += sizeof(ServiceRPCReply) + tmp; tmp = ntohl(reply->reply_state); val = ntohl(reply->state); if (tmp == RPC_REPLY_ACCEPTED) { if (val > RPC_MAX_ACCEPTED) return SERVICE_NOMATCH; if (rd->xid == 0xFFFFFFFF && reply->header.xid != 0xFFFFFFFF) { rd->state = RPC_STATE_CALL; return SERVICE_INPROCESS; } *program = rd->program; switch (rd->program) { case RPC_PROGRAM_PORTMAP: switch (rd->procedure) { case RPC_PORTMAP_GETPORT: if (end-data < (int)sizeof(ServiceRPCPortmapReply)) return SERVICE_NOMATCH; pmr = (ServiceRPCPortmapReply *)data; if (pmr->port) { sfaddr_t *sip; sfaddr_t *dip; dip = GET_DST_IP(pkt); sip = GET_SRC_IP(pkt); tmp = ntohl(pmr->port); #ifdef TARGET_BASED pf = rpc_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, (uint16_t)tmp, (uint8_t)ntohl(rd->proto), app_id, 0); if (pf) { rpc_service_mod.api->data_add_id(pf, (uint16_t)tmp, flowp->proto==IPPROTO_TCP ? &tcp_svc_element:&svc_element); pf->rnaServiceState = RNA_STATE_STATEFUL; setAppIdFlag(pf, getAppIdFlag(flowp, APPID_SESSION_RESPONDER_MONITORED | APPID_SESSION_INITIATOR_MONITORED | APPID_SESSION_SPECIAL_MONITORED | APPID_SESSION_RESPONDER_CHECKED | APPID_SESSION_INITIATOR_CHECKED | APPID_SESSION_DISCOVER_APP | APPID_SESSION_DISCOVER_USER)); } #endif } break; default: break; } *pname = "portmap"; break; default: rprog = FindRPCProgram(rd->program); if (rprog && rprog->name) *pname = rprog->name; break; } } else if (tmp == RPC_REPLY_DENIED) { if (val > RPC_MAX_DENIED) return SERVICE_NOMATCH; } else return SERVICE_NOMATCH; rd->state = RPC_STATE_CALL; return SERVICE_SUCCESS; default: return SERVICE_NOMATCH; } return SERVICE_INPROCESS; } static int rpc_validate(ServiceValidationArgs* args) { static char subname[64]; ServiceRPCData *rd; RNAServiceSubtype sub; RNAServiceSubtype *subtype; uint32_t program = 0; const char *pname = NULL; int rval; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; if (!size) { rval = SERVICE_INPROCESS; goto done; } rd = rpc_service_mod.api->data_get(flowp, rpc_service_mod.flow_data_index); if (!rd) { rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (rpc_service_mod.api->data_add(flowp, rd, rpc_service_mod.flow_data_index, &free)) { free(rd); return SERVICE_ENOMEM; } rd->state = (dir == APP_ID_FROM_INITIATOR) ? RPC_STATE_CALL:RPC_STATE_REPLY; rd->xid = 0xFFFFFFFF; } #ifdef RNA_DEBUG_RPC fprintf(SF_DEBUG_FILE, "Begin %u -> %u %u %d state %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state); #endif rval = validate_packet(data, size, dir, flowp, pkt, rd, &pname, &program); #ifdef RNA_DEBUG_RPC fprintf(SF_DEBUG_FILE, "End %u -> %u %u %d state %d rval %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state, rval); #endif done: switch (rval) { case SERVICE_INPROCESS: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { rpc_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); } return SERVICE_INPROCESS; case SERVICE_SUCCESS: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { if (pname && *pname) { memset(&sub, 0, sizeof(sub)); sub.service = pname; subtype = ⊂ } else if (program) { snprintf(subname, sizeof(subname), "(%u)", program); memset(&sub, 0, sizeof(sub)); sub.service = subname; subtype = ⊂ } else subtype = NULL; rpc_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_SUN_RPC, NULL, NULL, subtype, NULL); } setAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_SUCCESS; case SERVICE_NOT_COMPATIBLE: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { rpc_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element, rpc_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOT_COMPATIBLE; case SERVICE_NOMATCH: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { rpc_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, rpc_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOMATCH; default: return rval; } } static int rpc_tcp_validate(ServiceValidationArgs* args) { ServiceRPCData *rd; const ServiceRPCFragment *frag; uint32_t length; uint32_t fragsize; int ret; int retval = -1; ServiceRPCCall *call; ServiceRPCReply *reply; static char subname[64]; RNAServiceSubtype sub; RNAServiceSubtype *subtype; uint32_t program = 0; const char *pname = NULL; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; rd = rpc_service_mod.api->data_get(flowp, rpc_service_mod.flow_data_index); if (!rd) { rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (rpc_service_mod.api->data_add(flowp, rd, rpc_service_mod.flow_data_index, &free)) { free(rd); return SERVICE_ENOMEM; } rd->state = RPC_STATE_CALL; for (ret=0; rettcpstate[ret] = RPC_TCP_STATE_FRAG; rd->tcpfragstate[ret] = RPC_TCP_STATE_HEADER; } } while (size) { fragsize = min(size, (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK) - rd->tcpfragpos[dir]); switch (rd->tcpstate[dir]) { case RPC_TCP_STATE_FRAG: if (size < sizeof(ServiceRPCFragment)) goto bail; frag = (ServiceRPCFragment *)data; data += sizeof(ServiceRPCFragment); size -= sizeof(ServiceRPCFragment); rd->tcpsize[dir] = ntohl(frag->length); rd->tcpfragpos[dir] = 0; rd->tcpstate[dir] = rd->tcpfragstate[dir]; break; case RPC_TCP_STATE_HEADER: if (dir == APP_ID_FROM_INITIATOR) { length = min(fragsize, offsetof(ServiceRPCCall, cred) - rd->tcppos[dir]); memcpy(&rd->tcpdata[dir][rd->tcppos[dir]], data, length); rd->tcppos[dir] += length; rd->tcpfragpos[dir] += length; data += length; size -= length; if (rd->tcppos[dir] >= offsetof(ServiceRPCCall, cred)) { call = (ServiceRPCCall *)rd->tcpdata[dir]; if (ntohl(call->header.type) != RPC_TYPE_CALL) goto bail; if (ntohl(call->version) != 2) goto bail; rd->tcpstate[dir] = RPC_TCP_STATE_CRED; rd->tcppos[dir] = 0; } } else { length = min(fragsize, offsetof(ServiceRPCReply, verify) - rd->tcppos[dir]); memcpy(&rd->tcpdata[dir][rd->tcppos[dir]], data, length); rd->tcppos[dir] += length; rd->tcpfragpos[dir] += length; data += length; size -= length; if (rd->tcppos[dir] >= offsetof(ServiceRPCReply, verify)) { reply = (ServiceRPCReply *)rd->tcpdata[dir]; if (ntohl(reply->header.type) != RPC_TYPE_REPLY) goto fail; rd->tcpstate[dir] = RPC_TCP_STATE_VERIFY; rd->tcppos[dir] = 0; } } break; case RPC_TCP_STATE_CRED: if (dir != APP_ID_FROM_INITIATOR) goto bail; length = min(fragsize, sizeof(ServiceRPCAuth) - rd->tcppos[dir]); memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCCall, cred)+rd->tcppos[dir]], data, length); rd->tcppos[dir] += length; rd->tcpfragpos[dir] += length; data += length; size -= length; if (rd->tcppos[dir] >= sizeof(ServiceRPCAuth)) { call = (ServiceRPCCall *)rd->tcpdata[dir]; length = ntohl(call->cred.length); if (length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK) || rd->tcpfragpos[dir]+length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK)) goto bail; rd->tcpauthsize[dir] = length; rd->tcpstate[dir] = RPC_TCP_STATE_CRED_DATA; rd->tcppos[dir] = 0; } break; case RPC_TCP_STATE_CRED_DATA: if (dir != APP_ID_FROM_INITIATOR) goto bail; length = min(fragsize, rd->tcpauthsize[dir] - rd->tcppos[dir]); rd->tcppos[dir] += length; rd->tcpfragpos[dir] += length; data += length; size -= length; if (rd->tcppos[dir] >= rd->tcpauthsize[dir]) { call = (ServiceRPCCall *)rd->tcpdata[dir]; call->cred.flavor = 0; call->cred.length = 0; rd->tcpstate[dir] = RPC_TCP_STATE_VERIFY; rd->tcppos[dir] = 0; } break; case RPC_TCP_STATE_VERIFY: length = min(fragsize, sizeof(ServiceRPCAuth) - rd->tcppos[dir]); if (dir == APP_ID_FROM_INITIATOR) memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCCall, verify)+rd->tcppos[dir]], data, length); else memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCReply, verify)+rd->tcppos[dir]], data, length); rd->tcppos[dir] += length; rd->tcpfragpos[dir] += length; data += length; size -= length; fragsize -= length; if (rd->tcppos[dir] >= sizeof(ServiceRPCAuth)) { if (dir == APP_ID_FROM_INITIATOR) { call = (ServiceRPCCall *)rd->tcpdata[dir]; length = ntohl(call->verify.length); } else { reply = (ServiceRPCReply *)rd->tcpdata[dir]; length = ntohl(reply->verify.length); } if (length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK) || rd->tcpfragpos[dir]+length > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK)) goto bail; rd->tcpauthsize[dir] = length; rd->tcpstate[dir] = RPC_TCP_STATE_VERIFY_DATA; rd->tcppos[dir] = 0; } else { break; } case RPC_TCP_STATE_VERIFY_DATA: length = min(fragsize, rd->tcpauthsize[dir] - rd->tcppos[dir]); rd->tcppos[dir] += length; rd->tcpfragpos[dir] += length; data += length; size -= length; if (rd->tcppos[dir] >= rd->tcpauthsize[dir]) { if (dir == APP_ID_FROM_INITIATOR) { call = (ServiceRPCCall *)rd->tcpdata[dir]; call->verify.flavor = 0; call->verify.length = 0; rd->tcpstate[dir] = RPC_TCP_STATE_PARTIAL; rd->tcppos[dir] = sizeof(ServiceRPCCall); if (rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK)) { if (rd->tcpsize[dir] & RPC_TCP_FRAG_MASK) { #ifdef RNA_DEBUG_RPC fprintf(SF_DEBUG_FILE, "V Begin %u -> %u %u %d state %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state); #endif ret = validate_packet(rd->tcpdata[dir], rd->tcppos[dir], dir, flowp, pkt, rd, &pname, &program); #ifdef RNA_DEBUG_RPC fprintf(SF_DEBUG_FILE, "V End %u -> %u %u %d state %d rval %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state, ret); #endif if (retval == -1) retval = ret; rd->tcpfragstate[dir] = RPC_TCP_STATE_HEADER; rd->tcppos[dir] = 0; } else rd->tcpfragstate[dir] = rd->tcpstate[dir]; rd->tcpstate[dir] = RPC_TCP_STATE_FRAG; } } else { reply = (ServiceRPCReply *)rd->tcpdata[dir]; reply->verify.flavor = 0; reply->verify.length = 0; rd->tcpstate[dir] = RPC_TCP_STATE_REPLY_HEADER; if (rd->tcpfragpos[dir]+sizeof(uint32_t) > (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK)) goto bail; rd->tcppos[dir] = 0; } } break; case RPC_TCP_STATE_REPLY_HEADER: if (dir != APP_ID_FROM_RESPONDER) goto bail; length = min(fragsize, sizeof(uint32_t) - rd->tcppos[dir]); memcpy(&rd->tcpdata[dir][offsetof(ServiceRPCReply, state)+rd->tcppos[dir]], data, length); rd->tcppos[dir] += length; rd->tcpfragpos[dir] += length; data += length; size -= length; if (rd->tcppos[dir] >= sizeof(uint32_t)) { rd->tcpstate[dir] = RPC_TCP_STATE_PARTIAL; rd->tcppos[dir] = sizeof(ServiceRPCReply); } if (rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK)) { fragsize = 0; } else { break; } case RPC_TCP_STATE_PARTIAL: if (rd->tcppos[dir] < RPC_MAX_TCP_PACKET_SIZE && fragsize) { length = min(fragsize, RPC_MAX_TCP_PACKET_SIZE - rd->tcppos[dir]); memcpy(&rd->tcpdata[dir][rd->tcppos[dir]], data, length); rd->tcppos[dir] += length; } else { length = fragsize; } rd->tcpfragpos[dir] += length; data += length; size -= length; if (rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK)) { if (rd->tcpsize[dir] & RPC_TCP_FRAG_MASK) { #ifdef RNA_DEBUG_RPC fprintf(SF_DEBUG_FILE, "P Begin %u -> %u %u %d state %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state); #endif ret = validate_packet(rd->tcpdata[dir], rd->tcppos[dir], dir, flowp, pkt, rd, &pname, &program); #ifdef RNA_DEBUG_RPC fprintf(SF_DEBUG_FILE, "P End %u -> %u %u %d state %d rval %d\n", pkt->src_port, pkt->dst_port, flowp->proto, dir, rd->state, ret); #endif if (retval == -1) retval = ret; rd->tcpfragstate[dir] = RPC_TCP_STATE_HEADER; rd->tcppos[dir] = 0; } else rd->tcpfragstate[dir] = rd->tcpstate[dir]; rd->tcpstate[dir] = RPC_TCP_STATE_FRAG; } break; default: if (retval == -1) goto fail; else { clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); goto done; } } if (rd->tcpstate[dir] != RPC_TCP_STATE_FRAG && rd->tcpstate[dir] != RPC_TCP_STATE_PARTIAL && rd->tcpfragpos[dir] >= (rd->tcpsize[dir] & ~RPC_TCP_FRAG_MASK)) { if (rd->tcpsize[dir] & RPC_TCP_FRAG_MASK) goto bail; rd->tcpfragstate[dir] = rd->tcpstate[dir]; rd->tcpstate[dir] = RPC_TCP_STATE_FRAG; } } if (retval == -1) retval = SERVICE_INPROCESS; done: switch (retval) { case SERVICE_INPROCESS: inprocess: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { rpc_service_mod.api->service_inprocess(flowp, pkt, dir, &tcp_svc_element, NULL); } return SERVICE_INPROCESS; case SERVICE_SUCCESS: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { if (pname && *pname) { memset(&sub, 0, sizeof(sub)); sub.service = pname; subtype = ⊂ } else if (program) { sprintf(subname, "(%u)", program); memset(&sub, 0, sizeof(sub)); sub.service = subname; subtype = ⊂ } else subtype = NULL; rpc_service_mod.api->add_service(flowp, pkt, dir, &tcp_svc_element, APP_ID_SUN_RPC, NULL, NULL, subtype, NULL); } setAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_SUCCESS; case SERVICE_NOT_COMPATIBLE: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { rpc_service_mod.api->incompatible_data(flowp, pkt, dir, &tcp_svc_element, rpc_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOT_COMPATIBLE; case SERVICE_NOMATCH: fail: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { rpc_service_mod.api->fail_service(flowp, pkt, dir, &tcp_svc_element, rpc_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOMATCH; default: return retval; } bail: clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); rd->tcpstate[APP_ID_FROM_INITIATOR] = RPC_TCP_STATE_DONE; rd->tcpstate[APP_ID_FROM_RESPONDER] = RPC_TCP_STATE_DONE; if (dir == APP_ID_FROM_INITIATOR) { if (retval == -1) retval = SERVICE_NOT_COMPATIBLE; } else { if (retval == -1) retval = SERVICE_NOMATCH; } goto done; } static void rpc_clean(const CleanServiceAPI * const clean_api) { RPCProgram *prog = NULL; while(rpc_programs) { prog = rpc_programs; rpc_programs = rpc_programs->next; if(prog->name) free(prog->name); free(prog); } } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rlogin.h0000644000175000017500000000207014241075540025675 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_RLOGIN_H__ #define __SERVICE_RLOGIN_H__ #include "service_api.h" extern tRNAServiceValidationModule rlogin_service_mod; #endif /* __SERVICE_RLOGIN_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_flap.h0000644000175000017500000000206014241075505025325 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_FLAP_H__ #define __SERVICE_FLAP_H__ #include "service_api.h" extern tRNAServiceValidationModule flap_service_mod; #endif /* __SERVICE_FLAP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_bootp.h0000644000175000017500000000206414241075477025542 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_BOOTP_H__ #define __SERVICE_BOOTP_H__ #include "service_api.h" extern tRNAServiceValidationModule bootp_service_mod; #endif /* __SERVICE_BOOTP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rexec.c0000644000175000017500000002531314241075532025512 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define REXEC_PORT 512 #define REXEC_MAX_PORT_PACKET 6 typedef enum { REXEC_STATE_PORT, REXEC_STATE_SERVER_CONNECT, REXEC_STATE_USERNAME, REXEC_STATE_PASSWORD, REXEC_STATE_COMMAND, REXEC_STATE_REPLY, REXEC_STATE_DONE, REXEC_STATE_BAIL, REXEC_STATE_STDERR_CONNECT_SYN, REXEC_STATE_STDERR_CONNECT_SYN_ACK, REXEC_STATE_STDERR_WAIT, REXEC_STATE_STDERR_DONE } REXECState; typedef struct _SERVICE_REXEC_DATA { REXECState state; struct _SERVICE_REXEC_DATA *parent; struct _SERVICE_REXEC_DATA *child; } ServiceREXECData; static int rexec_init(const InitServiceAPI * const init_api); static int rexec_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &rexec_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "rexec", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&rexec_validate, REXEC_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule rexec_service_mod = { "REXEC", &rexec_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_EXEC, APPINFO_FLAG_SERVICE_ADDITIONAL}}; static int16_t app_id = 0; static int rexec_init(const InitServiceAPI * const init_api) { unsigned i; #ifdef TARGET_BASED app_id = init_api->dpd->addProtocolReference("rexec"); for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&rexec_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } #endif return 0; } static void rexec_free_state(void *data) { ServiceREXECData *rd = (ServiceREXECData *)data; if (rd) { if (rd->parent) { rd->parent->child = NULL; rd->parent->parent = NULL; } if (rd->child) { rd->child->parent = NULL; rd->child->child = NULL; } free(rd); } } /* Both the control & data sessions need to go to success else we bail. Let the control/data session know that we're bailing */ static void rexec_bail(ServiceREXECData *rd, tAppIdData *flowp) { clearAppIdFlag(flowp, APPID_SESSION_REXEC_STDERR); if (!rd) return; rd->state = REXEC_STATE_BAIL; if (rd->child) rd->child->state = REXEC_STATE_BAIL; if (rd->parent) rd->parent->state = REXEC_STATE_BAIL; } static int rexec_validate(ServiceValidationArgs* args) { ServiceREXECData *rd; ServiceREXECData *tmp_rd; int i; uint32_t port; tAppIdData *pf; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; bool app_id_debug_session_flag = args->app_id_debug_session_flag; char* app_id_debug_session = args->app_id_debug_session; rd = rexec_service_mod.api->data_get(flowp, rexec_service_mod.flow_data_index); if (!rd) { if (!size) goto inprocess; rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (rexec_service_mod.api->data_add(flowp, rd, rexec_service_mod.flow_data_index, &rexec_free_state)) { free(rd); return SERVICE_ENOMEM; } rd->state = REXEC_STATE_PORT; } if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s rexec state %d\n", app_id_debug_session, rd->state); switch (rd->state) { case REXEC_STATE_PORT: if (dir != APP_ID_FROM_INITIATOR) goto bail; if (size > REXEC_MAX_PORT_PACKET) goto bail; if (data[size-1]) goto bail; port = 0; for (i=0; i 65535) goto bail; if (port) { sfaddr_t *sip; sfaddr_t *dip; dip = GET_DST_IP(pkt); sip = GET_SRC_IP(pkt); pf = rexec_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, (uint16_t)port, IPPROTO_TCP, app_id, APPID_EARLY_SESSION_FLAG_FW_RULE); if (pf) { tmp_rd = calloc(1, sizeof(ServiceREXECData)); if (tmp_rd == NULL) return SERVICE_ENOMEM; tmp_rd->state = REXEC_STATE_STDERR_CONNECT_SYN; tmp_rd->parent = rd; if (rexec_service_mod.api->data_add(pf, tmp_rd, rexec_service_mod.flow_data_index, &rexec_free_state)) { pf->rnaServiceState = RNA_STATE_FINISHED; free(tmp_rd); return SERVICE_ENOMEM; } if (rexec_service_mod.api->data_add_id(pf, (uint16_t)port, &svc_element)) { pf->rnaServiceState = RNA_STATE_FINISHED; tmp_rd->state = REXEC_STATE_DONE; tmp_rd->parent = NULL; return SERVICE_ENULL; } pf->rnaServiceState = RNA_STATE_STATEFUL; pf->scan_flags |= SCAN_HOST_PORT_FLAG; PopulateExpectedFlow(flowp, pf, APPID_SESSION_CONTINUE | APPID_SESSION_REXEC_STDERR | APPID_SESSION_NO_TPI | APPID_SESSION_NOT_A_SERVICE | APPID_SESSION_PORT_SERVICE_DONE, APP_ID_FROM_RESPONDER); pf->rnaServiceState = RNA_STATE_STATEFUL; rd->child = tmp_rd; rd->state = REXEC_STATE_SERVER_CONNECT; setAppIdFlag(flowp, APPID_SESSION_CONTINUE); goto success; } else rd->state = REXEC_STATE_USERNAME; } else rd->state = REXEC_STATE_USERNAME; break; case REXEC_STATE_SERVER_CONNECT: if (!size) break; /* The only valid way out of this state is for the child flow to change it. */ goto fail; case REXEC_STATE_USERNAME: if (!size) break; if (dir != APP_ID_FROM_INITIATOR) goto bail; for (i=0; istate = REXEC_STATE_PASSWORD; if (i >= size) goto bail; i++; data += i; size -= i; /* Fall through */ case REXEC_STATE_PASSWORD: if (!size) break; if (dir != APP_ID_FROM_INITIATOR) goto bail; for (i=0; istate = REXEC_STATE_COMMAND; if (i >= size) goto bail; i++; data += i; size -= i; /* Fall through */ case REXEC_STATE_COMMAND: if (!size) break; if (dir != APP_ID_FROM_INITIATOR) goto bail; for (i=0; istate = REXEC_STATE_COMMAND; if (i >= size) goto bail; i++; data += i; size -= i; if (!size) { rd->state = REXEC_STATE_REPLY; break; } if (data[size-1]) goto bail; /* stdin */ for (i=0; istate = REXEC_STATE_REPLY; break; case REXEC_STATE_REPLY: if (!size) goto inprocess; if (dir != APP_ID_FROM_RESPONDER) goto fail; if (size != 1) goto fail; if (rd->child) { if(rd->child->state == REXEC_STATE_STDERR_WAIT) rd->child->state = REXEC_STATE_STDERR_DONE; else goto fail; } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); goto success; case REXEC_STATE_STDERR_CONNECT_SYN: rd->state = REXEC_STATE_STDERR_CONNECT_SYN_ACK; break; case REXEC_STATE_STDERR_CONNECT_SYN_ACK: if (rd->parent && rd->parent->state == REXEC_STATE_SERVER_CONNECT) { rd->parent->state = REXEC_STATE_USERNAME; rd->state = REXEC_STATE_STDERR_WAIT; break; } goto bail; case REXEC_STATE_STDERR_WAIT: /* The only valid way out of this state is for the parent flow to change it. */ if (!size) break; goto bail; case REXEC_STATE_STDERR_DONE: clearAppIdFlag(flowp, APPID_SESSION_REXEC_STDERR | APPID_SESSION_CONTINUE); goto success; case REXEC_STATE_BAIL: default: goto bail; } inprocess: rexec_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: rexec_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_EXEC, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; bail: rexec_bail(rd, flowp); rexec_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element, rexec_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; fail: rexec_bail(rd, flowp); rexec_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, rexec_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_flap.c0000644000175000017500000001452114241075504025324 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include "flow.h" #include "service_api.h" #define FLAP_PORT 5190 typedef enum { FLAP_STATE_ACK, FLAP_STATE_COOKIE } FLAPState; #define FNAC_SIGNON 0x0017 #define FNAC_GENERIC 0x0001 #define FNAC_SUB_SIGNON_REPLY 0x0007 #define FNAC_SUB_SERVER_READY 0x0003 typedef struct _SERVICE_FLAP_DATA { FLAPState state; } ServiceFLAPData; #pragma pack(1) typedef struct _FLAP_FNAC_SIGNON { uint16_t len; } FLAPFNACSignOn; typedef struct _FLAP_FNAC { uint16_t family; uint16_t subtype; uint16_t flags; uint32_t id; } FLAPFNAC; typedef struct _FLAP_TLV { uint16_t subtype; uint16_t len; } FLAPTLV; typedef struct _FLAP_HEADER { uint8_t start; uint8_t type; uint16_t seq; uint16_t len; } FLAPHeader; #pragma pack() static int flap_init(const InitServiceAPI * const init_api); static int flap_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &flap_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "flap", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&flap_validate, 5190, IPPROTO_TCP}, {&flap_validate, 9898, IPPROTO_TCP}, {&flap_validate, 4443, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule flap_service_mod = { "FLAP", &flap_init, pp }; static uint8_t FLAP_PATTERN[] = {0x2A, 0x01}; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_AOL_INSTANT_MESSENGER, 0}}; static int flap_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&flap_validate, IPPROTO_TCP, FLAP_PATTERN, sizeof(FLAP_PATTERN), 0, "flap", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&flap_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int flap_validate(ServiceValidationArgs* args) { ServiceFLAPData *sf; const uint8_t *data = args->data; const FLAPHeader *hdr = (const FLAPHeader *)args->data; const FLAPFNAC *ff; const FLAPTLV *tlv; tAppIdData *flowp = args->flowp; uint16_t size = args->size; uint16_t len; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; sf = flap_service_mod.api->data_get(flowp, flap_service_mod.flow_data_index); if (!sf) { sf = calloc(1, sizeof(*sf)); if (!sf) return SERVICE_ENOMEM; if (flap_service_mod.api->data_add(flowp, sf, flap_service_mod.flow_data_index, &free)) { free(sf); return SERVICE_ENOMEM; } sf->state = FLAP_STATE_ACK; } switch (sf->state) { case FLAP_STATE_ACK: sf->state = FLAP_STATE_COOKIE; if (size < sizeof(FLAPHeader)) goto fail; if (hdr->start != 0x2A) goto fail; if (hdr->type != 0x01) goto fail; if (ntohs(hdr->len) != 4) goto fail; if (size - sizeof(FLAPHeader) != 4) goto fail; if (ntohl(*((uint32_t *)(data + sizeof(FLAPHeader)))) != 0x00000001) goto fail; goto inprocess; case FLAP_STATE_COOKIE: if (size < sizeof(FLAPHeader) + sizeof(FLAPFNAC)) goto fail; if (hdr->start != 0x2A) goto fail; if ((uint16_t)ntohs(hdr->len) != (uint16_t)(size - sizeof(FLAPHeader))) goto fail; if (hdr->type == 0x02) { ff = (FLAPFNAC *)(data + sizeof(FLAPHeader)); if (ntohs(ff->family) == FNAC_SIGNON) { FLAPFNACSignOn *ffs = (FLAPFNACSignOn *)((uint8_t *)ff + sizeof(FLAPFNAC)); if (ntohs(ff->subtype) != FNAC_SUB_SIGNON_REPLY) goto fail; if ((uint16_t)ntohs(ffs->len) != (uint16_t)(size - (sizeof(FLAPHeader) + sizeof(FLAPFNAC) + sizeof(FLAPFNACSignOn)))) goto fail; } else if (ntohs(ff->family) == FNAC_GENERIC) { if (ntohs(ff->subtype) != FNAC_SUB_SERVER_READY) goto fail; } else goto fail; goto success; } if (hdr->type == 0x04) { data += sizeof(FLAPHeader); size -= sizeof(FLAPHeader); while (size >= sizeof(FLAPTLV)) { tlv = (FLAPTLV *)data; data += sizeof(FLAPTLV); size -= sizeof(FLAPTLV); len = ntohs(tlv->len); if (size < len) goto fail; size -= len; data += len; } if (size) goto fail; goto success; } goto fail; } fail: flap_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, flap_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; success: flap_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_AOL_INSTANT_MESSENGER, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; inprocess: flap_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rtmp.c0000644000175000017500000005224314241075547025376 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_base.h" #include "service_rtmp.h" #define RTMP_PORT 1935 #define RTMP_VER_3 3 #define RTMP_HANDSHAKE1_SIZE 1536 /* C1/S1 */ #define RTMP_HANDSHAKE2_SIZE 1536 /* C2/S2 */ #define RTMP_CHUNK_SIZE 128 #define RTMP_AMF0_COMMAND_MESSAGE_ID 20 #define RTMP_COMMAND_TYPE_CONNECT "connect" #define RTMP_COMMAND_TYPE_CONNECT_LEN 7 #define RTMP_PROPERTY_KEY_SWFURL "swfUrl" #define RTMP_PROPERTY_KEY_SWFURL_LEN 6 #define RTMP_PROPERTY_KEY_PAGEURL "pageUrl" #define RTMP_PROPERTY_KEY_PAGEURL_LEN 7 #define AMF0_TYPE_NUMBER 0x00 #define AMF0_TYPE_BOOLEAN 0x01 #define AMF0_TYPE_STRING 0x02 #define AMF0_TYPE_OBJECT 0x03 #define AMF0_TYPE_OBJECT_END 0x09 /* Preceded by 0x00,0x00. */ #define CHECK_SIZE(n) do { if (size < (n)) goto parse_rtmp_message_fail; } while (0) #define ADVANCE_DATA(n) do { data += (n); size -= (n); } while (0) typedef enum { RTMP_STATE_INIT = 0, /* Haven't seen anything yet. */ RTMP_STATE_SENT_HANDSHAKE0, /* C0/S0 */ RTMP_STATE_SENDING_HANDSHAKE1, /* C1/S1 -- client/server_bytes_left */ RTMP_STATE_SENT_HANDSHAKE1, /* C1/S1 */ RTMP_STATE_SENDING_HANDSHAKE2, /* C2/S2 -- client/server_bytes_left */ RTMP_STATE_SENT_HANDSHAKE2, /* C2/S2 */ RTMP_STATE_DONE /* As in "this detector is done watching the client or server". */ } RTMPState; typedef struct _SERVICE_RTMP_DATA { RTMPState client_state; RTMPState server_state; uint16_t client_bytes_left; uint16_t server_bytes_left; char *swfUrl; char *pageUrl; } ServiceRTMPData; static int rtmp_init(const InitServiceAPI * const api); static int rtmp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &rtmp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "rtmp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&rtmp_validate, 1935, IPPROTO_TCP}, {&rtmp_validate, 1935, IPPROTO_UDP}, {NULL, 0, 0} }; tRNAServiceValidationModule rtmp_service_mod = { "rtmp", &rtmp_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_RTMP, APPINFO_FLAG_SERVICE_ADDITIONAL} }; static int rtmp_init(const InitServiceAPI * const init_api) { unsigned i; for (i = 0; i < (sizeof(appIdRegistry) / sizeof(*appIdRegistry)); i++) { _dpd.debugMsg(DEBUG_LOG, "registering appId: %d\n", appIdRegistry[i].appId); init_api->RegisterAppId(&rtmp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } void rtmp_free(void *ss) /* AppIdFreeFCN */ { ServiceRTMPData *ss_tmp = (ServiceRTMPData*)ss; free(ss_tmp->swfUrl); free(ss_tmp->pageUrl); free(ss_tmp); } int parse_rtmp_chunk_basic_header(const uint8_t **data_inout, uint16_t *size_inout, uint8_t *format, uint32_t *chunk_stream_id) { const uint8_t *data = *data_inout; uint16_t size = *size_inout; if (size < 1) return 0; *format = (data[0] & 0xC0) >> 6; *chunk_stream_id = (data[0] & 0x3F); if (*chunk_stream_id == 0) { if (size < 2) return 0; *chunk_stream_id = data[1] + 64; data += 2; size -= 2; } else if (*chunk_stream_id == 1) { *chunk_stream_id = data[2] * 256 + data[1] + 64; if (size < 3) return 0; data += 3; size -= 3; } else { data += 1; size -= 1; } *data_inout = data; *size_inout = size; return 1; } int parse_rtmp_messgage_header(const uint8_t **data_inout, uint16_t *size_inout, uint32_t *chunk_stream_id, uint32_t *message_length, uint8_t *message_type_id) { const uint8_t *data = *data_inout; uint16_t size = *size_inout; uint8_t fmt; unsigned hdr_len; if (!parse_rtmp_chunk_basic_header(&data, &size, &fmt, chunk_stream_id)) return 0; switch (fmt) { case 0: hdr_len = 11; break; case 1: hdr_len = 7; break; default: return 0; } if (size < hdr_len) return 0; *message_length = (data[3] << 16) + (data[4] << 8) + data[5]; *message_type_id = data[6]; data += hdr_len; size -= hdr_len; *data_inout = data; *size_inout = size; return 1; } int unchunk_rtmp_message_body(const uint8_t **data_inout, uint16_t *size_inout, uint32_t chunk_stream_id, uint32_t message_length, uint8_t *message_body) { const uint8_t *data = *data_inout; uint16_t size = *size_inout; while (message_length > 0) { uint32_t chunk_len; chunk_len = message_length; if (message_length > RTMP_CHUNK_SIZE) chunk_len = RTMP_CHUNK_SIZE; if (size < chunk_len) return 0; memcpy(message_body, data, chunk_len); data += chunk_len; size -= chunk_len; message_body += chunk_len; message_length -= chunk_len; if (message_length > 0) { uint8_t fmt; uint32_t id; if (!parse_rtmp_chunk_basic_header(&data, &size, &fmt, &id)) return 0; if (fmt != 3) return 0; if (id != chunk_stream_id) return 0; } } *data_inout = data; *size_inout = size; return 1; } char * duplicate_string(const uint8_t **data_inout, uint16_t *size_inout) { const uint8_t *data = *data_inout; uint16_t size = *size_inout; uint16_t field_len; char *str; if (size < (1 + 2)) return NULL; if (data[0] != AMF0_TYPE_STRING) return NULL; field_len = (data[1] << 8) + data[2]; if (field_len == 0) return NULL; data += 1 + 2; size -= 1 + 2; if (size < field_len) return NULL; str = malloc(field_len + 1); if (str == NULL) return NULL; memcpy(str, data, field_len); str[field_len] = '\0'; data += field_len; size -= field_len; *data_inout = data; *size_inout = size; return str; } int skip_property_value(const uint8_t **data_inout, uint16_t *size_inout) { const uint8_t *data = *data_inout; uint16_t size = *size_inout; uint8_t type; uint16_t field_len; if (size < 1) return 0; type = data[0]; data += 1; size -= 1; switch (type) { case AMF0_TYPE_NUMBER: if (size < 8) return 0; data += 8; size -= 8; break; case AMF0_TYPE_BOOLEAN: if (size < 1) return 0; data += 1; size -= 1; break; case AMF0_TYPE_STRING: if (size < 2) return 0; field_len = (data[0] << 8) + data[1]; data += 2; size -= 2; if (size < field_len) return 0; data += field_len; size -= field_len; break; default: return 0; } *data_inout = data; *size_inout = size; return 1; } int parse_rtmp_message(const uint8_t **data_inout, uint16_t *size_inout, ServiceRTMPData *ss) { const uint8_t *data = *data_inout; uint16_t size = *size_inout; int ret = 1; uint32_t id; uint32_t msg_len; uint8_t msg_type; uint16_t field_len; uint8_t *body = NULL; if (!parse_rtmp_messgage_header(&data, &size, &id, &msg_len, &msg_type)) goto parse_rtmp_message_fail; if (msg_type != RTMP_AMF0_COMMAND_MESSAGE_ID) goto parse_rtmp_message_fail; body = malloc(msg_len); if (body == NULL) goto parse_rtmp_message_fail; if (!unchunk_rtmp_message_body(&data, &size, id, msg_len, body)) goto parse_rtmp_message_fail; *data_inout = data; *size_inout = size; /* Now we have a message body of a command (hopefully a connect). */ data = body; size = msg_len; /* Make sure it's a connect command. */ CHECK_SIZE(1 + 2); if (data[0] != AMF0_TYPE_STRING) goto parse_rtmp_message_fail; field_len = (data[1] << 8) + data[2]; if (field_len == 0) goto parse_rtmp_message_fail; ADVANCE_DATA(1 + 2); CHECK_SIZE(field_len); if (strncmp((const char *)data, RTMP_COMMAND_TYPE_CONNECT, field_len) != 0) goto parse_rtmp_message_fail; ADVANCE_DATA(field_len); /* Make sure transaction ID is next. */ CHECK_SIZE(1 + 8); if (data[0] != AMF0_TYPE_NUMBER) goto parse_rtmp_message_fail; ADVANCE_DATA(1 + 8); /* Make sure we have the command object next. */ CHECK_SIZE(1); if (data[0] != AMF0_TYPE_OBJECT) goto parse_rtmp_message_fail; ADVANCE_DATA(1); /* Search command object for desired metadata. */ do { /* Check for end of object. */ CHECK_SIZE(3); /* Need at least this much for full end of object. */ field_len = (data[0] << 8) + data[1]; if (field_len == 0) { if (data[2] == AMF0_TYPE_OBJECT_END) break; else goto parse_rtmp_message_fail; } ADVANCE_DATA(2); /* Not at end, so just get to start of key string for continued processing below. */ /* See if we're interested in this property key (or just skip it). */ CHECK_SIZE(field_len); if ( (ss->swfUrl == NULL) && (field_len == RTMP_PROPERTY_KEY_SWFURL_LEN) && (strncmp((const char *)data, RTMP_PROPERTY_KEY_SWFURL, RTMP_PROPERTY_KEY_SWFURL_LEN) == 0) ) { /* swfUrl */ ADVANCE_DATA(field_len); ss->swfUrl = duplicate_string(&data, &size); if (ss->swfUrl == NULL) goto parse_rtmp_message_fail; } else if ( (ss->pageUrl == NULL) && (field_len == RTMP_PROPERTY_KEY_PAGEURL_LEN) && (strncmp((const char *)data, RTMP_PROPERTY_KEY_PAGEURL, RTMP_PROPERTY_KEY_PAGEURL_LEN) == 0) ) { /* pageUrl */ ADVANCE_DATA(field_len); ss->pageUrl = duplicate_string(&data, &size); if (ss->pageUrl == NULL) goto parse_rtmp_message_fail; } else { /* Something we dont care about... */ ADVANCE_DATA(field_len); if (!skip_property_value(&data, &size)) goto parse_rtmp_message_fail; } } while (size > 0); parse_rtmp_message_done: free(body); return ret; parse_rtmp_message_fail: ret = 0; goto parse_rtmp_message_done; } static int rtmp_validate(ServiceValidationArgs* args) { ServiceRTMPData *ss; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; ss = rtmp_service_mod.api->data_get(flowp, rtmp_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return SERVICE_ENOMEM; if (rtmp_service_mod.api->data_add(flowp, ss, rtmp_service_mod.flow_data_index, &rtmp_free)) { free(ss); return SERVICE_ENOMEM; } } /* Client -> Server */ if (dir == APP_ID_FROM_INITIATOR) { /* Consume this packet. */ while (size > 0) { switch (ss->client_state) { case RTMP_STATE_INIT: /* C0 is just a version number. Must be valid. */ if (*data != RTMP_VER_3) { goto fail; } ss->client_state = RTMP_STATE_SENT_HANDSHAKE0; data += 1; size -= 1; break; case RTMP_STATE_SENT_HANDSHAKE0: /* Just skip RTMP_HANDSHAKE1_SIZE bytes for C1. */ ss->client_state = RTMP_STATE_SENDING_HANDSHAKE1; ss->client_bytes_left = RTMP_HANDSHAKE1_SIZE; /* fall through */ case RTMP_STATE_SENDING_HANDSHAKE1: if (size < ss->client_bytes_left) { /* We've still got more to get next time around. */ ss->client_bytes_left -= size; size = 0; } else { /* We've gotten all of the bytes that we wanted. */ ss->client_state = RTMP_STATE_SENT_HANDSHAKE1; data += ss->client_bytes_left; size -= ss->client_bytes_left; } break; case RTMP_STATE_SENT_HANDSHAKE1: /* Client can't start sending C2 until it has received S1. */ if (ss->server_state < RTMP_STATE_SENT_HANDSHAKE1) { goto fail; } /* Just skip RTMP_HANDSHAKE2_SIZE bytes for C2. */ ss->client_state = RTMP_STATE_SENDING_HANDSHAKE2; ss->client_bytes_left = RTMP_HANDSHAKE2_SIZE; /* fall through */ case RTMP_STATE_SENDING_HANDSHAKE2: if (size < ss->client_bytes_left) { /* We've still got more to get next time around. */ ss->client_bytes_left -= size; size = 0; } else { /* We've gotten all of the bytes that we wanted. */ ss->client_state = RTMP_STATE_SENT_HANDSHAKE2; data += ss->client_bytes_left; size -= ss->client_bytes_left; } break; case RTMP_STATE_SENT_HANDSHAKE2: if (parse_rtmp_message(&data, &size, ss)) { /* Got our connect command. We're done. */ ss->client_state = RTMP_STATE_DONE; } else { /* No connect command found. Bail out. */ goto fail; } /* fall through */ case RTMP_STATE_DONE: /* We're done with client, so just blindly consume all data. */ size = 0; break; default: goto fail; /* No reason to ever get here. */ } } } /* Server -> Client */ else if (dir == APP_ID_FROM_RESPONDER) { /* Consume this packet. */ while (size > 0) { switch (ss->server_state) { case RTMP_STATE_INIT: /* Client must initiate. */ if (ss->client_state < RTMP_STATE_SENT_HANDSHAKE0) { goto fail; } /* S0 is just a version number. Must be valid. */ if (*data != RTMP_VER_3) { goto fail; } ss->server_state = RTMP_STATE_SENT_HANDSHAKE0; data += 1; size -= 1; break; case RTMP_STATE_SENT_HANDSHAKE0: /* Just skip RTMP_HANDSHAKE1_SIZE bytes for S1. */ ss->server_state = RTMP_STATE_SENDING_HANDSHAKE1; ss->server_bytes_left = RTMP_HANDSHAKE1_SIZE; /* fall through */ case RTMP_STATE_SENDING_HANDSHAKE1: if (size < ss->server_bytes_left) { /* We've still got more to get next time around. */ ss->server_bytes_left -= size; size = 0; } else { /* We've gotten all of the bytes that we wanted. */ ss->server_state = RTMP_STATE_SENT_HANDSHAKE1; data += ss->server_bytes_left; size -= ss->server_bytes_left; } break; case RTMP_STATE_SENT_HANDSHAKE1: /* Server can't start sending S2 until it has received C1. */ if (ss->client_state < RTMP_STATE_SENT_HANDSHAKE1) { goto fail; } /* Just skip RTMP_HANDSHAKE2_SIZE bytes for S2. */ ss->server_state = RTMP_STATE_SENDING_HANDSHAKE2; ss->server_bytes_left = RTMP_HANDSHAKE2_SIZE; /* fall through */ case RTMP_STATE_SENDING_HANDSHAKE2: if (size < ss->server_bytes_left) { /* We've still got more to get next time around. */ ss->server_bytes_left -= size; size = 0; break; /* Not done yet. */ } else { /* We've gotten all of the bytes that we wanted. */ ss->server_state = RTMP_STATE_SENT_HANDSHAKE2; data += ss->server_bytes_left; size -= ss->server_bytes_left; } /* fall through */ case RTMP_STATE_SENT_HANDSHAKE2: /* No more interest in watching server. */ ss->server_state = RTMP_STATE_DONE; /* fall through */ case RTMP_STATE_DONE: /* We're done with server, so just blindly consume all data. */ size = 0; break; default: goto fail; /* No reason to ever get here. */ } } } /* Are we there yet? */ if ( (ss->client_state == RTMP_STATE_DONE) && (ss->server_state == RTMP_STATE_DONE) ) { goto success; } /* Give up if it's taking us too long to figure out this thing. */ if (flowp->session_packet_count >= appidStaticConfig->rtmp_max_packets) { goto fail; } inprocess: rtmp_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; fail: free(ss->swfUrl); free(ss->pageUrl); ss->swfUrl = ss->pageUrl = NULL; rtmp_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element, rtmp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; success: if (ss->swfUrl != NULL) { if (!flowp->hsession) { if (!(flowp->hsession = calloc(1, sizeof(*flowp->hsession)))) DynamicPreprocessorFatalMessage("Could not allocate httpSession data"); } if (flowp->hsession->url == NULL) { flowp->hsession->url = ss->swfUrl; flowp->scan_flags |= SCAN_HTTP_HOST_URL_FLAG; } else { free(ss->swfUrl); } ss->swfUrl = NULL; } if (ss->pageUrl != NULL) { if (!flowp->hsession) { if (!(flowp->hsession = calloc(1, sizeof(*flowp->hsession)))) DynamicPreprocessorFatalMessage("Could not allocate httpSession data"); } if (!appidStaticConfig->referred_appId_disabled && (flowp->hsession->referer == NULL)) flowp->hsession->referer = ss->pageUrl; else free(ss->pageUrl); ss->pageUrl = NULL; } rtmp_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element, APP_ID_RTMP, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_mysql.h0000644000175000017500000000206414241075520025551 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_MYSQL_H__ #define __SERVICE_MYSQL_H__ #include "service_api.h" extern tRNAServiceValidationModule mysql_service_mod; #endif /* __SERVICE_MYSQL_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_irc.h0000644000175000017500000000205414241075512025161 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_IRC_H__ #define __SERVICE_IRC_H__ #include "service_api.h" extern tRNAServiceValidationModule irc_service_mod; #endif /* __SERVICE_IRC_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_radius.h0000644000175000017500000000207014241075531025672 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_RADIUS_H__ #define __SERVICE_RADIUS_H__ #include "service_api.h" extern tRNAServiceValidationModule radius_service_mod; #endif /* __SERVICE_RADIUS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/serviceConfig.h0000644000175000017500000000670014241075567025466 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef SERVICE_CONFIG_H_ #define SERVICE_CONFIG_H_ /****************************** INCLUDES **************************************/ #include #include #include "service_api.h" #define RNA_SERVICE_MAX_PORT 65536 /********************************* TYPES **************************************/ struct RNAServiceElement; struct RNAServiceValidationModule; typedef struct { uint8_t type; tAppId appId; uint8_t *pattern; int pattern_size; } SSLCertPattern; typedef struct _DetectorSSLCertPattern { SSLCertPattern *dpattern; struct _DetectorSSLCertPattern *next; } DetectorSSLCertPattern; typedef struct { DetectorSSLCertPattern *DetectorSSLCertPatternList; DetectorSSLCertPattern *DetectorSSLCnamePatternList; void *ssl_host_matcher; void *ssl_cname_matcher; } tServiceSslConfig; /*DNS host pattern structure*/ typedef struct { uint8_t type; tAppId appId; uint8_t *pattern; int pattern_size; } DNSHostPattern; typedef struct _DetectorDNSHostPattern { DNSHostPattern *dpattern; struct _DetectorDNSHostPattern *next; } DetectorDNSHostPattern; typedef struct { DetectorDNSHostPattern *DetectorDNSHostPatternList; void *dns_host_host_matcher; } tServiceDnsConfig; typedef struct servicePatternData_ { struct servicePatternData_ *next; int position; unsigned size; struct RNAServiceElement *svc; } tServicePatternData; typedef struct { struct RNAServiceValidationModule *active_service_list; ///< List of all services (Lua and C) struct RNAServiceElement *tcp_service_list; ///< List of all TCP services (Lua and C) struct RNAServiceElement *udp_service_list; ///< List of all UDP services (Lua and C) struct RNAServiceElement *udp_reversed_service_list; ///< List of all UDP reversed services (Lua and C) //list nodes are RNAServiceElement*. SF_LIST *tcp_services[RNA_SERVICE_MAX_PORT]; SF_LIST *udp_services[RNA_SERVICE_MAX_PORT]; SF_LIST *udp_reversed_services[RNA_SERVICE_MAX_PORT]; void *tcp_patterns; tServicePatternData *tcp_pattern_data; int tcp_pattern_count; void *udp_patterns; tServicePatternData *udp_pattern_data; int udp_pattern_count; } tServiceConfig; #endif // SERVICE_CONFIG_H_ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rsync.h0000644000175000017500000000206414241075546025552 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_RSYNC_H__ #define __SERVICE_RSYNC_H__ #include "service_api.h" extern tRNAServiceValidationModule rsync_service_mod; #endif /* __SERVICE_RSYNC_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rexec.h0000644000175000017500000000206414241075533025516 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_REXEC_H__ #define __SERVICE_REXEC_H__ #include "service_api.h" extern tRNAServiceValidationModule rexec_service_mod; #endif /* __SERVICE_REXEC_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rfb.c0000644000175000017500000000762314241075534025163 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define RFB_BANNER_SIZE 12 #define RFB_BANNER "RFB " static int rfb_init(const InitServiceAPI * const init_api); static int rfb_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &rfb_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "rfb", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&rfb_validate, 5900, IPPROTO_TCP}, {&rfb_validate, 5901, IPPROTO_TCP}, {&rfb_validate, 5902, IPPROTO_TCP}, {&rfb_validate, 5903, IPPROTO_TCP}, {&rfb_validate, 5904, IPPROTO_TCP}, {&rfb_validate, 5905, IPPROTO_TCP}, {&rfb_validate, 5906, IPPROTO_TCP}, {&rfb_validate, 5907, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule rfb_service_mod = { "RFB", &rfb_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_VNC, APPINFO_FLAG_SERVICE_ADDITIONAL}, {APP_ID_VNC_RFB, APPINFO_FLAG_SERVICE_ADDITIONAL} }; static int rfb_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&rfb_validate, IPPROTO_TCP, (uint8_t *)RFB_BANNER, sizeof(RFB_BANNER)-1, 0, "rfb", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&rfb_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int rfb_validate(ServiceValidationArgs* args) { char version[RFB_BANNER_SIZE-4]; unsigned i; char *v; const unsigned char *p; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; if (size != RFB_BANNER_SIZE) goto fail; if (strncmp(RFB_BANNER, (char *)data, sizeof(RFB_BANNER)-1)) goto fail; if (data[7] != '.' || data[RFB_BANNER_SIZE-1] != 0x0A) goto fail; if (!isdigit(data[4]) || !isdigit(data[5]) || !isdigit(data[6]) || !isdigit(data[8]) || !isdigit(data[9]) || !isdigit(data[10])) { goto fail; } v = version; p = &data[4]; for (i=4; iadd_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_VNC_RFB, NULL, version, NULL, NULL); return SERVICE_SUCCESS; inprocess: rfb_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; fail: rfb_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, rfb_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_MDNS.h0000644000175000017500000000206014241075516025146 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_MDNS_H__ #define __SERVICE_MDNS_H__ #include "service_api.h" extern tRNAServiceValidationModule mdns_service_mod; #endif /* __SERVICE_MDNS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_nntp.h0000644000175000017500000000206014241075525025364 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_NNTP_H__ #define __SERVICE_NNTP_H__ #include "service_api.h" extern tRNAServiceValidationModule nntp_service_mod; #endif /* __SERVICE_NNTP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_direct_connect.c0000644000175000017500000002471214241075502027366 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" typedef enum { CONN_STATE_INIT, CONN_STATE_1, CONN_STATE_2, CONN_STATE_SERVICE_DETECTED, CONN_STATE_MAX } CONNECTION_STATES; #define MAX_PACKET_INSPECTION_COUNT 10 typedef struct _SERVICE_DATA { uint32_t state; uint32_t packetCount; } tServiceData; static int direct_connect_init(const InitServiceAPI * const init_api); static int direct_connect_validate(ServiceValidationArgs* args); static int validateDirectConnectTcp(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, const SFSnortPacket *pkt, tServiceData *serviceData, const struct appIdConfig_ *pConfig); static int validateDirectConnectUdp(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, const SFSnortPacket *pkt, tServiceData *serviceData, const struct appIdConfig_ *pConfig); static tRNAServiceElement svc_element = { .next = NULL, .validate = &direct_connect_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "direct_connect", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&direct_connect_validate, 411, IPPROTO_TCP}, {&direct_connect_validate, 411, IPPROTO_UDP}, {&direct_connect_validate, 412, IPPROTO_TCP}, {&direct_connect_validate, 412, IPPROTO_UDP}, {&direct_connect_validate, 413, IPPROTO_TCP}, {&direct_connect_validate, 413, IPPROTO_UDP}, {&direct_connect_validate, 414, IPPROTO_TCP}, {&direct_connect_validate, 414, IPPROTO_UDP}, {NULL, 0, 0} }; #define PATTERN1 "$Lock " #define PATTERN2 "$MyNick " #define PATTERN3 "HSUP ADBAS0" #define PATTERN4 "HSUP ADBASE" #define PATTERN5 "CSUP ADBAS0" #define PATTERN6 "CSUP ADBASE" #define PATTERN7 "$SR " tRNAServiceValidationModule directconnect_service_mod = { "DirectConnect", &direct_connect_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_DIRECT_CONNECT, 0}}; static int direct_connect_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&direct_connect_validate, IPPROTO_TCP, (uint8_t *)PATTERN1, sizeof(PATTERN1)-1, 0, "direct_connect", init_api->pAppidConfig); init_api->RegisterPattern(&direct_connect_validate, IPPROTO_TCP, (uint8_t *)PATTERN2, sizeof(PATTERN2)-1, 0, "direct_connect", init_api->pAppidConfig); init_api->RegisterPattern(&direct_connect_validate, IPPROTO_TCP, (uint8_t *)PATTERN3, sizeof(PATTERN3)-1, 0, "direct_connect", init_api->pAppidConfig); init_api->RegisterPattern(&direct_connect_validate, IPPROTO_TCP, (uint8_t *)PATTERN4, sizeof(PATTERN4)-1, 0, "direct_connect", init_api->pAppidConfig); init_api->RegisterPattern(&direct_connect_validate, IPPROTO_TCP, (uint8_t *)PATTERN5, sizeof(PATTERN5)-1, 0, "direct_connect", init_api->pAppidConfig); init_api->RegisterPattern(&direct_connect_validate, IPPROTO_TCP, (uint8_t *)PATTERN6, sizeof(PATTERN6)-1, 0, "direct_connect", init_api->pAppidConfig); init_api->RegisterPattern(&direct_connect_validate, IPPROTO_UDP, (uint8_t *)PATTERN7, sizeof(PATTERN7)-1, 0, "direct_connect", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&direct_connect_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int direct_connect_validate(ServiceValidationArgs* args) { tServiceData *fd; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) { directconnect_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; } fd = directconnect_service_mod.api->data_get(flowp, directconnect_service_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return SERVICE_ENOMEM; if (directconnect_service_mod.api->data_add(flowp, fd, directconnect_service_mod.flow_data_index, &free)) { free(fd); return SERVICE_ENOMEM; } } if (flowp->proto == IPPROTO_TCP) return validateDirectConnectTcp(data, size, args->dir, flowp, args->pkt, fd, args->pConfig); else return validateDirectConnectUdp(data, size, args->dir, flowp, args->pkt, fd, args->pConfig); } static int validateDirectConnectTcp(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, const SFSnortPacket *pkt, tServiceData *serviceData, const struct appIdConfig_ *pConfig) { switch (serviceData->state) { case CONN_STATE_INIT: if (size > 6 && data[size-2] == '|' && data[size-1] == '$') { if (memcmp(data, PATTERN1, sizeof(PATTERN1)-1) == 0) { printf("maybe first directconnect to hub detected\n"); serviceData->state = CONN_STATE_1; goto inprocess; } if (memcmp(data, PATTERN2, sizeof(PATTERN2)-1) == 0) { printf("maybe first dc connect between peers detected\n"); serviceData->state = CONN_STATE_2; goto inprocess; } } if (size >= 11) { if (memcmp(data, PATTERN3, sizeof(PATTERN3)-1) == 0 || memcmp(data, PATTERN4, sizeof(PATTERN4)-1) == 0 || memcmp(data, PATTERN5, sizeof(PATTERN5)-1) == 0 || memcmp(data, PATTERN6, sizeof(PATTERN6)-1) == 0) { goto success; } } break; case CONN_STATE_1: printf ("ValidateDirectConnectTcp(): state 1 size %d\n", size); if (size >= 11) { if (memcmp(data, PATTERN3, sizeof(PATTERN3)-1) == 0 || memcmp(data, PATTERN4, sizeof(PATTERN4)-1) == 0 || memcmp(data, PATTERN5, sizeof(PATTERN5)-1) == 0 || memcmp(data, PATTERN6, sizeof(PATTERN6)-1) == 0) { printf("found directconnect HSUP ADBAS E in second packet\n"); goto success; } } if (size > 6) { if ((data[0] == '$' || data[0] == '<') && data[size-2] == '|' && data[size-1] == '$') { goto success; } else { goto inprocess; } } break; case CONN_STATE_2: if (size > 6) { if (data[0] == '$' && data[size-2] == '|' && data[size-1] == '$') { goto success; } else { goto inprocess; } } break; case CONN_STATE_SERVICE_DETECTED: goto success; } inprocess: serviceData->packetCount++; if (serviceData->packetCount >= MAX_PACKET_INSPECTION_COUNT) goto fail; directconnect_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: if (dir != APP_ID_FROM_RESPONDER) { serviceData->state = CONN_STATE_SERVICE_DETECTED; goto inprocess; } directconnect_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_DIRECT_CONNECT, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: directconnect_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, directconnect_service_mod.flow_data_index, pConfig, NULL); return SERVICE_NOMATCH; } static int validateDirectConnectUdp(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, const SFSnortPacket *pkt, tServiceData *serviceData, const struct appIdConfig_ *pConfig) { if (dir == APP_ID_FROM_RESPONDER && serviceData->state == CONN_STATE_SERVICE_DETECTED) { goto reportSuccess; } if (size > 58) { if (memcmp(data, PATTERN7, sizeof(PATTERN7)-1) == 0 && data[size-3] == ')' && data[size-2] == '|' && data[size-1] == '$') { goto success; } serviceData->state += 1; if (serviceData->state != CONN_STATE_SERVICE_DETECTED) goto inprocess; else goto fail; } inprocess: serviceData->packetCount++; if (serviceData->packetCount >= MAX_PACKET_INSPECTION_COUNT) goto fail; directconnect_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: if (dir != APP_ID_FROM_RESPONDER) { serviceData->state = CONN_STATE_SERVICE_DETECTED; goto inprocess; } reportSuccess: directconnect_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_DIRECT_CONNECT, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: directconnect_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, directconnect_service_mod.flow_data_index, pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_mysql.c0000644000175000017500000000762714241075517025564 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #pragma pack(1) typedef struct _SERVICE_MYSQL_HEADER { union { uint32_t len; struct { uint8_t len[3]; uint8_t packet; } p; } l; uint8_t proto; } ServiceMYSQLHdr; #pragma pack() static int svc_mysql_init(const InitServiceAPI * const init_api); static int svc_mysql_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &svc_mysql_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "mysql", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&svc_mysql_validate, 3306, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule mysql_service_mod = { "MYSQL", &svc_mysql_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_MYSQL, APPINFO_FLAG_SERVICE_ADDITIONAL} }; static int svc_mysql_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&svc_mysql_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int svc_mysql_validate(ServiceValidationArgs* args) { const uint8_t *data = args->data; const ServiceMYSQLHdr *hdr = (const ServiceMYSQLHdr *)data; uint32_t len; const uint8_t *end; const uint8_t *p = NULL; tAppIdData *flowp = args->flowp; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; if (size < sizeof(ServiceMYSQLHdr)) goto fail; len = hdr->l.p.len[0]; len |= hdr->l.p.len[1] << 8; len |= hdr->l.p.len[2] << 16; len += 4; if (len > size) goto fail; if (hdr->l.p.packet) goto fail; if (hdr->proto != 0x0A) goto fail; end = data + len; data += sizeof(ServiceMYSQLHdr); p = data; for (; data= end) goto fail; if (data == p) p = NULL; data += 5; if (data >= end) goto fail; for (; data= end) goto fail; mysql_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_MYSQL, NULL, (char *)p, NULL, NULL); return SERVICE_SUCCESS; inprocess: mysql_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; fail: mysql_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, mysql_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rpc.h0000644000175000017500000000205414241075542025173 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_RPC_H__ #define __SERVICE_RPC_H__ #include "service_api.h" extern tRNAServiceValidationModule rpc_service_mod; #endif /* __SERVICE_RPC_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_base.c0000644000175000017500000027176414241075462025335 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** * @file service_base.c * @author Ron Dempster * */ #include #include #include #include #include #include #include #include "common_util.h" #include "service_base.h" #include "service_state.h" #include "service_api.h" #include "service_bgp.h" #include "service_bootp.h" #include "detector_cip.h" #include "service_dcerpc.h" #include "service_flap.h" #include "service_ftp.h" #include "service_irc.h" #include "service_lpr.h" #include "service_mysql.h" #include "service_netbios.h" #include "service_nntp.h" #include "service_ntp.h" #include "service_radius.h" #include "service_rexec.h" #include "service_rfb.h" #include "service_rlogin.h" #include "service_rpc.h" #include "service_rshell.h" #include "service_rsync.h" #include "service_rtmp.h" #include "service_snmp.h" #include "service_ssh.h" #include "service_ssl.h" #include "service_telnet.h" #include "service_tftp.h" #include "detector_dns.h" #include "detector_sip.h" #include "service_direct_connect.h" #include "service_battle_field.h" #include "service_MDNS.h" #include "detector_pattern.h" #include "luaDetectorModule.h" #include "cpuclock.h" #include "commonAppMatcher.h" #include "fw_appid.h" #include "flow.h" #include "appIdConfig.h" #include "ip_funcs.h" #include "luaDetectorApi.h" /*#define SERVICE_DEBUG 1 */ /*#define SERVICE_DEBUG_PORT 0 */ #define BUFSIZE 512 #define STATE_ID_INCONCLUSIVE_SERVICE_WEIGHT 3 #define STATE_ID_INVALID_CLIENT_THRESHOLD 9 #define STATE_ID_MAX_VALID_COUNT 5 #define STATE_ID_NEEDED_DUPE_DETRACT_COUNT 3 static void *service_flowdata_get(tAppIdData *flow, unsigned service_id); static int service_flowdata_add(tAppIdData *flow, void *data, unsigned service_id, AppIdFreeFCN fcn); static void AppIdAddHostInfo(tAppIdData *flow, SERVICE_HOST_INFO_CODE code, const void *info); static int AppIdAddDHCP(tAppIdData *flowp, unsigned op55_len, const uint8_t *op55, unsigned op60_len, const uint8_t *op60, const uint8_t *mac); static void AppIdAddHostIP(tAppIdData *flow, const uint8_t *mac, uint32_t ip4, int32_t zone, uint32_t subnetmask, uint32_t leaseSecs, uint32_t router); static void AppIdAddSMBData(tAppIdData *flow, unsigned major, unsigned minor, uint32_t flags); static void AppIdServiceAddMisc(tAppIdData* flow, tAppId miscId); const ServiceApi serviceapi = { .data_get = &service_flowdata_get, .data_add = &service_flowdata_add, .flow_new = &AppIdEarlySessionCreate, .data_add_id = &AppIdFlowdataAddId, .data_add_dhcp = &AppIdAddDHCP, .dhcpNewLease = &AppIdAddHostIP, .analyzefp = &AppIdAddSMBData, .add_service = &AppIdServiceAddService, .fail_service = &AppIdServiceFailService, .service_inprocess = &AppIdServiceInProcess, .incompatible_data = &AppIdServiceIncompatibleData, .add_host_info = &AppIdAddHostInfo, .add_payload = &AppIdAddPayload, .add_multipayload = &AppIdAddMultiPayload, .add_user = &AppIdAddUser, .add_service_consume_subtype = &AppIdServiceAddServiceSubtype, .add_misc = &AppIdServiceAddMisc, .add_dns_query_info = &AppIdAddDnsQueryInfo, .add_dns_response_info = &AppIdAddDnsResponseInfo, .reset_dns_info = &AppIdResetDnsInfo, }; #ifdef SERVICE_DEBUG static const char *serviceIdStateName[] = { "NEW", "VALID", "PORT", "PATTERN", "BRUTE_FORCE" }; #endif static tRNAServiceElement *ftp_service = NULL; static tServicePatternData *free_pattern_data; /*C service API */ static void ServiceRegisterPattern(RNAServiceValidationFCN fcn, u_int8_t proto, const u_int8_t *pattern, unsigned size, int position, struct _Detector *userdata, int provides_user, const char *name, tServiceConfig *pServiceConfig); static void CServiceRegisterPattern(RNAServiceValidationFCN fcn, uint8_t proto, const uint8_t *pattern, unsigned size, int position, const char *name, tAppIdConfig *pConfig); static void ServiceRegisterPatternUser(RNAServiceValidationFCN fcn, uint8_t proto, const uint8_t *pattern, unsigned size, int position, const char *name, tAppIdConfig *pConfig); static int CServiceAddPort(RNAServiceValidationPort *pp, tRNAServiceValidationModule *svm, tAppIdConfig *pConfig); static void CServiceRemovePorts(RNAServiceValidationFCN validate, tAppIdConfig *pConfig); static InitServiceAPI svc_init_api = { .RegisterPattern = &CServiceRegisterPattern, .AddPort = &CServiceAddPort, .RemovePorts = CServiceRemovePorts, .RegisterPatternUser = &ServiceRegisterPatternUser, .RegisterAppId = &appSetServiceValidator, .RegisterDetectorCallback = &appSetServiceDetectorCallback, }; static CleanServiceAPI svc_clean_api = { }; extern tRNAServiceValidationModule timbuktu_service_mod; extern tRNAServiceValidationModule bit_service_mod; extern tRNAServiceValidationModule tns_service_mod; extern tRNAServiceValidationModule http_service_mod; static tRNAServiceValidationModule *static_service_list[] = { &bgp_service_mod, &bootp_service_mod, &dcerpc_service_mod, &cip_service_mod, &dns_service_mod, &enip_service_mod, &flap_service_mod, &ftp_service_mod, &irc_service_mod, &lpr_service_mod, &mysql_service_mod, &netbios_service_mod, &nntp_service_mod, &ntp_service_mod, &radius_service_mod, &rexec_service_mod, &rfb_service_mod, &rlogin_service_mod, &rpc_service_mod, &rshell_service_mod, &rsync_service_mod, &rtmp_service_mod, &snmp_service_mod, &ssh_service_mod, &ssl_service_mod, &telnet_service_mod, &tftp_service_mod, &sip_service_mod, &directconnect_service_mod, &battlefield_service_mod, &mdns_service_mod, &timbuktu_service_mod, &bit_service_mod, &tns_service_mod, &pattern_service_mod, &http_service_mod }; typedef struct _SERVICE_MATCH { struct _SERVICE_MATCH *next; unsigned count; unsigned size; tRNAServiceElement *svc; } ServiceMatch; static DHCPInfo *dhcp_info_free_list; static FpSMBData *smb_data_free_list; static unsigned smOrderedListSize = 0; static ServiceMatch **smOrderedList = NULL; static ServiceMatch *free_service_match; static const uint8_t zeromac[6] = {0, 0, 0, 0, 0, 0}; /**free ServiceMatch List. */ void AppIdFreeServiceMatchList(ServiceMatch* sm) { ServiceMatch *tmpSm; if (!sm) return; for (tmpSm = sm; tmpSm->next; tmpSm = tmpSm->next); tmpSm->next = free_service_match; free_service_match = sm; } void cleanupFreeServiceMatch(void) { ServiceMatch *match; while ((match=free_service_match) != NULL) { free_service_match = match->next; free(match); } } int AddFTPServiceState(tAppIdData *fp) { if (!ftp_service) return -1; return AppIdFlowdataAddId(fp, 21, ftp_service); } /**allocate one ServiceMatch element. */ static inline ServiceMatch* allocServiceMatch(void) { ServiceMatch *sm; if ((sm = free_service_match)) { free_service_match = sm->next; memset(sm, 0, sizeof(*sm)); return sm; } return (ServiceMatch *)calloc(1, sizeof(ServiceMatch)); } static int pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { ServiceMatch **matches = (ServiceMatch **)data; tServicePatternData *pd = (tServicePatternData *)id; ServiceMatch *sm; if (pd->position >= 0 && pd->position != index) return 0; for (sm=*matches; sm; sm=sm->next) if (sm->svc == pd->svc) break; if (sm) sm->count++; else { if ((sm=allocServiceMatch()) == NULL) { _dpd.errMsg( "Error allocating a service match"); return 0; } sm->count++; sm->svc = pd->svc; sm->size = pd->size; sm->next = *matches; *matches = sm; } return 0; } tAppId getPortServiceId(uint8_t proto, uint16_t port, const tAppIdConfig *pConfig) { tAppId appId; if (proto == IPPROTO_TCP) appId = pConfig->tcp_port_only[port]; else appId = pConfig->udp_port_only[port]; checkSandboxDetection(appId); return appId; } tAppId getProtocolServiceId(uint8_t proto, const tAppIdConfig *pConfig) { tAppId appId; appId = pConfig->ip_protocol[proto]; checkSandboxDetection(appId); return appId; } static inline uint16_t sslPortRemap( uint16_t port ) { switch (port) { case 465: return 25; case 563: return 119; case 585: case 993: return 143; case 990: return 21; case 992: return 23; case 994: return 6667; case 995: return 110; default: return 0; } } static inline tRNAServiceElement *AppIdGetNextServiceByPort( uint8_t protocol, uint16_t port, const tRNAServiceElement * const lastService, tAppIdData *rnaData, const tAppIdConfig *pConfig ) { tRNAServiceElement *service = NULL; SF_LIST *list = NULL; if (AppIdServiceDetectionLevel(rnaData)) { unsigned remappedPort = sslPortRemap(port); if (remappedPort) list = pConfig->serviceConfig.tcp_services[remappedPort]; } else if (protocol == IPPROTO_TCP) { list = pConfig->serviceConfig.tcp_services[port]; } else { list = pConfig->serviceConfig.udp_services[port]; } if (list) { service = sflist_first(list); if (lastService) { while ( service && ((service->validate != lastService->validate) || (service->userdata != lastService->userdata))) service = sflist_next(list); if (service) service = sflist_next(list); } } #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (port == SERVICE_DEBUG_PORT) #endif fprintf(SF_DEBUG_FILE, "Port service for protocol %u port %u, service %s\n", (unsigned)protocol, (unsigned)port, (service && service->name) ? service->name:"UNKNOWN"); #endif return service; } static inline tRNAServiceElement *AppIdNextServiceByPattern(struct _SERVICE_MATCH **currentService #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT , uint16_t port #endif #endif ) { tRNAServiceElement *service = NULL; while (*currentService) { *currentService = (*currentService)->next; if (*currentService && (*currentService)->svc->current_ref_count) { service = (*currentService)->svc; break; } } #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (port == SERVICE_DEBUG_PORT) #endif fprintf(SF_DEBUG_FILE, "Next pattern service %s\n", (service && service->name) ? service->name:"UNKNOWN"); #endif return service; } tRNAServiceElement *ServiceGetServiceElement(RNAServiceValidationFCN fcn, struct _Detector *userdata, tAppIdConfig *pConfig) { tRNAServiceElement *li; for (li=pConfig->serviceConfig.tcp_service_list; li; li=li->next) { if ((li->validate == fcn) && (li->userdata == userdata)) return li; } for (li=pConfig->serviceConfig.udp_service_list; li; li=li->next) { if ((li->validate == fcn) && (li->userdata == userdata)) return li; } return NULL; } static void ServiceRegisterPattern(RNAServiceValidationFCN fcn, u_int8_t proto, const u_int8_t *pattern, unsigned size, int position, struct _Detector *userdata, int provides_user, const char *name, tServiceConfig *pServiceConfig) { void **patterns; tServicePatternData **pd_list; int *count; tServicePatternData *pd; tRNAServiceElement * *list; tRNAServiceElement *li; if (proto == IPPROTO_TCP) { patterns = &pServiceConfig->tcp_patterns; pd_list = &pServiceConfig->tcp_pattern_data; count = &pServiceConfig->tcp_pattern_count; list = &pServiceConfig->tcp_service_list; } else if (proto == IPPROTO_UDP) { patterns = &pServiceConfig->udp_patterns; pd_list = &pServiceConfig->udp_pattern_data; count = &pServiceConfig->udp_pattern_count; list = &pServiceConfig->udp_service_list; } else { _dpd.errMsg("Invalid protocol when registering a pattern: %u\n",(unsigned)proto); return; } for (li=*list; li; li=li->next) { if ((li->validate == fcn) && (li->userdata == userdata)) break; } if (!li) { if (!(li = calloc(1, sizeof(*li)))) { _dpd.errMsg( "Could not allocate a service list element"); return; } li->next = *list; *list = li; li->validate = fcn; li->userdata = userdata; li->detectorType = UINT_MAX; li->provides_user = provides_user; li->name = name; } if (!(*patterns)) { *patterns = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF); if (!(*patterns)) { _dpd.errMsg("Error initializing the pattern table for protocol %u\n",(unsigned)proto); return; } } if (free_pattern_data) { pd = free_pattern_data; free_pattern_data = pd->next; memset(pd, 0, sizeof(*pd)); } else if ((pd=(tServicePatternData *)calloc(1, sizeof(*pd))) == NULL) { _dpd.errMsg( "Error allocating pattern data"); return; } pd->svc = li; pd->size = size; pd->position = position; _dpd.searchAPI->search_instance_add_ex(*patterns, (void *)pattern, size, pd, STR_SEARCH_CASE_SENSITIVE); (*count)++; pd->next = *pd_list; *pd_list = pd; li->ref_count++; } void ServiceRegisterPatternDetector(RNAServiceValidationFCN fcn, u_int8_t proto, const u_int8_t *pattern, unsigned size, int position, struct _Detector *userdata, const char *name) { ServiceRegisterPattern(fcn, proto, pattern, size, position, userdata, 0, name, &userdata->pAppidNewConfig->serviceConfig); } static void ServiceRegisterPatternUser(RNAServiceValidationFCN fcn, uint8_t proto, const uint8_t *pattern, unsigned size, int position, const char *name, tAppIdConfig *pConfig) { ServiceRegisterPattern(fcn, proto, pattern, size, position, NULL, 1, name, &pConfig->serviceConfig); } static void CServiceRegisterPattern(RNAServiceValidationFCN fcn, uint8_t proto, const uint8_t *pattern, unsigned size, int position, const char *name, tAppIdConfig *pConfig) { ServiceRegisterPattern(fcn, proto, pattern, size, position, NULL, 0, name, &pConfig->serviceConfig); } static void RemoveServicePortsByType(RNAServiceValidationFCN validate, SF_LIST **services, tRNAServiceElement *list, struct _Detector* userdata) { tRNAServiceElement *li, *liTmp; SF_LNODE *node; SF_LNODE *nextNode; unsigned i; SF_LIST *listTmp; for (li=list; li; li=li->next) { if (li->validate == validate && li->userdata == userdata) break; } if (li == NULL) return; for (i=0; inext; li->ref_count--; sflist_remove_node(listTmp, node); node = nextNode; continue; } node = node->next; } } } } /** * \brief Remove all ports registered for all services * * This function takes care of removing ports for all services including C service modules, * Lua detector modules and services associated with C detector modules. * * @param pServiceConfig - Service configuration from which all ports need to be removed * @return void */ static void RemoveAllServicePorts(tServiceConfig *pServiceConfig) { int i; for (i=0; itcp_services[i]) { sflist_free(pServiceConfig->tcp_services[i]); pServiceConfig->tcp_services[i] = NULL; } } for (i=0; iudp_services[i]) { sflist_free(pServiceConfig->udp_services[i]); pServiceConfig->udp_services[i] = NULL; } } for (i=0; iudp_reversed_services[i]) { sflist_free(pServiceConfig->udp_reversed_services[i]); pServiceConfig->udp_reversed_services[i] = NULL; } } } void ServiceRemovePorts(RNAServiceValidationFCN validate, struct _Detector* userdata, tAppIdConfig *pConfig) { RemoveServicePortsByType(validate, pConfig->serviceConfig.tcp_services, pConfig->serviceConfig.tcp_service_list, userdata); RemoveServicePortsByType(validate, pConfig->serviceConfig.udp_services, pConfig->serviceConfig.udp_service_list, userdata); RemoveServicePortsByType(validate, pConfig->serviceConfig.udp_reversed_services, pConfig->serviceConfig.udp_reversed_service_list, userdata); } static void CServiceRemovePorts(RNAServiceValidationFCN validate, tAppIdConfig *pConfig) { ServiceRemovePorts(validate, NULL, pConfig); } int ServiceAddPort(RNAServiceValidationPort *pp, tRNAServiceValidationModule *svm, struct _Detector* userdata, tAppIdConfig *pConfig) { SF_LIST **services; tRNAServiceElement * *list = NULL; tRNAServiceElement *li; tRNAServiceElement *serviceElement; uint8_t isAllocated = 0; _dpd.debugMsg(DEBUG_LOG, "Adding service %s for protocol %u on port %u, %p", svm->name, (unsigned)pp->proto, (unsigned)pp->port, pp->validate); if (pp->proto == IPPROTO_TCP) { services = pConfig->serviceConfig.tcp_services; list = &pConfig->serviceConfig.tcp_service_list; } else if (pp->proto == IPPROTO_UDP) { if (!pp->reversed_validation) { services = pConfig->serviceConfig.udp_services; list = &pConfig->serviceConfig.udp_service_list; } else { services = pConfig->serviceConfig.udp_reversed_services; list = &pConfig->serviceConfig.udp_reversed_service_list; } } else { _dpd.errMsg( "Service %s did not have a valid protocol (%u)", svm->name, (unsigned)pp->proto); return 0; } for (li=*list; li; li=li->next) { if (li->validate == pp->validate && li->userdata == userdata) break; } if (!li) { if (!(li = calloc(1, sizeof(*li)))) { _dpd.errMsg( "Could not allocate a service list element"); return -1; } isAllocated = 1; li->next = *list; *list = li; li->validate = pp->validate; li->provides_user = svm->provides_user; li->userdata = userdata; li->detectorType = UINT_MAX; li->name = svm->name; } if (pp->proto == IPPROTO_TCP && pp->port == 21 && !ftp_service) { ftp_service = li; li->ref_count++; } /*allocate a new list if this is first detector for this port. */ if (!services[pp->port]) { if (!(services[pp->port] = malloc(sizeof(SF_LIST)))) { if (isAllocated) { *list = li->next; free(li); } _dpd.errMsg( "Could not allocate a service list"); return -1; } sflist_init(services[pp->port]); } /*search and add if not present. */ for (serviceElement = sflist_first(services[pp->port]); serviceElement && (serviceElement != li); serviceElement = sflist_next(services[pp->port])); if (!serviceElement) { if (sflist_add_tail(services[pp->port], li)) { _dpd.errMsg( "Could not add %s, service for protocol %u on port %u", svm->name, (unsigned)pp->proto, (unsigned)pp->port); if (isAllocated) { *list = li->next; free(li); } return -1; } } li->ref_count++; return 0; } static int CServiceAddPort(RNAServiceValidationPort *pp, tRNAServiceValidationModule *svm, tAppIdConfig *pConfig) { return ServiceAddPort(pp, svm, NULL, pConfig); } int serviceLoadForConfigCallback(void *symbol, tAppIdConfig *pConfig) { static unsigned service_module_index = 0; tRNAServiceValidationModule *svm = (tRNAServiceValidationModule *)symbol; RNAServiceValidationPort *pp; if (service_module_index >= 65536) { _dpd.errMsg( "Maximum number of service modules exceeded"); return -1; } svm->api = &serviceapi; pp = svm->pp; for (pp=svm->pp; pp && pp->validate; pp++) { if (CServiceAddPort(pp, svm, pConfig)) return -1; } if (svm->init(&svc_init_api)) { _dpd.errMsg("Error initializing service %s\n",svm->name); } svm->next = pConfig->serviceConfig.active_service_list; pConfig->serviceConfig.active_service_list = svm; svm->flow_data_index = service_module_index | APPID_SESSION_DATA_SERVICE_MODSTATE_BIT; service_module_index++; return 0; } int serviceLoadCallback(void *symbol) { return serviceLoadForConfigCallback(symbol, pAppidActiveConfig); } int LoadServiceModules(const char **dir_list, uint32_t instance_id, tAppIdConfig *pConfig) { unsigned i; svc_init_api.instance_id = instance_id; svc_init_api.debug = appidStaticConfig->app_id_debug; svc_init_api.dpd = &_dpd; svc_init_api.pAppidConfig = pConfig; for (i=0; iserviceConfig.active_service_list; svm; svm=svm->next) { // processing only non-lua service detectors. if (svm->init) { pp = svm->pp; for (pp=svm->pp; pp && pp->validate; pp++) { if (CServiceAddPort(pp, svm, pConfig)) return -1; } } } return 0; } void ServiceInit(tAppIdConfig *pConfig) { luaModuleInitAllServices(); } void ServiceFinalize(tAppIdConfig *pConfig) { if (pConfig->serviceConfig.tcp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->serviceConfig.tcp_patterns); } if (pConfig->serviceConfig.udp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->serviceConfig.udp_patterns); } } void UnconfigureServices(tAppIdConfig *pConfig) { tRNAServiceElement *li; tServicePatternData *pd; tRNAServiceValidationModule *svm; svc_clean_api.pAppidConfig = pConfig; if (pConfig->serviceConfig.tcp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->serviceConfig.tcp_patterns); pConfig->serviceConfig.tcp_patterns = NULL; } // Do not free memory for the pattern; this can be later reclaimed when a // new pattern needs to be created. Memory for these patterns will be freed // on exit. while (pConfig->serviceConfig.tcp_pattern_data) { pd = pConfig->serviceConfig.tcp_pattern_data; if ((li = pd->svc) != NULL) li->ref_count--; pConfig->serviceConfig.tcp_pattern_data = pd->next; pd->next = free_pattern_data; free_pattern_data = pd; } if (pConfig->serviceConfig.udp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->serviceConfig.udp_patterns); pConfig->serviceConfig.udp_patterns = NULL; } while (pConfig->serviceConfig.udp_pattern_data) { pd = pConfig->serviceConfig.udp_pattern_data; if ((li = pd->svc) != NULL) li->ref_count--; pConfig->serviceConfig.udp_pattern_data = pd->next; pd->next = free_pattern_data; free_pattern_data = pd; } RemoveAllServicePorts(&pConfig->serviceConfig); for (svm=pConfig->serviceConfig.active_service_list; svm; svm=svm->next) { if (svm->clean) svm->clean(&svc_clean_api); } CleanServicePortPatternList(pConfig); } void ReconfigureServices(tAppIdConfig *pConfig) { tRNAServiceValidationModule *svm; for (svm=pConfig->serviceConfig.active_service_list; svm; svm=svm->next) { /*processing only non-lua service detectors. */ if (svm->init) { if (svm->init(&svc_init_api)) { _dpd.errMsg("Error initializing service %s\n",svm->name); } else { _dpd.debugMsg(DEBUG_LOG,"Initialized service %s\n",svm->name); } } } ServiceInit(pConfig); } void CleanupServices(tAppIdConfig *pConfig) { #ifdef APPID_FULL_CLEANUP tServicePatternData *pattern; tRNAServiceElement *se; ServiceMatch *sm; tRNAServiceValidationModule *svm; FpSMBData *sd; DHCPInfo *info; svc_clean_api.pAppidConfig = pConfig; if (pConfig->serviceConfig.tcp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->serviceConfig.tcp_patterns); pConfig->serviceConfig.tcp_patterns = NULL; } if (pConfig->serviceConfig.udp_patterns) { _dpd.searchAPI->search_instance_free(pConfig->serviceConfig.udp_patterns); pConfig->serviceConfig.udp_patterns = NULL; } while ((pattern=pConfig->serviceConfig.tcp_pattern_data)) { pConfig->serviceConfig.tcp_pattern_data = pattern->next; free(pattern); } while ((pattern=pConfig->serviceConfig.udp_pattern_data)) { pConfig->serviceConfig.udp_pattern_data = pattern->next; free(pattern); } while ((pattern=free_pattern_data)) { free_pattern_data = pattern->next; free(pattern); } while ((se=pConfig->serviceConfig.tcp_service_list)) { pConfig->serviceConfig.tcp_service_list = se->next; free(se); } while ((se=pConfig->serviceConfig.udp_service_list)) { pConfig->serviceConfig.udp_service_list = se->next; free(se); } while ((se=pConfig->serviceConfig.udp_reversed_service_list)) { pConfig->serviceConfig.udp_reversed_service_list = se->next; free(se); } while ((sd = smb_data_free_list)) { smb_data_free_list = sd->next; free(sd); } while ((info = dhcp_info_free_list)) { dhcp_info_free_list = info->next; free(info); } while ((sm = free_service_match)) { free_service_match = sm->next; free(sm); } if (smOrderedList) { free(smOrderedList); smOrderedListSize = 0; } RemoveAllServicePorts(&pConfig->serviceConfig); for (svm=pConfig->serviceConfig.active_service_list; svm; svm=svm->next) { if (svm->clean) svm->clean(&svc_clean_api); } CleanServicePortPatternList(pConfig); #endif } static int AppIdPatternPrecedence(const void *a, const void *b) { const ServiceMatch *sm1 = (ServiceMatch*)a; const ServiceMatch *sm2 = (ServiceMatch*)b; /*higher precedence should be before lower precedence */ if (sm1->count != sm2->count) return (sm2->count - sm1->count); else return (sm2->size - sm1->size); } /**Perform pattern match of a packet and construct a list of services sorted in order of * precedence criteria. Criteria is count and then size. The first service in the list is * returned. The list itself is saved in AppIdServiceIDState. If * appId is already identified, then use it instead of searching again. RNA will capability * to try out other inferior matches. If appId is unknown i.e. searched and not found by FRE then * dont do any pattern match. This is a way degrades RNA detector selection if FRE is running on * this sensor. */ static inline tRNAServiceElement *AppIdGetServiceByPattern(const SFSnortPacket *pkt, uint8_t proto, const int dir, const tServiceConfig *pServiceConfig, struct _SERVICE_MATCH **serviceList, struct _SERVICE_MATCH **currentService) { void *patterns = NULL; ServiceMatch *match_list; ServiceMatch *sm; uint32_t count; uint32_t i; tRNAServiceElement *service = NULL; if (proto == IPPROTO_TCP) patterns = pServiceConfig->tcp_patterns; else patterns = pServiceConfig->udp_patterns; if (!patterns) { #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif fprintf(SF_DEBUG_FILE, "Pattern bailing due to no patterns\n"); #endif return NULL; } if (!smOrderedList) { smOrderedListSize = 32; if (!(smOrderedList = calloc(smOrderedListSize, sizeof(*smOrderedList)))) { _dpd.errMsg( "Pattern bailing due to failed allocation"); return NULL; } } #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) { #endif fprintf(SF_DEBUG_FILE, "Matching\n"); DumpHex(SF_DEBUG_FILE, pkt->payload, pkt->payload_size); #if SERVICE_DEBUG_PORT } #endif #endif /*FRE didn't search */ match_list = NULL; _dpd.searchAPI->search_instance_find_all(patterns, (char *)pkt->payload, pkt->payload_size, 0, &pattern_match, (void*)&match_list); count = 0; for (sm=match_list; sm; sm=sm->next) { if (count >= smOrderedListSize) { ServiceMatch **tmp; smOrderedListSize *= 2; tmp = realloc(smOrderedList, smOrderedListSize * sizeof(*smOrderedList)); if (!tmp) { /*log realloc failure */ _dpd.errMsg("Realloc failure %u\n",smOrderedListSize); smOrderedListSize /= 2; /*free the remaining elements. */ AppIdFreeServiceMatchList(sm); break; } _dpd.errMsg("Realloc %u\n",smOrderedListSize); smOrderedList = tmp; } smOrderedList[count++] = sm; } if (!count) return NULL; qsort(smOrderedList, count, sizeof(*smOrderedList), AppIdPatternPrecedence); /*rearrange the matchlist now */ for (i = 0; i < (count-1); i++) smOrderedList[i]->next = smOrderedList[i+1]; smOrderedList[i]->next = NULL; service = smOrderedList[0]->svc; if (*serviceList) AppIdFreeServiceMatchList(*serviceList); *serviceList = smOrderedList[0]; *currentService = smOrderedList[0]; #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif fprintf(SF_DEBUG_FILE, "Pattern service for protocol %u (%u->%u), %s\n", (unsigned)proto, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, (service && service->name) ? service->name:"UNKNOWN"); #endif return service; } static inline tRNAServiceElement * AppIdGetServiceByBruteForce( uint32_t protocol, const tRNAServiceElement *lastService, const tAppIdConfig *pConfig ) { tRNAServiceElement *service; if (lastService) service = lastService->next; else service = ((protocol == IPPROTO_TCP) ? pConfig->serviceConfig.tcp_service_list:pConfig->serviceConfig.udp_service_list); while (service && !service->current_ref_count) service = service->next; return service; } static void AppIdAddHostInfo(tAppIdData *flow, SERVICE_HOST_INFO_CODE code, const void *info) { if (code == SERVICE_HOST_INFO_NETBIOS_NAME) { if (flow->netbios_name) { if (strcmp(flow->netbios_name,(char *)info) == 0) return; free(flow->netbios_name); } flow->netbios_name = strdup((char *)info); } } void AppIdFreeDhcpData(DhcpFPData *dd) { free(dd); } static int AppIdAddDHCP(tAppIdData *flowp, unsigned op55_len, const uint8_t *op55, unsigned op60_len, const uint8_t *op60, const uint8_t *mac) { if(op55_len && op55_len <= DHCP_OPTION55_LEN_MAX && !getAppIdFlag(flowp, APPID_SESSION_HAS_DHCP_FP)) { DhcpFPData *rdd; rdd = malloc(sizeof(*rdd)); if (!rdd) return -1; if (AppIdFlowdataAdd(flowp, rdd, APPID_SESSION_DATA_DHCP_FP_DATA, (AppIdFreeFCN)AppIdFreeDhcpData)) { AppIdFreeDhcpData(rdd); return -1; } setAppIdFlag(flowp, APPID_SESSION_HAS_DHCP_FP); rdd->op55_len = (op55_len > DHCP_OP55_MAX_SIZE) ? DHCP_OP55_MAX_SIZE:op55_len; memcpy(rdd->op55, op55, rdd->op55_len); rdd->op60_len = (op60_len > DHCP_OP60_MAX_SIZE) ? DHCP_OP60_MAX_SIZE:op60_len; if(op60_len) memcpy(rdd->op60, op60, rdd->op60_len); memcpy(rdd->mac, mac, sizeof(rdd->mac)); } return 0; } void AppIdFreeDhcpInfo(DHCPInfo *dd) { if (dd) { dd->next = dhcp_info_free_list; dhcp_info_free_list = dd; } } static void AppIdAddHostIP(tAppIdData *flow, const uint8_t *mac, uint32_t ip, int32_t zone, uint32_t subnetmask, uint32_t leaseSecs, uint32_t router) { DHCPInfo *info; unsigned flags; if (memcmp(mac, zeromac, 6) == 0 || ip == 0) return; if (!getAppIdFlag(flow, APPID_SESSION_DO_RNA) || getAppIdFlag(flow, APPID_SESSION_HAS_DHCP_INFO)) return; flags = isIPv4HostMonitored(ntohl(ip), zone); if (!(flags & IPFUNCS_HOSTS_IP)) return; if (dhcp_info_free_list) { info = dhcp_info_free_list; dhcp_info_free_list = info->next; } else if (!(info = malloc(sizeof(*info)))) return; if (AppIdFlowdataAdd(flow, info, APPID_SESSION_DATA_DHCP_INFO, (AppIdFreeFCN)AppIdFreeDhcpInfo)) { AppIdFreeDhcpInfo(info); return; } setAppIdFlag(flow, APPID_SESSION_HAS_DHCP_INFO); info->ipAddr = ip; memcpy(info->macAddr, mac, sizeof(info->macAddr)); info->subnetmask = subnetmask; info->leaseSecs = leaseSecs; info->router = router; } void AppIdFreeSMBData(FpSMBData *sd) { if (sd) { sd->next = smb_data_free_list; smb_data_free_list = sd; } } static void AppIdAddSMBData(tAppIdData *flow, unsigned major, unsigned minor, uint32_t flags) { FpSMBData *sd; if (flags & FINGERPRINT_UDP_FLAGS_XENIX) return; if (getAppIdFlag(flow, APPID_SESSION_HAS_SMB_INFO)) return; if (smb_data_free_list) { sd = smb_data_free_list; smb_data_free_list = sd->next; } else sd = malloc(sizeof(*sd)); if (!sd) return; if (AppIdFlowdataAdd(flow, sd, APPID_SESSION_DATA_SMB_DATA, (AppIdFreeFCN)AppIdFreeSMBData)) { AppIdFreeSMBData(sd); return; } setAppIdFlag(flow, APPID_SESSION_HAS_SMB_INFO); sd->major = major; sd->minor = minor; sd->flags = flags & FINGERPRINT_UDP_FLAGS_MASK; } static int AppIdServiceAddServiceEx(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, tAppId appId, const char *vendor, const char *version, AppIdServiceIDState *id_state) { uint16_t port; sfaddr_t *ip; #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = GET_SFOUTER_IPH_PROTOID(pkt, pkt_header); #endif if (!flow || !pkt || !svc_element) { _dpd.errMsg("Invalid arguments to absinthe_add_appId"); return SERVICE_EINVALID; } #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t asId; #endif tAppId tmpServiceAppId = flow->serviceAppId; flow->serviceData = svc_element; if (vendor) { if (flow->serviceVendor) free(flow->serviceVendor); flow->serviceVendor = strdup(vendor); if (!flow->serviceVendor) _dpd.errMsg("failed to allocate service vendor name"); } if (version) { if (flow->serviceVersion) free(flow->serviceVersion); flow->serviceVersion = strdup(version); if (!flow->serviceVersion) _dpd.errMsg("failed to allocate service version"); } setAppIdFlag(flow, APPID_SESSION_SERVICE_DETECTED); flow->serviceAppId = appId; checkSandboxDetection(appId); if (appId > APP_ID_NONE && tmpServiceAppId != appId) CheckDetectorCallback(pkt, flow, (APPID_SESSION_DIRECTION) dir, appId, pAppidActiveConfig); if (getAppIdFlag(flow, APPID_SESSION_IGNORE_HOST)) return SERVICE_SUCCESS; if (!getAppIdFlag(flow, APPID_SESSION_UDP_REVERSED)) { if (dir == APP_ID_FROM_INITIATOR) { ip = GET_DST_IP(pkt); port = pkt->dst_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = pkt->pkt_header->address_space_id_dst; #endif } else { ip = GET_SRC_IP(pkt); port = pkt->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = pkt->pkt_header->address_space_id_src; #endif } if (flow->service_port) port = flow->service_port; } else { if (dir == APP_ID_FROM_INITIATOR) { ip = GET_SRC_IP(pkt); port = pkt->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = pkt->pkt_header->address_space_id_src; #endif } else { ip = GET_DST_IP(pkt); port = pkt->dst_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = pkt->pkt_header->address_space_id_dst; #endif } } if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId, cid); #else id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), cid); #endif #else /* No carrier id */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId); #else id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow)); #endif #endif } #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!id_state && !(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId, cid))) #else if (!id_state && !(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), cid))) #endif #else /* No carrier id */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!id_state && !(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId))) #else if (!id_state && !(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow)))) #endif #endif { _dpd.errMsg("Add service failed to create state"); return SERVICE_ENOMEM; } flow->service_ip = *ip; flow->service_port = port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) flow->serviceAsId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) flow->carrierId = cid; #endif id_state->reset_time = 0; if (id_state->state != SERVICE_ID_VALID) { id_state->state = SERVICE_ID_VALID; id_state->valid_count = 0; id_state->detract_count = 0; IP_CLEAR(id_state->last_detract); id_state->invalid_client_count = 0; IP_CLEAR(id_state->last_invalid_client); } id_state->svc = svc_element; #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { char ipstr[INET6_ADDRSTRLEN]; ipstr[0] = 0; inet_ntop(sfaddr_family(&flow->service_ip), (void *)sfaddr_get_ptr(&flow->service_ip), ipstr, sizeof(ipstr)); #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Valid: %s:%u:%u AS %u CID:%u %p %d\n", ipstr, (unsigned)flow->proto, (unsigned)flow->service_port, asId, (unsigned)cid, id_state, (int)id_state->state); #else fprintf(SF_DEBUG_FILE, "Valid: %s:%u:%u CID:%u %p %d\n", ipstr, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)cid, id_state, (int)id_state->state); #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Valid: %s:%u:%u AS %u %p %d\n", ipstr, (unsigned)flow->proto, (unsigned)flow->service_port, asId, id_state, (int)id_state->state); #else fprintf(SF_DEBUG_FILE, "Valid: %s:%u:%u %p %d\n", ipstr, (unsigned)flow->proto, (unsigned)flow->service_port, id_state, (int)id_state->state); #endif #endif } #endif if (!id_state->valid_count) { id_state->valid_count++; id_state->invalid_client_count = 0; IP_CLEAR(id_state->last_invalid_client); id_state->detract_count = 0; IP_CLEAR(id_state->last_detract); } else if (id_state->valid_count < STATE_ID_MAX_VALID_COUNT) id_state->valid_count++; #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) AS %u CID %u is valid\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, asId, (unsigned) cid); #else fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) CID %u is valid\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, (unsigned) cid); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) AS %u is valid\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, asId); #else fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) is valid\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port); #endif #endif } #endif return SERVICE_SUCCESS; } int AppIdServiceAddServiceSubtype(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, tAppId appId, const char *vendor, const char *version, RNAServiceSubtype *subtype, AppIdServiceIDState *id_state) { flow->subtype = subtype; if (!svc_element->current_ref_count) { #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) AS %u CID %u is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId, (unsigned)flow->carrierId); #else fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) CID %u is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, (unsigned)flow->carrierId); #endif #else /* No carrier id */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) AS %u is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId); #else fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port); #endif #endif } #endif return SERVICE_SUCCESS; } return AppIdServiceAddServiceEx(flow, pkt, dir, svc_element, appId, vendor, version, id_state); } int AppIdServiceAddService(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, tAppId appId, const char *vendor, const char *version, const RNAServiceSubtype *subtype, AppIdServiceIDState *id_state) { RNAServiceSubtype *new_subtype = NULL; RNAServiceSubtype *tmp_subtype; if (!svc_element->current_ref_count) { #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) AS %u CID %u is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId, (unsigned)flow->carrierId); #else fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) CID %u is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, (unsigned)flow->carrierId); #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) AS %u is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId); #else fprintf(SF_DEBUG_FILE, "Service %d for protocol %u on port %u (%u->%u) is valid, but skipped\n", (int)appId, (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port); #endif #endif } #endif return SERVICE_SUCCESS; } for ( ; subtype; subtype = subtype->next) { tmp_subtype = calloc(1, sizeof(*tmp_subtype)); if (tmp_subtype) { if (subtype->service) { tmp_subtype->service = strdup(subtype->service); if (!tmp_subtype->service) _dpd.errMsg("failed to allocate service subtype"); } if (subtype->vendor) { tmp_subtype->vendor = strdup(subtype->vendor); if (!tmp_subtype->vendor) _dpd.errMsg("failed to allocate service subtype vendor"); } if (subtype->version) { tmp_subtype->version = strdup(subtype->version); if (!tmp_subtype->version) _dpd.errMsg("failed to allocate service version"); } tmp_subtype->next = new_subtype; new_subtype = tmp_subtype; } } flow->subtype = new_subtype; return AppIdServiceAddServiceEx(flow, pkt, dir, svc_element, appId, vendor, version, id_state); } int AppIdServiceInProcess(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, AppIdServiceIDState *id_state) { if (!flow || !pkt) { _dpd.errMsg( "Invalid arguments to service_in_process"); return SERVICE_EINVALID; } if (dir == APP_ID_FROM_INITIATOR || getAppIdFlag(flow, APPID_SESSION_IGNORE_HOST|APPID_SESSION_UDP_REVERSED)) return SERVICE_SUCCESS; if (!sfaddr_is_set(&flow->service_ip)) { sfaddr_t *ip; ip = GET_SRC_IP(pkt); flow->service_ip = *ip; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) flow->serviceAsId = pkt->pkt_header->address_space_id_src; #endif if (!flow->service_port) flow->service_port = pkt->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) flow->carrierId = GET_SFOUTER_IPH_PROTOID(pkt, pkt_header); #endif } #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service for protocol %u on port %u is in process (%u->%u) AS %u CID %u, %p %s", (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId, (unsigned)flow->carrierId, svc_element->validate, svc_element->name ? :"UNKNOWN"); #else fprintf(SF_DEBUG_FILE, "Service for protocol %u on port %u is in process (%u->%u) CID %u, %p %s", (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, (unsigned)flow->carrierId, svc_element->validate, svc_element->name ? :"UNKNOWN"); #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "Service for protocol %u on port %u is in process (%u->%u) AS %u, %p %s", (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId, svc_element->validate, svc_element->name ? :"UNKNOWN"); #else fprintf(SF_DEBUG_FILE, "Service for protocol %u on port %u is in process (%u->%u), %p %s", (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, svc_element->validate, svc_element->name ? :"UNKNOWN"); #endif #endif } #endif return SERVICE_SUCCESS; } /**Called when service can not be identified on a flow but the checks failed on client request * rather than server response. When client request fails a check, it may be specific to a client * therefore we should not fail the service right away. If the same behavior is seen from the same * client ultimately we will have to fail the service. If the same behavior is seen from different * clients going to same service then this most likely the service is something else. */ int AppIdServiceIncompatibleData(tAppIdData *flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, unsigned flow_data_index, const tAppIdConfig *pConfig, AppIdServiceIDState *id_state) { if (!flow || !pkt) { _dpd.errMsg("Invalid arguments to service_incompatible_data"); return SERVICE_EINVALID; } if (flow_data_index != APPID_SESSION_DATA_NONE) AppIdFlowdataDelete(flow, flow_data_index); /* If we're still working on a port/pattern list of detectors, then ignore * individual fails until we're done looking at everything. */ if ( (flow->serviceData == NULL) /* we're working on a list of detectors, and... */ && (flow->candidate_service_list != NULL)) { if (sflist_count(flow->candidate_service_list) != 0) /* it's not empty */ { return SERVICE_SUCCESS; } } setAppIdFlag(flow, APPID_SESSION_SERVICE_DETECTED); clearAppIdFlag(flow, APPID_SESSION_CONTINUE); flow->serviceAppId = APP_ID_NONE; if (getAppIdFlag(flow, APPID_SESSION_IGNORE_HOST|APPID_SESSION_UDP_REVERSED) || (svc_element && !svc_element->current_ref_count)) return SERVICE_SUCCESS; if (dir == APP_ID_FROM_INITIATOR) { setAppIdFlag(flow, APPID_SESSION_INCOMPATIBLE); return SERVICE_SUCCESS; } uint16_t port; sfaddr_t *ip; ip = GET_SRC_IP(pkt); port = flow->service_port ? flow->service_port : pkt->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t asId = pkt->pkt_header->address_space_id_src; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = GET_SFOUTER_IPH_PROTOID(pkt, pkt_header); #endif if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId, cid); #else id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), cid); #endif #else /* No carrier id */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId); #else id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow)); #endif #endif } if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId, cid))) #else if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), cid))) #endif #else /* No carrier id */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), asId))) #else if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow)))) #endif #endif { _dpd.errMsg("Incompatible service failed to create state"); return SERVICE_ENOMEM; } id_state->svc = svc_element; } else { id_state->reset_time = 0; } flow->service_ip = *ip; flow->service_port = port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) flow->serviceAsId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) flow->carrierId = cid; #endif #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "service_IC: State %s for protocol %u on port %u (%u->%u) AS %u CID %u, count %u, %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, asId, (unsigned)flow->carrierId, id_state->invalid_client_count, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); #else fprintf(SF_DEBUG_FILE, "service_IC: State %s for protocol %u on port %u (%u->%u) CID %u, count %u, %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, (unsigned) flow->carrierId, id_state->invalid_client_count, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); #endif #else /* No carrier id */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "service_IC: State %s for protocol %u on port %u (%u->%u) AS %u, count %u, %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, asId, id_state->invalid_client_count, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); #else fprintf(SF_DEBUG_FILE, "service_IC: State %s for protocol %u on port %u (%u->%u), count %u, %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, id_state->invalid_client_count, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); #endif #endif } #endif #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { char ipstr[INET6_ADDRSTRLEN]; ipstr[0] = 0; inet_ntop(sfaddr_family(&flow->service_ip), (void *)sfaddr_get_ptr(&flow->service_ip), ipstr, sizeof(ipstr)); fprintf(SF_DEBUG_FILE, "Incompat: %s:%u:%u %p %d %s\n", ipstr, (unsigned)flow->proto, (unsigned)flow->service_port, id_state, (int)id_state->state, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); } #endif return SERVICE_SUCCESS; } int AppIdServiceFailService(tAppIdData* flow, const SFSnortPacket *pkt, int dir, const tRNAServiceElement *svc_element, unsigned flow_data_index, const tAppIdConfig *pConfig, AppIdServiceIDState *id_state) { if (flow_data_index != APPID_SESSION_DATA_NONE) AppIdFlowdataDelete(flow, flow_data_index); /* If we're still working on a port/pattern list of detectors, then ignore * individual fails until we're done looking at everything. */ if ( (flow->serviceData == NULL) /* we're working on a list of detectors, and... */ && (flow->candidate_service_list != NULL)) { if (sflist_count(flow->candidate_service_list) != 0) /* it's not empty */ { return SERVICE_SUCCESS; } } flow->serviceAppId = APP_ID_NONE; setAppIdFlag(flow, APPID_SESSION_SERVICE_DETECTED); clearAppIdFlag(flow, APPID_SESSION_CONTINUE); /* detectors should be careful in marking flow UDP_REVERSED otherwise the same detector * gets all future flows. UDP_REVERSE should be marked only when detector positively * matches opposite direction patterns. */ if (getAppIdFlag(flow, APPID_SESSION_IGNORE_HOST|APPID_SESSION_UDP_REVERSED) || (svc_element && !svc_element->current_ref_count)) return SERVICE_SUCCESS; /* For subsequent packets, avoid marking service failed on client packet, * otherwise the service will show up on client side. */ if (dir == APP_ID_FROM_INITIATOR) { setAppIdFlag(flow, APPID_SESSION_INCOMPATIBLE); return SERVICE_SUCCESS; } uint16_t port; sfaddr_t *ip; ip = GET_SRC_IP(pkt); port = flow->service_port ? flow->service_port : pkt->src_port; flow->service_ip = *ip; flow->service_port = port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) flow->serviceAsId = pkt->pkt_header->address_space_id_src; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) flow->carrierId = GET_SFOUTER_IPH_PROTOID(pkt, pkt_header); #endif if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), flow->serviceAsId, flow->carrierId); #else id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), flow->carrierId); #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), flow->serviceAsId); #else id_state = AppIdGetServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow)); #endif #endif } if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), flow->serviceAsId, flow->carrierId))) #else if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), flow->carrierId))) #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow), flow->serviceAsId))) #else if (!(id_state = AppIdAddServiceIDState(ip, flow->proto, port, AppIdServiceDetectionLevel(flow)))) #endif #endif { _dpd.errMsg("Fail service failed to create state"); return SERVICE_ENOMEM; } id_state->svc = svc_element; } id_state->reset_time = 0; #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "service_fail: State %s for protocol %u on port %u (%u->%u) AS %u CID %u, count %u, valid count %u, currSvc %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId, (unsigned)flow->carrierId, id_state->invalid_client_count, id_state->valid_count, (svc_element && svc_element->name) ? svc_element->name:"UNKNOWN"); #else fprintf(SF_DEBUG_FILE, "service_fail: State %s for protocol %u on port %u (%u->%u) CID %u, count %u, valid count %u, currSvc %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, (unsigned)flow->carrierId, id_state->invalid_client_count, id_state->valid_count, (svc_element && svc_element->name) ? svc_element->name:"UNKNOWN"); #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fprintf(SF_DEBUG_FILE, "service_fail: State %s for protocol %u on port %u (%u->%u) AS %u, count %u, valid count %u, currSvc %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, flow->serviceAsId, id_state->invalid_client_count, id_state->valid_count, (svc_element && svc_element->name) ? svc_element->name:"UNKNOWN"); #else fprintf(SF_DEBUG_FILE, "service_fail: State %s for protocol %u on port %u (%u->%u), count %u, valid count %u, currSvc %s\n", serviceIdStateName[id_state->state], (unsigned)flow->proto, (unsigned)flow->service_port, (unsigned)pkt->src_port, (unsigned)pkt->dst_port, id_state->invalid_client_count, id_state->valid_count, (svc_element && svc_element->name) ? svc_element->name:"UNKNOWN"); #endif #endif } #endif #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (pkt->dst_port == SERVICE_DEBUG_PORT || pkt->src_port == SERVICE_DEBUG_PORT) #endif { char ipstr[INET6_ADDRSTRLEN]; ipstr[0] = 0; inet_ntop(sfaddr_family(&flow->service_ip), (void *)sfaddr_get_ptr(&flow->service_ip), ipstr, sizeof(ipstr)); fprintf(SF_DEBUG_FILE, "Fail: %s:%u:%u %p %d %s\n", ipstr, (unsigned)flow->proto, (unsigned)flow->service_port, id_state, (int)id_state->state, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); } #endif return SERVICE_SUCCESS; } /* Handle some exception cases on failure: * - valid_count: If we have a detector that should be valid, but it keeps * failing, consider restarting the detector search. * - invalid_client_count: If our service detector search had trouble * simply because of unrecognized client data, then consider retrying * the search again. */ static void HandleFailure(tAppIdData *flowp, AppIdServiceIDState *id_state, sfaddr_t *client_ip, SFSnortPacket *p) { if (!id_state) return; /* If we had a valid detector, check for too many fails. If so, start * search sequence again. */ if (id_state->state == SERVICE_ID_VALID) { /* Too many invalid clients? If so, count it as an invalid detect. */ if (id_state->invalid_client_count >= STATE_ID_INVALID_CLIENT_THRESHOLD) { if (id_state->valid_count <= 1) { id_state->state = SERVICE_ID_NEW; id_state->invalid_client_count = 0; IP_CLEAR(id_state->last_invalid_client); id_state->valid_count = 0; id_state->detract_count = 0; IP_CLEAR(id_state->last_detract); id_state->svc = NULL; } else { id_state->valid_count--; id_state->last_invalid_client = *client_ip; id_state->invalid_client_count = 0; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state->asId = flowp->serviceAsId; #endif } } /* Just a plain old fail. If too many of these happen, start * search process over. */ else if (id_state->invalid_client_count == 0) { if (sfip_fast_eq6(&id_state->last_detract, client_ip)) id_state->detract_count++; else { id_state->last_detract = *client_ip; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state->asId = flowp->serviceAsId; #endif } if (id_state->detract_count >= STATE_ID_NEEDED_DUPE_DETRACT_COUNT) { if (id_state->valid_count <= 1) { id_state->state = SERVICE_ID_NEW; id_state->invalid_client_count = 0; IP_CLEAR(id_state->last_invalid_client); id_state->valid_count = 0; id_state->detract_count = 0; IP_CLEAR(id_state->last_detract); id_state->svc = NULL; } else id_state->valid_count--; } } } /* In SERVICE_ID_NEW, if port/pattern fails and not in a mid-stream, go to brute force. */ else if (id_state->state == SERVICE_ID_NEW && flowp->search_state == SERVICE_ID_PENDING && (sflist_count(flowp->candidate_service_list) == 0) && p && !(_dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM)) { id_state->state = SERVICE_ID_BRUTE_FORCE; } } /**Changes in_process service state to failed state when a flow is terminated. * * RNA used to repeat the same service detector if the detector remained in process till the flow terminated. Thus RNA * got stuck on this one detector and never tried another service detector. This function will treat such a detector * as returning incompatibleData when the flow is terminated. The intent here to make RNA try other service detectors but * unlike incompatibleData status, we dont want to undermine confidence in the service. * * @note SFSnortPacket may be NULL when this function is called upon session timeout. */ void FailInProcessService(tAppIdData *flowp, const tAppIdConfig *pConfig) { AppIdServiceIDState *id_state; sfaddr_t *tmp_ip; if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED|APPID_SESSION_UDP_REVERSED)) return; #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(&flowp->service_ip, flowp->proto, flowp->service_port, AppIdServiceDetectionLevel(flowp), flowp->serviceAsId, flowp->carrierId); #else id_state = AppIdGetServiceIDState(&flowp->service_ip, flowp->proto, flowp->service_port, AppIdServiceDetectionLevel(flowp), flowp->carrierId); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(&flowp->service_ip, flowp->proto, flowp->service_port, AppIdServiceDetectionLevel(flowp), flowp->serviceAsId); #else id_state = AppIdGetServiceIDState(&flowp->service_ip, flowp->proto, flowp->service_port, AppIdServiceDetectionLevel(flowp)); #endif #endif #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (flowp->service_port == SERVICE_DEBUG_PORT) #endif fprintf(SF_DEBUG_FILE, "FailInProcess %" PRIx64 ", %08X:%u proto %u\n", flowp->common.flags, sfaddr_get_ip4_value(&flowp->common.initiator_ip), (unsigned)flowp->service_port, (unsigned)flowp->proto); #endif if (!id_state || (id_state->svc && !id_state->svc->current_ref_count)) return; #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (flowp->service_port == SERVICE_DEBUG_PORT) #endif fprintf(SF_DEBUG_FILE, "FailInProcess: State %s for protocol %u on port %u, count %u, %s\n", serviceIdStateName[id_state->state], (unsigned)flowp->proto, (unsigned)flowp->service_port, id_state->invalid_client_count, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); #endif id_state->invalid_client_count += STATE_ID_INCONCLUSIVE_SERVICE_WEIGHT; #ifdef TARGET_BASED tmp_ip = _dpd.sessionAPI->get_session_ip_address(flowp->ssn, SSN_DIR_FROM_SERVER); if (sfip_fast_eq6(tmp_ip, &flowp->service_ip)) tmp_ip = _dpd.sessionAPI->get_session_ip_address(flowp->ssn, SSN_DIR_FROM_CLIENT); #endif HandleFailure(flowp, id_state, tmp_ip, 0); #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT if (flowp->service_port == SERVICE_DEBUG_PORT) #endif fprintf(SF_DEBUG_FILE, "FailInProcess: Changed State to %s for protocol %u on port %u, count %u, %s\n", serviceIdStateName[id_state->state], (unsigned)flowp->proto, (unsigned)flowp->service_port, id_state->invalid_client_count, (id_state->svc && id_state->svc->name) ? id_state->svc->name:"UNKNOWN"); #endif } /* This function should be called to find the next service detector to try when * we have not yet found a valid detector in the host tracker. It will try * both port and/or pattern (but not brute force - that should be done outside * of this function). This includes UDP reversed services. A valid id_state * (even if just initialized to the NEW state) should exist before calling this * function. The state coming out of this function will reflect the state in * which the next detector was found. If nothing is found, it'll indicate that * brute force should be tried next as a state (and return NULL). This * function can be called once or multiple times (to run multiple detectors in * parallel) per flow. Do not call this function if a detector has already * been specified (serviceData). Basically, this function handles going * through the main port/pattern search (and returning which detector to add * next to the list of detectors to try (even if only 1)). */ static const tRNAServiceElement * AppIdGetNextService(const SFSnortPacket *p, const int dir, tAppIdData *rnaData, const tAppIdConfig *pConfig, const tRNAServiceElement *lastService, struct _SERVICE_MATCH **serviceList, struct _SERVICE_MATCH **currentService) { tRNAServiceElement *svc = NULL; uint8_t proto; proto = rnaData->proto; /* See if there are any port detectors to try. If not, move onto patterns. */ if (rnaData->search_state == SERVICE_ID_PORT) { svc = AppIdGetNextServiceByPort(proto, (uint16_t)((dir == APP_ID_FROM_RESPONDER) ? p->src_port : p->dst_port), lastService, rnaData, pConfig); if (svc) return svc; else rnaData->search_state = SERVICE_ID_PATTERN; } if (rnaData->search_state == SERVICE_ID_PATTERN) { /* If we haven't found anything yet, try to see if we get any hits * first with UDP reversed services before moving onto pattern matches. */ if (dir == APP_ID_FROM_INITIATOR) { if (!getAppIdFlag(rnaData, APPID_SESSION_ADDITIONAL_PACKET) && (proto == IPPROTO_UDP) && !rnaData->tried_reverse_service ) { AppIdServiceIDState * reverse_id_state; const tRNAServiceElement * reverse_service = NULL; sfaddr_t * reverse_ip = GET_SRC_IP(p); rnaData->tried_reverse_service = true; #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = GET_SFOUTER_IPH_PROTOID(p, pkt_header); #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if((reverse_id_state = AppIdGetServiceIDState(reverse_ip, proto, p->src_port, AppIdServiceDetectionLevel(rnaData), p->pkt_header->address_space_id_src, cid))) #else if ((reverse_id_state = AppIdGetServiceIDState(reverse_ip, proto, p->src_port, AppIdServiceDetectionLevel(rnaData), cid))) #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if((reverse_id_state = AppIdGetServiceIDState(reverse_ip, proto, p->src_port, AppIdServiceDetectionLevel(rnaData), p->pkt_header->address_space_id_src))) #else if ((reverse_id_state = AppIdGetServiceIDState(reverse_ip, proto, p->src_port, AppIdServiceDetectionLevel(rnaData)))) #endif #endif { reverse_service = reverse_id_state->svc; } if ( reverse_service || (pConfig->serviceConfig.udp_reversed_services[p->src_port] && (reverse_service = sflist_first(pConfig->serviceConfig.udp_reversed_services[p->src_port]))) || (p->payload_size && (reverse_service = AppIdGetServiceByPattern(p, proto, dir, &pConfig->serviceConfig, serviceList, currentService))) ) { return reverse_service; } } return NULL; } /* Try pattern match detectors. */ else /* APP_ID_FROM_RESPONDER */ { if (*serviceList == NULL) // no list yet (need to make one) svc = AppIdGetServiceByPattern(p, proto, dir, &pConfig->serviceConfig, serviceList, currentService); else /* already have a pattern service list (just use it) */ { svc = AppIdNextServiceByPattern(currentService #ifdef SERVICE_DEBUG #if SERVICE_DEBUG_PORT , flow->service_port #endif #endif ); } if (svc) return svc; else rnaData->search_state = SERVICE_ID_PENDING; // We are done pattern matching } } /* If it was in VALID or BRUTE FORCE or no service found */ return NULL; } int AppIdDiscoverService(SFSnortPacket *p, const APPID_SESSION_DIRECTION dir, tAppIdData *rnaData, const tAppIdConfig *pConfig) { int ret = SERVICE_NOMATCH; const tRNAServiceElement *service = NULL; AppIdServiceIDState *id_state = NULL; uint8_t proto = rnaData->proto; SF_LNODE *node; ServiceValidationArgs args; bool bruteForceDone = false; bool appIdFailServiceDone = false; sfaddr_t *ip; uint16_t port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = 0; #endif /* Get packet info. */ if (sfaddr_is_set(&rnaData->service_ip)) { ip = &rnaData->service_ip; port = rnaData->service_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = rnaData->serviceAsId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) cid = rnaData->carrierId; #endif } else { if (dir == APP_ID_FROM_RESPONDER) { ip = GET_SRC_IP(p); port = p->src_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = p->pkt_header->address_space_id_src; #endif } else { ip = GET_DST_IP(p); port = p->dst_port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) asId = p->pkt_header->address_space_id_dst; #endif } rnaData->service_ip = *ip; rnaData->service_port = port; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) rnaData->serviceAsId = asId; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) rnaData->carrierId = GET_SFOUTER_IPH_PROTOID(p, pkt_header); #endif } /* When a new flow was initialized, rnaData->search_state = 0 (SERVICE_ID_START) */ if (rnaData->search_state == SERVICE_ID_START) { rnaData->search_state = SERVICE_ID_PORT; // Also ensures this block to be executed once per flow /* Get host tracker state. */ #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId, cid); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), cid); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData)); #endif #endif /* Create it if it doesn't exist yet. */ if (id_state == NULL) { /* New one is memset to 0, hence id_state->state = 0 (SERVICE_ID_NEW) */ #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!(id_state = AppIdAddServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId, cid))) #else if (!(id_state = AppIdAddServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), cid))) #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) if (!(id_state = AppIdAddServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId))) #else if (!(id_state = AppIdAddServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData)))) #endif #endif { _dpd.errMsg("Discover service failed to create state"); return SERVICE_ENOMEM; } } /* No more searching if brute force already walked the list unsuccessfully. */ else if (id_state->state == SERVICE_ID_BRUTE_FORCE_FAILED) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s brute-force failed state, no service match\n", app_id_debug_session); AppIdServiceFailService(rnaData, p, dir, NULL, APPID_SESSION_DATA_NONE, pConfig, id_state); return SERVICE_NOMATCH; } if (rnaData->serviceData == NULL) { /* If a valid service already exists in host tracker, give it a try. */ if ((id_state->svc != NULL) && (id_state->state == SERVICE_ID_VALID)) { rnaData->serviceData = id_state->svc; } /* If we've gotten to brute force, give next detector a try. */ else if ( id_state->state == SERVICE_ID_BRUTE_FORCE && (sflist_count(rnaData->candidate_service_list) == 0) ) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s brute-force state\n", app_id_debug_session); rnaData->serviceData = AppIdGetServiceByBruteForce(proto, id_state->svc, pConfig); id_state->svc = rnaData->serviceData; bruteForceDone = true; if (!rnaData->serviceData) id_state->state = SERVICE_ID_BRUTE_FORCE_FAILED; } } } args.data = p->payload; args.size = p->payload_size; args.dir = dir; args.flowp = rnaData; args.pkt = p; args.pConfig = pConfig; args.app_id_debug_session_flag = app_id_debug_session_flag; args.app_id_debug_session = app_id_debug_session; /* If we already have a service to try, then try it out. */ if (rnaData->serviceData != NULL) { service = rnaData->serviceData; args.userdata = service->userdata; ret = service->validate(&args); if (ret == SERVICE_NOT_COMPATIBLE) rnaData->got_incompatible_services = 1; rnaData->search_state = SERVICE_ID_PENDING; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s %s returned %d\n", app_id_debug_session, service->name ? service->name:"UNKNOWN", ret); } /* Else, try to find detector(s) to use based on ports and patterns. */ else if (!bruteForceDone) { if (rnaData->candidate_service_list == NULL) { if (!(rnaData->candidate_service_list = malloc(sizeof(SF_LIST)))) { _dpd.errMsg("Could not allocate a candidate service list."); return SERVICE_ENOMEM; } sflist_init(rnaData->candidate_service_list); } /* See if we've got more detector(s) to add to the candidate list. */ if ((rnaData->search_state == SERVICE_ID_PORT) || ((rnaData->search_state == SERVICE_ID_PATTERN) && (dir == APP_ID_FROM_RESPONDER))) { struct _SERVICE_MATCH *serviceList = NULL; struct _SERVICE_MATCH *currentService = NULL; const tRNAServiceElement *tmp = NULL; // Also used to remember last service in the loop while ((tmp = AppIdGetNextService(p, dir, rnaData, pConfig, tmp, &serviceList, ¤tService))) { // Add to list if not already there service = sflist_first(rnaData->candidate_service_list); while (service && (service != tmp)) service = sflist_next(rnaData->candidate_service_list); if (service == NULL) sflist_add_tail(rnaData->candidate_service_list, (void*)tmp); } if (serviceList) AppIdFreeServiceMatchList(serviceList); } /* Run all of the detectors that we currently have. */ ret = SERVICE_INPROCESS; node = sflist_first_node(rnaData->candidate_service_list); service = NULL; while (node != NULL) { int result; SF_LNODE *node_tmp; service = (tRNAServiceElement*)SFLIST_NODE_TO_DATA(node); args.userdata = service->userdata; result = service->validate(&args); if (result == SERVICE_NOT_COMPATIBLE) rnaData->got_incompatible_services = 1; if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s %s returned %d\n", app_id_debug_session, service->name ? service->name:"UNKNOWN", result); node_tmp = node; node = sflist_next_node(rnaData->candidate_service_list); if (result == SERVICE_SUCCESS) { ret = SERVICE_SUCCESS; rnaData->serviceData = service; sflist_free(rnaData->candidate_service_list); rnaData->candidate_service_list = NULL; break; /* done */ } else if (result != SERVICE_INPROCESS) /* fail */ { sflist_remove_node(rnaData->candidate_service_list, node_tmp); } } /* If we tried everything and found nothing, then fail. */ if (ret != SERVICE_SUCCESS) { if ( (sflist_count(rnaData->candidate_service_list) == 0) && (rnaData->search_state == SERVICE_ID_PENDING) ) { if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId, cid); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), cid); #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData)); #endif #endif } AppIdServiceFailService(rnaData, p, dir, NULL, APPID_SESSION_DATA_NONE, pConfig, id_state); appIdFailServiceDone = true; ret = SERVICE_NOMATCH; } } } /* We have seen bidirectional exchange and have not identified any service */ if (!service && (dir == APP_ID_FROM_RESPONDER)) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s no RNA service detector\n", app_id_debug_session); if (!appIdFailServiceDone) { if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId, cid); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), cid); #endif #else /* No carrierid support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData)); #endif #endif } AppIdServiceFailService(rnaData, p, dir, NULL, APPID_SESSION_DATA_NONE, pConfig, id_state); appIdFailServiceDone = true; ret = SERVICE_NOMATCH; } } if ( ((appIdFailServiceDone && !bruteForceDone) || rnaData->got_incompatible_services) && (ret != SERVICE_INPROCESS) && (ret != SERVICE_SUCCESS) ) { /* Handle failure exception cases in states. */ sfaddr_t *tmp_ip; if (dir == APP_ID_FROM_RESPONDER) tmp_ip = GET_DST_IP(p); else tmp_ip = GET_SRC_IP(p); if (rnaData->got_incompatible_services) { if (!id_state) { #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId, cid); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), cid); #endif #else /* No carrier id support */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData), asId); #else id_state = AppIdGetServiceIDState(ip, proto, port, AppIdServiceDetectionLevel(rnaData)); #endif #endif } if (id_state && id_state->invalid_client_count < STATE_ID_INVALID_CLIENT_THRESHOLD) { if (sfip_fast_equals_raw(&id_state->last_invalid_client, tmp_ip)) id_state->invalid_client_count++; else { id_state->invalid_client_count += 3; id_state->last_invalid_client = *tmp_ip; } } } HandleFailure(rnaData, id_state, tmp_ip, p); } return ret; } static void *service_flowdata_get(tAppIdData *flow, unsigned service_id) { return AppIdFlowdataGet(flow, service_id); } static int service_flowdata_add(tAppIdData *flow, void *data, unsigned service_id, AppIdFreeFCN fcn) { return AppIdFlowdataAdd(flow, data, service_id, fcn); } /** GUS: 2006 09 28 10:10:54 * A simple function that prints the * ports that have decoders registered. */ static void dumpServices(FILE *stream, SF_LIST *const *parray) { int i,n = 0; for(i = 0; i < RNA_SERVICE_MAX_PORT; i++) { if (parray[i] && (sflist_count(parray[i]) != 0)) { if( n != 0) { fprintf(stream," "); } n++; fprintf(stream,"%d",i); } } } void dumpPorts(FILE *stream, const tAppIdConfig *pConfig) { fprintf(stream,"(tcp "); dumpServices(stream,pConfig->serviceConfig.tcp_services); fprintf(stream,") \n"); fprintf(stream,"(udp "); dumpServices(stream,pConfig->serviceConfig.udp_services); fprintf(stream,") \n"); } static void AppIdServiceAddMisc(tAppIdData* flow, tAppId miscId) { if(flow != NULL) flow->miscAppId = miscId; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ssl.c0000644000175000017500000010410314241075556025206 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include "flow.h" #include "service_base.h" #include "service_ssl.h" #include "fw_appid.h" #include "serviceConfig.h" #include "thirdparty_appid_utils.h" #define SSL_PORT 443 typedef enum { SSL_CHANGE_CIPHER = 20, SSL_ALERT = 21, SSL_HANDSHAKE = 22, SSL_APPLICATION_DATA = 23 } SSLContentType; #define SSL_CLIENT_HELLO 1 #define SSL_SERVER_HELLO 2 #define SSL_CERTIFICATE 11 #define SSL_SERVER_KEY_XCHG 12 #define SSL_SERVER_CERT_REQ 13 #define SSL_SERVER_HELLO_DONE 14 #define SSL_CERTIFICATE_STATUS 22 #define SSL2_SERVER_HELLO 4 #define PCT_SERVER_HELLO 2 #define FIELD_SEPARATOR "/" #define COMMON_NAME_STR "/CN=" #define ORG_NAME_STR "/O=" /* Extension types. */ #define SSL_EXT_SERVER_NAME 0 typedef struct _MatchedSSLPatterns { SSLCertPattern *mpattern; int index; struct _MatchedSSLPatterns *next; } MatchedSSLPatterns; typedef enum { SSL_STATE_INITIATE, /* Client initiates. */ SSL_STATE_CONNECTION, /* Server responds... */ SSL_STATE_HEADER } SSLState; typedef struct _SERVICE_SSL_DATA { SSLState state; int pos; int length; int tot_length; /* From client: */ char *host_name; int host_name_strlen; /* While collecting certificates: */ int certs_len; /* (Total) length of certificate(s). */ uint8_t *certs_data; /* Certificate(s) data (each proceeded by length (3 bytes)). */ int in_certs; /* Currently collecting certificates? */ int certs_curr_len; /* Current amount of collected certificate data. */ /* Data collected from certificates afterwards: */ char *common_name; int common_name_strlen; int org_name_strlen; char *org_name; } ServiceSSLData; typedef struct _SERVICE_SSL_CERTIFICATE { X509 *cert; uint8_t *common_name_ptr; int common_name_len; uint8_t *org_name_ptr; int org_name_len; struct _SERVICE_SSL_CERTIFICATE *next; } ServiceSSLCertificate; #pragma pack(1) typedef struct _SERVICE_SSL_V3_HEADER /* Actually a TLS Record. */ { uint8_t type; uint16_t version; uint16_t len; } ServiceSSLV3Hdr; typedef struct _SERVICE_SSL_V3_RECORD /* Actually a Handshake. */ { uint8_t type; uint8_t length_msb; uint16_t length; uint16_t version; struct { uint32_t time; uint8_t data[28]; } random; } ServiceSSLV3Record; typedef struct _SERVICE_SSL_V3_CERTS_RECORD /* Actually a Certificate(s) Handshake. */ { uint8_t type; uint8_t length_msb; uint16_t length; uint8_t certs_len[3]; /* 3-byte length, network byte order. */ /* Certificate(s) follow. * For each: * - Length: 3 bytes * - Data : "Length" bytes */ } ServiceSSLV3CertsRecord; typedef struct _SERVICE_SSL_V3_EXTENSION_SERVER_NAME { uint16_t type; uint16_t length; uint16_t list_length; uint8_t string_length_msb; uint16_t string_length; /* String follows. */ } ServiceSSLV3ExtensionServerName; typedef struct _SERVICE_SSL_PCT_HEADER { uint8_t len; uint8_t len2; uint8_t type; uint8_t pad; uint16_t version; uint8_t restart; uint8_t auth; uint32_t cipher; uint16_t hash; uint16_t cert; uint16_t exch; uint8_t id[32]; uint16_t cert_len; uint16_t c_cert_len; uint16_t c_sig_len; uint16_t resp_len; } ServiceSSLPCTHdr; typedef struct _SERVICE_SSL_V2_HEADER { uint8_t len; uint8_t len2; uint8_t type; uint8_t id; uint8_t cert; uint16_t version; uint16_t cert_len; uint16_t cipher_len; uint16_t conn_len; } ServiceSSLV2Hdr; #pragma pack() /* Convert 3-byte lengths in TLS headers to integers. */ #define ntoh3(msb_ptr) ((uint32_t)( (uint32_t)(((uint8_t*)msb_ptr)[0] << 16) \ + (uint32_t)(((uint8_t*)msb_ptr)[1] << 8) \ + (uint32_t)(((uint8_t*)msb_ptr)[2] ) )) static int ssl_cert_pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { MatchedSSLPatterns *cm; MatchedSSLPatterns **matches = (MatchedSSLPatterns **)data; SSLCertPattern *target = (SSLCertPattern *)id; if (!(cm = (MatchedSSLPatterns *)malloc(sizeof(MatchedSSLPatterns)))) return 1; cm->mpattern = target; cm->index = index; cm->next = *matches; *matches = cm; return 0; } static int ssl_detector_create_matcher(void **matcher, DetectorSSLCertPattern *list) { size_t *patternIndex; size_t size = 0; DetectorSSLCertPattern *element = NULL; if (*matcher) _dpd.searchAPI->search_instance_free(*matcher); if (!(*matcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return 0; patternIndex = &size; /* Add patterns from Lua API */ for(element = list; element; element = element->next) { _dpd.searchAPI->search_instance_add_ex(*matcher, (char *)element->dpattern->pattern, element->dpattern->pattern_size, element->dpattern, STR_SEARCH_CASE_INSENSITIVE); (*patternIndex)++; } _dpd.searchAPI->search_instance_prep(*matcher); return 1; } int ssl_detector_process_patterns(tServiceSslConfig *pSslConfig) { int retVal = 1; if (!ssl_detector_create_matcher(&pSslConfig->ssl_host_matcher, pSslConfig->DetectorSSLCertPatternList)) retVal = 0; if (!ssl_detector_create_matcher(&pSslConfig->ssl_cname_matcher, pSslConfig->DetectorSSLCnamePatternList)) retVal = 0; return retVal; } static int ssl_init(const InitServiceAPI * const api); static int ssl_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &ssl_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "ssl", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&ssl_validate, 261, IPPROTO_TCP}, {&ssl_validate, 261, IPPROTO_UDP}, {&ssl_validate, 443, IPPROTO_TCP}, {&ssl_validate, 443, IPPROTO_UDP}, {&ssl_validate, 448, IPPROTO_TCP}, {&ssl_validate, 448, IPPROTO_UDP}, {&ssl_validate, 465, IPPROTO_TCP}, {&ssl_validate, 563, IPPROTO_TCP}, {&ssl_validate, 563, IPPROTO_UDP}, {&ssl_validate, 585, IPPROTO_TCP}, {&ssl_validate, 585, IPPROTO_UDP}, {&ssl_validate, 614, IPPROTO_TCP}, {&ssl_validate, 636, IPPROTO_TCP}, {&ssl_validate, 636, IPPROTO_UDP}, {&ssl_validate, 853, IPPROTO_TCP}, {&ssl_validate, 989, IPPROTO_TCP}, {&ssl_validate, 990, IPPROTO_TCP}, {&ssl_validate, 992, IPPROTO_TCP}, {&ssl_validate, 992, IPPROTO_UDP}, {&ssl_validate, 993, IPPROTO_TCP}, {&ssl_validate, 993, IPPROTO_UDP}, {&ssl_validate, 994, IPPROTO_TCP}, {&ssl_validate, 994, IPPROTO_UDP}, {&ssl_validate, 995, IPPROTO_TCP}, {&ssl_validate, 995, IPPROTO_UDP}, {&ssl_validate, 3269, IPPROTO_TCP}, {&ssl_validate, 8305, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule ssl_service_mod = { "ssl", &ssl_init, pp }; static uint8_t SSL_PATTERN_PCT[] = {0x02, 0x00, 0x80, 0x01}; static uint8_t SSL_PATTERN3_0[] = {0x16, 0x03, 0x00}; static uint8_t SSL_PATTERN3_1[] = {0x16, 0x03, 0x01}; static uint8_t SSL_PATTERN3_2[] = {0x16, 0x03, 0x02}; static uint8_t SSL_PATTERN3_3[] = {0x16, 0x03, 0x03}; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_SSL, APPINFO_FLAG_SERVICE_ADDITIONAL} }; static int ssl_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&ssl_validate, IPPROTO_TCP, SSL_PATTERN_PCT, sizeof(SSL_PATTERN_PCT), 2, "ssl", init_api->pAppidConfig); init_api->RegisterPattern(&ssl_validate, IPPROTO_TCP, SSL_PATTERN3_0, sizeof(SSL_PATTERN3_0), 0, "ssl", init_api->pAppidConfig); init_api->RegisterPattern(&ssl_validate, IPPROTO_TCP, SSL_PATTERN3_1, sizeof(SSL_PATTERN3_1), 0, "ssl", init_api->pAppidConfig); init_api->RegisterPattern(&ssl_validate, IPPROTO_TCP, SSL_PATTERN3_2, sizeof(SSL_PATTERN3_2), 0, "ssl", init_api->pAppidConfig); init_api->RegisterPattern(&ssl_validate, IPPROTO_TCP, SSL_PATTERN3_3, sizeof(SSL_PATTERN3_3), 0, "ssl", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&ssl_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } void ssl_free(void *ss) /* AppIdFreeFCN */ { ServiceSSLData *ss_tmp = (ServiceSSLData*)ss; free(ss_tmp->certs_data); free(ss_tmp->host_name); free(ss_tmp->common_name); free(ss_tmp->org_name); free(ss_tmp); } void parse_client_initiation(const uint8_t *data, uint16_t size, ServiceSSLData *ss) { const ServiceSSLV3Hdr *hdr3; const ServiceSSLV3Record *rec; int length; uint16_t ver; /* Sanity check header stuff. */ if (size < sizeof(ServiceSSLV3Hdr)) return; hdr3 = (ServiceSSLV3Hdr *)data; ver = ntohs(hdr3->version); if (hdr3->type != SSL_HANDSHAKE || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 && ver != 0x0303)) { return; } data += sizeof(ServiceSSLV3Hdr); size -= sizeof(ServiceSSLV3Hdr); if (size < sizeof(ServiceSSLV3Record)) return; rec = (ServiceSSLV3Record *)data; ver = ntohs(rec->version); if (rec->type != SSL_CLIENT_HELLO || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 && ver != 0x0303) || rec->length_msb) { return; } length = ntohs(rec->length) + offsetof(ServiceSSLV3Record, version); if (size < length) return; data += sizeof(ServiceSSLV3Record); size -= sizeof(ServiceSSLV3Record); /* Session ID (1-byte length). */ if (size < 1) return; length = *((uint8_t*)data); data += length + 1; if (size < (length + 1)) return; size -= length + 1; /* Cipher Suites (2-byte length). */ if (size < 2) return; length = ntohs(*((uint16_t*)data)); data += length + 2; if (size < (length + 2)) return; size -= length + 2; /* Compression Methods (1-byte length). */ if (size < 1) return; length = *((uint8_t*)data); data += length + 1; if (size < (length + 1)) return; size -= length + 1; /* Extensions (2-byte length) */ if (size < 2) return; length = ntohs(*((uint16_t*)data)); data += 2; size -= 2; if (size < length) return; // We need at least type (2 bytes) and length (2 bytes) fields in the extension while (length >= 4) { ServiceSSLV3ExtensionServerName *ext = (ServiceSSLV3ExtensionServerName*)data; if (ntohs(ext->type) == SSL_EXT_SERVER_NAME) { /* Found server host name. */ if (length < sizeof(ServiceSSLV3ExtensionServerName)) return; int len = ntohs(ext->string_length); if ((length - sizeof(ServiceSSLV3ExtensionServerName)) < len) return; const uint8_t *str = data + offsetof(ServiceSSLV3ExtensionServerName, string_length) + sizeof(ext->string_length); ss->host_name = malloc(len + 1); /* Plus NULL term. */ if (!ss->host_name) { _dpd.errMsg("parse_client_initiation: " "Could not allocate memory for host name in ServiceSSLData\n"); return; } else { memcpy(ss->host_name, str, len); ss->host_name[len] = '\0'; ss->host_name_strlen = len; } return; } data += ntohs(ext->length) + offsetof(ServiceSSLV3ExtensionServerName, list_length); length -= ntohs(ext->length) + offsetof(ServiceSSLV3ExtensionServerName, list_length); } } int parse_certificates(ServiceSSLData *ss) { int success = 0; if (ss->certs_data && ss->certs_len) { char *common_name = 0; char *org_name = 0; uint8_t *data = ss->certs_data; int len = ss->certs_len; int common_name_tot_len = 0; int org_name_tot_len = 0; success = 1; while (len > 0 && !(common_name && org_name)) { X509 *cert = NULL; char *cert_name = NULL; char *start = NULL; char *end = NULL; int length = 0; /* Get each certificate. */ int cert_len = ntoh3(data); data += 3; len -= 3; if (len < cert_len) { success = 0; break; } cert = d2i_X509(NULL, (const unsigned char **)&data, cert_len); len -= cert_len; /* Above call increments data pointer already. */ if (!cert) { success = 0; break; } /* look for common name or org name if we haven't seen either */ if (!common_name || !org_name) { if ((cert_name = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0))) { if (!common_name) { if ((start = strstr(cert_name, COMMON_NAME_STR))) { start += strlen(COMMON_NAME_STR); end = strstr(start, FIELD_SEPARATOR); if (end) *end = 0; length = strlen(start); if (length>2 && *start=='*' && *(start+1)=='.') { start += 2; // remove leading .* length -= 2; } common_name = strndup(start, length); common_name_tot_len += length; start = NULL; } } if (!org_name) { if ((start = strstr(cert_name, ORG_NAME_STR))) { start += strlen(ORG_NAME_STR); end = strstr(start, FIELD_SEPARATOR); if (end) *end = 0; length = strlen(start); if (length>2 && *start=='*' && *(start+1)=='.') { start += 2; // remove leading .* length -= 2; } org_name = strndup(start, length); org_name_tot_len += length; } } free(cert_name); cert_name = NULL; } } X509_free(cert); } if (common_name) { ss->common_name = common_name; ss->common_name_strlen = common_name_tot_len; } if (org_name) { ss->org_name = org_name; ss->org_name_strlen = org_name_tot_len; } /* No longer need entire certificates. We have what we came for. */ free(ss->certs_data); ss->certs_data = NULL; ss->certs_len = 0; } return success; // could be 1 even though common_name or org_name == 0 } static int ssl_validate(ServiceValidationArgs* args) { ServiceSSLData *ss; const ServiceSSLPCTHdr *pct; const ServiceSSLV2Hdr *hdr2; const ServiceSSLV3Hdr *hdr3; const ServiceSSLV3Record *rec; const ServiceSSLV3CertsRecord *certs_rec; uint16_t ver; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; ss = ssl_service_mod.api->data_get(flowp, ssl_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return SERVICE_ENOMEM; if (ssl_service_mod.api->data_add(flowp, ss, ssl_service_mod.flow_data_index, &ssl_free)) { free(ss); return SERVICE_ENOMEM; } ss->state = SSL_STATE_INITIATE; } /* Start off with a Client Hello from client to server. */ if (ss->state == SSL_STATE_INITIATE) { ss->state = SSL_STATE_CONNECTION; if (!(flowp->scan_flags & SCAN_CERTVIZ_ENABLED_FLAG) && dir == APP_ID_FROM_INITIATOR) { parse_client_initiation(data, size, ss); goto inprocess; } } if (dir != APP_ID_FROM_RESPONDER) { goto inprocess; } switch (ss->state) { case SSL_STATE_CONNECTION: pct = (ServiceSSLPCTHdr *)data; hdr2 = (ServiceSSLV2Hdr *)data; hdr3 = (ServiceSSLV3Hdr *)data; /* SSL PCT header? */ if (size >= sizeof(ServiceSSLPCTHdr) && pct->len >= 0x80 && pct->type == PCT_SERVER_HELLO && ntohs(pct->version) == 0x8001) { goto success; } /* SSL v2 header? */ if (size >= sizeof(ServiceSSLV2Hdr) && hdr2->len >= 0x80 && hdr2->type == SSL2_SERVER_HELLO && !(hdr2->cert & 0xFE)) { uint16_t h2v = ntohs(hdr2->version); if ((h2v == 0x0002 || h2v == 0x0300 || h2v == 0x0301 || h2v == 0x0303) && !(hdr2->cipher_len % 3)) { goto success; } } /* it is probably an SSLv3, TLS 1.2, or TLS 1.3 header. First record must be a handshake (type 22). */ if (size < sizeof(ServiceSSLV3Hdr) || hdr3->type != SSL_HANDSHAKE || (ntohs(hdr3->version) != 0x0300 && ntohs(hdr3->version) != 0x0301 && ntohs(hdr3->version) != 0x0302 && ntohs(hdr3->version) != 0x0303)) { goto fail; } data += sizeof(ServiceSSLV3Hdr); size -= sizeof(ServiceSSLV3Hdr); rec = (ServiceSSLV3Record *)data; if (size < sizeof(ServiceSSLV3Record) || rec->type != SSL_SERVER_HELLO || (ntohs(rec->version) != 0x0300 && ntohs(rec->version) != 0x0301 && ntohs(rec->version) != 0x0302 && ntohs(rec->version) != 0x0303) || rec->length_msb) { goto fail; } ss->tot_length = ntohs(hdr3->len); ss->length = ntohs(rec->length) + offsetof(ServiceSSLV3Record, version); if (ss->tot_length < ss->length) goto fail; ss->tot_length -= ss->length; if (size < ss->length) goto fail; data += ss->length; size -= ss->length; ss->state = SSL_STATE_HEADER; ss->pos = 0; /* fall through */ case SSL_STATE_HEADER: while (size > 0) { if (!ss->pos) { /* Need to move onto (and past) next header (i.e., record) if * previous was completely consumed. */ if (ss->tot_length == 0) { hdr3 = (ServiceSSLV3Hdr *)data; ver = ntohs(hdr3->version); if (size < sizeof(ServiceSSLV3Hdr) || (hdr3->type != SSL_HANDSHAKE && hdr3->type != SSL_CHANGE_CIPHER && hdr3->type != SSL_APPLICATION_DATA) || (ver != 0x0300 && ver != 0x0301 && ver != 0x0302 && ver != 0x0303)) { goto fail; } data += sizeof(ServiceSSLV3Hdr); size -= sizeof(ServiceSSLV3Hdr); ss->tot_length = ntohs(hdr3->len); if (hdr3->type == SSL_CHANGE_CIPHER || hdr3->type == SSL_APPLICATION_DATA) { goto success; } } rec = (ServiceSSLV3Record *)data; if ( rec->type != SSL_SERVER_HELLO_DONE && ( size < offsetof(ServiceSSLV3Record, version) || rec->length_msb) ) { goto fail; } switch (rec->type) { case SSL_CERTIFICATE: /* Start pulling out certificates. */ if (!ss->certs_data) { certs_rec = (ServiceSSLV3CertsRecord *)data; ss->certs_len = ntoh3(certs_rec->certs_len); ss->certs_data = malloc(ss->certs_len); if (!ss->certs_data) return SERVICE_ENOMEM; if ((size - sizeof(ServiceSSLV3CertsRecord)) < ss->certs_len) { /* Will have to get more next time around. */ ss->in_certs = 1; ss->certs_curr_len = size - sizeof(ServiceSSLV3CertsRecord); /* Skip over header to data. */ memcpy(ss->certs_data, data + sizeof(ServiceSSLV3CertsRecord), ss->certs_curr_len); } else { /* Can get it all this time. */ ss->in_certs = 0; ss->certs_curr_len = ss->certs_len; memcpy(ss->certs_data, data + sizeof(ServiceSSLV3CertsRecord), ss->certs_curr_len); break; } } /* fall through */ case SSL_CERTIFICATE_STATUS: case SSL_SERVER_KEY_XCHG: case SSL_SERVER_CERT_REQ: ss->length = ntohs(rec->length) + offsetof(ServiceSSLV3Record, version); if (ss->tot_length < ss->length) goto fail; ss->tot_length -= ss->length; if (size < ss->length) { ss->pos = size; size = 0; } else { data += ss->length; size -= ss->length; ss->pos = 0; } break; case SSL_SERVER_HELLO_DONE: if (size < offsetof(ServiceSSLV3Record, version)) { goto success; } if (rec->length) goto fail; if (ss->tot_length != offsetof(ServiceSSLV3Record, version)) goto fail; goto success; default: goto fail; } } else { /* See if there's more certificate data to grab. */ if (ss->in_certs && ss->certs_data) { if (size < (ss->certs_len - ss->certs_curr_len)) { /* Will have to get more next time around. */ memcpy(ss->certs_data + ss->certs_curr_len, data, size); ss->in_certs = 1; ss->certs_curr_len += size; } else { /* Can get it all this time. */ memcpy(ss->certs_data + ss->certs_curr_len, data, ss->certs_len - ss->certs_curr_len); ss->in_certs = 0; ss->certs_curr_len = ss->certs_len; } } if (size+ss->pos < ss->length) { ss->pos += size; size = 0; } else { data += ss->length - ss->pos; size -= ss->length - ss->pos; ss->pos = 0; } } } break; default: goto fail; } inprocess: ssl_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; fail: free(ss->certs_data); free(ss->host_name); free(ss->common_name); free(ss->org_name); ss->certs_data = NULL; ss->host_name = ss->common_name = ss->org_name = NULL; ssl_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element, ssl_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; success: if (ss->certs_data && ss->certs_len) { if (!(flowp->scan_flags & SCAN_CERTVIZ_ENABLED_FLAG) && !thirdparty_appid_module && !parse_certificates(ss)) { goto fail; } } setAppIdFlag(flowp, APPID_SESSION_SSL_SESSION); if (ss->host_name || ss->common_name || ss->org_name) { if (!flowp->tsession) { if (!(flowp->tsession = calloc(1, sizeof(*flowp->tsession)))) { goto fail; } } /* TLS Host */ if (ss->host_name) { if (flowp->tsession->tls_host) free(flowp->tsession->tls_host); flowp->tsession->tls_host = ss->host_name; flowp->tsession->tls_host_strlen = ss->host_name_strlen; flowp->scan_flags |= SCAN_SSL_HOST_FLAG; } else if (ss->common_name) // use common name (from server) if we didn't see host name (from client) { char *common_name = strndup(ss->common_name, ss->common_name_strlen); if (common_name) { if (flowp->tsession->tls_host) free(flowp->tsession->tls_host); flowp->tsession->tls_host = common_name; flowp->tsession->tls_host_strlen = ss->common_name_strlen; flowp->scan_flags |= SCAN_SSL_HOST_FLAG; } } /* TLS Common Name */ if (ss->common_name) { if (flowp->tsession->tls_cname) free(flowp->tsession->tls_cname); flowp->tsession->tls_cname = ss->common_name; flowp->tsession->tls_cname_strlen = ss->common_name_strlen; flowp->scan_flags |= SCAN_SSL_CERTIFICATE_FLAG; } /* TLS Org Unit */ if (ss->org_name) { if (flowp->tsession->tls_orgUnit) free(flowp->tsession->tls_orgUnit); flowp->tsession->tls_orgUnit = ss->org_name; flowp->tsession->tls_orgUnit_strlen = ss->org_name_strlen; } ss->host_name = ss->common_name = ss->org_name = NULL; flowp->tsession->tls_handshake_done = true; } ssl_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element, getSslServiceAppId(args->pkt->src_port), NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; } tAppId getSslServiceAppId( short srcPort) { switch (srcPort) { case 261: return APP_ID_NSIIOPS; case 443: return APP_ID_HTTPS; case 448: return APP_ID_DDM_SSL; case 465: return APP_ID_SMTPS; case 563: return APP_ID_NNTPS; case 585: /*Currently 585 is de-registered at IANA but old implementation may still use it. */ case 993: return APP_ID_IMAPS; case 614: return APP_ID_SSHELL; case 636: return APP_ID_LDAPS; case 853: return APP_ID_DNS_OVER_TLS; case 989: return APP_ID_FTPSDATA; case 990: return APP_ID_FTPS; case 992: return APP_ID_TELNETS; case 994: return APP_ID_IRCS; case 995: return APP_ID_POP3S; case 3269: return APP_ID_MSFT_GC_SSL; case 8305: return APP_ID_SF_APPLIANCE_MGMT; default: return APP_ID_SSL; } } bool isSslServiceAppId(tAppId appId) { switch (appId) { case APP_ID_NSIIOPS: case APP_ID_HTTPS: case APP_ID_DDM_SSL: case APP_ID_SMTPS: case APP_ID_NNTPS: case APP_ID_IMAPS: case APP_ID_SSHELL: case APP_ID_LDAPS: case APP_ID_FTPSDATA: case APP_ID_FTPS: case APP_ID_TELNETS: case APP_ID_IRCS: case APP_ID_POP3S: case APP_ID_MSFT_GC_SSL: case APP_ID_SF_APPLIANCE_MGMT: case APP_ID_SSL: return true; } return false; } static int ssl_scan_patterns(void * matcher, const u_int8_t *pattern, size_t size, tAppId *clientAppId, tAppId *payloadId) { MatchedSSLPatterns *mp = NULL; MatchedSSLPatterns *tmpMp; SSLCertPattern *best_match; if (!matcher) return 0; _dpd.searchAPI->search_instance_find_all(matcher, (char *)pattern, size, 0, ssl_cert_pattern_match, (void *)&mp); if (!mp) return 0; best_match = NULL; while (mp) { //only patterns that match start of payload, or patterns starting with '.' or patterns folowing '.' in payload //are considered a match. if (mp->index == 0 || *mp->mpattern->pattern == '.' || pattern[mp->index-1] == '.') { if (!best_match || mp->mpattern->pattern_size > best_match->pattern_size) { best_match = mp->mpattern; } } tmpMp = mp; mp = mp->next; free (tmpMp); } if (!best_match) return 0; switch (best_match->type) { /* type 0 means WEB APP */ case 0: *clientAppId = APP_ID_SSL_CLIENT; *payloadId = best_match->appId; break; /* type 1 means CLIENT */ case 1: *clientAppId = best_match->appId; *payloadId = 0; break; default: return 0; } return 1; } int ssl_scan_hostname(const u_int8_t *pattern, size_t size, tAppId *clientAppId, tAppId *payloadId, tServiceSslConfig *pSslConfig) { return ssl_scan_patterns(pSslConfig->ssl_host_matcher, pattern, size, clientAppId, payloadId); } int ssl_scan_cname(const u_int8_t *pattern, size_t size, tAppId *clientAppId, tAppId *payloadId, tServiceSslConfig *pSslConfig) { return ssl_scan_patterns(pSslConfig->ssl_cname_matcher, pattern, size, clientAppId, payloadId); } void service_ssl_clean(tServiceSslConfig *pSslConfig) { if (pSslConfig->ssl_host_matcher) { _dpd.searchAPI->search_instance_free(pSslConfig->ssl_host_matcher); pSslConfig->ssl_host_matcher = NULL; } if (pSslConfig->ssl_cname_matcher) { _dpd.searchAPI->search_instance_free(pSslConfig->ssl_cname_matcher); pSslConfig->ssl_cname_matcher = NULL; } } static int ssl_add_pattern(DetectorSSLCertPattern **list, uint8_t *pattern_str, size_t pattern_size, uint8_t type, tAppId app_id) { DetectorSSLCertPattern *new_ssl_pattern; new_ssl_pattern = calloc(1, sizeof(DetectorSSLCertPattern)); if (!new_ssl_pattern) { return 0; } new_ssl_pattern->dpattern = calloc(1, sizeof(SSLCertPattern)); if (!new_ssl_pattern->dpattern) { free(new_ssl_pattern); return 0; } new_ssl_pattern->dpattern->type = type; new_ssl_pattern->dpattern->appId = app_id; new_ssl_pattern->dpattern->pattern = pattern_str; new_ssl_pattern->dpattern->pattern_size = pattern_size; new_ssl_pattern->next = *list; *list = new_ssl_pattern; return 1; } int ssl_add_cert_pattern(uint8_t *pattern_str, size_t pattern_size, uint8_t type, tAppId app_id, tServiceSslConfig *pSslConfig) { return ssl_add_pattern(&pSslConfig->DetectorSSLCertPatternList, pattern_str, pattern_size, type, app_id); } int ssl_add_cname_pattern(uint8_t *pattern_str, size_t pattern_size, uint8_t type, tAppId app_id, tServiceSslConfig *pSslConfig) { return ssl_add_pattern(&pSslConfig->DetectorSSLCnamePatternList, pattern_str, pattern_size, type, app_id); } static void ssl_patterns_free(DetectorSSLCertPattern **list) { DetectorSSLCertPattern *tmp_pattern; while ((tmp_pattern = *list)) { *list = tmp_pattern->next; if (tmp_pattern->dpattern) { if (tmp_pattern->dpattern->pattern) free(tmp_pattern->dpattern->pattern); free (tmp_pattern->dpattern); } free(tmp_pattern); } } void ssl_detector_free_patterns(tServiceSslConfig *pSslConfig) { ssl_patterns_free(&pSslConfig->DetectorSSLCertPatternList); ssl_patterns_free(&pSslConfig->DetectorSSLCnamePatternList); } int setSSLSquelch(SFSnortPacket *p, int type, tAppId appId) { sfaddr_t *sip, *dip; tAppIdData *f; if (!appInfoEntryFlagGet(appId, APPINFO_FLAG_SSL_SQUELCH, appIdActiveConfigGet())) return 0; dip = GET_DST_IP(p); sip = GET_SRC_IP(p); if (!(f = AppIdEarlySessionCreate(NULL, p, sip, 0, dip, p->dst_port, IPPROTO_TCP, appId, 0))) return 0; switch (type) { case 1: f->payloadAppId = appId; break; case 2: f->clientAppId = appId; f->rnaClientState = RNA_STATE_FINISHED; break; default: return 0; } return 1; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_tftp.h0000644000175000017500000000206014241075563025364 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_TFTP_H__ #define __SERVICE_TFTP_H__ #include "service_api.h" extern tRNAServiceValidationModule tftp_service_mod; #endif /* __SERVICE_TFTP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/dcerpc.h0000644000175000017500000000203314241075457024131 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DCERPC_H__ #define __DCERPC_H__ #include int dcerpc_validate(const uint8_t *data, int size); #endif /* __DCERPC_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_nntp.c0000644000175000017500000002322614241075524025365 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" #define NNTP_PORT 119 #define NNTP_COUNT_THRESHOLD 2 typedef enum { NNTP_STATE_CONNECTION, NNTP_STATE_TRANSFER, NNTP_STATE_DATA, NNTP_STATE_CONNECTION_ERROR } NNTPState; #define NNTP_CR_RECEIVED 0x0001 #define NNTP_MID_LINE 0x0002 #define NNTP_MID_TERM 0x0004 typedef struct _SERVICE_NNTP_DATA { NNTPState state; uint32_t flags; unsigned count; } ServiceNNTPData; #pragma pack(1) typedef struct _SERVICE_NNTP_CODE { uint8_t code[3]; uint8_t sp; } ServiceNNTPCode; #pragma pack() static int nntp_init(const InitServiceAPI * const init_api); static int nntp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &nntp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "nntp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&nntp_validate, NNTP_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule nntp_service_mod = { "NNTP", &nntp_init, pp }; #define NNTP_PATTERN1 "200 " #define NNTP_PATTERN2 "201 " static tAppRegistryEntry appIdRegistry[] = {{APP_ID_NNTP, 0}}; static int nntp_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&nntp_validate, IPPROTO_TCP, (uint8_t *)NNTP_PATTERN1, sizeof(NNTP_PATTERN1)-1, 0, "nntp", init_api->pAppidConfig); init_api->RegisterPattern(&nntp_validate, IPPROTO_TCP, (uint8_t *)NNTP_PATTERN2, sizeof(NNTP_PATTERN2)-1, 0, "nntp", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&nntp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int nntp_validate_reply(const uint8_t *data, uint16_t *offset, uint16_t size) { const ServiceNNTPCode *code_hdr; int code; /* Trim any blank lines (be a little tolerant) */ for (; *offsetsp != ' ') return -1; if (code_hdr->code[0] < '1' || code_hdr->code[0] > '5') return -1; code = (code_hdr->code[0] - '0') * 100; if (code_hdr->code[1] < '0' || (code_hdr->code[1] > '5' && code_hdr->code[1] < '8') || code_hdr->code[1] > '9') { return -1; } code += (code_hdr->code[1] - '0') * 10; if (!isdigit(code_hdr->code[2])) return -1; code += code_hdr->code[2] - '0'; /* We have a valid code, now we need to see if the rest of the line is okay */ *offset += sizeof(ServiceNNTPCode); for (; *offset < size; (*offset)++) { if (data[*offset] == 0x0D) { (*offset)++; if (*offset >= size) return -1; if (data[*offset] != 0x0A) return -1; } if (data[*offset] == 0x0A) { (*offset)++; return code; } else if (!isprint(data[*offset])) return -1; } return 0; } static int nntp_validate_data(const uint8_t *data, uint16_t *offset, uint16_t size, int *flags) { if (*flags & NNTP_CR_RECEIVED) { if (data[*offset] != 0x0A) return -1; if (*flags & NNTP_MID_TERM) { *flags = 0; (*offset)++; return 1; } *flags &= ~NNTP_CR_RECEIVED; (*offset)++; } if (*flags & NNTP_MID_TERM) { if (*offset >= size) return 0; if (data[*offset] == 0x0D) { *flags |= NNTP_CR_RECEIVED; (*offset)++; if (*offset >= size) return 0; if (data[*offset] != 0x0A) return -1; *flags = 0; (*offset)++; return 1; } else if (data[*offset] == 0x0A) { *flags = 0; (*offset)++; return 1; } else if (data[*offset] != '.') return -1; *flags = NNTP_MID_LINE; (*offset)++; } for (; *offset < size; (*offset)++) { if (!(*flags & NNTP_MID_LINE)) { if (data[*offset] == '.') { *flags |= NNTP_MID_TERM; (*offset)++; if (*offset >= size) return 0; if (data[*offset] == 0x0D) { *flags |= NNTP_CR_RECEIVED; (*offset)++; if (*offset >= size) return 0; if (data[*offset] != 0x0A) return -1; *flags = 0; (*offset)++; return 1; } else if (data[*offset] == 0x0A) { *flags = 0; (*offset)++; return 1; } else if (data[*offset] != '.') return -1; (*offset)++; } } *flags = NNTP_MID_LINE; for (; *offset < size; (*offset)++) { if (data[*offset] == 0x0D) { (*offset)++; if (*offset >= size) { *flags |= NNTP_CR_RECEIVED; return 0; } if (data[*offset] != 0x0A) return -1; *flags = 0; break; } if (data[*offset] == 0x0A) { *flags = 0; break; } } } return 0; } static int nntp_validate(ServiceValidationArgs* args) { ServiceNNTPData *nd; uint16_t offset; int code; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; nd = nntp_service_mod.api->data_get(flowp, nntp_service_mod.flow_data_index); if (!nd) { nd = calloc(1, sizeof(*nd)); if (!nd) return SERVICE_ENOMEM; if (nntp_service_mod.api->data_add(flowp, nd, nntp_service_mod.flow_data_index, &free)) { free(nd); return SERVICE_ENOMEM; } nd->state = NNTP_STATE_CONNECTION; } offset = 0; while (offset < size) { if (nd->state == NNTP_STATE_DATA) { if ((code=nntp_validate_data(data, &offset, size, (int *)&nd->flags)) < 0) goto fail; if (!code) goto inprocess; nd->state = NNTP_STATE_TRANSFER; } if ((code=nntp_validate_reply(data, &offset, size)) < 0) goto fail; if (!code) goto inprocess; if (code == 400 || code == 502) { nd->state = NNTP_STATE_CONNECTION_ERROR; } else { switch (nd->state) { case NNTP_STATE_CONNECTION: switch (code) { case 201: case 200: nd->state = NNTP_STATE_TRANSFER; break; default: goto fail; } break; case NNTP_STATE_TRANSFER: nd->count++; if (nd->count >= NNTP_COUNT_THRESHOLD) goto success; switch (code) { case 100: case 215: case 220: case 221: case 222: case 224: case 230: case 231: nd->state = NNTP_STATE_DATA; break; } break; case NNTP_STATE_CONNECTION_ERROR: default: goto fail; } } } inprocess: nntp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: nntp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_NNTP, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: nntp_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, nntp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rtmp.h0000644000175000017500000000206014241075550025365 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_RTMP_H__ #define __SERVICE_RTMP_H__ #include "service_api.h" extern tRNAServiceValidationModule rtmp_service_mod; #endif /* __SERVICE_RTMP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_timbuktu.c0000644000175000017500000001246514241075564026261 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" static const char svc_name[] = "timbuktu"; static char TIMBUKTU_BANNER[] = "\001\001"; #define TIMBUKTU_PORT 407 #define TIMBUKTU_BANNER_LEN (sizeof(TIMBUKTU_BANNER)-1) typedef enum { TIMBUKTU_STATE_BANNER, TIMBUKTU_STATE_MESSAGE_LEN, TIMBUKTU_STATE_MESSAGE_DATA } TIMBUKTUState; typedef struct _SERVICE_TIMBUKTU_DATA { TIMBUKTUState state; unsigned stringlen; unsigned pos; } ServiceTIMBUKTUData; #pragma pack(1) typedef struct _SERVICE_TIMBUKTU_MSG { uint16_t any; uint8_t res; uint8_t len; uint8_t message; } ServiceTIMBUKTUMsg; #pragma pack() static int timbuktu_init(const InitServiceAPI * const init_api); static int timbuktu_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &timbuktu_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "timbuktu", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&timbuktu_validate, TIMBUKTU_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; SF_SO_PUBLIC tRNAServiceValidationModule timbuktu_service_mod = { svc_name, &timbuktu_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_TIMBUKTU, 0}}; static int timbuktu_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&timbuktu_validate, IPPROTO_TCP, (const u_int8_t *) TIMBUKTU_BANNER, sizeof(TIMBUKTU_BANNER)-1, 0, svc_name, init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&timbuktu_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int timbuktu_validate(ServiceValidationArgs* args) { ServiceTIMBUKTUData *ss; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; uint16_t offset=0; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; ss = timbuktu_service_mod.api->data_get(flowp, timbuktu_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return SERVICE_ENOMEM; if (timbuktu_service_mod.api->data_add(flowp, ss, timbuktu_service_mod.flow_data_index, &free)) { free(ss); return SERVICE_ENOMEM; } ss->state = TIMBUKTU_STATE_BANNER; } offset = 0; while(offset < size) { switch (ss->state) { case TIMBUKTU_STATE_BANNER: if(data[offset] != TIMBUKTU_BANNER[ss->pos]) goto fail; if(ss->pos >= TIMBUKTU_BANNER_LEN-1) { ss->pos = 0; ss->state = TIMBUKTU_STATE_MESSAGE_LEN; break; } ss->pos++; break; case TIMBUKTU_STATE_MESSAGE_LEN: ss->pos++; if(ss->pos >= offsetof(ServiceTIMBUKTUMsg , message)) { ss->stringlen = data[offset]; ss->state = TIMBUKTU_STATE_MESSAGE_DATA; if(!ss->stringlen) { if(offset == size-1) goto success; goto fail; } ss->pos = 0; } break; case TIMBUKTU_STATE_MESSAGE_DATA: ss->pos++; if(ss->pos == ss->stringlen) { if(offset == (size-1)) goto success; goto fail; } break; default: goto fail; } offset++; } inprocess: timbuktu_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: timbuktu_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_TIMBUKTU, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: timbuktu_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, timbuktu_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_radius.c0000644000175000017500000002305714241075530025674 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define RADIUS_CODE_ACCESS_REQUEST 1 #define RADIUS_CODE_ACCESS_ACCEPT 2 #define RADIUS_CODE_ACCESS_REJECT 3 #define RADIUS_CODE_ACCOUNTING_REQUEST 4 #define RADIUS_CODE_ACCOUNTING_RESPONSE 5 #define RADIUS_CODE_ACCESS_CHALLENGE 11 typedef enum { RADIUS_STATE_REQUEST, RADIUS_STATE_RESPONSE } RADIUSState; typedef struct _SERVICE_RADIUS_DATA { RADIUSState state; uint8_t id; } ServiceRADIUSData; #pragma pack(1) typedef struct _RADIUS_HEADER { uint8_t code; uint8_t id; uint16_t length; uint8_t auth[16]; } RADIUSHeader; #pragma pack() static int radius_init(const InitServiceAPI * const init_api); static int radius_validate(ServiceValidationArgs* args); static int radius_validate_accounting(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &radius_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "radius", .ref_count = 1, .current_ref_count = 1, }; static tRNAServiceElement acct_svc_element = { .next = NULL, .validate = &radius_validate_accounting, .detectorType = DETECTOR_TYPE_DECODER, .name = "radacct", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&radius_validate, 1812, IPPROTO_UDP}, {&radius_validate, 1812, IPPROTO_UDP, 1}, {&radius_validate_accounting, 1813, IPPROTO_UDP}, {&radius_validate_accounting, 1813, IPPROTO_UDP, 1}, {NULL, 0, 0} }; tRNAServiceValidationModule radius_service_mod = { "RADIUS", &radius_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_RADIUS_ACCT, APPINFO_FLAG_SERVICE_UDP_REVERSED}, {APP_ID_RADIUS, APPINFO_FLAG_SERVICE_UDP_REVERSED} }; static int radius_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&radius_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int radius_validate(ServiceValidationArgs* args) { ServiceRADIUSData *rd; const RADIUSHeader *hdr = (const RADIUSHeader *)args->data; uint16_t len; int new_dir; tAppIdData *flowp = args->flowp; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; if (size < sizeof(RADIUSHeader)) goto fail; rd = radius_service_mod.api->data_get(flowp, radius_service_mod.flow_data_index); if (!rd) { rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (radius_service_mod.api->data_add(flowp, rd, radius_service_mod.flow_data_index, &free)) { free(rd); return SERVICE_ENOMEM; } rd->state = RADIUS_STATE_REQUEST; } new_dir = dir; if (rd->state == RADIUS_STATE_REQUEST) { if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT || hdr->code == RADIUS_CODE_ACCESS_REJECT || hdr->code == RADIUS_CODE_ACCESS_CHALLENGE) { setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); rd->state = RADIUS_STATE_RESPONSE; new_dir = APP_ID_FROM_RESPONDER; } } else if (getAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED)) { new_dir = (dir == APP_ID_FROM_RESPONDER) ? APP_ID_FROM_INITIATOR:APP_ID_FROM_RESPONDER; } switch (rd->state) { case RADIUS_STATE_REQUEST: if (new_dir != APP_ID_FROM_INITIATOR) goto inprocess; if (hdr->code != RADIUS_CODE_ACCESS_REQUEST) { goto not_compatible; } len = ntohs(hdr->length); if (len > size) { goto not_compatible; } /* Must contain a username attribute */ if (len < sizeof(RADIUSHeader)+3) { goto not_compatible; } rd->id = hdr->id; rd->state = RADIUS_STATE_RESPONSE; break; case RADIUS_STATE_RESPONSE: if (new_dir != APP_ID_FROM_RESPONDER) goto inprocess; if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && hdr->code != RADIUS_CODE_ACCESS_REJECT && hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { goto fail; } len = ntohs(hdr->length); if (len > size) goto fail; /* Must contain a username attribute */ if (len < sizeof(RADIUSHeader)) goto fail; if (hdr->id != rd->id) { rd->state = RADIUS_STATE_REQUEST; goto inprocess; } goto success; default: goto fail; } inprocess: radius_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: radius_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element, APP_ID_RADIUS, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; not_compatible: radius_service_mod.api->incompatible_data(flowp, args->pkt, dir, &svc_element, radius_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; fail: radius_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element, radius_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } static int radius_validate_accounting(ServiceValidationArgs* args) { ServiceRADIUSData *rd; const RADIUSHeader *hdr = (const RADIUSHeader *)args->data; uint16_t len; int new_dir; tAppIdData *flowp = args->flowp; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; if (size < sizeof(RADIUSHeader)) goto fail; rd = radius_service_mod.api->data_get(flowp, radius_service_mod.flow_data_index); if (!rd) { rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (radius_service_mod.api->data_add(flowp, rd, radius_service_mod.flow_data_index, &free)) { free(rd); return SERVICE_ENOMEM; } rd->state = RADIUS_STATE_REQUEST; } new_dir = dir; if (rd->state == RADIUS_STATE_REQUEST) { if (hdr->code == RADIUS_CODE_ACCOUNTING_RESPONSE) { setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); rd->state = RADIUS_STATE_RESPONSE; new_dir = APP_ID_FROM_RESPONDER; } } else if (getAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED)) { new_dir = (dir == APP_ID_FROM_RESPONDER) ? APP_ID_FROM_INITIATOR:APP_ID_FROM_RESPONDER; } switch (rd->state) { case RADIUS_STATE_REQUEST: if (new_dir != APP_ID_FROM_INITIATOR) goto inprocess; if (hdr->code != RADIUS_CODE_ACCOUNTING_REQUEST) { goto not_compatible; } len = ntohs(hdr->length); if (len > size) { goto not_compatible; } /* Must contain a username attribute */ if (len < sizeof(RADIUSHeader)+3) { goto not_compatible; } rd->id = hdr->id; rd->state = RADIUS_STATE_RESPONSE; break; case RADIUS_STATE_RESPONSE: if (new_dir != APP_ID_FROM_RESPONDER) goto inprocess; if (hdr->code != RADIUS_CODE_ACCOUNTING_RESPONSE) goto fail; len = ntohs(hdr->length); if (len > size) goto fail; /* Must contain a NAS-IP-Address or NAS-Identifier attribute */ if (len < sizeof(RADIUSHeader)) goto fail; if (hdr->id != rd->id) { rd->state = RADIUS_STATE_REQUEST; goto inprocess; } goto success; default: goto fail; } inprocess: radius_service_mod.api->service_inprocess(flowp, args->pkt, dir, &acct_svc_element, NULL); return SERVICE_INPROCESS; success: radius_service_mod.api->add_service(flowp, args->pkt, dir, &acct_svc_element, APP_ID_RADIUS_ACCT, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; not_compatible: radius_service_mod.api->incompatible_data(flowp, args->pkt, dir, &acct_svc_element, radius_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; fail: radius_service_mod.api->fail_service(flowp, args->pkt, dir, &acct_svc_element, radius_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_bgp.h0000644000175000017500000000205414241075467025165 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_BGP_H__ #define __SERVICE_BGP_H__ #include "service_api.h" extern tRNAServiceValidationModule bgp_service_mod; #endif /* __SERVICE_BGP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_battle_field.h0000644000175000017500000000211414241075465027026 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_BATTLEFIELD_H__ #define __SERVICE_BATTLEFIELD_H__ #include "service_api.h" extern tRNAServiceValidationModule battlefield_service_mod; #endif /* __SERVICE_BATTLEFIELD_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_tns.c0000644000175000017500000002112714241075565025215 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_api.h" static const char svc_name[] = "oracle"; static const uint8_t TNS_BANNER[] = "\000\000"; #define TNS_BANNER_LEN (sizeof(TNS_BANNER)-1) #define TNS_PORT 1521 #define TNS_TYPE_CONNECT 1 #define TNS_TYPE_ACCEPT 2 #define TNS_TYPE_ACK 3 #define TNS_TYPE_REFUSE 4 #define TNS_TYPE_REDIRECT 5 #define TNS_TYPE_DATA 6 #define TNS_TYPE_NULL 7 #define TNS_TYPE_ABORT 9 #define TNS_TYPE_RESEND 11 #define TNS_TYPE_MARKER 12 #define TNS_TYPE_ATTENTION 13 #define TNS_TYPE_CONTROL 14 #define TNS_TYPE_MAX 19 typedef enum { TNS_STATE_MESSAGE_LEN, TNS_STATE_MESSAGE_CHECKSUM, TNS_STATE_MESSAGE, TNS_STATE_MESSAGE_RES, TNS_STATE_MESSAGE_HD_CHECKSUM, TNS_STATE_MESSAGE_ACCEPT, TNS_STATE_MESSAGE_DATA } TNSState; #define ACCEPT_VERSION_OFFSET 8 #define MAX_VERSION_SIZE 12 typedef struct _SERVICE_TNS_DATA { TNSState state; unsigned stringlen; unsigned pos; unsigned message; union { uint16_t len; uint8_t raw_len[2]; }l; const char *version; } ServiceTNSData; #pragma pack(1) typedef struct _SERVICE_TNS_MSG { uint16_t len; uint16_t checksum; uint8_t msg; uint8_t res; uint16_t hdchecksum; uint8_t data; } ServiceTNSMsg; #pragma pack() static int tns_init(const InitServiceAPI * const init_api); static int tns_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &tns_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "tns", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&tns_validate, TNS_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; SF_SO_PUBLIC tRNAServiceValidationModule tns_service_mod = { svc_name, &tns_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_ORACLE_TNS, APPINFO_FLAG_SERVICE_ADDITIONAL}, }; static int tns_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&tns_validate, IPPROTO_TCP, (const uint8_t *) TNS_BANNER, TNS_BANNER_LEN, 2, svc_name, init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&tns_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int tns_validate(ServiceValidationArgs* args) { ServiceTNSData *ss; uint16_t offset; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; ss = tns_service_mod.api->data_get(flowp, tns_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return SERVICE_ENOMEM; if (tns_service_mod.api->data_add(flowp, ss, tns_service_mod.flow_data_index, &free)) { free(ss); return SERVICE_ENOMEM; } ss->state = TNS_STATE_MESSAGE_LEN; } offset = 0; while(offset < size) { switch (ss->state) { case TNS_STATE_MESSAGE_LEN: ss->l.raw_len[ss->pos++] = data[offset]; if(ss->pos >= offsetof(ServiceTNSMsg , checksum)) { ss->stringlen = ntohs(ss->l.len); if(ss->stringlen == 2) { if(offset == (size - 1)) goto success; goto fail; } else if(ss->stringlen < 2) goto fail; else { ss->state = TNS_STATE_MESSAGE_CHECKSUM; } } break; case TNS_STATE_MESSAGE_CHECKSUM: if(data[offset] != 0) goto fail; ss->pos++; if(ss->pos >= offsetof(ServiceTNSMsg , msg)) { ss->state = TNS_STATE_MESSAGE; } break; case TNS_STATE_MESSAGE: ss->message = data[offset]; if(ss->message < TNS_TYPE_CONNECT || ss->message > TNS_TYPE_MAX) goto fail; ss->pos++; ss->state = TNS_STATE_MESSAGE_RES; break; case TNS_STATE_MESSAGE_RES: ss->pos++; ss->state = TNS_STATE_MESSAGE_HD_CHECKSUM; break; case TNS_STATE_MESSAGE_HD_CHECKSUM: ss->pos++; if(ss->pos >= offsetof(ServiceTNSMsg , data)) { switch(ss->message) { case TNS_TYPE_ACCEPT: ss->state = TNS_STATE_MESSAGE_ACCEPT; break; case TNS_TYPE_ACK: case TNS_TYPE_REFUSE: case TNS_TYPE_REDIRECT: case TNS_TYPE_DATA: case TNS_TYPE_NULL: case TNS_TYPE_ABORT: case TNS_TYPE_MARKER: case TNS_TYPE_ATTENTION: case TNS_TYPE_CONTROL: if(ss->pos == ss->stringlen) { if(offset == (size - 1)) goto success; else goto fail; } ss->state = TNS_STATE_MESSAGE_DATA; break; case TNS_TYPE_RESEND: if(ss->pos == ss->stringlen) { if(offset == (size - 1)) { ss->state = TNS_STATE_MESSAGE_LEN; ss->pos = 0; goto inprocess; } else goto fail; } break; case TNS_TYPE_CONNECT: default: goto fail; } } break; case TNS_STATE_MESSAGE_ACCEPT: ss->l.raw_len[ss->pos - ACCEPT_VERSION_OFFSET] = data[offset]; ss->pos++; if(ss->pos >= (ACCEPT_VERSION_OFFSET + 2)) { switch(ntohs(ss->l.len)) { case 0x136: ss->version = "8"; break; case 0x137: ss->version = "9i R1"; break; case 0x138: ss->version = "9i R2"; break; case 0x139: ss->version = "10g R1/R2"; break; case 0x13A: ss->version = "11g R1"; break; default: break; } ss->state = TNS_STATE_MESSAGE_DATA; } break; case TNS_STATE_MESSAGE_DATA: ss->pos++; if(ss->pos == ss->stringlen) { if(offset == (size - 1)) goto success; else goto fail; } break; default: goto fail; } offset++; } inprocess: tns_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: tns_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_ORACLE_TNS, NULL, ss->version ? ss->version:NULL, NULL, NULL); return SERVICE_SUCCESS; fail: tns_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, tns_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ssh.c0000644000175000017500000004177014241075554025212 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_base.h" #define SSH_PORT 22 #define SSH_BANNER "SSH-" #define SERVICE_SSH_MSG_KEYXINIT 20 #define SERVICE_SSH_MSG_IGNORE 2 #define SERVICE_SSH_MSG_PUBLIC_KEY 2 #define SERVICE_SSH_KEY_STRINGS 10 #define SSH_MAX_FIELDS 10 #define SSH_MAX_BANNER_LENGTH 255 #define SSH_VERSION_2 2 #define SSH_VERSION_1 1 #define MINIMUM_SSH_VERS_LEN 4 typedef enum { SSH_STATE_BANNER, SSH_STATE_KEY, SSH_STATE_DONE } SSHState; typedef enum { SSH_HEADER_BEGIN, SSH_HEADER_PLEN, SSH_HEADER_CODE, SSH_IGNORE, SSH_PADDING, SSH_KEYX_HEADER_FINISH, SSH_FIELD_LEN_BEGIN, SSH_FIELD_DATA_BEGIN, SSH_PAYLOAD_BEGIN } SSHHeaderState; typedef enum { OLD_SSH_HEADER_BEGIN, OLD_SSH_HEADER_PLEN, OLD_SSH_HEADER_FIND_CODE, OLD_SSH_HEADER_CODE, OLD_SSH_PUBLIC_KEY } OldSSHHeaderState; typedef struct _SERVICE_SSH_DATA { SSHState state; SSHHeaderState hstate; OldSSHHeaderState oldhstate; unsigned len; unsigned pos; unsigned field; unsigned field_len; unsigned read_data; union { uint32_t len; uint8_t raw_len[4]; } l; char *vendor; char *version; unsigned ssh_version; uint8_t plen; uint8_t code; } ServiceSSHData; #pragma pack(1) typedef struct _SERVICE_SSH_KEY_STRING { uint32_t len; uint8_t data; } ServiceSSHKeyString; typedef struct _SERVICE_SSH_MSG { uint32_t len; uint8_t plen; uint8_t code; } ServiceSSHMsg; typedef struct _SERVICE_SSH_KEY_EXCHANGE { ServiceSSHMsg msg; uint8_t cookie[16]; } ServiceSSHKeyExchange; typedef struct _SERVICE_SSH_KEY_EXCHANGE_V1 { uint32_t len; uint8_t code; } ServiceSSHKeyExchangeV1; typedef struct _SERVICE_SSH_KEY_EXCHANGE_FINAL { uint8_t kex_pkt; uint32_t future; } ServiceSSHKeyExchangeFinal; #pragma pack() static int ssh_init(const InitServiceAPI * const init_api); static int ssh_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &ssh_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "ssh", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&ssh_validate, SSH_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule ssh_service_mod = { "SSH", &ssh_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_SSH, APPINFO_FLAG_SERVICE_ADDITIONAL} }; static int ssh_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&ssh_validate, IPPROTO_TCP, (uint8_t *)SSH_BANNER, sizeof(SSH_BANNER)-1, 0, "ssh", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&ssh_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int ssh_validate_pubkey(const uint8_t *data, uint16_t size, ServiceSSHData *ss) { uint16_t offset = 0; const ServiceSSHMsg *skx; while (offset < size) { switch (ss->oldhstate) { case OLD_SSH_HEADER_BEGIN: ss->l.raw_len[ss->pos] = data[offset]; ss->pos++; if(ss->pos == sizeof(skx->len)) { ss->len = ntohl(ss->l.len) ; ss->oldhstate = OLD_SSH_HEADER_PLEN; } break; case OLD_SSH_HEADER_PLEN: if(size > (ss->len + sizeof(skx->len))) ss->plen = size - (ss->len + sizeof(skx->len)) ; else ss->plen = 0; ss->oldhstate = OLD_SSH_HEADER_FIND_CODE; case OLD_SSH_HEADER_FIND_CODE: if (ss->pos == ss->plen + sizeof(skx->len)) { ss->oldhstate = OLD_SSH_HEADER_CODE; ss->code = data[offset]; } ss->pos++; break; case OLD_SSH_HEADER_CODE: if (ss->code == SERVICE_SSH_MSG_PUBLIC_KEY) { ss->oldhstate = OLD_SSH_PUBLIC_KEY; ss->pos++; } else return SERVICE_NOMATCH; ss->len = ss->len + ss->plen + sizeof(skx->len); if (ss->len > 35000) return SERVICE_NOMATCH; break; case OLD_SSH_PUBLIC_KEY: ss->pos++; if (ss->pos >= ss->len) { offset++; if(offset == size) return SERVICE_SUCCESS; return SERVICE_NOMATCH; } break; } offset++; } return SERVICE_INPROCESS; } static int ssh_validate_keyx(const uint8_t *data, uint16_t size, ServiceSSHData *ss) { uint16_t offset = 0; const ServiceSSHMsg *skx; const ServiceSSHKeyString *sks; const ServiceSSHKeyExchange *skex; while (offset < size) { switch (ss->hstate) { case SSH_HEADER_BEGIN: ss->l.raw_len[ss->pos] = data[offset]; ss->pos++; if(ss->pos == sizeof(skx->len)) { ss->len = ntohl(ss->l.len) ; ss->hstate = SSH_HEADER_PLEN; } break; case SSH_HEADER_PLEN: ss->plen = data[offset]; ss->hstate = SSH_HEADER_CODE; ss->pos++; break; case SSH_HEADER_CODE: ss->code = data[offset]; if (ss->code == SERVICE_SSH_MSG_KEYXINIT) { ss->pos = 0; ss->hstate = SSH_KEYX_HEADER_FINISH; ss->read_data = ss->plen + sizeof(skex->cookie) + sizeof(skx->len); } else if (ss->code == SERVICE_SSH_MSG_IGNORE) { ss->pos = sizeof(skx->len) + 2; ss->hstate = SSH_IGNORE; } else return SERVICE_NOMATCH; ss->len = ntohl(ss->l.len) + sizeof(skx->len); if (ss->len > 35000) return SERVICE_NOMATCH; break; case SSH_IGNORE: ss->pos++; if (ss->pos >= ss->len) { ss->hstate = SSH_HEADER_BEGIN; ss->pos = 0; } break; case SSH_KEYX_HEADER_FINISH: ss->pos++; if (ss->pos >= sizeof(skex->cookie)) { ss->hstate = SSH_FIELD_LEN_BEGIN; ss->pos = 0; } break; case SSH_FIELD_LEN_BEGIN: ss->l.raw_len[ss->pos] = data[offset]; ss->pos++; if (ss->pos >= sizeof(sks->len)) { ss->pos = 0; ss->field_len = ntohl(ss->l.len); ss->read_data += ss->field_len + sizeof(sks->len); if (ss->read_data > ss->len) return SERVICE_NOMATCH; if (ss->field_len) ss->hstate = SSH_FIELD_DATA_BEGIN; else { ss->field++; if (ss->field >= 10) ss->hstate = SSH_PAYLOAD_BEGIN; } } break; case SSH_FIELD_DATA_BEGIN: ss->pos++; if (ss->pos >= ss->field_len) { ss->field++; if (ss->field >= 10) ss->hstate = SSH_PAYLOAD_BEGIN; else ss->hstate = SSH_FIELD_LEN_BEGIN; ss->pos = 0; } break; case SSH_PAYLOAD_BEGIN: if (ss->pos >= offsetof(ServiceSSHKeyExchangeFinal, future)) { ss->l.raw_len[ss->pos - offsetof(ServiceSSHKeyExchangeFinal, future)] = data[offset]; } ss->pos++; if (ss->pos >= sizeof(ServiceSSHKeyExchangeFinal)) { if (ss->l.len != 0) return SERVICE_NOMATCH; ss->hstate = SSH_PADDING; ss->pos = 0; } break; case SSH_PADDING: ss->pos++; if (ss->pos >= ss->plen) { offset++; if (offset == size) return SERVICE_SUCCESS; return SERVICE_NOMATCH; } break; } offset++; } return SERVICE_INPROCESS; } static void ssh_free_state(void *data) { ServiceSSHData *sd = (ServiceSSHData *)data; if (sd) { if (sd->vendor) { free(sd->vendor); sd->vendor = NULL; } if (sd->version) { free(sd->version); sd->version = NULL; } free(sd); } } static int ssh_validate(ServiceValidationArgs* args) { ServiceSSHData *ss; uint16_t offset; int retval; const char *ven; const char *ver; const char *end; unsigned len; int client_major; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; ss = ssh_service_mod.api->data_get(flowp, ssh_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return SERVICE_ENOMEM; if (ssh_service_mod.api->data_add(flowp, ss, ssh_service_mod.flow_data_index, &ssh_free_state)) { free(ss); return SERVICE_ENOMEM; } ss->state = SSH_STATE_BANNER; ss->hstate = SSH_HEADER_BEGIN; ss->oldhstate = OLD_SSH_HEADER_BEGIN; } if (args->dir != APP_ID_FROM_RESPONDER) { if (!ss->ssh_version) { if ((size_t)size > (sizeof(SSH_BANNER)-1+MINIMUM_SSH_VERS_LEN) && !strncmp(SSH_BANNER, (char *)data, sizeof(SSH_BANNER)-1)) { data += (sizeof(SSH_BANNER)-1); if(!isdigit(*data)) goto not_compatible; else client_major = *data; data++; if(*data != '.') goto not_compatible; switch(client_major) { case 0x31: if(*(data+1) == 0x39 && *(data+2) == 0x39) ss->ssh_version = SSH_VERSION_2; else ss->ssh_version = SSH_VERSION_1; break; case 0x32: ss->ssh_version = SSH_VERSION_2; break; default: goto not_compatible; } } } goto inprocess; } switch (ss->state) { case SSH_STATE_BANNER: offset = 0; ss->state = SSH_STATE_KEY; for (;;) { /* SSH-v-\n where v is at least 1 character */ if ((size_t)(size-offset) < ((sizeof(SSH_BANNER)-1)+3)) { goto fail; } if (!strncmp(SSH_BANNER, (char *)data+offset, sizeof(SSH_BANNER)-1)) { unsigned blen = sizeof(SSH_BANNER)-1; offset += sizeof(SSH_BANNER)-1; for (; offset= size || blen > SSH_MAX_BANNER_LENGTH) { goto fail; } ven = (char *) &data[offset]; for (; offset= size) goto fail; if (data[offset+1] != 0x0A) goto fail; } end = (char *)&data[offset]; if (ven == end) goto inprocess; for (ver=ven; ver < end && *ver && *ver != '_' && *ver != '-'; ver++); if (ver < (end - 1) && isdigit(*(ver+1))) { len = ver - ven; ss->vendor = malloc(len+1); if (ss->vendor) { memcpy(ss->vendor, ven, len); ss->vendor[len] = 0; } else _dpd.errMsg("ssh_validate: " "Memory allocation for vendir in ServiceSSHData failed\n"); ver++; len = end - ver; ss->version = malloc(len+1); if (ss->version) { memcpy(ss->version, ver, len); ss->version[len] = 0; } else _dpd.errMsg("ssh_validate: " "Memory allocation for version in ServiceSSHData failed\n"); } else { len = end - ven; ss->version = malloc(len+1); if (ss->version) { memcpy(ss->version, ven, len); ss->version[len] = 0; } else _dpd.errMsg("ssh_validate: " "Memory allocation for version in ServiceSSHData failed\n"); } goto inprocess; } else if (!isprint(data[offset])) goto fail; } goto fail; } else { for (; offsetssh_version) { case SSH_VERSION_2: retval = ssh_validate_keyx(data, size, ss); break; case SSH_VERSION_1: retval = ssh_validate_pubkey(data, size, ss); break; default: goto fail; } goto done; default: break; } goto fail; done: switch (retval) { case SERVICE_INPROCESS: inprocess: ssh_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; case SERVICE_SUCCESS: ssh_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_SSH, ss->vendor, ss->version, NULL, NULL); return SERVICE_SUCCESS; case SERVICE_NOMATCH: fail: ssh_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, ssh_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; not_compatible: ssh_service_mod.api->incompatible_data(flowp, args->pkt, args->dir, &svc_element, ssh_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; default: return retval; } } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_snmp.h0000644000175000017500000000206014241075552025362 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_SNMP_H__ #define __SERVICE_SNMP_H__ #include "service_api.h" extern tRNAServiceValidationModule snmp_service_mod; #endif /* __SERVICE_SNMP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_irc.c0000644000175000017500000002176514241075511025165 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" #define IRC_COUNT_THRESHOLD 10 static const char * const IRC_USER="USER "; static const char * const IRC_NOTICE="NOTICE "; static const char * const IRC_ERROR="ERROR "; static const char * const IRC_PONG="PONG "; static const char * const IRC_PING="PING "; typedef enum { IRC_STATE_BEGIN, IRC_STATE_MID_PREFIX, IRC_STATE_COMMAND_BEGIN, IRC_STATE_MID_COMMAND, IRC_STATE_MID_NUMERIC_COMMAND, IRC_STATE_LINE, IRC_STATE_MID_TERM, IRC_STATE_FOUND_USER, IRC_STATE_USER_USERNAME, IRC_STATE_USER_HOSTNAME, IRC_STATE_USER_SERVERNAME, IRC_STATE_USER_REALNAME_BEGIN, IRC_STATE_USER_REALNAME, IRC_STATE_USER_MID_TERM } IRCState; typedef struct _SERVICE_IRC_DATA { IRCState state; unsigned pos; const char *command; IRCState initiator_state; unsigned initiator_pos; const char *initiator_command; unsigned count; } ServiceIRCData; static int irc_init(const InitServiceAPI * const init_api); static int irc_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &irc_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "irc", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&irc_validate, 6667, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule irc_service_mod = { "IRC", &irc_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_IRCD, 0}}; static int irc_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&irc_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int irc_validate(ServiceValidationArgs* args) { ServiceIRCData *id; const uint8_t *end; IRCState *state; unsigned *pos; const char * *command; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; id = irc_service_mod.api->data_get(flowp, irc_service_mod.flow_data_index); if (!id) { id = calloc(1, sizeof(*id)); if (!id) return SERVICE_ENOMEM; if (irc_service_mod.api->data_add(flowp, id, irc_service_mod.flow_data_index, &free)) { free(id); return SERVICE_ENOMEM; } id->initiator_state = IRC_STATE_BEGIN; id->state = IRC_STATE_BEGIN; } end = (const uint8_t *)(data + size); if (dir == APP_ID_FROM_RESPONDER) { state = &id->state; pos = &id->pos; command = &id->command; } else { state = &id->initiator_state; pos = &id->initiator_pos; command = &id->initiator_command; } for (; datacount++; if (id->count >= IRC_COUNT_THRESHOLD && id->initiator_state == IRC_STATE_FOUND_USER) goto success; } } break; case IRC_STATE_MID_TERM: if (*data != 0x0A) goto fail; *state = IRC_STATE_BEGIN; if (dir == APP_ID_FROM_RESPONDER) { id->count++; if (id->count >= IRC_COUNT_THRESHOLD && id->initiator_state == IRC_STATE_FOUND_USER) goto success; } break; case IRC_STATE_MID_NUMERIC_COMMAND: if (*pos < 3) { if (!isdigit(*data)) goto fail; (*pos)++; } else { if (*data != ' ') goto fail; *state = IRC_STATE_LINE; } break; case IRC_STATE_MID_PREFIX: if (*data == ' ') *state = IRC_STATE_COMMAND_BEGIN; else if (!isprint(*data)) goto fail; break; case IRC_STATE_USER_USERNAME: if (*data == ' ') *state = IRC_STATE_USER_HOSTNAME; else if (*data == 0x0D || *data == 0x0A) goto fail; break; case IRC_STATE_USER_HOSTNAME: if (*data == ' ') *state = IRC_STATE_USER_SERVERNAME; else if (*data == 0x0D || *data == 0x0A) goto fail; break; case IRC_STATE_USER_SERVERNAME: if (*data == ' ') *state = IRC_STATE_USER_REALNAME_BEGIN; else if (*data == 0x0D || *data == 0x0A) goto fail; break; case IRC_STATE_USER_REALNAME_BEGIN: if (*data == ':') *state = IRC_STATE_USER_REALNAME; else goto fail; break; case IRC_STATE_USER_REALNAME: if (*data == 0x0D) *state = IRC_STATE_USER_MID_TERM; else if (*data == 0x0A) *state = IRC_STATE_FOUND_USER; break; case IRC_STATE_USER_MID_TERM: if (*data != 0x0A) goto fail; *state = IRC_STATE_FOUND_USER; break; case IRC_STATE_FOUND_USER: goto inprocess; default: goto fail; } } inprocess: irc_service_mod.api->service_inprocess(flowp, args->pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: irc_service_mod.api->add_service(flowp, args->pkt, dir, &svc_element, APP_ID_IRCD, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: if (dir == APP_ID_FROM_RESPONDER) { irc_service_mod.api->fail_service(flowp, args->pkt, dir, &svc_element, irc_service_mod.flow_data_index, args->pConfig, NULL); } else { irc_service_mod.api->incompatible_data(flowp, args->pkt, dir, &svc_element, irc_service_mod.flow_data_index, args->pConfig, NULL); } return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rshell.c0000644000175000017500000002610614241075543025700 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #define RSHELL_PORT 514 #define RSHELL_MAX_PORT_PACKET 6 typedef enum { RSHELL_STATE_PORT, RSHELL_STATE_SERVER_CONNECT, RSHELL_STATE_USERNAME, RSHELL_STATE_USERNAME2, RSHELL_STATE_COMMAND, RSHELL_STATE_REPLY, RSHELL_STATE_DONE, RSHELL_STATE_BAIL, RSHELL_STATE_STDERR_CONNECT_SYN, RSHELL_STATE_STDERR_CONNECT_SYN_ACK, RSHELL_STATE_STDERR_WAIT, RSHELL_STATE_STDERR_DONE } RSHELLState; typedef struct _SERVICE_RSHELL_DATA { RSHELLState state; struct _SERVICE_RSHELL_DATA *parent; struct _SERVICE_RSHELL_DATA *child; } ServiceRSHELLData; static int rshell_init(const InitServiceAPI * const init_api); static int rshell_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &rshell_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "rshell", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&rshell_validate, RSHELL_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule rshell_service_mod = { "RSHELL", &rshell_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_SHELL, APPINFO_FLAG_SERVICE_ADDITIONAL}}; static int16_t app_id = 0; static int rshell_init(const InitServiceAPI * const init_api) { unsigned i; #ifdef TARGET_BASED app_id = init_api->dpd->addProtocolReference("rsh-error"); #endif for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&rshell_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static void rshell_free_state(void *data) { ServiceRSHELLData *rd = (ServiceRSHELLData *)data; if (rd) { if (rd->parent) { rd->parent->child = NULL; rd->parent->parent = NULL; } if (rd->child) { rd->child->parent = NULL; rd->child->child = NULL; } free(rd); } } /* Both the control & data sessions need to go to success else we bail. Let the control/data session know that we're bailing */ static void rshell_bail(ServiceRSHELLData *rd, tAppIdData *flowp) { clearAppIdFlag(flowp, APPID_SESSION_REXEC_STDERR); if (!rd) return; rd->state = RSHELL_STATE_BAIL; if (rd->child) rd->child->state = RSHELL_STATE_BAIL; if (rd->parent) rd->parent->state = RSHELL_STATE_BAIL; } static int rshell_validate(ServiceValidationArgs* args) { ServiceRSHELLData *rd = NULL; ServiceRSHELLData *tmp_rd; int i; uint32_t port; tAppIdData *pf; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; bool app_id_debug_session_flag = args->app_id_debug_session_flag; char* app_id_debug_session = args->app_id_debug_session; rd = rshell_service_mod.api->data_get(flowp, rshell_service_mod.flow_data_index); if (!rd) { if (!size) goto inprocess; rd = calloc(1, sizeof(*rd)); if (!rd) return SERVICE_ENOMEM; if (rshell_service_mod.api->data_add(flowp, rd, rshell_service_mod.flow_data_index, &rshell_free_state)) { free(rd); return SERVICE_ENOMEM; } rd->state = RSHELL_STATE_PORT; } if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s rshell state %d\n", app_id_debug_session, rd->state); switch (rd->state) { case RSHELL_STATE_PORT: if (dir != APP_ID_FROM_INITIATOR) goto fail; if (size > RSHELL_MAX_PORT_PACKET) goto bail; if (data[size-1]) goto bail; port = 0; for (i=0; i 65535) goto bail; if (port) { sfaddr_t *sip; sfaddr_t *dip; dip = GET_DST_IP(pkt); sip = GET_SRC_IP(pkt); pf = rshell_service_mod.api->flow_new(flowp, pkt, dip, 0, sip, (uint16_t)port, IPPROTO_TCP, app_id, APPID_EARLY_SESSION_FLAG_FW_RULE); if (pf) { tmp_rd = calloc(1, sizeof(ServiceRSHELLData)); if (tmp_rd == NULL) return SERVICE_ENOMEM; tmp_rd->state = RSHELL_STATE_STDERR_CONNECT_SYN; tmp_rd->parent = rd; if (rshell_service_mod.api->data_add(pf, tmp_rd, rshell_service_mod.flow_data_index, &rshell_free_state)) { pf->rnaServiceState = RNA_STATE_FINISHED; free(tmp_rd); return SERVICE_ENOMEM; } if (rshell_service_mod.api->data_add_id(pf, (uint16_t)port, &svc_element)) { pf->rnaServiceState = RNA_STATE_FINISHED; tmp_rd->state = RSHELL_STATE_DONE; tmp_rd->parent = NULL; return SERVICE_ENOMEM; } pf->rnaClientState = RNA_STATE_FINISHED; pf->scan_flags |= SCAN_HOST_PORT_FLAG; PopulateExpectedFlow(flowp, pf, APPID_SESSION_CONTINUE | APPID_SESSION_REXEC_STDERR | APPID_SESSION_NO_TPI | APPID_SESSION_NOT_A_SERVICE | APPID_SESSION_PORT_SERVICE_DONE, APP_ID_FROM_RESPONDER); pf->rnaServiceState = RNA_STATE_STATEFUL; rd->child = tmp_rd; rd->state = RSHELL_STATE_SERVER_CONNECT; setAppIdFlag(flowp, APPID_SESSION_CONTINUE); goto success; } else rd->state = RSHELL_STATE_USERNAME; } else rd->state = RSHELL_STATE_USERNAME; break; case RSHELL_STATE_SERVER_CONNECT: if (!size) break; /* The only valid way out of this state is for the child flow to change it. */ goto fail; case RSHELL_STATE_USERNAME: if (!size) break; if (dir != APP_ID_FROM_INITIATOR) goto fail; for (i=0; istate = RSHELL_STATE_USERNAME2; if (i >= size) goto bail; i++; data += i; size -= i; /* Fall through */ case RSHELL_STATE_USERNAME2: if (!size) break; if (dir != APP_ID_FROM_INITIATOR) goto fail; for (i=0; istate = RSHELL_STATE_COMMAND; if (i >= size) goto bail; i++; data += i; size -= i; /* Fall through */ case RSHELL_STATE_COMMAND: if (!size) break; if (dir != APP_ID_FROM_INITIATOR) goto fail; for (i=0; istate = RSHELL_STATE_COMMAND; if (i >= size) goto bail; i++; data += i; size -= i; if (!size) { rd->state = RSHELL_STATE_REPLY; break; } if (data[size-1]) goto bail; /* stdin */ for (i=0; istate = RSHELL_STATE_REPLY; break; case RSHELL_STATE_REPLY: if (!size) goto inprocess; if (dir != APP_ID_FROM_RESPONDER) goto fail; if (size == 1 || *data == 0x01) { if (size != 1) { data++; size--; for (i=0; ichild) { if (rd->child->state == RSHELL_STATE_STDERR_WAIT) rd->child->state = RSHELL_STATE_STDERR_DONE; else goto fail; } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); goto success; } goto fail; case RSHELL_STATE_STDERR_CONNECT_SYN: rd->state = RSHELL_STATE_STDERR_CONNECT_SYN_ACK; break; case RSHELL_STATE_STDERR_CONNECT_SYN_ACK: if (rd->parent && rd->parent->state == RSHELL_STATE_SERVER_CONNECT) { rd->parent->state = RSHELL_STATE_USERNAME; rd->state = RSHELL_STATE_STDERR_WAIT; break; } goto bail; case RSHELL_STATE_STDERR_WAIT: /* The only valid way out of this state is for the parent flow to change it. */ if (!size) break; goto bail; case RSHELL_STATE_STDERR_DONE: clearAppIdFlag(flowp, APPID_SESSION_REXEC_STDERR | APPID_SESSION_CONTINUE); goto success; case RSHELL_STATE_BAIL: default: goto bail; } inprocess: rshell_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; success: rshell_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_SHELL, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; bail: rshell_bail(rd, flowp); rshell_service_mod.api->incompatible_data(flowp, pkt, dir, &svc_element, rshell_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; fail: rshell_bail(rd, flowp); rshell_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, rshell_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_bgp.c0000644000175000017500000001473314241075466025166 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" #include "service_bgp.h" #define BGP_PORT 179 #define BGP_V1_TYPE_OPEN 1 #define BGP_V1_TYPE_OPEN_CONFIRM 5 #define BGP_TYPE_OPEN 1 #define BGP_TYPE_KEEPALIVE 4 #define BGP_OPEN_LINK_MAX 3 #define BGP_VERSION_MAX 4 #define BGP_VERSION_MIN 2 typedef enum { BGP_STATE_CONNECTION, BGP_STATE_OPENSENT } BGPState; #pragma pack(1) typedef struct _SERVICE_BGP_DATA { BGPState state; int v1; } ServiceBGPData; typedef union _SERVICE_BGP_HEADER { struct { uint16_t marker; uint16_t len; uint8_t version; uint8_t type; uint16_t hold; } v1; struct { uint32_t marker[4]; uint16_t len; uint8_t type; } v; } ServiceBGPHeader; typedef struct _SERVICE_BGP_OPEN { uint8_t version; uint16_t as; uint16_t holdtime; } ServiceBGPOpen; typedef struct _SERVICE_BGP_V1_OPEN { uint16_t system; uint8_t link; uint8_t auth; } ServiceBGPV1Open; #pragma pack() static int bgp_init(const InitServiceAPI * const init_api); static int bgp_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &bgp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "bgp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&bgp_validate, BGP_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule bgp_service_mod = { "BGP", &bgp_init, pp }; static uint8_t BGP_PATTERN[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_BGP, 0}}; static int bgp_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&bgp_validate, IPPROTO_TCP, BGP_PATTERN, sizeof(BGP_PATTERN), 0, "bgp", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&bgp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int bgp_validate(ServiceValidationArgs* args) { ServiceBGPData *bd; const ServiceBGPHeader *bh; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; uint16_t len; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; if (size < sizeof(ServiceBGPHeader)) goto fail; bd = bgp_service_mod.api->data_get(flowp, bgp_service_mod.flow_data_index); if (!bd) { bd = calloc(1, sizeof(*bd)); if (!bd) return SERVICE_ENOMEM; if (bgp_service_mod.api->data_add(flowp, bd, bgp_service_mod.flow_data_index, &free)) { free(bd); return SERVICE_ENOMEM; } bd->state = BGP_STATE_CONNECTION; } bh = (const ServiceBGPHeader *)data; switch (bd->state) { case BGP_STATE_CONNECTION: if (size >= sizeof(bh->v1) + sizeof(ServiceBGPV1Open) && bh->v1.marker == 0xFFFF && bh->v1.version == 0x01 && bh->v1.type == BGP_V1_TYPE_OPEN) { ServiceBGPV1Open *open; len = ntohs(bh->v1.len); if (len > 1024) goto fail; open = (ServiceBGPV1Open *)(data + sizeof(bh->v1)); if (open->link > BGP_OPEN_LINK_MAX) goto fail; bd->v1 = 1; } else if (size >= sizeof(bh->v) + sizeof(ServiceBGPOpen) && bh->v.marker[0] == 0xFFFFFFFF && bh->v.marker[1] == 0xFFFFFFFF && bh->v.marker[2] == 0xFFFFFFFF && bh->v.marker[3] == 0xFFFFFFFF && bh->v.type == BGP_TYPE_OPEN) { ServiceBGPOpen *open; len = ntohs(bh->v.len); if (len > 4096) goto fail; open = (ServiceBGPOpen *)(data + sizeof(bh->v)); if (open->version > BGP_VERSION_MAX || open->version < BGP_VERSION_MIN) { goto fail; } bd->v1 = 0; } else goto fail; bd->state = BGP_STATE_OPENSENT; break; case BGP_STATE_OPENSENT: if (bd->v1) { if (size >= sizeof(bh->v1) && bh->v1.marker == 0xFFFF && bh->v1.version == 0x01 && bh->v1.type == BGP_V1_TYPE_OPEN_CONFIRM) { len = ntohs(bh->v1.len); if (len != sizeof(bh->v1)) goto fail; goto success; } } else { if (size >= sizeof(bh->v) && bh->v.type == BGP_TYPE_KEEPALIVE) { len = ntohs(bh->v.len); if (len != sizeof(bh->v)) goto fail; goto success; } } default: goto fail; } inprocess: bgp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; fail: bgp_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, bgp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; success: bgp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_BGP, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_telnet.c0000644000175000017500000001104114241075560025671 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" #define TELNET_COUNT_THRESHOLD 3 #define TELNET_IAC 255 #define TELNET_MIN_CMD 236 #define TELNET_MIN_DATA_CMD 250 #define TELNET_SUB_NEG_CMD 250 #define TELNET_SUB_NEG_END_CMD 240 #define TELNET_CMD_MAX_OPTION 44 typedef enum { TELNET_CMD_SE = 240, TELNET_CMD_NOP, TELNET_CMD_DMARK, TELNET_CMD_BREAK, TELNET_CMD_IP, TELNET_CMD_AO, TELNET_CMD_AYT, TELNET_CMD_EC, TELNET_CMD_EL, TELNET_CMD_GA, TELNET_CMD_SB, TELNET_CMD_WILL, TELNET_CMD_WONT, TELNET_CMD_DO, TELNET_CMD_DONT, TELNET_CMD_IAC } TELNET_COMMAND_VALUE; typedef struct _SERVICE_TELNET_DATA { unsigned count; } ServiceTelnetData; static int telnet_init(const InitServiceAPI * const init_api); static int telnet_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &telnet_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "telnet", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&telnet_validate, 23, IPPROTO_TCP}, {&telnet_validate, 23, IPPROTO_UDP}, {NULL, 0, 0} }; tRNAServiceValidationModule telnet_service_mod = { "TELNET", &telnet_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_TELNET, 0}}; static int telnet_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&telnet_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int telnet_validate(ServiceValidationArgs* args) { ServiceTelnetData *td; const uint8_t *end; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; td = telnet_service_mod.api->data_get(flowp, telnet_service_mod.flow_data_index); if (!td) { td = calloc(1, sizeof(*td)); if (!td) return SERVICE_ENOMEM; if (telnet_service_mod.api->data_add(flowp, td, telnet_service_mod.flow_data_index, &free)) { free(td); return SERVICE_ENOMEM; } } for (end=(data+size); data= end) goto fail; switch (*data) { case TELNET_CMD_WILL: case TELNET_CMD_WONT: case TELNET_CMD_DO: case TELNET_CMD_DONT: data++; if (data >= end) goto fail; td->count++; if (td->count >= TELNET_COUNT_THRESHOLD) goto success; break; default: goto fail; } } inprocess: telnet_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: telnet_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_TELNET, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: telnet_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, telnet_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_netbios.c0000644000175000017500000010325214241075521026044 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" /* for WORDS_BIGENDIAN */ #endif /*#define RNA_DEBUG_NETBIOS 1 */ #include "appIdApi.h" #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #include "dcerpc.h" #define NBSS_PORT 139 #define NBNS_NB 32 #define NBNS_NBSTAT 33 #define NBNS_LENGTH_FLAGS 0xC0 #define NBNS_OPCODE_QUERY 0 #define NBNS_OPCODE_REGISTRATION 5 #define NBNS_OPCODE_RELEASE 6 #define NBNS_OPCODE_WEACK 7 #define NBNS_OPCODE_REFRESH 8 #define NBNS_OPCODE_REFRESHALT 9 #define NBNS_OPCODE_MHREGISTRATION 15 #define NBSS_COUNT_THRESHOLD 2 #define NBNS_REPLYCODE_MAX 7 #define NBSS_TYPE_MESSAGE 0x00 #define NBSS_TYPE_REQUEST 0x81 #define NBSS_TYPE_RESP_POSITIVE 0x82 #define NBSS_TYPE_RESP_NEGATIVE 0x83 #define NBSS_TYPE_RESP_RETARGET 0x84 #define NBSS_TYPE_KEEP_ALIVE 0x85 typedef enum { NBSS_STATE_CONNECTION, NBSS_STATE_FLOW, NBSS_STATE_CONT, NBSS_STATE_ERROR } NBSSState; #define NBDGM_TYPE_DIRECT_UNIQUE 0x10 #define NBDGM_TYPE_DIRECT_GROUP 0x11 #define NBDGM_TYPE_BROADCAST 0x12 #define NBDGM_TYPE_ERROR 0x13 #define NBDGM_TYPE_REQUEST 0x14 #define NBDGM_TYPE_POSITIVE_REPSONSE 0x15 #define NBDGM_TYPE_NEGATIVE_RESPONSE 0x16 #define NBDGM_ERROR_CODE_MIN 0x82 #define NBDGM_ERROR_CODE_MAX 0x84 #define min(x,y) ((x)<(y) ? (x):(y)) #pragma pack(1) typedef struct _NBNS_HEADER { uint16_t id; #if defined(WORDS_BIGENDIAN) uint8_t response:1, Opcode:4, auth:1, trunc:1, RD:1; uint8_t RA:1, unused:2, broadcast:1, replycode:4; #else uint8_t RD:1, trunc:1, auth:1, Opcode:4, response:1; uint8_t replycode:4, broadcast:1, unused:2, RA:1; #endif uint16_t QCount; uint16_t ACount; uint16_t NSCount; uint16_t ARCount; } NBNSHeader; #define NBNS_NAME_LEN 0x20 typedef struct _NBNS_LABEL_LENGTH { uint8_t len; } NBNSLabelLength; typedef struct _NBNS_LABEL_DATA { uint8_t len; uint8_t data[NBNS_NAME_LEN]; uint8_t zero; } NBNSLabelData; typedef struct _NBNS_LABEL { uint16_t type; uint16_t class_id; } NBNSLabel; typedef struct _NBNS_LABEL_POINTER { uint8_t flag; uint8_t position; } NBNSLabelPtr; typedef struct _NBNS_ANSWER_DATA { uint32_t ttl; uint16_t data_len; } NBNSAnswerData; typedef struct _NBSS_HEADER { uint8_t type; uint8_t flags; uint16_t length; } NBSSHeader; static uint8_t NB_SMB_BANNER[] = { 0xFF, 'S', 'M', 'B' }; typedef struct _SERVICE_SMB_HEADER { uint8_t command; uint32_t status; uint8_t flags[3]; uint16_t pid_high; uint8_t signature[8]; uint16_t reserved2; uint16_t tid; uint16_t pid; uint16_t uid; uint16_t mid; } ServiceSMBHeader; typedef struct _SERVICE_SMB_ANDX_RESPONSE { uint8_t wc; uint8_t cmd; uint8_t reserved; uint16_t offset; uint16_t action; uint16_t sec_len; } ServiceSMBAndXResponse; typedef struct _SERVICE_SMB_NEGOTIATE_PROTOCOL_RESPONSE { uint8_t wc; uint16_t dialect_index; uint8_t security_mode; uint16_t max_mpx_count; uint16_t max_vcs; uint32_t max_buffer_size; uint32_t max_raw_buffer; uint32_t session_key; uint32_t capabilities; uint32_t system_time[2]; uint16_t time_zone; uint8_t sec_len; } ServiceSMBNegotiateProtocolResponse; typedef struct _SERVICE_SMB_TRANSACTION_HEADER { uint8_t wc; uint16_t total_pc; uint16_t total_dc; uint16_t max_pc; uint16_t max_dc; uint8_t max_sc; uint8_t reserved; uint16_t flags; uint32_t timeout; uint16_t reserved2; uint16_t pc; uint16_t po; uint16_t dc; uint16_t offset; uint8_t sc; uint8_t reserved3; } ServiceSMBTransactionHeader; /* sc * 2 to get to the transaction name */ #define SERVICE_SMB_STATUS_SUCCESS 0x00000000 #define SERVICE_SMB_TRANSACTION_COMMAND 0x25 #define SERVICE_SMB_COMMAND_SESSION_SETUP_ANDX_RESPONSE 0x73 #define SERVICE_SMB_COMMAND_NEGOTIATE_PROTOCOL 0x72 #define SERVICE_SMB_CAPABILITIES_EXTENDED_SECURITY 0x80000000 #define SERVICE_SMB_CAPABILITIES_UNICODE 0x00000004 #define SERVICE_SMB_FLAGS_RESPONSE 0x80 #define SERVICE_SMB_FLAGS_UNICODE 0x80 #define SERVICE_SMB_NOT_TRANSACTION_WC 8 #define SERVICE_SMB_MAILSLOT_HOST 0x01 #define SERVICE_SMB_MAILSLOT_LOCAL_MASTER 0x0f #define SERVICE_SMB_MAILSLOT_SERVER_TYPE_XENIX 0x00000800 #define SERVICE_SMB_MAILSLOT_SERVER_TYPE_NT 0x00001000 static char mailslot[] = "\\MAILSLOT\\BROWSE"; typedef struct _SERVICE_SMB_BROWSER_HEADER { uint8_t command; uint8_t count; uint32_t period; uint8_t hostname[16]; uint8_t major; uint8_t minor; uint32_t server_type; } ServiceSMBBrowserHeader; typedef struct _SERVICE_NBSS_DATA { NBSSState state; unsigned count; uint32_t length; tAppId serviceAppId; tAppId miscAppId; } ServiceNBSSData; typedef struct _NBDGM_HEADER { uint8_t type; #if defined(WORDS_BIGENDIAN) uint8_t zero:4, SNT:2, first:1, more:1; #else uint8_t more:1, first:1, SNT:2, zero:4; #endif uint16_t id; uint32_t src_ip; uint16_t src_port; } NBDgmHeader; typedef struct _NBDGM_ERROR { uint8_t code; } NBDgmError; #pragma pack() static int netbios_init(const InitServiceAPI * const init_api); static int nbns_validate(ServiceValidationArgs* args); static int nbss_validate(ServiceValidationArgs* args); static int nbdgm_validate(ServiceValidationArgs* args); static tRNAServiceElement nbns_svc_element = { .next = NULL, .validate = &nbns_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "nbns", .ref_count = 1, .current_ref_count = 1, }; static tRNAServiceElement nbdgm_svc_element = { .next = NULL, .validate = &nbdgm_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "nbdgm", .ref_count = 1, .current_ref_count = 1, }; static tRNAServiceElement nbss_svc_element = { .next = NULL, .validate = &nbss_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "nbss", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&nbns_validate, 137, IPPROTO_TCP}, {&nbns_validate, 137, IPPROTO_UDP}, {&nbns_validate, 137, IPPROTO_UDP, 1}, {&nbdgm_validate, 138, IPPROTO_UDP}, {&nbss_validate, 139, IPPROTO_TCP}, {&nbss_validate, 445, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule netbios_service_mod = { "NETBIOS", &netbios_init, pp }; static int netbios_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&nbss_validate, IPPROTO_TCP, NB_SMB_BANNER, sizeof(NB_SMB_BANNER), -1, "netbios", init_api->pAppidConfig); _dpd.debugMsg(DEBUG_LOG,"registering appId: %d for NetBIOS-ns\n",APP_ID_NETBIOS_NS); init_api->RegisterAppId(&nbns_validate, APP_ID_NETBIOS_NS, APPINFO_FLAG_SERVICE_UDP_REVERSED, init_api->pAppidConfig); _dpd.debugMsg(DEBUG_LOG,"registering appId: %d for NetBIOS-dgm\n",APP_ID_NETBIOS_DGM); init_api->RegisterAppId(&nbdgm_validate, APP_ID_NETBIOS_DGM, APPINFO_FLAG_SERVICE_ADDITIONAL, init_api->pAppidConfig); _dpd.debugMsg(DEBUG_LOG,"registering appId: %d for NetBIOS-ssn\n",APP_ID_NETBIOS_SSN); init_api->RegisterAppId(&nbss_validate, APP_ID_NETBIOS_SSN, APPINFO_FLAG_SERVICE_ADDITIONAL, init_api->pAppidConfig); _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",APP_ID_DCE_RPC); init_api->RegisterAppId(&nbss_validate, APP_ID_DCE_RPC, 0, init_api->pAppidConfig); return 0; } static int netbios_validate_name_and_decode(const uint8_t * *data, const uint8_t * const begin, const uint8_t * const end, char *name) { const NBNSLabelLength *lbl_len; const NBNSLabelData *lbl_data; const NBNSLabelPtr *lbl_ptr; int i; int j; if (end - *data < (int)sizeof(NBNSLabelLength)) return -1; lbl_len = (NBNSLabelLength *)(*data); switch (lbl_len->len & NBNS_LENGTH_FLAGS) { case 0x00: lbl_data = (NBNSLabelData *)(*data); if (end - *data < (int)sizeof(NBNSLabelData)) return -1; *data += sizeof(NBNSLabelData); break; case 0xC0: lbl_ptr = (NBNSLabelPtr *)(*data); *data += sizeof(NBNSLabelPtr); if (begin + lbl_ptr->position + sizeof(NBNSLabelData) > end) return -1; lbl_data = (NBNSLabelData *)(begin + lbl_ptr->position); break; default: return -1; } if (lbl_data->len != NBNS_NAME_LEN) return -1; if (lbl_data->zero) return -1; for (i=0; i<(NBNS_NAME_LEN/2); i++) { j = 2 * i; if (lbl_data->data[j] < 'A' || lbl_data->data[j] > 'Z') return -1; name[i] = (uint8_t)(((uint8_t)(lbl_data->data[j] - 'A')) << 4); j++; if (lbl_data->data[i] < 'A' || lbl_data->data[i] > 'Z') return -1; name[i] |= (uint8_t)(lbl_data->data[j] - 'A'); } name[(NBNS_NAME_LEN/2)] = 0; for (i=(NBNS_NAME_LEN/2)-1; i >= 0; i--) { if (name[i] == ' ') name[i] = 0; else if (name[i]) break; } return 0; } static int netbios_validate_name(const uint8_t * *data, const uint8_t * const begin, const uint8_t * const end) { const NBNSLabelLength *lbl_len; const NBNSLabelData *lbl_data; const NBNSLabelPtr *lbl_ptr; int i; if (end - *data < (int)sizeof(NBNSLabelLength)) return -1; lbl_len = (NBNSLabelLength *)(*data); switch (lbl_len->len & NBNS_LENGTH_FLAGS) { case 0x00: lbl_data = (NBNSLabelData *)(*data); if (end - *data < (int)sizeof(NBNSLabelData)) return -1; *data += sizeof(NBNSLabelData); break; case 0xC0: lbl_ptr = (NBNSLabelPtr *)(*data); *data += sizeof(NBNSLabelPtr); if (begin + lbl_ptr->position + sizeof(NBNSLabelData) > end) return -1; lbl_data = (NBNSLabelData *)(begin + lbl_ptr->position); break; default: return -1; } if (lbl_data->len != NBNS_NAME_LEN) return -1; if (lbl_data->zero) return -1; for (i=0; idata[i] < 'A' || lbl_data->data[i] > 'Z') return -1; return 0; } static int netbios_validate_label(const uint8_t * *data, const uint8_t * const end) { const NBNSLabel *lbl; uint16_t tmp; if (end - *data < (int)sizeof(NBNSLabel)) return -1; lbl = (NBNSLabel *)(*data); *data += sizeof(NBNSLabel); tmp = ntohs(lbl->type); if (tmp != NBNS_NB && tmp != NBNS_NBSTAT) return -1; return 0; } static int nbns_validate_query(const uint8_t * *data, const uint8_t * const begin, const uint8_t * const end) { int ret; ret = netbios_validate_name(data, begin, end); if (!ret) { return netbios_validate_label(data, end); } return ret; } static int nbns_validate_answer(const uint8_t * *data, const uint8_t * const begin, const uint8_t * const end) { int ret; uint16_t tmp; ret = netbios_validate_name(data, begin, end); if (ret) return ret; ret = netbios_validate_label(data, end); if (!ret) { const NBNSAnswerData *ad = (const NBNSAnswerData *)(*data); if (end - *data < (int)sizeof(NBNSAnswerData)) return -1; *data += sizeof(NBNSAnswerData); tmp = ntohs(ad->data_len); if (end - *data < tmp) return -1; *data += tmp; } return ret; } static int nbns_validate(ServiceValidationArgs* args) { uint16_t i; uint16_t count; const NBNSHeader *hdr; const uint8_t *begin; const uint8_t *end; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; if (size < sizeof(NBNSHeader)) goto fail; hdr = (NBNSHeader *)data; if ((hdr->Opcode > NBNS_OPCODE_QUERY && hdr->Opcode < NBNS_OPCODE_REGISTRATION) || (hdr->Opcode > NBNS_OPCODE_REFRESHALT && hdr->Opcode < NBNS_OPCODE_MHREGISTRATION)) { goto fail; } if (hdr->trunc) goto not_compatible; if (hdr->broadcast) goto not_compatible; begin = data; end = data + size; data += sizeof(NBNSHeader); if (!hdr->response) { if (dir == APP_ID_FROM_RESPONDER) { if (getAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED)) goto success; goto fail; } goto inprocess; } if (hdr->replycode > NBNS_REPLYCODE_MAX) goto fail; if (hdr->QCount) { count = ntohs(hdr->QCount); for (i=0; iACount) { count = ntohs(hdr->ACount); for (i=0; iNSCount) { count = ntohs(hdr->NSCount); for (i=0; iARCount) { count = ntohs(hdr->ARCount); for (i=0; iadd_service(flowp, args->pkt, dir, &nbns_svc_element, APP_ID_NETBIOS_NS, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; inprocess: netbios_service_mod.api->service_inprocess(flowp, args->pkt, dir, &nbns_svc_element, NULL); return SERVICE_INPROCESS; fail: netbios_service_mod.api->fail_service(flowp, args->pkt, dir, &nbns_svc_element, netbios_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; not_compatible: netbios_service_mod.api->incompatible_data(flowp, args->pkt, dir, &nbns_svc_element, netbios_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; } static void nbss_free_state(void *data) { ServiceNBSSData *nd = (ServiceNBSSData *)data; if (nd) { free(nd); } } static inline void smb_domain_skip_string(const uint8_t * *data, uint16_t *size, uint16_t *offset, const uint8_t unicode) { if (unicode) { if (*size != 0 && ((*offset) % 2)) { (*offset)++; (*data)++; (*size)--; } while (*size > 1) { *size -= 2; *offset += 2; if (**data == 0) { *data += 2; break; } else { *data += 2; } } } else { while (*size) { (*size)--; (*offset)++; if (**data == 0) { (*data)++; break; } else { (*data)++; } } } } static inline void smb_find_domain(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, const SFSnortPacket *pkt) { const ServiceSMBHeader *smb; const ServiceSMBAndXResponse *resp; const ServiceSMBNegotiateProtocolResponse *np; char domain[NBNS_NAME_LEN+1]; unsigned pos = 0; uint16_t byte_count; uint16_t sec_len; uint16_t wc; uint8_t unicode; uint32_t capabilities; uint16_t offset; if (size < sizeof(*smb) + sizeof(wc)) return; smb = (ServiceSMBHeader *)data; if (smb->status != SERVICE_SMB_STATUS_SUCCESS) return; if (!(smb->flags[0] & SERVICE_SMB_FLAGS_RESPONSE)) return; unicode = smb->flags[2] & SERVICE_SMB_FLAGS_UNICODE; data += sizeof(*smb); size -= sizeof(*smb); resp = (ServiceSMBAndXResponse *)data; np = (ServiceSMBNegotiateProtocolResponse *)data; wc = 2 * (uint16_t)*data; offset = 1; data++; size--; if (size < (wc + sizeof(byte_count))) return; data += wc; size -= wc; byte_count = LETOHS(data); data += sizeof(byte_count); size -= sizeof(byte_count); if (size < byte_count) return; offset += sizeof(byte_count); offset += wc; if (smb->command == SERVICE_SMB_COMMAND_SESSION_SETUP_ANDX_RESPONSE) { if (wc == 8) { sec_len = LETOHS(&resp->sec_len); if (sec_len >= byte_count) return; data += sec_len; byte_count -= sec_len; } else if (wc != 6) return; smb_domain_skip_string(&data, &byte_count, &offset, unicode); smb_domain_skip_string(&data, &byte_count, &offset, unicode); if (byte_count != 0 && (offset % 2)) { data++; byte_count--; } } else if (smb->command == SERVICE_SMB_COMMAND_NEGOTIATE_PROTOCOL) { if (wc == 34) { capabilities = LETOHL(&np->capabilities); if (capabilities & SERVICE_SMB_CAPABILITIES_EXTENDED_SECURITY) return; unicode = (capabilities & SERVICE_SMB_CAPABILITIES_UNICODE) || unicode; } else if (wc != 26) return; if (np->sec_len >= byte_count) return; data += np->sec_len; byte_count -= np->sec_len; } else return; if (unicode) { int found = 0; while (byte_count > 1) { byte_count -= 2; if (*data == 0) { data += 2; found = 1; break; } else { if (pos < NBNS_NAME_LEN) { domain[pos] = *data; pos++; domain[pos] = 0; } data++; if (*data != 0) { #ifdef RNA_DEBUG_NETBIOS _dpd.errMsg( "Failed command %02X %u 0x%08X:%u->0x%08X:%u", smb->command, byte_count, pkt->src_ip.s_addr, pkt->src_port, pkt->dst_ip.s_addr, pkt->dst_port); #endif return; } data++; } } if (!found && byte_count == 1 && *data == 0) { byte_count--; } if (byte_count && smb->command != SERVICE_SMB_COMMAND_NEGOTIATE_PROTOCOL) { #ifdef RNA_DEBUG_NETBIOS _dpd.errMsg( "Failed command %02X %u 0x%08X:%u->0x%08X:%u", smb->command, byte_count, pkt->src_ip.s_addr, pkt->src_port, pkt->dst_ip.s_addr, pkt->dst_port); #endif return; } } else { while (byte_count) { byte_count--; if (*data == 0) { data++; break; } else { if (pos < NBNS_NAME_LEN) { domain[pos] = *data; pos++; domain[pos] = 0; } data++; } } if (byte_count && smb->command != SERVICE_SMB_COMMAND_NEGOTIATE_PROTOCOL) { #ifdef RNA_DEBUG_NETBIOS _dpd.errMsg( "Failed command %02X %u 0x%08X:%u->0x%08X:%u", smb->command, byte_count, pkt->src_ip.s_addr, pkt->src_port, pkt->dst_ip.s_addr, pkt->dst_port); #endif return; } } if (pos) { #ifdef RNA_DEBUG_NETBIOS _dpd.debugMsg(DEBUG_LOG, "Found domain %s for command %02X 0x%08X:%u->0x%08X:%u", domain, smb->command, pkt->src_ip.s_addr, pkt->src_port, pkt->dst_ip.s_addr, pkt->dst_port); #endif if (!flowp->netbiosDomain) { flowp->netbiosDomain = strdup(domain); if (!flowp->netbiosDomain) _dpd.errMsg("failed to allocate netbios domain name"); } } } static int nbss_validate(ServiceValidationArgs* args) { ServiceNBSSData *nd; const NBSSHeader *hdr; const uint8_t *end; uint32_t tmp; int retval = -1; tAppIdData *flowp = args->flowp; SFSnortPacket *pkt = args->pkt; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (dir != APP_ID_FROM_RESPONDER) goto inprocess; if (!size) goto inprocess; nd = netbios_service_mod.api->data_get(flowp, netbios_service_mod.flow_data_index); if (!nd) { nd = calloc(1, sizeof(*nd)); if (!nd) return SERVICE_ENOMEM; if (netbios_service_mod.api->data_add(flowp, nd, netbios_service_mod.flow_data_index, &nbss_free_state)) { free(nd); return SERVICE_ENOMEM; } nd->state = NBSS_STATE_CONNECTION; nd->serviceAppId = APP_ID_NETBIOS_SSN; nd->miscAppId = APP_ID_NONE; } end = data + size; while (data < end) { switch (nd->state) { case NBSS_STATE_CONNECTION: if (size < sizeof(NBSSHeader)) goto fail; hdr = (NBSSHeader *)data; data += sizeof(NBSSHeader); nd->state = NBSS_STATE_ERROR; switch (hdr->type) { case NBSS_TYPE_RESP_POSITIVE: if (hdr->flags || hdr->length) goto fail; nd->state = NBSS_STATE_FLOW; break; case NBSS_TYPE_RESP_NEGATIVE: if (hdr->flags || ntohs(hdr->length) != 1) goto fail; if (data >= end) goto fail; if (*data < 0x80 || (*data > 0x83 && *data < 0x8F) || *data > 0x8F) goto fail; data++; break; case NBSS_TYPE_MESSAGE: if (hdr->flags & 0xFE) goto fail; nd->length = ((uint32_t)(hdr->flags & 0x01)) << 16; nd->length |= (uint32_t)ntohs(hdr->length); tmp = end - data; if (tmp >= sizeof(NB_SMB_BANNER) && nd->length >= sizeof(NB_SMB_BANNER) && !memcmp(data, NB_SMB_BANNER, sizeof(NB_SMB_BANNER))) { if (nd->serviceAppId != APP_ID_DCE_RPC) { nd->serviceAppId = APP_ID_NETBIOS_SSN; } if (nd->length <= tmp) { smb_find_domain(data + sizeof(NB_SMB_BANNER), nd->length - sizeof(NB_SMB_BANNER), dir, flowp, pkt); } } else if (tmp >= 4 && nd->length >= 4 && !(*((uint32_t *)data)) && dcerpc_validate(data+4, ((int)min(tmp, nd->length)) - 4) > 0) { nd->serviceAppId = APP_ID_DCE_RPC; nd->miscAppId = APP_ID_NETBIOS_SSN; } if (tmp < nd->length) { data = end; nd->length -= tmp; nd->state = NBSS_STATE_CONT; } else { data += nd->length; nd->count++; nd->state = NBSS_STATE_FLOW; } break; case NBSS_TYPE_RESP_RETARGET: if (hdr->flags || ntohs(hdr->length) != 6) goto fail; if (end - data < 6) goto fail; data += 6; break; default: goto fail; } break; case NBSS_STATE_FLOW: if (size < sizeof(NBSSHeader)) goto fail; hdr = (NBSSHeader *)data; data += sizeof(NBSSHeader); switch (hdr->type) { case NBSS_TYPE_KEEP_ALIVE: if (hdr->flags || hdr->length) goto fail; break; case NBSS_TYPE_MESSAGE: if (hdr->flags & 0xFE) goto fail; nd->length = ((uint32_t)(hdr->flags & 0x01)) << 16; nd->length += (uint32_t)ntohs(hdr->length); tmp = end - data; if (tmp >= sizeof(NB_SMB_BANNER) && nd->length >= sizeof(NB_SMB_BANNER) && !memcmp(data, NB_SMB_BANNER, sizeof(NB_SMB_BANNER))) { if (nd->serviceAppId != APP_ID_DCE_RPC) { nd->serviceAppId = APP_ID_NETBIOS_SSN; } if (nd->length <= tmp) { smb_find_domain(data + sizeof(NB_SMB_BANNER), nd->length, dir, flowp, pkt); } } else if (tmp >= 4 && nd->length >= 4 && !(*((uint32_t *)data)) && dcerpc_validate(data+4, ((int)min(tmp, nd->length)) - 4) > 0) { nd->serviceAppId = APP_ID_DCE_RPC; nd->miscAppId = APP_ID_NETBIOS_SSN; } if (tmp < nd->length) { data = end; nd->length -= tmp; nd->state = NBSS_STATE_CONT; } else { data += nd->length; if (nd->count < NBSS_COUNT_THRESHOLD) { nd->count++; if (nd->count >= NBSS_COUNT_THRESHOLD) { retval = SERVICE_SUCCESS; } } } break; default: goto fail; } break; case NBSS_STATE_CONT: tmp = end - data; if (tmp < nd->length) { data = end; nd->length -= tmp; } else { data += nd->length; nd->state = NBSS_STATE_FLOW; if (nd->count < NBSS_COUNT_THRESHOLD) { nd->count++; if (nd->count >= NBSS_COUNT_THRESHOLD) { retval = SERVICE_SUCCESS; } } } break; default: goto fail; } } if (retval == -1) goto inprocess; if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { if(netbios_service_mod.api->add_service(flowp, pkt, dir, &nbss_svc_element, nd->serviceAppId, NULL, NULL, NULL, NULL) == SERVICE_SUCCESS) { netbios_service_mod.api->add_misc(flowp, nd->miscAppId); } } return SERVICE_SUCCESS; inprocess: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { netbios_service_mod.api->service_inprocess(flowp, pkt, dir, &nbss_svc_element, NULL); } return SERVICE_INPROCESS; fail: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { netbios_service_mod.api->fail_service(flowp, pkt, dir, &nbss_svc_element, netbios_service_mod.flow_data_index, args->pConfig, NULL); } return SERVICE_NOMATCH; } static int nbdgm_validate(ServiceValidationArgs* args) { const NBDgmHeader *hdr; const NBDgmError *err; const uint8_t *end; const ServiceSMBHeader *smb; const ServiceSMBTransactionHeader *trans; const ServiceSMBBrowserHeader *browser; uint16_t len; char source_name[(NBNS_NAME_LEN/2)+1]; uint32_t server_type; tAppId serviceAppId = APP_ID_NETBIOS_DGM; tAppId miscAppId = APP_ID_NONE; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; if (size < sizeof(NBDgmHeader)) goto fail; if (pkt->src_port != pkt->dst_port) goto fail; source_name[0] = 0; end = data + size; hdr = (NBDgmHeader *)data; data += sizeof(NBDgmHeader); if (hdr->zero) goto fail; if (!hdr->first || hdr->more) goto fail; switch (hdr->type) { case NBDGM_TYPE_POSITIVE_REPSONSE: case NBDGM_TYPE_NEGATIVE_RESPONSE: case NBDGM_TYPE_REQUEST: if (netbios_validate_name(&data, data, end)) goto fail; if (end != data) goto fail; goto success; case NBDGM_TYPE_DIRECT_UNIQUE: case NBDGM_TYPE_DIRECT_GROUP: case NBDGM_TYPE_BROADCAST: data += sizeof(uint16_t) + sizeof(uint16_t); /* dgm_length and packet_offset */ if (data >= end) goto fail; if (netbios_validate_name_and_decode(&data, data, end, source_name)) goto fail; if (data >= end) goto fail; if (netbios_validate_name(&data, data, end)) goto fail; if (data >= end) goto fail; if (end-data >= (int)sizeof(NB_SMB_BANNER) && !memcmp(data, NB_SMB_BANNER, sizeof(NB_SMB_BANNER))) { if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { serviceAppId = APP_ID_NETBIOS_DGM; } data += sizeof(NB_SMB_BANNER); if (end-data < (int)sizeof(ServiceSMBHeader)) goto not_mailslot; smb = (ServiceSMBHeader *)data; data += sizeof(ServiceSMBHeader); if (smb->command != SERVICE_SMB_TRANSACTION_COMMAND) goto not_mailslot; if (end-data < (int)sizeof(ServiceSMBTransactionHeader)) goto not_mailslot; trans = (ServiceSMBTransactionHeader *)data; data += sizeof(ServiceSMBTransactionHeader); if (trans->wc == SERVICE_SMB_NOT_TRANSACTION_WC) goto not_mailslot; if ((unsigned)(end-data) < (trans->sc*2)+sizeof(uint16_t)+sizeof(mailslot)+sizeof(ServiceSMBBrowserHeader)) goto not_mailslot; data += (trans->sc*2); len = *((uint16_t *)data); data += sizeof(uint16_t); if (end-data < len) goto not_mailslot; if (memcmp(data, mailslot, sizeof(mailslot))) goto not_mailslot; data += sizeof(mailslot); browser = (ServiceSMBBrowserHeader *)data; if (browser->command != SERVICE_SMB_MAILSLOT_HOST && browser->command != SERVICE_SMB_MAILSLOT_LOCAL_MASTER) { goto not_mailslot; } server_type = LETOHL(&browser->server_type); netbios_service_mod.api->analyzefp(flowp, browser->major, browser->minor, server_type); } not_mailslot: if (source_name[0]) netbios_service_mod.api->add_host_info(flowp, SERVICE_HOST_INFO_NETBIOS_NAME, source_name); setAppIdFlag(flowp, APPID_SESSION_CONTINUE); goto success; case NBDGM_TYPE_ERROR: if (end-data < (int)sizeof(NBDgmError)) goto fail; err = (NBDgmError *)data; data += sizeof(NBDgmError); if (end != data) goto fail; if (err->code < NBDGM_ERROR_CODE_MIN || err->code > NBDGM_ERROR_CODE_MAX) { goto fail; } goto success; default: break; } fail: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { netbios_service_mod.api->fail_service(flowp, pkt, dir, &nbdgm_svc_element, netbios_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOMATCH; success: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { if (dir == APP_ID_FROM_RESPONDER) { if(netbios_service_mod.api->add_service(flowp, pkt, dir, &nbdgm_svc_element, serviceAppId, NULL, NULL, NULL, NULL) == SERVICE_SUCCESS) { netbios_service_mod.api->add_misc(flowp, miscAppId); } } } return SERVICE_SUCCESS; inprocess: if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { netbios_service_mod.api->service_inprocess(flowp, pkt, dir, &nbdgm_svc_element, NULL); } return SERVICE_INPROCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_dcerpc.c0000644000175000017500000001317014241075500025635 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include "flow.h" #include "service_api.h" #include "dcerpc.h" #define DCERPC_THRESHOLD 3 #define min(x,y) ((x)<(y) ? (x):(y)) typedef struct _SERVICE_DCERPC_DATA { unsigned count; } ServiceDCERPCData; static int dcerpc_init(const InitServiceAPI * const init_api); static int dcerpc_tcp_validate(ServiceValidationArgs* args); static int dcerpc_udp_validate(ServiceValidationArgs* args); static tRNAServiceElement tcp_svc_element = { .next = NULL, .validate = &dcerpc_tcp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "dcerpc", .ref_count = 1, .current_ref_count = 1, }; static tRNAServiceElement udp_svc_element = { .next = NULL, .validate = &dcerpc_udp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "udp dcerpc", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&dcerpc_tcp_validate, 135, IPPROTO_TCP}, {&dcerpc_udp_validate, 135, IPPROTO_UDP}, {NULL, 0, 0} }; tRNAServiceValidationModule dcerpc_service_mod = { "DCERPC", &dcerpc_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_DCE_RPC, 0}}; static int dcerpc_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&dcerpc_udp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int dcerpc_tcp_validate(ServiceValidationArgs* args) { ServiceDCERPCData *dd; int retval = SERVICE_INPROCESS; int length; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; if (!size) goto inprocess; dd = dcerpc_service_mod.api->data_get(flowp, dcerpc_service_mod.flow_data_index); if (!dd) { dd = calloc(1, sizeof(*dd)); if (!dd) return SERVICE_ENOMEM; if (dcerpc_service_mod.api->data_add(flowp, dd, dcerpc_service_mod.flow_data_index, &free)) { free(dd); return SERVICE_ENOMEM; } } while (size) { length = dcerpc_validate(data, size); if (length < 0) goto fail; dd->count++; if (dd->count >= DCERPC_THRESHOLD) retval = SERVICE_SUCCESS; data += length; size -= length; } if (retval == SERVICE_SUCCESS) { dcerpc_service_mod.api->add_service(flowp, args->pkt, args->dir, &tcp_svc_element, APP_ID_DCE_RPC, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; } inprocess: dcerpc_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &tcp_svc_element, NULL); return SERVICE_INPROCESS; fail: dcerpc_service_mod.api->fail_service(flowp, args->pkt, args->dir, &tcp_svc_element, dcerpc_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } static int dcerpc_udp_validate(ServiceValidationArgs* args) { ServiceDCERPCData *dd; int retval = SERVICE_NOMATCH; int length; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; if (!size) goto inprocess; dd = dcerpc_service_mod.api->data_get(flowp, dcerpc_service_mod.flow_data_index); if (!dd) { dd = calloc(1, sizeof(*dd)); if (!dd) return SERVICE_ENOMEM; if (dcerpc_service_mod.api->data_add(flowp, dd, dcerpc_service_mod.flow_data_index, &free)) { free(dd); return SERVICE_ENOMEM; } } while (size) { length = dcerpc_validate(data, size); if (length < 0) goto fail; dd->count++; if (dd->count >= DCERPC_THRESHOLD) retval = SERVICE_SUCCESS; data += length; size -= length; } if (retval == SERVICE_SUCCESS) { dcerpc_service_mod.api->add_service(flowp, args->pkt, args->dir, &udp_svc_element, APP_ID_DCE_RPC, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; } inprocess: dcerpc_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &udp_svc_element, NULL); return SERVICE_INPROCESS; fail: dcerpc_service_mod.api->fail_service(flowp, args->pkt, args->dir, &udp_svc_element, dcerpc_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_ftp.h0000644000175000017500000000205414241075510025173 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_FTP_H__ #define __SERVICE_FTP_H__ #include "service_api.h" extern tRNAServiceValidationModule ftp_service_mod; #endif /* __SERVICE_FTP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_dcerpc.h0000644000175000017500000000207014241075501025640 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_DCERPC_H__ #define __SERVICE_DCERPC_H__ #include "service_api.h" extern tRNAServiceValidationModule dcerpc_service_mod; #endif /* __SERVICE_DCERPC_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/dcerpc.c0000644000175000017500000000352614241075456024133 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include "dcerpc.h" #define min(x,y) ((x)<(y) ? (x):(y)) #define DCERPC_LE_FLAG 0x10 #pragma pack(1) typedef struct _DCERPC_HEADER { uint8_t version; uint8_t minor_version; uint8_t type; uint8_t flags; uint8_t drep[4]; uint16_t frag_length; uint16_t auth_length; uint32_t id; } DCERPCHeader; #pragma pack() int dcerpc_validate(const uint8_t *data, int size) { DCERPCHeader *hdr; uint16_t len; if (size < (int)sizeof(DCERPCHeader)) return -1; hdr = (DCERPCHeader *)data; if (hdr->version != 5) return -1; if (hdr->minor_version > 1) return -1; if (hdr->type > 19) return -1; if (hdr->drep[0] & DCERPC_LE_FLAG) { len = hdr->frag_length; } else { len = ntohs(hdr->frag_length); } if (len < sizeof(DCERPCHeader)) return -1; if (size < len) return -1; return (int)len; } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_lpr.h0000644000175000017500000000205414241075514025203 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_LPR_H__ #define __SERVICE_LPR_H__ #include "service_api.h" extern tRNAServiceValidationModule lpr_service_mod; #endif /* __SERVICE_LPR_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_telnet.h0000644000175000017500000000207014241075561025701 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_TELNET_H__ #define __SERVICE_TELNET_H__ #include "service_api.h" extern tRNAServiceValidationModule telnet_service_mod; #endif /* __SERVICE_TELNET_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_MDNS.c0000644000175000017500000004615614241075515025156 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #ifndef WIN32 #include #include #endif #include "fw_appid.h" #include "profiler.h" #include "client_app_base.h" #include "httpCommon.h" #include "luaDetectorApi.h" #include "http_url_patterns.h" #include "fw_appid.h" #include "detector_http.h" #include "service_ssl.h" #include "flow.h" #include "common_util.h" #include #include #include "flow.h" #include "service_api.h" #include "service_MDNS.h" #include "service_base.h" #include "appIdConfig.h" #define MDNS_PORT 5353 #define PATTERN_REFERENCE_PTR 3 #define PATTERN_STR_LOCAL_1 "\005local" #define PATTERN_STR_LOCAL_2 "\005LOCAL" #define PATTERN_STR_ARPA_1 "\004arpa" #define PATTERN_STR_ARPA_2 "\004ARPA" #define PATTERN_USERNAME_1 '@' #define MDNS_PATTERN1 "\x00\x00\x84\x00\x00\x00" #define MDNS_PATTERN2 "\x00\x00\x08\x00\x00\x00" #define MDNS_PATTERN3 "\x00\x00\x04\x00\x00\x00" #define MDNS_PATTERN4 "\x00\x00\x00\x00" #define SRV_RECORD "\x00\x21" #define SRV_RECORD_OFFSET 6 #define LENGTH_OFFSET 8 #define NEXT_MESSAGE_OFFSET 10 #define QUERY_OFFSET 4 #define ANSWER_OFFSET 6 #define RECORD_OFFSET 12 #define SHIFT_BITS 8 #define SHIFT_BITS_REFERENCE_PTR 6 #define REFERENCE_PTR_LENGTH 2 #define MAX_LENGTH_SERVICE_NAME 256 typedef enum { MDNS_STATE_CONNECTION, MDNS_STATE_CONNECTION_ERROR } MDNSState; typedef struct _SERVICE_MDNS_DATA { MDNSState state; } ServiceMDNSData; typedef struct { const uint8_t *pattern; unsigned length; } tMdnsPattern; typedef struct _MatchedPatterns { tMdnsPattern *mpattern; int index; struct _MatchedPatterns *next; } MatchedPatterns; typedef struct { void *mdnsMatcher; MatchedPatterns *patternList; } tMdnsConfig; static int MDNS_init(const InitServiceAPI * const init_api); static int ReferencePointer(const char *start_ptr,const char **resp_endptr, int *start_index , uint16_t data_size, uint8_t *user_name_len , unsigned offset, const tAppIdConfig *pConfig); static int MDNS_validate(ServiceValidationArgs* args); static int mdnsMatcherCreate(tAppIdConfig *pConfig); static void mdnsMatcherDestroy(tAppIdConfig *pConfig); static unsigned mdnsMatchListCreate(const char *data, uint16_t dataSize, const tAppIdConfig *pConfig); static void mdnsMatchListFind(const char *dataPtr, uint16_t index, const char **resp_endptr, int *pattern_length, const tAppIdConfig *pConfig); static void mdnsMatchListDestroy(const tAppIdConfig *pConfig); static void MDNS_clean(const CleanServiceAPI * const clean_api); static tRNAServiceElement svc_element = { .next = NULL, .validate = &MDNS_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "MDNS", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&MDNS_validate, 5353, IPPROTO_UDP}, {NULL, 0, 0} }; tRNAServiceValidationModule mdns_service_mod = { "MDNS", &MDNS_init, pp, .clean = MDNS_clean }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_MDNS, APPINFO_FLAG_SERVICE_ADDITIONAL }}; static int MDNS_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&MDNS_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } mdnsMatcherCreate(init_api->pAppidConfig); return 0; } static int MDNS_validate_reply(const uint8_t *data, uint16_t size ) { int ret_val; /* Check for the pattern match*/ if (size >= 6 && memcmp(data, MDNS_PATTERN1, sizeof(MDNS_PATTERN1)-1) == 0) ret_val = 1; else if (size >= 6 && memcmp(data, MDNS_PATTERN2, sizeof(MDNS_PATTERN2)-1) == 0) ret_val = 1; else if (size >= 6 && memcmp(data,MDNS_PATTERN3, sizeof(MDNS_PATTERN3)-1) == 0) ret_val = 1; else if (size >= 4 && memcmp(data,MDNS_PATTERN4, sizeof(MDNS_PATTERN4)-1) == 0) ret_val = 1; else ret_val = 0; return ret_val; } /* Input to this function is start_ptr and data_size. */ /* Output is resp_endptr, start_index and user_name_len */ /* Returns 0 or 1 for successful/unsuccessful hit for pattern '@' */ /* Returns -1 for invalid address pointer or past the data_size */ static int ReferencePointer(const char *start_ptr, const char **resp_endptr, int *start_index , uint16_t data_size, uint8_t *user_name_len, unsigned size, const tAppIdConfig *pConfig) { int index = 0; int pattern_length = 0; while(index< data_size && (start_ptr[index] == ' ' )) index++; if(index >= data_size) return -1; *start_index = index; const char *temp_start_ptr; temp_start_ptr = start_ptr+index; mdnsMatchListFind(start_ptr, size - data_size + index, resp_endptr, &pattern_length, pConfig); /* Contains reference pointer */ while((index < data_size) && !(*resp_endptr) && ((uint8_t )temp_start_ptr[index] >>SHIFT_BITS_REFERENCE_PTR != PATTERN_REFERENCE_PTR)) { if (temp_start_ptr[index] == PATTERN_USERNAME_1) { *user_name_len = index - *start_index ; index++; break; } index++; mdnsMatchListFind(start_ptr, size - data_size + index , resp_endptr, &pattern_length, pConfig); } if(index >= data_size) *user_name_len = 0; else if((uint8_t )temp_start_ptr[index] >> SHIFT_BITS_REFERENCE_PTR == PATTERN_REFERENCE_PTR) pattern_length = REFERENCE_PTR_LENGTH; else if (!(*resp_endptr) && ((uint8_t )temp_start_ptr[index] >>SHIFT_BITS_REFERENCE_PTR != PATTERN_REFERENCE_PTR )) { while((index < data_size) && !(*resp_endptr) && ((uint8_t )temp_start_ptr[index] >> SHIFT_BITS_REFERENCE_PTR != PATTERN_REFERENCE_PTR)) { index++; mdnsMatchListFind(start_ptr, size - data_size + index , resp_endptr, &pattern_length, pConfig); } if(index >= data_size) *user_name_len = 0; else if((uint8_t )temp_start_ptr[index] >> SHIFT_BITS_REFERENCE_PTR == PATTERN_REFERENCE_PTR) pattern_length = REFERENCE_PTR_LENGTH; } /* Add reference pointer bytes */ if ( index+ pattern_length < data_size) *resp_endptr = start_ptr + index+ pattern_length; else return -1; if(*user_name_len > 0) return 1; else return 0; } /* Input to this Function is pkt and size */ /* Processing: 1. Parses Multiple MDNS response packet */ /* 2. Calls the function which scans for pattern to identify the user */ /* 3. Calls the function which does the Username reporting along with the host */ /*MDNS User Analysis*/ static int MDNSUserAnalyser(tAppIdData *flowp, const SFSnortPacket *pkt, uint16_t size, const tAppIdConfig *pConfig) { const char *query_val; const char *answers; char user_name[MAX_LENGTH_SERVICE_NAME] = ""; char *user_name_bkp =NULL; const char *resp_endptr; const char *srv_original; const char *end_srv_original; const char *user_original; int query_val_int; int ans_count = 0; int start_index =0; int processed_ans =0; uint16_t data_len = 0; uint8_t *data_len_str; uint8_t user_name_len = 0; uint16_t data_size = size; /* Scan for MDNS response, decided on Query value */ query_val = (char *)pkt->payload + QUERY_OFFSET ; query_val_int = (short)(query_val[0]<payload + ANSWER_OFFSET; ans_count = (short) (answers[0]<< SHIFT_BITS | (answers[1] )); if ( query_val_int ==0) { srv_original = (char *)pkt->payload + RECORD_OFFSET ; mdnsMatchListCreate(srv_original, size-RECORD_OFFSET, pConfig); end_srv_original = (char *)pkt->payload + RECORD_OFFSET+data_size ; for(processed_ans =0; processed_ans< ans_count && data_size <= size && size > 0; processed_ans++ ) { /* Call Decode Reference pointer function if referenced value instead of direct value */ user_name_len = 0; int ret_value = ReferencePointer(srv_original, &resp_endptr, &start_index , data_size, &user_name_len, size, pConfig); int user_index =0; int user_printable_index =0; if (ret_value == -1) { return -1; } else if(ret_value) { while(start_index < data_size && (!isprint(srv_original[start_index]) || srv_original[start_index] == '"' || srv_original[start_index] =='\'')) { start_index++ ; user_index++; } user_name_len -=user_index; memcpy( user_name, srv_original+start_index , user_name_len ); user_name[user_name_len] = '\0'; user_index =0; while(user_index < user_name_len) { if(!isprint( user_name[user_index])) { return 1; } user_index++; } AppIdAddUser(flowp, user_name, APP_ID_MDNS, 1); break; } /* Find the length to Jump to the next response */ if((resp_endptr + NEXT_MESSAGE_OFFSET ) < (srv_original + data_size)) { data_len_str = (uint8_t *)(resp_endptr+ LENGTH_OFFSET); data_len = 0; data_len = (short) ( data_len_str[0]<< SHIFT_BITS | ( data_len_str[1] )); data_size = data_size - (resp_endptr + NEXT_MESSAGE_OFFSET + data_len - srv_original); /* Check if user name is available in the Domain Name field */ if(data_size < size) { if(memcmp(resp_endptr , SRV_RECORD, sizeof(SRV_RECORD)-1)==0) start_index = SRV_RECORD_OFFSET; else start_index =0; srv_original = resp_endptr + NEXT_MESSAGE_OFFSET; user_original = (char *)memchr((const uint8_t *)srv_original , PATTERN_USERNAME_1 , data_len ); if (user_original ) { user_name_len = user_original - srv_original - start_index; user_name_bkp = (char *) (srv_original + start_index); /* Non-Printable characters in the begining */ while(user_index < user_name_len) { if(isprint( user_name_bkp[user_index])) { break; } user_index++; } user_printable_index = user_index; /* Non-Printable characters in the between */ while(user_printable_index < user_name_len) { if(!isprint( user_name_bkp [user_printable_index ])) { return 0; } user_printable_index++; } /* Copy the user name if available */ if (( user_name_len - user_index ) < MAX_LENGTH_SERVICE_NAME ) { memcpy( user_name, user_name_bkp + user_index , user_name_len - user_index ); user_name[ user_name_len - user_index ] = '\0'; AppIdAddUser(flowp, user_name, APP_ID_MDNS, 1); return 1; } else return 0; } srv_original = srv_original + data_len; if(srv_original > end_srv_original) return 0; } else { return 0; } } else { return 0; } } } else return 0; return 1; } static int MDNS_validate(ServiceValidationArgs* args) { ServiceMDNSData *fd; int ret_val; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; uint16_t size = args->size; fd = mdns_service_mod.api->data_get(flowp, mdns_service_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return SERVICE_ENOMEM; if (mdns_service_mod.api->data_add(flowp, fd, mdns_service_mod.flow_data_index, &free)) { free(fd); return SERVICE_ENOMEM; } fd->state = MDNS_STATE_CONNECTION; } if(pkt->dst_port == MDNS_PORT || pkt->src_port == MDNS_PORT ) { ret_val = MDNS_validate_reply(data, size); if (ret_val == 1) { if (appidStaticConfig->mdns_user_reporting) { ret_val = MDNSUserAnalyser(flowp, pkt , size, args->pConfig); mdnsMatchListDestroy(args->pConfig); goto success; } goto success; } else goto fail; } else goto fail; success: mdns_service_mod.api->add_service(flowp, pkt, args->dir, &svc_element, APP_ID_MDNS, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: mdns_service_mod.api->fail_service(flowp, pkt, args->dir, &svc_element, mdns_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } static MatchedPatterns *patternFreeList; static tMdnsPattern patterns[] = { {(uint8_t *)PATTERN_STR_LOCAL_1, sizeof(PATTERN_STR_LOCAL_1)}, {(uint8_t *)PATTERN_STR_LOCAL_2, sizeof(PATTERN_STR_LOCAL_2)}, {(uint8_t *)PATTERN_STR_ARPA_1, sizeof(PATTERN_STR_ARPA_1)}, {(uint8_t *)PATTERN_STR_ARPA_2, sizeof(PATTERN_STR_ARPA_2)}, }; static int mdnsMatcherCreate(tAppIdConfig *pConfig) { unsigned i; tMdnsConfig *pMdnsConfig = calloc(1, sizeof(*pMdnsConfig)); if (!pMdnsConfig) return 0; if (!(pMdnsConfig->mdnsMatcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) { free(pMdnsConfig); return 0; } for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.searchAPI->search_instance_add_ex(pMdnsConfig->mdnsMatcher, (char *)patterns[i].pattern, patterns[i].length, &patterns[i], STR_SEARCH_CASE_INSENSITIVE); } _dpd.searchAPI->search_instance_prep(pMdnsConfig->mdnsMatcher); AppIdAddGenericConfigItem(pConfig, svc_element.name, pMdnsConfig); return 1; } static void mdnsMatcherDestroy(tAppIdConfig *pConfig) { tMdnsConfig *pMdnsConfig = AppIdFindGenericConfigItem(pConfig, svc_element.name); MatchedPatterns *node; if (pMdnsConfig->mdnsMatcher) _dpd.searchAPI->search_instance_free(pMdnsConfig->mdnsMatcher); pMdnsConfig->mdnsMatcher = NULL; mdnsMatchListDestroy(pConfig); while ((node = patternFreeList)) { patternFreeList = node->next; free(node); } free(pMdnsConfig); AppIdRemoveGenericConfigItem(pConfig, svc_element.name); } static int mdns_pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { MatchedPatterns *cm; MatchedPatterns **matches = (MatchedPatterns **)data; tMdnsPattern *target = (tMdnsPattern *)id; MatchedPatterns *element; MatchedPatterns *prevElement; if (patternFreeList) { cm = patternFreeList; patternFreeList = cm->next; } else { cm = malloc(sizeof(*cm)); } if (!cm) return 1; cm->mpattern = target; cm->index = index; for (prevElement = NULL, element = *matches; element; prevElement = element, element = element->next) { if (element->index > index) break; } if (prevElement) { cm->next = prevElement->next; prevElement->next = cm; } else { cm->next = *matches; *matches = cm; } return 0; } static unsigned mdnsMatchListCreate(const char *data, uint16_t dataSize, const tAppIdConfig *pConfig) { tMdnsConfig *pMdnsConfig = AppIdFindGenericConfigItem((tAppIdConfig *)pConfig, svc_element.name); if (pMdnsConfig->patternList) mdnsMatchListDestroy(pConfig); _dpd.searchAPI->search_instance_find_all(pMdnsConfig->mdnsMatcher, (char *)data, dataSize, 0, mdns_pattern_match, (void *)&pMdnsConfig->patternList); if (pMdnsConfig->patternList) return 1; return 0; } static void mdnsMatchListFind(const char *dataPtr, uint16_t index, const char **resp_endptr, int *pattern_length, const tAppIdConfig *pConfig) { tMdnsConfig *pMdnsConfig = AppIdFindGenericConfigItem((tAppIdConfig *)pConfig, svc_element.name); while (pMdnsConfig->patternList) { if (pMdnsConfig->patternList->index == index) { *resp_endptr = dataPtr+pMdnsConfig->patternList->index-index; *pattern_length = pMdnsConfig->patternList->mpattern->length; return; } if (pMdnsConfig->patternList->index > index) break; MatchedPatterns *element; element = pMdnsConfig->patternList; pMdnsConfig->patternList = pMdnsConfig->patternList->next; element->next = patternFreeList; patternFreeList = element; } *resp_endptr = NULL; *pattern_length = 0; } static void mdnsMatchListDestroy(const tAppIdConfig *pConfig) { MatchedPatterns *element; tMdnsConfig *pMdnsConfig = AppIdFindGenericConfigItem((tAppIdConfig *)pConfig, svc_element.name); while (pMdnsConfig->patternList) { element = pMdnsConfig->patternList; pMdnsConfig->patternList = pMdnsConfig->patternList->next; element->next = patternFreeList; patternFreeList = element; } } static void MDNS_clean(const CleanServiceAPI * const clean_api) { mdnsMatcherDestroy(clean_api->pAppidConfig); } snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_rfb.h0000644000175000017500000000205414241075535025162 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_RFB_H__ #define __SERVICE_RFB_H__ #include "service_api.h" extern tRNAServiceValidationModule rfb_service_mod; #endif /* __SERVICE_RFB_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/service_plugins/service_bit.c0000644000175000017500000001335414241075475025172 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "flow.h" #include "service_api.h" static const char svc_name[] = "bt"; static const uint8_t BIT_BANNER[] = "\023BitTorrent protocol"; #define BIT_PORT 6881 #define BIT_BANNER_LEN (sizeof(BIT_BANNER)-1) #define RES_LEN 8 #define SHA_LEN 20 #define PEER_ID_LEN 20 #define LAST_BANNER_OFFSET (BIT_BANNER_LEN+RES_LEN+SHA_LEN+PEER_ID_LEN - 1) typedef enum { BIT_STATE_BANNER, BIT_STATE_BANNER_DC, BIT_STATE_MESSAGE_LEN, BIT_STATE_MESSAGE_DATA } BITState; typedef struct _SERVICE_BIT_DATA { BITState state; unsigned stringlen; unsigned pos; union { uint32_t len; uint8_t raw_len[4]; }l; } ServiceBITData; #pragma pack(1) typedef struct _SERVICE_BIT_MSG { uint32_t len; uint8_t code; } ServiceBITMsg; #pragma pack() static int bit_init(const InitServiceAPI * const init_api); static int bit_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &bit_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "bit", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&bit_validate, BIT_PORT, IPPROTO_TCP}, {&bit_validate, BIT_PORT+1, IPPROTO_TCP}, {&bit_validate, BIT_PORT+2, IPPROTO_TCP}, {&bit_validate, BIT_PORT+3, IPPROTO_TCP}, {&bit_validate, BIT_PORT+4, IPPROTO_TCP}, {&bit_validate, BIT_PORT+5, IPPROTO_TCP}, {&bit_validate, BIT_PORT+6, IPPROTO_TCP}, {&bit_validate, BIT_PORT+7, IPPROTO_TCP}, {&bit_validate, BIT_PORT+8, IPPROTO_TCP}, {NULL, 0, 0} }; SF_SO_PUBLIC tRNAServiceValidationModule bit_service_mod = { svc_name, &bit_init, pp }; static tAppRegistryEntry appIdRegistry[] = {{APP_ID_BITTORRENT, 0}}; static int bit_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&bit_validate, IPPROTO_TCP, (const uint8_t *) BIT_BANNER, sizeof(BIT_BANNER)-1, 0, svc_name, init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&bit_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int bit_validate(ServiceValidationArgs* args) { ServiceBITData *ss; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; uint16_t offset; if (!size) goto inprocess; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; ss = bit_service_mod.api->data_get(flowp, bit_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return SERVICE_ENOMEM; if (bit_service_mod.api->data_add(flowp, ss, bit_service_mod.flow_data_index, &free)) { free(ss); return SERVICE_ENOMEM; } ss->state = BIT_STATE_BANNER; } offset = 0; while(offset < size) { switch (ss->state) { case BIT_STATE_BANNER: if(data[offset] != BIT_BANNER[ss->pos]) goto fail; if(ss->pos == BIT_BANNER_LEN-1) ss->state = BIT_STATE_BANNER_DC; ss->pos++; break; case BIT_STATE_BANNER_DC: if(ss->pos == LAST_BANNER_OFFSET) { ss->pos = 0; ss->state = BIT_STATE_MESSAGE_LEN; break; } ss->pos++; break; case BIT_STATE_MESSAGE_LEN: ss->l.raw_len[ss->pos] = data[offset]; ss->pos++; if(ss->pos >= offsetof(ServiceBITMsg , code)) { ss->stringlen = ntohl(ss->l.len) ; ss->state = BIT_STATE_MESSAGE_DATA; if(!ss->stringlen) { if(offset == size-1) goto success; goto fail; } ss->pos = 0; } break; case BIT_STATE_MESSAGE_DATA: ss->pos++; if(ss->pos == ss->stringlen) goto success; break; default: goto fail; } offset++; } inprocess: bit_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: bit_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_BITTORRENT, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: bit_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, bit_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/appid/appId.h0000644000175000017500000006402614241075340020526 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __APP_ID_H__ #define __APP_ID_H__ #include #include #include "thirdparty_appid_types.h" #include "sf_dynamic_preprocessor.h" typedef enum { APP_ID_UNKNOWN = -1, /*searched and not found any matching app id */ APP_ID_NONE = 0, /*AppId not searched */ APP_ID_3COM_TSMUX=2, APP_ID_8021Q=3, APP_ID_914CG=4, APP_ID_ACA_SERVICES=5, APP_ID_ACI=6, APP_ID_ACR_NEMA=7, APP_ID_ACTIVE_DIRECTORY=8, APP_ID_ACTIVESYNC=9, APP_ID_AD_BACKUP=10, APP_ID_AD_DRS=11, APP_ID_AD_DSAOP=12, APP_ID_AD_DSROL=13, APP_ID_AD_NSP=14, APP_ID_ADOBE=15, APP_ID_AD_RESTORE=16, APP_ID_ADRIVE=17, APP_ID_AD_XDS=18, APP_ID_AED512=19, APP_ID_AFP=20, APP_ID_AH=21, APP_ID_AJP=22, APP_ID_ALIAS=23, APP_ID_AMAZON=24, APP_ID_ANET=25, APP_ID_ANSA_NOTIFY=26, APP_ID_ANSA_REX_TRADER=27, APP_ID_APPLE_ARP=28, APP_ID_APPLEJUICE=29, APP_ID_APPLESHARE=30, APP_ID_APPLETALK=31, APP_ID_APPLE_UPDATE=32, APP_ID_ARCISDMS=33, APP_ID_ARIEL=34, APP_ID_ARNS=35, APP_ID_ARP=36, APP_ID_ASA=37, APP_ID_ASTRAWEB=38, APP_ID_ATM_FATE=39, APP_ID_ATM_MPOA=40, APP_ID_AUDITD=41, APP_ID_AUDIT=42, APP_ID_AURORA=43, APP_ID_AVG=44, APP_ID_AVIRA=45, APP_ID_AVOCENT=46, APP_ID_BACKBLAZE=47, APP_ID_BACKPACK=48, APP_ID_BATTLEFIELD=49, APP_ID_BATTLE_NET=50, APP_ID_BEETPH=51, APP_ID_BFTP=52, APP_ID_BGMP=53, APP_ID_BH611=54, APP_ID_BHEVENT=55, APP_ID_BHFHS=56, APP_ID_BHMDS=57, APP_ID_BING=58, APP_ID_BITDEFENDER=59, APP_ID_BITS=60, APP_ID_BITTORRENT=61, APP_ID_BLACKBOARD=62, APP_ID_BLACKJACK=63, APP_ID_BLAZEFS=64, APP_ID_BLIDM=65, APP_ID_BNET=66, APP_ID_CABLEPORT_AX=67, APP_ID_CAICCI=68, APP_ID_CAILIC=69, APP_ID_CAP=70, APP_ID_CDC=71, APP_ID_CFDPTKT=72, APP_ID_CHARGEN=73, APP_ID_CHECK_POINT=74, APP_ID_CISCO_DRP=76, APP_ID_CISCO_FNATIVE=77, APP_ID_CISCO_GDP=78, APP_ID_CISCO_SLA=79, APP_ID_CISCO_SYSMAINT=80, APP_ID_CISCO_TNATIVE=81, APP_ID_CITRIX_CGP=82, APP_ID_CITRIX_ICA=83, APP_ID_CITRIX_IMA=84, APP_ID_CITRIX_JEDI=85, APP_ID_CITRIX_LICENSING=86, APP_ID_CITRIX_ONLINE=87, APP_ID_CITRIX_RTMP=88, APP_ID_CITRIX_SLG=89, APP_ID_CITRIX_WANSCALER=90, APP_ID_CL1=91, APP_ID_CLEARCASE=92, APP_ID_CLOANTO=93, APP_ID_CMIP=94, APP_ID_CODA_AUTH=95, APP_ID_COMMVAULT=96, APP_ID_COMPRESSNET=97, APP_ID_COMSCM=98, APP_ID_CORBA=99, APP_ID_CORERJD=100, APP_ID_COVIA_CI=101, APP_ID_CSISGWP=102, APP_ID_CSNET_NS=103, APP_ID_CTF=104, APP_ID_CVCHOSTD=105, APP_ID_DASP=106, APP_ID_DATEX_ASN=107, APP_ID_DBASE=108, APP_ID_DCAP=109, APP_ID_DCCP=110, APP_ID_DCP=111, APP_ID_DEC_AUTH=112, APP_ID_DEC_DEBUG=113, APP_ID_DECVMS=114, APP_ID_DEOS=115, APP_ID_DHCPV6=116, APP_ID_DIGG=117, APP_ID_DIRECT_CONNECT=118, APP_ID_DIRECT=119, APP_ID_DIXIE=120, APP_ID_DLS=121, APP_ID_DNA_CML=122, APP_ID_DNSIX=123, APP_ID_DPSI=124, APP_ID_DROPBOX=125, APP_ID_DSFGW=126, APP_ID_DSP3270=127, APP_ID_DSP=128, APP_ID_DSSETUP=129, APP_ID_DTAG=130, APP_ID_DTK=131, APP_ID_EBAY=132, APP_ID_EBAY_BID=133, APP_ID_EBAY_SEARCH=134, APP_ID_EBAY_WATCH=135, APP_ID_EBUDDY=136, APP_ID_EGP=137, APP_ID_EMBLNDT=138, APP_ID_EMFIS=139, APP_ID_ENTRUSTTIME=140, APP_ID_EPMAP=141, APP_ID_ERPC=142, APP_ID_ESET=143, APP_ID_ESP=144, APP_ID_ESRO=145, APP_ID_ETH=146, APP_ID_ETOS=147, APP_ID_SAFARI_MOBILE_DUMMY=148, APP_ID_EXCHANGE=1780, APP_ID_FACEBOOK_APPS=149, APP_ID_FARK=150, APP_ID_FARMVILLE=151, APP_ID_FASP=152, APP_ID_FASTTRACK=153, APP_ID_FATMEN=154, APP_ID_FILEMAKER=155, APP_ID_FILER_CX=156, APP_ID_FILESTUBE=157, APP_ID_FLASHGET=158, APP_ID_FLICKR=159, APP_ID_FLIXSTER=160, APP_ID_FOGBUGZ=161, APP_ID_F_PROT=162, APP_ID_FREECAST=163, APP_ID_FRIENDFEED=164, APP_ID_FTP_CONTROL=165, APP_ID_FTP_DATA=166, APP_ID_FTPSDATA=167, APP_ID_FTPS=168, APP_ID_FXP=169, APP_ID_GACP=170, APP_ID_GANGLIA=171, APP_ID_GENESIS_PPP=172, APP_ID_GENIE=173, APP_ID_GENRAD=174, APP_ID_GIGANEWS=175, APP_ID_GIOP=176, APP_ID_GIST=177, APP_ID_GOOGLE_APIS=178, APP_ID_GOOGLE_APP_ENGINE=179, APP_ID_GOOGLE_DOCS=180, APP_ID_GOOGLE_TALK_GADGET=182, APP_ID_GOOGLE_TALK=183, APP_ID_GOOGLE=184, APP_ID_GOOGLE_TRANSLATE=185, APP_ID_GOOGLE_VIDEO=186, APP_ID_GOTOMEETING=187, APP_ID_GPFS=188, APP_ID_GRE=189, APP_ID_GROUPWISE=190, APP_ID_GSIFTP=191, APP_ID_GSS_LICENSE=192, APP_ID_H_225=193, APP_ID_H_245=194, APP_ID_H_248=195, APP_ID_H_323=196, APP_ID_HASSLE=197, APP_ID_HDAP=198, APP_ID_HEMS=199, APP_ID_HIVESTOR=200, APP_ID_HL7=201, APP_ID_HOPSTER=202, APP_ID_HOSTNAME=203, APP_ID_HOTFILE=204, APP_ID_HOTMAIL=205, APP_ID_HP_PERF=206, APP_ID_HP_VMM=207, APP_ID_HTTP_AUDIO=208, APP_ID_HTTPMGT=209, APP_ID_HTTP_VIDEO=210, APP_ID_HYPER_G=211, APP_ID_IASD=212, APP_ID_IBM_OPC=213, APP_ID_ICA_BROWSER=214, APP_ID_ICAD=215, APP_ID_ICAP=216, APP_ID_ICA=217, APP_ID_ICESHARE=218, APP_ID_ICP=221, APP_ID_ICQ2GO=222, APP_ID_IDP=223, APP_ID_IGMP=224, APP_ID_IKE=225, APP_ID_IMGAMES=226, APP_ID_IMSP=227, APP_ID_INBUSINESS=228, APP_ID_INFORMIX=229, APP_ID_INFOSEEK=230, APP_ID_INFOSTORE=231, APP_ID_INGRES_NET=232, APP_ID_IPCOMP=233, APP_ID_IPIP=234, APP_ID_IP=235, APP_ID_IPSEC=236, APP_ID_IPV6=237, APP_ID_IPX=238, APP_ID_IRC=239, APP_ID_IRCU=240, APP_ID_IS_99=241, APP_ID_ISAKMP=242, APP_ID_ISCHAT=243, APP_ID_ISI_GRAPHICS=244, APP_ID_ISOIP=245, APP_ID_ISO_TSAP=246, APP_ID_JARGON=247, APP_ID_KASPERSKY=248, APP_ID_KBLOCK=249, APP_ID_KFTPDATA=250, APP_ID_KFTP=251, APP_ID_KIS=252, APP_ID_KNETCMP=253, APP_ID_KRYPTOLAN=254, APP_ID_KTELNET=255, APP_ID_KUGOO=256, APP_ID_KVM=257, APP_ID_KWDB=258, APP_ID_L2TP=259, APP_ID_LA_MAINT=260, APP_ID_LAST_FM=261, APP_ID_LEGENT=262, APP_ID_LINK=263, APP_ID_LIVE365=264, APP_ID_LIVEMEETING=265, APP_ID_LIVESTATION=266, APP_ID_LLMNR=267, APP_ID_LOCUS_CONN=268, APP_ID_LOCUS_MAP=269, APP_ID_LOGMEIN=270, APP_ID_LSARPC=271, APP_ID_MAFIAWARS=272, APP_ID_MAGENTA_LOGIC=273, APP_ID_MAGICJACK=274, APP_ID_MAILQ=275, APP_ID_MANET=276, APP_ID_MAPI=277, APP_ID_MASQDIALER=278, APP_ID_MATIP=279, APP_ID_MCAFEE=280, APP_ID_MC_FTP=281, APP_ID_MCIDAS=282, APP_ID_MCK_IVPIP=283, APP_ID_MEDIAFIRE=285, APP_ID_MEEBO=286, APP_ID_MEETING_MAKER=287, APP_ID_META5=288, APP_ID_METAGRAM=289, APP_ID_MF_COBOL=290, APP_ID_MFTP=291, APP_ID_MINI_SQL=292, APP_ID_MIT_ML_DEV=293, APP_ID_MIT_SPOOLER=294, APP_ID_MIXI=295, APP_ID_MOBILEIP=296, APP_ID_MORTGAGEWARE=297, APP_ID_MPLS_MULTICAST=298, APP_ID_MPLS_UNICAST=299, APP_ID_MPM=300, APP_ID_MPP=301, APP_ID_MPTN=302, APP_ID_MS_CRS=303, APP_ID_MSDN=304, APP_ID_MSG=305, APP_ID_MSMQ=306, APP_ID_MSNP=307, APP_ID_MSN=308, APP_ID_MS_OLAP=309, APP_ID_MS_ONLINE=310, APP_ID_MSP=311, APP_ID_MS_SQL=312, APP_ID_MTA=313, APP_ID_MULTIPLEX=314, APP_ID_MUMPS=315, APP_ID_MYSPACE_CHAT=316, APP_ID_MYSPACE=317, APP_ID_NAMP=318, APP_ID_NAPSTER=319, APP_ID_NCED=320, APP_ID_NCLD=321, APP_ID_NDS_AUTH=322, APP_ID_NETBIOS=323, APP_ID_NETINFO=324, APP_ID_NETLOGON=325, APP_ID_NETMEETING=326, APP_ID_NETSC=327, APP_ID_NETSCOUT=328, APP_ID_NETWARE=329, APP_ID_NFA=330, APP_ID_NFS=331, APP_ID_NI_FTP=332, APP_ID_NI_MAIL=333, APP_ID_NIP=334, APP_ID_NNSP=335, APP_ID_NOVABACKUP=336, APP_ID_NPP=337, APP_ID_NSIIOPS=338, APP_ID_NSRMP=339, APP_ID_NSS=340, APP_ID_NSSTP=341, APP_ID_NXEDIT=342, APP_ID_NXTSTEP=343, APP_ID_OCBINDER=344, APP_ID_OCSERVER=345, APP_ID_OCS=346, APP_ID_ODMR=347, APP_ID_OFTP=348, APP_ID_OFTPS=349, APP_ID_ONMUX=350, APP_ID_OPALIS_ROBOT=351, APP_ID_OPENPORT=352, APP_ID_OPENVPN=353, APP_ID_ORACLE_SQLNET=355, APP_ID_ORKUT=356, APP_ID_OSCAR=357, APP_ID_OSUNMS=358, APP_ID_PANDA=359, APP_ID_PARTYPOKER=360, APP_ID_PAWSERV=361, APP_ID_PCMAIL=362, APP_ID_PDAP=363, APP_ID_PERSONALLINK=364, APP_ID_PFTP=365, APP_ID_PIM=366, APP_ID_PIP=367, APP_ID_PKIX_TIMESTAMP=368, APP_ID_PLAXO=369, APP_ID_POP2=370, APP_ID_PPLIVE=371, APP_ID_PPP_DISCOVERY=372, APP_ID_PPP_SESSION=373, APP_ID_PPSTREAM=374, APP_ID_PPTP=375, APP_ID_PRINTSRV=376, APP_ID_PROFILE=377, APP_ID_PROSPERO=378, APP_ID_PTP=379, APP_ID_PUP=380, APP_ID_PWDGEN=381, APP_ID_QBIK=382, APP_ID_QFT=383, APP_ID_QMTP=384, APP_ID_QOTD=385, APP_ID_QQ=386, APP_ID_QUICKTIME=387, APP_ID_RAP=388, APP_ID_RARP=389, APP_ID_REMAIL=390, APP_ID_REMOTE_JOB_SERVICE=391, APP_ID_REMOTE_TELNET=392, APP_ID_RESCAP=393, APP_ID_RFR=394, APP_ID_RIP=395, APP_ID_RIS=396, APP_ID_RJE=397, APP_ID_RLOGIN=398, APP_ID_RLP=399, APP_ID_RMT=400, APP_ID_RPC2PMAP=401, APP_ID_RRP=402, APP_ID_RSH=403, APP_ID_RSVD=404, APP_ID_RSVP=405, APP_ID_RSVP_TUNNEL=406, APP_ID_RTCP=407, APP_ID_RTSPS=408, APP_ID_SAMR=409, APP_ID_SAP_HOSTCONTROL=410, APP_ID_SBNTBCST=411, APP_ID_SCOI2DLG=412, APP_ID_SCSI_ST=413, APP_ID_SCTP=414, APP_ID_SECOND_LIFE=415, APP_ID_SECURSIGHT=416, APP_ID_SEMANTIX=417, APP_ID_SEND=418, APP_ID_SET=419, APP_ID_SFTP=420, APP_ID_SGCP=421, APP_ID_SGMP=422, APP_ID_SHAREPOINT=423, APP_ID_SHRINKWRAP=424, APP_ID_SILVERPLATTER=425, APP_ID_SIP=426, APP_ID_SKYPE_AUTH=428, APP_ID_SKYPE_OUT=429, APP_ID_SKYPE_P2P=430, APP_ID_SKYPE_PROBE=431, APP_ID_SLINGBOX=432, APP_ID_SMAKYNET=433, APP_ID_SMART_SDP=434, APP_ID_SMPTE=435, APP_ID_SMSP=436, APP_ID_SMUX=437, APP_ID_SNA_GATEWAY=438, APP_ID_SNET=439, APP_ID_SNPP=440, APP_ID_SOFTPC=441, APP_ID_SOULSEEK=442, APP_ID_SQL_SERVICES=443, APP_ID_SRC=444, APP_ID_SRMP=445, APP_ID_SRS_SEND=446, APP_ID_SSDP=447, APP_ID_STATIONLAUNCHER=448, APP_ID_STATSRV=449, APP_ID_STORE_ADMIN=450, APP_ID_SU_MIT_TELNET=451, APP_ID_SUN_RPC=452, APP_ID_SUPDUP=453, APP_ID_SUPERNEWS=454, APP_ID_SURMEAS=455, APP_ID_SVRLOC=456, APP_ID_SWIFT_RVFP=457, APP_ID_SYBASE_SQL=458, APP_ID_SYMANTEC_SYSTEM_CENTER=459, APP_ID_SYNOPTICS=460, APP_ID_SYSATT=461, APP_ID_SYSLOG=462, APP_ID_SYSTAT=463, APP_ID_TACACS=464, APP_ID_TAC_NEWS=465, APP_ID_TCPMUX=466, APP_ID_TCP=467, APP_ID_TEXAR=468, APP_ID_TFTPS=469, APP_ID_TIME=470, APP_ID_TMOBILE=471, APP_ID_TOBIT=472, APP_ID_TOR=473, APP_ID_TRIPWIRE=474, APP_ID_TUMBLR=475, APP_ID_UAAC=476, APP_ID_UARPS=477, APP_ID_UC4=478, APP_ID_UDP=479, APP_ID_UIS=480, APP_ID_ULSTPROC=481, APP_ID_UMA=482, APP_ID_UNICENTER=483, APP_ID_UNIDATA_LDM=484, APP_ID_UNIFY=485, APP_ID_UPS=486, APP_ID_USENET=487, APP_ID_UTMP=489, APP_ID_UUCP=490, APP_ID_VCHAT=491, APP_ID_VETTCP=492, APP_ID_VMNET=493, APP_ID_VMPWSCS=494, APP_ID_VONAGE=495, APP_ID_VSLMP=496, APP_ID_VUZE=497, APP_ID_WCCP=498, APP_ID_WEBFILTER=499, APP_ID_WEBLOGIC=500, APP_ID_WIKIPEDIA=501, APP_ID_WINDOWS_LIVE=502, APP_ID_WINDOWS_MEDIA=503, APP_ID_WINNY=504, APP_ID_WINS=505, APP_ID_WORDPRESS=506, APP_ID_WORLD_OF_WARCRAFT=507, APP_ID_X_224=508, APP_ID_X_25=509, APP_ID_XANGA=510, APP_ID_XBONE=511, APP_ID_XBOX_LIVE=512, APP_ID_XDMCP=513, APP_ID_XFER=514, APP_ID_XMPP=515, APP_ID_XNS_AUTHENTICATION=516, APP_ID_XNS_CLEARINGHOUSE=517, APP_ID_XNS_MAIL=518, APP_ID_XNS_TIME=519, APP_ID_XNS=520, APP_ID_XYPLEX=521, APP_ID_YAHOO_GAMES=522, APP_ID_YAHOO_MSG_FILE_TRANSFER=523, APP_ID_YAHOO=524, APP_ID_Z3950=525, APP_ID_ZANNET=526, APP_ID_ZEBRA=527, APP_ID_ZOHO=528, APP_ID_ZOHO_CHAT=529, APP_ID_ZOHO_MAIL=530, APP_ID_ZOHO_SHARE=531, APP_ID_ZOHO_WIKI=532, APP_ID_ZYNGA=533, APP_ID_ZYNGA_POKER=534, APP_ID_1_800_FLOWERS=535, APP_ID_100BAO=536, APP_ID_2CHANNEL=537, APP_ID_6_PM=538, APP_ID_ACE_HARDWARE_CORPORATION=539, APP_ID_ADDICTING_GAMES=540, APP_ID_ADOBE_UPDATE=541, APP_ID_ADORAMA=542, APP_ID_AIM_EXPRESS=543, APP_ID_AMERICAN_EXPRESS=544, APP_ID_ANDROID_BROWSER=545, APP_ID_AOL_EMAIL=546, APP_ID_AOL_INSTANT_MESSENGER=547, APP_ID_AOL_SOFTWARE=549, APP_ID_APPLE_EMAIL=550, APP_ID_APPLE_STORE=551, APP_ID_ARCSERVE=552, APP_ID_ARES=553, APP_ID_ARGOS=554, APP_ID_ATOM=555, APP_ID_ATOM_COM=556, APP_ID_AUTOBLOG=557, APP_ID_AUTOTRADER_COM=558, APP_ID_B_H_PHOTO_VIDEO=559, APP_ID_BANK_OF_AMERICA=560, APP_ID_BARNES_AND_NOBLE=561, APP_ID_BARNEYS_NEW_YORK=562, APP_ID_BASECAMP=563, APP_ID_BATTLENET=564, APP_ID_BEARSHARE=565, APP_ID_BEBO=566, APP_ID_BEST_BUY=567, APP_ID_BEWEEVEE=568, APP_ID_BGP=569, APP_ID_BITTORRENT_CLIENT=570, APP_ID_BITTRACKER_CLIENT=571, APP_ID_BLACK_DECKER_CORPORATION=572, APP_ID_BLACKBERRY_BROWSER=573, APP_ID_BLIP_TV=574, APP_ID_BLOCKBUSTER=575, APP_ID_BLOGGER=576, APP_ID_BLOOMINGDALES=577, APP_ID_BLUE_NILE=578, APP_ID_BLUEFLY=579, APP_ID_BOX_NET=580, APP_ID_CAMERASDIRECT_COM_AU=581, APP_ID_CAPITAL_ONE=582, APP_ID_CAR_AND_DRIVER=583, APP_ID_CARMAX=584, APP_ID_CDISCOUNT=585, APP_ID_CHARACTER_GENERATOR=586, APP_ID_CHASE=587, APP_ID_CHEAPTICKETS=588, APP_ID_CHROME=589, APP_ID_CITI=590, APP_ID_CITY_SPORTS=591, APP_ID_COLLABEDIT=592, APP_ID_COSTCO=593, APP_ID_CRAIGSLIST=594, APP_ID_CRUTCHFIELD=595, APP_ID_CURL=596, APP_ID_CVS=597, APP_ID_CVS_PSERVER=598, APP_ID_DAAP=599, APP_ID_DAILYMOTION=600, APP_ID_DAVID_JONES=601, APP_ID_DB2=602, APP_ID_DCE_RPC=603, APP_ID_DEALS_DIRECT=604, APP_ID_DELICIOUS=605, APP_ID_DELL=606, APP_ID_DESTRUCTOID=607, APP_ID_DEVIANTART=608, APP_ID_DHCP=609, APP_ID_DHCPV6_SERVER=610, APP_ID_DICKS_SPORTING_GOODS=611, APP_ID_DIIGO=612, APP_ID_DILLARDS=613, APP_ID_DISCARD=614, APP_ID_DISCOVER=615, APP_ID_DNP3=616, APP_ID_DNS=617, APP_ID_DRDA=618, APP_ID_DROPBEAR=619, APP_ID_DRUGSTORE_COM=620, APP_ID_E_TRADE=621, APP_ID_EDMUNDS_COM=622, APP_ID_EDONKEY=623, APP_ID_EUDORA=624, APP_ID_EUDORA_PRO=625, APP_ID_EVOLUTION=626, APP_ID_EXEC=627, APP_ID_EXPEDIA=628, APP_ID_FACEBOOK=629, APP_ID_FACEBOOK_CHAT=630, APP_ID_FACEBOOK_COMMENT=631, APP_ID_FACEBOOK_GAME_PREMIER_FOOTBALL=632, APP_ID_FACEBOOK_READ_EMAIL=633, APP_ID_FACEBOOK_SEND_EMAIL=634, APP_ID_FACEBOOK_STATUS_UPDATE=635, APP_ID_FIDELITY=636, APP_ID_FINGER=637, APP_ID_FIREFOX=638, APP_ID_FLASH_VIDEO=639, APP_ID_FNAC=640, APP_ID_FOXY=641, APP_ID_FRIENDSTER=642, APP_ID_FRYS_ELECTRONICS=643, APP_ID_FTD=644, APP_ID_FTP=645, APP_ID_G4=646, APP_ID_GAME_INFORMER=647, APP_ID_GAMESPOT=648, APP_ID_GAMESPY=649, APP_ID_GAMESTOP=650, APP_ID_GAMETRAILERS=651, APP_ID_GAWKER=652, APP_ID_GENERIC=653, APP_ID_GIFT=654, APP_ID_GMAIL=655, APP_ID_GNUCLEUS=656, APP_ID_GNUCLEUSLAN=657, APP_ID_GNUTELLA=658, APP_ID_GNUTELLA2=659, APP_ID_GOOGLE_ANALYTICS=660, APP_ID_GOOGLE_CALENDAR=661, APP_ID_GOOGLE_DESKTOP=662, APP_ID_GOOGLE_NEWS=663, APP_ID_GOOGLE_PRODUCT_SEARCH=664, APP_ID_GOOGLE_SAFEBROWSING=665, APP_ID_GOOGLE_TOOLBAR=1146, APP_ID_GOPHER=667, APP_ID_GTK_GNUTELLA=668, APP_ID_HAIKU_LEARNING_SYSTEMS=669, APP_ID_HOME_DEPOT=670, APP_ID_HOSTNAME_SERVER=671, APP_ID_GOOGLE_EARTH=672, APP_ID_HOTLINE=673, APP_ID_HOUSE_OF_FRASER=674, APP_ID_HSBC=675, APP_ID_HTTP=676, APP_ID_HULU=677, APP_ID_IBM_APP=678, APP_ID_ICQ=679, APP_ID_IGN=680, APP_ID_ILOVEIM=681, APP_ID_IMAGESHACK=682, APP_ID_IMAP=683, APP_ID_IMGUR=684, APP_ID_IMO_IM=685, APP_ID_INTERNET_EXPLORER=686, APP_ID_IRCD=687, APP_ID_ITU_H_323=688, APP_ID_ITUNES=689, APP_ID_J_C_PENNEY=690, APP_ID_J_R=691, APP_ID_JABBER=692, APP_ID_JALOPNIK=693, APP_ID_JAVA_RMI=694, APP_ID_JIRA=695, APP_ID_JOYSTIQ=696, APP_ID_KAD=697, APP_ID_KAY_JEWELERS=698, APP_ID_KAZAA=699, APP_ID_KMAIL=700, APP_ID_KERBEROS=701, APP_ID_KMART=702, APP_ID_KOGAN_TECHNOLOGIES=703, APP_ID_KOHLS=704, APP_ID_KONGREGATE=705, APP_ID_KONQUEROR=706, APP_ID_KOTAKU=707, APP_ID_LAUNCHPAD=708, APP_ID_LBPS=709, APP_ID_LDAP=710, APP_ID_LIMELIGHT=711, APP_ID_LIMEWIRE=712, APP_ID_LINKEDIN=713, APP_ID_LINKEDIN_JOB_SEARCH=714, APP_ID_LINUXCONF=715, APP_ID_LIVEJOURNAL=716, APP_ID_LOGIN=717, APP_ID_LOKALISTEN=718, APP_ID_LORD_TAYLOR=719, APP_ID_LOTUS_NOTES=720, APP_ID_LOVEFILM=721, APP_ID_LOWES=722, APP_ID_LSH=723, APP_ID_MANOLITO=724, APP_ID_MEGACO=725, APP_ID_MEGAVIDEO=726, APP_ID_MENARDS=727, APP_ID_METACAFE=728, APP_ID_METAFILTER=729, APP_ID_MGCP=730, APP_ID_MICROSOFT_UPDATE=731, APP_ID_MICROSOFT_WINDOWS_MESSENGER=732, APP_ID_MINUS=733, APP_ID_MIXX=734, APP_ID_MMS=735, APP_ID_SAFARI_MOBILE=736, APP_ID_MODBUS=737, APP_ID_MORGAN_STANLEY=738, APP_ID_MORPHEUS=739, APP_ID_MOVENETWORKS=740, APP_ID_MP4=741, APP_ID_MPEG=742, APP_ID_MSN_MESSENGER=743, APP_ID_MSN_MESSENGER_MAC=744, APP_ID_MUTE=745, APP_ID_MUTT=746, APP_ID_MYSQL=747, APP_ID_MYUDUTU=748, APP_ID_NCP=749, APP_ID_NECKERMANN=750, APP_ID_NEIMAN_MARCUS=751, APP_ID_NESSUS=752, APP_ID_NETBIOS_DGM=753, APP_ID_NETBIOS_NS=754, APP_ID_NETBIOS_SSN=755, APP_ID_NETFLIX=756, APP_ID_NETLOG=757, APP_ID_NETVIBES=758, APP_ID_NEWEGG=759, APP_ID_NEWSNOW=760, APP_ID_NEWSVINE=761, APP_ID_NICO_NICO_DOUGA=762, APP_ID_NNTP=763, APP_ID_NORSDTROM=764, APP_ID_NSPLAYER=765, APP_ID_NTALK=766, APP_ID_NTP=767, APP_ID_OFFICE_DEPOT=768, APP_ID_OFFICEMAX=769, APP_ID_OO_COM_AU=770, APP_ID_OPENSSH=771, APP_ID_OPERATING_SYSTEM=772, APP_ID_ORACLE_DATABASE=773, APP_ID_ORACLE_TNS=774, APP_ID_ORBITZ=775, APP_ID_OUTLOOK=776, APP_ID_OUTLOOK_EXPRESS=777, APP_ID_OVERSTOCK_COM=778, APP_ID_PANDORA=779, APP_ID_PC_DUO=780, APP_ID_PCANYWHERE=781, APP_ID_PEERCAST=782, APP_ID_PEERENABLER=783, APP_ID_PHOTOBUCKET=784, APP_ID_PICASA=785, APP_ID_POCO=786, APP_ID_POGO=787, APP_ID_POP3=788, APP_ID_POPCAP_GAMES=789, APP_ID_POPURLS=790, APP_ID_POSTGRESQL=791, APP_ID_PRICELINE_COM=792, APP_ID_PROFLOWERS=793, APP_ID_PUTTY=794, APP_ID_QUAKE=795, APP_ID_QUICKFLIX=796, APP_ID_QUILL_CORPORATION=797, APP_ID_QVC=798, APP_ID_QZONE=799, APP_ID_RADIUS=800, APP_ID_RADIUS_ACCT=801, APP_ID_RAPIDSHARE=802, APP_ID_RDP=803, APP_ID_REDDIT=804, APP_ID_REDMINE=805, APP_ID_REI=806, APP_ID_REMOTE_DESKTOP_CLIENT=807, APP_ID_RENREN=808, APP_ID_REVOLVECLOTHING=809, APP_ID_RONA=810, APP_ID_RSS=811, APP_ID_RTMP=812, APP_ID_RTP=813, APP_ID_RTSP=814, APP_ID_SAFARI=815, APP_ID_SAKS_FIFTH_AVENUE=816, APP_ID_SAMS_CLUB=817, APP_ID_SCHUELERVZ=818, APP_ID_SCHWAB=819, APP_ID_SCOTTRADE=820, APP_ID_SEARS=821, APP_ID_SHAREAZA=822, APP_ID_SHELL=823, APP_ID_SHOCKWAVE=824, APP_ID_SHOPLET=825, APP_ID_SHOPNBC=826, APP_ID_SHOPPING_HP_COM=827, APP_ID_SHOPSTYLE=828, APP_ID_SHOUTCAST_RADIO=829, APP_ID_SHOWCLIX=830, APP_ID_SHOWDOCUMENT=831, APP_ID_SKYPE=832, APP_ID_SKYPE_MAC=833, APP_ID_SLASHDOT=834, APP_ID_SLOW=835, APP_ID_SMTP=836, APP_ID_SNMP=837, APP_ID_SNMP_TRAP=838, APP_ID_SOCKS=839, APP_ID_SORIBADA=840, APP_ID_SPIN_DE=841, APP_ID_SPORTS_AUTHORITY=842, APP_ID_SQL_SERVER=843, APP_ID_SQUID=844, APP_ID_SQUIRREL_EMAIL=845, /*deprecated */ APP_ID_SSH=846, APP_ID_SSL=847, APP_ID_STAPLES=848, APP_ID_STAYFRIENDS=849, APP_ID_STUBHUB=850, APP_ID_STUDIVZ=851, APP_ID_STUMBLEUPON=852, APP_ID_STUN=853, APP_ID_SWAROVSKI=854, APP_ID_T_ROWE_PRICE=855, APP_ID_TABULAR_DATA_STREAM_TDS=856, APP_ID_TALK=857, APP_ID_TARGET=858, APP_ID_TCHIBO=859, APP_ID_TD_AMERITRADE=860, APP_ID_TELNET=861, APP_ID_TFTP=862, APP_ID_THE_GAP=863, APP_ID_THE_SHARPER_IMAGE=864, APP_ID_THINKGEEK=865, APP_ID_THUNDERBIRD=866, APP_ID_TICKETMASTER=867, APP_ID_TICKETS_COM=868, APP_ID_TICKETSNOW=869, APP_ID_TIFFANY_CO=870, APP_ID_TIGER_DIRECT=871, APP_ID_TIMBUKTU=872, APP_ID_TINYPIC=873, APP_ID_TIVOLI=874, APP_ID_TN3270=875, APP_ID_TOC=876, APP_ID_TOP_GEAR=877, APP_ID_TRAC=878, APP_ID_TRACEROUTE=879, APP_ID_TRAVELOCITY=880, APP_ID_TRIPADVISOR=881, APP_ID_TWITTER=882, APP_ID_URBAN_OUTFITTERS=883, APP_ID_USTREAM_TV=884, APP_ID_VANGUARD=885, APP_ID_VCOM=886, APP_ID_VEHIX=887, APP_ID_VENTE_PRIVEE_COM=888, APP_ID_VEOH=889, APP_ID_VERIZON_EMAIL=890, APP_ID_VIADEO=891, APP_ID_VICTORIAS_SECRET=892, APP_ID_VIMEO=893, APP_ID_VNC=894, APP_ID_VNC_RFB=895, APP_ID_VNC_SERVER_RFB=896, APP_ID_VOIP_RTP=897, APP_ID_VOIP_SIP=898, APP_ID_VOYAGES_SNCF_COM=899, APP_ID_WACHOVIA=900, APP_ID_WALMART=901, APP_ID_WAV=902, APP_ID_WEB_OF_TRUST=903, APP_ID_WEBDAV=904, APP_ID_WEBEX=905, APP_ID_WEBSPHERE_MQ=906, APP_ID_WELLS_FARGO=907, APP_ID_WER_KENNT_WEN=908, APP_ID_WGET=909, APP_ID_WINDOWS_LIVE_HOTMAIL=910, APP_ID_WINDOWS_LIVE_SKYDRIVE=911, APP_ID_WINDOWS_MEDIA_PLAYER=912, APP_ID_WINMX=913, APP_ID_WIZIQ=914, APP_ID_WMA=915, APP_ID_WMV=916, APP_ID_WOOT=917, APP_ID_WX=918, APP_ID_X_FONT_SERVER=919, APP_ID_X11=920, APP_ID_XBOX=921, APP_ID_XING=922, APP_ID_XM_RADIO_ONLINE=923, APP_ID_XUNLEI=924, APP_ID_XWINDOWS=925, APP_ID_YAHOO_VOICE=926, APP_ID_YET_ABC=927, APP_ID_YOUSENDIT=928, APP_ID_YOUTUBE=929, APP_ID_ZALES=930, APP_ID_ZAPPOS=931, APP_ID_ZIP_CA=932, APP_ID_ZOOOMR=933, APP_ID_YAHOO_MSG=936, APP_ID_YAHOOMAIL=946, APP_ID_YAHOO_TOOLBAR=947, APP_ID_RSYNC=1097, APP_ID_XSCPLS=1098, APP_ID_ROBUST_MPA=1100, APP_ID_VND_WAV=1101, APP_ID_GPP=1102, APP_ID_M4V=1103, APP_ID_X_WAV=1104, APP_ID_MPA=1105, APP_ID_MP4A=1106, APP_ID_AOL_NETSCAPE=1107, APP_ID_SMTP_IMO=1108, APP_ID_DDM_SSL=1111, APP_ID_SMTPS=1112, APP_ID_NNTPS=1113, APP_ID_IMAPS=1114, APP_ID_SSHELL=1115, APP_ID_LDAPS=1116, APP_ID_TELNETS=1117, APP_ID_IRCS=1118, APP_ID_POP3S=1119, APP_ID_MSFT_GC_SSL=1120, APP_ID_SF_APPLIANCE_MGMT=1121, APP_ID_HTTPS=1122, APP_ID_SKYPE_TUNNELING=1126, APP_ID_ASPROXY=1145, APP_ID_OPERA=1288, APP_ID_SSL_CLIENT=1296, APP_ID_AOL=1419, APP_ID_MDNS=1755, APP_ID_APPLE_CORE_MEDIA=2253, APP_ID_HTTP_TUNNEL=2296, APP_ID_RTP_AUDIO=2475, APP_ID_RTP_VIDEO=2476, APP_ID_ULTRASURF=2634, APP_ID_LYCOS=2775, APP_ID_DOGPILE=2804, APP_ID_SPDY=2886, APP_ID_HTTP2=2889, // only used for some quick bookkeeping -- treat as HTTP APP_ID_ANYCONNECT=2921, APP_ID_ANYCONNECT_SSL_CLIENT=2922, APP_ID_ANYCONNECT_IPSEC_CLIENT=2923, APP_ID_ICMP=3501, APP_ID_ICMPV6=3558, APP_ID_HTTP_SSL_TUNNEL=3860, APP_ID_FTP_ACTIVE=4002, APP_ID_FTP_PASSIVE=4003, APP_ID_PSIPHON=4075, APP_ID_DNS_OVER_TLS=4615, APP_ID_ENIP=5001, APP_ID_CIP=5002, APP_ID_CIP_UNKNOWN=5003, APP_ID_CIP_MALFORMED=5005, APP_ID_UNKNOWN_UI = 65535 /*This causes the UI to render Unknown instead of pending or blank */ } appIdEnum; typedef enum { APP_ID_TYPE_SERVICE, APP_ID_TYPE_CLIENT, APP_ID_TYPE_PAYLOAD, APP_ID_TYPE_MAX } APP_ID_TYPE; #define SF_APPID_MAX 40000 #define SF_APPID_BUILDIN_MAX 30000 #define APPID_MAX_PRIORITY 3 #define SF_APPID_CSD_MIN 1000000 #define SF_APPID_DYNAMIC_MIN 2000000 #define NUMBER_OF_PTYPES 9 #define RESPONSE_CODE_PACKET_THRESHHOLD 0 //Additional stuff typedef enum { APP_ID_FROM_INITIATOR, APP_ID_FROM_RESPONDER, APP_ID_APPID_SESSION_DIRECTION_MAX /* Maximum value of a direction (must be last in the list */ } APPID_SESSION_DIRECTION; typedef enum { SERVICE_HOST_INFO_NETBIOS_NAME = 1 } SERVICE_HOST_INFO_CODE; #define DHCP_OPTION55_LEN_MAX 255 #define FINGERPRINT_UDP_FLAGS_XENIX 0x00000800 #define FINGERPRINT_UDP_FLAGS_NT 0x00001000 #define FINGERPRINT_UDP_FLAGS_MASK (FINGERPRINT_UDP_FLAGS_XENIX | FINGERPRINT_UDP_FLAGS_NT) #define NEW_PAYLOAD_STATE 0xA0000000 #define OLD_PAYLOAD_STATE 0xB0000000 #endif /* __APP_ID_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/hostPortAppCache.h0000644000175000017500000000462414241075443022702 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _HOST_PORT_APP_CACHE_ #define _HOST_PORT_APP_CACHE_ #include #include #include #include #include #include "appId.h" #include "appIdApi.h" #include "sf_snort_packet.h" // Forward declaration for AppId config. Cannot include appIdConfig.h because of // circular dependency struct appIdConfig_; typedef struct _tHostPortKey { struct in6_addr ip; uint16_t port; uint16_t proto; } tHostPortKey; typedef struct _tHostPortVal { tAppId appId; APP_ID_TYPE type; } tHostPortVal; void hostPortAppCacheDynamicInit(); void hostPortAppCacheDynamicFini(); tHostPortVal *hostPortAppCacheDynamicFind(const sfaddr_t *ip, uint16_t port, uint16_t proto); int hostPortAppCacheDynamicAdd(const struct in6_addr *ip, uint16_t port, uint16_t proto, APP_ID_TYPE type, tAppId appId, bool sendUpdate); void hostPortAppCacheDynamicDump(); void hostPortAppCacheInit(struct appIdConfig_ *pConfig); void hostPortAppCacheFini(struct appIdConfig_ *pConfig); tHostPortVal *hostPortAppCacheFind(const sfaddr_t *ip, uint16_t port, uint16_t proto, const struct appIdConfig_ *pConfig); int hostPortAppCacheAdd(const struct in6_addr *ip, uint16_t port, uint16_t proto, APP_ID_TYPE type, tAppId appId, struct appIdConfig_ *pConfig); void hostPortAppCacheDump(const struct appIdConfig_ *pConfig); void updateHostCacheVersion(uint16_t *session_version); bool isHostCacheUpdated(uint16_t session_version); #ifdef SIDE_CHANNEL int ConsumeSSHostCache(const uint8_t *buf, uint32_t len); #endif #endif snort-2.9.20/src/dynamic-preprocessors/appid/app_forecast.c0000644000175000017500000000517114241075335022132 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include "app_forecast.h" static AFActKey master_key; static inline void rekeyMasterAFActKey (SFSnortPacket *p, int dir, tAppId forecast) { sfaddr_t *src; src = dir ? GET_DST_IP(p) : GET_SRC_IP(p); memcpy(master_key.ip, sfaddr_get_ip6_ptr(src), sizeof(master_key.ip)); master_key.forecast = forecast; } void checkSessionForAFIndicator(SFSnortPacket *p, int dir, const tAppIdConfig *pConfig, tAppId indicator) { AFElement *ind_element; if (!(ind_element = (AFElement*)sfxhash_find(pConfig->AF_indicators, &indicator))) return; rekeyMasterAFActKey(p, dir, ind_element->forecast); AFActVal *test_active_value; if ((test_active_value = (AFActVal*)sfxhash_find(pConfig->AF_actives, &master_key))) { test_active_value->last = GetPacketRealTime; test_active_value->target = ind_element->target; return; } AFActVal new_active_value; new_active_value.target = ind_element->target; new_active_value.last = GetPacketRealTime; sfxhash_add(pConfig->AF_actives, &master_key, &new_active_value); } tAppId checkSessionForAFForecast(tAppIdData *session, SFSnortPacket *p, int dir, const tAppIdConfig *pConfig, tAppId forecast) { AFActVal *check_act_val; rekeyMasterAFActKey(p, dir, forecast); //get out if there is no value if (!(check_act_val = (AFActVal*)sfxhash_find(pConfig->AF_actives, &master_key))) return APP_ID_UNKNOWN; //if the value is older than 5 minutes, remove it and get out time_t age; age = GetPacketRealTime - check_act_val->last; if (age < 0 || age > 300) { sfxhash_remove(pConfig->AF_actives, &master_key); return APP_ID_UNKNOWN; } session->payloadAppId = check_act_val->target; return forecast; } snort-2.9.20/src/dynamic-preprocessors/appid/util/0000755000175000017500000000000014242725717020300 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/appid/util/sf_mlmp.c0000644000175000017500000005116114241075614022076 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include "stdio.h" #include #include #include #include "string.h" #include "sf_dynamic_preprocessor.h" #include "sf_mlmp.h" #include "string.h" #define _MLMP_DEBUG 0 typedef struct _tPatternNode { tMlmpPattern pattern; void *userData; /*client/service info */ /**part number. Should start from 1. Ordering of parts does not matter in the sense * part 1 may appear after part 2 in payload.*/ uint32_t partNum; /**Total number of parts.*/ uint32_t partTotal; /**Uniq non-zero identifier to tie parts of a multi-part patterns together. */ uint32_t patternId; struct _tPatternNode *nextPattern; } tPatternNode ; typedef struct _tPatternPrimaryNode { tPatternNode patternNode; struct _tPatternPrimaryNode *nextPrimaryNode; /*Tree node for next level. Present only in primary pattern node i.e. */ struct tMlmpTree *nextLevelMatcher; } tPatternPrimaryNode ; /*Node for mlmp tree */ typedef struct tMlmpTree { void *patternTree; tPatternPrimaryNode *patternList; uint32_t level; } tTreeNode ; /*Used to track matched patterns. */ typedef struct _tMatchedPatternList { tPatternNode *patternNode; size_t index; /*uint32_t level; */ struct _tMatchedPatternList *next; } tMatchedPatternList; static int compareMlmpPatterns(const void *p1, const void *p2); static int createTreesRecusively(struct tMlmpTree *root); static void destroyTreesRecursively(struct tMlmpTree *root); static void dumpTreesRecursively(struct tMlmpTree *root); static int addPatternRecursively(struct tMlmpTree *root, const tMlmpPattern *inputPatternList, void *metaData, uint32_t level); static tPatternNode* urlPatternSelector (const tMatchedPatternList *matchList, const uint8_t* payload); static tPatternNode* genericPatternSelector (const tMatchedPatternList *matchList, const uint8_t* payload); static void *mlmpMatchPatternCustom(struct tMlmpTree *root, tMlmpPattern *inputPatternList, tPatternNode* (*callback)(const tMatchedPatternList *, const uint8_t*)); static int patternMatcherCallback (void *id, void *unused_tree, int index, void *data, void *unused_neg); static uint32_t gPatternId = 1; struct tMlmpTree* mlmpCreate(void) { tTreeNode *root = calloc(1, sizeof(tTreeNode)); if (root) root->level = 0; return root; } /*last pattern should be NULL */ int mlmpAddPattern(struct tMlmpTree *root, const tMlmpPattern *inputPatternList, void *metaData) { return addPatternRecursively(root, inputPatternList, metaData, 0); } int mlmpProcessPatterns(struct tMlmpTree *root) { int rvalue; rvalue = createTreesRecusively(root); if (rvalue) destroyTreesRecursively(root); return rvalue; } void *mlmpMatchPatternUrl(struct tMlmpTree *root, tMlmpPattern *inputPatternList) { return mlmpMatchPatternCustom(root, inputPatternList, urlPatternSelector); } void *mlmpMatchPatternGeneric(struct tMlmpTree *root, tMlmpPattern *inputPatternList) { return mlmpMatchPatternCustom(root, inputPatternList, genericPatternSelector); } static inline int matchDomainPattern(const tMatchedPatternList *mp, const uint8_t *pattern) { if (!pattern) return -1; return (mp->patternNode->pattern.level == 0 && !(mp->index == 0 || pattern[mp->index-1] == '.')); } static void *mlmpMatchPatternCustom(struct tMlmpTree *rootNode, tMlmpPattern *inputPatternList, tPatternNode* (*callback)(const tMatchedPatternList *, const uint8_t*)) { tMatchedPatternList *mp = NULL; tMatchedPatternList *tmpMp; void *data = NULL; void *tmpData = NULL; tPatternPrimaryNode *primaryNode; tMlmpPattern *pattern = inputPatternList; if (!rootNode || !pattern || !pattern->pattern) return NULL; _dpd.searchAPI->search_instance_find_all(rootNode->patternTree, (char *)pattern->pattern, pattern->patternSize, 0, patternMatcherCallback, (void*)&mp); primaryNode = (tPatternPrimaryNode *)callback(mp, pattern->pattern); while (mp) { tmpMp = mp; mp = mp->next; free(tmpMp); } if(primaryNode) { data = primaryNode->patternNode.userData; tmpData = mlmpMatchPatternCustom(primaryNode->nextLevelMatcher, ++inputPatternList, callback); if (tmpData) data = tmpData; } return data; } void mlmpDestroy(struct tMlmpTree *root) { destroyTreesRecursively(root); } void mlmpDump(struct tMlmpTree *root) { dumpTreesRecursively(root); } /**tMlmpPattern comparator: compares patterns based on pattern, patternSize. This will * result in alphabatical order. Notice that patternId is ignored here. */ static int compareMlmpPatterns(const void *p1, const void *p2) { tMlmpPattern *pat1 = (tMlmpPattern *)p1; tMlmpPattern *pat2 = (tMlmpPattern *)p2; int rValue; size_t minSize; /*first compare patterns by the smaller pattern size, if same then size wins */ minSize = (pat1->patternSize > pat2->patternSize)? pat2->patternSize: pat1->patternSize; rValue = memcmp(pat1->pattern, pat2->pattern, minSize); if (rValue) return rValue; return ((int)pat1->patternSize - (int)pat2->patternSize); } /*pattern trees are not freed on error because in case of error, caller should call detroyTreesRecursively. */ static int createTreesRecusively(struct tMlmpTree *rootNode) { void *patternMatcher; tPatternPrimaryNode *primaryPatternNode; tPatternNode *ddPatternNode; /* set up the MPSE for url patterns */ if (!(patternMatcher = rootNode->patternTree = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return -1; for (primaryPatternNode = rootNode->patternList; primaryPatternNode; primaryPatternNode = primaryPatternNode->nextPrimaryNode) { /*recursion into next lower level */ if (primaryPatternNode->nextLevelMatcher) { if (createTreesRecusively(primaryPatternNode->nextLevelMatcher)) return -1; } for (ddPatternNode = &primaryPatternNode->patternNode; ddPatternNode; ddPatternNode = ddPatternNode->nextPattern) { _dpd.searchAPI->search_instance_add_ex(patternMatcher, (void *)ddPatternNode->pattern.pattern, ddPatternNode->pattern.patternSize, ddPatternNode, STR_SEARCH_CASE_INSENSITIVE); } } _dpd.searchAPI->search_instance_prep(patternMatcher); return 0; } static void destroyTreesRecursively(struct tMlmpTree *rootNode) { tPatternPrimaryNode *primaryPatternNode; uint32_t partNum; if (!rootNode) return; while ((primaryPatternNode = rootNode->patternList)) { /*recursion into next lower level */ destroyTreesRecursively(primaryPatternNode->nextLevelMatcher); rootNode->patternList = primaryPatternNode->nextPrimaryNode; for (partNum = 2; partNum <= primaryPatternNode->patternNode.partTotal; partNum++) { tPatternNode *patternNode = primaryPatternNode->patternNode.nextPattern + (partNum -2); free((void*)patternNode->pattern.pattern); } free(primaryPatternNode->patternNode.nextPattern); free((void*)primaryPatternNode->patternNode.pattern.pattern); free(primaryPatternNode); } _dpd.searchAPI->search_instance_free(rootNode->patternTree); free(rootNode); } static void dumpTreesRecursively(struct tMlmpTree *rootNode) { tPatternPrimaryNode *primaryPatternNode; tPatternNode *ddPatternNode; char prefix[41]; uint32_t prefixSize; prefixSize = 4*(rootNode->level)+2; if (prefixSize > 40) prefixSize = 40; memset(prefix, ' ', prefixSize); prefix[prefixSize] = '\0'; for (primaryPatternNode = rootNode->patternList; primaryPatternNode; primaryPatternNode = primaryPatternNode->nextPrimaryNode) { printf("%s%u. Primary id %u. partTotal %u, Data %p\n", prefix, rootNode->level+1, primaryPatternNode->patternNode.patternId, primaryPatternNode->patternNode.partTotal, primaryPatternNode->patternNode.userData); for (ddPatternNode = &primaryPatternNode->patternNode; ddPatternNode; ddPatternNode = ddPatternNode->nextPattern) { printf("%s\t part %u/%u: Pattern %s, size %u\n", prefix, ddPatternNode->partNum, ddPatternNode->partTotal, (char *)ddPatternNode->pattern.pattern, (u_int32_t)ddPatternNode->pattern.patternSize); } if (primaryPatternNode->nextLevelMatcher) { dumpTreesRecursively(primaryPatternNode->nextLevelMatcher); } } } /*compares multipart patterns, and orders then according to . */ /*Comparing multi-parts alphanumerically does not make sense. */ static int compareMlmpPatternList(const tPatternNode *p1, const tPatternNode *p2) { if (p1->patternId != p2->patternId) return (p1->patternId - p2->patternId); return (p1->partNum - p2->partNum); } static tPatternNode* patternSelector (const tMatchedPatternList *patternMatchList, const uint8_t *payload, bool domain) { tPatternNode* bestNode = NULL; tPatternNode* currentPrimaryNode = NULL; const tMatchedPatternList *tmpList; uint32_t partNum, patternId, patternSize, maxPatternSize; /*partTotal = 0; */ partNum = 0; patternId = 0; patternSize = maxPatternSize = 0; #if _MLMP_DEBUG tPatternNode *ddPatternNode; printf("\tMatches found -------------------\n"); for (tmpList = patternMatchList; tmpList; tmpList = tmpList->next) { ddPatternNode = tmpList->patternNode; { printf("\t\tid %d, Pattern %s, size %u, partNum %u, partTotal %u, userData %p\n", ddPatternNode->patternId, ddPatternNode->pattern.pattern, (u_int32_t)ddPatternNode->pattern.patternSize, ddPatternNode->partNum, ddPatternNode->partTotal, ddPatternNode->userData); } } #endif for (tmpList = patternMatchList; tmpList; tmpList = tmpList->next) { if (tmpList->patternNode->patternId != patternId) { /*first pattern */ /*skip incomplete pattern */ if (tmpList->patternNode->partNum != 1) continue; /*new pattern started */ patternId = tmpList->patternNode->patternId; currentPrimaryNode = tmpList->patternNode; partNum = 0; patternSize = 0; } if (tmpList->patternNode->partNum == (partNum+1)) { partNum++; patternSize += tmpList->patternNode->pattern.patternSize; } if (tmpList->patternNode->partTotal != partNum) continue; /*backward compatibility */ if ((tmpList->patternNode->partTotal == 1) && domain && matchDomainPattern(tmpList, payload)) continue; /*last pattern part is seen in sequence */ if (patternSize >= maxPatternSize) { maxPatternSize = patternSize; bestNode = currentPrimaryNode; } } #if _MLMP_DEBUG if (bestNode) { ddPatternNode = bestNode; { printf("\t\tSELECTED Id %d, pattern %s, size %u, partNum %u, partTotal %u, userData %p\n", ddPatternNode->patternId, ddPatternNode->pattern.pattern, (u_int32_t)ddPatternNode->pattern.patternSize, ddPatternNode->partNum, ddPatternNode->partTotal, ddPatternNode->userData); } } printf("\tMatches end -------------------\n"); #endif return bestNode; } static tPatternNode* urlPatternSelector (const tMatchedPatternList *patternMatchList, const uint8_t *payload) { return patternSelector (patternMatchList, payload, true); } static tPatternNode* genericPatternSelector (const tMatchedPatternList *patternMatchList, const uint8_t *payload) { return patternSelector (patternMatchList, payload, false); } static int patternMatcherCallback (void *id, void *unused_tree, int index, void *data, void *unused_neg) { tPatternNode *target = (tPatternNode *)id; tMatchedPatternList **matchList = (tMatchedPatternList **)data; tMatchedPatternList *prevNode; tMatchedPatternList *tmpList; tMatchedPatternList *newNode; int cmp; /*sort matches by patternId, and then by partId or pattern// */ #if _MLMP_DEBUG printf("\tCallback id %d, Pattern %s, size %u, partNum %u, partTotal %u, userData %p\n", target->patternId, target->pattern.pattern, (u_int32_t)target->pattern.patternSize, target->partNum, target->partTotal, target->userData); #endif for (prevNode = NULL, tmpList = *matchList; tmpList; prevNode = tmpList, tmpList = tmpList->next) { cmp = compareMlmpPatternList (target, tmpList->patternNode); if (cmp > 0 ) continue; if (cmp == 0) return 0; break; } newNode = calloc(1,sizeof(*newNode)); if (!newNode) { /*terminate search */ return 1; } newNode->index = index; newNode->patternNode = target; if (prevNode == NULL) { /*first node */ newNode->next = *matchList; *matchList = newNode; } else { newNode->next = prevNode->next; prevNode->next = newNode; } return 0; } /*find a match and insertion point if no match is found. Insertion point NULL means */ static tPatternPrimaryNode * findMatchPattern(struct tMlmpTree* rootNode, const tMlmpPattern *inputPatternList, uint32_t partTotal, tPatternPrimaryNode** prevPrimaryPatternNode) { tPatternPrimaryNode *primaryPatternNode; tPatternNode *ddPatternNode; uint32_t partNum; int retVal; *prevPrimaryPatternNode = NULL; for (primaryPatternNode = rootNode->patternList; primaryPatternNode; *prevPrimaryPatternNode = primaryPatternNode, primaryPatternNode = primaryPatternNode->nextPrimaryNode ) { if (primaryPatternNode->patternNode.partTotal != partTotal) { continue; } partNum = 1; for (ddPatternNode = &primaryPatternNode->patternNode; ddPatternNode; ddPatternNode = ddPatternNode->nextPattern) { retVal = compareMlmpPatterns(inputPatternList+(partNum-1), &ddPatternNode->pattern); if (retVal == 0) { /*all nodes matched */ if (partNum == ddPatternNode->partTotal) return primaryPatternNode; else continue; } else if (retVal < 0) { return NULL; } break; } /**prevPrimaryPatternNode = primaryPatternNode; */ } return NULL; } /** * @Note * a. Patterns in each patternList must be unique. Multipart patterns should be unique i.e. no two multi-part patterns * should have same ordered sub-parts. * b. Patterns are add in alphabetical ordering of primary nodes. */ static int addPatternRecursively(struct tMlmpTree *rootNode, const tMlmpPattern *inputPatternList, void *metaData, uint32_t level) { tPatternNode *newNode; tPatternPrimaryNode* prevPrimaryPatternNode = NULL; tPatternPrimaryNode *primaryNode = NULL; const tMlmpPattern *nextPattern; const tMlmpPattern *patterns = inputPatternList; uint32_t partTotal = 0; uint32_t patternId = 0; uint32_t i; if (!rootNode || !inputPatternList) return -1; /*make it easier for user to add patterns by calculating partTotal and partNum */ for ( i = 0, patterns = inputPatternList; patterns->pattern && (patterns->level == level); patterns = inputPatternList + (++i)) { partTotal++; } /*see if pattern is present already. Multipart-messages are considered match only if all parts */ /*match. */ primaryNode = findMatchPattern(rootNode, inputPatternList, partTotal, &prevPrimaryPatternNode); /*pattern not found, insert it in order */ if (!primaryNode) { tPatternPrimaryNode *tmpPrimaryNode; uint32_t partNum; tmpPrimaryNode = (tPatternPrimaryNode *)calloc(1, sizeof(tPatternPrimaryNode)); if (!tmpPrimaryNode) { return -1; } if (partTotal > 1) { tmpPrimaryNode->patternNode.nextPattern = (tPatternNode *)calloc(partTotal-1, sizeof(tPatternNode)); if (!tmpPrimaryNode->patternNode.nextPattern) { free(tmpPrimaryNode); return -1; } } patternId = gPatternId++; i = 0; patterns = inputPatternList+i; /*initialize primary Node */ tmpPrimaryNode->patternNode.pattern.pattern = patterns->pattern; tmpPrimaryNode->patternNode.pattern.patternSize = patterns->patternSize; tmpPrimaryNode->patternNode.pattern.level = patterns->level; tmpPrimaryNode->patternNode.partNum = 1; tmpPrimaryNode->patternNode.partTotal = partTotal; tmpPrimaryNode->patternNode.patternId = patternId; if (prevPrimaryPatternNode) { tmpPrimaryNode->nextPrimaryNode = prevPrimaryPatternNode->nextPrimaryNode; prevPrimaryPatternNode->nextPrimaryNode = tmpPrimaryNode; } else { /*insert as first node since either this is the only node, or this is lexically smallest. */ tmpPrimaryNode->nextPrimaryNode = rootNode->patternList; rootNode->patternList = tmpPrimaryNode;; } i++; patterns = inputPatternList + i; /*create list of remaining nodes */ for (partNum = 2; partNum <= partTotal; partNum++) { newNode = tmpPrimaryNode->patternNode.nextPattern + (partNum -2); newNode->pattern.pattern = patterns->pattern; newNode->pattern.patternSize = patterns->patternSize; newNode->pattern.level = patterns->level; newNode->partNum = partNum; newNode->partTotal = partTotal; newNode->patternId = patternId; if (partNum < partTotal) newNode->nextPattern = newNode+1; else newNode->nextPattern = NULL; i++; patterns = inputPatternList + i; } primaryNode = tmpPrimaryNode; } else { for (i = 0; i < primaryNode->patternNode.partTotal; i++) free((void*)(inputPatternList+i)->pattern); } if (primaryNode) { /*move down the new node */ nextPattern = inputPatternList + partTotal; if (!nextPattern || !nextPattern->pattern) { primaryNode->patternNode.userData = metaData; } else { if (!primaryNode->nextLevelMatcher) { tTreeNode * tmpRootNode; tmpRootNode = (tTreeNode *)calloc(1, sizeof(tTreeNode)); if (!tmpRootNode) { /*log error */ return -1; } primaryNode->nextLevelMatcher = tmpRootNode; primaryNode->nextLevelMatcher->level = rootNode->level+1; } addPatternRecursively(primaryNode->nextLevelMatcher, inputPatternList+partTotal, metaData, level+1); } } return 0; } snort-2.9.20/src/dynamic-preprocessors/appid/util/NetworkSet.h0000644000175000017500000002546514241075611022560 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __NETWORK_SET_H__ #define __NETWORK_SET_H__ /* System includes */ #include #include #include #include #include #ifndef ULLONG_MAX # define ULLONG_MAX 18446744073709551615ULL #endif #define BYTE_SWAP_16(x) \ ((uint16_t)((((uint16_t)(x) & 0xff00) >> 8) | \ (((uint16_t)(x) & 0x00ff) << 8))) #define BYTE_SWAP_32(x) \ ((uint32_t)((((uint32_t)(x) & 0xff000000) >> 24) | \ (((uint32_t)(x) & 0x00ff0000) >> 8) | \ (((uint32_t)(x) & 0x0000ff00) << 8) | \ (((uint32_t)(x) & 0x000000ff) << 24))) #define BYTE_SWAP_64(x) \ ((uint64_t)((((uint64_t)(x) & 0xff00000000000000ULL) >> 56) | \ (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \ (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \ (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \ (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \ (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \ (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \ (((uint64_t)(x) & 0x00000000000000ffULL) << 56))) #if defined(WORDS_BIGENDIAN) typedef struct _NSIPv6Addr { uint64_t hi; uint64_t lo; } NSIPv6Addr; #else typedef struct _NSIPv6Addr { uint64_t lo; uint64_t hi; } NSIPv6Addr; #endif //IPv6 address a must be in network order #define NSIP_IS_ADDR_MULTICAST(a) \ (IN6_IS_ADDR_MULTICAST(a) \ || ((IN6_IS_ADDR_V4MAPPED(a) || IN6_IS_ADDR_V4COMPAT(a)) && (((__const uint32_t *) (a))[3] == 0xffffffff))) static inline void NSIPv6PackIpv4(NSIPv6Addr *ipv6Addr, uint32_t ipv4Addr) { ipv6Addr->hi = 0ULL; ipv6Addr->lo = (uint64_t)ipv4Addr | 0x0000FFFF00000000ULL; } static inline int NSIPv6UnpackIpv4(const NSIPv6Addr *ipv6Addr, uint32_t *ipv4Addr) { if (!ipv6Addr->hi) { uint64_t lo = ipv6Addr->lo & 0xFFFFFFFF00000000ULL; if (!lo || lo == 0x0000FFFF00000000ULL) { *ipv4Addr = (uint32_t)ipv6Addr->lo; return 0; } } return -1; } static inline void NSIPv6AddrCopy(const NSIPv6Addr *src, NSIPv6Addr *dst) { dst->hi = src->hi; dst->lo = src->lo; } static inline int NSIPv6AddrCompare(const NSIPv6Addr *a, const NSIPv6Addr *b) { if (a->hi < b->hi) return -1; else if (a->hi > b->hi) return 1; if (a->lo < b->lo) return -1; else if (a->lo > b->lo) return 1; return 0; } #if defined(WORDS_BIGENDIAN) #define NSIPv6AddrNtoH(ip6) do {} while(0) #else static inline void NSIPv6AddrNtoH(NSIPv6Addr *ip6) { uint64_t tmp; tmp = BYTE_SWAP_64(ip6->hi); ip6->hi = BYTE_SWAP_64(ip6->lo); ip6->lo = tmp; } #endif #if defined(WORDS_BIGENDIAN) static inline void _NSIPv6AddrConv(const NSIPv6Addr *ip6, NSIPv6Addr *ip6h) { ip6h->hi = ip6->hi; ip6h->lo = ip6->lo; } #else static inline void _NSIPv6AddrConv(const NSIPv6Addr *ip6, NSIPv6Addr *ip6h) { ip6h->hi = BYTE_SWAP_64(ip6->lo); ip6h->lo = BYTE_SWAP_64(ip6->hi); } #endif static inline void NSIPv6AddrNtoHConv(const struct in6_addr *ip6, NSIPv6Addr *ip6h) { _NSIPv6AddrConv((const NSIPv6Addr *)ip6, ip6h); } static inline void NSIPv6AddrHtoNConv(const NSIPv6Addr *ip6, struct in6_addr *ip6h) { _NSIPv6AddrConv(ip6, (NSIPv6Addr *)ip6h); } #define NSIPv6AddrHtoN(ip6) NSIPv6AddrNtoH(ip6) static inline void NSIPv6AddrInc(NSIPv6Addr *ip6) { if (ip6->lo == ULLONG_MAX) { ip6->lo = 0; ip6->hi++; } else ip6->lo++; } static inline void NSIPv6AddrDec(NSIPv6Addr *ip6) { if (!ip6->lo) { ip6->lo = ULLONG_MAX; ip6->hi--; } else ip6->lo--; } typedef struct _NSNetworkInfo { unsigned id; unsigned netmask; int ip_not; unsigned type; } NSNetworkInfo; typedef struct _Network { NSNetworkInfo info; uint32_t range_min; uint32_t range_max; } Network; typedef struct _Network6 { NSNetworkInfo info; NSIPv6Addr range_min; NSIPv6Addr range_max; } Network6; typedef struct _NetworkSet { struct _NetworkSet *next; SF_LIST networks; SFXHASH *ids; Network **pnetwork; unsigned count; SF_LIST networks6; SFXHASH *ids6; Network6 **pnetwork6; unsigned count6; } NetworkSet; /** * Create a new network set */ int NetworkSet_New(struct _NetworkSet **network_set); /** * Destroy a network set */ int NetworkSet_Destroy(struct _NetworkSet *network_set); /** * Copy a network set */ NetworkSet *NetworkSet_Copy(NetworkSet *network_set); /** * Add a network set to another network set */ int NetworkSet_AddSet(NetworkSet *dest_set, NetworkSet *src_set); /** * Add a network to the set using cidr block notation */ int NetworkSet_AddCidrBlockEx(struct _NetworkSet *network_set, uint32_t ip, unsigned cidr_bits, int ip_not, unsigned id, unsigned type); /** * Add a network to the set using cidr block notation */ int NetworkSet_AddCidrBlock6Ex(struct _NetworkSet *network_set, NSIPv6Addr *ip, unsigned cidr_bits, int ip_not, unsigned id, unsigned type); /** * Add a network to the set using cidr block notation */ int NetworkSet_AddCidrBlock(struct _NetworkSet *network_set, uint32_t ip, unsigned cidr_bits, int ip_not, unsigned id); /** * Add a network to the set using cidr block notation */ int NetworkSet_AddCidrBlock6(struct _NetworkSet *network_set, NSIPv6Addr *ip, unsigned cidr_bits, int ip_not, unsigned id); /** * Add a network to the set using a range */ int NetworkSet_AddNetworkRangeEx(NetworkSet *network_set, uint32_t range_min, uint32_t range_max, unsigned cidr_bits, int ip_not, unsigned id, unsigned type); /** * Add a network to the set using a range */ int NetworkSet_AddNetworkRange6Ex(NetworkSet *network_set, NSIPv6Addr *range_min, NSIPv6Addr *range_max, unsigned cidr_bits, int ip_not, unsigned id, unsigned type); /** * Add a network to the set using a range */ int NetworkSet_AddNetworkRange(NetworkSet *network_set, uint32_t range_min, uint32_t range_max, unsigned cidr_bits, int ip_not, unsigned id); /** * Add a network to the set using a range */ int NetworkSet_AddNetworkRange6(NetworkSet *network_set, NSIPv6Addr *range_min, NSIPv6Addr *range_max, unsigned cidr_bits, int ip_not, unsigned id); /** * Add a network to the set using a range of all IPv6, excluding IPv4 */ int NetworkSet_AddNetworkRangeOnlyIPv6(NetworkSet *network_set, int ip_not, unsigned id, unsigned type); /** * Reduce the networks to a list of existing ranges */ int NetworkSet_Reduce(struct _NetworkSet *network_set); /** * Print the network to the specified stream */ int NetworkSet_Fprintf(struct _NetworkSet *network_set, const char *prefix, FILE *stream); /* * Test is the set contains the specied address */ static inline int NetworkSet_ContainsEx(NetworkSet *network_set, uint32_t ipaddr, unsigned *type) { int low=0; int middle=0; int high=0; *type = 0; if(!network_set) return 0; if(!network_set->count) return 0; high = network_set->count - 1; if(ipaddr < network_set->pnetwork[low]->range_min || ipaddr > network_set->pnetwork[high]->range_max) return 0; while(low <= high) { middle = low + ((high - low)>>1); if(ipaddr < network_set->pnetwork[middle]->range_min) high = middle - 1; else if(ipaddr > network_set->pnetwork[middle]->range_max) low = middle + 1; else { *type = network_set->pnetwork[middle]->info.type; return 1; } } return 0; } /* * Test is the set contains the specied address */ static inline int NetworkSet_Contains6Ex(NetworkSet *network_set, NSIPv6Addr *ipaddr, unsigned *type) { int low=0; int middle=0; int high=0; *type = 0; if(!network_set) return 0; if(!network_set->count6) return 0; high = network_set->count6 - 1; if(NSIPv6AddrCompare(ipaddr, &network_set->pnetwork6[low]->range_min) < 0 || NSIPv6AddrCompare(ipaddr, &network_set->pnetwork6[high]->range_max) > 0) { return 0; } while(low <= high) { middle = low + ((high - low)>>1); if(NSIPv6AddrCompare(ipaddr, &network_set->pnetwork6[middle]->range_min) < 0) high = middle - 1; else if(NSIPv6AddrCompare(ipaddr, &network_set->pnetwork6[middle]->range_max) > 0) low = middle + 1; else { *type = network_set->pnetwork6[middle]->info.type; return 1; } } return 0; } /* * Test is the set contains the specied address */ static inline int NetworkSet_Contains(NetworkSet *network_set, uint32_t ipaddr) { unsigned type; return NetworkSet_ContainsEx(network_set, ipaddr, &type); } /* * Test is the set contains the specied address */ static inline int NetworkSet_Contains6(NetworkSet *network_set, NSIPv6Addr *ipaddr) { unsigned type; return NetworkSet_Contains6Ex(network_set, ipaddr, &type); } /** * Get a count of the number of networks in the set * */ static inline int NetworkSet_Count(NetworkSet *network_set, unsigned *count) { if (!network_set || !count) return -1; *count = sflist_count(&network_set->networks); return 0; } /** * Get a count of the number of networks in the set * */ static inline int NetworkSet_Count6(NetworkSet *network_set, unsigned *count) { if (!network_set || !count) return -1; *count = sflist_count(&network_set->networks6); return 0; } /** * Get a count of the number of networks in the set * */ static inline unsigned NetworkSet_CountEx(NetworkSet *network_set) { if (!network_set) return 0; return sflist_count(&network_set->networks); } /** * Get a count of the number of networks in the set * */ static inline unsigned NetworkSet_Count6Ex(NetworkSet *network_set) { if (!network_set) return 0; return sflist_count(&network_set->networks6); } #endif /* __NETWORK_SET_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/util/OutputFile.h0000644000175000017500000000223114241075613022537 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __OUTPUT_FILE__ #define __OUTPUT_FILE__ #include #include FILE *openOutputFile(const char * const filename, time_t tstamp); FILE *rolloverOutputFile(const char * const filename, FILE * const oldfp, time_t tstamp); #endif /* __OUTPUT_FILE__ */ snort-2.9.20/src/dynamic-preprocessors/appid/util/ip_funcs.h0000644000175000017500000000456714241075606022265 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __IP_FUNCS_H__ #define __IP_FUNCS_H__ #include #include #include #define IPFUNCS_EXCEPT_IP 0x01 #define IPFUNCS_SECONDARY_IP 0x02 #define IPFUNCS_APPID_SESSION_EXCLUDE_IP 0x04 #define IPFUNCS_USER_IP 0x08 #define IPFUNCS_HOSTS_IP 0x10 #define IPFUNCS_APPLICATION 0x20 #define IPFUNCS_CHECKED 0x80000000 typedef struct _RNAIpAddrSet { uint32_t range_min; uint32_t range_max; uint32_t addr_flags; unsigned netmask; uint32_t netmask_mask; } RNAIpAddrSet; RNAIpAddrSet *ParseIpCidr(char *, uint32_t *); typedef struct _RNAIpv6AddrSet { NSIPv6Addr range_min; NSIPv6Addr range_max; uint32_t addr_flags; unsigned netmask; NSIPv6Addr netmask_mask; } RNAIpv6AddrSet; RNAIpv6AddrSet *ParseIpv6Cidr(char *); static inline void copyIpv4ToIpv6Network(struct in6_addr *keyIp, const uint32_t ip) { keyIp->s6_addr32[0] = keyIp->s6_addr32[1] = 0; keyIp->s6_addr16[4] = 0; keyIp->s6_addr16[5] = 0xFFFF; keyIp->s6_addr32[3] = ip; } //these functions are needed since snort does not store IPv4 address in highest 4 bytes //of 16 byte ip. static inline void copySnortIpToIpv6Network(struct in6_addr *keyIp, const sfaddr_t *snortIp) { memcpy(keyIp, sfaddr_get_ip6_ptr(snortIp), sizeof(*keyIp)); } static inline int cmpSnortIpToHostKey(struct in6_addr *keyIp, const sfaddr_t *snortIp) { return memcmp(keyIp, sfaddr_get_ip6_ptr(snortIp), sizeof(*keyIp)); } #endif /* __IP_FUNCS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/util/sf_mlmp.h0000644000175000017500000000326414241075615022105 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SF_MULTI_PART_MPSE_H_ #define _SF_MULTI_PART_MPSE_H_ #include #include typedef struct _tMlmpPattern { /*binary pattern */ const uint8_t *pattern; /*binary pattern length in bytes */ size_t patternSize; /**level of pattern. It should start from 0.*/ uint32_t level; } tMlmpPattern; struct tMlmpTree; struct tMlmpTree* mlmpCreate(void); int mlmpAddPattern(struct tMlmpTree* root, const tMlmpPattern *patterns, void *metaData); int mlmpProcessPatterns(struct tMlmpTree* root); void *mlmpMatchPatternUrl(struct tMlmpTree* root, tMlmpPattern *inputPatternList); void *mlmpMatchPatternGeneric(struct tMlmpTree* root, tMlmpPattern *inputPatternList); void mlmpDestroy(struct tMlmpTree* root); void mlmpDump(struct tMlmpTree* root); #endif snort-2.9.20/src/dynamic-preprocessors/appid/util/NetworkSet.c0000644000175000017500000013014514241075607022550 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* System includes */ #include #include #include #include #include #include #include #include #ifdef LINUX #include #endif #include "sf_dynamic_preprocessor.h" /* Sourcefire includes */ #include "NetworkSet.h" /* Local includes */ #define MODULE_NAME "NetworkSet" int NetworkSet_New(NetworkSet **network_set) { NetworkSet *tmp = NULL; if(!network_set) return -1; if(!(tmp = calloc(1, sizeof(*tmp)))) { _dpd.errMsg("NetworkSet:Out of memory (wanted %zu bytes)", sizeof(NetworkSet)); NetworkSet_Destroy(tmp); return -1; } sflist_init(&tmp->networks); tmp->ids = sfxhash_new(64, sizeof(unsigned), 0, 0, 0, NULL, NULL, 1); if (tmp->ids == NULL) { _dpd.errMsg("NetworkSet:Out of memory (wanted %zu bytes)", sizeof(NetworkSet)); NetworkSet_Destroy(tmp); return -1; } sflist_init(&tmp->networks6); tmp->ids6 = sfxhash_new(64, sizeof(unsigned), 0, 0, 0, NULL, NULL, 1); if (tmp->ids6 == NULL) { _dpd.errMsg("NetworkSet:Out of memory (wanted %zu bytes)", sizeof(NetworkSet)); NetworkSet_Destroy(tmp); return -1; } *network_set = tmp; return 0; } int NetworkSet_Destroy(NetworkSet *network_set) { if(!network_set) return -1; if(network_set->pnetwork) { free(network_set->pnetwork); network_set->pnetwork = NULL; } sflist_static_free_all(&network_set->networks, &free); sfxhash_delete(network_set->ids); if(network_set->pnetwork6) { free(network_set->pnetwork6); network_set->pnetwork6 = NULL; } sflist_static_free_all(&network_set->networks6, &free); sfxhash_delete(network_set->ids6); free(network_set); return 0; } int NetworkSet_AddNetworkRangeEx(NetworkSet *network_set, uint32_t range_min, uint32_t range_max, unsigned cidr_bits, int ip_not, unsigned id, unsigned type) { Network *network; Network *iNetwork; int rval; if(!network_set) return -1; if(!(network = (Network *)calloc(1, sizeof(*network)))) { _dpd.errMsg("NetworkSet:Out of memory (wanted %zu bytes)", sizeof(*network)); return -1; } network->info.id = id; network->info.ip_not = ip_not; network->info.type = type; network->info.netmask = cidr_bits; if (range_min <= range_max) { network->range_min = range_min; network->range_max = range_max; } else { network->range_min = range_max; network->range_max = range_min; } if (!network->info.ip_not) { for (iNetwork = sflist_first(&network_set->networks); iNetwork; iNetwork = sflist_next(&network_set->networks)) { if (iNetwork->info.id == network->info.id && iNetwork->range_min == network->range_min && iNetwork->range_max == network->range_max) { iNetwork->info.type |= network->info.type; free(network); return 0; } } } if(sflist_add_tail(&network_set->networks, (void *)network)) { _dpd.errMsg("NetworkSet:Out of memory"); free(network); return -1; } rval = sfxhash_add(network_set->ids, &network->info.id, &network->info.id); if (rval != SFXHASH_OK && rval != SFXHASH_INTABLE) { _dpd.errMsg("NetworkSet:Out of memory"); free(network); return -1; } return 0; } int NetworkSet_AddNetworkRange(NetworkSet *network_set, uint32_t range_min, uint32_t range_max, unsigned cidr_bits, int ip_not, unsigned id) { return NetworkSet_AddNetworkRangeEx(network_set, range_min, range_max, cidr_bits, ip_not, id, 0); } int NetworkSet_AddNetworkRange6Ex(NetworkSet *network_set, NSIPv6Addr *range_min, NSIPv6Addr *range_max, unsigned cidr_bits, int ip_not, unsigned id, unsigned type) { Network6 *network; Network6 *iNetwork; int rval; if(!network_set) return -1; if(!(network = (Network6 *)calloc(1, sizeof(*network)))) { _dpd.errMsg("NetworkSet:Out of memory (wanted %zu bytes)", sizeof(*network)); return -1; } network->info.id = id; network->info.ip_not = ip_not; network->info.type = type; network->info.netmask = cidr_bits; if (NSIPv6AddrCompare(range_min, range_max) <= 0) { network->range_min = *range_min; network->range_max = *range_max; } else { network->range_min = *range_max; network->range_max = *range_min; } if (!network->info.ip_not) { for (iNetwork = sflist_first(&network_set->networks6); iNetwork; iNetwork = sflist_next(&network_set->networks6)) { if (iNetwork->info.id == network->info.id && !NSIPv6AddrCompare(&iNetwork->range_min, &network->range_min) && !NSIPv6AddrCompare(&iNetwork->range_max, &network->range_max)) { iNetwork->info.type |= network->info.type; free(network); return 0; } } } if(sflist_add_tail(&network_set->networks6, (void *)network)) { _dpd.errMsg("NetworkSet:Out of memory"); free(network); return -1; } rval = sfxhash_add(network_set->ids6, &network->info.id, &network->info.id); if (rval != SFXHASH_OK && rval != SFXHASH_INTABLE) { _dpd.errMsg("NetworkSet:Out of memory"); free(network); return -1; } return 0; } int NetworkSet_AddNetworkRange6(NetworkSet *network_set, NSIPv6Addr *range_min, NSIPv6Addr *range_max, unsigned cidr_bits, int ip_not, unsigned id) { return NetworkSet_AddNetworkRange6Ex(network_set, range_min, range_max, cidr_bits, ip_not, id, 0); } int NetworkSet_AddNetworkRangeOnlyIPv6(NetworkSet *network_set, int ip_not, unsigned id, unsigned type) { // Use two ranges to represent all of IPv6, excluding the IPv4-mapped range, ::FFFF:*.*.*.* int rval; NSIPv6Addr range_min, range_max; range_min.lo = 0; range_min.hi = 0; range_max.lo = 0x0000FFFEFFFFFFFFULL; // 0x0000FFFF00000000 - 1 range_max.hi = 0; rval = NetworkSet_AddNetworkRange6Ex(network_set, &range_min, &range_max, 0, ip_not, id, type); range_min.lo = 0x0001000000000000ULL; // 0x0000FFFFFFFFFFFF + 1 range_min.hi = 0; range_max.lo = 0xFFFFFFFFFFFFFFFFULL; range_max.hi = 0xFFFFFFFFFFFFFFFFULL; return rval ? rval : NetworkSet_AddNetworkRange6Ex(network_set, &range_min, &range_max, 0, ip_not, id, type); } static inline int NetworkSet_AddNetwork(NetworkSet *network_set, uint32_t ip, unsigned cidr_bits, uint32_t mask, int ip_not, unsigned id, unsigned type) { uint32_t range_min; uint32_t range_max; range_min = ip & mask; range_max = range_min + ~mask; return NetworkSet_AddNetworkRangeEx(network_set, range_min, range_max, cidr_bits, ip_not, id, type); } int NetworkSet_AddCidrBlockEx(NetworkSet *network_set, uint32_t ip, unsigned cidr_bits, int ip_not, unsigned id, unsigned type) { uint32_t mask; if(cidr_bits > 32) return -1; /* Convert cidr to netmask */ if(cidr_bits == 0) mask = 0; else mask = 0xffffffff << (32 - cidr_bits); return NetworkSet_AddNetwork(network_set, ip, cidr_bits, mask, ip_not, id, type); } int NetworkSet_AddCidrBlock(NetworkSet *network_set, uint32_t ip, unsigned cidr_bits, int ip_not, unsigned id) { return NetworkSet_AddCidrBlockEx(network_set, ip, cidr_bits, ip_not, id, 0); } static inline int NetworkSet_AddNetwork6(NetworkSet *network_set, NSIPv6Addr *ip, unsigned cidr_bits, NSIPv6Addr *mask, int ip_not, unsigned id, unsigned type) { NSIPv6Addr range_min; NSIPv6Addr range_max; range_min.lo = ip->lo & mask->lo; range_min.hi = ip->hi & mask->hi; range_max.lo = range_min.lo + ~mask->lo; range_max.hi = range_min.hi + ~mask->hi; return NetworkSet_AddNetworkRange6Ex(network_set, &range_min, &range_max, cidr_bits, ip_not, id, type); } int NetworkSet_AddCidrBlock6Ex(NetworkSet *network_set, NSIPv6Addr *ip, unsigned cidr_bits, int ip_not, unsigned id, unsigned type) { NSIPv6Addr mask; if(cidr_bits > 128) return -1; /* Convert cidr to netmask */ if(!cidr_bits) { mask.hi = 0; mask.lo = 0; } else if(cidr_bits < 64) { mask.hi = ULLONG_MAX << (64 - cidr_bits); mask.lo = 0; } else if(cidr_bits == 64) { mask.hi = ULLONG_MAX; mask.lo = 0; } else { mask.hi = ULLONG_MAX; mask.lo = ULLONG_MAX << (128 - cidr_bits); } return NetworkSet_AddNetwork6(network_set, ip, cidr_bits, &mask, ip_not, id, type); } int NetworkSet_AddCidrBlock6(NetworkSet *network_set, NSIPv6Addr *ip, unsigned cidr_bits, int ip_not, unsigned id) { return NetworkSet_AddCidrBlock6Ex(network_set, ip, cidr_bits, ip_not, id, 0); } static inline void NetworkList_Fprintf(NetworkSet *network_set, const char *prefix, FILE *stream) { Network *network; Network6 *network6; struct in_addr four; NSIPv6Addr six; char min_ip[INET6_ADDRSTRLEN]; char max_ip[INET6_ADDRSTRLEN]; for (network = (Network *)sflist_first(&network_set->networks); network; network = (Network *)sflist_next(&network_set->networks)) { four.s_addr = htonl(network->range_min); inet_ntop(AF_INET, &four, min_ip, sizeof(min_ip)); four.s_addr = htonl(network->range_max); inet_ntop(AF_INET, &four, max_ip, sizeof(max_ip)); /* check containment for this network */ fprintf(stream, "%s%s%s-%s for %u with %08X\n", prefix, network->info.ip_not ? "!":"", min_ip, max_ip, network->info.id, network->info.type); } for (network6 = (Network6 *)sflist_first(&network_set->networks6); network6; network6 = (Network6 *)sflist_next(&network_set->networks6)) { six = network6->range_min; NSIPv6AddrHtoN(&six); inet_ntop(AF_INET6, (struct in6_addr *)&six, min_ip, sizeof(min_ip)); six = network6->range_max; NSIPv6AddrHtoN(&six); inet_ntop(AF_INET6, (struct in6_addr *)&six, max_ip, sizeof(max_ip)); /* check containment for this network */ fprintf(stream, "%s%s%s-%s for %u with %08X\n", prefix, network6->info.ip_not ? "!":"", min_ip, max_ip, network6->info.id, network6->info.type); } } int NetworkSet_Fprintf(NetworkSet *network_set, const char *prefix, FILE *stream) { if(!network_set) return -1; if(!prefix) prefix = ""; if(!stream) stream = stdout; NetworkList_Fprintf(network_set, prefix, stream); return 0; } static inline int NetworkSet_OrderByNetmask(SF_LIST *ordered_networks, SF_LIST *networks, unsigned id) { SF_LNODE *node; SF_LNODE *i_node; NSNetworkInfo *network; sflist_init(ordered_networks); do { node = NULL; for (i_node = sflist_first_node(networks); i_node; i_node = sflist_next_node(networks)) { if ((network = SFLIST_NODE_TO_DATA(i_node))) { if (network->id == id && (node == NULL || network->netmask < ((NSNetworkInfo *)SFLIST_NODE_TO_DATA(node))->netmask || (network->netmask == ((NSNetworkInfo *)SFLIST_NODE_TO_DATA(node))->netmask && !network->ip_not))) { node = i_node; } } } if (node) { if (sflist_add_tail(ordered_networks, SFLIST_NODE_TO_DATA(node))) { return -1; } sflist_remove_node(networks, node); } } while (node); return 0; } static inline int NetworkSet_AddList(SF_LIST *networks, SF_LIST *new_networks) { void *network; while ((network = sflist_remove_head(new_networks))) { if (sflist_add_tail(networks, network)) return -1; } return 0; } static inline int NetworkSet_ReduceNetworkSet(SF_LIST *networks) { Network *ias; Network *i_ias; Network *new_ias; uint32_t tmp; SF_LNODE *node; SF_LNODE *inode; int changed; SF_LIST reduced_networks; if (!sflist_count(networks)) return 0; sflist_init(&reduced_networks); while ((ias = (Network *)sflist_remove_head(networks))) { /* ias is lowest in the list, so it takes precedence */ if (ias->info.ip_not) { node = sflist_first_node(&reduced_networks); while (node) { changed = 0; i_ias = SFLIST_NODE_TO_DATA(node); if (i_ias) { /* i_ias ****** ias *************** */ if (ias->range_min <= i_ias->range_min && ias->range_max >= i_ias->range_max) { sflist_remove_node(&reduced_networks, node); changed = 1; } /* i_ias ************ ias *** or i_ias ************ ias ************ */ else if (ias->range_min > i_ias->range_min && ias->range_min <= i_ias->range_max) { tmp = i_ias->range_max; i_ias->range_max = ias->range_min - 1; if (ias->range_max < tmp) { if (!(new_ias = calloc(1, sizeof(*new_ias)))) return -1; *new_ias = *i_ias; new_ias->range_min = ias->range_max + 1; new_ias->range_max = tmp; if (sflist_add_tail(&reduced_networks, new_ias)) { free(new_ias); return -1; } changed = 1; } } /* i_ias ************ ias ************ or i_ias ************ ias **** */ else if (ias->range_max >= i_ias->range_min && ias->range_max <= i_ias->range_max) { tmp = i_ias->range_min; i_ias->range_min = ias->range_max + 1; if (ias->range_min > tmp) { if (!(new_ias = calloc(1, sizeof(*new_ias)))) return -1; *new_ias = *i_ias; new_ias->range_min = tmp; new_ias->range_max = ias->range_min - 1; if (sflist_add_tail(&reduced_networks, new_ias)) { free(new_ias); return -1; } changed = 1; } } } else { sflist_remove_node(&reduced_networks, node); changed = 1; } if (changed) node = sflist_first_node(&reduced_networks); else node = sflist_next_node(&reduced_networks); } free(ias); } else { node = sflist_first_node(&reduced_networks); while (node) { changed = 0; i_ias = SFLIST_NODE_TO_DATA(node); if (i_ias) { if (ias->info.type == i_ias->info.type) { /* i_ias ****** ias *************** */ if (ias->range_min <= i_ias->range_min && ias->range_max >= i_ias->range_max) { sflist_remove_node(&reduced_networks, node); changed = 1; free(i_ias); i_ias = NULL; } /* i_ias *************** ias ****** */ else if (i_ias->range_min <= ias->range_min && i_ias->range_max >= ias->range_max) { ias->range_min = i_ias->range_min; ias->range_max = i_ias->range_max; sflist_remove_node(&reduced_networks, node); changed = 1; free(i_ias); i_ias = NULL; } /* i_ias ************ ias ************ */ else if (ias->range_min > i_ias->range_min && ias->range_min <= i_ias->range_max) { i_ias->range_max = ias->range_min - 1; } /* i_ias ************ ias ************ */ else if (ias->range_max >= i_ias->range_min && ias->range_max < i_ias->range_max) { i_ias->range_min = ias->range_max + 1; } } else /* different types */ { /* i_ias ****** ias ****** */ if (ias->range_min == i_ias->range_min && ias->range_max == i_ias->range_max) { i_ias->info.type = ias->info.type; free(ias); ias = NULL; break; } /* i_ias ****** ias *************** */ else if (ias->range_min < i_ias->range_min && ias->range_max >= i_ias->range_max) { sflist_remove_node(&reduced_networks, node); free(i_ias); i_ias = NULL; changed = 1; } /* i_ias ************ ias *** or i_ias ************ ias ************ or i_ias ************ ias ****** */ else if (ias->range_min > i_ias->range_min && ias->range_min <= i_ias->range_max) { tmp = i_ias->range_max; i_ias->range_max = ias->range_min - 1; if (ias->range_max < tmp) { if (!(new_ias = calloc(1, sizeof(*new_ias)))) return -1; *new_ias = *i_ias; new_ias->range_min = ias->range_max + 1; new_ias->range_max = tmp; if (sflist_add_tail(&reduced_networks, new_ias)) { free(new_ias); return -1; } changed = 1; } } /* i_ias ************ ias ************ or i_ias ************ ias **** */ else if (ias->range_max > i_ias->range_min && ias->range_max < i_ias->range_max) { i_ias->range_min = ias->range_max + 1; } } } else { sflist_remove_node(&reduced_networks, node); changed = 1; } if (changed) node = sflist_first_node(&reduced_networks); else node = sflist_next_node(&reduced_networks); } if (ias && sflist_add_tail(&reduced_networks, ias)) { return -1; } } } /* Minimize the ranges */ node = sflist_first_node(&reduced_networks); while(node) { /* i_ias is lowest in the list, so it takes precedence */ changed = 0; if ((ias = SFLIST_NODE_TO_DATA(node))) { inode = sflist_next_node(&reduced_networks); while (inode) { if ((i_ias = SFLIST_NODE_TO_DATA(inode))) { if (ias->info.type == i_ias->info.type) { /* i_ias ************ ias *** */ if (ias->range_min && (i_ias->range_max+1) == ias->range_min) { i_ias->range_max = ias->range_max; sflist_remove_node(&reduced_networks, node); free(ias); changed = 1; break; } /* i_ias ************ ias ***** */ else if (i_ias->range_min && (ias->range_max+1) == i_ias->range_min) { i_ias->range_min = ias->range_min; sflist_remove_node(&reduced_networks, node); free(ias); changed = 1; break; } } } inode = sflist_next_node(&reduced_networks); } } else sflist_remove_node(&reduced_networks, node); if (changed) node = sflist_first_node(&reduced_networks); else node = sflist_next_node(&reduced_networks); } sflist_static_free_all(networks, &free); while ((ias = (Network *)sflist_remove_head(&reduced_networks))) { if (sflist_add_tail(networks, ias)) { return -1; } } return 0; } static inline int NetworkSet_ReduceNetworkSet6(SF_LIST *networks) { Network6 *ias; Network6 *i_ias; Network6 *new_ias; NSIPv6Addr tmp; NSIPv6Addr tmp2; SF_LNODE *node; SF_LNODE *inode; int changed; SF_LIST reduced_networks; if (!sflist_count(networks)) return 0; sflist_init(&reduced_networks); while ((ias = (Network6 *)sflist_remove_head(networks))) { /* ias is lowest in the list, so it takes precedence */ if (ias->info.ip_not) { node = sflist_first_node(&reduced_networks); while (node) { changed = 0; i_ias = SFLIST_NODE_TO_DATA(node); if (i_ias) { /* i_ias ****** ias *************** */ if (NSIPv6AddrCompare(&ias->range_min, &i_ias->range_min) <= 0 && NSIPv6AddrCompare(&ias->range_max, &i_ias->range_max) >= 0) { sflist_remove_node(&reduced_networks, node); changed = 1; } /* i_ias ************ ias *** or i_ias ************ ias ************ */ else if (NSIPv6AddrCompare(&ias->range_min, &i_ias->range_min) > 0 && NSIPv6AddrCompare(&ias->range_min, &i_ias->range_max) <= 0) { tmp = i_ias->range_max; i_ias->range_max = ias->range_min; NSIPv6AddrDec(&i_ias->range_max); if (NSIPv6AddrCompare(&ias->range_max, &tmp) < 0) { if (!(new_ias = calloc(1, sizeof(*new_ias)))) return -1; *new_ias = *i_ias; new_ias->range_min = ias->range_max; NSIPv6AddrInc(&new_ias->range_min); new_ias->range_max = tmp; if (sflist_add_tail(&reduced_networks, new_ias)) { free(new_ias); return -1; } changed = 1; } } /* i_ias ************ ias ************ or i_ias ************ ias **** */ else if (NSIPv6AddrCompare(&ias->range_max, &i_ias->range_min) >= 0 && NSIPv6AddrCompare(&ias->range_max, &i_ias->range_max) <= 0) { tmp = i_ias->range_min; i_ias->range_min = ias->range_max; NSIPv6AddrInc(&i_ias->range_min); if (NSIPv6AddrCompare(&ias->range_min, &tmp) > 0) { if (!(new_ias = calloc(1, sizeof(*new_ias)))) return -1; *new_ias = *i_ias; new_ias->range_min = tmp; new_ias->range_max = ias->range_min; NSIPv6AddrDec(&new_ias->range_max); if (sflist_add_tail(&reduced_networks, new_ias)) { free(new_ias); return -1; } changed = 1; } } } else { sflist_remove_node(&reduced_networks, node); changed = 1; } if (changed) node = sflist_first_node(&reduced_networks); else node = sflist_next_node(&reduced_networks); } free(ias); } else { node = sflist_first_node(&reduced_networks); while (node) { changed = 0; i_ias = SFLIST_NODE_TO_DATA(node); if (i_ias) { if (ias->info.type == i_ias->info.type) { /* i_ias ****** ias *************** */ if (NSIPv6AddrCompare(&ias->range_min, &i_ias->range_min) <= 0 && NSIPv6AddrCompare(&ias->range_max, &i_ias->range_max) >= 0) { sflist_remove_node(&reduced_networks, node); changed = 1; free(i_ias); i_ias = NULL; } /* i_ias *************** ias ****** */ else if (NSIPv6AddrCompare(&i_ias->range_min, &ias->range_min) <= 0 && NSIPv6AddrCompare(&i_ias->range_max, &ias->range_max) >= 0) { ias->range_min = i_ias->range_min; ias->range_max = i_ias->range_max; sflist_remove_node(&reduced_networks, node); changed = 1; free(i_ias); i_ias = NULL; } /* i_ias ************ ias ************ */ else if (NSIPv6AddrCompare(&ias->range_min, &i_ias->range_min) > 0 && NSIPv6AddrCompare(&ias->range_min, &i_ias->range_max) <= 0) { i_ias->range_max = ias->range_min; NSIPv6AddrDec(&i_ias->range_max); } /* i_ias ************ ias ************ */ else if (NSIPv6AddrCompare(&ias->range_max, &i_ias->range_min) >= 0 && NSIPv6AddrCompare(&ias->range_max, &i_ias->range_max) < 0) { i_ias->range_min = ias->range_max; NSIPv6AddrInc(&i_ias->range_min); } } else /* different types */ { /* i_ias ****** ias ****** */ if (!NSIPv6AddrCompare(&ias->range_min, &i_ias->range_min) && !NSIPv6AddrCompare(&ias->range_max, &i_ias->range_max)) { i_ias->info.type = ias->info.type; free(ias); ias = NULL; break; } /* i_ias ****** ias *************** */ else if (NSIPv6AddrCompare(&ias->range_min, &i_ias->range_min) < 0 && NSIPv6AddrCompare(&ias->range_max, &i_ias->range_max) >= 0) { sflist_remove_node(&reduced_networks, node); free(i_ias); i_ias = NULL; changed = 1; } /* i_ias ************ ias *** or i_ias ************ ias ************ or i_ias ************ ias ****** */ else if (NSIPv6AddrCompare(&ias->range_min, &i_ias->range_min) > 0 && NSIPv6AddrCompare(&ias->range_min, &i_ias->range_max) <= 0) { tmp = i_ias->range_max; i_ias->range_max = ias->range_min; NSIPv6AddrDec(&i_ias->range_max); if (NSIPv6AddrCompare(&ias->range_max, &tmp) < 0) { if (!(new_ias = calloc(1, sizeof(*new_ias)))) return -1; *new_ias = *i_ias; new_ias->range_min = ias->range_max; NSIPv6AddrInc(&new_ias->range_min); new_ias->range_max = tmp; if (sflist_add_tail(&reduced_networks, new_ias)) { free(new_ias); return -1; } changed = 1; } } /* i_ias ************ ias ************ or i_ias ************ ias **** */ else if (NSIPv6AddrCompare(&ias->range_max, &i_ias->range_min) > 0 && NSIPv6AddrCompare(&ias->range_max, &i_ias->range_max) < 0) { i_ias->range_min = ias->range_max; NSIPv6AddrInc(&i_ias->range_min); } } } else { sflist_remove_node(&reduced_networks, node); changed = 1; } if (changed) node = sflist_first_node(&reduced_networks); else node = sflist_next_node(&reduced_networks); } if (ias && sflist_add_tail(&reduced_networks, ias)) { return -1; } } } /* Minimize the ranges */ node = sflist_first_node(&reduced_networks); while(node) { /* i_ias is lowest in the list, so it takes precedence */ changed = 0; if ((ias = SFLIST_NODE_TO_DATA(node))) { inode = sflist_next_node(&reduced_networks); while (inode) { if ((i_ias = SFLIST_NODE_TO_DATA(inode))) { if (ias->info.type == i_ias->info.type) { /* i_ias ************ ias *** */ tmp = i_ias->range_max; NSIPv6AddrInc(&tmp); tmp2 = ias->range_max; NSIPv6AddrInc(&tmp2); if ((ias->range_min.lo || ias->range_min.hi) && !NSIPv6AddrCompare(&tmp, &ias->range_min)) { i_ias->range_max = ias->range_max; sflist_remove_node(&reduced_networks, node); free(ias); changed = 1; break; } /* i_ias ************ ias ***** */ else if ((i_ias->range_min.lo || i_ias->range_min.hi) && !NSIPv6AddrCompare(&tmp2, &i_ias->range_min)) { i_ias->range_min = ias->range_min; sflist_remove_node(&reduced_networks, node); free(ias); changed = 1; break; } } } inode = sflist_next_node(&reduced_networks); } } else sflist_remove_node(&reduced_networks, node); if (changed) node = sflist_first_node(&reduced_networks); else node = sflist_next_node(&reduced_networks); } sflist_static_free_all(networks, &free); while ((ias = (Network6 *)sflist_remove_head(&reduced_networks))) { if (sflist_add_tail(networks, ias)) { return -1; } } return 0; } int NetworkSet_Reduce(NetworkSet *network_set) { SFXHASH_NODE *hnode; unsigned id; int rval; SF_LIST ordered_networks; Network *network; Network6 *network6; unsigned tmp; int count; int i; int j; if(!network_set) return -1; for (hnode=sfxhash_gfindfirst(network_set->ids); hnode; hnode=sfxhash_gfindnext(network_set->ids)) { id = *(unsigned *)(hnode->data); if ((rval = NetworkSet_OrderByNetmask(&ordered_networks, &network_set->networks, id)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } if ((rval = NetworkSet_ReduceNetworkSet(&ordered_networks)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } if ((rval = NetworkSet_AddList(&network_set->networks, &ordered_networks)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } } if ((rval = NetworkSet_ReduceNetworkSet(&network_set->networks)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } tmp = 0; if ((rval = NetworkSet_Count(network_set, &tmp)) != 0) return rval; count = (int)tmp; if (count > 0) { network_set->count = count; if (network_set->pnetwork) { free(network_set->pnetwork); network_set->pnetwork = NULL; } if (!(network_set->pnetwork = calloc(count, sizeof(*network_set->pnetwork)))) return -1; for (network = (Network *)sflist_first(&network_set->networks), i = 0; network && i < count; network = (Network *)sflist_next(&network_set->networks)) { network_set->pnetwork[i++] = network; } /* bubble sort this array */ for(i = (count - 1); i >= 0; i--) { for(j = 1; j <= i ; j++ ) { if(network_set->pnetwork[j-1]->range_min > network_set->pnetwork[j]->range_min) { network = network_set->pnetwork[j-1]; network_set->pnetwork[j-1] = network_set->pnetwork[j]; network_set->pnetwork[j] = network; } } } } for (hnode=sfxhash_gfindfirst(network_set->ids6); hnode; hnode=sfxhash_gfindnext(network_set->ids6)) { id = *(unsigned *)(hnode->data); if ((rval = NetworkSet_OrderByNetmask(&ordered_networks, &network_set->networks6, id)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } if ((rval = NetworkSet_ReduceNetworkSet6(&ordered_networks)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } if ((rval = NetworkSet_AddList(&network_set->networks6, &ordered_networks)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } } if ((rval = NetworkSet_ReduceNetworkSet6(&network_set->networks6)) != 0) { sflist_free_all(&ordered_networks, &free); return rval; } tmp = 0; if ((rval = NetworkSet_Count6(network_set, &tmp)) != 0) return rval; count = (int)tmp; if (count > 0) { network_set->count6 = count; if(network_set->pnetwork6) { free(network_set->pnetwork6); network_set->pnetwork6 = NULL; } if (!(network_set->pnetwork6 = calloc(count, sizeof(*network_set->pnetwork6)))) return -1; for (network6 = (Network6 *)sflist_first(&network_set->networks6), i = 0; network6 && i < count; network6 = (Network6 *)sflist_next(&network_set->networks6)) { network_set->pnetwork6[i++] = network6; } /* bubble sort this array */ for(i = (count - 1); i >= 0; i--) { for(j = 1; j <= i ; j++ ) { if(NSIPv6AddrCompare(&network_set->pnetwork6[j-1]->range_min, &network_set->pnetwork6[j]->range_min) > 0) { network6 = network_set->pnetwork6[j-1]; network_set->pnetwork6[j-1] = network_set->pnetwork6[j]; network_set->pnetwork6[j] = network6; } } } } return 0; } NetworkSet *NetworkSet_Copy(NetworkSet *network_set) { NetworkSet *new_set; Network *network; Network6 *network6; if(!network_set) return NULL; if (NetworkSet_New(&new_set) != 0) return NULL; for (network = (Network *)sflist_first(&network_set->networks); network; network = (Network *)sflist_next(&network_set->networks)) { if (NetworkSet_AddNetworkRangeEx(new_set, network->range_min, network->range_max, network->info.netmask, network->info.ip_not, network->info.id, network->info.type) != 0) { NetworkSet_Destroy(new_set); return NULL; } } for (network6 = (Network6 *)sflist_first(&network_set->networks6); network6; network6 = (Network6 *)sflist_next(&network_set->networks6)) { if (NetworkSet_AddNetworkRange6Ex(new_set, &network6->range_min, &network6->range_max, network6->info.netmask, network6->info.ip_not, network6->info.id, network6->info.type) != 0) { NetworkSet_Destroy(new_set); return NULL; } } return new_set; } int NetworkSet_AddSet(NetworkSet *dest_set, NetworkSet *src_set) { Network *network; Network6 *network6; int rval; if(!src_set || !dest_set) return -1; for (network = (Network *)sflist_first(&src_set->networks); network; network = (Network *)sflist_next(&src_set->networks)) { if ((rval=NetworkSet_AddNetworkRangeEx(dest_set, network->range_min, network->range_max, network->info.netmask, network->info.ip_not, network->info.id, network->info.type)) != 0) { return rval; } } for (network6 = (Network6 *)sflist_first(&src_set->networks6); network6; network6 = (Network6 *)sflist_next(&src_set->networks6)) { if ((rval=NetworkSet_AddNetworkRange6Ex(dest_set, &network6->range_min, &network6->range_max, network6->info.netmask, network6->info.ip_not, network6->info.id, network6->info.type)) != 0) { return rval; } } return 0; } snort-2.9.20/src/dynamic-preprocessors/appid/util/sfutil.h0000644000175000017500000000250414241075624021752 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef SFUTIL_H #define SFUTIL_H #include "stdint.h" #include "sflsq.h" #include "sfxhash.h" #include "sfghash.h" int SFGetRelocatePathForFile(const char * const content_file, const char ** const root_path); extern int Tokenize(char *data, char *toklist[]); extern int strip(char *data); extern void InitNetmasks(uint32_t netmasks[]); extern int Split(char *data, char **toklist, int max_toks, const char *separator); #endif /* SFUTIL_H */ snort-2.9.20/src/dynamic-preprocessors/appid/util/fw_avltree.c0000644000175000017500000002370414241075603022577 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include "fw_avltree.h" #include #include static inline int is_root(struct FwAvlNode* node) { return (node->parent == NULL); } static inline int get_balance(struct FwAvlNode* node) { return node->balance; } static inline void set_balance(int balance, struct FwAvlNode* node) { node->balance = balance; } static inline int inc_balance(struct FwAvlNode* node) { return ++node->balance; } static inline int dec_balance(struct FwAvlNode* node) { return --node->balance; } static inline struct FwAvlNode* get_parent(struct FwAvlNode* node) { return node->parent; } static inline void set_parent(struct FwAvlNode* parent, struct FwAvlNode* node) { node->parent = parent; } static inline struct FwAvlNode* new_node(uint32_t key, void* data) { struct FwAvlNode* node = calloc(1, sizeof(struct FwAvlNode)); if(node != NULL) { node->key = key; node->data = data; } return node; } static inline struct FwAvlNode* get_first(struct FwAvlNode* node) { while(node->left != NULL) node = node->left; return node; } static inline struct FwAvlNode* get_last(struct FwAvlNode* node) { while(node->right != NULL) node = node->right; return node; } struct FwAvlNode* fwAvlFirst(const struct FwAvlTree* tree) { if((tree != 0) && (tree->root != 0)) return get_first(tree->root); else return NULL; } struct FwAvlNode* fwAvlLast(const struct FwAvlTree* tree) { if((tree != 0) && (tree->root != 0)) return get_last(tree->root); else return NULL; } struct FwAvlNode* fwAvlNext(struct FwAvlNode* node) { struct FwAvlNode* parent = NULL; struct FwAvlNode* tmp; if(node->right != NULL) { return get_first(node->right); } else { tmp = node; while( ((parent = get_parent(tmp)) != NULL) && (parent->right == tmp) ) tmp = parent; } return parent; } struct FwAvlNode* fwAvlPrev(struct FwAvlNode* node) { struct FwAvlNode* parent; struct FwAvlNode* tmp; if(node->left != NULL) { tmp = get_first(node->left); } else { tmp = node; while( ((parent = get_parent(tmp)) != NULL) && (parent->left == tmp) ) tmp = parent; } return tmp; } static void rotate_left(struct FwAvlNode* node, struct FwAvlTree* tree) { struct FwAvlNode* p = node; struct FwAvlNode* q = node->right; struct FwAvlNode* parent = get_parent(node); if(!is_root(p)) { if(parent->left == p) parent->left = q; else parent->right = q; } else { tree->root = q; } set_parent(parent, q); set_parent(q, p); p->right = q->left; if(p->right != NULL) { set_parent(p, p->right); } q->left = p; } static void rotate_right(struct FwAvlNode* node, struct FwAvlTree* tree) { struct FwAvlNode* p = node; struct FwAvlNode* q = node->left; struct FwAvlNode* parent = get_parent(node); if(!is_root(p)) { if(parent->left == p) parent->left = q; else parent->right = q; } else { tree->root = q; } set_parent(parent, q); set_parent(q, p); p->left = q->right; if(p->left != NULL) { set_parent(p, p->left); } q->right = p; } static inline struct FwAvlNode* do_lookup(const uint32_t key, const struct FwAvlTree* tree, struct FwAvlNode** pparent, struct FwAvlNode** unbalanced, int* is_left) { struct FwAvlNode* node = tree->root; *pparent = NULL; *unbalanced = node; *is_left = 0; while (node != NULL) { if(get_balance(node) != 0) { *unbalanced = node; } *pparent = node; if(key == node->key) { return node; } else { if((*is_left = node->key > key) != 0) node = node->left; else node = node->right; } } return NULL; } void* fwAvlLookup(const uint32_t key, const struct FwAvlTree* tree) { struct FwAvlNode* node = NULL; struct FwAvlNode* pparent; struct FwAvlNode* unbalanced; int is_left; if(tree == 0) { return NULL; } node = do_lookup(key, tree, &pparent, &unbalanced, &is_left); if(node != NULL) { return node->data; } else { return NULL; } } static inline void set_child(struct FwAvlNode* child, struct FwAvlNode* node, int left) { if(left != 0) node->left = child; else node->right = child; } int fwAvlInsert(uint32_t key, void* data, struct FwAvlTree* tree) { int is_left; struct FwAvlNode* parent; struct FwAvlNode* right; struct FwAvlNode* left; struct FwAvlNode* unbalanced; struct FwAvlNode* node; if(do_lookup(key, tree, &parent, &unbalanced, &is_left) != NULL) return 1; if(!(node = new_node(key, data))) return -1; tree->count++; if(parent == NULL) { tree->root = node; tree->first = node; tree->last = node; return 0; } if(is_left != 0) { if(parent == tree->first) tree->first = node; } else { if(parent == tree->last) tree->last = node; } set_parent(parent, node); set_child(node, parent, is_left); while(1) { if(parent->left == node) dec_balance(parent); else inc_balance(parent); if(parent == unbalanced) break; node = parent; parent = get_parent(node); } switch(get_balance(unbalanced)) { case 1: case -1: tree->height++; break; case 0: break; case 2: right = unbalanced->right; if(get_balance(right) == 1) { set_balance(0, unbalanced); set_balance(0, right); } else { switch(get_balance(right->left)) { case 1: set_balance(-1, unbalanced); set_balance(0, right); break; case 0: set_balance(0, unbalanced); set_balance(0, right); break; case -1: set_balance(0, unbalanced); set_balance(1, right); break; } set_balance(0, right->left); rotate_right(right, tree); } rotate_left(unbalanced, tree); break; case -2: left = unbalanced->left; if(get_balance(left) == -1) { set_balance(0, unbalanced); set_balance(0, left); } else { switch(get_balance(left->right)) { case 1: set_balance(0, unbalanced); set_balance(-1, left); break; case 0: set_balance(0, unbalanced); set_balance(0, left); break; case -1: set_balance(1, unbalanced); set_balance(0, left); break; } set_balance(0, left->right); rotate_left(left, tree); } rotate_right(unbalanced, tree); break; } return 0; } struct FwAvlTree* fwAvlInit(void) { struct FwAvlTree* tree = calloc(1, sizeof(struct FwAvlTree)); return tree; /* The calling function checks for NULL */ } struct FwQNode* newFwQNode(struct FwAvlNode* treeNode) { struct FwQNode* q_node = calloc(1, sizeof(struct FwQNode)); if(q_node != NULL) { q_node->treeNode = treeNode; q_node->next = NULL; } return(q_node); } struct FwQNode* fwAvlSerialize(struct FwAvlTree* tree) { struct FwQNode* head; struct FwQNode* node; struct FwQNode* tail; if((tree == NULL) || (tree->root == NULL)) return NULL; head = newFwQNode(tree->root); node = head; tail = head; while(node) { if(node->treeNode->left != NULL) { tail->next = newFwQNode(node->treeNode->left); tail = tail->next; } if(node->treeNode->right != NULL) { tail->next = newFwQNode(node->treeNode->right); tail = tail->next; } node = node->next; } return head; } void fwAvlDeleteTree(struct FwAvlTree* tree, void (*dataDelete)(void* data)) { struct FwQNode* node = fwAvlSerialize(tree); struct FwQNode* tmp; while(node != NULL) { if(dataDelete) dataDelete(node->treeNode->data); free(node->treeNode); tmp = node; node = node->next; free(tmp); } free(tree); } snort-2.9.20/src/dynamic-preprocessors/appid/util/common_util.h0000644000175000017500000000656614241075602023001 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __COMMON_UTIL_H__ #define __COMMON_UTIL_H__ #include #include #include #include #include #include typedef struct _FWDebugSessionConstraints { struct in6_addr sip; int sip_flag; struct in6_addr dip; int dip_flag; uint16_t sport; uint16_t dport; uint8_t protocol; } FWDebugSessionConstraints; #define FW_DEBUG_SESSION_ID_SIZE (39+1+5+4+39+1+5+1+3+1+1+1+2+1+10+1+1+1+10+1) typedef struct _config_item { char *name; /* name of the config item */ char *value; /* config item value */ } ConfigItem; #define MAX_LINE 2048 #define MAX_TOKS 256 /* This structure should be same as struct define in util.h */ typedef struct _ThrottleInfo { time_t lastUpdate; /*Within this duration (in seconds), maximal one distinct message is logged*/ uint32_t duration_to_log; uint64_t count; /*Till the message count reaches to count_to_log, maximal one distinct message is logged*/ uint64_t count_to_log; }ThrottleInfo; extern time_t packetTimeOffset; extern time_t packetTime; extern int packetTimeOffsetSet; #define GetPacketRealTime packetTime static inline void SetPacketRealTime(time_t pktTime) { if (!packetTimeOffsetSet) { time_t tmp = time(NULL); packetTimeOffsetSet = 1; if (pktTime < tmp) packetTimeOffset = tmp - pktTime; } packetTime = pktTime + packetTimeOffset; } static inline void DumpHex(FILE *fp, const uint8_t *data, unsigned len) { char str[18]; unsigned i; unsigned pos; char c; for (i=0, pos=0; i #include #include #include #include #include #include #include #include #include #include "common_util.h" #include "sf_dynamic_preprocessor.h" #if !defined(s6_addr32) #define s6_addr8 __u6_addr.__u6_addr8 #define s6_addr16 __u6_addr.__u6_addr16 #define s6_addr32 __u6_addr.__u6_addr32 #endif void ConfigItemFree(ConfigItem *ci) { if (ci) { if (ci->name) free(ci->name); if (ci->value) free(ci->value); free(ci); } } int Split(char *data, char **toklist, int max_toks, const char *separator) { char **ap; int argcount = 0; memset(toklist, 0, max_toks * sizeof(*toklist)); for(ap= (char **) toklist; ap < &toklist[max_toks] && (*ap=strsep(&data, separator)) != NULL;) { if(**ap != '\0') { ap++; argcount++; } } return argcount; } void InitNetmasks(uint32_t netmasks[]) { netmasks[0] = 0x0; netmasks[1] = 0x80000000; netmasks[2] = 0xC0000000; netmasks[3] = 0xE0000000; netmasks[4] = 0xF0000000; netmasks[5] = 0xF8000000; netmasks[6] = 0xFC000000; netmasks[7] = 0xFE000000; netmasks[8] = 0xFF000000; netmasks[9] = 0xFF800000; netmasks[10] = 0xFFC00000; netmasks[11] = 0xFFE00000; netmasks[12] = 0xFFF00000; netmasks[13] = 0xFFF80000; netmasks[14] = 0xFFFC0000; netmasks[15] = 0xFFFE0000; netmasks[16] = 0xFFFF0000; netmasks[17] = 0xFFFF8000; netmasks[18] = 0xFFFFC000; netmasks[19] = 0xFFFFE000; netmasks[20] = 0xFFFFF000; netmasks[21] = 0xFFFFF800; netmasks[22] = 0xFFFFFC00; netmasks[23] = 0xFFFFFE00; netmasks[24] = 0xFFFFFF00; netmasks[25] = 0xFFFFFF80; netmasks[26] = 0xFFFFFFC0; netmasks[27] = 0xFFFFFFE0; netmasks[28] = 0xFFFFFFF0; netmasks[29] = 0xFFFFFFF8; netmasks[30] = 0xFFFFFFFC; netmasks[31] = 0xFFFFFFFE; netmasks[32] = 0xFFFFFFFF; } int strip(char *data) { int size; char *idx; idx = data; size = 0; while (*idx) { if ((*idx == '\n') || (*idx == '\r')) { *idx = 0; break; } if(*idx == '\t') { *idx = ' '; } size++; idx++; } return size; } int Tokenize(char *data, char *toklist[]) { char **ap; int argcount = 0; int i = 0; char *tok; int drop_further = 0; for (ap = (char **)toklist; ap < &toklist[MAX_TOKS] && (*ap = strsep(&data, " ")) != NULL;) { if (**ap != '\0') { ap++; argcount++; } } *ap = NULL; /* scan for comments */ while (i < argcount) { tok = toklist[i]; if (tok[0] == '#' && !drop_further) { argcount = i; drop_further = 1; } if (drop_further) { toklist[i] = NULL; } i++; } return argcount; } snort-2.9.20/src/dynamic-preprocessors/appid/util/sf_multi_mpse.c0000644000175000017500000003106714241075616023314 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include "stdio.h" #include #include #include #include "sf_multi_mpse.h" #include "string.h" #include "sf_dynamic_preprocessor.h" struct _patternRootNode; typedef struct _tPatternList { tMlpPattern pattern; void *userData; /*client/service info */ struct _tPatternList *nextPattern; struct _patternRootNode *nextLevelMatcher; } tPatternList ; /*Root node */ typedef struct _patternRootNode { void *patternTree; tPatternList *patternList; tPatternList *lastPattern; unsigned int level; /*some searches may be specific to levels. Increments from 1 at top level, */ } tPatternRootNode ; /*Used to track matched patterns. */ typedef struct { tPatternList *patternNode; size_t index; unsigned int level; } MatchedPattern; static int compareAppUrlPatterns(const void *p1, const void *p2); static int createTreesRecusively(void *root); static void destroyTreesRecursively(void *root); static void dumpTreesRecursively(void *root, int level); static int addPatternRecursively(void *root, const tMlpPattern **inputPatternList, void *metaData, int level); static int longest_pattern_match (void *id, void *unused_tree, int index, void *data, void *unused_neg); static int url_pattern_match (void *id, void *unused_tree, int index, void *data, void *unused_neg); void *mlpCreate(void) { tPatternRootNode *root = calloc(1, sizeof(tPatternRootNode)); if (root) root->level = 0; return root; } /*last pattern should be NULL */ int mlpAddPattern(void *root, const tMlpPattern **inputPatternList, void *metaData) { return addPatternRecursively(root, inputPatternList, metaData, 0); } int mlpProcessPatterns(void *root) { int rvalue; rvalue = createTreesRecusively(root); if (rvalue) destroyTreesRecursively(root); return rvalue; } void *mlpMatchPatternLongest(void *root, tMlpPattern **inputPatternList) { return mlpMatchPatternCustom(root, inputPatternList, longest_pattern_match); } void *mlpMatchPatternUrl(void *root, tMlpPattern **inputPatternList) { return mlpMatchPatternCustom(root, inputPatternList, url_pattern_match); } static inline int matchDomainPattern(MatchedPattern mp, const uint8_t *pattern) { if (!pattern) return -1; return (mp.level == 0 && !(mp.index == 0 || pattern[mp.index-1] == '.')); } void *mlpMatchPatternCustom(void *root, tMlpPattern **inputPatternList, int (*callback)(void*, void*, int, void*, void*)) { MatchedPattern mp = {0}; void *data = NULL; void *tmpData = NULL; tPatternList *patternNode; tPatternRootNode *rootNode = (tPatternRootNode *)root; tMlpPattern *pattern = *inputPatternList; if (!rootNode || !pattern || !pattern->pattern) return NULL; mp.level = rootNode->level; _dpd.searchAPI->search_instance_find_all(rootNode->patternTree, (char *)pattern->pattern, pattern->patternSize, 0, callback, (void*)&mp); patternNode = mp.patternNode; if(patternNode) { if (matchDomainPattern(mp, pattern->pattern) != 0) return NULL; data = patternNode->userData; tmpData = mlpMatchPatternCustom(patternNode->nextLevelMatcher, ++inputPatternList, callback); if (tmpData) data = tmpData; } return data; } void mlpDestroy(void *root) { destroyTreesRecursively(root); } void mlpDump(void *root) { dumpTreesRecursively(root, 0); } /*alphabetically ordering */ static int compareAppUrlPatterns(const void *p1, const void *p2) { tMlpPattern *pat1 = (tMlpPattern *)p1; tMlpPattern *pat2 = (tMlpPattern *)p2; int rValue; size_t minSize; /*first compare patterns by the smaller pattern size, if same then size wins */ minSize = (pat1->patternSize > pat2->patternSize)? pat2->patternSize: pat1->patternSize; rValue = memcmp(pat1->pattern, pat2->pattern, minSize); if (rValue) return rValue; return ((int)pat1->patternSize - (int)pat2->patternSize); } /*pattern trees are not freed on error because in case of error, caller should call detroyTreesRecursively. */ static int createTreesRecusively(void *root) { tPatternRootNode *rootNode = (tPatternRootNode *)root; void *patternMatcher; tPatternList *patternNode; /* set up the MPSE for url patterns */ if (!(patternMatcher = rootNode->patternTree = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return -1; for (patternNode = rootNode->patternList; patternNode; patternNode = patternNode->nextPattern) { /*recursion into next lower level */ if (patternNode->nextLevelMatcher) { if (createTreesRecusively(patternNode->nextLevelMatcher)) return -1; } _dpd.searchAPI->search_instance_add_ex(patternMatcher, (void *)patternNode->pattern.pattern, patternNode->pattern.patternSize, patternNode, STR_SEARCH_CASE_SENSITIVE); } _dpd.searchAPI->search_instance_prep(patternMatcher); return 0; } static void destroyTreesRecursively(void *root) { tPatternRootNode *rootNode = (tPatternRootNode *)root; tPatternList *patternNode; while ((patternNode = rootNode->patternList)) { /*recursion into next lower level */ if (patternNode->nextLevelMatcher) { destroyTreesRecursively(patternNode->nextLevelMatcher); } rootNode->patternList = patternNode->nextPattern; free(patternNode); } _dpd.searchAPI->search_instance_free(rootNode->patternTree); free(rootNode); } static void dumpTreesRecursively(void *root, int level) { tPatternRootNode *rootNode = (tPatternRootNode *)root; tPatternList *patternNode; char *offset; offset = malloc(4*level+2); if (!offset) return; memset(offset, ' ', 4*level+1); offset[4*level] = '\0'; for (patternNode = rootNode->patternList; patternNode; patternNode = patternNode->nextPattern) { printf("%sPattern %s, size %u, userData %p\n", offset, (char *)patternNode->pattern.pattern, (u_int32_t)patternNode->pattern.patternSize, patternNode->userData); /*recursion into next lower level */ if (patternNode->nextLevelMatcher) { dumpTreesRecursively(patternNode->nextLevelMatcher, (level+1)); } } free(offset); } static int longest_pattern_match (void *id, void *unused_tree, int index, void *data, void *unused_neg) { tPatternList *target = (tPatternList *)id; MatchedPattern *match = (MatchedPattern *)data; int newMatchWins = 0; /*printf("LongestMatcher: level %d, index: %d, matched %s\n", matches->level, index, target->pattern.pattern); */ /*first match */ if (!match->patternNode) newMatchWins = 1; /*subsequent longer match */ else if (match->patternNode->pattern.patternSize < target->pattern.patternSize) newMatchWins = 1; if (newMatchWins) { /*printf("new pattern wins\n"); */ match->patternNode = target; match->index = index; } return 0; } static int url_pattern_match (void *id, void *unused_tree, int index, void *data, void *unused_neg) { tPatternList *target = (tPatternList *)id; MatchedPattern *match = (MatchedPattern *)data; int newMatchWins = 0; /*printf("UrlMatcher: level %d, index: %d, matched %s\n", match->level, index, target->pattern.pattern); */ /*first match */ if (!match->patternNode) newMatchWins = 1; /*subsequent longer match */ else if (match->patternNode->pattern.patternSize < target->pattern.patternSize) newMatchWins = 1; else if (match->patternNode->pattern.patternSize == target->pattern.patternSize) { /*host part matching towards later part is better. This is not designed to prevent mis-identifying */ /*url 'www.spoof_for_google.google.com.phishing.com' as google. */ if ((match->level == 0) && (match->index < (unsigned int) index)) newMatchWins = 1; /*path part matching towards lower index is better */ if ((match->level == 1) && (match->index > (unsigned int) index)) newMatchWins = 1; } if (newMatchWins) { /*printf("new pattern wins\n"); */ match->patternNode = target; match->index = index; } return 0; } static int addPatternRecursively(void *root, const tMlpPattern **inputPatternList, void *metaData, int level) { tPatternRootNode *rootNode = (tPatternRootNode *)root; tPatternList* prevNode = NULL; tPatternList *patternList; tPatternList *newNode; const tMlpPattern *nextPattern; const tMlpPattern *patterns = *inputPatternList; int rvalue; if (!rootNode || !patterns || !patterns->pattern) return -1; for (patternList = rootNode->patternList; patternList; prevNode = patternList, patternList = patternList->nextPattern) { rvalue = compareAppUrlPatterns(patterns, patternList); if (rvalue < 0) continue; if (rvalue == 0) { nextPattern = *(inputPatternList+1); if (!nextPattern || !nextPattern->pattern) { /*overriding any previous userData. */ patternList->userData = metaData; return 0; } return addPatternRecursively(patternList->nextLevelMatcher, inputPatternList+1, metaData, level+1); } break; } /*allocate and initialize a new node */ newNode = (tPatternList *)calloc(1,sizeof(tPatternList)); if (!newNode) { return -1; } newNode->pattern.pattern = patterns->pattern; newNode->pattern.patternSize = patterns->patternSize; newNode->nextLevelMatcher = (tPatternRootNode *)calloc(1, sizeof(tPatternRootNode)); if (!newNode->nextLevelMatcher) { free (newNode); return -1; } newNode->nextLevelMatcher->level = rootNode->level+1; /*insert the new node */ if (!prevNode) { /*insert as first node since either this is the only node, or this is lexically smallest. */ newNode->nextPattern = rootNode->patternList; rootNode->patternList = newNode; } else { /*insert after previous node since either there is either a biggest node after prevNode or */ /*newNode is lexically largest. */ newNode->nextPattern = prevNode->nextPattern; prevNode->nextPattern = newNode; } /*move down the new node */ nextPattern = *(inputPatternList+1); if (!nextPattern || !nextPattern->pattern) { newNode->userData = metaData; } else { addPatternRecursively(newNode->nextLevelMatcher, inputPatternList+1, metaData, level+1); } return 0; } /**returns pattern tree at the level where inputPatternList runs out. */ void *mlpGetPatternMatcherTree(void *root, tMlpPattern **inputPatternList) { MatchedPattern mp = {0}; tPatternList *patternNode; tPatternRootNode *rootNode = (tPatternRootNode *)root; tMlpPattern *pattern = *inputPatternList; if (!rootNode || !pattern || !pattern->pattern) return NULL; mp.level = rootNode->level; _dpd.searchAPI->search_instance_find_all(rootNode->patternTree, (char *)pattern->pattern, pattern->patternSize, 0, longest_pattern_match, (void *)&mp); patternNode = mp.patternNode; if(patternNode) { ++inputPatternList; if (*inputPatternList && (*inputPatternList)->pattern) { return mlpMatchPatternCustom(patternNode->nextLevelMatcher, inputPatternList, longest_pattern_match); } return patternNode->nextLevelMatcher; } return NULL; } snort-2.9.20/src/dynamic-preprocessors/appid/util/fw_avltree.h0000644000175000017500000000401414241075604022576 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _FW_AVL_TREE_H_ #define _FW_AVL_TREE_H_ #include #include struct FwAvlNode { uint32_t key; void* data; int balance; struct FwAvlNode* left; struct FwAvlNode* right; struct FwAvlNode* parent; }; struct FwAvlTree { unsigned count; size_t height; struct FwAvlNode* root; struct FwAvlNode* first; struct FwAvlNode* last; }; struct FwQNode { struct FwAvlNode* treeNode; struct FwQNode* next; }; struct FwAvlTree* fwAvlInit(void); int fwAvlInsert(uint32_t key, void* data, struct FwAvlTree* tree); void* fwAvlLookup(const uint32_t key, const struct FwAvlTree* tree); struct FwAvlNode* fwAvlFirst(const struct FwAvlTree* tree); struct FwAvlNode* fwAvlLast(const struct FwAvlTree* tree); struct FwAvlNode* fwAvlNext(struct FwAvlNode* node); struct FwAvlNode* fwAvlPrev(struct FwAvlNode* node); struct FwQNode* fwAvlSerialize(struct FwAvlTree* tree); void fwAvlDeleteTree(struct FwAvlTree* tree, void (*dataDelete)(void* data)); #endif snort-2.9.20/src/dynamic-preprocessors/appid/util/OutputFile.c0000644000175000017500000000371114241075612022535 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** * RNA Output file stuff * * @file OutputFile.c * */ /* I N C L U D E S *****************************************************/ #include #include #include #include #include #include #include #include #include #include #include "sf_dynamic_preprocessor.h" FILE *openOutputFile(const char * const filename, time_t tstamp) { FILE *fp; char output_fullpath[512]; time_t curr_time; if (tstamp) curr_time = tstamp; else curr_time = time(NULL); snprintf(output_fullpath, sizeof(output_fullpath), "%s.%lu", filename, curr_time); _dpd.logMsg("*** Opening %s for output\n",output_fullpath); if((fp = fopen(output_fullpath, "w")) == NULL) { _dpd.errMsg("Unable to open output file \"%s\": %s\n",output_fullpath, strerror(errno)); } return fp; } FILE *rolloverOutputFile(const char * const filename, FILE * const oldfp, time_t tstamp) { fclose(oldfp); return openOutputFile(filename, tstamp); } snort-2.9.20/src/dynamic-preprocessors/appid/util/ip_funcs.c0000644000175000017500000001311714241075605022246 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "ip_funcs.h" #include "sfutil.h" #include "common_util.h" #include #include #include "sf_dynamic_preprocessor.h" RNAIpAddrSet *ParseIpCidr(char *ipstring, uint32_t *netmasks) { char *toks[2]; int num_toks; RNAIpAddrSet *ias; char *cp; struct in_addr ia; if (ipstring == NULL) return NULL; ias = calloc(1, sizeof(*ias)); if (!ias) { _dpd.errMsg("IPFunctions: Failed to allocate memory"); return NULL; } strip(ipstring); cp = ipstring; if (*cp == 'h') { ias->addr_flags |= IPFUNCS_HOSTS_IP; cp++; } if (*cp == 's') { ias->addr_flags |= IPFUNCS_APPLICATION; cp++; } if (*cp == '!') { ias->addr_flags |= IPFUNCS_EXCEPT_IP; cp++; } if (!strcasecmp(ipstring, "any")) { ias->range_max = ~0; return ias; } num_toks = Split(cp, toks, 2, "/"); if (inet_pton(AF_INET, toks[0], &ia) <= 0) { _dpd.errMsg("IPFunctions: %s failed to translate", toks[0]); free(ias); return NULL; } ias->range_min = ntohl(ia.s_addr); if (num_toks > 1) { ias->netmask = (unsigned)strtoul(toks[1], NULL, 0); if (ias->netmask < 32) { ias->netmask_mask = netmasks[ias->netmask]; ias->range_min &= ias->netmask_mask; ias->range_max = ias->range_min + ~ias->netmask_mask; } else { ias->netmask = 32; ias->netmask_mask = netmasks[ias->netmask]; ias->range_min &= ias->netmask_mask; ias->range_max = ias->range_min; } } else { ias->netmask = 32; ias->netmask_mask = netmasks[ias->netmask]; ias->range_min &= ias->netmask_mask; ias->range_max = ias->range_min; } return ias; } RNAIpv6AddrSet *ParseIpv6Cidr(char *ipstring) { char *toks[2]; int num_toks; RNAIpv6AddrSet *ias; char *cp; struct in6_addr ia; if (ipstring == NULL) return NULL; ias = calloc(1, sizeof(*ias)); if (!ias) { _dpd.errMsg("IPFunctions: Failed to allocate memory"); return NULL; } strip(ipstring); cp = ipstring; if (*cp == 'h') { ias->addr_flags |= IPFUNCS_HOSTS_IP; cp++; } if (*cp == 's') { ias->addr_flags |= IPFUNCS_APPLICATION; cp++; } if (*cp == '!') { ias->addr_flags |= IPFUNCS_EXCEPT_IP; cp++; } if (!strcasecmp(ipstring, "any")) { ias->range_max.lo = ULLONG_MAX; ias->range_max.hi = ULLONG_MAX; return ias; } num_toks = Split(cp, toks, 2, "/"); if (inet_pton(AF_INET6, toks[0], &ia) <= 0) { _dpd.errMsg("IPFunctions: %s failed to translate", toks[0]); free(ias); return NULL; } memcpy(&ias->range_min, ia.s6_addr32, sizeof(ias->range_min)); NSIPv6AddrNtoH(&ias->range_min); if (num_toks > 1) { ias->netmask = (unsigned)strtoul(toks[1], NULL, 0); /* Convert cidr to netmask */ if (!ias->netmask) { ias->range_max.hi = ULLONG_MAX; ias->range_max.lo = ULLONG_MAX; } else if (ias->netmask < 64) { ias->netmask_mask.hi = ULLONG_MAX << (64 - ias->netmask); ias->range_min.hi &= ias->netmask_mask.hi; ias->range_min.lo = 0; ias->range_max.hi = ias->range_min.hi + ~ias->netmask_mask.hi; ias->range_max.lo = ULLONG_MAX; } else if (ias->netmask == 64) { ias->netmask_mask.hi = ULLONG_MAX; ias->range_min.hi &= ias->netmask_mask.hi; ias->range_min.lo = 0; ias->range_max.hi = ias->range_min.hi + ~ias->netmask_mask.hi; ias->range_max.lo = ULLONG_MAX; } else if (ias->netmask < 128) { ias->netmask_mask.hi = ULLONG_MAX; ias->netmask_mask.lo = ULLONG_MAX << (128 - ias->netmask); ias->range_min.lo &= ias->netmask_mask.lo; ias->range_max.hi = ias->range_min.hi; ias->range_max.lo = ias->range_min.lo + ~ias->netmask_mask.lo; } else { ias->netmask_mask.hi = ULLONG_MAX; ias->netmask_mask.lo = ULLONG_MAX; ias->range_max = ias->range_min; } } else { ias->netmask = 128; ias->netmask_mask.lo = ULLONG_MAX; ias->netmask_mask.hi = ULLONG_MAX; ias->range_max = ias->range_min; } return ias; } snort-2.9.20/src/dynamic-preprocessors/appid/util/sf_multi_mpse.h0000644000175000017500000000315014241075617023312 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SF_MULTI_MPSE_H_ #define _SF_MULTI_MPSE_H_ #include #include typedef struct _tPattern { const uint8_t *pattern; size_t patternSize; } tMlpPattern; void *mlpCreate(void); int mlpAddPattern(void *root, const tMlpPattern **patterns, void *metaData); int mlpProcessPatterns(void *root); void *mlpMatchPatternLongest(void *root, tMlpPattern **inputPatternList); void *mlpMatchPatternUrl(void *root, tMlpPattern **inputPatternList); void *mlpMatchPatternCustom(void *root, tMlpPattern **inputPatternList, int (*callback)(void*, void*, int, void*, void*)); void mlpDestroy(void *root); void mlpDump(void *root); void *mlpGetPatternMatcherTree(void *root, tMlpPattern **inputPatternList); #endif snort-2.9.20/src/dynamic-preprocessors/appid/util/common_util.c0000644000175000017500000000173614241075601022765 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include time_t packetTimeOffset; time_t packetTime; int packetTimeOffsetSet; snort-2.9.20/src/dynamic-preprocessors/appid/luaDetectorModule.h0000644000175000017500000000442314241075455023114 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __LUA_DETECTOR_MODULE_H__ #define __LUA_DETECTOR_MODULE_H__ void luaModuleInit(void); void luaModuleFini(void); /** * \brief Load all Lua modules into a detector list * * Each RNA detector file in the folder \e app_id_detector_path is parsed for * detector information. If it is a valid detector, a detector data structure * is created for it and stored in \e allocatedDetectorList. * * @param - pointer to new AppId context * @return None */ void LoadLuaModules(tAppidStaticConfig* appidSC, tAppIdConfig *pConfig); /** * \brief Finalize Lua modules * * This function should be called after LoadLuaModules(). It sets up proper AppId references * and tracker size for all the detectors. * * @param pConfig - pointer to active AppId context * @return void */ void FinalizeLuaModules(tAppIdConfig *pConfig); /** * \brief Unload Lua modules * * This function cleans up all the data structures that were created for the Lua detectors * in a given AppId context. It should be called after FinalizeLuaModules(). * * @param Pointer to old AppId context * @return None */ void UnloadLuaModules(tAppIdConfig *pConfig); void luaModuleInitAllServices(void); void luaModuleCleanAllClients(void); void luaModuleInitAllClients(void); void RNAPndDumpLuaStats (void); void luaDetectorsUnload(tAppIdConfig *pConfig); void luaDetectorsSetTrackerSize(void); extern SF_LIST allocatedFlowList; #endif snort-2.9.20/src/dynamic-preprocessors/appid/appIdStats.c0000644000175000017500000003701114241075346021540 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include "fw_avltree.h" #include "OutputFile.h" #include "Unified2_common.h" #include #include #include #include #undef MAX_NAME_LEN /*#include "session_record.h" */ #include #include "appIdStats.h" #include "common_util.h" #include "sf_dynamic_preprocessor.h" #include "fw_appid.h" #include "appInfoTable.h" #define URLCATBUCKETS 100 #define URLREPBUCKETS 5 /*#define DEBUG_STATS */ static time_t bucketStart; static time_t bucketInterval; static time_t bucketEnd; struct AppIdStatRecord { uint32_t app_id; uint32_t initiatorBytes; uint32_t responderBytes; }; #ifdef WIN32 #pragma pack(push,app_stats,1) #else #pragma pack(1) #endif struct AppIdStatOutputRecord { char appName[MAX_EVENT_APPNAME_LEN]; uint32_t initiatorBytes; uint32_t responderBytes; }; #ifdef WIN32 #pragma pack(pop,app_stats) #else #pragma pack() #endif struct StatsBucket { uint32_t startTime; struct FwAvlTree* appsTree; struct _SessionStatsRecord { size_t txByteCnt; size_t rxByteCnt; } totalStats; uint32_t appRecordCnt; }; static SF_LIST* currBuckets; static SF_LIST* logBuckets; static char* appFilePath; static FILE* appfp; static size_t appSize; static time_t appTime; Serial_Unified2_Header header; static size_t rollSize; static time_t rollPeriod; static bool enableAppStats; static void endStats2Period(void); static void startStats2Period(time_t startTime); static struct StatsBucket* getStatsBucket(time_t startTime); static void dumpStats2(void); static void deleteRecord(void* record) { _dpd.snortFree(record, sizeof(struct AppIdStatRecord), PP_APP_ID, PP_MEM_CATEGORY_MISC); } void appIdStatsUpdate(tAppIdData* session) { struct StatsBucket* bucket = NULL; struct AppIdStatRecord * record = NULL; time_t now = time(NULL); time_t bucketTime; uint32_t app_id; tAppId web_app_id; tAppId service_app_id; tAppId client_app_id; if (!enableAppStats) return; now = now - (now % bucketInterval); if(now >= bucketEnd) { endStats2Period(); dumpStats2(); startStats2Period(now); } bucketTime = session->stats.firstPktsecond - (session->stats.firstPktsecond % bucketInterval); if((bucket = getStatsBucket(bucketTime)) == NULL) return; bucket->totalStats.txByteCnt += session->stats.initiatorBytes; bucket->totalStats.rxByteCnt += session->stats.responderBytes; web_app_id = pickPayloadId(session); if(web_app_id > APP_ID_NONE) { app_id = web_app_id; if(!(record = fwAvlLookup(app_id, bucket->appsTree))) { record = _dpd.snortAlloc(1, sizeof(*record), PP_APP_ID, PP_MEM_CATEGORY_MISC); if(record) { if (fwAvlInsert(app_id, record, bucket->appsTree) == 0) { record->app_id = app_id; bucket->appRecordCnt += 1; # ifdef DEBUG_STATS fprintf(SF_DEBUG_FILE, "New App: %u Count %u\n", record->app_id, bucket->appRecordCnt); # endif } else { _dpd.snortFree(record, sizeof(*record), PP_APP_ID, PP_MEM_CATEGORY_MISC); record = NULL; } } } if(record) { record->initiatorBytes += session->stats.initiatorBytes; record->responderBytes += session->stats.responderBytes; } } service_app_id = pickServiceAppId(session); if((service_app_id) && (service_app_id != web_app_id)) { app_id = service_app_id; if(!(record = fwAvlLookup(app_id, bucket->appsTree))) { record = _dpd.snortAlloc(1, sizeof(*record), PP_APP_ID, PP_MEM_CATEGORY_MISC); if(record) { if (fwAvlInsert(app_id, record, bucket->appsTree) == 0) { record->app_id = app_id; bucket->appRecordCnt += 1; # ifdef DEBUG_STATS fprintf(SF_DEBUG_FILE, "New App: %u Count %u\n", record->app_id, bucket->appRecordCnt); # endif } else { _dpd.snortFree(record, sizeof(*record), PP_APP_ID, PP_MEM_CATEGORY_MISC); record = NULL; } } } if(record) { record->initiatorBytes += session->stats.initiatorBytes; record->responderBytes += session->stats.responderBytes; } } client_app_id = pickClientAppId(session); if(client_app_id > APP_ID_NONE && client_app_id != service_app_id && client_app_id != web_app_id) { app_id = client_app_id; if(!(record = fwAvlLookup(app_id, bucket->appsTree))) { record = _dpd.snortAlloc(1, sizeof(*record), PP_APP_ID, PP_MEM_CATEGORY_MISC); if(record) { if (fwAvlInsert(app_id, record, bucket->appsTree) == 0) { record->app_id = app_id; bucket->appRecordCnt += 1; # ifdef DEBUG_STATS fprintf(SF_DEBUG_FILE, "New App: %u Count %u\n", record->app_id, bucket->appRecordCnt); # endif } else { _dpd.snortFree(record, sizeof(*record), PP_APP_ID, PP_MEM_CATEGORY_MISC); record = NULL; } } } if(record) { record->initiatorBytes += session->stats.initiatorBytes; record->responderBytes += session->stats.responderBytes; } } } void appIdStatsInit(char* appFileName, time_t statsPeriod, size_t rolloverSize, time_t rolloverPeriod) { time_t now; size_t pathLength; char *path; if (!appFileName || !*appFileName) { enableAppStats = false; return; } enableAppStats = true; path = _dpd.getLogDirectory(); rollPeriod = rolloverPeriod; rollSize = rolloverSize; pathLength = strlen(path) + strlen(appFileName) + 2; appFilePath = calloc(pathLength, 1); if(appFilePath != NULL) snprintf(appFilePath, pathLength, "%s/%s", path, appFileName); free(path); bucketInterval = statsPeriod; now = time(NULL); now = now - (now % bucketInterval); startStats2Period(now); appfp = NULL; } static void appIdStatsCloseFiles(void) { if(appfp) { fclose(appfp); appfp = NULL; } } void appIdStatsReinit(void) { if (!enableAppStats) return; appIdStatsCloseFiles(); } void appIdStatsIdleFlush(void) { time_t now; if (!enableAppStats) return; now = time(NULL); now = now - (now % bucketInterval); if(now >= bucketEnd) { endStats2Period(); dumpStats2(); startStats2Period(now); } } static void startStats2Period(time_t startTime) { bucketStart = startTime; bucketEnd = bucketStart + bucketInterval; } static void endStats2Period(void) { SF_LIST* bucketList = logBuckets; logBuckets = currBuckets; currBuckets = bucketList; } static struct StatsBucket* getStatsBucket(time_t startTime) { struct StatsBucket* bucket = NULL; struct StatsBucket* lBucket; SF_LNODE* lNode; if(currBuckets == NULL) { currBuckets = sflist_new(); # ifdef DEBUG_STATS fprintf(SF_DEBUG_FILE, "New Stats Bucket List\n"); # endif } if(currBuckets == NULL) return(NULL); for(lNode = sflist_first_node(currBuckets); lNode != NULL; lNode = sflist_next_node(currBuckets)) { lBucket = SFLIST_NODE_TO_DATA(lNode); if(startTime == lBucket->startTime) { bucket = lBucket; break; } else if(startTime < lBucket->startTime) { bucket = (struct StatsBucket*)_dpd.snortAlloc(1, sizeof(*bucket), PP_APP_ID, PP_MEM_CATEGORY_MISC); if(bucket != NULL) { bucket->startTime = startTime; bucket->appsTree = fwAvlInit(); sflist_add_before(currBuckets, lNode, bucket); # ifdef DEBUG_STATS fprintf(SF_DEBUG_FILE, "New Bucket Time: %u before %u\n", bucket->startTime, lBucket->startTime); # endif } break; } } if(lNode == NULL) { bucket = (struct StatsBucket*)_dpd.snortAlloc(1, sizeof(*bucket), PP_APP_ID, PP_MEM_CATEGORY_MISC); if(bucket != NULL) { bucket->startTime = startTime; bucket->appsTree = fwAvlInit(); sflist_add_tail(currBuckets, bucket); # ifdef DEBUG_STATS fprintf(SF_DEBUG_FILE, "New Bucket Time: %u at tail\n", bucket->startTime); # endif } } return(bucket); } static void dumpStats2(void) { struct StatsBucket* bucket = NULL; uint8_t* buffer; uint32_t* buffPtr; struct FwAvlNode* node; struct AppIdStatRecord* record; size_t buffSize; time_t currTime = time(NULL); if(logBuckets == NULL) return; while((bucket = (struct StatsBucket*) sflist_remove_head(logBuckets)) != NULL) { if(bucket->appRecordCnt) { buffSize = bucket->appRecordCnt * sizeof(struct AppIdStatOutputRecord) + 4 * sizeof(uint32_t); header.type = UNIFIED2_IDS_EVENT_APPSTAT; header.length = buffSize - 2*sizeof(uint32_t); buffer = malloc(buffSize); if(!buffer) { _dpd.errMsg("dumpStats2: " "Failed to allocate memory for appRecord in StatsBucket\n"); return; } # ifdef DEBUG_STATS fprintf(SF_DEBUG_FILE, "Write App Records %u Size: %lu\n", bucket->appRecordCnt, buffSize); # endif } else { buffer = NULL; } if(buffer) { buffPtr = (uint32_t*) buffer; *buffPtr++ = htonl(header.type); *buffPtr++ = htonl(header.length); *buffPtr++ = htonl(bucket->startTime); *buffPtr++ = htonl(bucket->appRecordCnt); for(node = fwAvlFirst(bucket->appsTree); node != NULL; node = fwAvlNext(node)) { struct AppIdStatOutputRecord *recBuffPtr; const char *appName; bool cooked_client = false; tAppId app_id; char tmpBuff[MAX_EVENT_APPNAME_LEN]; record = (struct AppIdStatRecord *) node->data; app_id = record->app_id; recBuffPtr = (struct AppIdStatOutputRecord*)buffPtr; if (app_id >= 2000000000) { cooked_client = true; app_id -= 2000000000; } AppInfoTableEntry* entry = appInfoEntryGet(app_id, appIdActiveConfigGet()); if (entry) { appName = entry->appName; if (cooked_client) { snprintf(tmpBuff, MAX_EVENT_APPNAME_LEN, "_cl_%s",appName); tmpBuff[MAX_EVENT_APPNAME_LEN-1] = 0; appName = tmpBuff; } } else if (app_id == APP_ID_UNKNOWN || app_id == APP_ID_UNKNOWN_UI) appName = "__unknown"; else if (app_id == APP_ID_NONE) appName = "__none"; else { _dpd.errMsg("invalid appid in appStatRecord (%u)\n", record->app_id); if (cooked_client) { snprintf(tmpBuff, MAX_EVENT_APPNAME_LEN, "_err_cl_%u",app_id); } else { snprintf(tmpBuff, MAX_EVENT_APPNAME_LEN, "_err_%u",app_id); // ODP out of sync? } tmpBuff[MAX_EVENT_APPNAME_LEN-1] = 0; appName = tmpBuff; } memcpy(recBuffPtr->appName, appName, MAX_EVENT_APPNAME_LEN); /**buffPtr++ = htonl(record->app_id); */ recBuffPtr->initiatorBytes = htonl(record->initiatorBytes); recBuffPtr->responderBytes = htonl(record->responderBytes); buffPtr += sizeof(*recBuffPtr)/sizeof(*buffPtr); } if(appFilePath) { if(!appfp) { appfp = openOutputFile(appFilePath, currTime); appTime = currTime; appSize = 0; } else if(((currTime - appTime) > rollPeriod) || ((appSize + buffSize) > rollSize)) { appfp = rolloverOutputFile(appFilePath, appfp, currTime); appTime = currTime; appSize = 0; } if(appfp) { if((fwrite(buffer, buffSize, 1, appfp) == 1) && (fflush(appfp) == 0)) { appSize += buffSize; } else { _dpd.errMsg("NGFW Rule Engine Failed to write to statistics file (%s): %s\n", appFilePath, strerror(errno)); fclose(appfp); appfp = NULL; } } } free(buffer); } fwAvlDeleteTree(bucket->appsTree, deleteRecord); _dpd.snortFree(bucket, sizeof(*bucket), PP_APP_ID, PP_MEM_CATEGORY_MISC); } } void appIdStatsFini() { struct StatsBucket* bucket = NULL; if (!enableAppStats) return; /*flush the last stats period. */ endStats2Period(); dumpStats2(); if(!currBuckets) return; while((bucket = (struct StatsBucket*) sflist_remove_head(currBuckets)) != NULL) { fwAvlDeleteTree(bucket->appsTree, deleteRecord); _dpd.snortFree(bucket, sizeof(*bucket), PP_APP_ID, PP_MEM_CATEGORY_MISC); } free(currBuckets); if(logBuckets) free(logBuckets); if(appFilePath) free(appFilePath); appIdStatsCloseFiles(); } snort-2.9.20/src/dynamic-preprocessors/appid/appIdConfig.h0000644000175000017500000002410214241075345021650 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __APP_ID_CONFIG_H___ #define __APP_ID_CONFIG_H___ /** * \file appIdConfig.h * * \brief AppId configuration data structures */ /****************************** INCLUDES **************************************/ #include #include "appId.h" #include "client_app_api.h" #include "service_api.h" #include "serviceConfig.h" #include "httpCommon.h" #include "clientAppConfig.h" #include "detector_sip.h" /******************************* DEFINES **************************************/ #define APP_ID_MAX_DIRS 16 #define APP_ID_PORT_ARRAY_SIZE 65536 #define MAX_ZONES 1024 /********************************* TYPES **************************************/ struct _AppInfoTableEntry; struct DynamicArray; struct ServicePortPattern; struct ClientPortPattern; typedef struct _port_ex { int family; struct in6_addr ip; struct in6_addr netmask; } PortExclusion; /** * \typedef tAppidGenericConfigItem * * \brief AppId generic configuration item * * Modules can use this generic data structure to store their configuration. * All such generic configurations are stored in genericConfigList. Modules * are responsible for populating the configuration in init() and cleaning it * up in clean() function. * * Currently, IMAP, PO3 and MDNS use this data structure. Lua modules currently * do not have any configuration. They can use this data structure in the future, * if needed. */ typedef struct appidGenericConfigItem_ { char *name; ///< Module name void *pData; ///< Module configuration data } tAppidGenericConfigItem; typedef enum { APPID_REQ_UNINITIALIZED = 0, APPID_REQ_YES, APPID_REQ_NO } tAppIdReq; /** * \typedef tAppIdConfig * * \brief AppId dynamic configuration data structure * * Members of this data structure get populated during initialization and reload. * They get freed after reload swap and during exit. */ typedef struct appIdConfig_ { unsigned max_service_info; unsigned net_list_count; NetworkSet *net_list_list; ///< list of network sets NetworkSet *net_list; ///< list of networks we're analyzing NetworkSet *net_list_by_zone[MAX_ZONES]; ///< list of networks we're analyzing tAppId tcp_port_only[65536]; ///< Service IDs for port-only TCP services tAppId udp_port_only[65536]; ///< Service IDs for port-only UDP services tAppId ip_protocol[256]; ///< Service IDs for non-TCP / UDP protocol services SF_LIST client_app_args; ///< List of Client App arguments SF_LIST *tcp_port_exclusions_src[APP_ID_PORT_ARRAY_SIZE]; ///< for each potential port, an sflist of PortExclusion structs SF_LIST *udp_port_exclusions_src[APP_ID_PORT_ARRAY_SIZE]; ///< for each potential port, an sflist of PortExclusion structs SF_LIST *tcp_port_exclusions_dst[APP_ID_PORT_ARRAY_SIZE]; ///< for each potential port, an sflist of PortExclusion structs SF_LIST *udp_port_exclusions_dst[APP_ID_PORT_ARRAY_SIZE]; ///< for each potential port, an sflist of PortExclusion structs SFXHASH *CHP_glossary; ///< keep track of http multipatterns here SFXHASH *AF_indicators; ///< App Forecasting list of "indicator apps" SFXHASH *AF_actives; ///< App Forecasting list of hosts to watch for "forecast apps" sfaddr_t *debugHostIp; struct _AppInfoTableEntry *AppInfoList; struct _AppInfoTableEntry *AppInfoTable[SF_APPID_MAX]; struct _AppInfoTableEntry *AppInfoTableByService[SF_APPID_MAX]; struct _AppInfoTableEntry *AppInfoTableByClient[SF_APPID_MAX]; struct _AppInfoTableEntry *AppInfoTableByPayload[SF_APPID_MAX]; struct DynamicArray *AppInfoTableDyn; SFGHASH *AppNameHash; SFXHASH *hostPortCache; SFXHASH *lengthCache; tDetectorHttpConfig detectorHttpConfig; ///< HTTP detector configuration tDetectorSipConfig detectorSipConfig; ///< SIP detector configuration tServiceConfig serviceConfig; ///< Common configuration for all services tServiceSslConfig serviceSslConfig; ///< SSL service configuration tServiceDnsConfig serviceDnsConfig; ///< DNS service configuration tClientAppConfig clientAppConfig; ///< Common configuration for all client applications HttpPatternLists httpPatternLists; struct ServicePortPattern *servicePortPattern; struct ClientPortPattern *clientPortPattern; SF_LIST genericConfigList; ///< List of tAppidGenericConfigItem structures tAppIdReq isAppIdAlwaysRequired; } tAppIdConfig; #ifdef SIDE_CHANNEL typedef struct _AppIdSSConfig { #ifdef REG_TEST char *startup_input_file; char *runtime_output_file; #endif bool use_side_channel; } AppIdSSConfig; #endif /** * \struct tAppidStaticConfig * * \brief AppId static configuration data structure * * Members of this data structure get populated during initialization and freed * during exit. They are not reloadable/reconfigurable. * Note: appid_tp_dir can be reconfigured but gets used by 3rd party reload. AppID * reload does not look at this variable. */ struct AppidStaticConfig { unsigned disable_safe_search; const char *appid_thirdparty_dir; /* directory where thirdparty modules are located.*/ char* tp_config_path; char* app_stats_filename; unsigned long app_stats_period; unsigned long app_stats_rollover_size; unsigned long app_stats_rollover_time; char* app_id_detector_path; unsigned long memcap; int app_id_dump_ports; int app_id_debug; uint32_t instance_id; char* conf_file; unsigned dns_host_reporting; unsigned referred_appId_disabled; unsigned rtmp_max_packets; unsigned mdns_user_reporting; unsigned ftp_userid_disabled; unsigned chp_userid_disabled; unsigned chp_body_collection_disabled; unsigned chp_fflow_disabled; unsigned chp_body_collection_max; unsigned max_tp_flow_depth; unsigned tp_allow_probes; unsigned host_port_app_cache_lookup_interval; unsigned host_port_app_cache_lookup_range; unsigned multipayload_max_packets; unsigned http_tunnel_detect; uint64_t max_bytes_before_service_fail; uint16_t max_packet_before_service_fail; uint16_t max_packet_service_fail_ignore_bytes; bool http2_detection_enabled; // internal HTTP/2 detection bool is_host_port_app_cache_runtime; bool check_host_port_app_cache; bool check_host_cache_unknown_ssl; bool recheck_for_unknown_appid; bool send_state_sharing_updates; bool allow_port_wildcard_host_cache; bool recheck_for_portservice_appid; tAppIdConfig* newAppIdConfig; // Used only during reload #ifdef SIDE_CHANNEL AppIdSSConfig *appId_ss_config; #endif #ifdef REG_TEST bool appid_reg_test_mode; #endif }; typedef struct AppidStaticConfig tAppidStaticConfig; void appIdConfigParse(tAppidStaticConfig* appidSC, char *args); /************************** GLOBAL VARIABLES **********************************/ /// AppId static configuration data extern tAppidStaticConfig* appidStaticConfig; /** * \brief Pointer to AppId dynamic configuration data * * This variable always points to the current active configuration that needs * to be used during packet processing. Lower level functions should restrain * from using this variable directly since they need to be context-agnostic. * A lower-level function (for example, clientCreatePattern()) could be called * during initalization, reload and reconfiguration. Pointer to the right * context information needs to be provided to such functions. */ extern tAppIdConfig *pAppidActiveConfig; extern tAppIdConfig *pAppidPassiveConfig; /********************* GLOBAL FUNCTION PROTOTYPES ****************************/ /** * \brief Add generic configuration item to AppID configuration list * * @param pConfig AppID configuration to which this item needs to be added * @param name Module name - needs to be unique per-module * @param pData pointer to module configuration data * @return None */ void AppIdAddGenericConfigItem(tAppIdConfig *pConfig, const char *name, void *pData); /** * \brief Find a module's configuration in AppID configuration list * * @param pConfig AppID configuration in which the module's configuration needs to be searched * @param name Module name * @return pointer to module configuration data */ void *AppIdFindGenericConfigItem(const tAppIdConfig *pConfig, const char *name); /** * \brief Remove a module's configuration from AppID configuration list * * Note: This function has to be called after the config item's data (pData) is freed * * @param pConfig AppID configuration in which the module's configuration needs to be searched * @param name Module name * @return None */ void AppIdRemoveGenericConfigItem(tAppIdConfig *pConfig, const char *name); /************************** LOCAL FUNCTIONS **********************************/ inline static tAppIdConfig *appIdActiveConfigGet(void) { return pAppidActiveConfig; } inline static tAppIdConfig *appIdNewConfigGet(void) { return pAppidPassiveConfig; } #endif // APPID_CONFIG_H_ snort-2.9.20/src/dynamic-preprocessors/appid/hostPortAppCache.c0000644000175000017500000002045314241075442022672 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include "common_util.h" #include "sf_dynamic_preprocessor.h" #include "hostPortAppCache.h" #include "ip_funcs.h" #include "sfxhash.h" #include "appIdConfig.h" #ifdef SIDE_CHANNEL #include "appId_ss.h" #endif #define HOST_PORT_DYNAMIC_CACHE_ROWS 2048 #define MAX_HOST_PORT_DYNAMIC_CACHE_ENTRIES 4096 static SFXHASH *hostPortCacheDynamic; static uint16_t hostPortCacheDynamicVersion = 0; extern bool app_id_debug_session_flag; extern char app_id_debug_session[FW_DEBUG_SESSION_ID_SIZE]; #ifdef SIDE_CHANNEL static int ProduceSSHostCache(tHostPortKey *hk, tHostPortVal *hv) { uint32_t offset = 0; void *msg_handle = NULL; void *hdr_ptr = NULL; void *data_ptr = NULL; if (!hk || !hv) { return -1; } if (CreateAppIdSSUpdate(&msg_handle, &hdr_ptr, &data_ptr, SC_MSG_TYPE_APPID_SS_HOST_CACHE, sizeof(*hk) + sizeof(*hv)) != 0) { return -1; } if (data_ptr) { memcpy(data_ptr, hk, sizeof(*hk)); offset += sizeof(*hk); memcpy((uint8_t *)data_ptr + offset, hv, sizeof(*hv)); offset += sizeof(*hv); SendAppIdSSUpdate(msg_handle, hdr_ptr, data_ptr, SC_MSG_TYPE_APPID_SS_HOST_CACHE, offset); } return 0; } int ConsumeSSHostCache(const uint8_t *buf, uint32_t len) { tHostPortKey *hk; tHostPortVal *hv; if( !buf ) return -1; if( len < sizeof(*hk) + sizeof(*hv) ) return -1; hk = (tHostPortKey *)buf; hv = (tHostPortVal *)(buf + sizeof(*hk)); hostPortAppCacheDynamicAdd(&hk->ip, (uint16_t)hk->port, (uint16_t)hk->proto, hv->type, hv->appId, false); return 0; } #endif void hostPortAppCacheInit(tAppIdConfig *pConfig) { if (!(pConfig->hostPortCache = sfxhash_new(1024, sizeof(tHostPortKey), sizeof(tHostPortVal), 0, 0, NULL, NULL, 0))) { _dpd.errMsg( "failed to allocate HostPort map"); } } void hostPortAppCacheFini(tAppIdConfig *pConfig) { if (pConfig->hostPortCache) { sfxhash_delete(pConfig->hostPortCache); pConfig->hostPortCache = NULL; } } tHostPortVal *hostPortAppCacheFind(const sfaddr_t *snort_ip, uint16_t port, uint16_t protocol, const tAppIdConfig *pConfig) { tHostPortKey hk; tHostPortVal *hv; sfip_set_ip((sfaddr_t *)&hk.ip, snort_ip); hk.port = (appidStaticConfig->allow_port_wildcard_host_cache) ? 0 : port; hk.proto = protocol; hv = (tHostPortVal*)sfxhash_find(pConfig->hostPortCache, &hk); return hv; } int hostPortAppCacheAdd(const struct in6_addr *ip, uint16_t port, uint16_t proto, APP_ID_TYPE type, tAppId appId, tAppIdConfig *pConfig) { int ret; tHostPortKey hk; tHostPortVal hv; memcpy(&hk.ip, ip, sizeof(hk.ip)); hk.port = (appidStaticConfig->allow_port_wildcard_host_cache) ? 0 : port; hk.proto = proto; hv.appId = appId; hv.type = type; ret = sfxhash_add(pConfig->hostPortCache, &hk, &hv); if (ret == SFXHASH_OK || ret == SFXHASH_INTABLE) return 1; else return 0; } void hostPortAppCacheDump(const tAppIdConfig *pConfig) { SFXHASH_NODE * node; for (node = sfxhash_findfirst(pConfig->hostPortCache); node; node = sfxhash_findnext(pConfig->hostPortCache)) { char inet_buffer[INET6_ADDRSTRLEN]; tHostPortKey *hk; tHostPortVal *hv; hk = (tHostPortKey *)node->key; hv = (tHostPortVal *)node->data; inet_ntop(AF_INET6, &hk->ip, inet_buffer, sizeof(inet_buffer)); printf("\tip=%s, \tport %d, \tproto %d, \ttype=%u, \tappId=%d\n", inet_buffer, hk->port, hk->proto, (unsigned)(hv->type), hv->appId); } } void hostPortAppCacheDynamicInit() { // number of entries * overhead per entry unsigned long maxmem = sfxhash_calc_maxmem(MAX_HOST_PORT_DYNAMIC_CACHE_ENTRIES, sizeof(tHostPortKey) + sizeof(tHostPortVal)); // add in fixed cost of hash table maxmem += (sizeof(SFXHASH_NODE**) * HOST_PORT_DYNAMIC_CACHE_ROWS) + sizeof(long); if (!(hostPortCacheDynamic = sfxhash_new(HOST_PORT_DYNAMIC_CACHE_ROWS, sizeof(tHostPortKey), sizeof(tHostPortVal), maxmem, 1, NULL, NULL, 0))) { _dpd.errMsg( "failed to allocate Dynamic HostPort map"); } } void hostPortAppCacheDynamicFini() { if (hostPortCacheDynamic) { sfxhash_delete(hostPortCacheDynamic); hostPortCacheDynamic = NULL; } } tHostPortVal *hostPortAppCacheDynamicFind(const sfaddr_t *snort_ip, uint16_t port, uint16_t protocol) { tHostPortKey hk; tHostPortVal *hv; sfip_set_ip((sfaddr_t *)&hk.ip, snort_ip); hk.port = (appidStaticConfig->allow_port_wildcard_host_cache) ? 0 : port; hk.proto = protocol; hv = (tHostPortVal*)sfxhash_find(hostPortCacheDynamic, &hk); return hv; } int hostPortAppCacheDynamicAdd(const struct in6_addr *ip, uint16_t port, uint16_t proto, APP_ID_TYPE type, tAppId appId, bool sendUpdate) { int rval; tHostPortKey hk; tHostPortVal hv; memcpy(&hk.ip, ip, sizeof(hk.ip)); hk.port = (appidStaticConfig->allow_port_wildcard_host_cache) ? 0 : port; hk.proto = proto; hv.appId = appId; hv.type = type; rval = sfxhash_add(hostPortCacheDynamic, &hk, &hv); if (rval == SFXHASH_OK) { hostPortCacheDynamicVersion++; if (hostPortCacheDynamicVersion == 0) hostPortCacheDynamicVersion++; if (app_id_debug_session_flag) { char inet_buffer[INET6_ADDRSTRLEN]; inet_ntop(AF_INET6, &hk.ip, inet_buffer, sizeof(inet_buffer)); _dpd.logMsg("AppIdDbg %s %s hostPortAppCache(count=%d) entry ip=%s port=%d proto=%d type=%u appId=%d\n", app_id_debug_session, sendUpdate ? "Added" : "Received", hostPortCacheDynamic->count, inet_buffer, hk.port, hk.proto, (unsigned)(hv.type), hv.appId); } #ifdef SIDE_CHANNEL if (sendUpdate) ProduceSSHostCache(&hk, &hv); #endif } else if (rval != SFXHASH_INTABLE) { return 0; } return 1; } void hostPortAppCacheDynamicDump() { SFXHASH_NODE * node; for (node = sfxhash_findfirst(hostPortCacheDynamic); node; node = sfxhash_findnext(hostPortCacheDynamic)) { char inet_buffer[INET6_ADDRSTRLEN]; tHostPortKey *hk; tHostPortVal *hv; hk = (tHostPortKey *)node->key; hv = (tHostPortVal *)node->data; inet_ntop(AF_INET6, &hk->ip, inet_buffer, sizeof(inet_buffer)); printf("\tip=%s, \tport %d, \tproto %d, \ttype=%u, \tappId=%d\n", inet_buffer, hk->port, hk->proto, (unsigned)(hv->type), hv->appId); } } void updateHostCacheVersion(uint16_t *session_version) { *session_version = hostPortCacheDynamicVersion; return; } bool isHostCacheUpdated(uint16_t session_version) { if(session_version != hostPortCacheDynamicVersion) return true; else return false; } snort-2.9.20/src/dynamic-preprocessors/appid/fw_appid.h0000644000175000017500000003375314241075441021267 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _APPID_H_ #define _APPID_H_ #include #include #include #include "profiler.h" #include "commonAppMatcher.h" #include "client_app_api.h" #include "service_api.h" #include "flow.h" #include "common_util.h" #include "sip_common.h" #include "appInfoTable.h" #include "thirdparty_appid_utils.h" #include "sfghash.h" #define PP_APP_ID 1 #define MIN_SFTP_PACKET_COUNT 30 #define MAX_SFTP_PACKET_COUNT 55 /*#define APPID_FULL_CLEANUP 1 */ typedef enum { APPID_DEBUG_HOST_MONITOR0, APPID_DEBUG_HOST_MONITOR1, APPID_DEBUG_HOST_MONITOR2, APPID_DEBUG_HOST_MONITOR3, APPID_DEBUG_HOST_MONITOR4, APPID_DEBUG_HOST_MONITOR5, APPID_DEBUG_HOST_NOT_MONITORED, } AppIdDebugHostMonitorType; typedef struct { struct in6_addr initiatorIp; int family; tAppIdData* session; uint16_t initiatorPort; APPID_SESSION_DIRECTION direction; uint8_t protocol; int monitorType; } AppIdDebugHostInfo_t; extern uint8_t appIdPriorityArray[SF_APPID_MAX+1]; extern AppIdDebugHostInfo_t AppIdDebugHostInfo; struct AppIdData * getAppIdData(void* lwssn); void fwAppIdInit(void); void fwAppIdFini(tAppIdConfig *pConfig); void fwAppIdSearch(SFSnortPacket *p); void httpHeaderCallback (SFSnortPacket *p, HttpParsedHeaders *const headers); void SipSessionSnortCallback (void *ssnptr, ServiceEventType eventType, void *eventData); void readRnaAppMappingTable(const char *path, tAppIdConfig *pConfig); tAppId appGetAppFromServiceId(uint32_t serviceId, tAppIdConfig *pConfig); tAppId appGetAppFromClientId(uint32_t clientId, tAppIdConfig *pConfig); tAppId appGetAppFromPayloadId(uint32_t payloadId, tAppIdConfig *pConfig); void appSharedDataDelete(tAppIdData * sharedData); void AppIdAddUser(tAppIdData *flowp, const char *username, tAppId appId, int success); void AppIdAddDnsQueryInfo(tAppIdData *flow, uint16_t id, const uint8_t *host, uint8_t host_len, uint16_t host_offset, uint16_t record_type, uint16_t options_offset, bool root_query); void AppIdAddDnsResponseInfo(tAppIdData *flow, uint16_t id, const uint8_t *host, uint8_t host_len, uint16_t host_offset, uint8_t response_type, uint32_t ttl); void AppIdResetDnsInfo(tAppIdData *flow); void AppIdAddPayload(tAppIdData *flow, tAppId payload_id); void AppIdAddMultiPayload(tAppIdData *flow, tAppId payload_id); tAppIdData* appSharedDataAlloc(uint8_t proto, const struct in6_addr *ip, uint16_t initiator_port); tAppId getOpenAppId(void *ssnptr); void appSetServiceDetectorCallback(RNAServiceCallbackFCN fcn, tAppId appId, struct _Detector *userdata, tAppIdConfig *pConfig); void appSetClientDetectorCallback(RNAClientAppCallbackFCN fcn, tAppId appId, struct _Detector *userdata, tAppIdConfig *pConfig); void appSetServiceValidator(RNAServiceValidationFCN fcn, tAppId appId, unsigned extractsInfo, tAppIdConfig *pConfig); void appSetLuaServiceValidator(RNAServiceValidationFCN fcn, tAppId appId, unsigned extractsInfo, struct _Detector *dat); void appSetClientValidator(RNAClientAppFCN fcn, tAppId appId, unsigned extractsInfo, tAppIdConfig *pConfig); void appSetLuaClientValidator(RNAClientAppFCN fcn, tAppId appId, unsigned extractsInfo, struct _Detector *data); int sslAppGroupIdLookup(void *ssnptr, const char * serverName, const char * commonName, tAppId *serviceAppId, tAppId *clientAppId, tAppId *payloadAppId); tAppId getAppId(void *ssnptr); void CheckDetectorCallback(const SFSnortPacket *p, tAppIdData *session, APPID_SESSION_DIRECTION direction, tAppId appId, const tAppIdConfig *pConfig); void setTlsHost(void *ssnptr, const char *serverName, const char *commonName, const char *orgName, const char *subjectAltName, bool isSniMismatch, tAppId *serviceAppId, tAppId *clientAppId, tAppId *payloadAppId); #ifdef FW_TRACKER_DEBUG void logAppIdInfo(SFSnortPacket *p, char *message, tAppId id); #endif int AppIdDebug(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char* statusBuf, int statusBuf_len); extern char app_id_debug_session[FW_DEBUG_SESSION_ID_SIZE]; extern bool app_id_debug_session_flag; #ifdef PERF_PROFILING extern PreprocStats httpPerfStats; extern PreprocStats clientMatchPerfStats; extern PreprocStats serviceMatchPerfStats; extern PreprocStats luaDetectorsPerfStats; extern PreprocStats luaCiscoPerfStats; extern PreprocStats luaCustomPerfStats; extern PreprocStats tpPerfStats; extern PreprocStats tpLibPerfStats; #endif extern unsigned dhcp_fp_table_size; extern unsigned long app_id_ongoing_session; extern unsigned long app_id_total_alloc; extern unsigned long app_id_raw_packet_count; extern unsigned long app_id_processed_packet_count; extern unsigned long app_id_ignored_packet_count; extern unsigned long app_id_session_heap_alloc_count; extern unsigned long app_id_session_freelist_alloc_count; extern unsigned long app_id_flow_data_free_list_count; extern unsigned long app_id_data_free_list_count; extern unsigned long app_id_tmp_free_list_count; extern int app_id_debug; extern unsigned isIPv4HostMonitored(uint32_t ip4, int32_t zone); extern void checkSandboxDetection(tAppId appId); static inline void initializePriorityArray() { int i; for (i=0; i < SF_APPID_MAX; i++) appIdPriorityArray[i] = 2; } static inline void setAppPriority (tAppId app_id, uint8_t bit_val) { if (app_id < SF_APPID_MAX && bit_val <= APPID_MAX_PRIORITY ) appIdPriorityArray[app_id] = bit_val; } static inline int getAppPriority (tAppId app_id) { if (app_id > APP_ID_NONE && app_id < SF_APPID_MAX) return appIdPriorityArray[app_id] ; else return -1; } static inline int ThirdPartyAppIDFoundProto(tAppId proto, tAppId* proto_list) { unsigned int proto_cnt = 0; while (proto_list[proto_cnt] != APP_ID_NONE) if (proto_list[proto_cnt++] == proto) return 1; // found return 0; // not found } static inline int TPIsAppIdDone(void *tpSession) { if (thirdparty_appid_module) { unsigned state; if (tpSession) state = thirdparty_appid_module->session_state_get(tpSession); else state = TP_STATE_INIT; return (state == TP_STATE_CLASSIFIED || state == TP_STATE_TERMINATED || state == TP_STATE_HA); } return true; } static inline int TPIsAppIdAvailable(void * tpSession) { if (thirdparty_appid_module) { unsigned state; if (tpSession) state = thirdparty_appid_module->session_state_get(tpSession); else state = TP_STATE_INIT; return (state == TP_STATE_CLASSIFIED || state == TP_STATE_TERMINATED || state == TP_STATE_MONITORING); } return true; } static inline int isTPProcessingDone(tAppIdData *flow) { if (thirdparty_appid_module && !getAppIdFlag(flow, APPID_SESSION_NO_TPI) && (!TPIsAppIdDone(flow->tpsession) || getAppIdFlag(flow, APPID_SESSION_APP_REINSPECT | APPID_SESSION_APP_REINSPECT_SSL))) return 0; else return 1; } static inline tAppId isAppDetectionDone(tAppIdData *flow) { return getAppIdFlag(flow, APPID_SESSION_SERVICE_DETECTED); } static inline tAppId pickServiceAppId(tAppIdData *flow) { tAppId rval; if (!flow || flow->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) return APP_ID_NONE; if (getAppIdFlag(flow, APPID_SESSION_SERVICE_DETECTED)) { bool deferred = appInfoEntryFlagGet(flow->serviceAppId, APPINFO_FLAG_DEFER, appIdActiveConfigGet()) || appInfoEntryFlagGet(flow->tpAppId, APPINFO_FLAG_DEFER, appIdActiveConfigGet()); if (flow->serviceAppId > APP_ID_NONE && !deferred) return flow->serviceAppId; if (TPIsAppIdAvailable(flow->tpsession)) { if (flow->tpAppId > APP_ID_NONE) return flow->tpAppId; else if (deferred) return flow->serviceAppId; else rval = APP_ID_UNKNOWN_UI; } else rval = flow->tpAppId; } else if (flow->tpAppId > APP_ID_NONE) return flow->tpAppId; else rval = APP_ID_NONE; if (flow->clientServiceAppId > APP_ID_NONE) return flow->clientServiceAppId; if (flow->portServiceAppId > APP_ID_NONE) return flow->portServiceAppId; return rval; } static inline tAppId pickOnlyServiceAppId(tAppIdData *flow) { if (!flow || flow->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) return APP_ID_NONE; bool deferred = appInfoEntryFlagGet(flow->serviceAppId, APPINFO_FLAG_DEFER, appIdActiveConfigGet()) || appInfoEntryFlagGet(flow->tpAppId, APPINFO_FLAG_DEFER, appIdActiveConfigGet()); if (flow->serviceAppId > APP_ID_NONE && !deferred) return flow->serviceAppId; if (TPIsAppIdAvailable(flow->tpsession) && flow->tpAppId > APP_ID_NONE) return flow->tpAppId; else if (deferred) return flow->serviceAppId; if (flow->serviceAppId < APP_ID_NONE) return APP_ID_UNKNOWN_UI; return APP_ID_NONE; } static inline tAppId pickMiscAppId(tAppIdData *flow) { if (!flow || flow->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) return APP_ID_NONE; if (flow->miscAppId > APP_ID_NONE) return flow->miscAppId; return APP_ID_NONE; } static inline tAppId pickClientAppId(tAppIdData *flow) { if (!flow || flow->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) return APP_ID_NONE; if (flow->clientAppId > APP_ID_NONE) return flow->clientAppId; return APP_ID_NONE; } static inline bool isSvcHttpType(tAppId app_id) { switch(app_id) { case APP_ID_HTTP: case APP_ID_HTTPS: case APP_ID_FTPS: case APP_ID_IMAPS: case APP_ID_IRCS: case APP_ID_LDAPS: case APP_ID_NNTPS: case APP_ID_POP3S: case APP_ID_SMTPS: case APP_ID_SSHELL: case APP_ID_SSL: return true; } return false; } static inline tAppId pickPayloadId(tAppIdData *flow) { if (!flow || flow->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) return APP_ID_NONE; // if we have a deferred payload, just use it. // we are not worried about the APP_ID_UNKNOWN case here if (appInfoEntryFlagGet(flow->tpPayloadAppId, APPINFO_FLAG_DEFER_PAYLOAD, appIdActiveConfigGet())) return flow->tpPayloadAppId; if (flow->payloadAppId > APP_ID_NONE) return flow->payloadAppId; if (flow->tpPayloadAppId > APP_ID_NONE) return flow->tpPayloadAppId; /* APP_ID_UNKNOWN is valid only for HTTP type services */ if (flow->payloadAppId == APP_ID_UNKNOWN && isSvcHttpType(flow->serviceAppId)) return APP_ID_UNKNOWN; return APP_ID_NONE; } static inline SFGHASH* pickMultiPayloadList(tAppIdData *flow) { if (!flow || flow->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) return NULL; if (flow->multiPayloadList) return flow->multiPayloadList; return NULL; } static inline tAppId pickReferredPayloadId(tAppIdData *flow) { if (!flow || flow->common.fsf_type.flow_type != APPID_SESSION_TYPE_NORMAL) return APP_ID_NONE; if (flow->referredPayloadAppId > APP_ID_NONE) return flow->referredPayloadAppId; return APP_ID_NONE; } static inline tAppId fwPickServiceAppId(tAppIdData *session) { tAppId appId; appId = pickServiceAppId(session); if (appId == APP_ID_NONE || appId== APP_ID_UNKNOWN_UI) appId = session->encrypted.serviceAppId; return appId; } static inline tAppId fwPickMiscAppId(tAppIdData *session) { tAppId appId; appId = pickMiscAppId(session); if (appId == APP_ID_NONE) appId = session->encrypted.miscAppId; return appId; } static inline tAppId fwPickClientAppId(tAppIdData *session) { tAppId appId; appId = pickClientAppId(session); return appId; } static inline tAppId fwPickPayloadAppId(tAppIdData *session) { tAppId appId; appId = pickPayloadId(session); if (appId == APP_ID_NONE || (appId == APP_ID_SPDY && session && session->hsession && session->hsession->url == NULL && session->encrypted.payloadAppId>APP_ID_NONE)) appId = session->encrypted.payloadAppId; return appId; } static inline tAppId fwPickReferredPayloadAppId(tAppIdData *session) { tAppId appId; appId = pickReferredPayloadId(session); if (appId == APP_ID_NONE) appId = session->encrypted.referredAppId; return appId; } static inline SFGHASH* fwPickMultiPayloadList(tAppIdData *session) { SFGHASH* multiPayloadList = NULL; multiPayloadList = pickMultiPayloadList(session); return multiPayloadList; } static inline tAppIdData* appSharedGetData(const SFSnortPacket *p) { return _dpd.sessionAPI->get_application_data(p->stream_session, PP_APP_ID); } static inline unsigned int isFwSessionSslDecrypted(tAppIdData *session) { return getAppIdFlag(session, APPID_SESSION_DECRYPTED); } static inline int testSSLAppIdForReinspect (tAppId app_id) { if (app_id <= SF_APPID_MAX && (app_id == APP_ID_SSL || appInfoEntryFlagGet(app_id, APPINFO_FLAG_SSL_INSPECT, appIdActiveConfigGet()))) return 1; else return 0; } #endif snort-2.9.20/src/dynamic-preprocessors/appid/commonAppMatcher.h0000644000175000017500000000552214241075401022720 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __COMMON_APP_MATCHER_H__ #define __COMMON_APP_MATCHER_H__ #include #include #include #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sflsq.h" #include "service_state.h" #include "flow.h" #include "appId.h" #include "NetworkSet.h" struct AppIdData; struct AppidStaticConfig; typedef struct _appRegistryEntry { tAppId appId; uint32_t additionalInfo; } tAppRegistryEntry; extern unsigned appIdPolicyId; extern uint32_t app_id_netmasks[]; int appMatcherIsAppDetected(void *appSet, tAppId app); int AppIdCommonInit(struct AppidStaticConfig *config); int AppIdCommonFini(void); /** * \brief Reload AppId configuration * * This function reloads AppId configuration. It is used both in the cases of Snort reload * and AppId reconfiguration. * * @param rnaConf - RNA configuration file name with full path * @param new_context - return reference that points to new AppId configuration * @return 0 on success, -1 on failure */ int AppIdCommonReload(struct AppidStaticConfig* appidSC, void **new_context); /** * \brief Swap AppId configuration * * This function swaps AppId configuration. This function is called after AppIdCommonReload(). * * @param swap_config - Pointer to new configuration. This pointer is returned by AppIdCommonReload(). * @return Pointer to old configuration */ void *AppIdCommonReloadSwap(void *new_context); /** * \brief Clean up AppId configuration * * This function cleans up all the data structures in an AppId configuration. It does not clean up * any global data structures that are used by AppId and are outside the configuration. This * function is called after AppIdCommonReloadSwap(). * * @param old_context - Pointer to old configuration. This pointer is returned by AppIdCommonReloadSwap(). * @return None */ void AppIdCommonUnload(void *old_context); void *AppIDFlowdataGet(struct AppIdData *flow, unsigned id); #endif /* __COMMON_APP_MATCHER_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/appInfoTable.c0000644000175000017500000011167514241075350022034 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include #include #include "appId.h" #include "appInfoTable.h" #include "common_util.h" #include "Unified2_common.h" #define APP_MAPPING_FILE "appMapping.data" #define APP_CONFIG_FILE "appid.conf" #define USR_CONFIG_FILE "userappid.conf" #define MAX_TABLE_LINE_LEN 1024 // Generic delimiter for conf file #define CONF_SEPARATORS "\t\n\r" // Delimiter for appid.conf and userappid.conf file #define CONF_SEPARATORS_USR_APPID " \t\n\r" #define MIN_MAX_TP_FLOW_DEPTH 1 #define MAX_MAX_TP_FLOW_DEPTH 1000000 #define MIN_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL 1 #define MAX_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL 1000000 #define MIN_HOST_PORT_APP_CACHE_LOOKUP_RANGE 1 #define MAX_HOST_PORT_APP_CACHE_LOOKUP_RANGE 1000000 #define MIN_MAX_BYTES_BEFORE_SERVICE_FAIL 4096 #define MIN_MAX_PACKET_BEFORE_SERVICE_FAIL 5 #define MIN_MAX_PACKET_BEFORE_SERVICE_FAIL_IGNORE_BYTES 15 struct DynamicArray { void **table; size_t indexStart; size_t indexCurrent; size_t usedCount; size_t allocatedCount; size_t stepSize; }; typedef struct DynamicArray tDynamicArray; static inline struct DynamicArray* dynamicArrayCreate(unsigned indexStart) { struct DynamicArray *array; if ((array = _dpd.snortAlloc(1, sizeof(*array), PP_APP_ID, PP_MEM_CATEGORY_CONFIG))) { array->stepSize = 1; array->indexStart = indexStart; } return array; } static inline void dynamicArrayDestroy(struct DynamicArray *array) { unsigned i; AppInfoTableEntry *entry; if (!array) return; for (i = 0; i < array->usedCount; i++) { entry = array->table[i]; free(entry->appName); _dpd.snortFree(entry, sizeof(*entry), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); } free(array->table); _dpd.snortFree(array, sizeof(*array), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); } static inline void dynamicArraySetIndex(struct DynamicArray *array, unsigned index, void* data) { if (index >= array->indexStart && index < (array->indexStart + array->usedCount)) array->table[index - array->indexStart] = data; } static inline void* dynamicArrayGetIndex(struct DynamicArray *array, unsigned index) { if (index >= array->indexStart && index < (array->indexStart + array->usedCount)) return array->table[index - array->indexStart]; return NULL; } static inline bool dynamicArrayCreateIndex(struct DynamicArray *array, unsigned *index) { if (array->usedCount == array->allocatedCount) { void** tmp = realloc(array->table, (array->allocatedCount + array->stepSize)*sizeof(*tmp)); if (!tmp) { return false; } array->table = tmp; array->allocatedCount += array->stepSize; } *index = array->indexStart + (array->usedCount++); return true; } static inline void* dynamicArrayGetFirst(struct DynamicArray *array) { AppInfoTableEntry *entry; for (array->indexCurrent = 0; array->indexCurrent < array->usedCount; array->indexCurrent++) { if ((entry = array->table[array->indexCurrent])) return entry; } return NULL; } static inline void* dynamicArrayGetNext(struct DynamicArray *array) { AppInfoTableEntry *entry; for (array->indexCurrent++; array->indexCurrent < array->usedCount; array->indexCurrent++) { if ((entry = array->table[array->indexCurrent])) return entry; } return NULL; } // End of Dynamic array SFGHASH* appNameHashInit() { SFGHASH *appNameHash; appNameHash = sfghash_new(65, 0, 0 /* alloc copies of lowercased keys */, NULL); if (!appNameHash) { _dpd.fatalMsg("AppNameHash: Failed to Initialize\n"); } return appNameHash; } void appNameHashFini(SFGHASH *appNameHash) { if (appNameHash) { sfghash_delete(appNameHash); } } static inline char *strdupToLower(const char *source) { int index; char *dest = malloc(strlen(source)+1); if (dest) { for(index = 0;; index++) { if (source[index]) { dest[index] = tolower(source[index]); continue; } else { dest[index] = '\0'; break; } } } else _dpd.errMsg("strdupToLower: Failed to allocate memory for destination\n"); return dest; } void appNameHashAdd(SFGHASH *appNameHash, const char *appName, void *data) { char *searchName; int errCode; if (!appName || !appNameHash) return; searchName = strdupToLower(appName); if (!searchName) return; if (SFGHASH_OK == (errCode = sfghash_add(appNameHash, searchName, data))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "App name added for %s\n", appName);); } else if (SFGHASH_INTABLE == errCode) { /* Note that, although this entry is not placed in the hash table, being a duplicate, it remains in the list of allocated entries for cleanup by appInfoTableFini() */ // Rediscover the existing, hashed entry for the purpose of a complete error message. AppInfoTableEntry* tableEntry = (AppInfoTableEntry*)sfghash_find(appNameHash, searchName); if (tableEntry) { _dpd.errMsg("App name, \"%s\", is a duplicate of \"%s\" and has been ignored.\n", appName, tableEntry->appName ); } else { _dpd.errMsg("App name, \"%s\", has been ignored. Hash key \"%s\" is not unique.\n", appName, searchName ); } } free(searchName); } void* appNameHashFind(SFGHASH *appNameHash, const char *appName) { void *data; char *searchName; if (!appName || !appNameHash) return NULL; searchName = strdupToLower(appName); if (!searchName) return NULL; data = sfghash_find(appNameHash, searchName); free(searchName); return data; } // End of appName hash static void appIdConfLoad (tAppidStaticConfig* appidSC, const char *path); static unsigned int getAppIdStaticIndex(tAppId appid) { if (appid > 0 && appid < SF_APPID_BUILDIN_MAX) return appid; if (appid >= SF_APPID_CSD_MIN && appid < SF_APPID_CSD_MIN+(SF_APPID_MAX-SF_APPID_BUILDIN_MAX)) return (SF_APPID_BUILDIN_MAX + appid - SF_APPID_CSD_MIN); return 0; } AppInfoTableEntry* appInfoEntryGet(tAppId appId, const tAppIdConfig *pConfig) { tAppId tmp; if ((tmp = getAppIdStaticIndex(appId))) return pConfig->AppInfoTable[tmp]; return dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId); } AppInfoTableEntry* appInfoEntryCreate(const char *appName, tAppIdConfig *pConfig) { tAppId appId; AppInfoTableEntry *entry; if (!appName || strlen(appName) >= MAX_EVENT_APPNAME_LEN) { _dpd.errMsg("Appname invalid\n", appName); return NULL; } entry = appNameHashFind(pConfig->AppNameHash, appName); if (!entry) { if (!dynamicArrayCreateIndex(pConfig->AppInfoTableDyn, (uint32_t *)&appId)) { return NULL; } if ((entry = _dpd.snortAlloc(1, sizeof(*entry), PP_APP_ID, PP_MEM_CATEGORY_CONFIG))) { entry->appId = appId; entry->serviceId = entry->appId; entry->clientId = entry->appId; entry->payloadId = entry->appId; entry->appName = strdup(appName); if (!entry->appName) { _dpd.errMsg("failed to allocate appName"); _dpd.snortFree(entry, sizeof(*entry), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); return NULL; } dynamicArraySetIndex(pConfig->AppInfoTableDyn, appId, entry); if (pConfig->AppNameHash != NULL) appNameHashAdd(pConfig->AppNameHash, appName, entry); } else { _dpd.errMsg("calloc failure\n"); } } return entry; } void appInfoTableInit(tAppidStaticConfig* appidSC, tAppIdConfig* pConfig) { FILE *tableFile; const char *token; char buf[MAX_TABLE_LINE_LEN]; AppInfoTableEntry *entry; tAppId appId; uint32_t clientId, serviceId, payloadId; char filepath[PATH_MAX]; char *appName=NULL; char *snortName=NULL; pConfig->AppInfoTableDyn = dynamicArrayCreate(SF_APPID_DYNAMIC_MIN); snprintf(filepath, sizeof(filepath), "%s/odp/%s", appidSC->app_id_detector_path, APP_MAPPING_FILE); tableFile = fopen(filepath, "r"); if (tableFile == NULL) { _dpd.errMsg("Could not open RnaAppMapping Table file: %s\n", filepath); return; } DEBUG_WRAP(DebugMessage(DEBUG_APPID, " AppInfo read from %s\n", filepath);); while (fgets(buf, sizeof(buf), tableFile)) { token = strtok(buf, CONF_SEPARATORS ); if (!token) { _dpd.errMsg("Could not read id for Rna Id\n"); continue; } appId = strtol(token, NULL, 10); token = strtok(NULL, CONF_SEPARATORS ); if (!token) { _dpd.errMsg("Could not read appName. Line %s\n", buf); continue; } appName = strdup(token); if (!appName) { _dpd.errMsg("Could not allocate space for appName\n"); continue; } token = strtok(NULL, CONF_SEPARATORS ); if (!token) { _dpd.errMsg("Could not read service id for Rna Id\n"); free(appName); continue; } serviceId = strtol(token, NULL, 10); token = strtok(NULL, CONF_SEPARATORS ); if (!token) { _dpd.errMsg("Could not read client id for Rna Id\n"); free(appName); continue; } clientId = strtol(token, NULL, 10); token = strtok(NULL, CONF_SEPARATORS ); if (!token) { _dpd.errMsg("Could not read payload id for Rna Id\n"); free(appName); continue; } payloadId = strtol(token, NULL, 10); /* snort service key, if it exists */ token = strtok(NULL, CONF_SEPARATORS ); if (token) { snortName = strdup(token); if (!snortName) { _dpd.errMsg("malloc failure\n"); free(appName); continue; } } entry = _dpd.snortAlloc(1, sizeof(*entry), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); if (!entry) { _dpd.errMsg("AppInfoTable: Memory allocation failure\n"); free(appName); free(snortName); continue; } entry->next = pConfig->AppInfoList; pConfig->AppInfoList = entry; if (snortName) { #ifdef TARGET_BASED entry->snortId = _dpd.addProtocolReference(snortName); free(snortName); snortName = NULL; #endif } entry->appName = appName; entry->appId = appId; entry->serviceId = serviceId; entry->clientId = clientId; entry->payloadId = payloadId; entry->priority = APP_PRIORITY_DEFAULT; if ((appId = getAppIdStaticIndex(entry->appId))) pConfig->AppInfoTable[appId] = entry; if ((appId = getAppIdStaticIndex(entry->serviceId))) pConfig->AppInfoTableByService[appId] = entry; if ((appId = getAppIdStaticIndex(entry->clientId))) pConfig->AppInfoTableByClient[appId] = entry; if ((appId = getAppIdStaticIndex(entry->payloadId))) pConfig->AppInfoTableByPayload[appId] = entry; if (!pConfig->AppNameHash) { pConfig->AppNameHash = appNameHashInit(); } appNameHashAdd(pConfig->AppNameHash, appName, entry); } fclose(tableFile); /* Configuration defaults. */ appidSC->rtmp_max_packets = 15; appidSC->mdns_user_reporting = 1; appidSC->dns_host_reporting = 1; appidSC->max_tp_flow_depth = 5; appidSC->http2_detection_enabled = 0; appidSC->host_port_app_cache_lookup_interval = 10; appidSC->host_port_app_cache_lookup_range = 100000; appidSC->is_host_port_app_cache_runtime = 1; appidSC->check_host_port_app_cache = 0; appidSC->check_host_cache_unknown_ssl = 0; appidSC->recheck_for_unknown_appid = 0; appidSC->send_state_sharing_updates = 1; appidSC->allow_port_wildcard_host_cache = 0; appidSC->recheck_for_portservice_appid = 0; appidSC->max_packet_before_service_fail = MIN_MAX_PACKET_BEFORE_SERVICE_FAIL; appidSC->max_bytes_before_service_fail = MIN_MAX_BYTES_BEFORE_SERVICE_FAIL; appidSC->max_packet_service_fail_ignore_bytes = MIN_MAX_PACKET_BEFORE_SERVICE_FAIL_IGNORE_BYTES; appidSC->http_tunnel_detect = HTTP_TUNNEL_DETECT_RESTART; snprintf(filepath, sizeof(filepath), "%s/odp/%s", appidSC->app_id_detector_path, APP_CONFIG_FILE); appIdConfLoad (appidSC, filepath); snprintf(filepath, sizeof(filepath), "%s/../%s", appidSC->app_id_detector_path, USR_CONFIG_FILE); appIdConfLoad (appidSC, filepath); } void appInfoTableFini(tAppIdConfig *pConfig) { AppInfoTableEntry *entry; while ((entry = pConfig->AppInfoList)) { pConfig->AppInfoList = entry->next; if (entry->appName) free(entry->appName); _dpd.snortFree(entry, sizeof(*entry), PP_APP_ID, PP_MEM_CATEGORY_CONFIG); } dynamicArrayDestroy(pConfig->AppInfoTableDyn); pConfig->AppInfoTableDyn = NULL; appNameHashFini(pConfig->AppNameHash); } void appInfoTableDump(tAppIdConfig *pConfig) { AppInfoTableEntry *entry; tAppId appId; _dpd.errMsg("Cisco provided detectors:\n"); for (appId = 1; appId < SF_APPID_MAX; appId++) { entry = pConfig->AppInfoTable[appId]; if (entry) _dpd.errMsg("%s\t%d\t%s\n", entry->appName, entry->appId, (entry->flags & APPINFO_FLAG_ACTIVE)? "active":"inactive"); } _dpd.errMsg("User provided detectors:\n"); for (entry = dynamicArrayGetFirst(pConfig->AppInfoTableDyn); entry; entry = dynamicArrayGetNext(pConfig->AppInfoTableDyn)) { _dpd.errMsg("%s\t%d\t%s\n", entry->appName, entry->appId, (entry->flags & APPINFO_FLAG_ACTIVE)? "active":"inactive"); } } tAppId appGetAppFromServiceId(uint32_t appId, tAppIdConfig *pConfig) { AppInfoTableEntry *entry; tAppId tmp; if ((tmp = getAppIdStaticIndex(appId))) entry = pConfig->AppInfoTableByService[tmp]; else entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId); return entry ? entry->appId : APP_ID_NONE; } tAppId appGetAppFromClientId(uint32_t appId, tAppIdConfig *pConfig) { AppInfoTableEntry *entry; tAppId tmp; if ((tmp = getAppIdStaticIndex(appId))) entry = pConfig->AppInfoTableByClient[tmp]; else entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId); return entry ? entry->appId : APP_ID_NONE; } tAppId appGetAppFromPayloadId(uint32_t appId, tAppIdConfig *pConfig) { AppInfoTableEntry *entry; tAppId tmp; if ((tmp = getAppIdStaticIndex(appId))) entry = pConfig->AppInfoTableByPayload[tmp]; else entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId); return entry ? entry->appId : APP_ID_NONE; } const char * appGetAppName(int32_t appId) { AppInfoTableEntry *entry; tAppIdConfig *pConfig = appIdActiveConfigGet(); tAppId tmp; if ((tmp = getAppIdStaticIndex(appId))) entry = pConfig->AppInfoTable[tmp]; else entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId); return entry ? entry->appName : NULL; } int32_t appGetAppId(const char *appName) { AppInfoTableEntry *entry; tAppIdConfig *pConfig = appIdActiveConfigGet(); entry = appNameHashFind(pConfig->AppNameHash, appName); return entry?entry->appId:0; } void appInfoSetActive(tAppId appId, bool active) { AppInfoTableEntry *entry = NULL; tAppIdConfig *pConfig = appIdActiveConfigGet(); tAppId tmp; if (appId == APP_ID_NONE) return; if ((tmp = getAppIdStaticIndex(appId))) entry = pConfig->AppInfoTable[tmp]; else entry = dynamicArrayGetIndex(pConfig->AppInfoTableDyn, appId); if (entry) { if (active) entry->flags |= APPINFO_FLAG_ACTIVE; else entry->flags &= ~APPINFO_FLAG_ACTIVE; } else { _dpd.errMsg("AppInfo: AppId %d is UNKNOWN\n", appId); } } static void appIdConfLoad (tAppidStaticConfig* appidSC, const char *path) { FILE *config_file; char *token; char buf[1024]; char referred_app_list[4096]; int referred_app_index; char *conf_type; char *conf_key; char *conf_val; unsigned line = 0; tAppIdConfig *pConfig = appIdNewConfigGet(); int max_tp_flow_depth; int host_port_app_cache_lookup_interval; int host_port_app_cache_lookup_range; config_file = fopen(path, "r"); if (config_file == NULL) { return; } else { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "Loading configuration file %s\n", path);); } while (fgets(buf, sizeof(buf), config_file) != NULL) { line++; token = strtok(buf, CONF_SEPARATORS_USR_APPID); if (token == NULL) { _dpd.errMsg("Could not read configuration at line %s:%u\n", path, line); continue; } conf_type = token; token = strtok(NULL, CONF_SEPARATORS_USR_APPID); if (token == NULL) { _dpd.errMsg("Could not read configuration value at line %s:%u\n", path, line); continue; } conf_key = token; token = strtok(NULL, CONF_SEPARATORS_USR_APPID); if (token == NULL) { _dpd.errMsg("Could not read configuration value at line %s:%u\n", path, line); continue; } conf_val = token; /* APPID configurations are for anything else - currently we only have ssl_reinspect */ if (!(strcasecmp(conf_type, "appid"))) { if (!(strcasecmp(conf_key, "max_tp_flow_depth"))) { max_tp_flow_depth = atoi(conf_val); if (max_tp_flow_depth < MIN_MAX_TP_FLOW_DEPTH || max_tp_flow_depth > MAX_MAX_TP_FLOW_DEPTH) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: invalid max_tp_flow_depth %d, must be between %d and %d\n.", max_tp_flow_depth, MIN_MAX_TP_FLOW_DEPTH, MAX_MAX_TP_FLOW_DEPTH);); } else { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: setting max thirdparty inspection flow depth to %d packets.\n", max_tp_flow_depth);); appidSC->max_tp_flow_depth = max_tp_flow_depth; } } else if (!(strcasecmp(conf_key, "host_port_app_cache_lookup_interval"))) { host_port_app_cache_lookup_interval = atoi(conf_val); if (host_port_app_cache_lookup_interval < MIN_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL || host_port_app_cache_lookup_interval > MAX_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: invalid host_port_app_cache_lookup_interval %d, must be between %d and %d\n.", host_port_app_cache_lookup_interval, MIN_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL, MAX_HOST_PORT_APP_CACHE_LOOKUP_INTERVAL);); } else { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: hostPortCache lookup performed every %d packet(s).\n", host_port_app_cache_lookup_interval);); appidSC->host_port_app_cache_lookup_interval = host_port_app_cache_lookup_interval; } } else if (!(strcasecmp(conf_key, "host_port_app_cache_lookup_range"))) { host_port_app_cache_lookup_range = atoi(conf_val); if (host_port_app_cache_lookup_range < MIN_HOST_PORT_APP_CACHE_LOOKUP_RANGE || host_port_app_cache_lookup_range > MAX_HOST_PORT_APP_CACHE_LOOKUP_RANGE) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: invalid host_port_app_cache_lookup_range %d, must be between %d and %d\n.", host_port_app_cache_lookup_range, MIN_HOST_PORT_APP_CACHE_LOOKUP_RANGE, MAX_HOST_PORT_APP_CACHE_LOOKUP_RANGE);); } else { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: hostPortCache lookup performed till maximum of %d packet(s) per session.\n", host_port_app_cache_lookup_range);); appidSC->host_port_app_cache_lookup_range = host_port_app_cache_lookup_range; } } else if (!(strcasecmp(conf_key, "is_host_port_app_cache_runtime"))) { if (!(strcasecmp(conf_val, "disabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: hostPortCache not configured for runtime modification.\n");); appidSC->is_host_port_app_cache_runtime = 0; } } else if (!(strcasecmp(conf_key, "check_host_port_app_cache"))) { if (!(strcasecmp(conf_val, "enabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: hostPortCache lookup performed for every flow.\n");); appidSC->check_host_port_app_cache = 1; } } else if (!(strcasecmp(conf_key, "check_host_cache_unknown_ssl"))) { if (!(strcasecmp(conf_val, "enabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: hostPortCache lookup performed for SSL flows not having either of server name or certificate.\n");); appidSC->check_host_cache_unknown_ssl = 1; } } else if (!(strcasecmp(conf_key, "recheck_for_unknown_appid"))) { if (!(strcasecmp(conf_val, "enabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: Flows with unknown AppID's will not be ignored.\n");); appidSC->recheck_for_unknown_appid = 1; } } else if (!(strcasecmp(conf_key, "recheck_for_portservice_appid"))) { if (!(strcasecmp(conf_val, "enabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: Checking hostPortCache for flows with only port service AppID.\n");); appidSC->recheck_for_portservice_appid = 1; } } else if (!(strcasecmp(conf_key, "tp_allow_probes"))) { if (!(strcasecmp(conf_val, "enabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: TCP probes will be analyzed by NAVL.\n");); appidSC->tp_allow_probes = 1; } } else if (!(strcasecmp(conf_key, "tp_client_app"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: if thirdparty reports app %d, we will use it as a client.\n", atoi(conf_val));); appInfoEntryFlagSet(atoi(conf_val), APPINFO_FLAG_TP_CLIENT, pConfig); } else if (!(strcasecmp(conf_key, "ssl_reinspect"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: adding app %d to list of SSL apps that get more granular inspection.\n", atoi(conf_val));); appInfoEntryFlagSet(atoi(conf_val), APPINFO_FLAG_SSL_INSPECT, pConfig); } else if (!(strcasecmp(conf_key, "disable_safe_search"))) { if (!(strcasecmp(conf_val, "disabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: disabling safe search enforcement.\n");); appidSC->disable_safe_search = 1; } } else if (!(strcasecmp(conf_key, "ssl_squelch"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: adding app %d to list of SSL apps that may open a second SSL connection.\n", atoi(conf_val));); appInfoEntryFlagSet(atoi(conf_val), APPINFO_FLAG_SSL_SQUELCH, pConfig); } else if (!(strcasecmp(conf_key, "defer_to_thirdparty"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: adding app %d to list of apps where we should take thirdparty ID over the NDE's.\n", atoi(conf_val));); appInfoEntryFlagSet(atoi(conf_val), APPINFO_FLAG_DEFER, pConfig); } else if (!(strcasecmp(conf_key, "defer_payload_to_thirdparty"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: adding app %d to list of apps where we should take thirdparty payload ID over the NDE's.\n", atoi(conf_val));); appInfoEntryFlagSet(atoi(conf_val), APPINFO_FLAG_DEFER_PAYLOAD, pConfig); } else if (!(strcasecmp(conf_key, "chp_userid"))) { if (!(strcasecmp(conf_val, "disabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: HTTP UserID collection disabled.\n");); appidSC->chp_userid_disabled = 1; continue; } } else if (!(strcasecmp(conf_key, "chp_body_collection"))) { if (!(strcasecmp(conf_val, "disabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: HTTP Body header reading disabled.\n");); appidSC->chp_body_collection_disabled = 1; continue; } } else if (!(strcasecmp(conf_key, "chp_fflow"))) { if (!(strcasecmp(conf_val, "disabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: HTTP future flow creation disabled.\n");); appidSC->chp_fflow_disabled = 1; continue; } } else if (!(strcasecmp(conf_key, "ftp_userid"))) { if (!(strcasecmp(conf_val, "disabled"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: FTP userID disabled.\n");); appidSC->ftp_userid_disabled = 1; continue; } } else if (!(strcasecmp(conf_key, "max_bytes_before_service_fail"))) { uint64_t max_bytes_before_service_fail = atoi(conf_val); if (max_bytes_before_service_fail < MIN_MAX_BYTES_BEFORE_SERVICE_FAIL) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: invalid max_bytes_before_service_fail %"PRIu64" must be greater than %u\n.", max_bytes_before_service_fail, MIN_MAX_BYTES_BEFORE_SERVICE_FAIL );); } else appidSC->max_bytes_before_service_fail = max_bytes_before_service_fail; } else if (!(strcasecmp(conf_key, "max_packet_before_service_fail"))) { uint16_t max_packet_before_service_fail = atoi(conf_val); if (max_packet_before_service_fail < MIN_MAX_PACKET_BEFORE_SERVICE_FAIL) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: invalid max_packet_before_service_fail %"PRIu16", must be greater than %u \n.", max_packet_before_service_fail, MIN_MAX_PACKET_BEFORE_SERVICE_FAIL);); } else appidSC->max_packet_before_service_fail = max_packet_before_service_fail; } else if (!(strcasecmp(conf_key, "max_packet_service_fail_ignore_bytes"))) { uint16_t max_packet_service_fail_ignore_bytes = atoi(conf_val); if (max_packet_service_fail_ignore_bytes < MIN_MAX_PACKET_BEFORE_SERVICE_FAIL_IGNORE_BYTES) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: invalid max_packet_service_fail_ignore_bytes %"PRIu16", must be greater than %u\n.", max_packet_service_fail_ignore_bytes, MIN_MAX_PACKET_BEFORE_SERVICE_FAIL_IGNORE_BYTES);); } else appidSC->max_packet_service_fail_ignore_bytes= max_packet_service_fail_ignore_bytes; } else if (!(strcasecmp(conf_key, "http_tunnel_detect"))) { if (!(strcasecmp(conf_val, "restart_and_reset"))) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: HTTP tunnel detect set to restart and reset.\n");); appidSC->http_tunnel_detect = HTTP_TUNNEL_DETECT_RESTART_AND_RESET; continue; } } /* App Priority bit set*/ else if (!(strcasecmp(conf_key, "app_priority"))) { int temp_appid; temp_appid = strtol(conf_val, NULL, 10 ); token = strtok(NULL, CONF_SEPARATORS_USR_APPID); if (token == NULL) { _dpd.errMsg("Could not read app_priority at line %u\n", line); continue; } conf_val = token; uint8_t temp_val; temp_val = strtol(conf_val, NULL, 10 ); appInfoEntryPrioritySet (temp_appid, temp_val, pConfig); DEBUG_WRAP(DebugMessage(DEBUG_APPID,"AppId: %d Setting priority bit %d .\n", temp_appid, temp_val);); } else if (!(strcasecmp(conf_key, "referred_appId"))) { if (!(strcasecmp(conf_val, "disabled"))) { appidSC->referred_appId_disabled = 1; continue; } else if (!appidSC->referred_appId_disabled) { referred_app_index=0; referred_app_index += sprintf(referred_app_list, "%d ", atoi(conf_val)); appInfoEntryFlagSet(atoi(conf_val), APPINFO_FLAG_REFERRED, pConfig); while ((token = strtok(NULL, CONF_SEPARATORS_USR_APPID)) != NULL) { referred_app_index += sprintf(referred_app_list+referred_app_index, "%d ", atoi(token)); appInfoEntryFlagSet(atoi(token), APPINFO_FLAG_REFERRED, pConfig); } DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppId: adding appIds to list of referred web apps: %s\n", referred_app_list);); } } else if (!(strcasecmp(conf_key, "rtmp_max_packets"))) { appidSC->rtmp_max_packets = atoi(conf_val); } else if (!(strcasecmp(conf_key, "mdns_user_report"))) { appidSC->mdns_user_reporting = atoi(conf_val); } else if (!(strcasecmp(conf_key, "dns_host_report"))) { appidSC->dns_host_reporting = atoi(conf_val); } else if (!(strcasecmp(conf_key, "chp_body_max_bytes"))) { appidSC->chp_body_collection_max = atoi(conf_val); } else if (!(strcasecmp(conf_key, "ignore_thirdparty_appid"))) { _dpd.logMsg("AppId: adding app %d to list of ignore thirdparty apps.\n", atoi(conf_val)); appInfoEntryFlagSet(atoi(conf_val), APPINFO_FLAG_IGNORE, pConfig); } else if (!(strcasecmp(conf_key, "http2_detection"))) { // This option will control our own HTTP/2 detection. We can // still be told externally, though, that it's HTTP/2 (either // from HTTP Inspect or 3rd Party). This is intended to be // used to ask AppID to detect unencrypted HTTP/2 on non-std // ports. if (!(strcasecmp(conf_val, "disabled"))) { _dpd.logMsg("AppId: disabling internal HTTP/2 detection.\n"); appidSC->http2_detection_enabled = 0; } else if (!(strcasecmp(conf_val, "enabled"))) { _dpd.logMsg("AppId: enabling internal HTTP/2 detection.\n"); appidSC->http2_detection_enabled = 1; } else { _dpd.logMsg("AppId: ignoring invalid option for http2_detection: %s\n", conf_val); } } else if (!(strcasecmp(conf_key, "send_state_sharing_updates"))) { if (!(strcasecmp(conf_val, "disabled"))) { _dpd.logMsg("AppId: Disabling state sharing updates.\n"); appidSC->send_state_sharing_updates = 0; } } else if (!(strcasecmp(conf_key, "allow_port_wildcard_host_cache"))) { if (!(strcasecmp(conf_val, "enabled"))) { _dpd.logMsg("AppId: Enabling wild card for port numbers in hostPortAppCache.\n"); appidSC->allow_port_wildcard_host_cache = 1; } } else if (!(strcasecmp(conf_key, "bittorrent_aggressiveness"))) { int aggressiveness = atoi(conf_val); _dpd.logMsg("AppId: bittorrent_aggressiveness %d\n", aggressiveness); if (aggressiveness >= 50) { appidSC->recheck_for_unknown_appid = 1; appidSC->recheck_for_portservice_appid = 1; appidSC->host_port_app_cache_lookup_interval = 5; appidSC->max_tp_flow_depth = 25; appInfoEntryFlagSet(APP_ID_BITTORRENT, APPINFO_FLAG_DEFER, pConfig); appInfoEntryFlagSet(APP_ID_BITTORRENT, APPINFO_FLAG_DEFER_PAYLOAD, pConfig); } if (aggressiveness >= 80) { appidSC->allow_port_wildcard_host_cache = 1; } } else if (!(strcasecmp(conf_key, "ultrasurf_aggressiveness"))) { int aggressiveness = atoi(conf_val); _dpd.logMsg("AppId: ultrasurf_aggressiveness %d\n", aggressiveness); if (aggressiveness >= 50) { appidSC->check_host_cache_unknown_ssl = 1; appidSC->max_tp_flow_depth = 25; appInfoEntryFlagSet(APP_ID_ULTRASURF, APPINFO_FLAG_DEFER, pConfig); appInfoEntryFlagSet(APP_ID_ULTRASURF, APPINFO_FLAG_DEFER_PAYLOAD, pConfig); } if (aggressiveness >= 80) { appidSC->recheck_for_unknown_appid = 1; appidSC->allow_port_wildcard_host_cache = 1; } } else if (!(strcasecmp(conf_key, "psiphon_aggressiveness"))) { int aggressiveness = atoi(conf_val); _dpd.logMsg("AppId: psiphon_aggressiveness %d\n", aggressiveness); if (aggressiveness >= 50) { appidSC->check_host_cache_unknown_ssl = 1; appidSC->max_tp_flow_depth = 25; appInfoEntryFlagSet(APP_ID_PSIPHON, APPINFO_FLAG_DEFER, pConfig); appInfoEntryFlagSet(APP_ID_PSIPHON, APPINFO_FLAG_DEFER_PAYLOAD, pConfig); } if (aggressiveness >= 80) { appidSC->recheck_for_unknown_appid = 1; appidSC->allow_port_wildcard_host_cache = 1; } } else if (!(strcasecmp(conf_key, "multipayload_max_packets"))) { appidSC->multipayload_max_packets = atoi(conf_val); _dpd.logMsg("AppId: Multipayload feature will scan up to %d packets.\n", appidSC->multipayload_max_packets); } } } fclose(config_file); } snort-2.9.20/src/dynamic-preprocessors/appid/lengthAppCache.c0000644000175000017500000000436514241075445022340 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include "lengthAppCache.h" #include #include "flow.h" #include "sfutil.h" #include "commonAppMatcher.h" #include "appIdConfig.h" #define HASH_NUM_ROWS (1024) void lengthAppCacheInit(tAppIdConfig *pConfig) { if (!(pConfig->lengthCache = sfxhash_new(HASH_NUM_ROWS, sizeof(tLengthKey), sizeof(tAppId), 0, 0, NULL, NULL, 0))) { _dpd.errMsg("lengthAppCache: Failed to allocate length cache!"); } } void lengthAppCacheFini(tAppIdConfig *pConfig) { if (pConfig->lengthCache) { sfxhash_delete(pConfig->lengthCache); pConfig->lengthCache = NULL; } } tAppId lengthAppCacheFind(const tLengthKey *key, const tAppIdConfig *pConfig) { tAppId *val; val = (tAppId*)sfxhash_find(pConfig->lengthCache, (void *)key); if (val == NULL) { return APP_ID_NONE; /* no match */ } else { return *val; /* match found */ } } int lengthAppCacheAdd(const tLengthKey *key, tAppId val, tAppIdConfig *pConfig) { if (sfxhash_add(pConfig->lengthCache, (void *)key, (void *)&val)) { return 0; } return 1; /* OK */ } snort-2.9.20/src/dynamic-preprocessors/appid/flow.c0000644000175000017500000001407614241075435020440 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include "common_util.h" #include "flow.h" #include "service_api.h" #include "fw_appid.h" static AppIdFlowData *fd_free_list; void AppIdFlowdataFree(tAppIdData *flowp) { AppIdFlowData *tmp_fd; while ((tmp_fd = flowp->flowData)) { flowp->flowData = tmp_fd->next; if (tmp_fd->fd_data && tmp_fd->fd_free) tmp_fd->fd_free(tmp_fd->fd_data); tmp_fd->next = fd_free_list; fd_free_list = tmp_fd; app_id_flow_data_free_list_count++; } } void AppIdFlowdataFini() { AppIdFlowData *tmp_fd; while ((tmp_fd = fd_free_list)) { fd_free_list = fd_free_list->next; app_id_flow_data_free_list_count--; _dpd.snortFree(tmp_fd, sizeof(*tmp_fd), PP_APP_ID, PP_MEM_CATEGORY_SESSION); } } void *AppIdFlowdataGet(tAppIdData *flowp, unsigned id) { AppIdFlowData *tmp_fd; for (tmp_fd = flowp->flowData; tmp_fd && tmp_fd->fd_id != id; tmp_fd = tmp_fd->next); return tmp_fd ? tmp_fd->fd_data : NULL; } void *AppIdFlowdataRemove(tAppIdData *flowp, unsigned id) { AppIdFlowData **pfd; AppIdFlowData *fd; for (pfd = &flowp->flowData; *pfd && (*pfd)->fd_id != id; pfd = &(*pfd)->next); if ((fd = *pfd)) { *pfd = fd->next; fd->next = fd_free_list; fd_free_list = fd; app_id_flow_data_free_list_count++; return fd->fd_data; } return NULL; } void AppIdFlowdataDelete(tAppIdData *flowp, unsigned id) { AppIdFlowData **pfd; AppIdFlowData *fd; for (pfd = &flowp->flowData; *pfd && (*pfd)->fd_id != id; pfd = &(*pfd)->next); if ((fd = *pfd)) { *pfd = fd->next; if (fd->fd_data && fd->fd_free) fd->fd_free(fd->fd_data); fd->next = fd_free_list; fd_free_list = fd; app_id_flow_data_free_list_count++; } } void AppIdFlowdataDeleteAllByMask(tAppIdData *flowp, unsigned mask) { AppIdFlowData **pfd; AppIdFlowData *fd; pfd = &flowp->flowData; while (*pfd) { if ((*pfd)->fd_id & mask) { fd = *pfd; *pfd = fd->next; if (fd->fd_data && fd->fd_free) fd->fd_free(fd->fd_data); fd->next = fd_free_list; fd_free_list = fd; app_id_flow_data_free_list_count++; } else { pfd = &(*pfd)->next; } } } int AppIdFlowdataAdd(tAppIdData *flowp, void *data, unsigned id, AppIdFreeFCN fcn) { AppIdFlowData *tmp_fd; if (fd_free_list) { tmp_fd = fd_free_list; fd_free_list = tmp_fd->next; app_id_flow_data_free_list_count--; } else if (!(tmp_fd = _dpd.snortAlloc(1, sizeof(*tmp_fd), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) return -1; tmp_fd->fd_id = id; tmp_fd->fd_data = data; tmp_fd->fd_free = fcn; tmp_fd->next = flowp->flowData; flowp->flowData = tmp_fd; return 0; } int AppIdFlowdataAddId(tAppIdData *flowp, uint16_t port, const tRNAServiceElement *svc_element) { if (flowp->serviceData) return -1; flowp->serviceData = svc_element; flowp->service_port = port; return 0; } #ifdef RNA_DEBUG_EXPECTED_FLOWS static void flowAppSharedDataDelete(tAppIdData *sharedData) { _dpd.errMsg("Deleting %p\n",sharedData); appSharedDataDelete(sharedData); } #endif tAppIdData *AppIdEarlySessionCreate(tAppIdData *flowp, SFSnortPacket *ctrlPkt, sfaddr_t *cliIp, uint16_t cliPort, sfaddr_t *srvIp, uint16_t srvPort, uint8_t proto, int16_t app_id, int flags) { char src_ip[INET6_ADDRSTRLEN]; char dst_ip[INET6_ADDRSTRLEN]; struct _ExpectNode** node; tAppIdData *data; if (app_id_debug_session_flag) { inet_ntop(sfaddr_family(cliIp), (void *)sfaddr_get_ptr(cliIp), src_ip, sizeof(src_ip)); inet_ntop(sfaddr_family(srvIp), (void *)sfaddr_get_ptr(srvIp), dst_ip, sizeof(dst_ip)); } data = appSharedDataAlloc(proto, (struct in6_addr*)sfaddr_get_ip6_ptr(cliIp), 0); if (data) data->common.policyId = appIdPolicyId; node = (flags & APPID_EARLY_SESSION_FLAG_FW_RULE) ? &ctrlPkt->expectedSession : NULL; if (_dpd.sessionAPI->set_application_protocol_id_expected(ctrlPkt, cliIp, cliPort, srvIp, srvPort, proto, app_id, PP_APP_ID, data, #ifdef RNA_DEBUG_EXPECTED_FLOWS (void (*)(void *))flowAppSharedDataDelete #else (void (*)(void *))appSharedDataDelete #endif , node) ) { if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s failed to create a related flow for %s-%u -> %s-%u %u\n", app_id_debug_session, src_ip, (unsigned)cliPort, dst_ip, (unsigned)srvPort, (unsigned)proto); appSharedDataDelete(data); return NULL; } else if (app_id_debug_session_flag) _dpd.logMsg("AppIdDbg %s created a related flow for %s-%u -> %s-%u %u\n", app_id_debug_session, src_ip, (unsigned)cliPort, dst_ip, (unsigned)srvPort, (unsigned)proto); return data; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/0000755000175000017500000000000014242725717022675 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_sip.h0000644000175000017500000000334714241075422025527 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DETECTOR_SIP_H__ #define __DETECTOR_SIP_H__ #include "client_app_api.h" #include "service_api.h" #include "sf_multi_mpse.h" #include "sf_mlmp.h" struct RNAServiceValidationModule; typedef struct { tAppId clientAppId; char* clientVersion; } tSipUaUserData; typedef struct _tDetectorAppSipPattern { tMlpPattern pattern; tSipUaUserData userData; struct _tDetectorAppSipPattern *next; } tDetectorAppSipPattern; typedef struct { void *sipUaMatcher; tDetectorAppSipPattern *appSipUaList; void *sipServerMatcher; tDetectorAppSipPattern *appSipServerList; } tDetectorSipConfig; extern struct RNAClientAppModule sip_udp_client_mod; extern struct RNAClientAppModule sip_tcp_client_mod; extern struct RNAServiceValidationModule sip_service_mod; #endif /* __DETECTOR_SIP_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_smtp.c0000644000175000017500000011627014241075430025711 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" //#include "flow.h" #include "detector_api.h" //#define UNIT_TESTING #ifdef UNIT_TESTING #include "fw_appid.h" #endif typedef enum { SMTP_CLIENT_STATE_NONE, SMTP_CLIENT_STATE_HELO, SMTP_CLIENT_STATE_MAIL_FROM, SMTP_CLIENT_STATE_RCPT_TO, SMTP_CLIENT_STATE_DATA, SMTP_CLIENT_STATE_MESSAGE, SMTP_CLIENT_STATE_GET_PRODUCT_VERSION, SMTP_CLIENT_STATE_SKIP_LINE, SMTP_CLIENT_STATE_SKIP_SPACE, SMTP_CLIENT_STATE_SKIP_EOL, SMTP_CLIENT_STATE_CONNECTION_ERROR, SMTP_CLIENT_STATE_STARTTLS, SMTP_CLIENT_STATE_LOGIN_USER, SMTP_CLIENT_STATE_LOGIN_PASSWORD } SMTPClientState; #define MAX_HEADER_LINE_SIZE 1024 #ifdef UNIT_TESTING char *stateName [] = { "SMTP_CLIENT_STATE_NONE", "SMTP_CLIENT_STATE_HELO", "SMTP_CLIENT_STATE_MAIL_FROM", "SMTP_CLIENT_STATE_RCPT_TO", "SMTP_CLIENT_STATE_DATA", "SMTP_CLIENT_STATE_MESSAGE", "SMTP_CLIENT_STATE_GET_PRODUCT_VERSION", "SMTP_CLIENT_STATE_SKIP_LINE", "SMTP_CLIENT_STATE_CONNECTION_ERROR", "SMTP_CLIENT_STATE_STARTTLS" }; #endif /* flag values for ClientSMTPData */ #define CLIENT_FLAG_STARTTLS_SUCCESS 0x01 #define MAX_VERSION_SIZE 64 #define SSL_WAIT_PACKETS 8 // This many un-decrypted packets without a HELO and we quit. typedef struct { int flags; SMTPClientState state; SMTPClientState nextstate; uint8_t version[MAX_VERSION_SIZE]; unsigned pos; uint8_t *headerline; int decryption_countdown; } ClientSMTPData; typedef struct _SMTP_CLIENT_APP_CONFIG { int enabled; } SMTP_CLIENT_APP_CONFIG; static SMTP_CLIENT_APP_CONFIG smtp_config; static CLIENT_APP_RETCODE smtp_ca_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE smtp_ca_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig); tRNAClientAppModule smtp_client_mod = { .name = "SMTP", .proto = IPPROTO_TCP, .init = &smtp_ca_init, .validate = &smtp_ca_validate, .minimum_matches = 1 }; typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; #define HELO "HELO" #define EHLO "EHLO" #define MAILFROM "MAIL FROM:" #define RCPTTO "RCPT TO:" #define DATA "DATA" #define RSET "RSET" #define AUTH "AUTH " #define AUTH_PLAIN "AUTH PLAIN" #define AUTH_LOGIN "AUTH LOGIN" #define STARTTLS "STARTTLS" #define STARTTLS_COMMAND_SUCCESS "220 " #define MICROSOFT "Microsoft" #define OUTLOOK "Outlook" #define EXPRESS "Express " #define IMO "IMO, " #define MAC "Mac" #define XMAILER "X-Mailer: " #define USERAGENT "User-Agent: " static const uint8_t APP_SMTP_OUTLOOK[] = "Microsoft Outlook"; static const uint8_t APP_SMTP_OUTLOOK_MAC[] = "Microsoft-MacOutlook"; static const uint8_t APP_SMTP_OUTLOOK_EXPRESS[] = "Microsoft Outlook Express "; static const uint8_t APP_SMTP_IMO[] = "IMO, "; static const uint8_t APP_SMTP_EVOLUTION[] = "Ximian Evolution "; static const uint8_t APP_SMTP_LOTUSNOTES[] = "Lotus Notes "; static const uint8_t APP_SMTP_APPLEMAIL[] = "Apple Mail ("; static const uint8_t APP_SMTP_EUDORA[] = "QUALCOMM Windows Eudora Version "; static const uint8_t APP_SMTP_EUDORAPRO[] = "Windows Eudora Pro Version "; static const uint8_t APP_SMTP_AOL[] = "AOL "; static const uint8_t APP_SMTP_MUTT[] = "Mutt/"; static const uint8_t APP_SMTP_KMAIL[] = "KMail/"; static const uint8_t APP_SMTP_MTHUNDERBIRD[] = "Mozilla Thunderbird "; static const uint8_t APP_SMTP_THUNDERBIRD[] = "Thunderbird "; static const uint8_t APP_SMTP_MOZILLA[] = "Mozilla"; static const uint8_t APP_SMTP_THUNDERBIRD_SHORT[] = "Thunderbird/"; static Client_App_Pattern patterns[] = { {(uint8_t *)HELO, sizeof(HELO)-1, 0, APP_ID_SMTP}, {(uint8_t *)EHLO, sizeof(EHLO)-1, 0, APP_ID_SMTP}, {APP_SMTP_OUTLOOK, sizeof(APP_SMTP_OUTLOOK)-1, -1, APP_ID_OUTLOOK}, {APP_SMTP_OUTLOOK_MAC, sizeof(APP_SMTP_OUTLOOK_MAC)-1, -1, APP_ID_OUTLOOK}, {APP_SMTP_OUTLOOK_EXPRESS, sizeof(APP_SMTP_OUTLOOK_EXPRESS)-1,-1, APP_ID_OUTLOOK_EXPRESS}, {APP_SMTP_IMO, sizeof(APP_SMTP_IMO)-1, -1, APP_ID_SMTP_IMO}, {APP_SMTP_EVOLUTION, sizeof(APP_SMTP_EVOLUTION)-1, -1, APP_ID_EVOLUTION}, {APP_SMTP_LOTUSNOTES, sizeof(APP_SMTP_LOTUSNOTES)-1, -1, APP_ID_LOTUS_NOTES}, {APP_SMTP_APPLEMAIL, sizeof(APP_SMTP_APPLEMAIL)-1, -1, APP_ID_APPLE_EMAIL}, {APP_SMTP_EUDORA, sizeof(APP_SMTP_EUDORA)-1, -1, APP_ID_EUDORA}, {APP_SMTP_EUDORAPRO, sizeof(APP_SMTP_EUDORAPRO)-1, -1, APP_ID_EUDORA_PRO}, {APP_SMTP_AOL, sizeof(APP_SMTP_AOL)-1, -1, APP_ID_AOL_EMAIL}, {APP_SMTP_MUTT, sizeof(APP_SMTP_MUTT)-1, -1, APP_ID_MUTT}, {APP_SMTP_KMAIL, sizeof(APP_SMTP_KMAIL)-1, -1, APP_ID_KMAIL}, {APP_SMTP_MTHUNDERBIRD, sizeof(APP_SMTP_MTHUNDERBIRD)-1, -1, APP_ID_THUNDERBIRD}, {APP_SMTP_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD)-1, -1, APP_ID_THUNDERBIRD}, }; static tAppRegistryEntry clientAppIdRegistry[] = { {APP_ID_THUNDERBIRD, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_OUTLOOK, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_KMAIL, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_EUDORA_PRO, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_EVOLUTION, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_SMTP_IMO, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_EUDORA, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_LOTUS_NOTES, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_APPLE_EMAIL, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_AOL_EMAIL, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_MUTT, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_SMTP, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_OUTLOOK_EXPRESS, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_SMTPS, APPINFO_FLAG_CLIENT_ADDITIONAL} }; static CLIENT_APP_RETCODE smtp_ca_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; smtp_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { smtp_config.enabled = atoi(item->value); } } } if (smtp_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { init_api->RegisterPattern(&smtp_ca_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(clientAppIdRegistry)/sizeof(*clientAppIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",clientAppIdRegistry[j].appId); init_api->RegisterAppId(&smtp_ca_validate, clientAppIdRegistry[j].appId, clientAppIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } #define SMTP_PORT 25 #define SMTP_CLOSING_CONN "closing connection\x0d\x0a" typedef enum { SMTP_SERVICE_STATE_NONE, SMTP_SERVICE_STATE_CONNECTION, SMTP_SERVICE_STATE_HELO, SMTP_SERVICE_STATE_BAD_CLIENT, SMTP_SERVICE_STATE_TRANSFER, SMTP_SERVICE_STATE_CONNECTION_ERROR, SMTP_SERVICE_STATE_STARTTLS, SMTP_SERVICE_STATE_SSL_HANDSHAKE } SMTPServiceState; typedef struct _SERVICE_SMTP_DATA { SMTPServiceState state; int code; int multiline; int set_flags; } ServiceSMTPData; #pragma pack(1) typedef struct _SERVICE_SMTP_CODE { uint8_t code[3]; uint8_t sp; } ServiceSMTPCode; #pragma pack() static int smtp_svc_init(const InitServiceAPI * const init_api); static int smtp_svc_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &smtp_svc_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "smtp", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&smtp_svc_validate, SMTP_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; tRNAServiceValidationModule smtp_service_mod = { .name = "SMTP", .init = &smtp_svc_init, .pp = pp, }; #define SMTP_PATTERN1 "220 " #define SMTP_PATTERN2 "220-" #define SMTP_PATTERN3 "SMTP" #define SMTP_PATTERN4 "smtp" static tAppRegistryEntry appIdRegistry[] = { {APP_ID_SMTP, APPINFO_FLAG_SERVICE_ADDITIONAL}, {APP_ID_SMTPS, APPINFO_FLAG_SERVICE_ADDITIONAL} }; typedef struct _SMTP_DETECTOR_DATA { ClientSMTPData client; ServiceSMTPData server; int need_continue; } SMTPDetectorData; SF_SO_PUBLIC RNADetectorValidationModule smtp_detector_mod = { .service = &smtp_service_mod, .client = &smtp_client_mod, }; static int smtp_svc_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN1, sizeof(SMTP_PATTERN1)-1, 0, "smtp", init_api->pAppidConfig); init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN2, sizeof(SMTP_PATTERN2)-1, 0, "smtp", init_api->pAppidConfig); init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN3, sizeof(SMTP_PATTERN3)-1, -1, "smtp", init_api->pAppidConfig); init_api->RegisterPattern(&smtp_svc_validate, IPPROTO_TCP, (uint8_t *)SMTP_PATTERN4, sizeof(SMTP_PATTERN4)-1, -1, "smtp", init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&smtp_svc_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int ExtractVersion(ClientSMTPData * const fd, const uint8_t *product, const uint8_t *data, tAppIdData *flowp, SFSnortPacket *pkt, const int dir, const tAppIdConfig *pConfig) { const u_int8_t *p; u_int8_t *v; u_int8_t *v_end; unsigned len; unsigned sublen; v_end = fd->version; v_end += MAX_VERSION_SIZE - 1; len = data - product; if (len >= sizeof(MICROSOFT) && memcmp(product, MICROSOFT, sizeof(MICROSOFT)-1) == 0) { p = product + sizeof(MICROSOFT) - 1; if (*p == '-' || isspace(*p)) p++; else return 1; if (data-p >= (int)sizeof(MAC) && memcmp(p, MAC, sizeof(MAC)-1) == 0) p += sizeof(MAC) - 1; if (data-p >= (int)sizeof(OUTLOOK) && memcmp(p, OUTLOOK, sizeof(OUTLOOK)-1) == 0) { p += sizeof(OUTLOOK) - 1; if (*p == ',' || *p == '/' || isspace(*p)) { p++; if (data-p >= (int)sizeof(EXPRESS) && memcmp(p, EXPRESS, sizeof(EXPRESS)-1) == 0) { p += sizeof(EXPRESS) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_OUTLOOK_EXPRESS, (char *)fd->version); return 0; } else if (data-p >= (int)sizeof(IMO) && memcmp(p, IMO, sizeof(IMO)-1) == 0) { p += sizeof(IMO) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_SMTP_IMO, (char *)fd->version); return 0; } else { if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_OUTLOOK, (char *)fd->version); return 0; } } } } else if (len >= sizeof(APP_SMTP_EVOLUTION) && memcmp(product, APP_SMTP_EVOLUTION, sizeof(APP_SMTP_EVOLUTION)-1) == 0) { p = product + sizeof(APP_SMTP_EVOLUTION) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_EVOLUTION, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_LOTUSNOTES) && memcmp(product, APP_SMTP_LOTUSNOTES, sizeof(APP_SMTP_LOTUSNOTES)-1) == 0) { p = product + sizeof(APP_SMTP_LOTUSNOTES) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_LOTUS_NOTES, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_APPLEMAIL) && memcmp(product, APP_SMTP_APPLEMAIL, sizeof(APP_SMTP_APPLEMAIL)-1) == 0) { p = product + sizeof(APP_SMTP_APPLEMAIL) - 1; if (p >= data || *(data - 1) != ')' || *p == ')' || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_APPLE_EMAIL, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_EUDORA) && memcmp(product, APP_SMTP_EUDORA, sizeof(APP_SMTP_EUDORA)-1) == 0) { p = product + sizeof(APP_SMTP_EUDORA) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_EUDORA, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_EUDORAPRO) && memcmp(product, APP_SMTP_EUDORAPRO, sizeof(APP_SMTP_EUDORAPRO)-1) == 0) { p = product + sizeof(APP_SMTP_EUDORAPRO) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_EUDORA_PRO, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_AOL) && memcmp(product, APP_SMTP_AOL, sizeof(APP_SMTP_AOL)-1) == 0) { p = product + sizeof(APP_SMTP_AOL) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_AOL_EMAIL, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_MUTT) && memcmp(product, APP_SMTP_MUTT, sizeof(APP_SMTP_MUTT)-1) == 0) { p = product + sizeof(APP_SMTP_MUTT) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_MUTT, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_KMAIL) && memcmp(product, APP_SMTP_KMAIL, sizeof(APP_SMTP_KMAIL)-1) == 0) { p = product + sizeof(APP_SMTP_KMAIL) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_SMTP/*KMAIL_ID*/, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_THUNDERBIRD) && memcmp(product, APP_SMTP_THUNDERBIRD, sizeof(APP_SMTP_THUNDERBIRD)-1) == 0) { p = product + sizeof(APP_SMTP_THUNDERBIRD) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_THUNDERBIRD, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_MTHUNDERBIRD) && memcmp(product, APP_SMTP_MTHUNDERBIRD, sizeof(APP_SMTP_MTHUNDERBIRD)-1) == 0) { p = product + sizeof(APP_SMTP_MTHUNDERBIRD) - 1; if (p >= data || isspace(*p)) return 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_THUNDERBIRD, (char *)fd->version); return 0; } else if (len >= sizeof(APP_SMTP_MOZILLA) && memcmp(product, APP_SMTP_MOZILLA, sizeof(APP_SMTP_MOZILLA)-1) == 0) { for (p = product + sizeof(APP_SMTP_MOZILLA) - 1; p < data; p++) { if (*p == 'T') { sublen = data - p; if (sublen >= sizeof(APP_SMTP_THUNDERBIRD_SHORT) && memcmp(p, APP_SMTP_THUNDERBIRD_SHORT, sizeof(APP_SMTP_THUNDERBIRD_SHORT)-1) == 0) { p = p + sizeof(APP_SMTP_THUNDERBIRD_SHORT) - 1; for (v=fd->version; vadd_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_THUNDERBIRD, (char *)fd->version); return 0; } } } } return 1; } static void smtp_free_state(void *data) { SMTPDetectorData *dd = (SMTPDetectorData *)data; ClientSMTPData *cd; if (dd) { cd = &dd->client; if (cd->headerline) free(cd->headerline); free(dd); } } static inline SMTPDetectorData *smtp_get_SMTPDetectorData(tAppIdData *flowp) { SMTPDetectorData *dd = smtp_detector_mod.api->data_get(flowp, smtp_detector_mod.flow_data_index); if (dd) return dd; if ((dd = calloc(1, sizeof(*dd))) == NULL) return NULL; if (smtp_detector_mod.api->data_add(flowp, dd, smtp_detector_mod.flow_data_index, &smtp_free_state)) { free(dd); return NULL; } dd->server.state = SMTP_SERVICE_STATE_CONNECTION; dd->client.state = SMTP_CLIENT_STATE_HELO; dd->need_continue = 1; setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); return dd; } // #define UNIT_TEST_SKIP static CLIENT_APP_RETCODE smtp_ca_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const struct appIdConfig_ *pConfig) { SMTPDetectorData *dd; ClientSMTPData *fd; const uint8_t *end; unsigned len; int line_break = 0; SMTPServiceState serviceState; #ifdef UNIT_TESTING SMTPClientState currState = SMTP_CLIENT_STATE_NONE; #endif #ifdef APP_ID_USES_REASSEMBLED smtp_detector_mod.streamAPI->response_flush_stream(pkt); #endif if ((dd = smtp_get_SMTPDetectorData(flowp)) == NULL) return CLIENT_APP_ENOMEM; if (dir != APP_ID_FROM_INITIATOR) return CLIENT_APP_INPROCESS; fd = &dd->client; if (getAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_DECRYPTED) == APPID_SESSION_ENCRYPTED) { if ((fd->flags & CLIENT_FLAG_STARTTLS_SUCCESS)) { fd->decryption_countdown--; if (!fd->decryption_countdown) #ifdef UNIT_TEST_SKIP if (flowp->session_packet_count == 0) #endif { /* Because we can't see any further info without decryption we settle for plain APP_ID_SMTPS instead of perhaps finding data that would make calling ExtractVersion() worthwhile, So set the appid and call it good. */ smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTPS, APP_ID_SMTPS, NULL); goto done; } } return CLIENT_APP_INPROCESS; } for (end = data + size; data < end; data++) { #ifdef UNIT_TESTING if (app_id_debug_session_flag && currState != fd->state) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "AppIdDbg %s SMTP client state %s\n", app_id_debug_session, stateName[fd->state]);); currState = fd->state; } #endif len = end - data; switch (fd->state) { case SMTP_CLIENT_STATE_HELO: if (len >= (sizeof(HELO)-1) && strncasecmp((const char *)data, HELO, sizeof(HELO)-1) == 0) { data += (sizeof(HELO)-1)-1; fd->nextstate = SMTP_CLIENT_STATE_MAIL_FROM; fd->state = SMTP_CLIENT_STATE_SKIP_SPACE; fd->flags &= ~ CLIENT_FLAG_STARTTLS_SUCCESS; } else if (len >= (sizeof(EHLO)-1) && strncasecmp((const char *)data, EHLO, sizeof(EHLO)-1) == 0) { data += (sizeof(EHLO)-1)-1; fd->nextstate = SMTP_CLIENT_STATE_MAIL_FROM; fd->state = SMTP_CLIENT_STATE_SKIP_SPACE; fd->flags &= ~ CLIENT_FLAG_STARTTLS_SUCCESS; } else goto done; break; case SMTP_CLIENT_STATE_MAIL_FROM: serviceState = dd->server.state; if (len >= (sizeof(MAILFROM)-1) && strncasecmp((const char *)data, MAILFROM, sizeof(MAILFROM)-1) == 0) { data += (sizeof(MAILFROM)-1)-1; fd->nextstate = SMTP_CLIENT_STATE_RCPT_TO; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else if (len >= (sizeof(RSET)-1) && strncasecmp((const char *)data, RSET, sizeof(RSET)-1) == 0) { data += (sizeof(RSET)-1)-1; fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else if (len >= (sizeof(AUTH_PLAIN)-1) && strncasecmp((const char *)data, AUTH_PLAIN, sizeof(AUTH_PLAIN)-1) == 0) { data += (sizeof(AUTH_PLAIN)-1)-1; fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else if (len >= (sizeof(AUTH_LOGIN)-1) && strncasecmp((const char *)data, AUTH_LOGIN, sizeof(AUTH_LOGIN)-1) == 0) { data += (sizeof(AUTH_LOGIN)-1)-1; fd->nextstate = SMTP_CLIENT_STATE_LOGIN_USER; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else if (len >= (sizeof(AUTH)-1) && strncasecmp((const char *)data, AUTH, sizeof(AUTH)-1) == 0) { data += (sizeof(AUTH)-1)-1; fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else if (len >= (sizeof(STARTTLS)-1) && strncasecmp((const char *)data, STARTTLS, sizeof(STARTTLS)-1) == 0) { data += (sizeof(STARTTLS)-1)-1; serviceState = dd->server.state = SMTP_SERVICE_STATE_STARTTLS; fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } /* check for state reversion */ else if (len >= (sizeof(HELO)-1) && strncasecmp((const char *)data, HELO, sizeof(HELO)-1) == 0) { data += (sizeof(HELO)-1)-1; fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; dd->server.state = SMTP_SERVICE_STATE_HELO; // make sure that service side expects the 250 } else if (len >= (sizeof(EHLO)-1) && strncasecmp((const char *)data, EHLO, sizeof(EHLO)-1) == 0) { data += (sizeof(EHLO)-1)-1; fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; dd->server.state = SMTP_SERVICE_STATE_HELO; // make sure that service side expects the 250 } else goto done; if (serviceState == SMTP_SERVICE_STATE_TRANSFER) { setAppIdFlag(flowp, APPID_SESSION_CONTINUE); smtp_service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_SMTP, NULL, NULL, NULL, NULL); } break; case SMTP_CLIENT_STATE_LOGIN_USER: { fd->nextstate = SMTP_CLIENT_STATE_LOGIN_PASSWORD; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } break; case SMTP_CLIENT_STATE_LOGIN_PASSWORD: { fd->nextstate = SMTP_CLIENT_STATE_MAIL_FROM; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } break; case SMTP_CLIENT_STATE_RCPT_TO: if (len >= (sizeof(RCPTTO)-1) && strncasecmp((const char *)data, RCPTTO, sizeof(RCPTTO)-1) == 0) { data += (sizeof(RCPTTO)-1)-1; fd->nextstate = SMTP_CLIENT_STATE_DATA; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else goto done; break; case SMTP_CLIENT_STATE_DATA: if (len >= (sizeof(DATA)-1) && strncasecmp((const char *)data, DATA, sizeof(DATA)-1) == 0) { data += (sizeof(DATA)-1)-1; fd->nextstate = SMTP_CLIENT_STATE_MESSAGE; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else if (len >= (sizeof(RCPTTO)-1) && strncasecmp((const char *)data, RCPTTO, sizeof(RCPTTO)-1) == 0) { data += (sizeof(RCPTTO)-1)-1; fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } break; case SMTP_CLIENT_STATE_MESSAGE: if (*data == '.') { if (len == 0 || (len >= 1 && data[1] == '\n') || (len >= 2 && data[1] == '\r' && data[2] == '\n')) { smtp_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_SMTP, APP_ID_SMTP, NULL); goto done; } } else if (len >= (sizeof(XMAILER)-1) && strncasecmp((const char *)data, XMAILER, sizeof(XMAILER)-1) == 0) { data += (sizeof(XMAILER)-1)-1; fd->state = SMTP_CLIENT_STATE_GET_PRODUCT_VERSION; } else if (len >= (sizeof(USERAGENT)-1) && strncasecmp((const char *)data, USERAGENT, sizeof(USERAGENT)-1) == 0) { data += (sizeof(USERAGENT)-1)-1; fd->state = SMTP_CLIENT_STATE_GET_PRODUCT_VERSION; } else if (!isprint(*data) && *data != '\t') goto done; else { fd->nextstate = fd->state; fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } break; case SMTP_CLIENT_STATE_GET_PRODUCT_VERSION: if (!fd->headerline) { if (!(fd->headerline = malloc(MAX_HEADER_LINE_SIZE))) goto done; } while((data < end) && (fd->pos < (MAX_HEADER_LINE_SIZE-1))) { if ((*data == ' ') || (*data == '\t')) { line_break = 0; fd->headerline[fd->pos++] = *data; } else if((*data == '\n') && (line_break != 1)) { /* Can't have multiple LFs in a row, but if we get one it * needs to be followed by at least one space */ line_break = 1; } else if(*data == '\r') { // CR needs to be followed by LF and can't start a line line_break = 2; } else if (!isprint(*data)) { free(fd->headerline); fd->headerline = NULL; fd->pos = 0; goto done; } else if(!line_break) { fd->headerline[fd->pos++] = *data; } else { // We have reached the end of the header break; } data++; } data--; if (line_break || fd->pos >= (MAX_HEADER_LINE_SIZE-1)) { ExtractVersion(fd, fd->headerline, fd->headerline + fd->pos, flowp, pkt, dir, pConfig); free(fd->headerline); fd->headerline = NULL; fd->pos = 0; goto done; } break; case SMTP_CLIENT_STATE_SKIP_SPACE: if (*data == ' ') { fd->state = SMTP_CLIENT_STATE_SKIP_LINE; } else if (*data == '\n') { fd->pos = 0; fd->state = fd->nextstate; fd->nextstate = SMTP_CLIENT_STATE_NONE; } else if (*data == '\r') fd->state = SMTP_CLIENT_STATE_SKIP_EOL; else goto done; break; case SMTP_CLIENT_STATE_SKIP_EOL: if (*data == '\n') { fd->pos = 0; fd->state = fd->nextstate; fd->nextstate = SMTP_CLIENT_STATE_NONE; } else goto done; break; case SMTP_CLIENT_STATE_SKIP_LINE: if (*data == '\n') { fd->pos = 0; fd->state = fd->nextstate; fd->nextstate = SMTP_CLIENT_STATE_NONE; } else if (!(*data == '\r' || isprint(*data))) goto done; break; default: goto done; } } return CLIENT_APP_INPROCESS; done: dd->need_continue = 0; if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) clearAppIdFlag(flowp, APPID_SESSION_CONTINUE | APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); else clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } static inline int smtp_validate_reply(const uint8_t *data, uint16_t *offset, uint16_t size, int *multi, int *code) { const ServiceSMTPCode *code_hdr; int tmp; /* Trim any blank lines (be a little tolerant) */ for (; *offsetcode[0] < '1' || code_hdr->code[0] > '5') return -1; tmp = (code_hdr->code[0] - '0') * 100; if (code_hdr->code[1] < '0' || code_hdr->code[1] > '5') return -1; tmp += (code_hdr->code[1] - '0') * 10; if (!isdigit(code_hdr->code[2])) return -1; tmp += code_hdr->code[2] - '0'; if (*multi && tmp != *code) return -1; *code = tmp; if (code_hdr->sp == '-') *multi = 1; else if (code_hdr->sp == ' ') *multi = 0; else return -1; /* We have a valid code, now we need to see if the rest of the line is okay */ *offset += sizeof(ServiceSMTPCode); for (; *offset < size; (*offset)++) { if (data[*offset] == 0x0D) { (*offset)++; if (*offset >= size) return -1; if (data[*offset] != 0x0A) return -1; } if (data[*offset] == 0x0A) { if (*multi) { if ((*offset + 1) >= size) return 0; if (size - (*offset + 1) < (int)sizeof(ServiceSMTPCode)) return -1; code_hdr = (ServiceSMTPCode *)(data + *offset + 1); if (code_hdr->code[0] < '1' || code_hdr->code[0] > '5') return -1; tmp = (code_hdr->code[0] - '0') * 100; if (code_hdr->code[1] < '1' || code_hdr->code[1] > '5') return -1; tmp += (code_hdr->code[1] - '0') * 10; if (!isdigit(code_hdr->code[2])) return -1; tmp += code_hdr->code[2] - '0'; if (tmp != *code) return -1; if (code_hdr->sp == ' ') *multi = 0; else if (code_hdr->sp != '-') return -1; *offset += sizeof(ServiceSMTPCode); } else { (*offset)++; return *code; } } else if (!isprint(data[*offset])) return -1; } return 0; } static int smtp_svc_validate(ServiceValidationArgs* args) { SMTPDetectorData *dd; ServiceSMTPData *fd; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; uint16_t offset; #ifdef APP_ID_USES_REASSEMBLED pop3_detector_mod.streamAPI->response_flush_stream(pkt); #endif if ((dd = smtp_get_SMTPDetectorData(flowp)) == NULL) return SERVICE_ENOMEM; if (!size) goto inprocess; if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { if (!dd->need_continue) clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_SUCCESS; // client made the decision so we are totally done } fd = &dd->server; if (args->dir != APP_ID_FROM_RESPONDER) { if (SMTP_SERVICE_STATE_HELO == fd->state) { if (!((size >= (sizeof(HELO)-1) && strncasecmp((const char *)data, HELO, sizeof(HELO)-1) == 0) || (size >= (sizeof(EHLO)-1) && strncasecmp((const char *)data, EHLO, sizeof(EHLO)-1) == 0))) { fd->state = SMTP_SERVICE_STATE_BAD_CLIENT; } } goto inprocess; } offset = 0; while (offset < size) { if (smtp_validate_reply(data, &offset, size, &fd->multiline, &fd->code) < 0) { if (!(dd->client.flags & CLIENT_FLAG_STARTTLS_SUCCESS)) goto fail; goto inprocess; } if (!fd->code) goto inprocess; switch (fd->state) { case SMTP_SERVICE_STATE_CONNECTION: switch (fd->code) { case 220: fd->state = SMTP_SERVICE_STATE_HELO; break; case 421: if (service_strstr(data, size, (const uint8_t *)SMTP_CLOSING_CONN, sizeof(SMTP_CLOSING_CONN)-1)) goto success; goto fail; case 554: goto success; default: goto fail; } break; case SMTP_SERVICE_STATE_HELO: switch (fd->code) { case 250: fd->state = SMTP_SERVICE_STATE_TRANSFER; break; case 220: case 500: case 501: case 502: case 504: break; case 421: case 553: fd->state = SMTP_SERVICE_STATE_CONNECTION_ERROR; break; default: goto fail; } break; case SMTP_SERVICE_STATE_STARTTLS: // success or fail, return client to connection-complete state. dd->client.state = SMTP_CLIENT_STATE_HELO; fd->state = SMTP_SERVICE_STATE_HELO; if (fd->code == 220) { dd->client.flags |= CLIENT_FLAG_STARTTLS_SUCCESS; if (_dpd.isSSLPolicyEnabled(NULL)) dd->client.decryption_countdown = SSL_WAIT_PACKETS; // max wait if decryption fails (e.g., cert error) else dd->client.decryption_countdown = 1; // no wait for decryption smtp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_SMTPS, NULL, NULL, NULL, NULL); if (dd->need_continue > 0) setAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_STICKY_SERVICE | APPID_SESSION_CONTINUE); else setAppIdFlag(flowp, APPID_SESSION_ENCRYPTED | APPID_SESSION_STICKY_SERVICE); return SERVICE_SUCCESS; } /* STARTTLS failed. Fall through and call this SMTP */ case SMTP_SERVICE_STATE_TRANSFER: goto success; case SMTP_SERVICE_STATE_BAD_CLIENT: switch (fd->code) { case 500: case 501: case 502: case 550: goto not_compatible; } case SMTP_SERVICE_STATE_CONNECTION_ERROR: default: goto fail; } } inprocess: smtp_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; success: if (dd->need_continue > 0) setAppIdFlag(flowp, APPID_SESSION_CONTINUE); smtp_service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_SMTP, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; fail: smtp_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, smtp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; not_compatible: smtp_service_mod.api->incompatible_data(flowp, args->pkt, args->dir, &svc_element, smtp_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_http.c0000644000175000017500000026107114241075412025705 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "str_search.h" #include "appInfoTable.h" #include "common_util.h" #include "detector_api.h" #include "httpCommon.h" #include "http_url_patterns.h" #include "detector_http.h" #include "fw_appid.h" #include "client_app_base.h" /* URL line patterns for identifying client */ #define HTTP_GET "GET " #define HTTP_PUT "PUT " #define HTTP_POST "POST " #define HTTP_HEAD "HEAD " #define HTTP_TRACE "TRACE " #define HTTP_DELETE "DELETE " #define HTTP_OPTIONS "OPTIONS " #define HTTP_PROPFIND "PROPFIND " #define HTTP_PROPPATCH "PROPPATCH " #define HTTP_MKCOL "MKCOL " #define HTTP_COPY "COPY " #define HTTP_MOVE "MOVE " #define HTTP_LOCK "LOCK " #define HTTP_UNLOCK "UNLOCK " #define HTTP_GET_SIZE (sizeof(HTTP_GET)-1) #define HTTP_PUT_SIZE (sizeof(HTTP_PUT)-1) #define HTTP_POST_SIZE (sizeof(HTTP_POST)-1) #define HTTP_HEAD_SIZE (sizeof(HTTP_HEAD)-1) #define HTTP_TRACE_SIZE (sizeof(HTTP_GET)-1) #define HTTP_DELETE_SIZE (sizeof(HTTP_DELETE)-1) #define HTTP_OPTIONS_SIZE (sizeof(HTTP_OPTIONS)-1) #define HTTP_PROPFIND_SIZE (sizeof(HTTP_PROPFIND)-1) #define HTTP_PROPPATCH_SIZE (sizeof(HTTP_PROPPATCH)-1) #define HTTP_MKCOL_SIZE (sizeof(HTTP_GET)-1) #define HTTP_COPY_SIZE (sizeof(HTTP_COPY)-1) #define HTTP_MOVE_SIZE (sizeof(HTTP_MOVE)-1) #define HTTP_LOCK_SIZE (sizeof(HTTP_LOCK)-1) #define HTTP_UNLOCK_SIZE (sizeof(HTTP_UNLOCK)-1) /* media type patterns*/ #define VIDEO_BANNER "video/" #define AUDIO_BANNER "audio/" #define APPLICATION_BANNER "application/" #define QUICKTIME_BANNER "quicktime" #define MPEG_BANNER "mpeg" #define MPA_BANNER "mpa" #define ROBUST_MPA_BANNER "robust-mpa" #define MP4A_BANNER "mp4a-latm" #define SHOCKWAVE_BANNER "x-shockwave-flash" #define RSS_BANNER "rss+xml" #define ATOM_BANNER "atom+xml" #define MP4_BANNER "mp4" #define WMV_BANNER "x-ms-wmv" #define WMA_BANNER "x-ms-wma" #define WAV_BANNER "wav" #define X_WAV_BANNER "x-wav" #define VND_WAV_BANNER "vnd.wav" #define FLV_BANNER "x-flv" #define M4V_BANNER "x-m4v" #define GPP_BANNER "3gpp" #define XSCPLS_BANNER "x-scpls" #define VIDEO_BANNER_MAX_POS (sizeof(VIDEO_BANNER)-2) #define AUDIO_BANNER_MAX_POS (sizeof(AUDIO_BANNER)-2) #define APPLICATION_BANNER_MAX_POS (sizeof(APPLICATION_BANNER)-2) #define QUICKTIME_BANNER_MAX_POS (sizeof(QUICKTIME_BANNER)-2) #define MPEG_BANNER_MAX_POS (sizeof(MPEG_BANNER)-2) #define MPA_BANNER_MAX_POS (sizeof(MPA_BANNER)-2) #define ROBUST_MPA_BANNER_MAX_POS (sizeof(ROBUST_MPA_BANNER)-2) #define MP4A_BANNER_MAX_POS (sizeof(MP4A_BANNER)-2) #define SHOCKWAVE_BANNER_MAX_POS (sizeof(SHOCKWAVE_BANNER)-2) #define RSS_BANNER_MAX_POS (sizeof(RSS_BANNER)-2) #define ATOM_BANNER_MAX_POS (sizeof(ATOM_BANNER)-2) #define MP4_BANNER_MAX_POS (sizeof(MP4_BANNER)-2) #define WMV_BANNER_MAX_POS (sizeof(WMV_BANNER)-2) #define WMA_BANNER_MAX_POS (sizeof(WMA_BANNER)-2) #define WAV_BANNER_MAX_POS (sizeof(WAV_BANNER)-2) #define X_WAV_BANNER_MAX_POS (sizeof(X_WAV_BANNER)-2) #define VND_WAV_BANNER_MAX_POS (sizeof(VND_WAV_BANNER)-2) #define FLV_BANNER_MAX_POS (sizeof(FLV_BANNER)-2) #define M4V_BANNER_MAX_POS (sizeof(M4V_BANNER)-2) #define GPP_BANNER_MAX_POS (sizeof(GPP_BANNER)-2) #define XSCPLS_BANNER_MAX_POS (sizeof(XSCPLS_BANNER)-2) /* version patterns*/ static const char MSIE_PATTERN[] = "MSIE"; static const char KONQUEROR_PATTERN[] = "Konqueror"; static const char SKYPE_PATTERN[] = "Skype"; static const char BITTORRENT_PATTERN[] = "BitTorrent"; static const char FIREFOX_PATTERN[] = "Firefox"; static const char WGET_PATTERN[] = "Wget/"; static const char CURL_PATTERN[] = "curl"; static const char GOOGLE_DESKTOP_PATTERN[] = "Google Desktop"; static const char PICASA_PATTERN[] = "Picasa"; static const char SAFARI_PATTERN[] = "Safari"; static const char CHROME_PATTERN[] = "Chrome"; static const char MOBILE_PATTERN[] = "Mobile"; static const char BLACKBERRY_PATTERN[] = "BlackBerry"; static const char ANDROID_PATTERN[] = "Android"; static const char MEDIAPLAYER_PATTERN[] = "Windows-Media-Player"; static const char APPLE_EMAIL_PATTERN[] = "Maci"; static const char *APPLE_EMAIL_PATTERNS[] = { "Mozilla/5.0","AppleWebKit","(KHTML, like Gecko)"}; /* "fake" patterns for user-agent matching */ static const char VERSION_PATTERN[] = "Version"; #define VERSION_PATTERN_SIZE (sizeof(VERSION_PATTERN)-1) #define FAKE_VERSION_APP_ID 3 /* proxy patterns*/ static const char SQUID_PATTERN[] = "squid"; #define SQUID_PATTERN_SIZE (sizeof(SQUID_PATTERN)-1) static const char MYSPACE_PATTERN[] = "myspace.com"; static const char GMAIL_PATTERN[] = "gmail.com"; static const char GMAIL_PATTERN2[] = "mail.google.com"; static const char AOL_PATTERN[] = "webmail.aol.com"; static const char MSUP_PATTERN[] = "update.microsoft.com"; static const char MSUP_PATTERN2[] = "windowsupdate.com"; static const char YAHOO_MAIL_PATTERN[] = "mail.yahoo.com"; static const char YAHOO_TB_PATTERN[] = "rd.companion.yahoo.com"; static const char ADOBE_UP_PATTERN[] = "swupmf.adobe.com"; static const char HOTMAIL_PATTERN1[] = "hotmail.com"; static const char HOTMAIL_PATTERN2[] = "mail.live.com"; static const char GOOGLE_TB_PATTERN[] = "toolbarqueries.google.com"; #define MYSPACE_PATTERN_SIZE (sizeof(MYSPACE_PATTERN)-1) #define GMAIL_PATTERN_SIZE (sizeof(GMAIL_PATTERN)-1) #define GMAIL_PATTERN2_SIZE (sizeof(GMAIL_PATTERN2)-1) #define AOL_PATTERN_SIZE (sizeof(AOL_PATTERN)-1) #define MSUP_PATTERN_SIZE (sizeof(MSUP_PATTERN)-1) #define MSUP_PATTERN2_SIZE (sizeof(MSUP_PATTERN2)-1) #define YAHOO_MAIL_PATTERN_SIZE (sizeof(YAHOO_MAIL_PATTERN)-1) #define YAHOO_TB_PATTERN_SIZE (sizeof(YAHOO_TB_PATTERN)-1) #define ADOBE_UP_PATTERN_SIZE (sizeof(ADOBE_UP_PATTERN)-1) #define HOTMAIL_PATTERN1_SIZE (sizeof(HOTMAIL_PATTERN1)-1) #define HOTMAIL_PATTERN2_SIZE (sizeof(HOTMAIL_PATTERN2)-1) #define GOOGLE_TB_PATTERN_SIZE (sizeof(GOOGLE_TB_PATTERN)-1) #define COMPATIBLE_BROWSER_STRING " (Compat)" typedef struct _MatchedPatterns { DetectorHTTPPattern *mpattern; int index; struct _MatchedPatterns *next; } MatchedPatterns; static DetectorHTTPPattern content_type_patterns[] = { { SINGLE, 0, APP_ID_QUICKTIME, 0, sizeof(QUICKTIME_BANNER)-1, (u_int8_t *) QUICKTIME_BANNER, APP_ID_QUICKTIME }, { SINGLE, 0, APP_ID_MPEG, 0, sizeof(MPEG_BANNER)-1, (u_int8_t *) MPEG_BANNER, APP_ID_MPEG }, { SINGLE, 0, APP_ID_MPEG, 0, sizeof(MPA_BANNER)-1, (u_int8_t *) MPA_BANNER, APP_ID_MPEG }, { SINGLE, 0, APP_ID_MPEG, 0, sizeof(MP4A_BANNER)-1, (u_int8_t *) MP4A_BANNER, APP_ID_MPEG }, { SINGLE, 0, APP_ID_MPEG, 0, sizeof(ROBUST_MPA_BANNER)-1, (u_int8_t *) ROBUST_MPA_BANNER, APP_ID_MPEG }, { SINGLE, 0, APP_ID_MPEG, 0, sizeof(XSCPLS_BANNER)-1, (u_int8_t *) XSCPLS_BANNER, APP_ID_MPEG }, { SINGLE, 0, APP_ID_SHOCKWAVE, 0, sizeof(SHOCKWAVE_BANNER)-1, (u_int8_t *) SHOCKWAVE_BANNER, APP_ID_SHOCKWAVE }, { SINGLE, 0, APP_ID_RSS, 0, sizeof(RSS_BANNER)-1, (u_int8_t *) RSS_BANNER, APP_ID_RSS }, { SINGLE, 0, APP_ID_ATOM, 0, sizeof(ATOM_BANNER)-1, (u_int8_t *) ATOM_BANNER, APP_ID_ATOM }, { SINGLE, 0, APP_ID_MP4, 0, sizeof(MP4_BANNER)-1, (u_int8_t *) MP4_BANNER, APP_ID_MP4 }, { SINGLE, 0, APP_ID_WMV, 0, sizeof(WMV_BANNER)-1, (u_int8_t *) WMV_BANNER, APP_ID_WMV }, { SINGLE, 0, APP_ID_WMA, 0, sizeof(WMA_BANNER)-1, (u_int8_t *) WMA_BANNER, APP_ID_WMA }, { SINGLE, 0, APP_ID_WAV, 0, sizeof(WAV_BANNER)-1, (u_int8_t *) WAV_BANNER, APP_ID_WAV }, { SINGLE, 0, APP_ID_WAV, 0, sizeof(X_WAV_BANNER)-1, (u_int8_t *) X_WAV_BANNER, APP_ID_WAV }, { SINGLE, 0, APP_ID_WAV, 0, sizeof(VND_WAV_BANNER)-1, (u_int8_t *) VND_WAV_BANNER, APP_ID_WAV }, { SINGLE, 0, APP_ID_FLASH_VIDEO, 0, sizeof(FLV_BANNER)-1, (u_int8_t *) FLV_BANNER, APP_ID_FLASH_VIDEO }, { SINGLE, 0, APP_ID_FLASH_VIDEO, 0, sizeof(M4V_BANNER)-1, (u_int8_t *) M4V_BANNER, APP_ID_FLASH_VIDEO }, { SINGLE, 0, APP_ID_FLASH_VIDEO, 0, sizeof(GPP_BANNER)-1, (u_int8_t *) GPP_BANNER, APP_ID_FLASH_VIDEO }, { SINGLE, 0, APP_ID_GENERIC, 0, sizeof(VIDEO_BANNER)-1, (u_int8_t *) VIDEO_BANNER, APP_ID_GENERIC }, { SINGLE, 0, APP_ID_GENERIC, 0, sizeof(AUDIO_BANNER)-1, (u_int8_t *) AUDIO_BANNER, APP_ID_GENERIC }, }; static DetectorHTTPPattern via_http_detector_patterns[] = { { SINGLE, APP_ID_SQUID, 0, 0, SQUID_PATTERN_SIZE, (u_int8_t *) SQUID_PATTERN, APP_ID_SQUID }, }; static DetectorHTTPPattern host_payload_http_detector_patterns[] = { { SINGLE, 0, 0, APP_ID_MYSPACE, MYSPACE_PATTERN_SIZE, (u_int8_t *) MYSPACE_PATTERN, APP_ID_MYSPACE }, { SINGLE, 0, 0, APP_ID_GMAIL, GMAIL_PATTERN_SIZE, (u_int8_t *) GMAIL_PATTERN, APP_ID_GMAIL, }, { SINGLE, 0, 0, APP_ID_GMAIL, GMAIL_PATTERN2_SIZE, (u_int8_t *) GMAIL_PATTERN2, APP_ID_GMAIL, }, { SINGLE, 0, 0, APP_ID_AOL_EMAIL, AOL_PATTERN_SIZE, (u_int8_t *) AOL_PATTERN, APP_ID_AOL_EMAIL, }, { SINGLE, 0, 0, APP_ID_MICROSOFT_UPDATE, MSUP_PATTERN_SIZE, (u_int8_t *) MSUP_PATTERN, APP_ID_MICROSOFT_UPDATE, }, { SINGLE, 0, 0, APP_ID_MICROSOFT_UPDATE, MSUP_PATTERN2_SIZE, (u_int8_t *) MSUP_PATTERN2, APP_ID_MICROSOFT_UPDATE, }, { SINGLE, 0, 0, APP_ID_YAHOOMAIL, YAHOO_MAIL_PATTERN_SIZE, (u_int8_t *)YAHOO_MAIL_PATTERN, APP_ID_YAHOOMAIL, }, { SINGLE, 0, 0, APP_ID_YAHOO_TOOLBAR, YAHOO_TB_PATTERN_SIZE, (u_int8_t *)YAHOO_TB_PATTERN, APP_ID_YAHOO_TOOLBAR, }, { SINGLE, 0, 0, APP_ID_ADOBE_UPDATE, ADOBE_UP_PATTERN_SIZE, (u_int8_t *)ADOBE_UP_PATTERN, APP_ID_ADOBE_UPDATE, }, { SINGLE, 0, 0, APP_ID_HOTMAIL, HOTMAIL_PATTERN1_SIZE, (u_int8_t *)HOTMAIL_PATTERN1, APP_ID_HOTMAIL, }, { SINGLE, 0, 0, APP_ID_HOTMAIL, HOTMAIL_PATTERN2_SIZE, (u_int8_t *)HOTMAIL_PATTERN2, APP_ID_HOTMAIL, }, { SINGLE, 0, 0, APP_ID_GOOGLE_TOOLBAR, GOOGLE_TB_PATTERN_SIZE, (u_int8_t *)GOOGLE_TB_PATTERN, APP_ID_GOOGLE_TOOLBAR, }, }; static DetectorHTTPPattern client_agent_patterns[] = { { USER_AGENT_HEADER, 0, FAKE_VERSION_APP_ID, 0, VERSION_PATTERN_SIZE, (u_int8_t *)VERSION_PATTERN, FAKE_VERSION_APP_ID, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_INTERNET_EXPLORER, 0, sizeof(MSIE_PATTERN)-1, (u_int8_t *)MSIE_PATTERN, APP_ID_INTERNET_EXPLORER, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_KONQUEROR, 0, sizeof(KONQUEROR_PATTERN)-1, (u_int8_t *)KONQUEROR_PATTERN, APP_ID_KONQUEROR, }, { USER_AGENT_HEADER, APP_ID_SKYPE_AUTH, APP_ID_SKYPE, 0, sizeof(SKYPE_PATTERN)-1, (u_int8_t *)SKYPE_PATTERN, APP_ID_SKYPE, }, { USER_AGENT_HEADER, APP_ID_BITTORRENT, APP_ID_BITTORRENT, 0, sizeof(BITTORRENT_PATTERN)-1, (u_int8_t *)BITTORRENT_PATTERN, APP_ID_BITTORRENT, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_FIREFOX, 0, sizeof(FIREFOX_PATTERN)-1, (u_int8_t *)FIREFOX_PATTERN, APP_ID_FIREFOX, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_WGET, 0, sizeof(WGET_PATTERN)-1, (u_int8_t *)WGET_PATTERN, APP_ID_WGET, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_CURL, 0, sizeof(CURL_PATTERN)-1, (u_int8_t *)CURL_PATTERN, APP_ID_CURL, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_GOOGLE_DESKTOP, 0, sizeof(GOOGLE_DESKTOP_PATTERN)-1, (u_int8_t *)GOOGLE_DESKTOP_PATTERN, APP_ID_GOOGLE_DESKTOP, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_PICASA, 0, sizeof(PICASA_PATTERN)-1, (u_int8_t *)PICASA_PATTERN, APP_ID_PICASA, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_SAFARI, 0, sizeof(SAFARI_PATTERN)-1, (u_int8_t *)SAFARI_PATTERN, APP_ID_SAFARI, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_CHROME, 0, sizeof(CHROME_PATTERN)-1, (u_int8_t *)CHROME_PATTERN, APP_ID_CHROME, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_SAFARI_MOBILE_DUMMY, 0, sizeof(MOBILE_PATTERN)-1, (u_int8_t *)MOBILE_PATTERN, APP_ID_SAFARI_MOBILE_DUMMY, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_BLACKBERRY_BROWSER, 0, sizeof(BLACKBERRY_PATTERN)-1, (u_int8_t *)BLACKBERRY_PATTERN, APP_ID_BLACKBERRY_BROWSER, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_ANDROID_BROWSER, 0, sizeof(ANDROID_PATTERN)-1, (u_int8_t *)ANDROID_PATTERN, APP_ID_ANDROID_BROWSER, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_WINDOWS_MEDIA_PLAYER, 0, sizeof(MEDIAPLAYER_PATTERN)-1, (u_int8_t *)MEDIAPLAYER_PATTERN, APP_ID_WINDOWS_MEDIA_PLAYER, }, { USER_AGENT_HEADER, APP_ID_HTTP, APP_ID_APPLE_EMAIL, 0, sizeof(APPLE_EMAIL_PATTERN)-1, (u_int8_t *)APPLE_EMAIL_PATTERN, APP_ID_APPLE_EMAIL, }, }; struct _PATTERN_HTTP_HEADER; typedef struct _HEADER_PATTERN { int id; uint8_t *data; unsigned length; } HeaderPattern; static const char HTTP_HEADER_CONTENT_TYPE[] = "Content-Type: "; static const char HTTP_HEADER_SERVER_FIELD[] = "Server: "; static const char HTTP_HEADER_X_WORKING_WITH[] = "X-Working-With: "; static const char HTTP_HEADER_CRLF[] = "\r"; static const char HTTP_HEADER_LF[] = "\n"; #define HTTP_HEADER_CONTENT_TYPE_SIZE (sizeof(HTTP_HEADER_CONTENT_TYPE)-1) #define HTTP_HEADER_SERVER_FIELD_SIZE (sizeof(HTTP_HEADER_SERVER_FIELD)-1) #define HTTP_HEADER_X_WORKING_WITH_SIZE (sizeof(HTTP_HEADER_X_WORKING_WITH)-1) #define HTTP_HEADER_CRLF_SIZE (sizeof(HTTP_HEADER_CRLF)-1) #define HTTP_HEADER_LF_SIZE (sizeof(HTTP_HEADER_LF)-1) static HeaderPattern header_patterns[] = { {HTTP_ID_CONTENT_TYPE, (uint8_t *) HTTP_HEADER_CONTENT_TYPE,HTTP_HEADER_CONTENT_TYPE_SIZE}, {HTTP_ID_SERVER, (uint8_t *) HTTP_HEADER_SERVER_FIELD,HTTP_HEADER_SERVER_FIELD_SIZE}, {HTTP_ID_COPY, (uint8_t *) HTTP_COPY, HTTP_COPY_SIZE}, {HTTP_ID_DELETE, (uint8_t *) HTTP_DELETE, HTTP_DELETE_SIZE}, {HTTP_ID_GET, (uint8_t *) HTTP_GET, HTTP_GET_SIZE}, {HTTP_ID_HEAD, (uint8_t *) HTTP_HEAD, HTTP_HEAD_SIZE}, {HTTP_ID_OPTIONS, (uint8_t *) HTTP_OPTIONS, HTTP_OPTIONS_SIZE}, {HTTP_ID_PROPFIND, (uint8_t *) HTTP_PROPFIND, HTTP_PROPFIND_SIZE}, {HTTP_ID_PROPPATCH, (uint8_t *) HTTP_PROPPATCH, HTTP_PROPPATCH_SIZE}, {HTTP_ID_MKCOL, (uint8_t *) HTTP_MKCOL, HTTP_MKCOL_SIZE}, {HTTP_ID_LOCK, (uint8_t *) HTTP_LOCK, HTTP_LOCK_SIZE}, {HTTP_ID_MOVE, (uint8_t *) HTTP_MOVE, HTTP_MOVE_SIZE}, {HTTP_ID_PUT, (uint8_t *) HTTP_PUT, HTTP_PUT_SIZE}, {HTTP_ID_TRACE, (uint8_t *) HTTP_TRACE, HTTP_TRACE_SIZE}, {HTTP_ID_UNLOCK, (uint8_t *) HTTP_UNLOCK, HTTP_UNLOCK_SIZE}, {HTTP_ID_X_WORKING_WITH, (uint8_t *) HTTP_HEADER_X_WORKING_WITH,HTTP_HEADER_X_WORKING_WITH_SIZE}, {HTTP_ID_LEN, (uint8_t *) HTTP_HEADER_CRLF, HTTP_HEADER_CRLF_SIZE}, {HTTP_ID_LEN, (uint8_t *) HTTP_HEADER_LF, HTTP_HEADER_LF_SIZE} }; static int content_pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { MatchedPatterns *cm; MatchedPatterns **matches = (MatchedPatterns **)data; DetectorHTTPPattern *target = (DetectorHTTPPattern *)id; if (!(cm = (MatchedPatterns *)malloc(sizeof(MatchedPatterns)))) return 1; cm->mpattern = target; cm->index = index; cm->next = *matches; *matches = cm; return 0; } static int chp_pattern_match(void *id, void *unused_tree, int index, void *data, void *unused_neg) { MatchedCHPAction *new_match; MatchedCHPAction *current_search; MatchedCHPAction *prev_search; MatchedCHPAction **matches = (MatchedCHPAction **)data; CHPAction *target = (CHPAction *)id; if (!(new_match = (MatchedCHPAction *)malloc(sizeof(MatchedCHPAction)))) return 1; new_match->mpattern = target; new_match->index = index; // preserving order is required: sort by appIdInstance, then by precedence for (current_search = *matches, prev_search = NULL; NULL != current_search; prev_search = current_search, current_search = current_search->next) { register CHPAction *match_data = current_search->mpattern; if (target->appIdInstance < match_data->appIdInstance) break; if (target->appIdInstance == match_data->appIdInstance) { if (target->precedence < match_data->precedence) break; } } if (prev_search) { new_match->next = prev_search->next; prev_search->next = new_match; } else { // insert at head of list. new_match->next = *matches; *matches = new_match; } return 0; } #define CHP_TALLY_GROWTH_FACTOR 10 static inline void chp_add_candidate_to_tally( CHPMatchTally **ppTally, CHPApp *chpapp ) { int index; CHPMatchTally *pTally = *ppTally; if (!pTally) { pTally = (CHPMatchTally *)malloc(sizeof(CHPMatchTally)+CHP_TALLY_GROWTH_FACTOR*sizeof(CHPMatchCandidate)); if (!pTally) return; pTally->in_use_elements = 0; pTally->allocated_elements = CHP_TALLY_GROWTH_FACTOR; *ppTally = pTally; } for (index=0; index < pTally->in_use_elements; index++ ) { if (chpapp == pTally->item[index].chpapp) { pTally->item[index].key_pattern_countdown--; return; } } // Not found. Add to array if (pTally->in_use_elements == pTally->allocated_elements) { int newCount = pTally->allocated_elements + CHP_TALLY_GROWTH_FACTOR; CHPMatchTally *pNewTally = (CHPMatchTally *)realloc( pTally, sizeof(CHPMatchTally)+newCount*sizeof(CHPMatchCandidate)); if (pNewTally) { *ppTally = pTally = pNewTally; pTally->allocated_elements = newCount; } else return; // failed to allocate a bigger chunk } // index == pTally->in_use_elements pTally->in_use_elements++; pTally->item[index].chpapp = chpapp; pTally->item[index].key_pattern_length_sum = chpapp->key_pattern_length_sum; pTally->item[index].key_pattern_countdown = chpapp->key_pattern_count - 1; // the count would have included this find. } typedef struct _CHPTallyAndActions { CHPMatchTally *pTally; MatchedCHPAction *matches; } CHPTallyAndActions; // In addition to creating the linked list of matching actions this function will // create the CHPMatchTally needed to find the longest matching pattern. static int chp_key_pattern_match(void *id, void *unused_tree, int index, void *data, void *unused_neg) { CHPTallyAndActions *pTallyAndActions = (CHPTallyAndActions*)data; CHPAction *target = (CHPAction *)id; if (target->key_pattern) { // We have a match from a key pattern. We need to have it's parent chpapp represented in the tally. // If the chpapp has never been seen then add an item to the tally's array // else decrement the count of expected key_patterns until zero so that we know when we have them all. chp_add_candidate_to_tally( &pTallyAndActions->pTally, target->chpapp ); } return chp_pattern_match(id, unused_tree, index, &pTallyAndActions->matches, unused_neg); } static int http_pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { MatchedPatterns *cm = NULL; MatchedPatterns **tmp; MatchedPatterns **matches = (MatchedPatterns **)data; DetectorHTTPPattern *target = (DetectorHTTPPattern *)id; /* make sure we haven't already seen this pattern */ for (tmp = matches; *tmp; tmp = &(*tmp)->next) { cm = *tmp; } if (!*tmp) { if (!(cm = (MatchedPatterns *)malloc(sizeof(MatchedPatterns)))) return 1; else { cm->mpattern = target; cm->index = index; cm->next = NULL; *tmp = cm; } } /* if its one of the host patterns, return after first match*/ if (cm->mpattern->seq == SINGLE) return 1; else return 0; } static void* processPatterns( DetectorHTTPPattern *patternList, size_t patternListCount, size_t *patternIndex, HTTPListElement* luaPatternList ) { void *patternMatcher; HTTPListElement* element; u_int32_t i; if (!(patternMatcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return NULL; for (i=0; i < patternListCount; i++) { _dpd.searchAPI->search_instance_add_ex(patternMatcher, (char *)patternList[i].pattern, patternList[i].pattern_size, &patternList[i], STR_SEARCH_CASE_SENSITIVE); } /* Add patterns from Lua API */ for(element = luaPatternList; element != 0; element = element->next) { _dpd.searchAPI->search_instance_add_ex(patternMatcher, (char *)element->detectorHTTPPattern.pattern, element->detectorHTTPPattern.pattern_size, &element->detectorHTTPPattern, STR_SEARCH_CASE_SENSITIVE); } _dpd.searchAPI->search_instance_prep(patternMatcher); return patternMatcher; } static int processHostPatterns( DetectorHTTPPattern *patternList, size_t patternListCount, HTTPListElement* luaPatternList, DetectorAppUrlList *urlPatternList, DetectorAppUrlList *RTMPUrlList, tDetectorHttpConfig *pHttpConfig ) { HTTPListElement* element; u_int32_t i; DetectorAppUrlPattern *appUrlPattern; if (!pHttpConfig->hostUrlMatcher) pHttpConfig->hostUrlMatcher = mlmpCreate(); if (!pHttpConfig->RTMPHostUrlMatcher) pHttpConfig->RTMPHostUrlMatcher = mlmpCreate(); for (i=0; i < patternListCount; i++) { if (addMlmpPattern(pHttpConfig->hostUrlMatcher, &pHttpConfig->hostUrlPatternsList, patternList[i].pattern, patternList[i].pattern_size, NULL, 0, NULL, 0, patternList[i].appId, patternList[i].payload, patternList[i].service_id, patternList[i].client_app, patternList[i].seq) < 0) return -1; } for(element = luaPatternList; element != 0; element = element->next) { if (addMlmpPattern(pHttpConfig->hostUrlMatcher, &pHttpConfig->hostUrlPatternsList, element->detectorHTTPPattern.pattern, element->detectorHTTPPattern.pattern_size, NULL, 0, NULL, 0, element->detectorHTTPPattern.appId, element->detectorHTTPPattern.payload, element->detectorHTTPPattern.service_id, element->detectorHTTPPattern.client_app, element->detectorHTTPPattern.seq) < 0) return -1; } for (i = 0; i < RTMPUrlList->usedCount; i++) { appUrlPattern = RTMPUrlList->urlPattern[i]; if (addMlmpPattern(pHttpConfig->RTMPHostUrlMatcher, &pHttpConfig->hostUrlPatternsList, appUrlPattern->patterns.host.pattern, appUrlPattern->patterns.host.patternSize, appUrlPattern->patterns.path.pattern, appUrlPattern->patterns.path.patternSize, appUrlPattern->userData.query.pattern, appUrlPattern->userData.query.patternSize, appUrlPattern->userData.appId, appUrlPattern->userData.payload, appUrlPattern->userData.service_id, appUrlPattern->userData.client_app, SINGLE) < 0) return -1; } for (i = 0; i < urlPatternList->usedCount; i++) { appUrlPattern = urlPatternList->urlPattern[i]; if (addMlmpPattern(pHttpConfig->hostUrlMatcher, &pHttpConfig->hostUrlPatternsList, appUrlPattern->patterns.host.pattern, appUrlPattern->patterns.host.patternSize, appUrlPattern->patterns.path.pattern, appUrlPattern->patterns.path.patternSize, appUrlPattern->userData.query.pattern, appUrlPattern->userData.query.patternSize, appUrlPattern->userData.appId, appUrlPattern->userData.payload, appUrlPattern->userData.service_id, appUrlPattern->userData.client_app, SINGLE) < 0) return -1; } mlmpProcessPatterns(pHttpConfig->hostUrlMatcher); mlmpProcessPatterns(pHttpConfig->RTMPHostUrlMatcher); return 0; } static void* processContentTypePatterns( DetectorHTTPPattern *patternList, size_t patternListCount, HTTPListElement* luaPatternList, size_t *patternIndex) { void *patternMatcher; HTTPListElement* element; u_int32_t i; if (!(patternMatcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return NULL; for (i=0; i < patternListCount; i++) { _dpd.searchAPI->search_instance_add_ex(patternMatcher, (char *)patternList[i].pattern, patternList[i].pattern_size, &patternList[i], STR_SEARCH_CASE_SENSITIVE); } /* Add patterns from Lua API */ for(element = luaPatternList; element; element = element->next) { _dpd.searchAPI->search_instance_add_ex(patternMatcher, (char *)element->detectorHTTPPattern.pattern, element->detectorHTTPPattern.pattern_size, &element->detectorHTTPPattern, STR_SEARCH_CASE_SENSITIVE); } _dpd.searchAPI->search_instance_prep(patternMatcher); return patternMatcher; } static int processCHPList(CHPListElement *chplist, tDetectorHttpConfig *pHttpConfig) { CHPListElement *chpe; int i; for (i = 0; i < sizeof(pHttpConfig->chp_matchers)/sizeof(pHttpConfig->chp_matchers[0]); i++) { if (!(pHttpConfig->chp_matchers[i] = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return 0; } for(chpe = chplist; chpe; chpe=chpe->next) { _dpd.searchAPI->search_instance_add_ex(pHttpConfig->chp_matchers[chpe->chp_action.ptype], (char *)chpe->chp_action.pattern, chpe->chp_action.psize, &chpe->chp_action, STR_SEARCH_CASE_INSENSITIVE); } for (i = 0; i < sizeof(pHttpConfig->chp_matchers)/sizeof(pHttpConfig->chp_matchers[0]); i++) { _dpd.searchAPI->search_instance_prep(pHttpConfig->chp_matchers[i]); } return 1; } static void* registerHeaderPatterns( HeaderPattern *patternList, size_t patternListCount) { void *patternMatcher; u_int32_t i; if (!(patternMatcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return NULL; for (i=0; i < patternListCount; i++) { /* add patterns with case insensitivity */ _dpd.searchAPI->search_instance_add_ex(patternMatcher, (char *)patternList[i].data, patternList[i].length, &patternList[i], STR_SEARCH_CASE_INSENSITIVE); } _dpd.searchAPI->search_instance_prep(patternMatcher); return patternMatcher; } #define HTTP_FIELD_PREFIX_USER_AGENT "\r\nUser-Agent: " #define HTTP_FIELD_PREFIX_USER_AGENT_SIZE (sizeof(HTTP_FIELD_PREFIX_USER_AGENT)-1) #define HTTP_FIELD_PREFIX_HOST "\r\nHost: " #define HTTP_FIELD_PREFIX_HOST_SIZE (sizeof(HTTP_FIELD_PREFIX_HOST)-1) #define HTTP_FIELD_PREFIX_REFERER "\r\nReferer: " #define HTTP_FIELD_PREFIX_REFERER_SIZE (sizeof(HTTP_FIELD_PREFIX_REFERER)-1) #define HTTP_FIELD_PREFIX_URI " " #define HTTP_FIELD_PREFIX_URI_SIZE (sizeof(HTTP_FIELD_PREFIX_URI)-1) #define HTTP_FIELD_PREFIX_COOKIE "\r\nCookie: " #define HTTP_FIELD_PREFIX_COOKIE_SIZE (sizeof(HTTP_FIELD_PREFIX_COOKIE)-1) typedef struct _FIELD_PATTERN { PatternType patternType; uint8_t *data; unsigned length; } FieldPattern; static FieldPattern http_field_patterns[] = { {URI_PT, (uint8_t *) HTTP_FIELD_PREFIX_URI, HTTP_FIELD_PREFIX_URI_SIZE}, {HOST_PT, (uint8_t *) HTTP_FIELD_PREFIX_HOST,HTTP_FIELD_PREFIX_HOST_SIZE}, {REFERER_PT, (uint8_t *) HTTP_FIELD_PREFIX_REFERER, HTTP_FIELD_PREFIX_REFERER_SIZE}, {COOKIE_PT, (uint8_t *) HTTP_FIELD_PREFIX_COOKIE, HTTP_FIELD_PREFIX_COOKIE_SIZE}, {AGENT_PT, (uint8_t *) HTTP_FIELD_PREFIX_USER_AGENT,HTTP_FIELD_PREFIX_USER_AGENT_SIZE}, }; static void* processHttpFieldPatterns( FieldPattern* patternList, size_t patternListCount) { void *patternMatcher; u_int32_t i; if (!(patternMatcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return NULL; for (i=0; i < patternListCount; i++) { /* add patterns with case sensitivity */ _dpd.searchAPI->search_instance_add_ex(patternMatcher, (char *)patternList[i].data, patternList[i].length, &patternList[i], STR_SEARCH_CASE_SENSITIVE); } _dpd.searchAPI->search_instance_prep(patternMatcher); return patternMatcher; } typedef struct fieldPatternData_t { const uint8_t *payload; unsigned length; httpSession *hsession; } FieldPatternData; static int http_field_pattern_match(void *id, void *unused_tree, int index, void *data, void *unused_neg) { static const uint8_t crlf[] = "\r\n"; static unsigned crlfLen = sizeof(crlf)-1; FieldPatternData *pFieldData = (FieldPatternData*)data; FieldPattern *target = (FieldPattern *)id; const uint8_t* p; unsigned fieldOffset = target->length + index; unsigned remainingLength = pFieldData->length - fieldOffset; if (!(p = (uint8_t *)service_strstr(&pFieldData->payload[fieldOffset], remainingLength, crlf, crlfLen))) { return 1; } pFieldData->hsession->fieldOffset[target->patternType] = (uint16_t)fieldOffset; pFieldData->hsession->fieldEndOffset[target->patternType] = p - pFieldData->payload; return 1; } void httpGetNewOffsetsFromPacket(SFSnortPacket *pkt, httpSession *hsession, tAppIdConfig *pConfig) { #define MIN_HTTP_REQ_HEADER_SIZE (sizeof("GET /\r\n\r\n")-1) static const uint8_t crlfcrlf[] = "\r\n\r\n"; static unsigned crlfcrlfLen = sizeof(crlfcrlf)-1; tDetectorHttpConfig *pHttpConfig = &pConfig->detectorHttpConfig; const uint8_t* p; uint8_t *headerEnd; PatternType fieldId; FieldPatternData patternMatchData; for (fieldId = AGENT_PT; fieldId <= COOKIE_PT; fieldId++) { // clear offset for default case; hsession->fieldOffset[fieldId] = 0; } if (!pkt || !pkt->payload || pkt->payload_size < MIN_HTTP_REQ_HEADER_SIZE) return; p = pkt->payload; patternMatchData.hsession = hsession; patternMatchData.payload = p; if (!(headerEnd = (uint8_t *)service_strstr(p, pkt->payload_size, crlfcrlf, crlfcrlfLen))) return; headerEnd += crlfcrlfLen; patternMatchData.length = (unsigned)(headerEnd - p); _dpd.searchAPI->search_instance_find_all(pHttpConfig->field_matcher, (char *)p, patternMatchData.length, 0, &http_field_pattern_match, (void *)(&patternMatchData)); } int http_detector_finalize(tAppIdConfig *pConfig) { size_t upc = 0; size_t apc = 0; size_t ctc = 0; size_t vpc = 0; tDetectorHttpConfig *pHttpConfig = &pConfig->detectorHttpConfig; HttpPatternLists* patternLists = &pConfig->httpPatternLists; u_int32_t numPatterns; /*create via pattern matcher */ numPatterns = sizeof(via_http_detector_patterns)/sizeof(*via_http_detector_patterns); pHttpConfig->via_matcher = processPatterns(via_http_detector_patterns, numPatterns, &vpc, NULL); if (!pHttpConfig->via_matcher) return -1; /*create url pattern matcher */ pHttpConfig->url_matcher = processPatterns(NULL, 0, &upc, patternLists->urlPatternList); if (!pHttpConfig->url_matcher) return -1; /*create client agent pattern matcher */ numPatterns = sizeof(client_agent_patterns)/sizeof(*client_agent_patterns); pHttpConfig->client_agent_matcher = processPatterns(client_agent_patterns,numPatterns, &apc, patternLists->clientAgentPatternList); if (!pHttpConfig->client_agent_matcher) return -1; numPatterns = sizeof(header_patterns)/sizeof(*header_patterns); pHttpConfig->header_matcher = registerHeaderPatterns(header_patterns,numPatterns); if (!pHttpConfig->header_matcher) return -1; numPatterns = sizeof(host_payload_http_detector_patterns)/sizeof(*host_payload_http_detector_patterns); if (processHostPatterns(host_payload_http_detector_patterns, numPatterns, patternLists->hostPayloadPatternList, &patternLists->appUrlList, &patternLists->RTMPUrlList, pHttpConfig) < 0) return -1; numPatterns = sizeof(content_type_patterns)/sizeof(*content_type_patterns); pHttpConfig->content_type_matcher = processContentTypePatterns(content_type_patterns, numPatterns, patternLists->contentTypePatternList, &ctc); if (!pHttpConfig->content_type_matcher) return -1; numPatterns = sizeof(http_field_patterns)/sizeof(*http_field_patterns); pHttpConfig->field_matcher = processHttpFieldPatterns(http_field_patterns, numPatterns); if (!pHttpConfig->field_matcher) return -1; if (!processCHPList(patternLists->chpList, pHttpConfig)) return -1; pHttpConfig->chp_user_agent_matcher = pHttpConfig->chp_matchers[AGENT_PT]; pHttpConfig->chp_host_matcher = pHttpConfig->chp_matchers[HOST_PT]; pHttpConfig->chp_referer_matcher = pHttpConfig->chp_matchers[REFERER_PT]; pHttpConfig->chp_uri_matcher = pHttpConfig->chp_matchers[URI_PT]; pHttpConfig->chp_cookie_matcher = pHttpConfig->chp_matchers[COOKIE_PT]; pHttpConfig->chp_req_body_matcher = pHttpConfig->chp_matchers[REQ_BODY_PT]; pHttpConfig->chp_content_type_matcher = pHttpConfig->chp_matchers[CONTENT_TYPE_PT]; pHttpConfig->chp_location_matcher = pHttpConfig->chp_matchers[LOCATION_PT]; pHttpConfig->chp_body_matcher = pHttpConfig->chp_matchers[BODY_PT]; return 0; } void http_detector_clean(tDetectorHttpConfig *pHttpConfig) { int i; for (i = 0; i < sizeof(pHttpConfig->chp_matchers)/sizeof(pHttpConfig->chp_matchers[0]); i++) { _dpd.searchAPI->search_instance_free(pHttpConfig->chp_matchers[i]); pHttpConfig->chp_matchers[i] = NULL; } // These are same as chp_matchers[] and freed in the loop above pHttpConfig->chp_user_agent_matcher = pHttpConfig->chp_host_matcher = pHttpConfig->chp_referer_matcher = pHttpConfig->chp_uri_matcher = pHttpConfig->chp_cookie_matcher = pHttpConfig->chp_req_body_matcher = pHttpConfig->chp_content_type_matcher = pHttpConfig->chp_location_matcher = pHttpConfig->chp_body_matcher = NULL; if (pHttpConfig->via_matcher) { _dpd.searchAPI->search_instance_free(pHttpConfig->via_matcher); pHttpConfig->via_matcher = NULL; } if (pHttpConfig->url_matcher) { _dpd.searchAPI->search_instance_free(pHttpConfig->url_matcher); pHttpConfig->url_matcher = NULL; } if (pHttpConfig->client_agent_matcher) { _dpd.searchAPI->search_instance_free(pHttpConfig->client_agent_matcher); pHttpConfig->client_agent_matcher = NULL; } if (pHttpConfig->header_matcher) { _dpd.searchAPI->search_instance_free(pHttpConfig->header_matcher); pHttpConfig->header_matcher = NULL; } if (pHttpConfig->content_type_matcher) { _dpd.searchAPI->search_instance_free(pHttpConfig->content_type_matcher); pHttpConfig->content_type_matcher = NULL; } if (pHttpConfig->field_matcher) { _dpd.searchAPI->search_instance_free(pHttpConfig->field_matcher); pHttpConfig->field_matcher = NULL; } destroyHostUrlMatcher(&pHttpConfig->hostUrlMatcher); destroyHostUrlMatcher(&pHttpConfig->RTMPHostUrlMatcher); destroyHostUrlPatternList(&pHttpConfig->hostUrlPatternsList); } static inline void FreeMatchStructures(MatchedPatterns *mp) { MatchedPatterns *tmp; while(mp) { tmp = mp; mp = mp->next; free(tmp); } } static void rewriteCHP (const char *buf, int bs, int start, int psize, char *adata, char **outbuf, int insert) { int maxs, bufcont, as; char *copyPtr; // special behavior for insert vs. rewrite if (insert) { // we don't want to insert a string that is already present if (!adata || _dpd.SnortStrcasestr((const char *)buf, bs, adata)) return; start += psize; bufcont = start; as = strlen(adata); maxs = bs+as; } else { if (adata) { // we also don't want to replace a string with an identical one. if (!strncmp(buf+start,adata,psize)) return; as = strlen(adata); } else as = 0; bufcont = start+psize; maxs = bs+(as-psize); } *outbuf = copyPtr = (char *)calloc(maxs+1,sizeof(char)); if (!copyPtr) return; memcpy(copyPtr, buf, start); copyPtr += start; if (adata) { memcpy(copyPtr, adata, as); copyPtr += as; } memcpy(copyPtr, buf+bufcont, bs-bufcont); } static char * normalize_userid (char *user) { int i, old_size; int percent_count = 0; char a, b; char *tmp_ret, *tmp_user; old_size = strlen(user); // find number of '%' for (i = 0; i < old_size; i++) { if (*(user+i) == '%') percent_count++; } if (0 == percent_count) { /* no change allows an early out */ return user; } /* Shrink user string in place */ tmp_ret = user; tmp_user = user; while (*tmp_user) { if ((*tmp_user == '%') && ((a = tmp_user[1]) && (b = tmp_user[2])) && (isxdigit(a) && isxdigit(b))) { if (a >= 'a') a -= 'a'-'A'; if (a >= 'A') a -= ('A' - 10); else a -= '0'; if (b >= 'a') b -= 'a'-'A'; if (b >= 'A') b -= ('A' - 10); else b -= '0'; *tmp_ret++ = 16*a+b; tmp_user+=3; } else { *tmp_ret++ = *tmp_user++; } } *tmp_ret++ = '\0'; return user; } static void extractCHP (char *buf, int bs, int start, int psize, char *adata, char **outbuf) { char *begin = buf+start+psize; char *end = NULL; char *tmp; int i, as; if (adata) as = strlen(adata); else as = 0; // find where the pattern ends so we can allocate a buffer for (i = 0; i < as; i++) { tmp = strchr(begin, *(adata+i)); if (tmp) { if (!end || (tmp < end)) end = tmp; } } if (!end) { if ((tmp = strchr(begin, 0x0d))) { end = tmp; } if ((tmp = strchr(begin, 0x0a))) { if (!end || (tmp < end)) end = tmp; } } if (!end) end = begin+bs; *outbuf = strndup(begin, end-begin); } static uint32_t ddToIp (char *start, int size) { uint32_t ret_addr = 0; char *p; int tmp = 0; int octet = 3; int digit_count = 1; int done = 0; for (p = start; p < start+size; p++) { if (isdigit(*p)) { // if there are more than three digits in a row if (digit_count > 3) { // this might be a spurrious digit after the IP address if (octet == 0 && tmp && tmp <= 255) { ret_addr += tmp; done = 1; break; } else return 0; } // otherwise, increase the value of tmp tmp *= 10; tmp += *p - '0'; digit_count++; } // 0x2e is '.' else if (*p == 0x2e) { // make sure we don't have random dots in there if (!tmp) return 0; // otherwise, increase the return value else { // octet value must fit in 8-bit boundary if (tmp > 255) return 0; ret_addr += tmp < 255) return 0; if (!done) ret_addr += tmp; return htonl(ret_addr); } static uint32_t ffSetIp (char *buf, int buf_size, int start, int psize) { uint32_t ret_address; ret_address = ddToIp(buf+start+psize, buf_size); return ret_address; } static uint16_t ffSetPort (char *buf, int buf_size, int start, int psize) { uint16_t temp_port = 0; uint16_t new_digit; char *p; int i; for (p = buf+start+psize, i = 1; p < buf+buf_size && isdigit(*p); p++, i++) { new_digit = *p -'0'; // we don't want to try to put a value gt 65535 into a uint_16t if ((i > 5) || (temp_port > 6535 || (temp_port == 6535 && new_digit > 5))) return 0; temp_port *= 10; temp_port += *p - '0'; } return temp_port; } static uint8_t ffSetProtocol (char *buf, int buf_size, int start, int psize) { uint8_t temp_protocol = 0; uint8_t new_digit; char *p; int i; for (p = buf+start+psize, i = 1; p < buf+buf_size && isdigit(*p); p++, i++) { new_digit = *p - '0'; // we don't want to try to put a value gt 255 into a uint8_t if ((i > 3) || (temp_protocol > 25 || (temp_protocol == 25 && new_digit > 5))) return 0; temp_protocol *= 10; temp_protocol += new_digit; } return temp_protocol; } static void fflowCreate (char *adata, fflow_info *fflow, SFSnortPacket *p, tAppId target_appid) { char *saddr_string = NULL; char *daddr_string = NULL; char *sport_string = NULL; char *dport_string = NULL; char *protocol_string = NULL; char *appid = NULL; sfaddr_t *sip, *dip; int temp_port = 0; /* The Action Data for this action is special THE SEQUENCE MUST BE source_address source_port dest_address dest_port protocol appid DELIMITED BY A SPACE if any value is '*', that means we should have already set this value with a previous action */ if (!(saddr_string = strtok(adata, " "))) return; if (!(sport_string = strtok(NULL, " "))) return; if (!(daddr_string = strtok(NULL, " "))) return; if (!(dport_string = strtok(NULL, " "))) return; if (!(protocol_string = strtok(NULL, " "))) return; if (!(appid = strtok(NULL, " "))) return; switch (*saddr_string) { case 'S': sip = GET_SRC_IP(p); fflow->sip = sfaddr_get_ip4_value(sip); break; case 'D': sip = GET_DST_IP(p); fflow->sip = sfaddr_get_ip4_value(sip); break; case '0': sip = 0; break; case '*': if (!fflow->sip) return; break; default: if ((!fflow->sip) && (!(fflow->sip = ddToIp(saddr_string, strlen(saddr_string))))) return; } switch (*sport_string) { case 'S': if (strlen(sport_string) > 2) { if ((temp_port = strtol(sport_string+1, NULL, 10))) fflow->sport = p->src_port + temp_port; else return; } else fflow->sport = p->src_port; break; case 'D': if (strlen(sport_string) > 2) { if ((temp_port = strtol(sport_string+1, NULL, 10))) fflow->sport = p->dst_port + temp_port; else return; } else fflow->sport = p->dst_port; break; case '0': fflow->sport = 0; break; case '*': if (!fflow->sport) return; break; default: if ((!fflow->sport) && (!(fflow->sport = ffSetPort(sport_string, strlen(sport_string), 0, 0)))) return; } switch (*daddr_string) { case 'S': dip = GET_SRC_IP(p); fflow->dip = sfaddr_get_ip4_value(dip); break; case 'D': dip = GET_DST_IP(p); fflow->dip = sfaddr_get_ip4_value(dip); break; case '0': fflow->dip = 0; break; case '*': if (!fflow->dip) return; break; default: if ((!fflow->dip) && (!(fflow->dip = ddToIp(daddr_string, strlen(daddr_string))))) return; } switch (*dport_string) { case 'S': if (strlen(dport_string) > 2) { if ((temp_port = strtol(dport_string+1, NULL, 10))) fflow->dport = p->src_port + temp_port; else return; } else fflow->dport = p->src_port; break; case 'D': if (strlen(dport_string) > 2) { if ((temp_port = strtol(dport_string+1, NULL, 10))) fflow->dport = p->dst_port + temp_port; else return; } else fflow->dport = p->dst_port; break; case '0': fflow->dport = 0; break; case '*': if (!fflow->dport) return; break; default: if ((!fflow->dport) && (!(fflow->dport = ffSetPort(dport_string, strlen(dport_string), 0, 0)))) return; } switch (*protocol_string) { case 'T': fflow->protocol = IPPROTO_TCP; break; case 'U': fflow->protocol = IPPROTO_UDP; break; case '0': fflow->protocol = 0; break; case 'S': case 'D': fflow->protocol = IsTCP(p) ? IPPROTO_TCP : IPPROTO_UDP; break; case '*': if (!fflow->protocol) return; break; default: if ((!fflow->protocol) && (!(fflow->protocol = ffSetProtocol(protocol_string, strlen(protocol_string), 0, 0)))) return; } switch (*appid) { case '*': fflow->appId = target_appid; break; default: fflow->appId = strtol(appid, NULL, 10); } fflow->flow_prepared = 1; } void finalizeFflow (fflow_info *fflow, unsigned app_type_flags, tAppId target_appId, SFSnortPacket *p) { tAppIdData *fp; sfaddr_t saddr, daddr; sfip_set_raw(&saddr, &fflow->sip, AF_INET); sfip_set_raw(&daddr, &fflow->dip, AF_INET); if (!(fp = AppIdEarlySessionCreate(NULL, p, &saddr, fflow->sport, &daddr, fflow->dport, fflow->protocol, target_appId, 0))) return; if (app_type_flags & APP_TYPE_SERVICE) { fp->serviceAppId = target_appId; fp->rnaServiceState = RNA_STATE_FINISHED; fp->rnaClientState = RNA_STATE_FINISHED; } if (app_type_flags & APP_TYPE_CLIENT) { fp->clientAppId = target_appId; fp->rnaClientState = RNA_STATE_FINISHED; } if (app_type_flags & APP_TYPE_PAYLOAD) { fp->payloadAppId = target_appId; } } int scanKeyCHP (PatternType ptype, char *buf, int buf_size, CHPMatchTally **ppTally, MatchedCHPAction **ppmatches, const tDetectorHttpConfig *pHttpConfig) { CHPTallyAndActions tallyAndActions; tallyAndActions.pTally = *ppTally; tallyAndActions.matches = *ppmatches; _dpd.searchAPI->search_instance_find_all(pHttpConfig->chp_matchers[ptype], (char *)buf, buf_size, 0, &chp_key_pattern_match, (void *)(&tallyAndActions)); *ppTally = tallyAndActions.pTally; *ppmatches = tallyAndActions.matches; return (int)(tallyAndActions.pTally != NULL); } tAppId scanCHP (PatternType ptype, char *buf, int buf_size, MatchedCHPAction *mp, char **version, char **user, char **new_field, int *total_found, httpSession *hsession, SFSnortPacket *p, const tDetectorHttpConfig *pHttpConfig) { MatchedCHPAction *second_sweep_for_inserts = NULL; int do_not_further_modify_field = 0; CHPAction *match = NULL; tAppId ret = APP_ID_NONE; MatchedCHPAction *tmp; if (ptype > MAX_KEY_PATTERN) { // There is no previous attempt to match generated by scanKeyCHP() mp = NULL; _dpd.searchAPI->search_instance_find_all(pHttpConfig->chp_matchers[ptype], (char *)buf, buf_size, 0, &chp_pattern_match, (void *)(&mp)); } if (!mp) return APP_ID_NONE; if (appidStaticConfig->disable_safe_search) { new_field = NULL; } for(tmp = mp; tmp; tmp = tmp->next) { match = (CHPAction *)tmp->mpattern; if (match->appIdInstance > hsession->chp_candidate) break; // because the list is sorted we know there are no more else if (match->appIdInstance == hsession->chp_candidate) { switch (match->action) { case DEFER_TO_SIMPLE_DETECT: // Ignore all other patterns; we are done. FreeMatchedCHPActions(mp); // Returning APP_ID_NONE will trigger the clearing of hsession->skip_simple_detect // and the freeing of any planned field rewrites. return APP_ID_NONE; default: (*total_found)++; break; case ALTERNATE_APPID: // an "optional" action that doesn't count towards totals case REWRITE_FIELD: // handled when the action completes successfully case INSERT_FIELD: // handled when the action completes successfully break; } if (!ret) ret = hsession->chp_candidate; } else continue; // keep looking switch (match->action) { case COLLECT_VERSION: if (!*version) extractCHP(buf, buf_size, tmp->index, match->psize, match->action_data, version); hsession->skip_simple_detect = true; break; case EXTRACT_USER: if (!*user && !appidStaticConfig->chp_userid_disabled) { extractCHP(buf, buf_size, tmp->index, match->psize, match->action_data, user); if (*user) *user = normalize_userid(*user); } break; case REWRITE_FIELD: if (!do_not_further_modify_field && NULL != new_field && NULL == *new_field) { // The field supports rewrites, and a rewrite hasn't happened. rewriteCHP(buf, buf_size, tmp->index, match->psize, match->action_data, new_field, 0); (*total_found)++; do_not_further_modify_field = 1; } break; case FUTURE_APPID_SESSION_SIP: if (appidStaticConfig->chp_fflow_disabled) break; if (!hsession->fflow) { if (!(hsession->fflow = (fflow_info*)_dpd.snortAlloc(1, sizeof(fflow_info), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) { appidStaticConfig->chp_fflow_disabled = 1; break; } } if (!hsession->fflow->sip) hsession->fflow->sip = ffSetIp(buf, buf_size, tmp->index, match->psize); break; case FUTURE_APPID_SESSION_DIP: if (appidStaticConfig->chp_fflow_disabled) break; if (!hsession->fflow) { if (!(hsession->fflow = (fflow_info*)_dpd.snortAlloc(1, sizeof(fflow_info), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) { appidStaticConfig->chp_fflow_disabled = 1; break; } } if (!hsession->fflow->dip) hsession->fflow->dip = ffSetIp(buf, buf_size, tmp->index, match->psize); break; case FUTURE_APPID_SESSION_SPORT: if (appidStaticConfig->chp_fflow_disabled) break; if (!hsession->fflow) { if (!(hsession->fflow = (fflow_info*)_dpd.snortAlloc(1, sizeof(fflow_info), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) { appidStaticConfig->chp_fflow_disabled = 1; break; } } if (!hsession->fflow->sport) hsession->fflow->sport = ffSetPort(buf, buf_size, tmp->index, match->psize); break; case FUTURE_APPID_SESSION_DPORT: if (appidStaticConfig->chp_fflow_disabled) break; if (!hsession->fflow) { if (!(hsession->fflow = (fflow_info*)_dpd.snortAlloc(1, sizeof(fflow_info), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) { appidStaticConfig->chp_fflow_disabled = 1; break; } } if (!hsession->fflow->dport) hsession->fflow->dport = ffSetPort(buf, buf_size, tmp->index, match->psize); break; case FUTURE_APPID_SESSION_PROTOCOL: if (appidStaticConfig->chp_fflow_disabled) break; if (!hsession->fflow) { if (!(hsession->fflow = (fflow_info*)_dpd.snortAlloc(1, sizeof(fflow_info), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) { appidStaticConfig->chp_fflow_disabled = 1; break; } } if (!hsession->fflow->protocol) hsession->fflow->protocol = ffSetProtocol(buf, buf_size, tmp->index, match->psize); break; case FUTURE_APPID_SESSION_CREATE: if (appidStaticConfig->chp_fflow_disabled) break; if (!hsession->fflow) { if (!(hsession->fflow = (fflow_info*)_dpd.snortAlloc(1, sizeof(fflow_info), PP_APP_ID, PP_MEM_CATEGORY_SESSION))) { appidStaticConfig->chp_fflow_disabled = 1; break; } } fflowCreate(match->action_data, hsession->fflow, p, hsession->chp_candidate); break; case INSERT_FIELD: if (!do_not_further_modify_field && second_sweep_for_inserts == NULL) { if (match->action_data) { // because this insert is the first one we have come across // we only need to remember this ONE for later. second_sweep_for_inserts = tmp; } else { // This is an attempt to "insert nothing"; call it a match // The side effect is to set the do_not_further_modify_field to 1 (true) // Note that an attempt to "rewrite with identical string" // is NOT equivalent to an "insert nothing" because of case- // insensitive pattern matching do_not_further_modify_field = 1; (*total_found)++; } } break; case ALTERNATE_APPID: hsession->chp_alt_candidate = strtol(match->action_data, NULL, 10); hsession->skip_simple_detect = true; break; case HOLD_FLOW: hsession->chp_hold_flow = 1; break; case GET_OFFSETS_FROM_REBUILT: hsession->get_offsets_from_rebuilt = 1; hsession->chp_hold_flow = 1; break; case SEARCH_UNSUPPORTED: case NO_ACTION: hsession->skip_simple_detect = true; break; default: break; } } // non-NULL second_sweep_for_inserts indicates the insert action we will use. if (!do_not_further_modify_field && second_sweep_for_inserts && NULL != new_field && NULL == *new_field) { // We will take the first INSERT_FIELD with an action string, // which was decided with the setting of second_sweep_for_inserts. rewriteCHP(buf, buf_size, second_sweep_for_inserts->index, second_sweep_for_inserts->mpattern->psize, second_sweep_for_inserts->mpattern->action_data, new_field, 1); // insert (*total_found)++; } FreeMatchedCHPActions(mp); return ret; } static inline int optionallyReplaceWithStrdup(char **optionalStr, const char *strToDup) { if (optionalStr) { if (*optionalStr) free(*optionalStr); if (NULL == (*optionalStr = strdup(strToDup))) return -1; // malloc failed, if they care. } return 0; // success } void identifyUserAgent(const u_int8_t *start, int size, tAppId *serviceAppId, tAppId *clientAppId, char **version, const tDetectorHttpConfig *pHttpConfig) { int skypeDetect; int mobileDetect; int safariDetect; unsigned int appleEmailDetect; int firefox_detected, android_browser_detected; int dominant_pattern_detected; int longest_misc_match; const u_int8_t *end; MatchedPatterns *mp = NULL; MatchedPatterns *tmp; DetectorHTTPPattern *match; u_int8_t* buffPtr; unsigned int i; char temp_ver[MAX_VERSION_SIZE]; temp_ver[0] = 0; _dpd.searchAPI->search_instance_find_all(pHttpConfig->client_agent_matcher, (char *)start, size, 0, &http_pattern_match, (void *)&mp); if (mp) { end = start + size; temp_ver[0] = 0; skypeDetect = 0; mobileDetect = 0; safariDetect = 0; firefox_detected = 0; android_browser_detected = 0; dominant_pattern_detected = 0; longest_misc_match = 0; i = 0; *clientAppId = APP_ID_NONE; *serviceAppId = APP_ID_HTTP; for(tmp = mp; tmp; tmp = tmp->next) { match = (DetectorHTTPPattern *)tmp->mpattern; switch (match->client_app) { case APP_ID_INTERNET_EXPLORER: case APP_ID_FIREFOX: if (dominant_pattern_detected) break; buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; if (*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != '/') break; buffPtr++; while (i < MAX_VERSION_SIZE-1 && buffPtr < end) { if(*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != ';' && *buffPtr != ')') temp_ver[i++] = *buffPtr++; else break; } if(i == 0) break; temp_ver[i] = 0; /*compatibility check */ if (match->client_app == APP_ID_INTERNET_EXPLORER && strstr((char *)buffPtr, "SLCC2")) { if ((MAX_VERSION_SIZE-i) >= (sizeof(COMPATIBLE_BROWSER_STRING) - 1)) { strcat(temp_ver, COMPATIBLE_BROWSER_STRING); } } // Pick firefox over some things, but pick a misc app over Firefox. if (match->client_app == APP_ID_FIREFOX) firefox_detected = 1; *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; break; case APP_ID_CHROME: if (dominant_pattern_detected) break; buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; if (*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != '/') break; buffPtr++; while (i < MAX_VERSION_SIZE-1 && buffPtr < end) { if(*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != ';' && *buffPtr != ')') temp_ver[i++] = *buffPtr++; else break; } if(i == 0) break; dominant_pattern_detected = 1; temp_ver[i] = 0; *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; break; case APP_ID_ANDROID_BROWSER: if (dominant_pattern_detected) break; buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; if (*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != '/') break; buffPtr++; while (i < MAX_VERSION_SIZE-1 && buffPtr < end) { if(*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != ';' && *buffPtr != ')') temp_ver[i++] = *buffPtr++; else break; } if(i == 0) break; temp_ver[i] = 0; android_browser_detected = 1; break; case APP_ID_KONQUEROR: case APP_ID_CURL: case APP_ID_PICASA: if (dominant_pattern_detected) break; case APP_ID_WINDOWS_MEDIA_PLAYER: case APP_ID_BITTORRENT: buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; if (*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != '/') break; buffPtr++; while (i < MAX_VERSION_SIZE-1 && buffPtr < end) { if(*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != ';' && *buffPtr != ')') temp_ver[i++] = *buffPtr++; else break; } if(i == 0) break; temp_ver[i] = 0; *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; goto done; case APP_ID_GOOGLE_DESKTOP: buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; if (*buffPtr != ')') { if (*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != '/') break; buffPtr++; while (i < MAX_VERSION_SIZE-1 && buffPtr < end) { if(*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != ';') temp_ver[i++] = *buffPtr++; else break; } if(i == 0) break; temp_ver[i] = 0; } *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; goto done; case APP_ID_SAFARI_MOBILE_DUMMY: mobileDetect = 1; break; case APP_ID_SAFARI: if (dominant_pattern_detected) break; safariDetect = 1; break; case APP_ID_APPLE_EMAIL: appleEmailDetect = 1; for (i = 0; i < 3 && appleEmailDetect; i++) { buffPtr = (u_int8_t*) strstr((char *)start, (char *)APPLE_EMAIL_PATTERNS[i]); appleEmailDetect = ((u_int8_t*)buffPtr && (i != 0 || buffPtr == ((u_int8_t*)start))); } if (appleEmailDetect) { dominant_pattern_detected = !(buffPtr && strstr((char *)buffPtr, SAFARI_PATTERN) != NULL); temp_ver[0] = 0; *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; } i = 0; break; case APP_ID_WGET: buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; while (i < MAX_VERSION_SIZE - 1 && buffPtr < end) { temp_ver[i++] = *buffPtr++; } temp_ver[i] = 0; *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; goto done; case APP_ID_BLACKBERRY_BROWSER: while( start < end && *start != '/' ) start++; if(start >= end) break; start++; while (i < MAX_VERSION_SIZE -1 && start < end) { if(*start != ' ' && *start != 0x09 && *start != ';') temp_ver[i++] = *start++; else break; } if(i == 0) break; temp_ver[i] = 0; *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; goto done; case APP_ID_SKYPE: skypeDetect = 1; break; case APP_ID_HTTP: break; case APP_ID_OPERA: *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; break; case FAKE_VERSION_APP_ID: if (temp_ver[0]) { temp_ver[0] = 0; i = 0; } buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; if (*buffPtr == (u_int8_t)'/') { buffPtr++; while (i < MAX_VERSION_SIZE - 1 && buffPtr < end) { if(*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != ';' && *buffPtr != ')') temp_ver[i++] = *buffPtr++; else break; } } temp_ver[i] = 0; break; default: if (match->client_app) { if (match->pattern_size <= longest_misc_match) break; longest_misc_match = match->pattern_size; i =0; /* if we already collected temp_ver information after seeing 'Version', let's use that*/ buffPtr = (u_int8_t*) start + tmp->index + match->pattern_size; /* we may have to enter the pattern with the / in it. */ if (*buffPtr == (u_int8_t)'/' || *buffPtr == (u_int8_t)' ') buffPtr++; if (buffPtr-1 > start && buffPtr < end && (*(buffPtr-1) == (u_int8_t)'/' || *(buffPtr-1) == (u_int8_t)' ')) { while (i < MAX_VERSION_SIZE -1 && buffPtr < end) { if(*buffPtr != ' ' && *buffPtr != 0x09 && *buffPtr != ';' && *buffPtr != ')') temp_ver[i++] = *buffPtr++; else break; } temp_ver[i] = 0; } dominant_pattern_detected = 1; *serviceAppId = APP_ID_HTTP; *clientAppId = match->client_app; } } } if (mobileDetect && safariDetect && !dominant_pattern_detected) { *serviceAppId = APP_ID_HTTP; *clientAppId = APP_ID_SAFARI_MOBILE; } else if (safariDetect && !dominant_pattern_detected) { *serviceAppId = APP_ID_HTTP; *clientAppId = APP_ID_SAFARI; } else if (firefox_detected && !dominant_pattern_detected) { *serviceAppId = APP_ID_HTTP; *clientAppId = APP_ID_FIREFOX; } else if (android_browser_detected && !dominant_pattern_detected) { *serviceAppId = APP_ID_HTTP; *clientAppId = APP_ID_ANDROID_BROWSER; } /* Better to choose Skype over any other ID */ else if (skypeDetect) { *serviceAppId = APP_ID_SKYPE_AUTH; *clientAppId = APP_ID_SKYPE; } } done: optionallyReplaceWithStrdup(version,temp_ver); FreeMatchStructures(mp); } int getAppidByViaPattern(const uint8_t *data, unsigned size, char **version, const tDetectorHttpConfig *pHttpConfig) { unsigned i; const uint8_t *data_ptr; const uint8_t *end = data + size; MatchedPatterns *mp = NULL; DetectorHTTPPattern *match = NULL; char temp_ver[MAX_VERSION_SIZE]; if (pHttpConfig->via_matcher) { _dpd.searchAPI->search_instance_find_all(pHttpConfig->via_matcher, (char *)data, size, 0, &http_pattern_match, (void *)&mp); } if(mp) { match = (DetectorHTTPPattern *)mp->mpattern; switch (match->service_id) { case APP_ID_SQUID: data_ptr = (u_int8_t*) data + mp->index + match->pattern_size; if (*data_ptr == '/') { data_ptr++; for (i = 0; data_ptr < end && i < (MAX_VERSION_SIZE-1) && *data_ptr != ')' && isprint(*data_ptr); data_ptr++) { temp_ver[i++] = (char)*data_ptr; } } else i = 0; temp_ver[i] = 0; optionallyReplaceWithStrdup(version,temp_ver); FreeMatchStructures(mp); return APP_ID_SQUID; default: FreeMatchStructures(mp); return APP_ID_NONE; } } return APP_ID_NONE; } #define HTTP_HEADER_WORKINGWITH_ASPROXY "ASProxy/" tAppId scan_header_x_working_with(const uint8_t *data, uint32_t size, char **version) { uint32_t i; const uint8_t *end; char temp_ver[MAX_VERSION_SIZE]; temp_ver[0] = 0; if (size >= (sizeof(HTTP_HEADER_WORKINGWITH_ASPROXY)-1) && memcmp(data,HTTP_HEADER_WORKINGWITH_ASPROXY,sizeof(HTTP_HEADER_WORKINGWITH_ASPROXY)-1) == 0) { end = data+size; data += sizeof(HTTP_HEADER_WORKINGWITH_ASPROXY)-1; for (i = 0; data < end && i < (MAX_VERSION_SIZE-1) && *data != ')' && isprint(*data); data++) { temp_ver[i++] = (char)*data; } temp_ver[i] = 0; optionallyReplaceWithStrdup(version,temp_ver); return APP_ID_ASPROXY; } return APP_ID_NONE; } tAppId getAppidByContentType(const uint8_t *data, int size, const tDetectorHttpConfig *pHttpConfig) { MatchedPatterns *mp = NULL; DetectorHTTPPattern *match; tAppId payloadId; if (pHttpConfig->content_type_matcher) { _dpd.searchAPI->search_instance_find_all(pHttpConfig->content_type_matcher, (char *)data, size, 0, &content_pattern_match, (void *)&mp); } if (!mp) return APP_ID_NONE; match = mp->mpattern; payloadId = match->appId; FreeMatchStructures(mp); return payloadId; } static int http_header_pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { HeaderMatchedPatterns *matches = (HeaderMatchedPatterns *)data; HeaderPattern *target = (HeaderPattern *)id; HTTPHeaderIndices *headers = matches->headers; if (matches->last_match >= 0) { headers[matches->last_match].end = index; matches->last_match = -1; } if (target->id < HTTP_ID_LEN) { if (index == 0) { goto store_index; } else if (index == matches->last_index_end) { /* This checks if the last match was \r or \n */ /* It is still possible to have nefarious payloads */ /* that have HTTP Headers in "" */ goto store_index; } } goto done; store_index: headers[target->id].start = index + target->length; headers[target->id].end = 0; matches->last_match = target->id; done: matches->last_index_end = index + target->length; return 0; } int getHTTPHeaderLocation(const uint8_t *data, unsigned size, HttpId id, int *start, int *end, HeaderMatchedPatterns *hmp, const tDetectorHttpConfig *pHttpConfig) { HTTPHeaderIndices *match; if (hmp->headers[id].start > 0) { *start = hmp->headers[id].start; *end = hmp->headers[id].end; return 1; } if (hmp->searched) return 0; if (pHttpConfig->header_matcher) { _dpd.searchAPI->search_instance_find_all(pHttpConfig->header_matcher, (char *)data, size, 0, &http_header_pattern_match, (void*)hmp); } hmp->searched = 1; /*Close out search space for last matched if needed */ if (hmp->last_match > 0 && hmp->headers[hmp->last_match].end <= 0) hmp->headers[hmp->last_match].end = size; match = &(hmp->headers[id]); if (match->start > 0) { *start = match->start; *end = match->end; return 1; } return 0; } tAppId getAppIdFromUrl(char *host, char *url, char **version, char *referer, tAppId *clientAppId, tAppId *serviceAppId, tAppId *payloadAppId, tAppId *referredPayloadAppId, unsigned from_rtmp, const tDetectorHttpConfig *pHttpConfig) { char *path; char *referer_start; char *temp_host = NULL; const char *referer_path = NULL; int host_len; int referer_len = 0; int referer_path_len = 0; int path_len = 0; tMlmpPattern patterns[3]; tMlpPattern query; HostUrlDetectorPattern *data; char *q; int payload_found = 0; int url_len; static void *matcher; #define RTMP_MEDIA_STREAM_OFFSET 50000000 #define URL_SCHEME_END_PATTERN "://" #define URL_SCHEME_MAX_LEN (sizeof("https://")-1) matcher = (from_rtmp ? pHttpConfig->RTMPHostUrlMatcher : pHttpConfig->hostUrlMatcher); if (!host && !url) return 0; if (url) { int scheme_len = strlen(url); if (scheme_len > URL_SCHEME_MAX_LEN) scheme_len = URL_SCHEME_MAX_LEN; // only need to search the first few bytes for scheme char *url_offset = (char *)service_strstr((uint8_t *)url, scheme_len, (uint8_t *)URL_SCHEME_END_PATTERN, sizeof(URL_SCHEME_END_PATTERN)-1); if (url_offset) url_offset += sizeof(URL_SCHEME_END_PATTERN)-1; else return 0; url = url_offset; url_len = strlen(url); } else { url_len = 0; } if (!host) { host_len = url_len; temp_host = host = strdup(url); if (!temp_host) { return 0; } host = strchr(host, '/'); if (host != NULL) { *host = '\0'; } host = temp_host; } host_len = strlen(host); if (url_len) { if (url_len < host_len) { free(temp_host); return 0; } path = strchr(url, '/'); if (path) path_len = url + url_len - path; } if (!path_len) { path = "/"; path_len = 1; } patterns[0].pattern = (uint8_t *) host; patterns[0].patternSize = host_len; patterns[1].pattern = (uint8_t *) path; patterns[1].patternSize = path_len; patterns[2].pattern = NULL; data = (HostUrlDetectorPattern *)mlmpMatchPatternUrl(matcher, patterns); if (data) { payload_found = 1; if (url) { q = strchr(url, '?'); if (q != NULL) { char temp_ver[MAX_VERSION_SIZE]; temp_ver[0] = 0; query.pattern = (uint8_t *) ++q; query.patternSize = strlen(q); matchQueryElements(&query, &data->query, temp_ver, MAX_VERSION_SIZE); if (temp_ver[0] != 0) { optionallyReplaceWithStrdup(version,temp_ver); } } } *clientAppId = data->client_id; *serviceAppId = data->service_id; *payloadAppId = data->payload_id; } free(temp_host); /* if referred_id feature id disabled, referer will be null */ if (referer && (!payload_found || appInfoEntryFlagGet(data->payload_id, APPINFO_FLAG_REFERRED, appIdActiveConfigGet()))) { referer_start = referer; char *referer_offset = (char *)service_strstr((uint8_t *)referer_start, URL_SCHEME_MAX_LEN, (uint8_t *)URL_SCHEME_END_PATTERN, sizeof(URL_SCHEME_END_PATTERN)-1); if (referer_offset) { referer_offset += sizeof(URL_SCHEME_END_PATTERN)-1; } else return 0; referer_start = referer_offset; referer_len = strlen(referer_start); referer_path = strchr(referer_start, '/'); if (referer_path) { referer_path_len = strlen(referer_path); referer_len -= referer_path_len; } else { referer_path = "/"; referer_path_len = 1; } if (referer_start && referer_len > 0) { data = NULL; patterns[0].pattern = (uint8_t *)referer_start; patterns[0].patternSize = referer_len; patterns[1].pattern = (uint8_t *)referer_path; patterns[1].patternSize = referer_path_len; patterns[2].pattern = NULL; data = (HostUrlDetectorPattern *)mlmpMatchPatternUrl(matcher, patterns); if (data != NULL) { if (payload_found) *referredPayloadAppId = *payloadAppId; else payload_found = 1; *payloadAppId = data->payload_id; } } } return payload_found; } void getServerVendorVersion(const uint8_t *data, int len, char **version, char **vendor, RNAServiceSubtype **subtype) { const uint8_t *subname; const uint8_t *subver; int subname_len; int subver_len; const uint8_t *paren; const uint8_t *ver; const uint8_t *p; const uint8_t *end = data + len; RNAServiceSubtype *sub; int vendor_len; int version_len; char *tmp; ver = memchr(data, '/', len); if (ver) { version_len = 0; vendor_len = ver - data; ver++; subname = NULL; subname_len = 0; subver = NULL; paren = NULL; for (p=ver; *p && p < end; p++) { if (*p == '(') { subname = NULL; paren = p; } else if (*p == ')') { subname = NULL; paren = NULL; } /* some admins put tags in their http response lines. the anchors will cause problems for adaptive profiles in snort, so let's just get rid of them */ else if (*p == '<') break; else if (!paren) { if (*p == ' ' || *p == '\t') { if (subname && subname_len > 0 && subver && *subname) { sub = calloc(1, sizeof(*sub)); if (sub) { if ((tmp = malloc(subname_len + 1))) { memcpy(tmp, subname, subname_len); tmp[subname_len] = 0; sub->service = tmp; } else { _dpd.errMsg("getServerVendorVersion: " "Failed to allocate memory for service in sub\n"); } subver_len = p - subver; if (subver_len > 0 && *subver) { if ((tmp = malloc(subver_len + 1))) { memcpy(tmp, subver, subver_len); tmp[subver_len] = 0; sub->version = tmp; } else { _dpd.errMsg("getServerVendorVersion: " "Failed to allocate memory for version in sub\n"); } } sub->next = *subtype; *subtype = sub; } } subname = p + 1; subname_len = 0; subver = NULL; } else if (*p == '/' && subname) { if (version_len <= 0) version_len = subname - ver - 1; subname_len = p - subname; subver = p + 1; } } } if (subname && subname_len > 0 && subver && *subname) { sub = calloc(1, sizeof(*sub)); if (sub) { if ((tmp = malloc(subname_len + 1))) { memcpy(tmp, subname, subname_len); tmp[subname_len] = 0; sub->service = tmp; } else { _dpd.errMsg("getServerVendorVersion: " "Failed to allocate memory for service in sub\n"); } subver_len = p - subver; if (subver_len > 0 && *subver) { if ((tmp = malloc(subver_len + 1))) { memcpy(tmp, subver, subver_len); tmp[subver_len] = 0; sub->version = tmp; } else { _dpd.errMsg("getServerVendorVersion: " "Failed to allocate memory for version in sub\n"); } } sub->next = *subtype; *subtype = sub; } } if (version_len <= 0) version_len = p - ver; if (version_len >= MAX_VERSION_SIZE) version_len = MAX_VERSION_SIZE - 1; if (NULL != (*version = (char *)malloc(sizeof(char)*(version_len+1)))) { memcpy(*version, ver, version_len); *(*version+version_len) = '\0'; } } else { vendor_len = len; } if (vendor_len >= MAX_VERSION_SIZE) vendor_len = MAX_VERSION_SIZE - 1; if (NULL != (*vendor = (char *)malloc(sizeof(char)*(vendor_len+1)))) { memcpy(*vendor, data, vendor_len); *(*vendor+vendor_len) = '\0'; } } int webdav_found(HeaderMatchedPatterns *hmp) { // to check for webdav, look for one of the special methods int found = 0; if (hmp->headers[HTTP_ID_COPY].start > 0) found = 1; else if (hmp->headers[HTTP_ID_MOVE].start > 0) found = 1; else if (hmp->headers[HTTP_ID_LOCK].start > 0) found = 1; else if (hmp->headers[HTTP_ID_UNLOCK].start > 0) found = 1; else if (hmp->headers[HTTP_ID_MKCOL].start > 0) found = 1; else if (hmp->headers[HTTP_ID_PROPPATCH].start > 0) found = 1; else if (hmp->headers[HTTP_ID_PROPFIND].start > 0) found = 1; return found; } // Start of HTTP/2 detection logic. // // This is intended to simply detect the presence of HTTP version 2 as a // service protocol if it is seen (unencrypted) on non-std ports. That way, we // can notify Snort for future reference. this covers the "with prior // knowledge" case for HTTP/2 (i.e., the client knows the server supports // HTTP/2 and jumps right in with the preface). static CLIENT_APP_RETCODE http_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE http_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static int http_service_init(const InitServiceAPI * const init_api); static int http_service_validate(ServiceValidationArgs* args); static tAppRegistryEntry appIdRegistry[] = { {APP_ID_HTTP, 0} }; static const char HTTP2_PREFACE[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; #define HTTP2_PREFACE_LEN (sizeof(HTTP2_PREFACE)-1) #define HTTP2_PREFACE_MAXPOS (sizeof(HTTP2_PREFACE)-2) typedef struct { const u_int8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)HTTP2_PREFACE, sizeof(HTTP2_PREFACE)-1, 0, APP_ID_HTTP}, }; SF_SO_PUBLIC tRNAClientAppModule http_client_mod = { .name = "HTTP", .proto = IPPROTO_TCP, .init = &http_client_init, .validate = &http_client_validate, .minimum_matches = 1 }; static RNAServiceValidationPort pp[] = { {NULL, 0, 0} }; static tRNAServiceElement http_service_element = { .next = NULL, .validate = &http_service_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "http", .ref_count = 1, .current_ref_count = 1, }; tRNAServiceValidationModule http_service_mod = { "HTTP", &http_service_init, pp }; static CLIENT_APP_RETCODE http_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; if (appidStaticConfig->http2_detection_enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG, "registering patterns: %s: %d", (const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&http_client_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG, "registering appId: %d\n", appIdRegistry[j].appId); init_api->RegisterAppId(&http_client_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE http_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { http_client_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_HTTP, APP_ID_HTTP + GENERIC_APP_OFFSET, NULL); flowp->rnaClientState = RNA_STATE_FINISHED; http_service_mod.api->add_service(flowp, pkt, dir, &http_service_element, APP_ID_HTTP, NULL, NULL, NULL, NULL); flowp->rnaServiceState = RNA_STATE_FINISHED; setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED | APPID_SESSION_SERVICE_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); flowp->is_http2 = true; return CLIENT_APP_SUCCESS; } static int http_service_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG, "registering appId: %d\n", appIdRegistry[i].appId); init_api->RegisterAppId(&http_service_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int http_service_validate(ServiceValidationArgs* args) { return SERVICE_INPROCESS; } // End of HTTP/2 detection logic. snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_pop3.c0000644000175000017500000007646314241075420025617 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "detector_api.h" /*#define DEBUG_POP3 1 */ typedef struct _POP3_CLIENT_APP_CONFIG { int enabled; } POP3_CLIENT_APP_CONFIG; typedef enum { POP3_CLIENT_STATE_AUTH, // POP3 - AUTHORIZATION state POP3_CLIENT_STATE_TRANS, // POP3 - TRANSACTION state POP3_CLIENT_STATE_STLS_CMD // POP3 - AUTHORIZATION hybrid state (probable POP3S) } POP3ClientState; typedef struct _CLIENT_POP3_DATA { int auth; char *username; POP3ClientState state; int set_flags; int detected; int got_user; } ClientPOP3Data; static POP3_CLIENT_APP_CONFIG pop3_config; static CLIENT_APP_RETCODE pop3_ca_init(const InitClientAppAPI * const init_api, SF_LIST *config); static void pop3_ca_clean(const CleanClientAppAPI * const clean_api); static CLIENT_APP_RETCODE pop3_ca_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static tRNAClientAppModule client_app_mod = { .name = "POP3", .proto = IPPROTO_TCP, .init = &pop3_ca_init, .clean = &pop3_ca_clean, .validate = &pop3_ca_validate, .minimum_matches = 1, .provides_user = 1, }; typedef struct { const uint8_t *pattern; unsigned length; int eoc; } Client_App_Pattern; static const uint8_t APOP[] = "APOP "; static const uint8_t DELE[] = "DELE "; static const uint8_t LISTC[] = "LIST "; static const uint8_t LISTEOC[] = "LIST\x00d\x00a"; static const uint8_t LISTEOC2[] = "LIST\x00a"; static const uint8_t NOOP[] = "NOOP\x00d\x00a"; static const uint8_t NOOP2[] = "NOOP\x00a"; static const uint8_t QUIT[] = "QUIT\x00d\x00a"; static const uint8_t QUIT2[] = "QUIT\x00a"; static const uint8_t RETR[] = "RETR "; static const uint8_t STAT[] = "STAT\x00d\x00a" ; static const uint8_t STAT2[] = "STAT\x00a" ; static const uint8_t RSET[] = "RSET\x00d\x00a"; static const uint8_t RSET2[] = "RSET\x00a"; static const uint8_t TOP[] = "TOP "; static const uint8_t UIDL[] = "UIDL "; static const uint8_t UIDLEOC[] = "UIDL\x00d\x00a"; static const uint8_t UIDLEOC2[] = "UIDL\x00a"; static const uint8_t USER[] = "USER "; static const uint8_t PASS[] = "PASS "; static const uint8_t CAPA[] = "CAPA\x00d\x00a"; static const uint8_t CAPA2[] = "CAPA\x00a"; static const uint8_t AUTH[] = "AUTH "; static const uint8_t AUTHEOC[] = "AUTH\x00d\x00a"; static const uint8_t AUTHEOC2[] = "AUTH\x00a"; static const uint8_t AUTHEOC3[] = "AUTH \x00d\x00a"; static const uint8_t AUTHEOC4[] = "AUTH \x00a"; static const uint8_t STLSEOC[] = "STLS\x00d\x00a"; static const uint8_t STLSEOC2[] = "STLS\x00a"; static const uint8_t STLSEOC3[] = "STLS \x00d\x00a"; static const uint8_t STLSEOC4[] = "STLS \x00a"; typedef enum { /* order MUST correspond to that in the array, patterns[], below */ PATTERN_USER, PATTERN_PASS, PATTERN_APOP, PATTERN_AUTH, PATTERN_AUTHEOC, PATTERN_AUTHEOC2, PATTERN_AUTHEOC3, PATTERN_AUTHEOC4, PATTERN_STLSEOC, PATTERN_STLSEOC2, PATTERN_STLSEOC3, PATTERN_STLSEOC4, PATTERN_POP3_OTHER // always last } Client_App_Pattern_Index; static Client_App_Pattern patterns[] = { {USER, sizeof(USER)-1, 0}, {PASS, sizeof(PASS)-1, 0}, {APOP, sizeof(APOP)-1, 0}, {AUTH, sizeof(AUTH)-1, 0}, {AUTHEOC, sizeof(AUTHEOC)-1, 1}, {AUTHEOC2, sizeof(AUTHEOC2)-1, 1}, {AUTHEOC3, sizeof(AUTHEOC3)-1, 1}, {AUTHEOC4, sizeof(AUTHEOC4)-1, 1}, {STLSEOC, sizeof(STLSEOC)-1, 1}, {STLSEOC2, sizeof(STLSEOC2)-1, 1}, {STLSEOC3, sizeof(STLSEOC3)-1, 1}, {STLSEOC4, sizeof(STLSEOC4)-1, 1}, /* These are represented by index >= PATTERN_POP3_OTHER */ {DELE, sizeof(DELE)-1, 0}, {LISTC, sizeof(LISTC)-1, 0}, {LISTEOC, sizeof(LISTEOC)-1, 1}, {LISTEOC2, sizeof(LISTEOC2)-1, 1}, {NOOP, sizeof(NOOP)-1, 1}, {NOOP2, sizeof(NOOP2)-1, 1}, {QUIT, sizeof(QUIT)-1, 1}, {QUIT2, sizeof(QUIT2)-1, 1}, {RETR, sizeof(RETR)-1, 0}, {STAT, sizeof(STAT)-1, 1}, {STAT2, sizeof(STAT2)-1, 1}, {RSET, sizeof(RSET)-1, 1}, {RSET2, sizeof(RSET2)-1, 1}, {TOP, sizeof(TOP)-1, 0}, {UIDL, sizeof(UIDL)-1, 0}, {UIDLEOC, sizeof(UIDLEOC)-1, 1}, {UIDLEOC2, sizeof(UIDLEOC2)-1, 1}, {CAPA, sizeof(CAPA)-1, 1}, {CAPA2, sizeof(CAPA2)-1, 1}, }; static size_t longest_pattern; #define POP3_PORT 110 #define POP3_COUNT_THRESHOLD 3 #define POP3_OK "+OK" #define POP3_ERR "-ERR" #define POP3_TERM ".\x00D\x00A" typedef enum { POP3_STATE_CONNECT, POP3_STATE_RESPONSE, POP3_STATE_CONTINUE } POP3State; #define MAX_VERSION_SIZE 64 typedef struct _SERVICE_POP3_DATA { POP3State state; unsigned count; const char *vendor; char version[MAX_VERSION_SIZE]; RNAServiceSubtype *subtype; int error; } ServicePOP3Data; static int pop3_init(const InitServiceAPI * const init_api); static int pop3_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &pop3_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "pop3", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&pop3_validate, POP3_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; static tRNAServiceValidationModule service_mod = { .name = "POP3", .init = &pop3_init, .pp = pp, .provides_user = 1, }; typedef struct _POP3_DETECTOR_DATA { ClientPOP3Data client; ServicePOP3Data server; int need_continue; } POP3DetectorData; SF_SO_PUBLIC RNADetectorValidationModule pop3_detector_mod = { .service = &service_mod, .client = &client_app_mod, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_POP3, APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_CLIENT_USER}, {APP_ID_POP3S, APPINFO_FLAG_SERVICE_ADDITIONAL | APPINFO_FLAG_CLIENT_USER} }; static CLIENT_APP_RETCODE pop3_ca_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; void *cmd_matcher; if (!(cmd_matcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return CLIENT_APP_EINVALID; for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.searchAPI->search_instance_add_ex(cmd_matcher, (char *)patterns[i].pattern, patterns[i].length, &patterns[i], STR_SEARCH_CASE_INSENSITIVE ); if (patterns[i].length > longest_pattern) longest_pattern = patterns[i].length; } _dpd.searchAPI->search_instance_prep(cmd_matcher); AppIdAddGenericConfigItem(init_api->pAppidConfig, client_app_mod.name, cmd_matcher); pop3_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { pop3_config.enabled = atoi(item->value); } } } if (pop3_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering pattern: %s\n",(const char *)patterns[i].pattern); init_api->RegisterPatternNoCase(&pop3_ca_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, 0, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&pop3_ca_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static int pop3_init(const InitServiceAPI * const init_api) { init_api->RegisterPatternUser(&pop3_validate, IPPROTO_TCP, (uint8_t *)POP3_OK, sizeof(POP3_OK)-1, 0, "pop3", init_api->pAppidConfig); init_api->RegisterPatternUser(&pop3_validate, IPPROTO_TCP, (uint8_t *)POP3_ERR, sizeof(POP3_ERR)-1, 0, "pop3", init_api->pAppidConfig); unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&pop3_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return 0; } static void pop3_ca_clean(const CleanClientAppAPI * const clean_api) { void *cmd_matcher = AppIdFindGenericConfigItem(clean_api->pAppidConfig, client_app_mod.name); if (cmd_matcher) { _dpd.searchAPI->search_instance_free(cmd_matcher); cmd_matcher = NULL; } AppIdRemoveGenericConfigItem(clean_api->pAppidConfig, client_app_mod.name); } static int pop3_pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { Client_App_Pattern **pcmd = (Client_App_Pattern **)data; if (index) return 0; pcmd = (Client_App_Pattern **)data; *pcmd = id; return 1; } static void pop3_free_state(void *data) { POP3DetectorData *dd = (POP3DetectorData *)data; ClientPOP3Data *cd; ServicePOP3Data *sd; RNAServiceSubtype *sub; if (dd) { sd = &dd->server; while (sd->subtype) { sub = sd->subtype; sd->subtype = sub->next; if (sub->service) free((void *)sub->service); if (sub->version) free((void *)sub->version); free(sub); } cd = &dd->client; if (cd->username) free(cd->username); free(dd); } } static int pop3_check_line(const uint8_t * *data, const uint8_t *end) { /* Line in the form (textCRLF) */ for (; (*data)server; const uint8_t *begin = NULL; const uint8_t *end; const uint8_t *line_end; const uint8_t *p; const uint8_t *p2; const uint8_t *ver; const uint8_t *rel; const uint8_t *s; unsigned len; char *v; char *v_end; end = data + size; v_end = pd->version; v_end += MAX_VERSION_SIZE - 1; switch (pd->state) { case POP3_STATE_CONNECT: pd->state = POP3_STATE_RESPONSE; begin = data; case POP3_STATE_RESPONSE: if (!begin && data[0] == '+' && data[1] == ' ') { data += 2; if (pop3_check_line(&data, end)) return -1; if (data != end) return -1; return 0; } if (size < sizeof(POP3_ERR)) return -1; if (!strncmp((char *)data, POP3_OK, sizeof(POP3_OK)-1)) { data += sizeof(POP3_OK) - 1; pd->error = 0; } else if (!strncmp((char *)data, POP3_ERR, sizeof(POP3_ERR)-1)) { begin = NULL; data += sizeof(POP3_ERR) - 1; pd->error = 1; } else return -1; if (pop3_check_line(&data, end) < 0) return -1; if (dd->client.state == POP3_CLIENT_STATE_STLS_CMD) { if (pd->error) { /* We failed to transition to POP3S - fall back to normal POP3 state, AUTHORIZATION */ dd->client.state = POP3_CLIENT_STATE_AUTH; } else { setAppIdFlag(flowp, APPID_SESSION_ENCRYPTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); /* we are potentially overriding the APP_ID_POP3 assessment that was made earlier. */ client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_POP3S, APP_ID_POP3S, NULL); // sets APPID_SESSION_CLIENT_DETECTED } } else if (dd->client.username) // possible only with non-TLS authentication therefor: APP_ID_POP3 { if (pd->error) { service_mod.api->add_user(flowp, dd->client.username, APP_ID_POP3, 0); free(dd->client.username); dd->client.username = NULL; } else { if (dd->client.state == POP3_CLIENT_STATE_TRANS) { service_mod.api->add_user(flowp, dd->client.username, APP_ID_POP3, 1); free(dd->client.username); dd->client.username = NULL; dd->need_continue = 0; clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); if (dd->client.detected) setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); } else dd->client.got_user = 1; } } if (server && begin) { line_end = &data[-1]; len = line_end - begin; if ((p=service_strstr(begin, len, (unsigned char *)ven_cppop, sizeof(ven_cppop)-1))) { pd->vendor = ven_cppop; p += (sizeof(ven_cppop) - 1); if (*p == ' ') { p++; v = pd->version; for (; p < line_end && *p && *p != ']'; p++) { if (v < v_end) { *v = *p; v++; } } if (p < line_end && *p) *v = 0; else pd->version[0] = 0; } } else if ((p=service_strstr(begin, len, (unsigned char *)ven_cc, sizeof(ven_cc)-1))) { pd->vendor = ven_cc; p += (sizeof(ven_cc) - 1); if (line_end-p >= (int)sizeof(ver_cc)-1 && memcmp(p, ver_cc, sizeof(ver_cc)-1) == 0) { p += sizeof(ver_cc) - 1; v = pd->version; for (; p < line_end && *p && *p != ' '; p++) { if (v < v_end) { *v = *p; v++; } } if (p < line_end && *p) *v = 0; else pd->version[0] = 0; } } else if (service_strstr(begin, len, (unsigned char *)ven_im, sizeof(ven_im)-1)) pd->vendor = ven_im; else if ((p=service_strstr(begin, len, (unsigned char *)ven_po, sizeof(ven_po)-1))) { RNAServiceSubtype *sub; pd->vendor = ven_po; p += (sizeof(ven_po) - 1); if (line_end-p < (int)sizeof(ver_po)-1 || memcmp(p, ver_po, sizeof(ver_po)-1) != 0) goto ven_ver_done; p += sizeof(ver_po) - 1; ver = p; for (; p < line_end && *p && *p != ' '; p++); if (p == ver || p >= line_end || !(*p)) goto ven_ver_done; if (line_end-p < (int)sizeof(ver_po2)-1 || memcmp(p, ver_po2, sizeof(ver_po2)-1) != 0) { /* Does not have release */ v = pd->version; for (; ver < p && *ver; ver++) { if (v < v_end) { *v = *ver; v++; } else break; } *v = 0; goto ven_ver_done; } /* Move past release and look for number followed by a space */ p2 = p + sizeof(ver_po2) - 1; rel = p2; for (; p2 < line_end && *p2 && *p2 != ' '; p2++); if (p2 >= line_end || p2 == rel || !(*p2)) { v = pd->version; for (; ver < p && *ver; ver++) { if (v < v_end) { *v = *ver; v++; } else break; } *v = 0; goto ven_ver_done; } v = pd->version; for (; ver < p2 && *ver; ver++) { if (v < v_end) { *v = *ver; v++; } else break; } *v = 0; if (line_end-p2 < (int)sizeof(sub_po)-1 || memcmp(p2, sub_po, sizeof(sub_po)-1) != 0) goto ven_ver_done; s = p2 + (sizeof(sub_po) - 1); for (p=s; p < line_end && *p && *p != ' '; p++); if (p == s || p >= line_end || !(*p)) goto ven_ver_done; sub = calloc(1, sizeof(RNAServiceSubtype)); if (sub) { unsigned sub_len; sub_len = p - s; sub->service = malloc(sub_len+1); if (sub->service) { memcpy((char *)sub->service, s, sub_len); ((char *)sub->service)[sub_len] = 0; sub->next = pd->subtype; pd->subtype = sub; if (line_end-p > (int)sizeof(subver_po)-1 && memcmp(p, subver_po, sizeof(subver_po)-1) == 0) { s = p + (sizeof(subver_po) - 1); for (p=s; p < line_end && *p && *p != ' '; p++); if (p != s && p < line_end && *p) { sub_len = p - s; sub->version = malloc(sub_len+1); if (sub->version) { memcpy((char *)sub->version, s, sub_len); ((char *)sub->version)[sub_len] = 0; } else { _dpd.errMsg("pop3_server_validate: " "Failed to allocate memory for version in sub\n"); free(sub); } } } } else free(sub); } } ven_ver_done:; } if (data >= end) { pd->count++; return 0; } pd->state = POP3_STATE_CONTINUE; /* Fall through */ case POP3_STATE_CONTINUE: while (data < end) { if ((end-data) == (sizeof(POP3_TERM)-1) && !strncmp((char *)data, POP3_TERM, sizeof(POP3_TERM)-1)) { pd->count++; pd->state = POP3_STATE_RESPONSE; return 0; } if (pop3_check_line(&data, end) < 0) return -1; } return 0; } return 0; } static CLIENT_APP_RETCODE pop3_ca_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { const uint8_t *s = data; const uint8_t *end = (data + size); unsigned length; Client_App_Pattern *cmd; POP3DetectorData *dd; ClientPOP3Data *fd; if (!size) return CLIENT_APP_INPROCESS; #ifdef APP_ID_USES_REASSEMBLED pop3_detector_mod.streamAPI->response_flush_stream(pkt); #endif dd = pop3_detector_mod.api->data_get(flowp, pop3_detector_mod.flow_data_index); if (!dd) { if ((dd = calloc(1, sizeof(*dd))) == NULL) return CLIENT_APP_ENOMEM; if (pop3_detector_mod.api->data_add(flowp, dd, pop3_detector_mod.flow_data_index, &pop3_free_state)) { free(dd); return CLIENT_APP_ENOMEM; } dd->server.state = POP3_STATE_CONNECT; fd = &dd->client; fd->state = POP3_CLIENT_STATE_AUTH; } else fd = &dd->client; if (!fd->set_flags) { dd->need_continue = 1; fd->set_flags = 1; setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } if (dir == APP_ID_FROM_RESPONDER) { #ifdef DEBUG_POP3 _dpd.debugMsg(DEBUG_LOG,"%p Calling server\n",flowp); DumpHex(SF_DEBUG_FILE, data, size); #endif if (pop3_server_validate(dd, data, size, flowp, 0, pkt, dir, pConfig)) clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); return CLIENT_APP_INPROCESS; } #ifdef DEBUG_POP3 _dpd.debugMsg(DEBUG_LOG,"%p Client\n",flowp); DumpHex(SF_DEBUG_FILE, data, size); #endif while ((length = (end - s))) { unsigned pattern_index; void *cmd_matcher = AppIdFindGenericConfigItem((tAppIdConfig *)pConfig, client_app_mod.name); cmd = NULL; _dpd.searchAPI->search_instance_find_all(cmd_matcher, (char *)s, (length > longest_pattern ? longest_pattern:length), 0, &pop3_pattern_match, (void*)&cmd); if (!cmd) { dd->need_continue = 0; setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); return CLIENT_APP_SUCCESS; } s += cmd->length; pattern_index = cmd - patterns; // diff of ptr into array and its base addr is the corresponding index. switch (fd->state) { case POP3_CLIENT_STATE_STLS_CMD: /* We failed to transition to POP3S - fall back to normal POP3 AUTHORIZATION state */ fd->state = POP3_CLIENT_STATE_AUTH; // fall through case POP3_CLIENT_STATE_AUTH: switch (pattern_index) { case PATTERN_STLSEOC: case PATTERN_STLSEOC2: case PATTERN_STLSEOC3: case PATTERN_STLSEOC4: { /* If the STLS command succeeds we will be in a TLS negotiation state. Wait for the "+OK" from the server using this STLS hybrid state. */ fd->state = POP3_CLIENT_STATE_STLS_CMD; /* skip extra CRLFs */ for (; (s < end) && (*s == '\r' || *s == '\n'); s++); } break; case PATTERN_APOP: case PATTERN_USER: { char username[((255 - (sizeof(USER) - 1)) - 2) + 1]; char *p = username; char *p_end = p + sizeof(username) - 1; int found_tick = 0; for (; s < end && p < p_end; s++) { if (isalnum(*s) || *s == '.' || *s == '@' || *s == '-' || *s == '_') { if (!found_tick) { *p = *s; p++; } } else if (*s == '`') found_tick = 1; else if (*s == '\r' || *s == '\n' || *s == ' ') // test for space for APOP case { *p = 0; if (username[0]) { if (fd->username) free(fd->username); fd->username = strdup(username); if (!fd->username) _dpd.errMsg("failed to allocate user name"); } break; } else break; } if (pattern_index == PATTERN_APOP) { /* the APOP command contains the user AND the equivalent of a password. */ fd->state = POP3_CLIENT_STATE_TRANS; } for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); } break; case PATTERN_AUTH: /* the AUTH command, containing a parameter implies non-TLS security negotiation */ fd->state = POP3_CLIENT_STATE_TRANS; // look ahead for normal POP3 commands for (; (s < end) && *s != '\r' && *s != '\n'; s++); // having skipped to the end of the line, fall through for the empty-line skip case PATTERN_AUTHEOC: // used with subsequent CAPA; no state change; case PATTERN_AUTHEOC2: case PATTERN_AUTHEOC3: // AUTH with nothing after, Mircosoft ext., is query-only behavior; no state change; case PATTERN_AUTHEOC4: for (; (s < end) && (*s == '\r' || *s == '\n'); s++); break; case PATTERN_PASS: if (fd->got_user) { fd->state = POP3_CLIENT_STATE_TRANS; for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); break; } // fall through because we are not changing to TRANSACTION state, yet default: { if (!cmd->eoc) for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); } break; } // end of switch(pattern_index) break; case POP3_CLIENT_STATE_TRANS: if (pattern_index >= PATTERN_POP3_OTHER) { /* We stayed in non-secure mode and received a TRANSACTION-state command: POP3 found */ client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_POP3, APP_ID_POP3, NULL); // sets APPID_SESSION_CLIENT_DETECTED fd->detected = 1; } else { ; // ignore AUTHORIZATION-state commands while in TRANSACTION state } if (!cmd->eoc) for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); break; } } return CLIENT_APP_INPROCESS; } static int pop3_validate(ServiceValidationArgs* args) { POP3DetectorData *dd; ServicePOP3Data *pd; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; #ifdef APP_ID_USES_REASSEMBLED pop3_detector_mod.streamAPI->response_flush_stream(pkt); #endif if (dir != APP_ID_FROM_RESPONDER) goto inprocess; #ifdef DEBUG_POP3 _dpd.debugMsg(DEBUG_LOG,"%p Dir %d\n",flowp, dir); DumpHex(SF_DEBUG_FILE, data, size); #endif dd = pop3_detector_mod.api->data_get(flowp, pop3_detector_mod.flow_data_index); if (!dd) { dd = calloc(1, sizeof(*dd)); if (dd == NULL) return SERVICE_ENOMEM; if (pop3_detector_mod.api->data_add(flowp, dd, pop3_detector_mod.flow_data_index, &pop3_free_state)) { free(dd); return SERVICE_ENOMEM; } dd->client.state = POP3_CLIENT_STATE_AUTH; pd = &dd->server; pd->state = POP3_STATE_CONNECT; } else pd = &dd->server; if (dd->need_continue) setAppIdFlag(flowp, APPID_SESSION_CONTINUE); else { clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) return SERVICE_SUCCESS; } if (!pop3_server_validate(dd, data, size, flowp, 1, pkt, dir, args->pConfig)) { if (pd->count >= POP3_COUNT_THRESHOLD && !getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { service_mod.api->add_service_consume_subtype(flowp, pkt, dir, &svc_element, dd->client.state == POP3_CLIENT_STATE_STLS_CMD ? APP_ID_POP3S : APP_ID_POP3, pd->vendor, pd->version[0] ? pd->version:NULL, pd->subtype, NULL); pd->subtype = NULL; return SERVICE_SUCCESS; } } else if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { service_mod.api->fail_service(flowp, pkt, dir, &svc_element, service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } else { clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_SUCCESS; } inprocess:; service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_cip.c0000644000175000017500000003672414241075405025510 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** * @author RA/Cisco */ /* Description: AppID detector for CIP protocol. This is used to match against Lua-C API registrations. */ #include // For calloc #include "appInfoTable.h" // For appGetSnortIdFromAppId #include "cip_common.h" // For event data. #include "flow.h" // For tAppIdData #include "preprocids.h" // For PP_APP_ID #include "session_api.h" // For SessionAPI #include "stream_api.h" // For StreamAPI #include "sf_dynamic_preprocessor.h" // For _dpd #include "sf_snort_packet.h" // For SFSnortPacket #include "detector_cip.h" #define CIP_DUMMY_PORT 25965 #define ENIP_DUMMY_PORT 49548 static const char svc_name[] = "cip"; CipPatternLists cipPatternLists; static CLIENT_APP_RETCODE cip_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static int cip_service_init(const InitServiceAPI * const init_api); static CLIENT_APP_RETCODE cip_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); MakeRNAServiceValidationPrototype(cip_service_validate); static tAppRegistryEntry appIdClientRegistry[] = { {APP_ID_ENIP, APPINFO_FLAG_CLIENT_ADDITIONAL}, {APP_ID_CIP, APPINFO_FLAG_CLIENT_ADDITIONAL}, }; static tAppRegistryEntry appIdServiceRegistry[] = { {APP_ID_ENIP, APPINFO_FLAG_SERVICE_ADDITIONAL}, {APP_ID_CIP, APPINFO_FLAG_SERVICE_ADDITIONAL}, }; tRNAClientAppModule cip_client_mod = { .name = "CIP", .proto = IPPROTO_TCP, .init = &cip_client_init, .validate = &cip_client_validate, .minimum_matches = 1, .provides_user = 0, }; tRNAClientAppModule enip_client_mod = { .name = "ENIP", .proto = IPPROTO_TCP, .init = &cip_client_init, .validate = &cip_client_validate, .minimum_matches = 1, .provides_user = 0, }; static RNAServiceValidationPort cip_pp[] = { {&cip_service_validate, CIP_DUMMY_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; static RNAServiceValidationPort enip_pp[] = { {&cip_service_validate, ENIP_DUMMY_PORT, IPPROTO_TCP}, {NULL, 0, 0} }; SF_SO_PUBLIC tRNAServiceValidationModule cip_service_mod = { svc_name, &cip_service_init, cip_pp }; SF_SO_PUBLIC tRNAServiceValidationModule enip_service_mod = { svc_name, &cip_service_init, enip_pp }; static tRNAServiceElement svc_element = { .next = NULL, .validate = &cip_service_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "cip", .ref_count = 1, .current_ref_count = 1, }; static CLIENT_APP_RETCODE cip_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned j; for (j=0; j < sizeof(appIdClientRegistry)/sizeof(*appIdClientRegistry); j++) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "registering appId: %d\n",appIdClientRegistry[j].appId);); init_api->RegisterAppId(&cip_client_validate, appIdClientRegistry[j].appId, appIdClientRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static int cip_service_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdServiceRegistry)/sizeof(*appIdServiceRegistry); i++) { DEBUG_WRAP(DebugMessage(DEBUG_APPID, "registering appId: %d\n",appIdServiceRegistry[i].appId);); init_api->RegisterAppId(&cip_service_validate, appIdServiceRegistry[i].appId, appIdServiceRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static CLIENT_APP_RETCODE cip_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { return CLIENT_APP_INPROCESS; } MakeRNAServiceValidationPrototype(cip_service_validate) { return SERVICE_INPROCESS; } int CipAddEnipCommand(tAppId app_id, uint16_t command_id) { EnipCommandList *pattern = calloc(1, sizeof(EnipCommandList)); if (!pattern) { return -1; } pattern->data.app_id = app_id; pattern->data.command_id = command_id; pattern->next = cipPatternLists.enip_command_list; cipPatternLists.enip_command_list = pattern; return 0; } int CipAddPath(tAppId app_id, uint32_t class_id, uint8_t service_id) { CipPathList *pattern = calloc(1, sizeof(CipPathList)); if (!pattern) { return -1; } pattern->data.app_id = app_id; pattern->data.class_id = class_id; pattern->data.service_id = service_id; pattern->next = cipPatternLists.path_list; cipPatternLists.path_list = pattern; return 0; } int CipAddSetAttribute(tAppId app_id, uint32_t class_id, bool is_class_instance, uint32_t attribute_id) { CipSetAttributeList *pattern = calloc(1, sizeof(CipSetAttributeList)); if (!pattern) { return -1; } pattern->data.app_id = app_id; pattern->data.class_id = class_id; pattern->data.is_class_instance = is_class_instance; pattern->data.attribute_id = attribute_id; pattern->next = cipPatternLists.set_attribute_list; cipPatternLists.set_attribute_list = pattern; return 0; } int CipAddConnectionClass(tAppId app_id, uint32_t class_id) { CipConnectionClassList *pattern = calloc(1, sizeof(CipConnectionClassList)); if (!pattern) { return -1; } pattern->data.app_id = app_id; pattern->data.class_id = class_id; pattern->next = cipPatternLists.connection_list; cipPatternLists.connection_list = pattern; return 0; } int CipAddExtendedSymbolService(tAppId app_id, uint8_t service_id) { CipServiceList *pattern = calloc(1, sizeof(CipServiceList)); if (!pattern) { return -1; } pattern->data.app_id = app_id; pattern->data.service_id = service_id; pattern->next = cipPatternLists.symbol_list; cipPatternLists.symbol_list = pattern; return 0; } int CipAddService(tAppId app_id, uint8_t service_id) { CipServiceList *pattern = calloc(1, sizeof(CipServiceList)); if (!pattern) { return -1; } pattern->data.app_id = app_id; pattern->data.service_id = service_id; pattern->next = cipPatternLists.service_list; cipPatternLists.service_list = pattern; return 0; } static tAppId MatchEnipCommand(const EnipCommandList *enip_command_list, const CipEventData *event_data) { tAppId found_app_id = APP_ID_ENIP; while (enip_command_list) { if (event_data->enip_command_id == enip_command_list->data.command_id) { found_app_id = enip_command_list->data.app_id; break; } enip_command_list = enip_command_list->next; } return found_app_id; } static tAppId MatchCipService(const CipServiceList *service_list, const CipEventData *event_data) { tAppId found_app_id = APP_ID_CIP_UNKNOWN; while (service_list) { if (event_data->service_id == service_list->data.service_id) { found_app_id = service_list->data.app_id; break; } service_list = service_list->next; } return found_app_id; } static tAppId MatchCipPath(const CipPathList *path_list, const CipEventData *event_data) { tAppId found_app_id = APP_ID_CIP_UNKNOWN; while (path_list) { if ((event_data->class_id == path_list->data.class_id) && (event_data->service_id == path_list->data.service_id)) { found_app_id = path_list->data.app_id; break; } path_list = path_list->next; } return found_app_id; } static tAppId MatchCipSetAttribute(const CipSetAttributeList *set_attribute_list, const CipEventData *event_data) { tAppId found_app_id = APP_ID_CIP_UNKNOWN; bool is_class_instance = (event_data->instance_id == 0); while (set_attribute_list) { if ((event_data->class_id == set_attribute_list->data.class_id) && (is_class_instance == set_attribute_list->data.is_class_instance) && (event_data->attribute_id == set_attribute_list->data.attribute_id)) { found_app_id = set_attribute_list->data.app_id; break; } set_attribute_list = set_attribute_list->next; } return found_app_id; } static tAppId MatchCipConnection(const CipConnectionClassList *connection_list, const CipEventData *event_data) { tAppId found_app_id = APP_ID_CIP_UNKNOWN; while (connection_list) { if (event_data->class_id == connection_list->data.class_id) { found_app_id = connection_list->data.app_id; break; } connection_list = connection_list->next; } return found_app_id; } static tAppId GetCipPayloadId(const CipPatternLists *cip_pattern_lists, const CipEventData *event_data) { tAppId found_app_id = APP_ID_CIP_UNKNOWN; // Look up the appid. switch (event_data->type) { case CIP_DATA_TYPE_PATH_CLASS: { found_app_id = MatchCipPath(cip_pattern_lists->path_list, event_data); if (found_app_id == APP_ID_CIP_UNKNOWN) { found_app_id = MatchCipService(cip_pattern_lists->service_list, event_data); } } break; case CIP_DATA_TYPE_PATH_EXT_SYMBOL: { found_app_id = MatchCipService(cip_pattern_lists->symbol_list, event_data); if (found_app_id == APP_ID_CIP_UNKNOWN) { found_app_id = MatchCipService(cip_pattern_lists->service_list, event_data); } } break; case CIP_DATA_TYPE_SET_ATTRIBUTE: { found_app_id = MatchCipSetAttribute(cip_pattern_lists->set_attribute_list, event_data); if (found_app_id == APP_ID_CIP_UNKNOWN) { found_app_id = MatchCipService(cip_pattern_lists->symbol_list, event_data); if (found_app_id == APP_ID_CIP_UNKNOWN) { found_app_id = MatchCipService(cip_pattern_lists->service_list, event_data); } } } break; case CIP_DATA_TYPE_CONNECTION: { found_app_id = MatchCipConnection(cip_pattern_lists->connection_list, event_data); } break; case CIP_DATA_TYPE_IMPLICIT: { found_app_id = MatchCipConnection(cip_pattern_lists->connection_list, event_data); } break; case CIP_DATA_TYPE_OTHER: { found_app_id = APP_ID_CIP_UNKNOWN; } break; case CIP_DATA_TYPE_MALFORMED: { found_app_id = APP_ID_CIP_MALFORMED; } break; case CIP_DATA_TYPE_ENIP_COMMAND: { found_app_id = MatchEnipCommand(cip_pattern_lists->enip_command_list, event_data); } break; default: // Will not happen. break; } return found_app_id; } void CipSessionSnortCallback(void *ssnptr, ServiceEventType eventType, void *data) { tAppIdData *flowp = NULL; CipEventData *event_data = (CipEventData *)data; const SFSnortPacket *p = event_data->snort_packet; int direction; if (!p) { _dpd.errMsg("Missing packet: CipSessionSnortCallback\n"); return; } if (p->stream_session) { flowp = getAppIdData(p->stream_session); } if (!flowp) { _dpd.errMsg("Missing session: CipSessionSnortCallback\n"); return; } if (appidStaticConfig->multipayload_max_packets && flowp->session_packet_count > appidStaticConfig->multipayload_max_packets) return; tAppId found_app_id = GetCipPayloadId(&cipPatternLists, event_data); // Set CIP app and payload id. direction = (_dpd.sessionAPI->get_packet_direction((SFSnortPacket *)p) & FLAG_FROM_CLIENT) ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; cip_service_mod.api->add_service(flowp, p, direction, &svc_element, APP_ID_CIP, NULL, NULL, NULL, NULL); cip_service_mod.api->add_multipayload(flowp, found_app_id); } static int FreeEnipCommandList(EnipCommandList* enip_command_list) { int number_enip_rules = 0; EnipCommandList* command_node; for (command_node = enip_command_list; command_node != NULL; command_node = enip_command_list) { enip_command_list = command_node->next; free(command_node); number_enip_rules++; } return number_enip_rules; } static int FreeCipPathList(CipPathList* path_list) { int number_path_rules = 0; CipPathList* path_node; for (path_node = path_list; path_node != NULL; path_node = path_list) { path_list = path_node->next; free(path_node); number_path_rules++; } return number_path_rules; } static int FreeCipSetAttributeList(CipSetAttributeList* set_attribute_list) { int number_set_attribute_rules = 0; CipSetAttributeList* set_attribute_node; for (set_attribute_node = set_attribute_list; set_attribute_node != NULL; set_attribute_node = set_attribute_list) { set_attribute_list = set_attribute_node->next; free(set_attribute_node); number_set_attribute_rules++; } return number_set_attribute_rules; } static int FreeCipConnectionClassList(CipConnectionClassList* connection_list) { int number_connection_rules = 0; CipConnectionClassList* connection_node; for (connection_node = connection_list; connection_node != NULL; connection_node = connection_list) { connection_list = connection_node->next; free(connection_node); number_connection_rules++; } return number_connection_rules; } static int FreeCipServiceList(CipServiceList* service_list) { int number_service_rules = 0; CipServiceList* symbol_node; for (symbol_node = service_list; symbol_node != NULL; symbol_node = service_list) { service_list = symbol_node->next; free(symbol_node); number_service_rules++; } return number_service_rules; } void CipClean(void) { FreeEnipCommandList(cipPatternLists.enip_command_list); cipPatternLists.enip_command_list = NULL; FreeCipPathList(cipPatternLists.path_list); cipPatternLists.path_list = NULL; FreeCipSetAttributeList(cipPatternLists.set_attribute_list); cipPatternLists.set_attribute_list = NULL; FreeCipConnectionClassList(cipPatternLists.connection_list); cipPatternLists.connection_list = NULL; FreeCipServiceList(cipPatternLists.symbol_list); cipPatternLists.symbol_list = NULL; FreeCipServiceList(cipPatternLists.service_list); cipPatternLists.service_list = NULL; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_imap.c0000644000175000017500000010232014241075414025645 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "str_search.h" #include "appIdApi.h" #include "appInfoTable.h" #include "detector_api.h" #include "appIdConfig.h" /*#define DEBUG_IMAP_DETECTOR 1 */ #define IMAP_USER_NAME_MAX_LEN 32 #define IMAP_TAG_MAX_LEN 6 #define OK_LOGIN " LOGIN Ok." #define NO_LOGIN " Login failed." typedef struct _CLIENT_APP_CONFIG { int enabled; } CLIENT_APP_CONFIG; typedef enum { IMAP_CLIENT_STATE_NON_AUTH, // IMAP - Non-Authenticated state IMAP_CLIENT_STATE_AUTH, // IMAP - Authenticated state IMAP_CLIENT_STATE_AUTHENTICATE_CMD, // IMAP - authentication-in-progress state IMAP_CLIENT_STATE_STARTTLS_CMD, // IMAP - authentication-in-progress state (probable IMAPS) } IMAPClientState; typedef struct _CLIENT_APP_DATA { IMAPClientState state; unsigned count; int detected; int got_user; int auth; int set_flags; char username[IMAP_USER_NAME_MAX_LEN+1]; char imapCmdTag[IMAP_TAG_MAX_LEN+1]; } ClientAppData; #define MIN_CMDS 3 static CLIENT_APP_CONFIG ca_config; static CLIENT_APP_RETCODE init(const InitClientAppAPI * const init_api, SF_LIST *config); static void clean(const CleanClientAppAPI * const clean_api); static CLIENT_APP_RETCODE validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static tRNAClientAppModule client_app_mod = { .name = "IMAP", .proto = IPPROTO_TCP, .init = &init, .clean = &clean, .validate = &validate, .minimum_matches = 1, .provides_user = 1, }; typedef struct { const uint8_t *pattern; unsigned length; int eoc; } Client_App_Pattern; static const uint8_t CAPA[] = "CAPABILITY\x00d\x00a"; static const uint8_t CAPA2[] = "CAPABILITY\x00a"; static const uint8_t NOOP[] = "NOOP\x00d\x00a"; static const uint8_t NOOP2[] = "NOOP\x00a"; static const uint8_t LOGOUT[] = "LOGOUT\x00d\x00a"; static const uint8_t LOGOUT2[] = "LOGOUT\x00a"; static const uint8_t AUTHENTICATE[] = "AUTHENTICATE "; static const uint8_t LOGIN[] = "LOGIN "; static const uint8_t SELECT[] = "SELECT "; /*static const uint8_t EXAMINE[] = "EXAMINE "; */ static const uint8_t CREATE[] = "CREATE "; static const uint8_t DELETE[] = "DELETE "; static const uint8_t RENAME[] = "RENAME "; static const uint8_t SUBSCRIBE[] = "SUBSCRIBE "; static const uint8_t UNSUBSCRIBE[] = "UNSUBSCRIBE "; static const uint8_t LISTC[] = "LIST "; static const uint8_t LSUB[] = "LSUB "; static const uint8_t APPEND[] = "APPEND "; static const uint8_t CHECK[] = "CHECK\x00d\x00a"; static const uint8_t CHECK2[] = "CHECK\x00a"; static const uint8_t CLOSE[] = "CLOSE\x00d\x00a"; static const uint8_t CLOSE2[] = "CLOSE\x00a"; static const uint8_t EXPUNGE[] = "EXPUNGE\x00d\x00a"; static const uint8_t EXPUNGE2[] = "EXPUNGE\x00a"; static const uint8_t SEARCH[] = "SEARCH "; static const uint8_t FETCH[] = "FETCH "; static const uint8_t PARTIAL[] = "PARTIAL "; static const uint8_t STORE[] = "STORE "; static const uint8_t COPY[] = "COPY "; static const uint8_t UID[] = "UID "; static const uint8_t STARTTLS[] = "STARTTLS\x00d\x00a"; static const uint8_t STARTTLS2[] = "STARTTLS\x00a"; typedef enum { /* order MUST correspond to that in the array, patterns[], below */ PATTERN_LOGIN, PATTERN_AUTHENTICATE, PATTERN_STARTTLS, PATTERN_STARTTLS2, PATTERN_IMAP_OTHER // always last } Client_App_Pattern_Index; static Client_App_Pattern patterns[] = { {LOGIN, sizeof(LOGIN)-1, 0}, {AUTHENTICATE, sizeof(AUTHENTICATE)-1, 0}, {STARTTLS, sizeof(STARTTLS)-1, 1}, {STARTTLS2, sizeof(STARTTLS2)-1, 1}, /* These are represented by index >= PATTERN_IMAP_OTHER */ {CAPA, sizeof(CAPA)-1, 1}, {CAPA2, sizeof(CAPA2)-1, 1}, {NOOP, sizeof(NOOP)-1, 1}, {NOOP2, sizeof(NOOP2)-1, 1}, {LOGOUT, sizeof(LOGOUT)-1, 1}, {LOGOUT2, sizeof(LOGOUT2)-1, 1}, {SELECT, sizeof(SELECT)-1, 0}, {CREATE, sizeof(CREATE)-1, 0}, {DELETE, sizeof(DELETE)-1, 0}, {RENAME, sizeof(RENAME)-1, 0}, {SUBSCRIBE, sizeof(SUBSCRIBE)-1, 0}, {UNSUBSCRIBE, sizeof(UNSUBSCRIBE)-1, 0}, {LISTC, sizeof(LISTC)-1, 0}, {LSUB, sizeof(LSUB)-1, 0}, {APPEND, sizeof(APPEND)-1, 0}, {CHECK, sizeof(CHECK)-1, 1}, {CHECK2, sizeof(CHECK2)-1, 1}, {CLOSE, sizeof(CLOSE)-1, 1}, {CLOSE2, sizeof(CLOSE2)-1, 1}, {EXPUNGE, sizeof(EXPUNGE)-1, 1}, {EXPUNGE2, sizeof(EXPUNGE2)-1, 1}, {SEARCH, sizeof(SEARCH)-1, 0}, {FETCH, sizeof(FETCH)-1, 0}, {PARTIAL, sizeof(PARTIAL)-1, 0}, {STORE, sizeof(STORE)-1, 0}, {COPY, sizeof(COPY)-1, 0}, {UID, sizeof(UID)-1, 0}, }; static size_t longest_pattern; #define IMAP_PORT 143 #define IMAP_COUNT_THRESHOLD 1 #define OK "OK" #define BAD "BAD" #define NO "NO" #define IMAP_FLAG_ALNUM 0x01 #define IMAP_FLAG_FIRST_PACKET 0x02 #define IMAP_FLAG_RESULT_OK 0x04 #define IMAP_FLAG_RESULT_NO 0x08 #define IMAP_FLAG_RESULT_BAD 0x10 #define IMAP_FLAG_RESULT_ALL (IMAP_FLAG_RESULT_OK | IMAP_FLAG_RESULT_NO | IMAP_FLAG_RESULT_BAD) #define IMAP_MAX_BANNER 192 typedef enum { IMAP_STATE_BEGIN, IMAP_STATE_BANNER_SPACE, IMAP_STATE_BANNER_OK, IMAP_STATE_BANNER_WHITE_SPACE, IMAP_STATE_BANNER, IMAP_STATE_MID_LINE, IMAP_STATE_MID_ALNUM, IMAP_STATE_ALNUM_CODE, IMAP_STATE_ALNUM_CODE_TERM, IMAP_STATE_MID_OK, IMAP_STATE_MID_NO, IMAP_STATE_MID_BAD, IMAP_STATE_MID_TERM, IMAP_STATE_MID_OK_LOGIN, IMAP_STATE_MID_NO_LOGIN, IMAP_STATE_ALNUM_TAG } IMAPState; typedef struct _SERVICE_IMAP_DATA { IMAPState state; unsigned pos; unsigned flags; unsigned count; unsigned parens; char tagValue[IMAP_TAG_MAX_LEN+1]; #ifdef DEBUG_IMAP_DETECTOR IMAPState last_state; #endif } ServiceIMAPData; static int imap_init(const InitServiceAPI * const init_api); static int imap_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &imap_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "imap", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&imap_validate, IMAP_PORT, IPPROTO_TCP}, {&imap_validate, 220, IPPROTO_TCP}, {NULL, 0, 0} }; static tRNAServiceValidationModule service_mod = { .name = "IMAP", .init = &imap_init, .pp = pp, .provides_user = 1, }; #define IMAP_PATTERN "* OK" typedef struct _DETECTOR_DATA { ClientAppData client; ServiceIMAPData server; int need_continue; } DetectorData; SF_SO_PUBLIC RNADetectorValidationModule imap_detector_mod = { .service = &service_mod, .client = &client_app_mod, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_IMAP, APPINFO_FLAG_CLIENT_USER}, {APP_ID_IMAPS, APPINFO_FLAG_CLIENT_USER} }; static CLIENT_APP_RETCODE init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; void *cmd_matcher; if (!(cmd_matcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return CLIENT_APP_EINVALID; for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.searchAPI->search_instance_add_ex(cmd_matcher, (char *)patterns[i].pattern, patterns[i].length, &patterns[i], STR_SEARCH_CASE_INSENSITIVE); if (patterns[i].length > longest_pattern) longest_pattern = patterns[i].length; } _dpd.searchAPI->search_instance_prep(cmd_matcher); AppIdAddGenericConfigItem(init_api->pAppidConfig, client_app_mod.name, cmd_matcher); ca_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { ca_config.enabled = atoi(item->value); } } } if (ca_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering pattern: %s\n",(const char *)patterns[i].pattern); init_api->RegisterPatternNoCase(&validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, -1, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static int imap_init(const InitServiceAPI * const init_api) { init_api->RegisterPatternUser(&imap_validate, IPPROTO_TCP, (uint8_t *)IMAP_PATTERN, sizeof(IMAP_PATTERN)-1, 0, "imap", init_api->pAppidConfig); unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&imap_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return 0; } static void clean(const CleanClientAppAPI * const clean_api) { void *cmd_matcher = AppIdFindGenericConfigItem(clean_api->pAppidConfig, client_app_mod.name); if (cmd_matcher) { _dpd.searchAPI->search_instance_free(cmd_matcher); cmd_matcher = NULL; } AppIdRemoveGenericConfigItem(clean_api->pAppidConfig, client_app_mod.name); } static int pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { Client_App_Pattern **pcmd = (Client_App_Pattern **)data; if (index) return 0; pcmd = (Client_App_Pattern **)data; *pcmd = id; return 1; } inline static int isImapTagChar(uint8_t tag) { /* Per RFC 3501 */ /* tag char's cannot consist of ", %, { */ if ((tag == 0x7B) || (tag == 0x22) || (tag == 0x25)) return 0; /* Alpha Numeric's */ if (isalnum(tag) /* valid tag chars: 0-9, A-Z, a-z */ || (tag >=0x2C && tag <=0x2F) /* valid tag chars: , - . / */ || (tag >=0x5D && tag <= 0x60) /* valid tag chars: ] ^ _ ` */ || (tag >= 0x21 && tag <= 0x27) /* valid tag chars: ! # $ & , */ /* 0x22 " and 0x25 % invalid as above */ || (tag >= 0x3a && tag <= 0x40) /*valid tag chars: : ; < = > ? @ */ || (tag == 0x5b) /*valid tag chars: [ */ || (tag >= 0x7c && tag <= 0x7e) /* valid tag chars: | } ~ */ ) return 1; return 0; } static int imap_server_validate(SFSnortPacket *pkt, const int dir, const tAppIdConfig *pConfig, DetectorData *dd, const uint8_t *data, uint16_t size, tAppIdData *flowp) { const uint8_t *end = data + size; ServiceIMAPData *id = &dd->server; id->flags &= ~ IMAP_FLAG_RESULT_ALL; // when we are done these flags will tell us OK vs. NO vs. BAD for (; data < end; data++) { #ifdef DEBUG_IMAP_DETECTOR if (id->state != id->last_state) { _dpd.debugMsg(DEBUG_LOG,"%p State %d\n",flowp, id->state); id->last_state = id->state; } #endif switch (id->state) { case IMAP_STATE_BEGIN: if (id->flags & IMAP_FLAG_FIRST_PACKET) { id->flags &= ~IMAP_FLAG_FIRST_PACKET; if (*data == '*') { id->state = IMAP_STATE_BANNER_SPACE; break; } } if (*data == '+' || *data == '*') { id->state = IMAP_STATE_MID_LINE; id->flags &= ~IMAP_FLAG_ALNUM; } else if (isImapTagChar(*data)) { id->flags |= IMAP_FLAG_ALNUM; id->tagValue[0] = *data; id->pos = 1; id->state = IMAP_STATE_ALNUM_TAG; } else return -1; break; case IMAP_STATE_BANNER_SPACE: if (*data == ' ') { id->state = IMAP_STATE_BANNER_OK; id->pos = 0; } else id->state = IMAP_STATE_MID_LINE; break; case IMAP_STATE_BANNER_OK: if (*data == OK[id->pos]) { id->pos++; if (id->pos >= sizeof(OK)-1) id->state = IMAP_STATE_BANNER_WHITE_SPACE; } else id->state = IMAP_STATE_MID_LINE; break; case IMAP_STATE_BANNER_WHITE_SPACE: if (*data==' ' || *data=='\t') break; else if (*data == 0x0D) id->state = IMAP_STATE_MID_TERM; else if (*data == 0x0A) id->state = IMAP_STATE_BEGIN; else if (!isprint(*data)) return -1; else id->state = IMAP_STATE_BANNER; break; case IMAP_STATE_BANNER: if (*data == 0x0D) id->state = IMAP_STATE_MID_TERM; else if (*data == 0x0A) id->state = IMAP_STATE_BEGIN; else if (!isprint(*data)) return -1; break; case IMAP_STATE_MID_LINE: if (*data == 0x0D) { if (!id->parens) id->state = IMAP_STATE_MID_TERM; } else if (*data == 0x0A) { if (!id->parens) { id->state = IMAP_STATE_BEGIN; if (id->flags & IMAP_FLAG_ALNUM) id->count++; } } else if (*data == '(') id->parens++; else if (*data == ')') { if (id->parens) id->parens--; } else if (!isprint(*data) && *data != 0x09) return -1; break; case IMAP_STATE_MID_TERM: if (*data == 0x0A) { id->state = IMAP_STATE_BEGIN; if (id->flags & IMAP_FLAG_ALNUM) id->count++; } else return -1; break; case IMAP_STATE_MID_ALNUM: if (*data == ' ') id->state = IMAP_STATE_ALNUM_CODE; else return -1; break; case IMAP_STATE_ALNUM_TAG: if ((id->pos < (sizeof(id->tagValue)-1)) && (isImapTagChar(*data))) { id->tagValue[id->pos++] = *data; } else { id->tagValue[id->pos] = '\0'; id->state = IMAP_STATE_ALNUM_CODE; } break; case IMAP_STATE_ALNUM_CODE: if (*data == OK[0]) { id->state = IMAP_STATE_MID_OK; id->pos = 1; } else if (*data == NO[0]) { id->state = IMAP_STATE_MID_NO; id->pos = 1; } else if (*data == BAD[0]) { id->state = IMAP_STATE_MID_BAD; id->pos = 1; } else return -1; break; case IMAP_STATE_MID_OK: if (*data == OK[id->pos]) { id->pos++; if (id->pos >= sizeof(OK)-1) { id->pos = 0; id->state = IMAP_STATE_MID_OK_LOGIN; if (!strcasecmp(id->tagValue, dd->client.imapCmdTag)) { dd->client.imapCmdTag[0] = '\0'; id->flags |= IMAP_FLAG_RESULT_OK; } } } else return -1; break; case IMAP_STATE_MID_OK_LOGIN: /*add user successful */ if ((id->flags & IMAP_FLAG_RESULT_OK) && dd->client.username[0]) { service_mod.api->add_user(flowp, dd->client.username, APP_ID_IMAP, 1); // use of LOGIN cmd implies no IMAPS } id->state = IMAP_STATE_MID_LINE; break; case IMAP_STATE_MID_NO: if (*data == NO[id->pos]) { id->pos++; if (id->pos >= sizeof(NO)-1) { id->pos = 0; id->state = IMAP_STATE_MID_NO_LOGIN; if (!strcasecmp(id->tagValue, dd->client.imapCmdTag)) { dd->client.imapCmdTag[0] = '\0'; id->flags |= IMAP_FLAG_RESULT_NO; } } } else return -1; break; case IMAP_STATE_MID_NO_LOGIN: if (*data == NO_LOGIN[id->pos]) { id->pos++; if (id->pos >= sizeof(NO_LOGIN)-1) { id->state = IMAP_STATE_ALNUM_CODE_TERM; /*add user login failed */ if ((id->flags & IMAP_FLAG_RESULT_NO) && dd->client.username[0]) { service_mod.api->add_user(flowp, dd->client.username, APP_ID_IMAP, 0); // use of LOGIN cmd implies no IMAPS } } } else id->state = IMAP_STATE_MID_LINE; break; case IMAP_STATE_MID_BAD: if (*data == BAD[id->pos]) { id->pos++; if (id->pos >= sizeof(BAD)-1) { id->state = IMAP_STATE_ALNUM_CODE_TERM; if (!strcasecmp(id->tagValue, dd->client.imapCmdTag)) { dd->client.imapCmdTag[0] = '\0'; id->flags |= IMAP_FLAG_RESULT_BAD; } } } else return -1; break; case IMAP_STATE_ALNUM_CODE_TERM: if (*data == 0x0D) id->state = IMAP_STATE_MID_TERM; else if (*data == 0x0A) { id->state = IMAP_STATE_BEGIN; id->count++; } else if (*data == ' ') id->state = IMAP_STATE_MID_LINE; else return -1; break; } } if (dd->client.state == IMAP_CLIENT_STATE_STARTTLS_CMD) { if (id->flags & IMAP_FLAG_RESULT_OK) { clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); /* we are potentially overriding any APP_ID_IMAP assessment that was made earlier. */ client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_IMAPS, APP_ID_IMAPS, NULL); // sets APPID_SESSION_CLIENT_DETECTED } else { /* We failed to transition to IMAPS - fall back to normal IMAP state, Non-Authenticated */ dd->client.state = IMAP_CLIENT_STATE_NON_AUTH; } } else if (dd->client.state == IMAP_CLIENT_STATE_AUTHENTICATE_CMD) { dd->client.auth = 0; // stop discarding intervening command packets (part of the authenticate) /* Change state as appropriate */ dd->client.state = (id->flags & IMAP_FLAG_RESULT_OK) ? IMAP_CLIENT_STATE_AUTH : IMAP_CLIENT_STATE_NON_AUTH; } return 0; } static CLIENT_APP_RETCODE validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { const uint8_t *s = data; const uint8_t *end = (data + size); unsigned length; Client_App_Pattern *cmd = NULL; DetectorData *dd; ClientAppData *fd; char tag[IMAP_TAG_MAX_LEN+1] = {0}; void *cmd_matcher = AppIdFindGenericConfigItem((tAppIdConfig *)pConfig, client_app_mod.name); #ifdef APP_ID_USES_REASSEMBLED imap_detector_mod.streamAPI->response_flush_stream(pkt); #endif if (!size) return CLIENT_APP_INPROCESS; dd = imap_detector_mod.api->data_get(flowp, imap_detector_mod.flow_data_index); if (!dd) { if ((dd = calloc(1, sizeof(*dd))) == NULL) return CLIENT_APP_ENOMEM; if (imap_detector_mod.api->data_add(flowp, dd, imap_detector_mod.flow_data_index, &free)) { free(dd); return CLIENT_APP_ENOMEM; } dd->server.flags = IMAP_FLAG_FIRST_PACKET; fd = &dd->client; } else fd = &dd->client; if (!fd->set_flags) { dd->need_continue = 1; fd->set_flags = 1; setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } if (dir == APP_ID_FROM_RESPONDER) { if (imap_server_validate(pkt, dir, pConfig, dd, data, size, flowp)) clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); return CLIENT_APP_INPROCESS; } while ((length = (end - s)) > 0) { unsigned pattern_index; if (fd->auth) { /* authentication exchange in progress ignore all client-side packets until server-side OK/BAD/NO received */ for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); continue; } { /*processing tags */ char *p = tag; char *p_end = p + sizeof(tag) - 1; for (; (s < end) && isImapTagChar(*s); s++) { if (p < p_end) { *p++ = *s; } } for (; (s < end) && !isspace(*s); s++); *p = '\0'; } if (end == s || !isblank(*s)) { dd->need_continue = 0; setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); return CLIENT_APP_SUCCESS; } for (; (s < end) && isblank(*s); s++); /*s is now at command beginning */ if ((length = (end - s)) <= 0) { dd->need_continue = 0; setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); return CLIENT_APP_SUCCESS; } cmd = NULL; _dpd.searchAPI->search_instance_find_all(cmd_matcher, (char *)s, (length > longest_pattern ? longest_pattern:length), 0, &pattern_match, (void*)&cmd); if (!cmd) { if ( (s[0] >= 'A' && s[0] <= 'Z') || (s[0] >= 'a' && s[0] <= 'z') ) { // Command was not in the recognized list. Keep searching. return CLIENT_APP_INPROCESS; } else { // IMAP commands are English words, or at least start with X. return CLIENT_APP_ENULL; // anything but CLIENT_APP_SUCCESS or CLIENT_APP_INPROCESS } } s += cmd->length; pattern_index = cmd - patterns; // diff of ptr into array and its base addr is the corresponding index. switch (fd->state) { case IMAP_CLIENT_STATE_AUTHENTICATE_CMD: case IMAP_CLIENT_STATE_STARTTLS_CMD: /* The command we received was rejected by the server side - fall back to normal IMAP Non-Authorized state */ fd->state = IMAP_CLIENT_STATE_NON_AUTH; // fall through case IMAP_CLIENT_STATE_NON_AUTH: switch (pattern_index) { case PATTERN_LOGIN: strncpy(fd->imapCmdTag, tag, sizeof(fd->imapCmdTag)); { char *p = fd->username; char *p_end = p + sizeof(fd->username) - 1; int found_tick = 0; if (*s == '"') { s++; for (; s < end && p < p_end; s++) { if (*s == '"') { fd->count++; if (fd->count == MIN_CMDS) { client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_IMAP, APP_ID_IMAP, NULL); fd->detected = 1; if (fd->got_user) { setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } fd->state = IMAP_CLIENT_STATE_AUTH; } *p = 0; fd->got_user = 1; break; } else if (isalnum(*s) || *s == '.' || *s == '@' || *s == '-' || *s == '_' || *s == '`' || *s == ' ') { *p = *s; p++; } else break; } } else { for (; s < end && p < p_end; s++) { if (isalnum(*s) || *s == '.' || *s == '@' || *s == '-' || *s == '_') { if (!found_tick) { *p = *s; p++; } } else if (*s == '`') found_tick = 1; else if (*s == ' ') { fd->count++; if (fd->count == MIN_CMDS) { client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_IMAP, APP_ID_IMAP, NULL); fd->detected = 1; if (fd->got_user) { setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } } *p = 0; fd->got_user = 1; break; } else break; } } for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); } break; case PATTERN_STARTTLS: case PATTERN_STARTTLS2: strncpy(fd->imapCmdTag, tag, sizeof(fd->imapCmdTag)); fd->state = IMAP_CLIENT_STATE_STARTTLS_CMD; for (; (s < end) && (*s == '\r' || *s == '\n'); s++); // all we need because cmd->eoc == 1 /* No other commands will be coming until the result from this one. */ break; case PATTERN_AUTHENTICATE: strncpy(fd->imapCmdTag, tag, sizeof(fd->imapCmdTag)); fd->auth = 1; // gobble additional client packets until the server OK/BAD/NO response fd->state = IMAP_CLIENT_STATE_AUTHENTICATE_CMD; for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); break; default: { fd->count++; if (fd->count == MIN_CMDS) { client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_IMAP, APP_ID_IMAP, NULL); fd->detected = 1; if (fd->got_user) { setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } } if (!cmd->eoc) for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); } break; } break; case IMAP_CLIENT_STATE_AUTH: { fd->count++; if (fd->count == MIN_CMDS) { client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_IMAP, APP_ID_IMAP, NULL); fd->detected = 1; if (fd->got_user) { setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } } if (!cmd->eoc) for (; (s < end) && *s != '\r' && *s != '\n'; s++); for (; (s < end) && (*s == '\r' || *s == '\n'); s++); } break; } // end switch(fd->state) } // end 'while' return CLIENT_APP_INPROCESS; } static int imap_validate(ServiceValidationArgs* args) { DetectorData *dd; ServiceIMAPData *id; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; uint16_t size = args->size; if (args->dir != APP_ID_FROM_RESPONDER) goto inprocess; #ifdef APP_ID_USES_REASSEMBLED imap_detector_mod.streamAPI->response_flush_stream(pkt); #endif if (!size) goto inprocess; dd = imap_detector_mod.api->data_get(flowp, imap_detector_mod.flow_data_index); if (!dd) { if ((dd = calloc(1, sizeof(*dd))) == NULL) return SERVICE_ENOMEM; if (imap_detector_mod.api->data_add(flowp, dd, imap_detector_mod.flow_data_index, &free)) { free(dd); return SERVICE_ENOMEM; } id = &dd->server; id->state = IMAP_STATE_BEGIN; id->flags = IMAP_FLAG_FIRST_PACKET; } else id = &dd->server; if (dd->need_continue) setAppIdFlag(flowp, APPID_SESSION_CONTINUE); else { clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) return SERVICE_SUCCESS; } if (!imap_server_validate(args->pkt, args->dir, args->pConfig, dd, data, size, flowp)) { if ((id->flags & IMAP_FLAG_RESULT_OK) && dd->client.state == IMAP_CLIENT_STATE_STARTTLS_CMD) { /* IMAP server response to STARTTLS command from client was OK */ service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_IMAPS, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; } if (id->count >= IMAP_COUNT_THRESHOLD && !getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { service_mod.api->add_service(flowp, args->pkt, args->dir, &svc_element, APP_ID_IMAP, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; } } else if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } else { clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_SUCCESS; } inprocess: service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); return SERVICE_INPROCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_api.h0000644000175000017500000000325114241075402025475 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DETECTOR_API_H__ #define __DETECTOR_API_H__ #include "client_app_api.h" #include "service_api.h" #include "client_app_base.h" #include "service_base.h" typedef void *(*DetectorFlowdataGet)(tAppIdData *, unsigned); typedef int (*DetectorFlowdataAdd)(tAppIdData *, void *, unsigned, AppIdFreeFCN); typedef struct _DETECTOR_API { DetectorFlowdataGet data_get; DetectorFlowdataAdd data_add; } DetectorApi; /**compound detector with both service and client side. */ typedef struct _RNA_DETECTOR_VALIDATION_MODULE { /**service side.*/ tRNAServiceValidationModule *service; /**client side.*/ tRNAClientAppModule *client; const DetectorApi *api; unsigned flow_data_index; StreamAPI *streamAPI; } RNADetectorValidationModule; #endif /*__DETECTOR_API_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_http.h0000644000175000017500000001317014241075413025706 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DETECTOR_HTTP_H__ #define __DETECTOR_HTTP_H__ #include "detector_api.h" #define MAX_VERSION_SIZE 64 typedef enum { /* Only store Content-Type, Server, User-Agent & Via headers now. */ HTTP_ID_CONTENT_TYPE, HTTP_ID_SERVER, /* HTTP Responses */ HTTP_ID_COPY, HTTP_ID_DELETE, HTTP_ID_GET, HTTP_ID_HEAD, HTTP_ID_OPTIONS, HTTP_ID_PROPFIND, HTTP_ID_PROPPATCH, HTTP_ID_MKCOL, HTTP_ID_LOCK, HTTP_ID_MOVE, HTTP_ID_PUT, HTTP_ID_POST, HTTP_ID_TRACE, HTTP_ID_UNLOCK, HTTP_ID_X_WORKING_WITH, /* When others are needed, move value to be above HTTP_ID_LEN */ HTTP_ID_ACCEPT, HTTP_ID_ACCEPT_CHARSET, HTTP_ID_ACCEPT_ENCODING, HTTP_ID_ACCEPT_LANGUAGE, HTTP_ID_ACCEPT_RANGES, HTTP_ID_AGE, HTTP_ID_ALLOW, HTTP_ID_AUTHORIZATION, HTTP_ID_CACHE_CONTROL, HTTP_ID_CONNECTION, HTTP_ID_COOKIE, HTTP_ID_CONTENT_DISPOSITION, HTTP_ID_CONTENT_ENCODING, HTTP_ID_CONTENT_LANGUAGE, HTTP_ID_CONTENT_LENGTH, HTTP_ID_CONTENT_LOCATION, HTTP_ID_CONTENT_MD5, HTTP_ID_CONTENT_RANGE, HTTP_ID_DATE, HTTP_ID_ETAG, HTTP_ID_EXPECT, HTTP_ID_EXPIRES, HTTP_ID_FROM, HTTP_ID_HOST, HTTP_ID_IF_MATCH, HTTP_ID_IF_MODIFIED_SINCE, HTTP_ID_IF_NONE_MATCH, HTTP_ID_IF_RANGE, HTTP_ID_IF_UNMODIFIED_SINCE, HTTP_ID_LAST_MODIFIED, HTTP_ID_LINK, HTTP_ID_LOCATION, HTTP_ID_MAX_FORWARDS, HTTP_ID_P3P, HTTP_ID_PRAGMA, HTTP_ID_PROXY_AUTHORIZATION, HTTP_ID_PROXY_AUTHENTICATE, HTTP_ID_RANGE, HTTP_ID_REFERER, HTTP_ID_REFRESH, HTTP_ID_RETRY_AFTER, HTTP_ID_SET_COOKIE, HTTP_ID_STRICT_TRANSPORT_SECURITY, HTTP_ID_TE, HTTP_ID_TRAILER, HTTP_ID_TRANSFER_ENCODING, HTTP_ID_UPGRADE, HTTP_ID_USER_AGENT, HTTP_ID_VARY, HTTP_ID_VIA, HTTP_ID_WARNING, HTTP_ID_WWW_AUTHENTICATE, HTTP_ID_LEN } HttpId; typedef struct _HTTP_HEADER_INDICES { int start; int end; } HTTPHeaderIndices; #if 1 typedef struct _HEADER_MATCHED_PATTERNS { HTTPHeaderIndices headers[HTTP_ID_LEN]; int last_match; int last_index_end; int searched; } HeaderMatchedPatterns; #endif typedef struct _MatchedCHPAction { CHPAction *mpattern; int index; struct _MatchedCHPAction *next; } MatchedCHPAction; // This is an array element for the dynamically growing tally below typedef struct _CHPMatchCandidate { CHPApp *chpapp; int key_pattern_length_sum; int key_pattern_countdown; } CHPMatchCandidate; // This is a structure which will grow using realloc as needed to keep all candidates typedef struct _CHPMatchTally { int allocated_elements; int in_use_elements; CHPMatchCandidate item[0]; } CHPMatchTally; int getAppidByViaPattern(const u_int8_t *data, unsigned size, char **version, const tDetectorHttpConfig *pHttpConfig); int getHTTPHeaderLocation(const uint8_t *data, unsigned size, HttpId id, int *start, int *end, HeaderMatchedPatterns *hmp, const tDetectorHttpConfig *pHttpConfig); static inline void FreeMatchedCHPActions(MatchedCHPAction *ma) { MatchedCHPAction *tmp; while(ma) { tmp = ma; ma = ma->next; free(tmp); } } void httpGetNewOffsetsFromPacket(SFSnortPacket *pkt, httpSession *hsession, tAppIdConfig *pConfig); int scanKeyCHP (PatternType ptype, char *buf, int buf_size, CHPMatchTally **ppTally, MatchedCHPAction **ppmatches, const tDetectorHttpConfig *pHttpConfig); tAppId scanCHP (PatternType ptype, char *buf, int buf_size, MatchedCHPAction *mp, char **version, char **user, char **new_field, int *total_found, httpSession *hsession, SFSnortPacket *p, const tDetectorHttpConfig *pHttpConfig); tAppId getAppIdFromUrl(char *host, char *url, char **version, char *referer, tAppId *clientAppId, tAppId *serviceAppId, tAppId *payloadAppId, tAppId *referredPayloadAppId, unsigned from_rtmp, const tDetectorHttpConfig *pHttpConfig); tAppId getAppidByContentType(const uint8_t *data, int size, const tDetectorHttpConfig *pHttpConfig); tAppId scan_header_x_working_with(const uint8_t *data, uint32_t size, char **version); void identifyUserAgent(const u_int8_t *start, int size, tAppId *serviceAppId, tAppId *clientAppId, char **version, const tDetectorHttpConfig *pHttpConfig); void getServerVendorVersion(const uint8_t *data, int len, char **version, char **vendor, RNAServiceSubtype **subtype); int webdav_found(HeaderMatchedPatterns *hmp); int http_detector_finalize(tAppIdConfig *pConfig); void http_detector_clean(tDetectorHttpConfig *pHttpConfig); void finalizeFflow (fflow_info *fflow, unsigned app_type_flags, tAppId target_appId, SFSnortPacket *p); #endif snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_cip.h0000644000175000017500000000736314241075406025513 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /** * @author RA/Cisco */ #ifndef DETECTOR_CIP_H #define DETECTOR_CIP_H #include // For bool in stream_api.h #include // For C integer types #include // For tAppId #include "appIdConfig.h" // For appidStaticConfig #include "stream_api.h" // For ServiceEventType #include "fw_appid.h" // For AppIdAddMultiPayload #include "client_app_api.h" // For RNAClientAppModule #include "service_api.h" // For RNAServiceValidationModule // ENIP Command Data typedef struct _EnipCommandData { tAppId app_id; uint16_t command_id; } EnipCommandData; typedef struct _EnipCommandList { EnipCommandData data; struct _EnipCommandList *next; } EnipCommandList; // CIP Path Data typedef struct _CipPathData { tAppId app_id; uint32_t class_id; uint8_t service_id; } CipPathData; typedef struct _CipPathList { CipPathData data; struct _CipPathList *next; } CipPathList; // CIP Set Attribute Data typedef struct _CipSetAttributeData { tAppId app_id; uint32_t class_id; bool is_class_instance; uint32_t attribute_id; } CipSetAttributeData; typedef struct _CipSetAttributeList { CipSetAttributeData data; struct _CipSetAttributeList *next; } CipSetAttributeList; // CIP Connection Class Data typedef struct _CipConnectionClassData { tAppId app_id; uint32_t class_id; } CipConnectionClassData; typedef struct _CipConnectionClassList { CipConnectionClassData data; struct _CipConnectionClassList *next; } CipConnectionClassList; // CIP Service Data typedef struct _CipServiceData { tAppId app_id; uint8_t service_id; } CipServiceData; typedef struct _CipServiceList { CipServiceData data; struct _CipServiceList *next; } CipServiceList; // CIP registration data struct typedef struct _CipPatternLists { EnipCommandList *enip_command_list; CipPathList *path_list; CipSetAttributeList *set_attribute_list; CipConnectionClassList *connection_list; CipServiceList *symbol_list; CipServiceList *service_list; } CipPatternLists; // CIP Registration int CipAddEnipCommand(tAppId app_id, uint16_t command_id); int CipAddPath(tAppId app_id, uint32_t class_id, uint8_t service_id); int CipAddSetAttribute(tAppId app_id, uint32_t class_id, bool is_class_instance, uint32_t attribute_id); int CipAddConnectionClass(tAppId app_id, uint32_t class_id); int CipAddExtendedSymbolService(tAppId app_id, uint8_t service_id); int CipAddService(tAppId app_id, uint8_t service_id); // CIP Event Handling void CipSessionSnortCallback(void *ssnptr, ServiceEventType eventType, void *data); void CipClean(void); extern struct RNAClientAppModule cip_client_mod; extern struct RNAClientAppModule enip_client_mod; extern struct RNAServiceValidationModule cip_service_mod; extern struct RNAServiceValidationModule enip_service_mod; #endif // DETECTOR_CIP_H snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_dns.c0000644000175000017500000006055314241075410025512 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appInfoTable.h" #include "service_base.h" #include "detector_dns.h" #include "service_api.h" #include "str_search.h" #include "client_app_base.h" #include "client_app_api.h" #include "dns_defs.h" typedef enum { DNS_STATE_QUERY, DNS_STATE_RESPONSE } DNSState; typedef struct _SERVICE_DNS_DATA { DNSState state; uint16_t id; } ServiceDNSData; typedef struct _MatchedDNSPatterns { DNSHostPattern *mpattern; int index; struct _MatchedDNSPatterns *next; } MatchedDNSPatterns; static int dns_service_init(const InitServiceAPI * const init_api); static int dns_udp_validate(ServiceValidationArgs* args); static int dns_tcp_validate(ServiceValidationArgs* args); static tRNAServiceElement udp_svc_element = { .next = NULL, .validate = &dns_udp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "dns", .ref_count = 1, .current_ref_count = 1, }; static tRNAServiceElement tcp_svc_element = { .next = NULL, .validate = &dns_tcp_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "tcp dns", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&dns_tcp_validate, 53, IPPROTO_TCP}, {&dns_udp_validate, 53, IPPROTO_UDP}, {&dns_udp_validate, 53, IPPROTO_UDP, 1}, {&dns_tcp_validate, 5300, IPPROTO_TCP}, {&dns_udp_validate, 5300, IPPROTO_UDP}, {NULL, 0, 0} }; tRNAServiceValidationModule dns_service_mod = { "DNS", &dns_service_init, pp }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_DNS, APPINFO_FLAG_SERVICE_UDP_REVERSED | APPINFO_FLAG_SERVICE_ADDITIONAL} }; static CLIENT_APP_RETCODE dns_udp_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE dns_tcp_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE dns_udp_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static CLIENT_APP_RETCODE dns_tcp_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); SF_SO_PUBLIC tRNAClientAppModule dns_udp_client_mod = { .name = "DNS", .proto = IPPROTO_UDP, .init = &dns_udp_client_init, .validate = &dns_udp_client_validate, .minimum_matches = 1 }; SF_SO_PUBLIC tRNAClientAppModule dns_tcp_client_mod = { .name = "DNS", .proto = IPPROTO_TCP, .init = &dns_tcp_client_init, .validate = &dns_tcp_client_validate, .minimum_matches = 1 }; static CLIENT_APP_RETCODE dns_udp_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE dns_tcp_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE dns_udp_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { return CLIENT_APP_INPROCESS; } static CLIENT_APP_RETCODE dns_tcp_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { return CLIENT_APP_INPROCESS; } static int dns_host_pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { MatchedDNSPatterns *cm; MatchedDNSPatterns **matches = (MatchedDNSPatterns **)data; DNSHostPattern *target = (DNSHostPattern *)id; if (!(cm = (MatchedDNSPatterns *)malloc(sizeof(MatchedDNSPatterns)))) return 1; cm->mpattern = target; cm->index = index; cm->next = *matches; *matches = cm; return 0; } static int dns_host_detector_create_matcher(void **matcher, DetectorDNSHostPattern *list) { DetectorDNSHostPattern *element = NULL; if (*matcher) _dpd.searchAPI->search_instance_free(*matcher); if (!(*matcher = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF))) return 0; /* Add patterns from Lua API */ for(element = list; element; element = element->next) { _dpd.searchAPI->search_instance_add_ex(*matcher, (char *)element->dpattern->pattern, element->dpattern->pattern_size, element->dpattern, STR_SEARCH_CASE_INSENSITIVE); } _dpd.searchAPI->search_instance_prep(*matcher); return 1; } int dns_host_detector_process_patterns(tServiceDnsConfig *pDnsConfig) { int retVal = 1; if (!dns_host_detector_create_matcher(&pDnsConfig->dns_host_host_matcher, pDnsConfig->DetectorDNSHostPatternList)) retVal = 0; return retVal; } static int dns_service_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(appIdRegistry)/sizeof(*appIdRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[i].appId); init_api->RegisterAppId(&dns_udp_validate, appIdRegistry[i].appId, appIdRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int dns_validate_label(const uint8_t *data, uint16_t *offset, uint16_t size, uint8_t *len, unsigned *len_valid) { const DNSLabel *lbl; const DNSLabelPtr *lbl_ptr; const DNSLabelBitfield *lbl_bit; uint16_t tmp; *len = 0; *len_valid = 1; for (;;) { if ((size <= *offset) || (size-(*offset)) < (int)offsetof(DNSLabel, name)) return SERVICE_NOMATCH; lbl = (DNSLabel *)(data + (*offset)); switch (lbl->len & DNS_LENGTH_FLAGS) { case 0xC0: *len_valid = 0; lbl_ptr = (DNSLabelPtr *)lbl; *offset += offsetof(DNSLabelPtr, data); if (*offset > size) return SERVICE_NOMATCH; tmp = (uint16_t)(ntohs(lbl_ptr->position) & 0x3FFF); if (tmp > size - offsetof(DNSLabel, name)) return SERVICE_NOMATCH; return SERVICE_SUCCESS; case 0x00: *offset += offsetof(DNSLabel, name); if (!lbl->len) { if (*len > 0) (*len)--; // take off the extra '.' at the end return SERVICE_SUCCESS; } *offset += lbl->len; *len += lbl->len + 1; // add 1 for '.' break; case 0x40: *len_valid = 0; if (lbl->len != 0x41) return SERVICE_NOMATCH; *offset += offsetof(DNSLabelBitfield, data); if (*offset >= size) return SERVICE_NOMATCH; lbl_bit = (DNSLabelBitfield *)lbl; if (lbl_bit->len) { *offset += ((lbl_bit->len - 1) / 8) + 1; } else { *offset += 32; } break; default: *len_valid = 0; return SERVICE_NOMATCH; } } } static int dns_validate_query(const uint8_t *data, uint16_t *offset, uint16_t size, uint16_t id, unsigned host_reporting, tAppIdData *flowp) { int ret; const uint8_t *host; uint8_t host_len; unsigned host_len_valid; uint16_t host_offset; DNSQueryFixed *query; uint16_t record_type; bool root_query = false; host = data + *offset; host_offset = *offset; ret = dns_validate_label(data, offset, size, &host_len, &host_len_valid); if (ret == SERVICE_SUCCESS) { query = (DNSQueryFixed*)(data + *offset); *offset += sizeof(DNSQueryFixed); if (host_reporting) { record_type = ntohs(query->QType); if (!host_len && host_len_valid) root_query = true; else if ((host_len == 0) || (!host_len_valid)) { host = NULL; host_len = 0; host_offset = 0; } switch (record_type) { case PATTERN_A_REC: case PATTERN_AAAA_REC: case PATTERN_CNAME_REC: case PATTERN_SRV_REC: case PATTERN_TXT_REC: case PATTERN_MX_REC: case PATTERN_SOA_REC: case PATTERN_NS_REC: case PATTERN_ANY_REC: dns_service_mod.api->add_dns_query_info(flowp, id, host, host_len, host_offset, record_type, *offset, root_query); break; case PATTERN_PTR_REC: dns_service_mod.api->add_dns_query_info(flowp, id, NULL, 0, 0, record_type, *offset, false); break; default: break; } } } return ret; } static int dns_validate_answer(const uint8_t *data, uint16_t *offset, uint16_t size, uint16_t id, uint8_t rcode, unsigned host_reporting, tAppIdData *flowp) { int ret; const uint8_t *host; uint8_t host_len; unsigned host_len_valid; uint16_t host_offset; uint16_t record_type; uint32_t ttl; uint16_t r_data_offset; ret = dns_validate_label(data, offset, size, &host_len, &host_len_valid); if (ret == SERVICE_SUCCESS) { DNSAnswerData *ad = (DNSAnswerData *)(data + (*offset)); *offset += sizeof(DNSAnswerData); if (*offset > size) return SERVICE_NOMATCH; r_data_offset = *offset; *offset += ntohs(ad->r_len); if (*offset > size) return SERVICE_NOMATCH; if (host_reporting) { record_type = ntohs(ad->type); ttl = ntohl(ad->ttl); switch (record_type) { case PATTERN_A_REC: case PATTERN_AAAA_REC: case PATTERN_CNAME_REC: case PATTERN_SRV_REC: case PATTERN_TXT_REC: case PATTERN_MX_REC: case PATTERN_SOA_REC: case PATTERN_NS_REC: // case PATTERN_ANY_REC: // commented out by design dns_service_mod.api->add_dns_response_info(flowp, id, NULL, 0, 0, rcode, ttl); break; case PATTERN_PTR_REC: host = data + r_data_offset; host_offset = r_data_offset; ret = dns_validate_label(data, &r_data_offset, size, &host_len, &host_len_valid); if ((host_len == 0) || (!host_len_valid)) { host = NULL; host_len = 0; host_offset = 0; } dns_service_mod.api->add_dns_response_info(flowp, id, host, host_len, host_offset, rcode, ttl); break; default: break; } } } return ret; } static int dns_validate_header(const int dir, DNSHeader *hdr, unsigned host_reporting, tAppIdData *flowp) { if (hdr->Opcode > MAX_OPCODE || hdr->Opcode == INVALID_OPCODE) { return SERVICE_NOMATCH; } if (hdr->Z) { return SERVICE_NOMATCH; } if (hdr->RCODE > MAX_RCODE) { return SERVICE_NOMATCH; } if (!hdr->QR) { // Query. if (host_reporting) dns_service_mod.api->reset_dns_info(flowp); return dir == APP_ID_FROM_INITIATOR ? SERVICE_SUCCESS:SERVICE_REVERSED; } // Response. return dir == APP_ID_FROM_INITIATOR ? SERVICE_REVERSED:SERVICE_SUCCESS; } static int validate_packet(const uint8_t *data, uint16_t size, const int dir, unsigned host_reporting, tAppIdData *flowp) { uint16_t i; uint16_t count; const DNSHeader *hdr = (const DNSHeader *)data; uint16_t offset; if (hdr->TC && size == 512) return SERVICE_SUCCESS; offset = sizeof(DNSHeader); if (hdr->QDCount) { count = ntohs(hdr->QDCount); for (i=0; iid), host_reporting, flowp) != SERVICE_SUCCESS) { return SERVICE_NOMATCH; } } } if (hdr->ANCount) { count = ntohs(hdr->ANCount); for (i=0; iid), hdr->RCODE, host_reporting, flowp) != SERVICE_SUCCESS) { return SERVICE_NOMATCH; } } } if (hdr->NSCount) { count = ntohs(hdr->NSCount); for (i=0; iid), hdr->RCODE, host_reporting, flowp) != SERVICE_SUCCESS) { return SERVICE_NOMATCH; } } } if (hdr->ARCount) { count = ntohs(hdr->ARCount); for (i=0; iid), hdr->RCODE, host_reporting, flowp) != SERVICE_SUCCESS) { return SERVICE_NOMATCH; } } } if (hdr->QR && (hdr->RCODE != 0)) // error response dns_service_mod.api->add_dns_response_info(flowp, ntohs(hdr->id), NULL, 0, 0, hdr->RCODE, 0); return SERVICE_SUCCESS; } static int dns_udp_validate(ServiceValidationArgs* args) { int rval; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (!size) return SERVICE_INPROCESS; if (size < sizeof(DNSHeader)) { rval = (dir == APP_ID_FROM_INITIATOR) ? SERVICE_INVALID_CLIENT:SERVICE_NOMATCH; goto udp_done; } if ((rval = dns_validate_header(dir, (DNSHeader *)data, appidStaticConfig->dns_host_reporting, flowp)) != SERVICE_SUCCESS) { if (rval == SERVICE_REVERSED) { if (dir == APP_ID_FROM_RESPONDER) { if (getAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED)) { // To get here, we missed the initial query, got a // response, and now we've got another query. rval = validate_packet(data, size, dir, appidStaticConfig->dns_host_reporting, flowp); if (rval == SERVICE_SUCCESS) goto success; } goto invalid; } else { // To get here, we missed the initial query, but now we've got // a response. rval = validate_packet(data, size, dir, appidStaticConfig->dns_host_reporting, flowp); if (rval == SERVICE_SUCCESS) { setAppIdFlag(flowp, APPID_SESSION_UDP_REVERSED); goto success; } goto nomatch; } } rval = (dir == APP_ID_FROM_INITIATOR) ? SERVICE_INVALID_CLIENT:SERVICE_NOMATCH; goto udp_done; } rval = validate_packet(data, size, dir, appidStaticConfig->dns_host_reporting, flowp); udp_done: switch (rval) { case SERVICE_SUCCESS: success: // We will declare DNS as soon as we have seen a good query (we do not // wait until we get a reply). setAppIdFlag(flowp, APPID_SESSION_CONTINUE); dns_service_mod.api->add_service(flowp, args->pkt, dir, &udp_svc_element, APP_ID_DNS, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; case SERVICE_INVALID_CLIENT: invalid: dns_service_mod.api->incompatible_data(flowp, args->pkt, dir, &udp_svc_element, dns_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; case SERVICE_NOMATCH: nomatch: dns_service_mod.api->fail_service(flowp, args->pkt, dir, &udp_svc_element, dns_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; case SERVICE_INPROCESS: // inprocess: dns_service_mod.api->service_inprocess(flowp, args->pkt, dir, &udp_svc_element, NULL); return SERVICE_INPROCESS; default: return rval; } } static int dns_tcp_validate(ServiceValidationArgs* args) { ServiceDNSData *dd; const DNSTCPHeader *hdr; uint16_t tmp; int rval; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; const int dir = args->dir; uint16_t size = args->size; if (!size) goto inprocess; if (size < sizeof(DNSTCPHeader)) { if (dir == APP_ID_FROM_INITIATOR) goto not_compatible; else goto fail; } hdr = (DNSTCPHeader *)data; data += sizeof(DNSTCPHeader); size -= sizeof(DNSTCPHeader); tmp = ntohs(hdr->length); if (tmp < sizeof(DNSHeader) || dns_validate_header(dir, (DNSHeader *)data, appidStaticConfig->dns_host_reporting, flowp)) { if (dir == APP_ID_FROM_INITIATOR) goto not_compatible; else goto fail; } if (tmp > size) goto not_compatible; rval = validate_packet(data, size, dir, appidStaticConfig->dns_host_reporting, flowp); if (rval != SERVICE_SUCCESS) goto tcp_done; dd = dns_service_mod.api->data_get(flowp, dns_service_mod.flow_data_index); if (!dd) { dd = calloc(1, sizeof(*dd)); if (!dd) return SERVICE_ENOMEM; if (dns_service_mod.api->data_add(flowp, dd, dns_service_mod.flow_data_index, &free)) { free(dd); return SERVICE_ENOMEM; } dd->state = DNS_STATE_QUERY; } if (dd->state == DNS_STATE_QUERY) { if (dir != APP_ID_FROM_INITIATOR) goto fail; dd->id = ((DNSHeader *)data)->id; dd->state = DNS_STATE_RESPONSE; } else { if (dir != APP_ID_FROM_RESPONDER || dd->id != ((DNSHeader *)data)->id) goto fail; } tcp_done: switch (rval) { case SERVICE_SUCCESS: goto success; case SERVICE_INVALID_CLIENT: goto not_compatible; case SERVICE_NOMATCH: goto fail; case SERVICE_INPROCESS: goto inprocess; default: return rval; } success: // We will declare DNS as soon as we have seen a good query (we do not // wait until we get a reply). setAppIdFlag(flowp, APPID_SESSION_CONTINUE); dns_service_mod.api->add_service(flowp, args->pkt, dir, &tcp_svc_element, APP_ID_DNS, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; not_compatible: dns_service_mod.api->incompatible_data(flowp, args->pkt, dir, &tcp_svc_element, dns_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOT_COMPATIBLE; fail: dns_service_mod.api->fail_service(flowp, args->pkt, dir, &tcp_svc_element, dns_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; inprocess: dns_service_mod.api->service_inprocess(flowp, args->pkt, dir, &tcp_svc_element, NULL); return SERVICE_INPROCESS; } static int dns_host_scan_patterns(void * matcher, const u_int8_t *pattern, size_t size, tAppId *clientAppId, tAppId *payloadId) { MatchedDNSPatterns *mp = NULL; MatchedDNSPatterns *tmpMp; DNSHostPattern *best_match; if (!matcher) return 0; _dpd.searchAPI->search_instance_find_all(matcher, (char *)pattern, size, 0, dns_host_pattern_match, (void *)(&mp)); if (!mp) return 0; best_match = mp->mpattern; tmpMp = mp->next; free(mp); while ((mp = tmpMp)) { tmpMp = mp->next; if (mp->mpattern->pattern_size > best_match->pattern_size) { best_match = mp->mpattern; } free(mp); } switch (best_match->type) { // type 0 means WEB APP case 0: *clientAppId = APP_ID_DNS; *payloadId = best_match->appId; break; // type 1 means CLIENT case 1: *clientAppId = best_match->appId; *payloadId = 0; break; default: return 0; } return 1; } int dns_host_scan_hostname(const u_int8_t *pattern, size_t size, tAppId *clientAppId, tAppId *payloadId, const tServiceDnsConfig* pDnsConfig) { return dns_host_scan_patterns(pDnsConfig->dns_host_host_matcher , pattern, size, clientAppId, payloadId); } void service_dns_host_clean(tServiceDnsConfig* pDnsConfig) { if (pDnsConfig->dns_host_host_matcher ) { _dpd.searchAPI->search_instance_free(pDnsConfig->dns_host_host_matcher ); pDnsConfig->dns_host_host_matcher = NULL; } } static int dns_add_pattern(DetectorDNSHostPattern **list, uint8_t *pattern_str, size_t pattern_size, uint8_t type, tAppId app_id) { DetectorDNSHostPattern *new_dns_host_pattern; new_dns_host_pattern = calloc(1, sizeof(DetectorDNSHostPattern)); if (!new_dns_host_pattern) { return 0; } new_dns_host_pattern->dpattern = calloc(1, sizeof(DNSHostPattern)); if (!new_dns_host_pattern->dpattern) { free(new_dns_host_pattern); return 0; } new_dns_host_pattern->dpattern->type = type; new_dns_host_pattern->dpattern->appId = app_id; new_dns_host_pattern->dpattern->pattern = pattern_str; new_dns_host_pattern->dpattern->pattern_size = pattern_size; new_dns_host_pattern->next = *list; *list = new_dns_host_pattern; return 1; } int dns_add_host_pattern(uint8_t *pattern_str, size_t pattern_size, uint8_t type, tAppId app_id, tServiceDnsConfig *pDnsConfig) { return dns_add_pattern(&pDnsConfig->DetectorDNSHostPatternList, pattern_str, pattern_size, type, app_id); } static void dns_patterns_free(DetectorDNSHostPattern **list) { DetectorDNSHostPattern *tmp_pattern; while ((tmp_pattern = *list)) { *list = tmp_pattern->next; if (tmp_pattern->dpattern) { if (tmp_pattern->dpattern->pattern) free(tmp_pattern->dpattern->pattern); free (tmp_pattern->dpattern); } free(tmp_pattern); } } void dns_detector_free_patterns(tServiceDnsConfig *pDnsConfig) { dns_patterns_free(&pDnsConfig->DetectorDNSHostPatternList); } char *dns_parse_host(const uint8_t *host, uint8_t host_len) { char *str; const uint8_t *src; char *dst; uint8_t len; uint32_t dstLen = 0; str = malloc(host_len + 1); // plus '\0' at end if (str != NULL) { src = host; dst = str; while (*src != 0) { len = *src; src++; if ((dstLen + len) <= host_len) memcpy(dst, src, len); else { // Malformed DNS host, return free(str); return NULL; } src += len; dst += len; *dst = '.'; dstLen += len + 1; dst++; } str[host_len] = '\0'; // NULL term } return str; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_base.h0000644000175000017500000000202214241075404025633 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DETECTOR_BASE_H__ #define __DETECTOR_BASE_H__ int LoadDetectorModules(const char **dir_list); #endif /*__DETECTOR_BASE_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_pattern.c0000644000175000017500000005742214241075416026412 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include #include #include #include "appInfoTable.h" #include "flow.h" #include "service_api.h" #include "client_app_base.h" #include "detector_pattern.h" #include "fw_appid.h" #include "limits.h" static int service_validate(ServiceValidationArgs* args); static int csdPatternTreeSearch(const uint8_t *data, uint16_t size, int protocol, SFSnortPacket *pkt, const tRNAServiceElement** serviceData, bool isClient, const tAppIdConfig *pConfig); static int pattern_service_init(const InitServiceAPI * const initServiceApi); static void pattern_service_clean(const CleanServiceAPI * const clean_api); static CLIENT_APP_RETCODE client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE client_init_tcp(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static void client_clean(const CleanClientAppAPI * const clean_api); static const InitServiceAPI *initServiceApi; static const InitClientAppAPI *initClientApi; static tRNAServiceElement svc_element = { .next = NULL, .validate = &service_validate, .detectorType = DETECTOR_TYPE_PATTERN, .name = "pattern", .ref_count = 1, .current_ref_count = 1 }; tRNAServiceValidationModule pattern_service_mod = { name: "pattern", init: &pattern_service_init, pp: NULL, clean: &pattern_service_clean }; //client side tRNAClientAppModule pattern_udp_client_mod = { .name = "pattern", .proto = IPPROTO_UDP, .init = &client_init, .clean = &client_clean, .validate = &client_validate, }; tRNAClientAppModule pattern_tcp_client_mod = { .name = "pattern", .proto = IPPROTO_TCP, .init = &client_init_tcp, .validate = &client_validate, }; #define PATTERN_TCP_PROTO 1 #define PATTERN_UDP_PROTO 2 #define PATTERN_BOTH_PROTOS (PATTERN_TCP_PROTO | PATTERN_UDP_PROTO) static RNAServiceValidationPort pp = { validate: &service_validate }; static void FreePattern(Pattern *pattern) { if (pattern) { if (pattern->data) free(pattern->data); free(pattern); } } static void FreePatternService(PatternService *ps) { Pattern *pattern; Port *port; if (ps) { while ((pattern = ps->pattern)) { ps->pattern = pattern->next; FreePattern(pattern); } while ((port = ps->port)) { ps->port = port->next; free(port); } free(ps); } } static void read_patterns(tPortPatternNode *portPatternList, PatternService **serviceList) { PatternService *ps = NULL; Pattern *pattern; Port *port; tPortPatternNode *pNode; char *lastName = NULL; short lastPort = 0; uint8_t lastProto = 0; bool newPs; for (pNode = portPatternList; pNode; pNode = pNode->next) { newPs = false; if (!ps || !lastName || strcmp(lastName, pNode->detectorName) || lastProto != pNode->protocol) { if ((ps = calloc(1, sizeof(*ps))) == NULL) { _dpd.errMsg( "Failed to allocate a pattern"); return; } lastName = pNode->detectorName; lastProto = pNode->protocol; newPs = true; ps->id = pNode->appId; ps->proto = pNode->protocol; ps->next = *serviceList; *serviceList = ps; } if (pNode->port && (newPs || lastPort != pNode->port)) { if ((port = calloc(1, sizeof(*port))) == NULL) { _dpd.errMsg( "Failed to allocate a port struct"); return; } port->port = pNode->port; port->next = ps->port; lastPort = pNode->port; ps->port = port; } if ((pattern = calloc(1, sizeof(*pattern))) == NULL) { _dpd.errMsg( "Failed to allocate a pattern struct"); return; } if ((pattern->data = malloc(pNode->length)) == NULL) { FreePattern(pattern); _dpd.errMsg( "Failed to allocate a %u byte pattern in pattern detector '%s'", (unsigned)pNode->length, pNode->detectorName); return; } memcpy(pattern->data, pNode->pattern, pNode->length); pattern->length = pNode->length; if (pattern->length > ps->longest) ps->longest = pattern->length; pattern->ps = ps; pattern->offset = pNode->offset; pattern->next = ps->pattern; ps->pattern = pattern; appInfoSetActive(ps->id, true); } } /**Register ports for detectors which have a pattern associated with it. */ static void install_ports(PatternService *serviceList, const InitServiceAPI * const initServiceApi) { PatternService *ps; Port *port; for (ps = serviceList; ps; ps = ps->next) { if (!ps->port) continue; for (port = ps->port; port; port = port->next) { pp.port = port->port; pp.proto = ps->proto; if (initServiceApi->AddPort(&pp, &pattern_service_mod, initServiceApi->pAppidConfig)) _dpd.errMsg("Failed to add port - %d:%u:%d\n",ps->id, (unsigned)pp.port, pp.proto); else _dpd.debugMsg(DEBUG_LOG,"Installed ports - %d:%u:%d\n",ps->id, (unsigned)pp.port, pp.proto); } } } static void RegisterPattern(void **patterns, Pattern *pattern) { if (!*patterns) { *patterns = _dpd.searchAPI->search_instance_new_ex(MPSE_ACF); if (!*patterns) { _dpd.errMsg( "Error initializing the pattern table"); return; } } _dpd.searchAPI->search_instance_add_ex(*patterns, (char *)pattern->data, pattern->length, pattern, STR_SEARCH_CASE_SENSITIVE); } /**Creates unique subset of services registered on ports, and then creates pattern trees. */ static void createServicePatternTrees( tAppIdConfig *pConfig ) { PatternService *ps; Pattern *pattern; Port *port; unsigned i; for (ps = pConfig->servicePortPattern->servicePortPattern; ps; ps = ps->next) { for (port = ps->port; port; port = port->next) { for (pattern = ps->pattern; pattern; pattern = pattern->next) { if (ps->proto == IPPROTO_TCP) RegisterPattern(&pConfig->servicePortPattern->tcpPortPatternTree[port->port], pattern); else RegisterPattern(&pConfig->servicePortPattern->udpPortPatternTree[port->port], pattern); } } } for (i = 0; i < 65536; i++) { if (pConfig->servicePortPattern->tcpPortPatternTree[i]) { for (ps = pConfig->servicePortPattern->servicePortPattern; ps; ps = ps->next) { if (ps->port || (ps->proto != IPPROTO_TCP)) continue; for (pattern = ps->pattern; pattern; pattern = pattern->next) RegisterPattern(&pConfig->servicePortPattern->tcpPortPatternTree[i], pattern); } _dpd.searchAPI->search_instance_prep(pConfig->servicePortPattern->tcpPortPatternTree[i]); } if (pConfig->servicePortPattern->udpPortPatternTree[i]) { for (ps = pConfig->servicePortPattern->servicePortPattern; ps; ps = ps->next) { if (ps->port || (ps->proto != IPPROTO_UDP)) continue; for (pattern = ps->pattern; pattern; pattern = pattern->next) RegisterPattern(&pConfig->servicePortPattern->udpPortPatternTree[i], pattern); } _dpd.searchAPI->search_instance_prep(pConfig->servicePortPattern->udpPortPatternTree[i]); } } } static void createClientPatternTrees( tAppIdConfig *pConfig ) { PatternService *ps; Pattern *pattern; for (ps = pConfig->clientPortPattern->servicePortPattern; ps; ps = ps->next) { for (pattern = ps->pattern; pattern; pattern = pattern->next) { if (ps->proto == IPPROTO_TCP) RegisterPattern(&pConfig->clientPortPattern->tcp_patterns, pattern); else RegisterPattern(&pConfig->clientPortPattern->udp_patterns, pattern); } } } void registerServicePatterns( tAppIdConfig *pConfig ) { PatternService *ps; Pattern *pattern; /**Register patterns with no associated ports, to RNA and local * pattern tree. Register patterns with ports with local pattern * tree only. */ for (ps = pConfig->servicePortPattern->servicePortPattern; ps; ps = ps->next) { if (!ps->port) { for (pattern = ps->pattern; pattern; pattern = pattern->next) { if (pattern->data && pattern->length) { if (ps->proto == IPPROTO_TCP) { _dpd.debugMsg(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length); initServiceApi->RegisterPattern(&service_validate, IPPROTO_TCP, pattern->data, pattern->length, pattern->offset, "pattern", initServiceApi->pAppidConfig); RegisterPattern(&pConfig->servicePortPattern->tcp_patterns, pattern); } else { _dpd.debugMsg(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length); initServiceApi->RegisterPattern(&service_validate, IPPROTO_UDP, pattern->data, pattern->length, pattern->offset, "pattern", initServiceApi->pAppidConfig); RegisterPattern(&pConfig->servicePortPattern->udp_patterns, pattern); } } } } else { for (pattern = ps->pattern; pattern; pattern = pattern->next) ps->count++; } } if (pConfig->servicePortPattern->tcp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->servicePortPattern->tcp_patterns); } if (pConfig->servicePortPattern->udp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->servicePortPattern->udp_patterns); } } void registerClientPatterns( tAppIdConfig *pConfig ) { PatternService *ps; Pattern *pattern; /**Register patterns with no associated ports, to RNA and local * pattern tree. Register patterns with ports with local pattern * tree only. */ for (ps = pConfig->clientPortPattern->servicePortPattern; ps; ps = ps->next) { for (pattern = ps->pattern; pattern; pattern = pattern->next) { if (pattern->data && pattern->length) { if (ps->proto == IPPROTO_TCP) { _dpd.debugMsg(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length); initClientApi->RegisterPattern(&client_validate, IPPROTO_TCP, pattern->data, pattern->length, pattern->offset, initClientApi->pAppidConfig); RegisterPattern(&pConfig->clientPortPattern->tcp_patterns, pattern); } else { _dpd.debugMsg(DEBUG_LOG,"Adding pattern with length %u\n",pattern->length); initClientApi->RegisterPattern(&client_validate, IPPROTO_UDP, pattern->data, pattern->length, pattern->offset, initClientApi->pAppidConfig); RegisterPattern(&pConfig->clientPortPattern->udp_patterns, pattern); } } ps->count++; } } if (pConfig->clientPortPattern->tcp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->clientPortPattern->tcp_patterns); } if (pConfig->clientPortPattern->udp_patterns) { _dpd.searchAPI->search_instance_prep(pConfig->clientPortPattern->udp_patterns); } } void dumpPatterns(char *name, PatternService *pList) { PatternService *ps; Pattern *pattern; /**Register patterns with no associated ports, to RNA and local * pattern tree. Register patterns with ports with local pattern * tree only. */ _dpd.debugMsg(DEBUG_LOG,"Adding pattern for \"%s\"\n",name); for (ps = pList; ps; ps = ps->next) { for (pattern = ps->pattern; pattern; pattern = pattern->next) { _dpd.debugMsg(DEBUG_LOG,"\t%s, %d\n",pattern->data, pattern->length); if (pattern->data && pattern->length) { _dpd.debugMsg(DEBUG_LOG,"\t\t%s, %d\n",pattern->data, pattern->length); } } } } int portPatternFinalize(tAppIdConfig *pConfig) { if (pConfig->clientPortPattern) { read_patterns(pConfig->clientPortPattern->luaInjectedPatterns, &pConfig->clientPortPattern->servicePortPattern); createClientPatternTrees(pConfig); registerClientPatterns(pConfig); dumpPatterns("Client", pConfig->clientPortPattern->servicePortPattern); } if (pConfig->servicePortPattern) { read_patterns(pConfig->servicePortPattern->luaInjectedPatterns, &pConfig->servicePortPattern->servicePortPattern); install_ports(pConfig->servicePortPattern->servicePortPattern, initServiceApi); createServicePatternTrees(pConfig); registerServicePatterns(pConfig); dumpPatterns("Server", pConfig->servicePortPattern->servicePortPattern); } return 0; } static int pattern_service_init(const InitServiceAPI * const init_api) { initServiceApi = init_api; _dpd.debugMsg(DEBUG_LOG,"Initializing with instance %u\n",initServiceApi->instance_id); return 0; } static void pattern_service_clean(const CleanServiceAPI * const clean_api) { PatternService *ps; tAppIdConfig *pConfig = clean_api->pAppidConfig; if (pConfig->servicePortPattern && pConfig->servicePortPattern->servicePortPattern) { unsigned i; if (pConfig->servicePortPattern->tcp_patterns) _dpd.searchAPI->search_instance_free(pConfig->servicePortPattern->tcp_patterns); pConfig->servicePortPattern->tcp_patterns = NULL; if (pConfig->servicePortPattern->udp_patterns) _dpd.searchAPI->search_instance_free(pConfig->servicePortPattern->udp_patterns); pConfig->servicePortPattern->udp_patterns = NULL; for (i = 0; i < 65536; i++) { if (pConfig->servicePortPattern->tcpPortPatternTree[i]) { _dpd.searchAPI->search_instance_free(pConfig->servicePortPattern->tcpPortPatternTree[i]); pConfig->servicePortPattern->tcpPortPatternTree[i] = NULL; } if (pConfig->servicePortPattern->udpPortPatternTree[i]) { _dpd.searchAPI->search_instance_free(pConfig->servicePortPattern->udpPortPatternTree[i]); pConfig->servicePortPattern->udpPortPatternTree[i] = NULL; } } while (pConfig->servicePortPattern->servicePortPattern) { ps = pConfig->servicePortPattern->servicePortPattern; pConfig->servicePortPattern->servicePortPattern = ps->next; FreePatternService(ps); } } } typedef struct _P_SERVICE_MATCH { /**Matches are aggregated by PatternService first and then by patterns. next is used to walk matches by PatternService*/ struct _P_SERVICE_MATCH *next; /**Walks matches by pattern within a PatternService. */ struct _P_SERVICE_MATCH *ps_next; Pattern *data; } PServiceMatch; static PServiceMatch *free_servicematch_list; static int pattern_match(void* id, void *unused_tree, int index, void* data, void *unused_neg) { PServiceMatch **matches = (PServiceMatch **)data; Pattern *pd = (Pattern *)id; PServiceMatch *psm; PServiceMatch *sm; if (pd->offset >= 0 && pd->offset != index) return 0; /*find if previously this PS was matched. */ for (psm=*matches; psm; psm=psm->next) if (psm->data->ps == pd->ps) break; if (psm) { /*walks patterns within a PatternService. */ for (sm=psm; sm; sm=sm->ps_next) if (sm->data == pd) return 0; if (free_servicematch_list) { sm = free_servicematch_list; free_servicematch_list = sm->next; memset(sm, 0, sizeof(*sm)); } else if ((sm=calloc(1, sizeof(*sm))) == NULL) { _dpd.errMsg( "Failed to allocate a service match"); return 0; } sm->data = pd; sm->ps_next = psm->ps_next; psm->ps_next = sm; return 0; } else if (free_servicematch_list) { sm = free_servicematch_list; free_servicematch_list = sm->next; memset(sm, 0, sizeof(*sm)); } else if ((sm = calloc(1, sizeof(*sm))) == NULL) { _dpd.errMsg( "Failed to allocate a service match"); return 0; } sm->data = pd; sm->next = *matches; *matches = sm; return 0; } static int csdPatternTreeSearch(const uint8_t *data, uint16_t size, int protocol, SFSnortPacket *pkt, const tRNAServiceElement** serviceData, bool isClient, const tAppIdConfig *pConfig) { void *patternTree = NULL; PatternService *ps; PServiceMatch *matches = NULL; PServiceMatch *sm; PServiceMatch *psm; Pattern *pattern; if (!data || !pkt || !size) return 0; *serviceData = NULL; if (!isClient) { if (protocol == IPPROTO_UDP) patternTree = pConfig->servicePortPattern->udpPortPatternTree[pkt->src_port]; else patternTree = pConfig->servicePortPattern->tcpPortPatternTree[pkt->src_port]; } if (!patternTree) { if (protocol == IPPROTO_UDP) patternTree = (isClient)? pConfig->clientPortPattern->udp_patterns: pConfig->servicePortPattern->udp_patterns; else patternTree = (isClient)? pConfig->clientPortPattern->tcp_patterns: pConfig->servicePortPattern->tcp_patterns; } if (patternTree) { _dpd.searchAPI->search_instance_find_all(patternTree, (char *)data, size, 0, pattern_match, (void*)&matches); } if (matches == NULL) return 0; /*match highest count and then longest pattern. */ ps = NULL; for (sm = matches; sm; sm = sm->next) { /*walk all patterns in PatternService */ for (pattern = sm->data->ps->pattern; pattern; pattern = pattern->next) { for (psm = sm; psm; psm = psm->ps_next) if (pattern == psm->data) break; if (psm == NULL) break; } if (pattern == NULL) /*all patterns in PatternService were matched */ { if (ps) { if (sm->data->ps->count > ps->count) ps = sm->data->ps; else if (sm->data->ps->count == ps->count && sm->data->ps->longest > ps->longest) ps = sm->data->ps; } else ps = sm->data->ps; } } /*free match list */ while (matches) { while (matches->ps_next) { sm = matches->ps_next; matches->ps_next = sm->ps_next; sm->next = free_servicematch_list; free_servicematch_list = sm; } sm = matches; matches = sm->next; sm->next = free_servicematch_list; free_servicematch_list = sm; } if (ps == NULL) return 0; *serviceData = &svc_element; return ps->id; } static int service_validate(ServiceValidationArgs* args) { uint32_t id; const tRNAServiceElement *service = NULL; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; if (!data || !pattern_service_mod.api || !flowp || !pkt) return SERVICE_ENULL; if (!size) goto inprocess; if (dir != APP_ID_FROM_RESPONDER) goto inprocess; id = csdPatternTreeSearch(data, size, flowp->proto, pkt, &service, false, args->pConfig); if (!id) goto fail; pattern_service_mod.api->add_service(flowp, pkt, dir, &svc_element, id, NULL, NULL, NULL, NULL); return SERVICE_SUCCESS; inprocess: pattern_service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; fail: pattern_service_mod.api->fail_service(flowp, pkt, dir, &svc_element, pattern_service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } static CLIENT_APP_RETCODE client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { initClientApi = init_api; return CLIENT_APP_SUCCESS; } static CLIENT_APP_RETCODE client_init_tcp(const InitClientAppAPI * const init_api, SF_LIST *config) { return CLIENT_APP_SUCCESS; } static void client_clean(const CleanClientAppAPI * const clean_api) { tAppIdConfig *pConfig = clean_api->pAppidConfig; if (pConfig->clientPortPattern && pConfig->clientPortPattern->servicePortPattern) { if (pConfig->clientPortPattern->tcp_patterns) _dpd.searchAPI->search_instance_free(pConfig->clientPortPattern->tcp_patterns); pConfig->clientPortPattern->tcp_patterns = NULL; if (pConfig->clientPortPattern->udp_patterns) _dpd.searchAPI->search_instance_free(pConfig->clientPortPattern->udp_patterns); pConfig->clientPortPattern->udp_patterns = NULL; } } static CLIENT_APP_RETCODE client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { tAppId id; const tRNAServiceElement *service = NULL; if (!data || !flowp || !pkt) return CLIENT_APP_ENULL; if (!size) goto inprocess; if (dir == APP_ID_FROM_RESPONDER) goto inprocess; id = csdPatternTreeSearch(data, size, flowp->proto, pkt, &service, true, (tAppIdConfig *)pConfig); if (!id) goto fail; pattern_tcp_client_mod.api->add_app(pkt, dir, pConfig, flowp, id, id, NULL); return CLIENT_APP_SUCCESS; inprocess: return CLIENT_APP_INPROCESS; fail: return CLIENT_APP_EINVALID; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_dns.h0000644000175000017500000000320014241075411025502 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DETECTOR_DNS_H__ #define __DETECTOR_DNS_H__ #include #include "service_api.h" #include "serviceConfig.h" extern struct RNAServiceValidationModule dns_service_mod; extern struct RNAClientAppModule dns_udp_client_mod; extern struct RNAClientAppModule dns_tcp_client_mod; int dns_host_scan_hostname(const u_int8_t*, size_t, tAppId*, tAppId*, const tServiceDnsConfig*); void service_dns_host_clean(tServiceDnsConfig *pConfig); int dns_host_detector_process_patterns(tServiceDnsConfig *pConfig); int dns_add_host_pattern(uint8_t *, size_t , uint8_t , tAppId , tServiceDnsConfig *); void dns_detector_free_patterns(tServiceDnsConfig *pConfig); char *dns_parse_host(const uint8_t *host, uint8_t host_len); #endif /* __DETECTOR_DNS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_pattern.h0000644000175000017500000000452314241075417026412 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SERVICE_PATTERN_H__ #define __SERVICE_PATTERN_H__ #include "service_api.h" extern tRNAServiceValidationModule pattern_service_mod; typedef struct PortPatternNode { tAppId appId; unsigned char protocol; unsigned short port; unsigned char *pattern; unsigned length; int32_t offset; char *detectorName; struct PortPatternNode *next; } tPortPatternNode; struct _PATTERN_SERVICE; typedef struct _PATTERN { struct _PATTERN *next; unsigned length; int offset; uint8_t *data; struct _PATTERN_SERVICE *ps; } Pattern; typedef struct _PORT { struct _PORT *next; uint16_t port; } Port; /**list for pattern services. Each pattern service is unique for a given uuid. */ typedef struct _PATTERN_SERVICE { struct _PATTERN_SERVICE *next; tAppId id; Pattern *pattern; Port *port; unsigned proto; unsigned count; unsigned longest; } PatternService; typedef struct ServicePortPattern { struct PortPatternNode *luaInjectedPatterns; PatternService *servicePortPattern; void *tcp_patterns; void *udp_patterns; void *tcpPortPatternTree[65536]; void *udpPortPatternTree[65536]; } tServicePortPattern; typedef struct ClientPortPattern { struct PortPatternNode *luaInjectedPatterns; PatternService *servicePortPattern; void *tcp_patterns; void *udp_patterns; } tClientPortPattern; #endif /* __SERVICE_PATTERN_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/http_url_patterns.h0000644000175000017500000000341114241075433026616 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __HTTP_URL_PATTERNS_H__ #define __HTTP_URL_PATTERNS_H__ #include #include #include "httpCommon.h" int addMlmpPattern(void *mlpMatcher, HostUrlPatternsList **hostUrlPatternsList, const uint8_t *host_pattern, int host_pattern_size, const uint8_t *path_pattern, int path_pattern_size, const uint8_t *query_pattern, int query_pattern_size, tAppId appId, uint32_t payload_id, uint32_t service_id, uint32_t client_id, DHPSequence seq); void destroyHostUrlMatcher(void **mlpMatcher); int matchQueryElements(tMlpPattern *packetData, tMlpPattern *userPattern, char *appVersion, size_t appVersionSize); uint32_t parseMultipleHTTPPatterns(const char *pattern, tMlmpPattern *parts, u_int32_t numPartLimit, int level); void destroyHostUrlPatternList(HostUrlPatternsList **pHostUrlPatternsList); #endif /*__HTTP_URL_PATTERNS_H__ */ snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/http_url_patterns.c0000644000175000017500000001712214241075432026614 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include "string.h" #include "sf_multi_mpse.h" #include "httpCommon.h" #include "http_url_patterns.h" #include "commonAppMatcher.h" #define FP_OPERATION_AND "%&%" #define PATTERN_PART_MAX 10 static void destroyHostUrlDetectorPattern(HostUrlDetectorPattern *pattern) { if (!pattern) return; destroyHostUrlDetectorPattern(pattern->next); if (pattern->host.pattern) free(*(void **)&pattern->host.pattern); if (pattern->path.pattern) free(*(void **)&pattern->path.pattern); if (pattern->query.pattern) free(*(void **)&pattern->query.pattern); free(pattern); } static int addHostUrlPatternToList(HostUrlDetectorPattern *detector, HostUrlPatternsList **hostUrlPatternsList) { if (!detector) return -1; if (!(*hostUrlPatternsList)) { if ((*hostUrlPatternsList = malloc(sizeof(HostUrlPatternsList))) == NULL) return -1; (*hostUrlPatternsList)->head = detector; (*hostUrlPatternsList)->tail = detector; } else { (*hostUrlPatternsList)->tail->next = detector; (*hostUrlPatternsList)->tail = detector; } return 0; } void destroyHostUrlPatternList(HostUrlPatternsList **pHostUrlPatternsList) { if (!(*pHostUrlPatternsList)) return; destroyHostUrlDetectorPattern((*pHostUrlPatternsList)->head); free(*pHostUrlPatternsList); *pHostUrlPatternsList = NULL; } int addMlmpPattern(void *hostUrlMatcher, HostUrlPatternsList **hostUrlPatternsList, const uint8_t *host_pattern, int host_pattern_size, const uint8_t *path_pattern, int path_pattern_size, const uint8_t *query_pattern, int query_pattern_size, tAppId appId, uint32_t payload_id, uint32_t service_id, uint32_t client_id, DHPSequence seq) { static tMlmpPattern patterns[PATTERN_PART_MAX]; int num_patterns; if (!host_pattern) return -1; if (!hostUrlMatcher) return -1; HostUrlDetectorPattern *detector = malloc(sizeof(*detector)); if (!detector) return -1; detector->host.pattern = (uint8_t *)strdup((char *)host_pattern); if (!detector->host.pattern) { free(detector); return -1; } if (path_pattern) { detector->path.pattern = (uint8_t *)strdup((char *)path_pattern); if (!detector->path.pattern) { free((void*)detector->host.pattern); free(detector); return -1; } } else { detector->path.pattern = NULL; } if (query_pattern) { detector->query.pattern = (uint8_t *)strdup((char *)query_pattern); if (!detector->query.pattern) { free((void*)detector->host.pattern); free((void*)detector->path.pattern); free(detector); return -1; } } else { detector->query.pattern = NULL; } detector->host.patternSize = host_pattern_size; detector->path.patternSize = path_pattern_size; detector->query.patternSize = query_pattern_size; detector->payload_id = payload_id; detector->service_id = service_id; detector->client_id = client_id; detector->seq = seq; detector->next = NULL; if (appId > APP_ID_NONE) detector->appId = appId; else if (payload_id > APP_ID_NONE) detector->appId = payload_id; else if (client_id > APP_ID_NONE) detector->appId = client_id; else detector->appId = service_id; num_patterns = parseMultipleHTTPPatterns((const char *)host_pattern, patterns, PATTERN_PART_MAX, 0); if (path_pattern) num_patterns += parseMultipleHTTPPatterns((const char *)path_pattern, patterns+num_patterns, PATTERN_PART_MAX-num_patterns, 1); patterns[num_patterns].pattern = NULL; if (addHostUrlPatternToList(detector, hostUrlPatternsList)) return -1; return mlmpAddPattern(hostUrlMatcher, patterns, detector); } u_int32_t parseMultipleHTTPPatterns(const char *pattern, tMlmpPattern *parts, u_int32_t numPartLimit, int level) { u_int32_t partNum = 0; const char *tmp; char *tmp2; u_int32_t i; if (!pattern) return 0; tmp = pattern; while (tmp && (partNum < numPartLimit)) { tmp2 = strstr(tmp, FP_OPERATION_AND); if (tmp2) { parts[partNum].pattern = (uint8_t *)strndup(tmp, tmp2-tmp); if (parts[partNum].pattern) { parts[partNum].patternSize = strlen((const char*)parts[partNum].pattern); tmp = tmp2+strlen(FP_OPERATION_AND); } } else { parts[partNum].pattern = (uint8_t *)strdup(tmp); if (parts[partNum].pattern) { parts[partNum].patternSize = strlen((const char*)parts[partNum].pattern); tmp = NULL; } } parts[partNum].level = level; if (!parts[partNum].pattern) { for (i = 0; i <= partNum; i++) free((void*)parts[i].pattern); _dpd.errMsg( "Failed to allocate memory"); return 0; } partNum++; } return partNum; } /**recursively destroy matcher. */ void destroyHostUrlMatcher(void **hostUrlMatcher) { if (hostUrlMatcher && *hostUrlMatcher) { mlmpDestroy(*hostUrlMatcher); *hostUrlMatcher = NULL; } } int matchQueryElements( tMlpPattern *packetData, tMlpPattern *userPattern, char *appVersion, size_t appVersionSize ) { const u_int8_t *index, *endKey; const u_int8_t *queryEnd; u_int32_t extractedSize; u_int32_t copySize = 0; if (appVersion == NULL) return 0; appVersion[0] = '\0'; if (!userPattern->pattern || !packetData->pattern) return 0; /*queryEnd is 1 past the end. */ queryEnd = packetData->pattern + packetData->patternSize; index = packetData->pattern; endKey = queryEnd; /*?key1=value1&key2=value2 */ for (index = packetData->pattern; index < queryEnd; index = endKey+1) { /*find end of query tuple */ endKey = memchr ( index, '&', queryEnd - index ); if (!endKey) endKey = queryEnd; if (userPattern->patternSize < (u_int32_t)(endKey-index)) { if (memcmp(index, userPattern->pattern, userPattern->patternSize) == 0) { index += userPattern->patternSize; extractedSize = (endKey-index); appVersionSize--; copySize = (extractedSize < appVersionSize) ? extractedSize: appVersionSize; memcpy(appVersion, index, copySize); appVersion[copySize] = '\0'; break; } } } return copySize; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_base.c0000644000175000017500000000701414241075403025633 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include "detector_base.h" #include "detector_api.h" #include "detector_http.h" static void *detector_flowdata_get(tAppIdData *flowp, unsigned detector_id); static int detector_flowdata_add(tAppIdData *flowp, void *data, unsigned detector_id, AppIdFreeFCN fcn); static const DetectorApi detector_api = { .data_get = &detector_flowdata_get, .data_add = &detector_flowdata_add, }; extern RNADetectorValidationModule imap_detector_mod; extern RNADetectorValidationModule pop3_detector_mod; extern RNADetectorValidationModule smtp_detector_mod; extern RNADetectorValidationModule kerberos_detector_mod; extern RNADetectorValidationModule pattern_detector_mod; static RNADetectorValidationModule *static_detector_list[] = { &imap_detector_mod, &pop3_detector_mod, &smtp_detector_mod, &kerberos_detector_mod }; /**callback function for initializing static and dynamic detectors. */ static int detectorLoadCallback(void *symbol) { static unsigned detector_module_index = 0; RNADetectorValidationModule *svm = (RNADetectorValidationModule *)symbol; if (detector_module_index >= 65536) { _dpd.errMsg( "Maximum number of detector modules exceeded"); return -1; } if (svm->service) { if (serviceLoadCallback(svm->service)) { return -1; } } if (svm->client) { if (clientAppLoadCallback(svm->client)) { return -1; } } svm->api = &detector_api; svm->flow_data_index = detector_module_index | APPID_SESSION_DATA_DETECTOR_MODSTATE_BIT; svm->streamAPI = _dpd.streamAPI; detector_module_index++; return 0; } int LoadDetectorModules(const char **dir_list) { unsigned i; for (i=0; i #include #include #include #include #include #include "appIdApi.h" #include "appInfoTable.h" #include "detector_api.h" /*#define DEBUG_KERBEROS 1 */ typedef enum { KRB_STATE_TCP_LENGTH, KRB_STATE_APP, KRB_STATE_SEQ, KRB_STATE_VERSION, KRB_STATE_VERSION_2, KRB_STATE_VERSION_VALUE, KRB_STATE_TYPE, KRB_STATE_TYPE_VALUE, KRB_STATE_ERROR, KRB_STATE_ERROR_VALUE, KRB_STATE_FIELD, KRB_STATE_FIELD_DATA, KRB_STATE_FIELD_DATA_2, KRB_STATE_CNAME_SEQ, KRB_STATE_CNAME_TYPE, KRB_STATE_CNAME_TYPE_2, KRB_STATE_CNAME_TYPE_VALUE, KRB_STATE_CNAME, KRB_STATE_CNAME_PRINCIPAL_SEQ, KRB_STATE_CNAME_PRINCIPAL_KS, KRB_STATE_CNAME_PRINCIPAL_DATA, KRB_STATE_CNAME_PRINCIPAL_DATA_2, KRB_STATE_LEN, KRB_STATE_LEN_2, KRB_STATE_REQBODY_SEQ, KRB_STATE_REQBODY_TYPE, KRB_STATE_FIELD_LEVEL2, KRB_STATE_FIELD_DATA_LEVEL2, KRB_STATE_FIELD_DATA_2_LEVEL2, KRB_STATE_INVALID, } KerberosState; /*error codes from RFC 4120 */ #define KDC_ERR_PREAUTH_FAILED 24 #define KRB_FLAG_AUTH_FAILED 0x01 #define KRB_FLAG_USER_DETECTED 0x02 #define KRB_FLAG_SERVICE_DETECTED 0x04 typedef enum { KRB_INPROCESS, KRB_FAILED, } KRB_RETCODE; typedef struct _KRB_STATE { KerberosState state; KerberosState next_state; uint8_t msg_type; unsigned msg_len; uint8_t tag; unsigned len; unsigned pos; int added; unsigned cname_len; char cname[256]; char ver[2]; #ifdef DEBUG_KERBEROS KerberosState last_state; #endif unsigned flags; } KRBState; typedef struct _KRB_CLIENT_APP_CONFIG { int enabled; int failedLogin; } KRB_CLIENT_APP_CONFIG; typedef struct _DETECTOR_DATA { KRBState clnt_state; KRBState svr_state; int set_flags; int need_continue; } DetectorData; static KRB_CLIENT_APP_CONFIG krb_client_config; static CLIENT_APP_RETCODE krb_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE krb_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static tRNAClientAppModule client_app_mod = { .name = "KERBEROS", .proto = IPPROTO_UDP, .init = &krb_client_init, .validate = &krb_client_validate, .minimum_matches = 1, .provides_user = 1, }; typedef struct { const uint8_t *pattern; unsigned length; } Detector_Pattern; static const uint8_t AS_REQ[] = "\x0a1\x003\x002\x001\x005\x0a2\x003\x002\x001\x00a"; static const uint8_t TGS_REQ[] = "\x0a1\x003\x002\x001\x005\x0a2\x003\x002\x001\x00c"; static const uint8_t AS_REQ_4[] = "\x0a1\x003\x002\x001\x004\x0a2\x003\x002\x001\x00a"; static const uint8_t TGS_REQ_4[] = "\x0a1\x003\x002\x001\x004\x0a2\x003\x002\x001\x00c"; static Detector_Pattern client_patterns[] = { {AS_REQ, sizeof(AS_REQ)-1}, {TGS_REQ, sizeof(TGS_REQ)-1}, {AS_REQ_4, sizeof(AS_REQ_4)-1}, {TGS_REQ_4, sizeof(TGS_REQ_4)-1}, }; static int krb_server_init(const InitServiceAPI * const init_api); static int krb_server_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &krb_server_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "kerberos", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&krb_server_validate, 88, IPPROTO_TCP}, {&krb_server_validate, 88, IPPROTO_UDP}, {NULL, 0, 0} }; static tRNAServiceValidationModule service_mod = { .name = "KRB", .init = &krb_server_init, .pp = pp, .provides_user = 1, }; static const uint8_t AS_REP[] = "\x0a0\x003\x002\x001\x005\x0a1\x003\x002\x001\x00b"; static const uint8_t TGS_REP[] = "\x0a0\x003\x002\x001\x005\x0a1\x003\x002\x001\x00d"; static const uint8_t AS_REP_4[] = "\x0a0\x003\x002\x001\x004\x0a1\x003\x002\x001\x00b"; static const uint8_t TGS_REP_4[] = "\x0a0\x003\x002\x001\x004\x0a1\x003\x002\x001\x00d"; static Detector_Pattern service_patterns[] = { {AS_REP, sizeof(AS_REP)-1}, {TGS_REP, sizeof(TGS_REP)-1}, {AS_REP_4, sizeof(AS_REP_4)-1}, {TGS_REP_4, sizeof(TGS_REP_4)-1}, }; SF_SO_PUBLIC RNADetectorValidationModule kerberos_detector_mod = { .service = &service_mod, .client = &client_app_mod, }; static tAppRegistryEntry appIdRegistry[] = { {APP_ID_KERBEROS, APPINFO_FLAG_CLIENT_USER | APPINFO_FLAG_SERVICE_ADDITIONAL} }; static CLIENT_APP_RETCODE krb_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; krb_client_config.enabled = 1; krb_client_config.failedLogin = 0; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { krb_client_config.enabled = atoi(item->value); } if (strcasecmp(item->name, "failed-login") == 0) { krb_client_config.failedLogin = atoi(item->value); } } } if (krb_client_config.enabled) { for (i=0; i < sizeof(client_patterns)/sizeof(*client_patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering pattern with length %u\n",client_patterns[i].length); init_api->RegisterPattern(&krb_client_validate, IPPROTO_UDP, client_patterns[i].pattern, client_patterns[i].length, -1, init_api->pAppidConfig); init_api->RegisterPattern(&krb_client_validate, IPPROTO_TCP, client_patterns[i].pattern, client_patterns[i].length, -1, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&krb_client_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static int krb_server_init(const InitServiceAPI * const init_api) { unsigned i; for (i=0; i < sizeof(service_patterns)/sizeof(*service_patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering pattern with length %u\n",service_patterns[i].length); init_api->RegisterPatternUser(&krb_server_validate, IPPROTO_UDP, service_patterns[i].pattern, service_patterns[i].length, -1, "kerberos", init_api->pAppidConfig); init_api->RegisterPatternUser(&krb_server_validate, IPPROTO_TCP, service_patterns[i].pattern, service_patterns[i].length, -1, "kerberos", init_api->pAppidConfig); } unsigned j; for (j=0; j < sizeof(appIdRegistry)/sizeof(*appIdRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdRegistry[j].appId); init_api->RegisterAppId(&krb_server_validate, appIdRegistry[j].appId, appIdRegistry[j].additionalInfo, init_api->pAppidConfig); } return 0; } #define ASN_1_APPLICATION 0x40 #define ASN_1_CONSTRUCT 0x20 #define ASN_1_TYPE_MASK 0xe0 #define AS_REQ_MSG_TYPE 0x0a #define AS_REP_MSG_TYPE 0x0b #define TGS_REQ_MSG_TYPE 0x0c #define TGS_REP_MSG_TYPE 0x0d #define ERROR_MSG_TYPE 0x1e static KRB_RETCODE krb_walk_client_packet(KRBState *krbs, const uint8_t *s, const uint8_t *end, tAppIdData *flowp, SFSnortPacket *pkt, int dir, const tAppIdConfig *pConfig) { static const uint8_t KRB_CLIENT_VERSION[] = "\x0a1\x003\x002\x001"; static const uint8_t KRB_CLIENT_TYPE[] = "\x0a2\x003\x002\x001"; static const uint8_t KRB_CNAME_TYPE[] = "\x0a0\x003\x002\x001"; #define KRB_CNAME_TYPE_SIZE (sizeof(KRB_CNAME_TYPE) - 1) while (s < end) { #ifdef DEBUG_KERBEROS if (krbs->state != krbs->last_state) { _dpd.debugMsg(DEBUG_LOG,"%p State %d\n",flowp, krbs->state); krbs->last_state = krbs->state; } #endif switch (krbs->state) { case KRB_STATE_TCP_LENGTH: if (krbs->pos >= 3) krbs->state = KRB_STATE_APP; else krbs->pos++; break; case KRB_STATE_APP: #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Type %u (%02X)\n",flowp, *s & (~ASN_1_TYPE_MASK), *s); #endif if ((*s & ASN_1_TYPE_MASK) != (ASN_1_APPLICATION|ASN_1_CONSTRUCT)) return KRB_FAILED; krbs->msg_type = *s & (~ASN_1_TYPE_MASK); switch (krbs->msg_type) { case AS_REQ_MSG_TYPE: case TGS_REQ_MSG_TYPE: case ERROR_MSG_TYPE: case 14: case 16: case 20: case 21: case 22: krbs->next_state = KRB_STATE_SEQ; break; default: return KRB_FAILED; } krbs->state = KRB_STATE_LEN; krbs->msg_len = 0xFFFFFFFF; break; case KRB_STATE_SEQ: if (krbs->len < 2 || *s != 0x30) return KRB_FAILED; krbs->msg_len = krbs->len; krbs->next_state = KRB_STATE_VERSION; krbs->state = KRB_STATE_LEN; krbs->pos = 0; break; case KRB_STATE_VERSION: if (krbs->len < 10 || krbs->len != krbs->msg_len) return KRB_FAILED; krbs->state = KRB_STATE_VERSION_2; krbs->pos = 0; case KRB_STATE_VERSION_2: if (*s != KRB_CLIENT_VERSION[krbs->pos]) return KRB_FAILED; krbs->pos++; if (krbs->pos >= sizeof(KRB_CLIENT_VERSION) - 1) krbs->state = KRB_STATE_VERSION_VALUE; break; case KRB_STATE_VERSION_VALUE: if (*s != 5 && *s != 4) return KRB_FAILED; krbs->state = KRB_STATE_TYPE; krbs->pos = 0; krbs->ver[0] = *s + '0'; break; case KRB_STATE_TYPE: if (*s != KRB_CLIENT_TYPE[krbs->pos]) return KRB_FAILED; if (krbs->pos >= (sizeof(KRB_CLIENT_TYPE) - 1) - 1) { krbs->state = KRB_STATE_TYPE_VALUE; break; } krbs->pos++; break; case KRB_STATE_TYPE_VALUE: if (*s != krbs->msg_type) return KRB_FAILED; krbs->state = KRB_STATE_FIELD; krbs->tag = 0xa2; break; case KRB_STATE_FIELD: #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Tag %02X\n",flowp, *s); #endif if (krbs->msg_len < 2 || *s <= krbs->tag || (*s & ASN_1_TYPE_MASK) != 0xa0) return KRB_FAILED; krbs->tag = *s; if (krbs->tag == 0xa4 && (krbs->msg_type == AS_REQ_MSG_TYPE || krbs->msg_type == TGS_REQ_MSG_TYPE) && krb_client_config.failedLogin) { krbs->next_state = KRB_STATE_REQBODY_SEQ; } else krbs->next_state = KRB_STATE_FIELD_DATA; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_FIELD_DATA: if (krbs->msg_len < krbs->len) return KRB_FAILED; krbs->state = KRB_STATE_FIELD_DATA_2; case KRB_STATE_FIELD_DATA_2: if (krbs->len <= 1) { if (krbs->msg_len <= 1) { #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Valid\n",flowp); #endif if (!krbs->added) { client_app_mod.api->add_app(pkt, dir, pConfig, flowp, APP_ID_KERBEROS, APP_ID_KERBEROS, krbs->ver); krbs->added = 1; } krbs->state = KRB_STATE_APP; if (!krbs->msg_len) continue; break; } krbs->state = KRB_STATE_FIELD; if (!krbs->len) continue; break; } krbs->len--; break; case KRB_STATE_REQBODY_SEQ: /*REQ_BODY is the last level 1 element in AS-REQ and TSG-REQ messages therefore */ /*a. its length is not maintained, remaining msg_len is assumed to be req_body length */ /*b. krbs->rtag is reused at level 2 */ if (*s != 0x30) return KRB_FAILED; krbs->next_state = KRB_STATE_FIELD_LEVEL2; krbs->state = KRB_STATE_LEN; krbs->tag = 0; break; case KRB_STATE_FIELD_LEVEL2: #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Tag %02X\n",flowp, *s); #endif if (krbs->msg_len <= 1) { krbs->state = KRB_STATE_APP; if (!krbs->msg_len) continue; break; } if (krbs->msg_len < 2 || *s <= krbs->tag || (*s & ASN_1_TYPE_MASK) != 0xa0) return KRB_FAILED; krbs->tag = *s; if (krbs->tag == 0xa1) krbs->next_state = KRB_STATE_CNAME_SEQ; else krbs->next_state = KRB_STATE_FIELD_DATA_LEVEL2; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_FIELD_DATA_LEVEL2: if (krbs->msg_len < krbs->len) return KRB_FAILED; krbs->next_state = KRB_STATE_FIELD_DATA_2_LEVEL2; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_FIELD_DATA_2_LEVEL2: if (krbs->len <= 1) { krbs->state = KRB_STATE_FIELD_LEVEL2; if (!krbs->len) continue; break; } krbs->len--; break; case KRB_STATE_CNAME_SEQ: if (krbs->len < (KRB_CNAME_TYPE_SIZE + 5) || krbs->len > krbs->msg_len || *s != 0x30) return KRB_FAILED; krbs->cname_len = krbs->len; krbs->next_state = KRB_STATE_CNAME_TYPE; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_TYPE: if (krbs->len > krbs->cname_len || krbs->len < (KRB_CNAME_TYPE_SIZE + 3)) return KRB_FAILED; krbs->state = KRB_STATE_CNAME_TYPE_2; krbs->pos = 0; case KRB_STATE_CNAME_TYPE_2: if (*s != KRB_CNAME_TYPE[krbs->pos]) return KRB_FAILED; krbs->pos++; if (krbs->pos >= KRB_CNAME_TYPE_SIZE) krbs->state = KRB_STATE_CNAME_TYPE_VALUE; break; case KRB_STATE_CNAME_TYPE_VALUE: if (krbs->cname_len < 3 || (*s > 7 && *s != 10)) return KRB_FAILED; if (*s != 1) { krbs->len = krbs->cname_len; krbs->state = KRB_STATE_FIELD_DATA_2; break; } krbs->state = KRB_STATE_CNAME; break; case KRB_STATE_CNAME: if (krbs->cname_len < 3 || *s != 0xa1) return KRB_FAILED; krbs->next_state = KRB_STATE_CNAME_PRINCIPAL_SEQ; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_PRINCIPAL_SEQ: if (krbs->len != krbs->cname_len || krbs->cname_len < 3 || *s != 0x30) return KRB_FAILED; krbs->next_state = KRB_STATE_CNAME_PRINCIPAL_KS; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_PRINCIPAL_KS: if (krbs->len != krbs->cname_len || krbs->cname_len < 3 || *s != 0x1b) return KRB_FAILED; krbs->next_state = KRB_STATE_CNAME_PRINCIPAL_DATA; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_PRINCIPAL_DATA: if (krbs->len != krbs->cname_len) return KRB_FAILED; krbs->state = KRB_STATE_CNAME_PRINCIPAL_DATA_2; krbs->pos = 0; case KRB_STATE_CNAME_PRINCIPAL_DATA_2: if (krbs->len) { if (krbs->pos < (sizeof(krbs->cname) - 2)) { if (isalnum(*s) || *s == '.' || *s == '@' || *s == '-' || *s == '_' || *s == '`' || *s == ' ') { krbs->cname[krbs->pos] = *s; krbs->pos++; } else { krbs->len = krbs->cname_len; krbs->state = KRB_STATE_FIELD_DATA_2; break; } } } if (krbs->len <= 1) { if (krbs->pos) { #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Name %u\n",flowp, krbs->pos); #endif krbs->cname[krbs->pos] = 0; } if (krbs->msg_len <= 1) { krbs->state = KRB_STATE_APP; if (!krbs->msg_len) continue; } krbs->state = KRB_STATE_FIELD_LEVEL2; if (!krbs->len) continue; break; } krbs->len--; break; case KRB_STATE_LEN: if (*s & 0x80) { krbs->pos = *s & 0x7F; if (!krbs->pos || krbs->pos > 4) { /* Not handling indeterminate length or length greater than 32 bits */ return KRB_FAILED; } krbs->len = 0; krbs->state = KRB_STATE_LEN_2; } else { krbs->len = *s; krbs->state = krbs->next_state; } break; case KRB_STATE_LEN_2: if (krbs->msg_len) { krbs->len <<= 8; krbs->len |= *s; if (krbs->pos <= 1) { krbs->state = krbs->next_state; break; } krbs->pos--; } else return KRB_FAILED; break; default: /* This should never happen */ return KRB_FAILED; } krbs->msg_len--; krbs->cname_len--; s++; } return KRB_INPROCESS; } static KRB_RETCODE krb_walk_server_packet(KRBState *krbs, const uint8_t *s, const uint8_t *end, tAppIdData *flowp, SFSnortPacket *pkt, const int dir, const char * reqCname) { static const uint8_t KRB_SERVER_VERSION[] = "\x0a0\x003\x002\x001"; static const uint8_t KRB_SERVER_TYPE[] = "\x0a1\x003\x002\x001"; static const uint8_t KRB_CNAME_TYPE[] = "\x0a0\x003\x002\x001"; static const uint8_t KRB_ERROR[] = "\x003\x002\x001"; #define KRB_CNAME_TYPE_SIZE (sizeof(KRB_CNAME_TYPE) - 1) while (s < end) { #ifdef DEBUG_KERBEROS if (krbs->state != krbs->last_state) { _dpd.debugMsg(DEBUG_LOG,"%p State %d\n",flowp, krbs->state); krbs->last_state = krbs->state; } #endif switch (krbs->state) { case KRB_STATE_TCP_LENGTH: if (krbs->pos >= 3) krbs->state = KRB_STATE_APP; else krbs->pos++; break; case KRB_STATE_APP: #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Type %u (%02X)\n",flowp, *s & (~ASN_1_TYPE_MASK), *s); #endif if ((*s & ASN_1_TYPE_MASK) != (ASN_1_APPLICATION|ASN_1_CONSTRUCT)) return KRB_FAILED; krbs->msg_type = *s & (~ASN_1_TYPE_MASK); switch (krbs->msg_type) { case AS_REP_MSG_TYPE: case TGS_REP_MSG_TYPE: case ERROR_MSG_TYPE: case 15: case 17: case 20: case 21: case 22: krbs->next_state = KRB_STATE_SEQ; break; default: return KRB_FAILED; } krbs->state = KRB_STATE_LEN; krbs->msg_len = 0xFFFFFFFF; break; case KRB_STATE_SEQ: if (krbs->len < 2 || *s != 0x30) return KRB_FAILED; krbs->msg_len = krbs->len; krbs->next_state = KRB_STATE_VERSION; krbs->state = KRB_STATE_LEN; krbs->pos = 0; break; case KRB_STATE_VERSION: if (krbs->len < 10 || krbs->len != krbs->msg_len) return KRB_FAILED; krbs->state = KRB_STATE_VERSION_2; krbs->pos = 0; case KRB_STATE_VERSION_2: if (*s != KRB_SERVER_VERSION[krbs->pos]) return KRB_FAILED; krbs->pos++; if (krbs->pos >= sizeof(KRB_SERVER_VERSION) - 1) krbs->state = KRB_STATE_VERSION_VALUE; break; case KRB_STATE_VERSION_VALUE: if (*s != 5 && *s != 4) return KRB_FAILED; krbs->state = KRB_STATE_TYPE; krbs->pos = 0; krbs->ver[0] = *s + '0'; break; case KRB_STATE_TYPE: if (*s != KRB_SERVER_TYPE[krbs->pos]) return KRB_FAILED; if (krbs->pos >= (sizeof(KRB_SERVER_TYPE) - 1) - 1) { krbs->state = KRB_STATE_TYPE_VALUE; break; } krbs->pos++; break; case KRB_STATE_TYPE_VALUE: if (*s != krbs->msg_type) return KRB_FAILED; krbs->state = KRB_STATE_FIELD; krbs->tag = 0xa1; break; case KRB_STATE_ERROR: if (*s != KRB_ERROR[krbs->pos]) return KRB_FAILED; if (krbs->pos >= (sizeof(KRB_ERROR) - 1) - 1) { krbs->state = KRB_STATE_ERROR_VALUE; break; } krbs->pos++; break; case KRB_STATE_ERROR_VALUE: #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Error %u\n",flowp, *s); #endif if (krbs->msg_len <= 1) { krbs->flags |= KRB_FLAG_SERVICE_DETECTED; krbs->state = KRB_STATE_APP; if (!krbs->msg_len) continue; break; } if (*s == KDC_ERR_PREAUTH_FAILED) { #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p unAuthorized\n",flowp); #endif krbs->flags |= KRB_FLAG_AUTH_FAILED; } krbs->state = KRB_STATE_FIELD; break; case KRB_STATE_FIELD: #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Tag %02X\n",flowp, *s); #endif if (krbs->msg_len < 2 || *s <= krbs->tag || (*s & ASN_1_TYPE_MASK) != 0xa0) return KRB_FAILED; krbs->tag = *s; if ((krbs->tag == 0xa4 && (krbs->msg_type == AS_REP_MSG_TYPE || krbs->msg_type == TGS_REP_MSG_TYPE)) || (krbs->tag == 0xa8 && (krbs->msg_type == ERROR_MSG_TYPE))) { krbs->next_state = KRB_STATE_CNAME_SEQ; } else if (krbs->tag == 0xa6 && krbs->msg_type == ERROR_MSG_TYPE) { krbs->state = KRB_STATE_ERROR; krbs->pos = 0; break; } else krbs->next_state = KRB_STATE_FIELD_DATA; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_FIELD_DATA: if (krbs->msg_len < krbs->len) return KRB_FAILED; krbs->state = KRB_STATE_FIELD_DATA_2; case KRB_STATE_FIELD_DATA_2: if (krbs->len <= 1) { if (krbs->msg_len <= 1) { krbs->flags |= KRB_FLAG_SERVICE_DETECTED; krbs->state = KRB_STATE_APP; if (!krbs->msg_len) continue; break; } krbs->state = KRB_STATE_FIELD; if (!krbs->len) continue; break; } krbs->len--; break; case KRB_STATE_CNAME_SEQ: if (krbs->len < (KRB_CNAME_TYPE_SIZE + 5) || krbs->len > krbs->msg_len || *s != 0x30) return KRB_FAILED; krbs->cname_len = krbs->len; krbs->next_state = KRB_STATE_CNAME_TYPE; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_TYPE: if (krbs->len > krbs->cname_len || krbs->len < (KRB_CNAME_TYPE_SIZE + 3)) return KRB_FAILED; krbs->state = KRB_STATE_CNAME_TYPE_2; krbs->pos = 0; case KRB_STATE_CNAME_TYPE_2: if (*s != KRB_CNAME_TYPE[krbs->pos]) return KRB_FAILED; krbs->pos++; if (krbs->pos >= KRB_CNAME_TYPE_SIZE) krbs->state = KRB_STATE_CNAME_TYPE_VALUE; break; case KRB_STATE_CNAME_TYPE_VALUE: if (krbs->cname_len < 3 || (*s > 7 && *s != 10)) return KRB_FAILED; if (*s != 1) { krbs->len = krbs->cname_len; krbs->state = KRB_STATE_FIELD_DATA_2; break; } krbs->state = KRB_STATE_CNAME; break; case KRB_STATE_CNAME: if (krbs->cname_len < 3 || *s != 0xa1) return KRB_FAILED; krbs->next_state = KRB_STATE_CNAME_PRINCIPAL_SEQ; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_PRINCIPAL_SEQ: if (krbs->len != krbs->cname_len || krbs->cname_len < 3 || *s != 0x30) return KRB_FAILED; krbs->next_state = KRB_STATE_CNAME_PRINCIPAL_KS; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_PRINCIPAL_KS: if (krbs->len != krbs->cname_len || krbs->cname_len < 3 || *s != 0x1b) return KRB_FAILED; krbs->next_state = KRB_STATE_CNAME_PRINCIPAL_DATA; krbs->state = KRB_STATE_LEN; break; case KRB_STATE_CNAME_PRINCIPAL_DATA: if (krbs->len != krbs->cname_len) return KRB_FAILED; krbs->state = KRB_STATE_CNAME_PRINCIPAL_DATA_2; krbs->pos = 0; case KRB_STATE_CNAME_PRINCIPAL_DATA_2: if (krbs->len) { if (krbs->pos < (sizeof(krbs->cname) - 2)) { if (isalnum(*s) || *s == '.' || *s == '@' || *s == '-' || *s == '_' || *s == '`' || *s == ' ') { krbs->cname[krbs->pos] = *s; krbs->pos++; } else { krbs->len = krbs->cname_len; krbs->state = KRB_STATE_FIELD_DATA_2; break; } } } if (krbs->len <= 1) { if (krbs->pos) { #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Name %u\n",flowp, krbs->pos); #endif krbs->cname[krbs->pos] = 0; krbs->flags |= KRB_FLAG_USER_DETECTED; } if (krbs->msg_len <= 1) { krbs->flags |= KRB_FLAG_SERVICE_DETECTED; krbs->state = KRB_STATE_APP; if (!krbs->msg_len) continue; } krbs->state = KRB_STATE_FIELD; if (!krbs->len) continue; break; } krbs->len--; break; case KRB_STATE_LEN: if (*s & 0x80) { krbs->pos = *s & 0x7F; if (!krbs->pos || krbs->pos > 4) { /* Not handling indeterminate length or length greater than 32 bits */ return KRB_FAILED; } krbs->len = 0; krbs->state = KRB_STATE_LEN_2; } else { krbs->len = *s; krbs->state = krbs->next_state; } break; case KRB_STATE_LEN_2: if (krbs->msg_len) { krbs->len <<= 8; krbs->len |= *s; if (krbs->pos <= 1) { krbs->state = krbs->next_state; break; } krbs->pos--; } else return KRB_FAILED; break; default: /* This should never happen */ return KRB_FAILED; } krbs->msg_len--; krbs->cname_len--; s++; } if (krbs->msg_len <= 1) { /*end of server response message */ #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Valid\n",flowp); #endif if (krbs->flags & KRB_FLAG_SERVICE_DETECTED) { if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED) && pkt) { service_mod.api->add_service(flowp, pkt, dir, &svc_element, APP_ID_KERBEROS, NULL, krbs->ver, NULL, NULL); setAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED); } } if (krbs->flags & KRB_FLAG_AUTH_FAILED) { if (krb_client_config.failedLogin && ((krbs->flags & KRB_FLAG_USER_DETECTED) || reqCname)) { service_mod.api->add_user(flowp, (krbs->flags & KRB_FLAG_USER_DETECTED)? krbs->cname:reqCname, APP_ID_LDAP, 0); } } else if (krbs->flags & KRB_FLAG_USER_DETECTED) { service_mod.api->add_user(flowp, krbs->cname, APP_ID_LDAP, 1); } krbs->flags = 0; } return KRB_INPROCESS; } static CLIENT_APP_RETCODE krb_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { const uint8_t *s = data; const uint8_t *end = (data + size); DetectorData *fd; #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG, "%p Processing %u %u->%u %u %d", flowp, flowp->proto, pkt->src_port, pkt->dst_port, size, dir); #endif #ifdef APP_ID_USES_REASSEMBLED kerberos_detector_mod.streamAPI->response_flush_stream(pkt); #endif if (!size) return CLIENT_APP_INPROCESS; fd = kerberos_detector_mod.api->data_get(flowp, kerberos_detector_mod.flow_data_index); if (!fd) { if ((fd = calloc(1, sizeof(*fd))) == NULL) return CLIENT_APP_ENOMEM; if (kerberos_detector_mod.api->data_add(flowp, fd, kerberos_detector_mod.flow_data_index, &free)) { free(fd); return CLIENT_APP_ENOMEM; } if (flowp->proto == IPPROTO_TCP) { fd->clnt_state.state = KRB_STATE_TCP_LENGTH; fd->svr_state.state = KRB_STATE_TCP_LENGTH; } else { fd->clnt_state.state = KRB_STATE_APP; fd->svr_state.state = KRB_STATE_APP; } #ifdef DEBUG_KERBEROS fd->clnt_state.last_state = KRB_STATE_INVALID; fd->svr_state.last_state = KRB_STATE_INVALID; #endif } if (!fd->set_flags) { fd->need_continue = 1; fd->set_flags = 1; setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } if (dir == APP_ID_FROM_INITIATOR) { if (krb_walk_client_packet(&fd->clnt_state, s, end, flowp, pkt, dir, pConfig) == KRB_FAILED) { #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Failed\n",flowp); #endif setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); return CLIENT_APP_SUCCESS; } } else if (krb_walk_server_packet(&fd->svr_state, s, end, flowp, NULL, dir, fd->clnt_state.cname) == KRB_FAILED) { #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Server Failed\n",flowp); #endif clearAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } return CLIENT_APP_INPROCESS; } static int krb_server_validate(ServiceValidationArgs* args) { DetectorData *fd; tAppIdData *flowp = args->flowp; const uint8_t *data = args->data; SFSnortPacket *pkt = args->pkt; const int dir = args->dir; uint16_t size = args->size; const uint8_t *s = data; const uint8_t *end = (data + size); #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG, "%p Processing %u %u->%u %u %d", flowp, flowp->proto, pkt->src_port, pkt->dst_port, size, dir); #endif if (dir != APP_ID_FROM_RESPONDER) goto inprocess; #ifdef APP_ID_USES_REASSEMBLED kerberos_detector_mod.streamAPI->response_flush_stream(pkt); #endif if (!size) goto inprocess; fd = kerberos_detector_mod.api->data_get(flowp, kerberos_detector_mod.flow_data_index); if (!fd) { if ((fd = calloc(1, sizeof(*fd))) == NULL) return SERVICE_ENOMEM; if (kerberos_detector_mod.api->data_add(flowp, fd, kerberos_detector_mod.flow_data_index, &free)) { free(fd); return SERVICE_ENOMEM; } if (flowp->proto == IPPROTO_TCP) { fd->clnt_state.state = KRB_STATE_TCP_LENGTH; fd->svr_state.state = KRB_STATE_TCP_LENGTH; } else { fd->clnt_state.state = KRB_STATE_APP; fd->svr_state.state = KRB_STATE_APP; } #ifdef DEBUG_KERBEROS fd->clnt_state.last_state = KRB_STATE_INVALID; fd->svr_state.last_state = KRB_STATE_INVALID; #endif } if (fd->need_continue) setAppIdFlag(flowp, APPID_SESSION_CONTINUE); else { clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); if (getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) return SERVICE_SUCCESS; } if (krb_walk_server_packet(&fd->svr_state, s, end, flowp, pkt, dir, fd->clnt_state.cname) == KRB_FAILED) { #ifdef DEBUG_KERBEROS _dpd.debugMsg(DEBUG_LOG,"%p Failed\n",flowp); #endif if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { service_mod.api->fail_service(flowp, pkt, dir, &svc_element, service_mod.flow_data_index, args->pConfig, NULL); return SERVICE_NOMATCH; } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_SUCCESS; } inprocess: service_mod.api->service_inprocess(flowp, pkt, dir, &svc_element, NULL); return SERVICE_INPROCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/detector_plugins/detector_sip.c0000644000175000017500000006670014241075421025523 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #include #include "appIdApi.h" #include "client_app_base.h" #include "sf_multi_mpse.h" #include "sf_mlmp.h" #include "flow.h" #include "fw_appid.h" #include "client_app_api.h" #include "service_api.h" #include "sip_common.h" static const char SIP_REGISTER_BANNER[] = "REGISTER "; static const char SIP_INVITE_BANNER[] = "INVITE "; static const char SIP_CANCEL_BANNER[] = "CANCEL "; static const char SIP_ACK_BANNER[] = "ACK "; static const char SIP_BYE_BANNER[] = "BYE "; static const char SIP_OPTIONS_BANNER[] = "OPTIONS "; static const char SIP_BANNER[] = "SIP/2.0 "; static const char SIP_BANNER_END[] = "SIP/2.0\x00d\x00a"; #define SIP_REGISTER_BANNER_LEN (sizeof(SIP_REGISTER_BANNER)-1) #define SIP_INVITE_BANNER_LEN (sizeof(SIP_INVITE_BANNER)-1) #define SIP_CANCEL_BANNER_LEN (sizeof(SIP_CANCEL_BANNER)-1) #define SIP_ACK_BANNER_LEN (sizeof(SIP_ACK_BANNER)-1) #define SIP_BYE_BANNER_LEN (sizeof(SIP_BYE_BANNER)-1) #define SIP_OPTIONS_BANNER_LEN (sizeof(SIP_OPTIONS_BANNER)-1) #define SIP_BANNER_LEN (sizeof(SIP_BANNER)-1) #define SIP_BANNER_END_LEN (sizeof(SIP_BANNER_END)-1) #define SIP_BANNER_LEN (sizeof(SIP_BANNER)-1) #define USER_STRING "from: " #define MAX_USER_POS ((int)sizeof(USER_STRING) - 2) static const char svc_name[] = "sip"; #define SIP_PORT 5060 #define MAX_ADDRESS_SIZE 16 #define MAX_CALLID_SIZE 64 #define MAX_VENDOR_SIZE 64 #define MAX_PORT_SIZE 6 typedef enum { SIP_STATE_INIT=0, SIP_STATE_REGISTER, SIP_STATE_CALL } SIPState; #define SIP_STATUS_OK 200 #define SIP_MAX_INFO_SIZE 63 typedef enum { SIP_FLAG_SERVER_CHECKED = (1<< 0) } tSIP_FLAGS; typedef struct _CLIENT_SIP_DATA { void *owner; SIPState state; uint32_t flags; char *userName; char *clientUserAgent; char *from; } ClientSIPData; typedef struct _SIP_CLIENT_APP_CONFIG { int enabled; } SIP_CLIENT_APP_CONFIG; static SIP_CLIENT_APP_CONFIG sip_config; static CLIENT_APP_RETCODE sip_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static void sip_clean(const CleanClientAppAPI * const clean_api); static CLIENT_APP_RETCODE sip_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static CLIENT_APP_RETCODE sip_tcp_client_init(const InitClientAppAPI * const init_api, SF_LIST *config); static CLIENT_APP_RETCODE sip_tcp_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig); static int sipAppGetClientApp(void *patternMatcher, char *pattern, uint32_t patternLen, tAppId *clientAppId, char **clientVersion); static void sipUaClean(tDetectorSipConfig *pConfig); static void sipServerClean(tDetectorSipConfig *pConfig); tRNAClientAppModule sip_udp_client_mod = { .name = "SIP", .proto = IPPROTO_UDP, .init = &sip_client_init, .clean = &sip_clean, .validate = &sip_client_validate, .minimum_matches = 2, .provides_user = 1, }; tRNAClientAppModule sip_tcp_client_mod = { .name = "SIP", .proto = IPPROTO_TCP, .init = &sip_tcp_client_init, .validate = &sip_tcp_client_validate, .minimum_matches = 2, .provides_user = 1, }; typedef struct { const uint8_t *pattern; unsigned length; int index; unsigned appId; } Client_App_Pattern; static Client_App_Pattern patterns[] = { {(const uint8_t *)SIP_REGISTER_BANNER, sizeof(SIP_REGISTER_BANNER)-1, 0, APP_ID_SIP}, {(const uint8_t *)SIP_INVITE_BANNER, sizeof(SIP_INVITE_BANNER)-1, 0, APP_ID_SIP}, {(const uint8_t *)SIP_CANCEL_BANNER, sizeof(SIP_CANCEL_BANNER)-1, 0, APP_ID_SIP}, {(const uint8_t *)SIP_ACK_BANNER, sizeof(SIP_ACK_BANNER)-1, 0, APP_ID_SIP}, {(const uint8_t *)SIP_BYE_BANNER, sizeof(SIP_BYE_BANNER)-1, 0, APP_ID_SIP}, {(const uint8_t *)SIP_OPTIONS_BANNER, sizeof(SIP_OPTIONS_BANNER)-1, 0, APP_ID_SIP}, {(const uint8_t *)SIP_BANNER, sizeof(SIP_BANNER)-1, 0, APP_ID_SIP}, {(const uint8_t *)SIP_BANNER_END, sizeof(SIP_BANNER_END)-1, -1, APP_ID_SIP}, }; static tAppRegistryEntry appIdClientRegistry[] = { {APP_ID_SIP, APPINFO_FLAG_CLIENT_ADDITIONAL|APPINFO_FLAG_CLIENT_USER}, }; static tAppRegistryEntry appIdServiceRegistry[] = { {APP_ID_SIP, APPINFO_FLAG_SERVICE_ADDITIONAL|APPINFO_FLAG_CLIENT_USER}, {APP_ID_RTP, APPINFO_FLAG_SERVICE_ADDITIONAL} }; //service side typedef struct _SERVICE_SIP_DATA { uint8_t serverPkt; char vendor[MAX_VENDOR_SIZE]; } ServiceSIPData; static int sip_service_init(const InitServiceAPI * const init_api); static int sip_service_validate(ServiceValidationArgs* args); static tRNAServiceElement svc_element = { .next = NULL, .validate = &sip_service_validate, .detectorType = DETECTOR_TYPE_DECODER, .name = "sip", .ref_count = 1, .current_ref_count = 1, }; static RNAServiceValidationPort pp[] = { {&sip_service_validate, SIP_PORT, IPPROTO_TCP}, {&sip_service_validate, SIP_PORT, IPPROTO_UDP}, {NULL, 0, 0} }; SF_SO_PUBLIC tRNAServiceValidationModule sip_service_mod = { .name = svc_name, .init = &sip_service_init, .pp = pp, .provides_user = 1 }; static CLIENT_APP_RETCODE sip_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; /*configuration is read by sip_tcp_init(), which is called first */ if (sip_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&sip_client_validate, IPPROTO_UDP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdClientRegistry)/sizeof(*appIdClientRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdClientRegistry[j].appId); init_api->RegisterAppId(&sip_client_validate, appIdClientRegistry[j].appId, appIdClientRegistry[j].additionalInfo, init_api->pAppidConfig); } if (init_api->pAppidConfig->detectorSipConfig.sipUaMatcher) { sipUaClean(&init_api->pAppidConfig->detectorSipConfig); } if (init_api->pAppidConfig->detectorSipConfig.sipServerMatcher) { sipServerClean(&init_api->pAppidConfig->detectorSipConfig); } return CLIENT_APP_SUCCESS; } static void sip_clean(const CleanClientAppAPI * const clean_api) { if (clean_api->pAppidConfig->detectorSipConfig.sipUaMatcher) { sipUaClean(&clean_api->pAppidConfig->detectorSipConfig); } if (clean_api->pAppidConfig->detectorSipConfig.sipServerMatcher) { sipServerClean(&clean_api->pAppidConfig->detectorSipConfig); } } static CLIENT_APP_RETCODE sip_tcp_client_init(const InitClientAppAPI * const init_api, SF_LIST *config) { unsigned i; RNAClientAppModuleConfigItem *item; sip_config.enabled = 1; if (config) { for (item = (RNAClientAppModuleConfigItem *)sflist_first(config); item; item = (RNAClientAppModuleConfigItem *)sflist_next(config)) { _dpd.debugMsg(DEBUG_LOG,"Processing %s: %s\n",item->name, item->value); if (strcasecmp(item->name, "enabled") == 0) { sip_config.enabled = atoi(item->value); } } } if (sip_config.enabled) { for (i=0; i < sizeof(patterns)/sizeof(*patterns); i++) { _dpd.debugMsg(DEBUG_LOG,"registering patterns: %s: %d\n",(const char *)patterns[i].pattern, patterns[i].index); init_api->RegisterPattern(&sip_tcp_client_validate, IPPROTO_TCP, patterns[i].pattern, patterns[i].length, patterns[i].index, init_api->pAppidConfig); } } unsigned j; for (j=0; j < sizeof(appIdClientRegistry)/sizeof(*appIdClientRegistry); j++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdClientRegistry[j].appId); init_api->RegisterAppId(&sip_tcp_client_validate, appIdClientRegistry[j].appId, appIdClientRegistry[j].additionalInfo, init_api->pAppidConfig); } return CLIENT_APP_SUCCESS; } static void clientDataFree(void *data) { ClientSIPData *fd = data; free(fd->from); free(fd->clientUserAgent); free(fd->userName); free (fd); } #define SIP_USRNAME_BEGIN_MARKER "data_get(flowp, sip_udp_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return CLIENT_APP_ENOMEM; if (sip_udp_client_mod.api->data_add(flowp, fd, sip_udp_client_mod.flow_data_index, &clientDataFree)) { free(fd); return CLIENT_APP_ENOMEM; } fd->owner = &sip_udp_client_mod; setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } return CLIENT_APP_INPROCESS; } static CLIENT_APP_RETCODE sip_tcp_client_validate(const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, struct _Detector *userData, const tAppIdConfig *pConfig) { return sip_client_validate(data, size, dir, flowp, pkt, userData, pConfig); } static int sipAppAddPattern( tDetectorAppSipPattern **patternList, tAppId clientAppId, const char* clientVersion, const char* serverPattern ) { /* Allocate memory for data structures */ tDetectorAppSipPattern *pattern = malloc(sizeof(tDetectorAppSipPattern)); if (!pattern) { return -1; } pattern->userData.clientAppId = clientAppId; pattern->userData.clientVersion = strdup(clientVersion); if (!pattern->userData.clientVersion) { _dpd.errMsg("failed to allocate client version"); free(pattern); return -1; } pattern->pattern.pattern = (uint8_t *)strdup(serverPattern); if (!pattern->pattern.pattern) { _dpd.errMsg("failed to allocate patterns"); free(pattern->userData.clientVersion); free(pattern); return -1; } pattern->pattern.patternSize = (int) strlen(serverPattern); pattern->next = *patternList; *patternList = pattern; return 0; } int sipUaPatternAdd( tAppId clientAppId, const char* clientVersion, const char* pattern, tDetectorSipConfig *pSipConfig ) { return sipAppAddPattern( &pSipConfig->appSipUaList, clientAppId, clientVersion, pattern); } int sipServerPatternAdd( tAppId clientAppId, const char* clientVersion, const char* pattern, tDetectorSipConfig *pSipConfig ) { return sipAppAddPattern( &pSipConfig->appSipServerList, clientAppId, clientVersion, pattern); } #include "http_url_patterns.h" #define PATTERN_PART_MAX 10 int sipUaFinalize(tDetectorSipConfig *pSipConfig) { static tMlmpPattern patterns[PATTERN_PART_MAX]; int num_patterns; tDetectorAppSipPattern *patternNode; pSipConfig->sipUaMatcher = mlmpCreate(); if (!pSipConfig->sipUaMatcher) return -1; pSipConfig->sipServerMatcher = mlmpCreate(); if (!pSipConfig->sipServerMatcher) { mlmpDestroy(pSipConfig->sipUaMatcher); pSipConfig->sipUaMatcher = NULL; return -1; } for (patternNode = pSipConfig->appSipUaList; patternNode; patternNode = patternNode->next) { num_patterns = parseMultipleHTTPPatterns((const char *)patternNode->pattern.pattern, patterns, PATTERN_PART_MAX, 0); patterns[num_patterns].pattern = NULL; mlmpAddPattern(pSipConfig->sipUaMatcher, patterns, patternNode); } for (patternNode = pSipConfig->appSipServerList; patternNode; patternNode = patternNode->next) { num_patterns = parseMultipleHTTPPatterns((const char *)patternNode->pattern.pattern, patterns, PATTERN_PART_MAX, 0); patterns[num_patterns].pattern = NULL; mlmpAddPattern(pSipConfig->sipServerMatcher, patterns, patternNode); } mlmpProcessPatterns(pSipConfig->sipUaMatcher); mlmpProcessPatterns(pSipConfig->sipServerMatcher); return 0; } static void sipUaClean(tDetectorSipConfig *pSipConfig) { tDetectorAppSipPattern *node; if (pSipConfig->sipUaMatcher) { mlmpDestroy(pSipConfig->sipUaMatcher); pSipConfig->sipUaMatcher = NULL; } for (node = pSipConfig->appSipUaList; node; node = pSipConfig->appSipUaList) { pSipConfig->appSipUaList = node->next; free((void*)node->pattern.pattern); free(node->userData.clientVersion); free(node); } } static void sipServerClean(tDetectorSipConfig *pSipConfig) { tDetectorAppSipPattern *node; if (pSipConfig->sipServerMatcher) { mlmpDestroy(pSipConfig->sipServerMatcher); pSipConfig->sipServerMatcher = NULL; } for (node = pSipConfig->appSipServerList; node; node = pSipConfig->appSipServerList) { pSipConfig->appSipServerList = node->next; free((void*)node->pattern.pattern); free(node->userData.clientVersion); free(node); } } static int sipAppGetClientApp( void *patternMatcher, char *pattern, uint32_t patternLen, tAppId *clientAppId, char **clientVersion) { tMlmpPattern patterns[3]; tDetectorAppSipPattern *data; if (!pattern) return 0; patterns[0].pattern = (uint8_t *)pattern; patterns[0].patternSize = patternLen; patterns[1].pattern = NULL; data = (tDetectorAppSipPattern *)mlmpMatchPatternGeneric(patternMatcher, patterns); if (data == NULL) return 0; *clientAppId = data->userData.clientAppId; *clientVersion = data->userData.clientVersion; return 1; } #define SIP_FAILURE -1 #define SIP_SUCCESS 0 #define SIP_KEYWORD "SIP/" #define SIP_KEYWORD_LEN 4 #define SIP_VERSION_NUM_LEN 3 /*2.0 or 1.0 or 1.1*/ #define SIP_VERSION_LEN SIP_KEYWORD_LEN + SIP_VERSION_NUM_LEN #define SIP_MIN_MSG_LEN SIP_VERSION_LEN #define MAX_STAT_CODE 999 #define MIN_STAT_CODE 100 static void createRtpFlow(tAppIdData *flowp, SFSnortPacket *pkt, sfaddr_t *cliIp, uint16_t cliPort, sfaddr_t *srvIp, uint16_t srvPort, uint8_t proto, int16_t app_id) { tAppIdData *fp, *fp2; fp = sip_service_mod.api->flow_new(flowp, pkt, cliIp, cliPort, srvIp, srvPort, proto, app_id, APPID_EARLY_SESSION_FLAG_FW_RULE); if(fp) { fp->clientAppId = flowp->clientAppId; fp->payloadAppId = flowp->payloadAppId; fp->serviceAppId = APP_ID_RTP; PopulateExpectedFlow(flowp, fp, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_APPID_SESSION_DIRECTION_MAX); } // create an RTCP flow as well fp2 = sip_service_mod.api->flow_new(flowp, pkt, cliIp, cliPort+1, srvIp, srvPort+1, proto, app_id, APPID_EARLY_SESSION_FLAG_FW_RULE); if (fp2) { fp2->clientAppId = flowp->clientAppId; fp2->payloadAppId = flowp->payloadAppId; fp2->serviceAppId = APP_ID_RTCP; PopulateExpectedFlow(flowp, fp2, APPID_SESSION_EXPECTED_EVALUATE, APP_ID_APPID_SESSION_DIRECTION_MAX); } } static int addFutureRtpFlows( tAppIdData *flowp, const SipDialog *dialog, SFSnortPacket *p) { SIP_MediaData *mdataA,*mdataB; // check the first media session if (NULL == dialog->mediaSessions) return -1; // check the second media session if (NULL == dialog->mediaSessions->nextS) return -1; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Adding future media sessions ID: %u and %u\n", dialog->mediaSessions->sessionID, dialog->mediaSessions->nextS->sessionID);); mdataA = dialog->mediaSessions->medias; mdataB = dialog->mediaSessions->nextS->medias; while((NULL != mdataA)&&(NULL != mdataB)) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Adding future channels Source IP: %s Port: %u\n", sfip_to_str(&mdataA->maddress), mdataA->mport);); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Adding future channels Destine IP: %s Port: %u\n", sfip_to_str(&mdataB->maddress), mdataB->mport);); createRtpFlow(flowp, p, &mdataA->maddress, mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, APP_ID_RTP); createRtpFlow(flowp, p, &mdataB->maddress, mdataB->mport, &mdataA->maddress, mdataA->mport, IPPROTO_UDP, APP_ID_RTP); mdataA = mdataA->nextM; mdataB = mdataB->nextM; } return 0; } static void SipSessionCbClientProcess (SFSnortPacket *p, const SipHeaders *headers, const SipDialog *dialog, tAppIdData *flowp) { ClientSIPData *fd; tAppId clientAppId = APP_ID_SIP; char *clientVersion = NULL; int direction; fd = sip_udp_client_mod.api->data_get(flowp, sip_udp_client_mod.flow_data_index); if (!fd) { fd = calloc(1, sizeof(*fd)); if (!fd) return; if (sip_udp_client_mod.api->data_add(flowp, fd, sip_udp_client_mod.flow_data_index, &clientDataFree)) { free(fd); return; } fd->owner = &sip_udp_client_mod; setAppIdFlag(flowp, APPID_SESSION_CLIENT_GETS_SERVER_PACKETS); } if (fd->owner != &sip_udp_client_mod && fd->owner != &sip_tcp_client_mod) return; direction = (_dpd.sessionAPI->get_packet_direction(p) & FLAG_FROM_CLIENT) ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; if (headers->methodFlag == SIP_METHOD_INVITE && direction == APP_ID_FROM_INITIATOR) { if (headers->from && headers->fromLen) { free(fd->from); fd->from = strndup(headers->from, headers->fromLen); } if (headers->userName && headers->userNameLen) { free(fd->userName); fd->userName = strndup(headers->userName, headers->userNameLen); } if (headers->userAgent && headers->userAgentLen) { free(fd->clientUserAgent); fd->clientUserAgent = strndup(headers->userAgent, headers->userAgentLen); } } if (fd->clientUserAgent) { if (sipAppGetClientApp(pAppidActiveConfig->detectorSipConfig.sipUaMatcher, fd->clientUserAgent, strlen(fd->clientUserAgent), &clientAppId, &clientVersion)) goto success; } if ( fd->from && !(fd->flags & SIP_FLAG_SERVER_CHECKED)) { fd->flags |= SIP_FLAG_SERVER_CHECKED; if (sipAppGetClientApp(pAppidActiveConfig->detectorSipConfig.sipServerMatcher, (char *)fd->from, strlen(fd->from), &clientAppId, &clientVersion)) goto success; } if (!dialog || dialog->state != SIP_DLG_ESTABLISHED) return; success: //client detection successful sip_udp_client_mod.api->add_app(p, direction, pAppidActiveConfig, flowp, APP_ID_SIP, clientAppId, clientVersion); if(fd->userName) sip_udp_client_mod.api->add_user(flowp, (char *)fd->userName, APP_ID_SIP, 1); setAppIdFlag(flowp, APPID_SESSION_CLIENT_DETECTED); } static void SipSessionCbServiceProcess (SFSnortPacket *p, const SipHeaders *headers, const SipDialog *dialog, tAppIdData *flowp) { ServiceSIPData *ss; int direction; ss = sip_service_mod.api->data_get(flowp, sip_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return; if (sip_service_mod.api->data_add(flowp, ss, sip_service_mod.flow_data_index, &free)) { free(ss); return; } } ss->serverPkt = 0; direction = (_dpd.sessionAPI->get_packet_direction(p) & FLAG_FROM_CLIENT) ? APP_ID_FROM_INITIATOR : APP_ID_FROM_RESPONDER; if (direction == APP_ID_FROM_RESPONDER) { if (headers->userAgent && headers->userAgentLen) { memcpy(ss->vendor, headers->userAgent, headers->userAgentLen > (MAX_VENDOR_SIZE - 1) ? (MAX_VENDOR_SIZE - 1): headers->userAgentLen); } else if (headers->server && headers->serverLen) { memcpy(ss->vendor, headers->server, headers->serverLen > (MAX_VENDOR_SIZE - 1) ? (MAX_VENDOR_SIZE - 1): headers->serverLen); } } if (!dialog) return; if (dialog->mediaUpdated) addFutureRtpFlows(flowp, dialog, p); if (dialog->state == SIP_DLG_ESTABLISHED) { if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { setAppIdFlag(flowp, APPID_SESSION_CONTINUE); sip_service_mod.api->add_service(flowp, p, direction, &svc_element, APP_ID_SIP, ss->vendor[0] ? ss->vendor:NULL, NULL, NULL, NULL); } } } #if defined(DEBUG_MSGS) || defined(DEBUG_APP_ID_SESSIONS) static void printPacketInfo(SFSnortPacket *p, tAppIdData *flowp) { char src_ip[INET6_ADDRSTRLEN]; char dst_ip[INET6_ADDRSTRLEN]; sfaddr_t* ip; src_ip[0] = 0; ip = GET_SRC_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), src_ip, sizeof(src_ip)); dst_ip[0] = 0; ip = GET_DST_IP(p); inet_ntop(sfaddr_family(ip), (void *)sfaddr_get_ptr(ip), dst_ip, sizeof(dst_ip)); #ifdef DEBUG_MSGS if (!flowp) { _dpd.debugMsg(DEBUG_LOG, "Sip preproc callback missing AppID session for %s-%u - %s-%u %d\n", src_ip, (unsigned)p->src_port, dst_ip, (unsigned)p->dst_port, IsTCP(p) ? IPPROTO_TCP:IPPROTO_UDP); } #endif #ifdef DEBUG_APP_ID_SESSIONS fprintf(SF_DEBUG_FILE, "Sip preproc callback for %s-%u -> %s-%u %d, AppID session %p\n", src_ip, (unsigned)p->src_port, dst_ip, (unsigned)p->dst_port, IsTCP(p) ? IPPROTO_TCP:IPPROTO_UDP, flowp); #endif } #endif void SipSessionSnortCallback (void *ssnptr, ServiceEventType eventType, void *data) { tAppIdData *flowp = NULL; SipEventData *eventData = (SipEventData *)data; SFSnortPacket *p = eventData->packet; const SipHeaders *headers = eventData->headers; const SipDialog *dialog = eventData->dialog; if (p->stream_session) flowp = getAppIdData(p->stream_session); #if defined(DEBUG_MSGS) || defined(DEBUG_APP_ID_SESSIONS) printPacketInfo(p, flowp); #endif if (!flowp) return; SipSessionCbClientProcess(p, headers, dialog, flowp); SipSessionCbServiceProcess(p, headers, dialog, flowp); } static int sip_service_init(const InitServiceAPI * const init_api) { init_api->RegisterPattern(&sip_service_validate, IPPROTO_UDP, (const uint8_t *) SIP_BANNER, SIP_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_TCP, (const uint8_t *) SIP_BANNER, SIP_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_UDP, (const uint8_t *) SIP_INVITE_BANNER, SIP_INVITE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_TCP, (const uint8_t *) SIP_INVITE_BANNER, SIP_INVITE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_UDP, (const uint8_t *) SIP_ACK_BANNER, SIP_ACK_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_TCP, (const uint8_t *) SIP_ACK_BANNER, SIP_ACK_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_UDP, (const uint8_t *) SIP_REGISTER_BANNER, SIP_REGISTER_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_TCP, (const uint8_t *) SIP_REGISTER_BANNER, SIP_REGISTER_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_UDP, (const uint8_t *) SIP_CANCEL_BANNER, SIP_CANCEL_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_TCP, (const uint8_t *) SIP_CANCEL_BANNER, SIP_CANCEL_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_UDP, (const uint8_t *) SIP_BYE_BANNER, SIP_BYE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_TCP, (const uint8_t *) SIP_BYE_BANNER, SIP_BYE_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_UDP, (const uint8_t *) SIP_OPTIONS_BANNER, SIP_OPTIONS_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); init_api->RegisterPattern(&sip_service_validate, IPPROTO_TCP, (const uint8_t *) SIP_OPTIONS_BANNER, SIP_OPTIONS_BANNER_LEN, 0, svc_name, init_api->pAppidConfig); unsigned i; for (i=0; i < sizeof(appIdServiceRegistry)/sizeof(*appIdServiceRegistry); i++) { _dpd.debugMsg(DEBUG_LOG,"registering appId: %d\n",appIdServiceRegistry[i].appId); init_api->RegisterAppId(&sip_service_validate, appIdServiceRegistry[i].appId, appIdServiceRegistry[i].additionalInfo, init_api->pAppidConfig); } return 0; } static int sip_service_validate(ServiceValidationArgs* args) { ServiceSIPData *ss; tAppIdData *flowp = args->flowp; ss = sip_service_mod.api->data_get(flowp, sip_service_mod.flow_data_index); if (!ss) { ss = calloc(1, sizeof(*ss)); if (!ss) return SERVICE_ENOMEM; if (sip_service_mod.api->data_add(flowp, ss, sip_service_mod.flow_data_index, &free)) { free(ss); return SERVICE_ENOMEM; } } if (args->size && args->dir == APP_ID_FROM_RESPONDER) { ss->serverPkt++; } if (ss->serverPkt > 10) { if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { sip_service_mod.api->fail_service(flowp, args->pkt, args->dir, &svc_element, sip_service_mod.flow_data_index, args->pConfig, NULL); } clearAppIdFlag(flowp, APPID_SESSION_CONTINUE); return SERVICE_NOMATCH; } if (!getAppIdFlag(flowp, APPID_SESSION_SERVICE_DETECTED)) { sip_service_mod.api->service_inprocess(flowp, args->pkt, args->dir, &svc_element, NULL); } return SERVICE_INPROCESS; } snort-2.9.20/src/dynamic-preprocessors/appid/luaDetectorApi.h0000644000175000017500000001254314241075451022376 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _LUA_DETECTOR_API_H_ #define _LUA_DETECTOR_API_H_ #include #include #include #include "client_app_base.h" #include "service_base.h" #include "lua.h" #include "lauxlib.h" #include "lualib.h" #include "service_api.h" #include "client_app_api.h" #include "appIdConfig.h" #include "profiler.h" typedef struct { char *name; int proto; struct { char *initFunctionName; /*client init function */ char *cleanFunctionName; /*client clean function */ char *validateFunctionName; /*client validate function */ int minMatches; /*minimum number of matched patterns */ } client; struct { char *initFunctionName; /*server init function */ char *cleanFunctionName; /*server clean function */ char *validateFunctionName; /*server validate function */ } server; }tDetectorPackageInfo; typedef struct _Detector { struct _Detector *next; /**Identifies customer created detectors using SDL. */ unsigned isCustom:1; unsigned isActive:1; unsigned wasActive:1; struct { const uint8_t *data; uint16_t size; int dir; tAppIdData *flowp; SFSnortPacket *pkt; uint8_t macAddress[6]; } validateParams; /**Pointer to flow created by a validator. */ tAppIdData *pFlow; struct { unsigned int serviceId; /**present only for server detectors*/ struct RNAServiceValidationModule serviceModule; /**calloced buffer to satisfy internal flow API. */ struct RNAServiceElement *pServiceElement; } server; /**constructed from packageInfo read from lua detector directly. Present * only for client detectors. */ struct { /**application fingerprint id.*/ unsigned int appFpId; /**Client Application Module. */ tRNAClientAppModule appModule; } client; char *callbackFcnName; lua_State *myLuaState; /**Reference to lua userdata. This is a key into LUA_REGISTRYINDEX */ int detectorUserDataRef; /**Detector name. Lua file name is used as detector name*/ char *name; /**Package information retrieved from detector lua file. */ tDetectorPackageInfo packageInfo; unsigned detector_version; char *validatorBuffer; unsigned char digest[16]; tAppIdConfig *pAppidActiveConfig; ///< AppId context in which this detector should be used; used during packet processing tAppIdConfig *pAppidOldConfig; ///< AppId context in which this detector should be cleaned; used at reload free and exit tAppIdConfig *pAppidNewConfig; ///< AppId context in which this detector should be loaded; used at initialization and reload #ifdef PERF_PROFILING /**Snort profiling stats for individual Lua detector.*/ struct _PreprocStats *pPerfStats; #endif pthread_mutex_t luaReloadMutex; } Detector; /**data directly accessed by Lua code should be here. */ typedef struct { /**points to detector allocated on the C side. This is needed to get detector from callback functions. This pointer must be set to * NULL when Detector is destroyed. */ Detector *pDetector; /*lua accessible data elements should be defined below. */ } DetectorUserData; int Detector_register ( lua_State *L ); DetectorUserData *checkDetectorUserData ( lua_State *L, int index ); void Detector_fini(void *detector); void detectorRemoveAllPorts ( Detector *detector, tAppIdConfig *pConfig ); Detector *createDetector( lua_State *L, const char *filename ); void freeDetector( Detector *detector ); int validateAnyClientApp( const uint8_t *data, uint16_t size, const int dir, tAppIdData *flowp, SFSnortPacket *pkt, Detector *userdata, const tAppIdConfig *pConfig ); enum httpPatternType { HTTP_PAYLOAD = 1, HTTP_USER_AGENT = 2, HTTP_URL = 3 }; int Detector_addSSLCertPattern (lua_State *); int Detector_addDNSHostPattern (lua_State *); int Detector_addHttpPattern(lua_State *L); void CleanHttpPatternLists(tAppIdConfig *pConfig); void CleanClientPortPatternList(tAppIdConfig*); void CleanServicePortPatternList(tAppIdConfig*); int validateAnyService(ServiceValidationArgs *args); int checkServiceElement( Detector *detector); #endif snort-2.9.20/src/dynamic-preprocessors/dns/0000755000175000017500000000000014242725716017011 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/dns/dns_buffer_dump.c0000644000175000017500000000464414241075722022321 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file dns_buffer_dump.c ** ** @author Seshaiah Erugu ** ** @brief This file contains structures and functions for ** dumping buffers used during DNS inspection. */ #include "dns_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_DNS_BUFFER_DUMP] = {{"DNS_PAYLOAD_DUMP", "", 0}, {"DNS_QUESTION_DUMP", "", 0}, {"DNS_ANSWER_DUMP", "", 0}, {"DNS_RESP_STATE_ANS_RR_DUMP", "", 0}, {"DNS_RESP_STATE_AUTH_RR_DUMP", "", 0}, {"DNS_RESP_STATE_ADD_RR_DUMP", "", 0}, {"DNS_RR_TYPE_TXT_VULN_DUMP", "", 0}, {"DNS_OBSOLETE_TYPES_DUMP", "", 0}, {"DNS_EXPERIMENTAL_TYPES_DUMP", "", 0}, {"DNS_SKIP_RDATA_DUMP", "", 0}}; void dumpBuffer(DNS_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content =(char*) content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_DNS_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getDNSBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/dns/sf_dns.vcxproj0000444000175000017500000004037514230012554021673 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {A3CA91AD-6A00-4A8E-978D-17BC7DBC6117} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_dns.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_dns.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_dns.bsc true true Console .\Release\sf_dns.dll .\Release\sf_dns.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_dns.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_dns.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_dns.bsc true true Console .\Release\sf_dns.dll .\Release\sf_dns.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_dns.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_dns.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_dns.bsc true true true Console .\Debug\sf_dns.dll .\Debug\sf_dns.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_dns.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_dns.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_dns.bsc true true true Console .\Debug\sf_dns.dll .\Debug\sf_dns.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/dns/spp_dns.h0000644000175000017500000002061014241075726020625 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * spp_dns.h: Definitions, structs, function prototype(s) for * the DNS preprocessor. * Author: Steven Sturges */ #ifndef SPP_DNS_H #define SPP_DNS_H #define MAX_PORTS 65536 /* * Default DNS port */ #define DNS_PORT 53 /* * Error codes. */ #define DNS_SUCCESS 1 #define DNS_FAILURE 0 /* * Directional defines */ #define DNS_DIR_FROM_SERVER 1 #define DNS_DIR_FROM_CLIENT 2 /* * Global DNS preprocessor configuration. * * autodetect: Whether or not to apply auto-detection of DNS * to ports other than those configured. * enabled_alerts: Bit vector describing which alerts are enabled. */ typedef struct _DNSConfig { #if 0 uint8_t autodetect; #endif uint16_t enabled_alerts; uint8_t ports[MAX_PORTS/8]; } DNSConfig; /****** A few data structures ******/ typedef struct _DNSHdr { uint16_t id; uint16_t flags; uint16_t questions; uint16_t answers; uint16_t authorities; uint16_t additionals; } DNSHdr; #define DNS_HDR_FLAG_REPLY_CODE_MASK 0x000F #define DNS_HDR_FLAG_NON_AUTHENTICATED_OK 0x0010 #define DNS_HDR_FLAG_ANS_AUTHENTICATED 0x0020 #define DNS_HDR_FLAG_RESERVED 0x0040 #define DNS_HDR_FLAG_RECURSION_AVAIL 0x0080 #define DNS_HDR_FLAG_RECURSION_DESIRED 0x0100 #define DNS_HDR_FLAG_TRUNCATED 0x0200 #define DNS_HDR_FLAG_AUTHORITATIVE 0x0400 #define DNS_HDR_FLAG_OPCODE_MASK 0x7800 #define DNS_HDR_FLAG_RESPONSE 0x8000 typedef struct _DNSQuestion { uint16_t type; uint16_t dns_class; } DNSQuestion; typedef struct _DNSRR { uint16_t type; uint16_t dns_class; uint32_t ttl; uint16_t length; } DNSRR; typedef struct _DNSNameState { uint32_t txt_count; uint32_t total_txt_len; uint8_t txt_len; uint8_t txt_bytes_seen; uint8_t name_state; uint8_t alerted; uint16_t offset; uint8_t relative; } DNSNameState; #define DNS_RR_TYPE_A 0x0001 #define DNS_RR_TYPE_NS 0x0002 #define DNS_RR_TYPE_MD 0x0003 /* obsolete */ #define DNS_RR_TYPE_MF 0x0004 /* obsolete */ #define DNS_RR_TYPE_CNAME 0x0005 #define DNS_RR_TYPE_SOA 0x0006 #define DNS_RR_TYPE_MB 0x0007 /* experimental */ #define DNS_RR_TYPE_MG 0x0008 /* experimental */ #define DNS_RR_TYPE_MR 0x0009 /* experimental */ #define DNS_RR_TYPE_NULL 0x000a /* experimental */ #define DNS_RR_TYPE_WKS 0x000b #define DNS_RR_TYPE_PTR 0x000c #define DNS_RR_TYPE_HINFO 0x000d #define DNS_RR_TYPE_MINFO 0x000e /* experimental */ #define DNS_RR_TYPE_MX 0x000f #define DNS_RR_TYPE_TXT 0x0010 /* * Per-session data block containing current state * of the DNS preprocessor for the session. * * state: The current state of the session. * num_records: Number of records in the session. * curr_record: Record number for the current record * curr_record_length: Current record length. * total_record_length: Total data length of records. * length: Total length of DNS response (TCP only) * hdr: Copy of the data from the DNS Header */ typedef struct _DNSSessionData { uint32_t state; uint16_t curr_rec; uint16_t curr_rec_length; uint16_t bytes_seen_curr_rec; uint16_t length; uint8_t curr_rec_state; DNSHdr hdr; DNSQuestion curr_q; DNSRR curr_rr; DNSNameState curr_txt; uint8_t flags; } DNSSessionData; #define DNS_FLAG_NOT_DNS 0x01 /* DNSSessionData States */ #define DNS_RESP_STATE_LENGTH 0x00 /* 2 bytes - TCP only*/ #define DNS_RESP_STATE_LENGTH_PART 0x01 /* Partial length */ #define DNS_RESP_STATE_HDR 0x10 /* 12 bytes */ #define DNS_RESP_STATE_HDR_ID 0x11 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_ID_PART 0x12 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_FLAGS 0x13 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_FLAGS_PART 0x14 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_QS 0x15 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_QS_PART 0x16 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_ANSS 0x17 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_ANSS_PART 0x18 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_AUTHS 0x19 /* (2 bytes) */ #define DNS_RESP_STATE_HDR_AUTHS_PART 0x1a /* (2 bytes) */ #define DNS_RESP_STATE_HDR_ADDS 0x1b /* (2 bytes) */ #define DNS_RESP_STATE_HDR_ADDS_PART 0x1c /* (2 bytes) */ #define DNS_RESP_STATE_QUESTION 0x20 /* 4 bytes */ #define DNS_RESP_STATE_Q_NAME 0x21 /* (size depends on data) */ #define DNS_RESP_STATE_Q_NAME_COMPLETE 0x22 /* (size depends on data) */ #define DNS_RESP_STATE_Q_TYPE 0x23 /* (2 bytes) */ #define DNS_RESP_STATE_Q_TYPE_PART 0x24 /* (2 bytes) */ #define DNS_RESP_STATE_Q_CLASS 0x25 /* (2 bytes) */ #define DNS_RESP_STATE_Q_CLASS_PART 0x26 /* (2 bytes) */ #define DNS_RESP_STATE_Q_COMPLETE 0x27 #define DNS_RESP_STATE_NAME_SIZE 0x31 /* (1 byte) */ #define DNS_RESP_STATE_NAME 0x32 /* (size depends on field) */ #define DNS_RESP_STATE_NAME_COMPLETE 0x33 #define DNS_RESP_STATE_ANS_RR 0x40 /* (size depends on field) */ #define DNS_RESP_STATE_RR_NAME_SIZE 0x41 /* (1 byte) */ #define DNS_RESP_STATE_RR_NAME 0x42 /* (size depends on field) */ #define DNS_RESP_STATE_RR_NAME_COMPLETE 0x43 #define DNS_RESP_STATE_RR_TYPE 0x44 /* (2 bytes) */ #define DNS_RESP_STATE_RR_TYPE_PART 0x45 /* (2 bytes) */ #define DNS_RESP_STATE_RR_CLASS 0x46 /* (2 bytes) */ #define DNS_RESP_STATE_RR_CLASS_PART 0x47 /* (2 bytes) */ #define DNS_RESP_STATE_RR_TTL 0x48 /* (4 bytes) */ #define DNS_RESP_STATE_RR_TTL_PART 0x49 /* (4 bytes) */ #define DNS_RESP_STATE_RR_RDLENGTH 0x4a /* (2 bytes) */ #define DNS_RESP_STATE_RR_RDLENGTH_PART 0x4b /* (2 bytes) */ #define DNS_RESP_STATE_RR_RDATA_START 0x4c /* (size depends on RDLENGTH) */ #define DNS_RESP_STATE_RR_RDATA_MID 0x4d /* (size depends on RDLENGTH) */ #define DNS_RESP_STATE_RR_COMPLETE 0x4e #define DNS_RESP_STATE_AUTH_RR 0x50 #define DNS_RESP_STATE_ADD_RR 0x60 /* * Keyword strings for parsing configuration options. */ #define DNS_PORTS_KEYWORD "ports" #if 0 #define DNS_AUTODETECT_KEYWORD "autodetect" #endif #define DNS_ENABLE_OBSOLETE_TYPES_KEYWORD "enable_obsolete_types" #define DNS_ENABLE_EXPERIMENTAL_TYPES_KEYWORD "enable_experimental_types" #define DNS_ENABLE_RDATA_OVERFLOW_KEYWORD "enable_rdata_overflow" /* * DNS preprocessor alert types. */ #define DNS_EVENT_OBSOLETE_TYPES 1 #define DNS_EVENT_EXPERIMENTAL_TYPES 2 #define DNS_EVENT_RDATA_OVERFLOW 3 /* * DNS alert flags */ #define DNS_ALERT_NONE 0x0 #define DNS_ALERT_OBSOLETE_TYPES 0x1 #define DNS_ALERT_EXPERIMENTAL_TYPES 0x2 #define DNS_ALERT_RDATA_OVERFLOW 0x4 #define DNS_ALERT_ALL 0xFFFF /* * DNS preprocessor alert strings. */ #define DNS_EVENT_OBSOLETE_TYPES_STR "(spp_dns) Obsolete DNS RR Types" #define DNS_EVENT_EXPERIMENTAL_TYPES_STR "(spp_dns) Experimental DNS RR Types" #define DNS_EVENT_RDATA_OVERFLOW_STR "(spp_dns) DNS Client rdata txt Overflow" /* Prototypes for public interface */ extern void SetupDNS(void); #endif /* SPP_DNS_H */ snort-2.9.20/src/dynamic-preprocessors/dns/sf_dns.dsp0000555000175000017500000001154714230012554020770 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_dns" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_dns - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_dns.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_dns.mak" CFG="sf_dns - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_dns - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_dns - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_dns - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_dns - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_dns - Win32 Release" # Name "sf_dns - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\spp_dns.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\spp_dns.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/dns/spp_dns.c0000644000175000017500000016443214241075725020632 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * DNS preprocessor * Author: Steven Sturges * * * Alert for DNS client rdata buffer overflow. * Alert for Obsolete or Experimental RData types (per RFC 1035) * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #ifdef HAVE_STRINGS_H #include #endif #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "preprocids.h" #include "snort_debug.h" #include "spp_dns.h" #include "sf_preproc_info.h" #include #include #include #include #ifndef WIN32 #include #endif #include #include #include #include "profiler.h" #ifdef PERF_PROFILING PreprocStats dnsPerfStats; #endif #include "sf_types.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" #ifdef DUMP_BUFFER #include "dns_buffer_dump.h" #endif #ifdef TARGET_BASED int16_t dns_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 4; const char *PREPROC_NAME = "SF_DNS"; #define SetupDNS DYNAMIC_PREPROC_SETUP /* * Generator id. Define here the same as the official registry * in generators.h */ #define GENERATOR_SPP_DNS 131 /* * Function prototype(s) */ DNSSessionData * GetDNSSessionData(SFSnortPacket *, DNSConfig *); static void DNSInit( struct _SnortConfig *, char* ); static void PrintDNSConfig(DNSConfig *); static void FreeDNSSessionData( void* ); static void ParseDNSArgs(DNSConfig *, u_char*); static void ProcessDNS( void*, void* ); static inline int CheckDNSPort(DNSConfig *, uint16_t); static void DNSReset(int, void *); static void DNSResetStats(int, void *); static void enablePortStreamServices(struct _SnortConfig *, DNSConfig *, tSfPolicyId); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static void DNSFreeConfig(tSfPolicyUserContextId config); static int DNSCheckConfig(struct _SnortConfig *); static void DNSCleanExit(int, void *); int dns_print_mem_stats(FILE *fd, char* buffer, PreprocMemInfo *meminfo); /* Ultimately calls SnortEventqAdd */ /* Arguments are: gid, sid, rev, classification, priority, message, rule_info */ #define DNS_ALERT(x,y) { _dpd.alertAdd(GENERATOR_SPP_DNS, x, 1, 0, 3, y, 0 ); } /* Convert port value into an index for the dns_config ports array */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) #define DNS_RR_PTR 0xC0 static tSfPolicyUserContextId dns_config = NULL; DNSConfig *dns_eval_config = NULL; #ifdef SNORT_RELOAD static void DNSReload(struct _SnortConfig *, char *, void **); static int DNSReloadVerify(struct _SnortConfig *, void *); static void * DNSReloadSwap(struct _SnortConfig *, void *); static void DNSReloadSwapFree(void *); #endif /* Called at preprocessor setup time. Links preprocessor keyword * to corresponding preprocessor initialization function. * * PARAMETERS: None. * * RETURNS: Nothing. * */ void SetupDNS(void) { /* Link preprocessor keyword to initialization function * in the preprocessor list. */ #ifndef SNORT_RELOAD _dpd.registerPreproc( "dns", DNSInit ); #else _dpd.registerPreproc("dns", DNSInit, DNSReload, DNSReloadVerify, DNSReloadSwap, DNSReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getDNSBuffers, DNS_BUFFER_DUMP_FUNC); #endif _dpd.registerMemoryStatsFunc(PP_DNS, dns_print_mem_stats); } #ifdef REG_TEST static inline void PrintDNSSize(void) { _dpd.logMsg("\nDNS Session Size: %lu\n", (long unsigned int)sizeof(DNSSessionData)); } #endif /* Initializes the DNS preprocessor module and registers * it in the preprocessor list. * * PARAMETERS: * * argp: Pointer to argument string to process for config * data. * * RETURNS: Nothing. */ static void DNSInit( struct _SnortConfig *sc, char* argp ) { int policy_id = _dpd.getParserPolicy(sc); DNSConfig *pPolicyConfig = NULL; #ifdef REG_TEST PrintDNSSize(); #endif if (dns_config == NULL) { //create a context dns_config = sfPolicyConfigCreate(); if (dns_config == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "DNS configuration.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Dns preprocessor requires the " "stream5 preprocessor to be enabled.\n", *(_dpd.config_file), *(_dpd.config_line)); } _dpd.addPreprocReset(DNSReset, NULL, PRIORITY_LAST, PP_DNS); _dpd.addPreprocResetStats(DNSResetStats, NULL, PRIORITY_LAST, PP_DNS); _dpd.addPreprocConfCheck(sc, DNSCheckConfig); _dpd.addPreprocExit(DNSCleanExit, NULL, PRIORITY_LAST, PP_DNS); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("dns", (void *)&dnsPerfStats, 0, _dpd.totalPerfStats, NULL); #endif #ifdef TARGET_BASED dns_app_id = _dpd.findProtocolReference("dns"); if (dns_app_id == SFTARGET_UNKNOWN_PROTOCOL) { dns_app_id = _dpd.addProtocolReference("dns"); } // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_DNS, dns_app_id ); #endif } sfPolicyUserPolicySet (dns_config, policy_id); pPolicyConfig = (DNSConfig *)sfPolicyUserDataGetCurrent(dns_config); if (pPolicyConfig) { DynamicPreprocessorFatalMessage("%s(%d) Dns preprocessor can only " "be configured once.\n", *(_dpd.config_file), *(_dpd.config_line)); } pPolicyConfig = (DNSConfig *)_dpd.snortAlloc(1, sizeof(DNSConfig), PP_DNS, PP_MEM_CATEGORY_CONFIG); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "DNS configuration.\n"); } sfPolicyUserDataSetCurrent(dns_config, pPolicyConfig); ParseDNSArgs(pPolicyConfig, (u_char *)argp); _dpd.addPreproc(sc, ProcessDNS, PRIORITY_APPLICATION, PP_DNS, PROTO_BIT__TCP | PROTO_BIT__UDP); enablePortStreamServices(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } /* Parses and processes the configuration arguments * supplied in the DNS preprocessor rule. * * PARAMETERS: * * argp: Pointer to string containing the config arguments. * * RETURNS: Nothing. */ static void ParseDNSArgs(DNSConfig *config, u_char* argp) { char* cur_tokenp = NULL; char* argcpyp = NULL; int port; if (config == NULL) return; /* Set up default port to listen on */ config->ports[ PORT_INDEX( DNS_PORT ) ] |= CONV_PORT(DNS_PORT); /* Sanity check(s) */ if ( !argp ) { PrintDNSConfig(config); return; } argcpyp = strdup( (char*) argp ); if ( !argcpyp ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse DNS options.\n"); return; } cur_tokenp = strtok( argcpyp, " "); while ( cur_tokenp ) { if ( !strcmp( cur_tokenp, DNS_PORTS_KEYWORD )) { /* If the user specified ports, remove 'DNS_PORT' for now since * it now needs to be set explicitely. */ config->ports[ PORT_INDEX( DNS_PORT ) ] = 0; /* Eat the open brace. */ cur_tokenp = strtok( NULL, " "); if (( !cur_tokenp ) || ( strcmp(cur_tokenp, "{" ))) { DynamicPreprocessorFatalMessage("%s(%d) Bad value specified for %s. Must start " "with '{' and be space separated.\n", *(_dpd.config_file), *(_dpd.config_line), DNS_PORTS_KEYWORD); //free(argcpyp); //return; } cur_tokenp = strtok( NULL, " "); while (( cur_tokenp ) && strcmp(cur_tokenp, "}" )) { if ( !isdigit( (int)cur_tokenp[0] )) { DynamicPreprocessorFatalMessage("%s(%d) Bad port %s.\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp ); //free(argcpyp); //return; } else { port = atoi( cur_tokenp ); if( port < 0 || port > MAX_PORTS ) { DynamicPreprocessorFatalMessage("%s(%d) Port value illegitimate: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp ); //free(argcpyp); //return; } config->ports[ PORT_INDEX( port ) ] |= CONV_PORT(port); } cur_tokenp = strtok( NULL, " "); } } else if ( !strcmp( cur_tokenp, DNS_ENABLE_RDATA_OVERFLOW_KEYWORD )) { config->enabled_alerts |= DNS_ALERT_RDATA_OVERFLOW; } else if ( !strcmp( cur_tokenp, DNS_ENABLE_OBSOLETE_TYPES_KEYWORD )) { config->enabled_alerts |= DNS_ALERT_OBSOLETE_TYPES; } else if ( !strcmp( cur_tokenp, DNS_ENABLE_EXPERIMENTAL_TYPES_KEYWORD )) { config->enabled_alerts |= DNS_ALERT_EXPERIMENTAL_TYPES; } #if 0 else if ( !strcmp( cur_tokenp, DNS_AUTODETECT_KEYWORD )) { config->autodetect++; } #endif else { DynamicPreprocessorFatalMessage("Invalid argument: %s\n", cur_tokenp); return; } cur_tokenp = strtok( NULL, " " ); } PrintDNSConfig(config); free(argcpyp); } /* Display the configuration for the DNS preprocessor. * * PARAMETERS: None. * * RETURNS: Nothing. */ static void PrintDNSConfig(DNSConfig *config) { int index; if (config == NULL) return; _dpd.logMsg("DNS config: \n"); #if 0 _dpd.logMsg(" Autodetection: %s\n", config->autodetect ? "ENABLED":"DISABLED"); #endif _dpd.logMsg(" DNS Client rdata txt Overflow Alert: %s\n", config->enabled_alerts & DNS_ALERT_RDATA_OVERFLOW ? "ACTIVE" : "INACTIVE" ); _dpd.logMsg(" Obsolete DNS RR Types Alert: %s\n", config->enabled_alerts & DNS_ALERT_OBSOLETE_TYPES ? "ACTIVE" : "INACTIVE" ); _dpd.logMsg(" Experimental DNS RR Types Alert: %s\n", config->enabled_alerts & DNS_ALERT_EXPERIMENTAL_TYPES ? "ACTIVE" : "INACTIVE" ); /* Printing ports */ _dpd.logMsg(" Ports:"); for(index = 0; index < MAX_PORTS; index++) { if( config->ports[ PORT_INDEX(index) ] & CONV_PORT(index) ) { _dpd.logMsg(" %d", index); } } _dpd.logMsg("\n"); } int dns_print_mem_stats(FILE *fd, char* buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; size_t total_heap_memory = meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory; if (fd) { len = fprintf(fd, ",%lu,%u,%u,%lu,%u,%u,%lu" , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , total_heap_memory); return len; } if (buffer) { /* * No stats apart from the one by the Infra */ len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics for DNS at: %s\n" "DNS Preprocessor Statistics:\n" , ctime(&curr_time)); } else { /* * No stats apart from the one by the Infra */ _dpd.logMsg("\n"); _dpd.logMsg("Memory Statistics of DNS at: %s\n", ctime(&curr_time)); } return len; } /* Retrieves the DNS data block registered with the stream * session associated w/ the current packet. If none exists, * allocates it and registers it with the stream API. * * PARAMETERS: * * p: Pointer to the packet from which/in which to * retrieve/store the DNS data block. * * RETURNS: Pointer to an DNS data block, upon success. * NULL, upon failure. */ static DNSSessionData udpSessionData; #define MIN_UDP_PAYLOAD 0x1FFF DNSSessionData * GetDNSSessionData(SFSnortPacket *p, DNSConfig *config) { DNSSessionData* dnsSessionData = NULL; if (config == NULL) return NULL; if (p->udp_header) { if (!(config->enabled_alerts & DNS_ALERT_OBSOLETE_TYPES) && !(config->enabled_alerts & DNS_ALERT_EXPERIMENTAL_TYPES)) { if (config->enabled_alerts & DNS_ALERT_RDATA_OVERFLOW) { /* Checking RData Overflow... */ if (p->payload_size < (sizeof(DNSHdr) + sizeof(DNSRR) + MIN_UDP_PAYLOAD)) { /* But we don't have sufficient data. Go away. */ return NULL; } } else { /* Not checking for experimental or obsolete types. Go away. */ return NULL; } } /* Its a UDP packet, use the "stateless" one */ dnsSessionData = &udpSessionData; memset(dnsSessionData, 0, sizeof(DNSSessionData)); return dnsSessionData; } /* More Sanity check(s) */ if ( !p->stream_session ) { return NULL; } dnsSessionData = _dpd.snortAlloc(1, sizeof(DNSSessionData), PP_DNS, PP_MEM_CATEGORY_SESSION); if ( !dnsSessionData ) return NULL; /*Register the new DNS data block in the stream session. */ _dpd.sessionAPI->set_application_data( p->stream_session, PP_DNS, dnsSessionData, FreeDNSSessionData ); return dnsSessionData; } /* Registered as a callback with the DNS data when they are * added to the stream session. Called by stream when a * session is about to be destroyed to free that data. * * PARAMETERS: * * application_data: Pointer to the DNS data * * RETURNS: Nothing. */ static void FreeDNSSessionData( void* application_data ) { DNSSessionData* dnsSessionData = (DNSSessionData*)application_data; if ( dnsSessionData ) { _dpd.snortFree(dnsSessionData, sizeof(DNSSessionData), PP_DNS, PP_MEM_CATEGORY_SESSION); } } /* Validates given port as an DNS server port. * * PARAMETERS: * * port: Port to validate. * * RETURNS: DNS_TRUE, if the port is indeed an DNS server port. * DNS_FALSE, otherwise. */ static inline int CheckDNSPort(DNSConfig *config, uint16_t port) { return config->ports[PORT_INDEX(port)] & CONV_PORT(port); } static uint16_t ParseDNSHeader(const unsigned char *data, uint16_t bytes_unused, DNSSessionData *dnsSessionData) { if (bytes_unused == 0) { return bytes_unused; } switch (dnsSessionData->state) { case DNS_RESP_STATE_LENGTH: /* First two bytes are length in TCP */ dnsSessionData->length = ((uint8_t)*data) << 8; dnsSessionData->state = DNS_RESP_STATE_LENGTH_PART; data++; bytes_unused--; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_LENGTH_PART: dnsSessionData->length |= ((uint8_t)*data); dnsSessionData->state = DNS_RESP_STATE_HDR_ID; data++; bytes_unused--; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_ID: dnsSessionData->hdr.id = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_ID_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_ID_PART: dnsSessionData->hdr.id |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_FLAGS; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_FLAGS: dnsSessionData->hdr.flags = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_FLAGS_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_FLAGS_PART: dnsSessionData->hdr.flags |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_QS; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_QS: dnsSessionData->hdr.questions = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_QS_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_QS_PART: dnsSessionData->hdr.questions |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_ANSS; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_ANSS: dnsSessionData->hdr.answers = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_ANSS_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_ANSS_PART: dnsSessionData->hdr.answers |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_AUTHS; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_AUTHS: dnsSessionData->hdr.authorities = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_AUTHS_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_AUTHS_PART: dnsSessionData->hdr.authorities |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_ADDS; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_ADDS: dnsSessionData->hdr.additionals = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_HDR_ADDS_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_HDR_ADDS_PART: dnsSessionData->hdr.additionals |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->state = DNS_RESP_STATE_QUESTION; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ default: /* Continue -- we're beyond the header */ break; } return bytes_unused; } uint16_t ParseDNSName(const unsigned char *data, uint16_t bytes_unused, DNSSessionData *dnsSessionData) { uint16_t bytes_required = dnsSessionData->curr_txt.txt_len - dnsSessionData->curr_txt.txt_bytes_seen; while (dnsSessionData->curr_txt.name_state != DNS_RESP_STATE_NAME_COMPLETE) { if (bytes_unused == 0) { return bytes_unused; } switch (dnsSessionData->curr_txt.name_state) { case DNS_RESP_STATE_NAME_SIZE: dnsSessionData->curr_txt.txt_len = (uint8_t)*data; data++; bytes_unused--; dnsSessionData->bytes_seen_curr_rec++; if (dnsSessionData->curr_txt.txt_len == 0) { dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME_COMPLETE; return bytes_unused; } dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME; dnsSessionData->curr_txt.txt_bytes_seen = 0; if ((dnsSessionData->curr_txt.txt_len & DNS_RR_PTR) == DNS_RR_PTR) { /* A reference to another location... */ /* This is an offset */ dnsSessionData->curr_txt.offset = (dnsSessionData->curr_txt.txt_len & ~0xC0) << 8; bytes_required = dnsSessionData->curr_txt.txt_len = 1; dnsSessionData->curr_txt.relative = 1; /* Setup to read 2nd Byte of Location */ } else { bytes_required = dnsSessionData->curr_txt.txt_len; dnsSessionData->curr_txt.offset = 0; dnsSessionData->curr_txt.relative = 0; } if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_NAME: if (bytes_required <= bytes_unused) { bytes_unused -= bytes_required; if (dnsSessionData->curr_txt.relative) { /* If this one is a relative offset, read that extra byte */ dnsSessionData->curr_txt.offset |= *data; } data += bytes_required; dnsSessionData->bytes_seen_curr_rec += bytes_required; dnsSessionData->curr_txt.txt_bytes_seen += bytes_required; if (bytes_unused == 0) { return bytes_unused; } } else { dnsSessionData->bytes_seen_curr_rec+= bytes_unused; dnsSessionData->curr_txt.txt_bytes_seen += bytes_unused; return 0; } if (dnsSessionData->curr_txt.relative) { /* And since its relative, we're done */ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME_COMPLETE; return bytes_unused; } break; } /* Go to the next portion of the name */ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_NAME_SIZE; } return bytes_unused; } static uint16_t ParseDNSQuestion(const unsigned char *data, uint16_t data_size, uint16_t bytes_unused, DNSSessionData *dnsSessionData) { uint16_t bytes_used = 0; uint16_t new_bytes_unused = 0; if (bytes_unused == 0) { return bytes_unused; } if (dnsSessionData->curr_rec_state < DNS_RESP_STATE_Q_NAME_COMPLETE) { new_bytes_unused = ParseDNSName(data, bytes_unused, dnsSessionData); bytes_used = bytes_unused - new_bytes_unused; #ifdef DUMP_BUFFER dumpBuffer(DNS_QUESTION_DUMP,data,bytes_used); #endif if (dnsSessionData->curr_txt.name_state == DNS_RESP_STATE_NAME_COMPLETE) { dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_TYPE; memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState)); data = data + bytes_used; bytes_unused = new_bytes_unused; if (bytes_unused == 0) { /* ran out of data */ return bytes_unused; } } else { /* Should be 0 -- ran out of data */ return new_bytes_unused; } } switch (dnsSessionData->curr_rec_state) { case DNS_RESP_STATE_Q_TYPE: dnsSessionData->curr_q.type = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_TYPE_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_Q_TYPE_PART: dnsSessionData->curr_q.type |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_CLASS; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_Q_CLASS: dnsSessionData->curr_q.dns_class = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_CLASS_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_Q_CLASS_PART: dnsSessionData->curr_q.dns_class |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_COMPLETE; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ default: /* Continue -- we're beyond this question */ break; } return bytes_unused; } uint16_t ParseDNSAnswer(const unsigned char *data, uint16_t data_size, uint16_t bytes_unused, DNSSessionData *dnsSessionData) { uint16_t bytes_used = 0; uint16_t new_bytes_unused = 0; if (bytes_unused == 0) { return bytes_unused; } if (dnsSessionData->curr_rec_state < DNS_RESP_STATE_RR_NAME_COMPLETE) { new_bytes_unused = ParseDNSName(data, bytes_unused, dnsSessionData); bytes_used = bytes_unused - new_bytes_unused; #ifdef DUMP_BUFFER dumpBuffer(DNS_ANSWER_DUMP,data,bytes_used); #endif if (dnsSessionData->curr_txt.name_state == DNS_RESP_STATE_NAME_COMPLETE) { dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TYPE; memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState)); data = data + bytes_used; } bytes_unused = new_bytes_unused; if (bytes_unused == 0) { /* ran out of data */ return bytes_unused; } } switch (dnsSessionData->curr_rec_state) { case DNS_RESP_STATE_RR_TYPE: dnsSessionData->curr_rr.type = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TYPE_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_RR_TYPE_PART: dnsSessionData->curr_rr.type |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_CLASS; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_RR_CLASS: dnsSessionData->curr_rr.dns_class = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_CLASS_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_RR_CLASS_PART: dnsSessionData->curr_rr.dns_class |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TTL; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_RR_TTL: dnsSessionData->curr_rr.ttl = (uint8_t)*data << 24; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_TTL_PART; dnsSessionData->bytes_seen_curr_rec = 1; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_RR_TTL_PART: while (dnsSessionData->bytes_seen_curr_rec < 4) { dnsSessionData->bytes_seen_curr_rec++; dnsSessionData->curr_rr.ttl |= (uint8_t)*data << (4-dnsSessionData->bytes_seen_curr_rec)*8; data++; bytes_unused--; if (bytes_unused == 0) { return bytes_unused; } } dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDLENGTH; /* Fall through */ case DNS_RESP_STATE_RR_RDLENGTH: dnsSessionData->curr_rr.length = (uint8_t)*data << 8; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDLENGTH_PART; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_RR_RDLENGTH_PART: dnsSessionData->curr_rr.length |= (uint8_t)*data; data++; bytes_unused--; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_START; if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ default: /* Continue -- we're beyond this answer */ break; } return bytes_unused; } /* The following check is to look for an attempt to exploit * a vulnerability in the DNS client, per MS 06-041. * * For details, see: * http://www.microsoft.com/technet/security/bulletin/ms06-007.mspx * http://cve.mitre.org/cgi-bin/cvename.cgi?name=2006-3441 * * Vulnerability Research by Lurene Grenier, Judy Novak, * and Brian Caswell. */ uint16_t CheckRRTypeTXTVuln(const unsigned char *data, uint16_t bytes_unused, DNSSessionData *dnsSessionData) { uint16_t bytes_required = dnsSessionData->curr_txt.txt_len - dnsSessionData->curr_txt.txt_bytes_seen; while (dnsSessionData->curr_txt.name_state != DNS_RESP_STATE_RR_NAME_COMPLETE) { if (dnsSessionData->bytes_seen_curr_rec == dnsSessionData->curr_rr.length) { /* Done with the name */ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_RR_NAME_COMPLETE; /* Got to the end of the rdata in this packet! */ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_COMPLETE; return bytes_unused; } #ifdef DUMP_BUFFER dumpBuffer(DNS_RR_TYPE_TXT_VULN_DUMP,data,bytes_unused); #endif if (bytes_unused == 0) { return bytes_unused; } switch (dnsSessionData->curr_txt.name_state) { case DNS_RESP_STATE_RR_NAME_SIZE: dnsSessionData->curr_txt.txt_len = (uint8_t)*data; dnsSessionData->curr_txt.txt_count++; dnsSessionData->curr_txt.total_txt_len += dnsSessionData->curr_txt.txt_len + 1; /* include the NULL */ if (!dnsSessionData->curr_txt.alerted) { uint32_t overflow_check = (dnsSessionData->curr_txt.txt_count * 4) + (dnsSessionData->curr_txt.total_txt_len * 2) + 4; /* if txt_count * 4 + total_txt_len * 2 + 4 > FFFF, vulnerability! */ if (overflow_check > 0xFFFF) { if (dns_eval_config->enabled_alerts & DNS_ALERT_RDATA_OVERFLOW) { /* Alert on obsolete DNS RR types */ DNS_ALERT(DNS_EVENT_RDATA_OVERFLOW, DNS_EVENT_RDATA_OVERFLOW_STR); } dnsSessionData->curr_txt.alerted = 1; } } data++; bytes_unused--; dnsSessionData->bytes_seen_curr_rec++; if (dnsSessionData->curr_txt.txt_len > 0) { dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_RR_NAME; dnsSessionData->curr_txt.txt_bytes_seen = 0; bytes_required = dnsSessionData->curr_txt.txt_len; } else { continue; } if (bytes_unused == 0) { return bytes_unused; } /* Fall through */ case DNS_RESP_STATE_RR_NAME: if (bytes_required <= bytes_unused) { bytes_unused -= bytes_required; dnsSessionData->bytes_seen_curr_rec += bytes_required; data += bytes_required; dnsSessionData->curr_txt.txt_bytes_seen += bytes_required; if (bytes_unused == 0) { return bytes_unused; } } else { dnsSessionData->curr_txt.txt_bytes_seen += bytes_unused; dnsSessionData->bytes_seen_curr_rec += bytes_unused; return 0; } break; } /* Go to the next portion of the name */ dnsSessionData->curr_txt.name_state = DNS_RESP_STATE_RR_NAME_SIZE; } return bytes_unused; } uint16_t SkipDNSRData(const unsigned char *data, uint16_t bytes_unused, DNSSessionData *dnsSessionData) { uint16_t bytes_required = dnsSessionData->curr_rr.length - dnsSessionData->bytes_seen_curr_rec; if (bytes_required <= bytes_unused) { bytes_unused -= bytes_required; data += bytes_required; dnsSessionData->bytes_seen_curr_rec += bytes_required; } else { dnsSessionData->bytes_seen_curr_rec += bytes_unused; return 0; } /* Got to the end of the rdata in this packet! */ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_COMPLETE; return bytes_unused; } uint16_t ParseDNSRData(SFSnortPacket *p, const unsigned char *data, uint16_t bytes_unused, DNSSessionData *dnsSessionData) { if (bytes_unused == 0) { return bytes_unused; } switch (dnsSessionData->curr_rr.type) { case DNS_RR_TYPE_TXT: /* Check for RData Overflow */ bytes_unused = CheckRRTypeTXTVuln(data, bytes_unused, dnsSessionData); break; case DNS_RR_TYPE_MD: case DNS_RR_TYPE_MF: if (dns_eval_config->enabled_alerts & DNS_ALERT_OBSOLETE_TYPES) { /* Alert on obsolete DNS RR types */ DNS_ALERT(DNS_EVENT_OBSOLETE_TYPES, DNS_EVENT_OBSOLETE_TYPES_STR); } bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData); #ifdef DUMP_BUFFER dumpBuffer(DNS_OBSOLETE_TYPES_DUMP,data,bytes_unused); #endif break; case DNS_RR_TYPE_MB: case DNS_RR_TYPE_MG: case DNS_RR_TYPE_MR: case DNS_RR_TYPE_NULL: case DNS_RR_TYPE_MINFO: if (dns_eval_config->enabled_alerts & DNS_ALERT_EXPERIMENTAL_TYPES) { /* Alert on experimental DNS RR types */ DNS_ALERT(DNS_EVENT_EXPERIMENTAL_TYPES, DNS_EVENT_EXPERIMENTAL_TYPES_STR); } bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData); #ifdef DUMP_BUFFER dumpBuffer(DNS_EXPERIMENTAL_TYPES_DUMP,data,bytes_unused); #endif break; case DNS_RR_TYPE_A: case DNS_RR_TYPE_NS: case DNS_RR_TYPE_CNAME: case DNS_RR_TYPE_SOA: case DNS_RR_TYPE_WKS: case DNS_RR_TYPE_PTR: case DNS_RR_TYPE_HINFO: case DNS_RR_TYPE_MX: bytes_unused = SkipDNSRData(data, bytes_unused, dnsSessionData); #ifdef DUMP_BUFFER dumpBuffer(DNS_SKIP_RDATA_DUMP,data,bytes_unused); #endif break; default: /* Not one of the known types. Stop looking at this session * as DNS. */ dnsSessionData->flags |= DNS_FLAG_NOT_DNS; break; } return bytes_unused; } void ParseDNSResponseMessage(SFSnortPacket *p, DNSSessionData *dnsSessionData) { uint16_t bytes_unused = p->payload_size; int i; const unsigned char *data = p->payload; while (bytes_unused) { /* Parse through the DNS Header */ if (dnsSessionData->state < DNS_RESP_STATE_QUESTION) { /* Length only applies on a TCP packet, skip to header ID * if at beginning of a UDP Response. */ if ((dnsSessionData->state == DNS_RESP_STATE_LENGTH) && (p->udp_header)) { dnsSessionData->state = DNS_RESP_STATE_HDR_ID; } bytes_unused = ParseDNSHeader(data, bytes_unused, dnsSessionData); if (bytes_unused > 0) { data = p->payload + (p->payload_size - bytes_unused); } else { /* No more data */ return; } dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_NAME; dnsSessionData->curr_rec = 0; } /* Print out the header (but only once -- when we're ready to parse the Questions */ #ifdef DEBUG_MSGS if ((dnsSessionData->curr_rec_state == DNS_RESP_STATE_Q_NAME) && (dnsSessionData->curr_rec == 0)) { DebugMessage(DEBUG_DNS, "DNS Header: length %d, id 0x%x, flags 0x%x, " "questions %d, answers %d, authorities %d, additionals %d\n", dnsSessionData->length, dnsSessionData->hdr.id, dnsSessionData->hdr.flags, dnsSessionData->hdr.questions, dnsSessionData->hdr.answers, dnsSessionData->hdr.authorities, dnsSessionData->hdr.additionals); } #endif if (!(dnsSessionData->hdr.flags & DNS_HDR_FLAG_RESPONSE)) { /* Not a response */ return; } /* Handle the DNS Queries */ if (dnsSessionData->state == DNS_RESP_STATE_QUESTION) { /* Skip over the 4 byte question records... */ for (i=dnsSessionData->curr_rec; i< dnsSessionData->hdr.questions; i++) { bytes_unused = ParseDNSQuestion(data, p->payload_size, bytes_unused, dnsSessionData); if (dnsSessionData->curr_rec_state == DNS_RESP_STATE_Q_COMPLETE) { DEBUG_WRAP( DebugMessage(DEBUG_DNS, "DNS Question %d: type %d, class %d\n", i, dnsSessionData->curr_q.type, dnsSessionData->curr_q.dns_class); ); dnsSessionData->curr_rec_state = DNS_RESP_STATE_Q_NAME; dnsSessionData->curr_rec++; } if (bytes_unused > 0) { data = p->payload + (p->payload_size - bytes_unused); } else { /* No more data */ return; } } dnsSessionData->state = DNS_RESP_STATE_ANS_RR; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE; dnsSessionData->curr_rec = 0; } /* Handle the RRs */ switch (dnsSessionData->state) { case DNS_RESP_STATE_ANS_RR: /* ANSWERS section */ for (i=dnsSessionData->curr_rec; ihdr.answers; i++) { bytes_unused = ParseDNSAnswer(data, p->payload_size, bytes_unused, dnsSessionData); #ifdef DUMP_BUFFER dumpBuffer(DNS_RESP_STATE_ANS_RR_DUMP,data,bytes_unused); #endif if (bytes_unused == 0) { /* No more data */ return; } switch (dnsSessionData->curr_rec_state) { case DNS_RESP_STATE_RR_RDATA_START: DEBUG_WRAP( DebugMessage(DEBUG_DNS, "DNS ANSWER RR %d: type %d, class %d, " "ttl %d rdlength %d\n", i, dnsSessionData->curr_rr.type, dnsSessionData->curr_rr.dns_class, dnsSessionData->curr_rr.ttl, dnsSessionData->curr_rr.length); ); dnsSessionData->bytes_seen_curr_rec = 0; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_MID; /* Fall through */ case DNS_RESP_STATE_RR_RDATA_MID: /* Data now points to the beginning of the RDATA */ data = p->payload + (p->payload_size - bytes_unused); bytes_unused = ParseDNSRData(p, data, bytes_unused, dnsSessionData); if (dnsSessionData->curr_rec_state != DNS_RESP_STATE_RR_COMPLETE) { /* Out of data, pick up on the next packet */ return; } else { /* Go to the next record */ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE; dnsSessionData->curr_rec++; if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT) { /* Reset the state tracking for this record */ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState)); } data = p->payload + (p->payload_size - bytes_unused); } } } dnsSessionData->state = DNS_RESP_STATE_AUTH_RR; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE; dnsSessionData->curr_rec = 0; /* Fall through */ case DNS_RESP_STATE_AUTH_RR: /* AUTHORITIES section */ for (i=dnsSessionData->curr_rec; ihdr.authorities; i++) { bytes_unused = ParseDNSAnswer(data, p->payload_size, bytes_unused, dnsSessionData); #ifdef DUMP_BUFFER dumpBuffer(DNS_RESP_STATE_AUTH_RR_DUMP,data,bytes_unused); #endif if (bytes_unused == 0) { /* No more data */ return; } switch (dnsSessionData->curr_rec_state) { case DNS_RESP_STATE_RR_RDATA_START: DEBUG_WRAP( DebugMessage(DEBUG_DNS, "DNS AUTH RR %d: type %d, class %d, " "ttl %d rdlength %d\n", i, dnsSessionData->curr_rr.type, dnsSessionData->curr_rr.dns_class, dnsSessionData->curr_rr.ttl, dnsSessionData->curr_rr.length); ); dnsSessionData->bytes_seen_curr_rec = 0; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_MID; /* Fall through */ case DNS_RESP_STATE_RR_RDATA_MID: /* Data now points to the beginning of the RDATA */ data = p->payload + (p->payload_size - bytes_unused); bytes_unused = ParseDNSRData(p, data, bytes_unused, dnsSessionData); if (dnsSessionData->curr_rec_state != DNS_RESP_STATE_RR_COMPLETE) { /* Out of data, pick up on the next packet */ return; } else { /* Go to the next record */ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE; dnsSessionData->curr_rec++; if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT) { /* Reset the state tracking for this record */ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState)); } data = p->payload + (p->payload_size - bytes_unused); } } } dnsSessionData->state = DNS_RESP_STATE_ADD_RR; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE; dnsSessionData->curr_rec = 0; /* Fall through */ case DNS_RESP_STATE_ADD_RR: /* ADDITIONALS section */ for (i=dnsSessionData->curr_rec; ihdr.authorities; i++) { bytes_unused = ParseDNSAnswer(data, p->payload_size, bytes_unused, dnsSessionData); #ifdef DUMP_BUFFER dumpBuffer(DNS_RESP_STATE_ADD_RR_DUMP,data,bytes_unused); #endif if (bytes_unused == 0) { /* No more data */ return; } switch (dnsSessionData->curr_rec_state) { case DNS_RESP_STATE_RR_RDATA_START: DEBUG_WRAP( DebugMessage(DEBUG_DNS, "DNS ADDITONAL RR %d: type %d, class %d, " "ttl %d rdlength %d\n", i, dnsSessionData->curr_rr.type, dnsSessionData->curr_rr.dns_class, dnsSessionData->curr_rr.ttl, dnsSessionData->curr_rr.length); ); dnsSessionData->bytes_seen_curr_rec = 0; dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_RDATA_MID; /* Fall through */ case DNS_RESP_STATE_RR_RDATA_MID: /* Data now points to the beginning of the RDATA */ data = p->payload + (p->payload_size - bytes_unused); bytes_unused = ParseDNSRData(p, data, bytes_unused, dnsSessionData); if (dnsSessionData->curr_rec_state != DNS_RESP_STATE_RR_COMPLETE) { /* Out of data, pick up on the next packet */ return; } else { /* Go to the next record */ dnsSessionData->curr_rec_state = DNS_RESP_STATE_RR_NAME_SIZE; dnsSessionData->curr_rec++; if (dnsSessionData->curr_rr.type == DNS_RR_TYPE_TXT) { /* Reset the state tracking for this record */ memset(&dnsSessionData->curr_txt, 0, sizeof(DNSNameState)); } data = p->payload + (p->payload_size - bytes_unused); } } } /* Done with this one, onto the next -- may also be in this packet */ dnsSessionData->state = DNS_RESP_STATE_LENGTH; dnsSessionData->curr_rec_state = 0; dnsSessionData->curr_rec = 0; } } return; } /* Main runtime entry point for DNS preprocessor. * Analyzes DNS packets for anomalies/exploits. * * PARAMETERS: * * p: Pointer to current packet to process. * context: Pointer to context block, not used. * * RETURNS: Nothing. */ static void ProcessDNS( void* packetPtr, void* context ) { DNSSessionData* dnsSessionData = NULL; uint8_t src = 0; uint8_t dst = 0; uint8_t known_port = 0; uint8_t direction = 0; SFSnortPacket* p; #ifdef TARGET_BASED int16_t app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif DNSConfig *config = NULL; PROFILE_VARS; sfPolicyUserPolicySet (dns_config, _dpd.getNapRuntimePolicy()); config = (DNSConfig *)sfPolicyUserDataGetCurrent(dns_config); if (config == NULL) return; #ifdef DUMP_BUFFER dumpBufferInit(); #endif dns_eval_config = config; p = (SFSnortPacket*) packetPtr; // preconditions - what we registered for assert((IsUDP(p) || IsTCP(p)) && p->payload_size && p->payload); /* Attempt to get a previously allocated DNS block. If none exists, * allocate and register one with the stream layer. */ dnsSessionData = _dpd.sessionAPI->get_application_data( p->stream_session, PP_DNS ); #ifdef DUMP_BUFFER dumpBuffer(DNS_PAYLOAD_DUMP,p->payload,p->payload_size); #endif if (dnsSessionData == NULL) { /* Check the ports to make sure this is a DNS port. * Otherwise no need to examine the traffic. */ #ifdef TARGET_BASED app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if (app_id == SFTARGET_UNKNOWN_PROTOCOL) return; if (app_id && (app_id != dns_app_id)) return; if (!app_id) { #endif src = CheckDNSPort(config, p->src_port); dst = CheckDNSPort(config, p->dst_port); #ifdef TARGET_BASED } #endif /* See if a known server port is involved. */ known_port = ( src || dst ? 1 : 0 ); #if 0 if ( !dns_config->autodetect && !src && !dst ) { /* Not one of the ports we care about. */ return; } #endif #ifdef TARGET_BASED if (!app_id && !known_port) #else if (!known_port) #endif { /* Not one of the ports we care about. */ return; } } /* For TCP, do a few extra checks... */ if (p->tcp_header) { /* If session picked up mid-stream, do not process further. * Would be almost impossible to tell where we are in the * data stream. */ if ( _dpd.sessionAPI->get_session_flags( p->stream_session) & SSNFLAG_MIDSTREAM ) { return; } if ( !_dpd.streamAPI->is_stream_sequenced(p->stream_session, SSN_DIR_TO_SERVER)) { return; } if (!(_dpd.streamAPI->get_reassembly_direction(p->stream_session) & SSN_DIR_TO_SERVER)) { /* This should only happen for the first packet (SYN or SYN-ACK) * in the TCP session */ _dpd.streamAPI->set_reassembly(p->stream_session, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_TO_SERVER, STREAM_FLPOLICY_SET_ABSOLUTE); return; } /* If we're waiting on stream reassembly, don't process this packet. */ if ( p->flags & FLAG_STREAM_INSERT) { return; } /* Get the direction of the packet. */ direction = ( (p->flags & FLAG_FROM_SERVER ) ? DNS_DIR_FROM_SERVER : DNS_DIR_FROM_CLIENT ); } else if (p->udp_header) { #ifdef TARGET_BASED if (app_id == dns_app_id) { direction = ( (p->flags & FLAG_FROM_SERVER ) ? DNS_DIR_FROM_SERVER : DNS_DIR_FROM_CLIENT ); } else { #endif if (src) direction = DNS_DIR_FROM_SERVER; else if (dst) direction = DNS_DIR_FROM_CLIENT; #ifdef TARGET_BASED } #endif } PREPROC_PROFILE_START(dnsPerfStats); /* Check the stream session. If it does not currently * have our DNS data-block attached, create one. */ if (dnsSessionData == NULL) dnsSessionData = GetDNSSessionData(p, config); if ( !dnsSessionData ) { /* Could not get/create the session data for this packet. */ PREPROC_PROFILE_END(dnsPerfStats); return; } if (dnsSessionData->flags & DNS_FLAG_NOT_DNS) { /* determined that this session wasn't DNS, we're done */ PREPROC_PROFILE_END(dnsPerfStats); return; } if (direction == DNS_DIR_FROM_SERVER) { ParseDNSResponseMessage(p, dnsSessionData); } PREPROC_PROFILE_END(dnsPerfStats); } static void DNSReset(int signal, void *data) { return; } static void DNSResetStats(int signal, void *data) { return; } static void enablePortStreamServices(struct _SnortConfig *sc, DNSConfig *config, tSfPolicyId policy_id) { uint32_t port; if (config == NULL) return; for (port = 0; port < MAXPORTS; port++) { if( isPortEnabled( config->ports, port ) ) { // set port filter status _dpd.streamAPI->set_port_filter_status (sc, IPPROTO_TCP, (uint16_t)port, PORT_MONITOR_SESSION, policy_id, 1); _dpd.streamAPI->set_port_filter_status (sc, IPPROTO_UDP, (uint16_t)port, PORT_MONITOR_SESSION, policy_id, 1); // register for reassembly _dpd.streamAPI->register_reassembly_port( NULL, port, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); // enable dns preproc for dispatch on configure ports _dpd.sessionAPI->enable_preproc_for_port( sc, PP_DNS, PROTO_BIT__TCP | PROTO_BIT__UDP, port ); } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status (sc, dns_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif static int DnsFreeConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { DNSConfig *pPolicyConfig = (DNSConfig *)pData; //do any housekeeping before freeing DnsConfig sfPolicyUserDataClear (config, policyId); _dpd.snortFree(pPolicyConfig, sizeof(DNSConfig), PP_DNS, PP_MEM_CATEGORY_CONFIG); return 0; } static void DNSFreeConfig(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, DnsFreeConfigPolicy); sfPolicyConfigDelete(config); } static int DNSCheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { _dpd.setParserPolicy(sc, policyId); if (_dpd.streamAPI == NULL) { _dpd.errMsg("Streaming & reassembly must be enabled for DNS preprocessor\n"); return -1; } return 0; } static int DNSCheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate (sc, dns_config, DNSCheckPolicyConfig))) return rval; return 0; } static void DNSCleanExit(int signal, void *data) { DNSFreeConfig(dns_config); dns_config = NULL; } #ifdef SNORT_RELOAD static void DNSReload(struct _SnortConfig *sc, char *argp, void **new_config) { tSfPolicyUserContextId dns_swap_config = (tSfPolicyUserContextId)*new_config; int policy_id = _dpd.getParserPolicy(sc); DNSConfig *pPolicyConfig = NULL; if (dns_swap_config == NULL) { //create a context dns_swap_config = sfPolicyConfigCreate(); if (dns_swap_config == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "DNS configuration.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Dns preprocessor requires the " "stream5 preprocessor to be enabled.\n", *(_dpd.config_file), *(_dpd.config_line)); } *new_config = (void *)dns_swap_config; } sfPolicyUserPolicySet (dns_swap_config, policy_id); pPolicyConfig = (DNSConfig *)sfPolicyUserDataGetCurrent(dns_swap_config); if (pPolicyConfig) { DynamicPreprocessorFatalMessage("%s(%d) Dns preprocessor can only " "be configured once.\n", *(_dpd.config_file), *(_dpd.config_line)); } pPolicyConfig = (DNSConfig *)_dpd.snortAlloc(1, sizeof(DNSConfig), PP_DNS, PP_MEM_CATEGORY_CONFIG); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "DNS configuration.\n"); } sfPolicyUserDataSetCurrent(dns_swap_config, pPolicyConfig); ParseDNSArgs(pPolicyConfig, (u_char *)argp); _dpd.addPreproc(sc, ProcessDNS, PRIORITY_APPLICATION, PP_DNS, PROTO_BIT__TCP | PROTO_BIT__UDP); enablePortStreamServices(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } static int DNSReloadVerify(struct _SnortConfig *sc, void *swap_config) { int rval; if( ( rval = sfPolicyUserDataIterate( sc, swap_config, DNSCheckPolicyConfig ) ) ) return rval; return 0; } static void * DNSReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId dns_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = dns_config; if (dns_swap_config == NULL) return NULL; dns_config = dns_swap_config; return (void *)old_config; } static void DNSReloadSwapFree(void *data) { if (data == NULL) return; DNSFreeConfig((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/dns/Makefile.in0000644000175000017500000005256214242725546021071 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@dns_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@dns_buffer_dump.h subdir = src/dynamic-preprocessors/dns ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_dns_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_dns_preproc_la_SOURCES_DIST = spp_dns.c spp_dns.h \ dns_buffer_dump.c dns_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = dns_buffer_dump.lo am_libsf_dns_preproc_la_OBJECTS = spp_dns.lo $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_dns_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_dns_preproc_la_OBJECTS = $(am_libsf_dns_preproc_la_OBJECTS) \ $(nodist_libsf_dns_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_dns_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_dns_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_dns_preproc_la_SOURCES) \ $(nodist_libsf_dns_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_dns_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_dns_preproc.la libsf_dns_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_dns_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_dns_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c libsf_dns_preproc_la_SOURCES = spp_dns.c spp_dns.h $(am__append_1) EXTRA_DIST = \ sf_dns.vcxproj \ sf_dns.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/dns/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/dns/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_dns_preproc.la: $(libsf_dns_preproc_la_OBJECTS) $(libsf_dns_preproc_la_DEPENDENCIES) $(EXTRA_libsf_dns_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_dns_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_dns_preproc_la_OBJECTS) $(libsf_dns_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/dns/dns_buffer_dump.h0000644000175000017500000000344014241075724022321 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file dns_buffer_dump.h ** ** @author Seshaiah Erugu ** ** @brief This file contains structures and functions for ** dumping buffers used during DNS inspection. */ #ifndef __DNS_BUFFER_DUMP_H__ #define __DNS_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { DNS_PAYLOAD_DUMP, DNS_QUESTION_DUMP, DNS_ANSWER_DUMP, DNS_RESP_STATE_ANS_RR_DUMP, DNS_RESP_STATE_AUTH_RR_DUMP, DNS_RESP_STATE_ADD_RR_DUMP, DNS_RR_TYPE_TXT_VULN_DUMP, DNS_OBSOLETE_TYPES_DUMP, DNS_EXPERIMENTAL_TYPES_DUMP, DNS_SKIP_RDATA_DUMP } DNS_BUFFER_DUMP; void dumpBuffer(DNS_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getDNSBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/dns/Makefile.am0000444000175000017500000000141014230012554021021 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_dns_preproc.la libsf_dns_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_dns_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_dns_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c endif libsf_dns_preproc_la_SOURCES = \ spp_dns.c \ spp_dns.h if BUILD_BUFFER_DUMP libsf_dns_preproc_la_SOURCES += \ dns_buffer_dump.c \ dns_buffer_dump.h endif EXTRA_DIST = \ sf_dns.vcxproj \ sf_dns.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/sdf/0000755000175000017500000000000014242725716017001 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/sdf/sdf_us_ssn.c0000644000175000017500000002415614241076272021316 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_sdf.h" #include "sdf_us_ssn.h" #include "sf_dynamic_preprocessor.h" #include #include #include #include #include #include static int SDFCompareGroupNumbers(int group, int max_group); static int SSNGroupCategory(int group); /* This function takes a string representation of a US Social Security number and checks that it is valid. The string may include or omit hyphens.*/ int SDFSocialCheck(char *buf, uint32_t buflen, struct _SDFConfig *config) { uint32_t i; int digits, area, group, serial; char numbuf[9]; if (buf == NULL || buflen > 13 || buflen < 9) return 0; /* Generally, the string will have a non-digit byte on each side. * Sometimes, when the string is pointing to the first line of the * data, it might start with a digit, instead of a non-digit. * Strip the non-digits only. */ if (isdigit((int)buf[0])) buflen -= 1; else { buf++; buflen -= 2; } /* Check that the string is made of digits, and strip hyphens. */ digits = 0; for (i = 0; i < buflen; i++) { if (isdigit((int)buf[i])) { /* Check for too many digits */ if (digits == 9) return 0; numbuf[digits++] = buf[i]; } else if (buf[i] != '-') break; } if (digits != 9) return 0; /* Convert to ints */ area = (numbuf[0] - '0') * 100 + (numbuf[1] - '0') * 10 + (numbuf[2] - '0'); group = (numbuf[3] - '0') * 10 + (numbuf[4] - '0'); serial = (numbuf[5] - '0') * 1000 + (numbuf[6] - '0') * 100 + (numbuf[7] - '0') * 10 + (numbuf[8] - '0'); /* This range was reserved for advertising */ if (area == 987 && group == 65) { if (serial >= 4320 && serial <= 4329) return 0; } /* Start validating */ if (area > MAX_AREA || area == 666 || area <= 0 || group <= 0 || group > 99 || serial <= 0 || serial > 9999) return 0; return SDFCompareGroupNumbers(group, config->ssn_max_group[area]); } static int SDFCompareGroupNumbers(int group, int max_group) { /* Group numbers are not issued in consecutive order. They go in this order: 1. ODD numbers from 01 through 09 2. EVEN numbers from 10 through 98 3. EVEN numbers from 02 through 08 4. ODD numbers from 11 through 99 For this reason, the group check is not simple. */ int group_category = SSNGroupCategory(group); int max_group_category = SSNGroupCategory(max_group); if (group_category == 0 || max_group_category == 0) return 0; if (group_category < max_group_category) return 1; if ((group_category == max_group_category) && (group <= max_group)) return 1; return 0; } static int SSNGroupCategory(int group) { if ((group % 2 == 1) && (group < 10)) return 1; if ((group % 2 == 0) && (group >= 10) && (group <= 98)) return 2; if ((group % 2 == 0) && (group < 10)) return 3; if ((group % 2 == 1) && (group >= 11) && (group <= 99)) return 4; return 0; } int ParseSSNGroups(char *filename, struct _SDFConfig *config) { FILE *ssn_file; char *contents, *token, *saveptr, *endptr; long length; int i = 1; if (filename == NULL || config == NULL) return -1; ssn_file = fopen(filename, "r"); if (ssn_file == NULL) { _dpd.logMsg("Sensitive Data preprocessor: Failed to open SSN groups " "file \"%s\": %s.\n", filename, strerror(errno)); return -1; } /* Determine size of file */ if (fseek(ssn_file, 0, SEEK_END) == -1) { _dpd.logMsg("Sensitive Data preprocessor: Failed to fseek() to end of " "SSN groups file \"%s\": %s.\n", filename, strerror(errno)); fclose(ssn_file); return -1; } if ((length = ftell(ssn_file)) <= 0) { if (length == -1) { _dpd.logMsg("Sensitive Data preprocessor: Failed to get size of SSN " "groups file \"%s\": %s.\n", filename, strerror(errno)); } else { _dpd.logMsg("Sensitive Data preprocessor: SSN groups file \"%s\" " "is empty.\n", filename); } fclose(ssn_file); return -1; } rewind(ssn_file); contents = (char *)malloc(length + 1); if (contents == NULL) { _dpd.logMsg("Sensitive Data preprocessor: Failed to allocate memory " "for SSN groups.\n"); fclose(ssn_file); free(contents); return -1; } /* Read file into memory */ if (fread(contents, sizeof(char), length, ssn_file) != (size_t)length) { _dpd.logMsg("Sensitive Data preprocessor: Failed read contents of " "SSN groups file \"%s\".\n", filename); free(contents); fclose(ssn_file); return -1; } fclose(ssn_file); contents[length] = '\0'; /* Parse! */ token = strtok_r(contents, " ,\n", &saveptr); while (token) { if (i > MAX_AREA) { /* TODO: Print error - too many ints */ free(contents); return -1; } config->ssn_max_group[i++] = strtol(token, &endptr, 10); if (*endptr != '\0') { /* TODO: Print error - not a complete number */ free(contents); return -1; } token = strtok_r(NULL, " ,\n", &saveptr); } free(contents); return 0; } /* Default array of maximum group numbers for each area. These values were last up-to-date as of November 2009. */ int SSNSetDefaultGroups(struct _SDFConfig *config) { int i; int default_max_group[MAX_AREA+1] = { 0, 8, 8, 6, 11, 11, 11, 8, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 92, 90, 90, 90, 90, 90, 90, 74, 74, 72, 72, 72, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 98, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 86, 86, 86, 86, 86, 86, 86, 86, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 85, 85, 85, 8, 8, 99, 99, 99, 99, 99, 99, 99, 99, 99, 55, 55, 55, 55, 55, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 35, 35, 35, 35, 35, 35, 35, 35, 33, 33, 33, 33, 33, 33, 33, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 6, 6, 6, 6, 6, 6, 6, 6, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 29, 71, 71, 71, 71, 71, 71, 69, 69, 99, 99, 99, 99, 99, 99, 99, 99, 65, 65, 65, 65, 65, 65, 65, 63, 63, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 27, 25, 25, 25, 25, 25, 25, 25, 25, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 55, 53, 53, 53, 53, 53, 53, 53, 53, 53, 41, 41, 39, 39, 39, 39, 39, 39, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 35, 35, 43, 43, 55, 55, 55, 55, 31, 31, 31, 29, 29, 29, 29, 47, 47, 83, 83, 59, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 67, 67, 67, 67, 67, 67, 67, 67, 65, 79, 79, 79, 77, 77, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 57, 99, 99, 49, 49, 49, 39, 99, 99, 99, 99, 99, 65, 99, 5, 99, 99, 99, 99, 99, 99, 99, 90, 88, 88, 88, 99, 99, 79, 79, 79, 79, 79, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 77, 23, 23, 23, 23, 23, 23, 23, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 13, 11, 52, 52, 56, 56, 54, 54, 32, 32, 32, 32, 32, 20, 20, 20, 20, 18, 18, 18, 44, 42, 42, 42, 42, 42, 42, 42, 42, 18, 18, 18, 16, 17, 20, 20, 20, 20, 18, 18, 18, 18, 18, 18, 12, 12, 12, 12, 12, 12, 12, 12, 12, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 28, 18, 18, 10, 14, 20, 18, 18, 18, 18, 14, 14, 5, 5, 5, 5, 10, 9, 9, 9, 9, 9, 9, 9, 11, 8, 86, 86, 86, 86, 84, 84, 84 }; if (config == NULL) return -1; for (i = 0; i < MAX_AREA+1; i++) { config->ssn_max_group[i] = default_max_group[i]; } return 1; } snort-2.9.20/src/dynamic-preprocessors/sdf/sdf_pattern_match.c0000644000175000017500000005246714241076267022647 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sdf_pattern_match.h" #include "treenodes.h" #include "sf_dynamic_preprocessor.h" /* Main pattern-adding function. * Arguments: * head => pointer to top node in PII tree * data => pointer to SDFOptionData struct w/ new pattern * otn => pointer to OptTreeNode struct that this pattern belongs to * Return values: * -1: error * 1: pattern added successfully */ static int AddPiiPattern(sdf_tree_node *head, SDFOptionData *data) { char *pattern = data->pii; int i = 0; int pattern_added = 0; if (head == NULL || pattern == NULL) return -1; /* If the root has some children, try to fit the pattern under them first. */ while(i < head->num_children && !pattern_added) { pattern_added = AddPiiPiece(head->children[i], pattern, data); i++; } /* Otherwise, add a new child to the root node */ if (!pattern_added) { AddChild(head, data, data->pii); pattern_added = 1; } return pattern_added; } /* Check that the brackets in a pattern match up, and only contain numbers. * * Arguments: * pii - string containing pattern. * * Returns: void function. Raises fatal error if there's a problem. */ static void ExpandBrackets(char **pii) { char *bracket_index, *new_pii, *endptr, *pii_position; unsigned long int new_pii_size, repetitions, total_reps = 0; unsigned int num_brackets = 0; if (pii == NULL || *pii == NULL) return; /* Locate first '{' */ bracket_index = strchr(*pii, '{'); /* Brackets at the beginning have nothing to modify. */ if (bracket_index == *pii) { DynamicPreprocessorFatalMessage("SDF Pattern \"%s\" starts with curly " "brackets which have nothing to modify.\n", *pii); } /* Check for various error cases. Total up the # of bytes needed in new pattern */ while (bracket_index) { /* Ignore escaped brackets */ if ((bracket_index > *pii) && (*(bracket_index-1) == '\\')) { bracket_index = strchr(bracket_index+1, '{'); continue; } /* Check for the case of one bracket set modifying another, i.e. "{3}{4}" Note: "\}{4}" is OK */ if ((bracket_index > (*pii)+1) && (*(bracket_index-1) == '}') && (*(bracket_index-2) != '\\') ) { DynamicPreprocessorFatalMessage("SDF Pattern \"%s\" contains curly " "brackets which have nothing to modify.\n", *pii); } /* Get the number from inside the brackets */ repetitions = strtoul(bracket_index+1, &endptr, 10); if (*endptr != '}' && *endptr != '\0') { DynamicPreprocessorFatalMessage("SDF Pattern \"%s\" contains curly " "brackets with non-digits inside.\n", *pii); } else if (*endptr == '\0') { DynamicPreprocessorFatalMessage("SDF Pattern \"%s\" contains " "an unterminated curly bracket.\n", *pii); } /* The brackets look OK. Increase the rep count. */ if ((bracket_index > (*pii)+1) && (*(bracket_index-2) == '\\')) total_reps += (repetitions * 2); else total_reps += repetitions; num_brackets++; /* Next bracket */ bracket_index = strchr(bracket_index+1, '{'); } /* By this point, the brackets all match up. */ if (num_brackets == 0) return; /* Allocate the new pii string. */ new_pii_size = (strlen(*pii) + total_reps - 2*num_brackets + 1); new_pii = (char *) calloc(new_pii_size, sizeof(char)); if (new_pii == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory for " "SDF preprocessor.\n"); } /* Copy the PII string, expanding repeated sections. */ pii_position = *pii; while (*pii_position != '\0') { char repeated_section[3] = {'\0'}; unsigned long int i, reps = 1; repeated_section[0] = pii_position[0]; pii_position++; if (repeated_section[0] == '\\' && pii_position[0] != '\0') { repeated_section[1] = pii_position[0]; pii_position++; } if (pii_position[0] == '{') { reps = strtoul(pii_position+1, &endptr, 10); pii_position = endptr+1; } /* Channeling "Shlemiel the Painter" here. */ for (i = 0; i < reps; i++) { strncat(new_pii, repeated_section, 2); } } /* Switch out the pii strings. */ free(*pii); *pii = new_pii; } /* Perform any modifications needed to a pattern string, then add it to the tree. */ int AddPii(sdf_tree_node *head, SDFOptionData *data) { if (head == NULL || data == NULL) return -1; ExpandBrackets(&(data->pii)); return AddPiiPattern(head, data); } /* Recursive pattern-adding function. * Return values: * -1: error * 0: pattern did not go in this subtree * 1: pattern was added in this subtree */ int AddPiiPiece(sdf_tree_node *node, char *new_pattern, SDFOptionData *data) { /* Potential cases: 1) node->pattern and new_pattern overlap by some number of bytes, but both end differently. Split the current node then add a second child. 2) node->pattern is a substring of new_pattern. Preserve current node, go on to children. If no children exist, add one and stop. 3) new_pattern is a substring of node->pattern. Split the current node, AND add an end-of-pattern marker. 4) Pattern doesn't fit here at all. Return 0 to caller. */ char *node_pattern_copy; uint16_t overlapping_bytes = 0; if (node == NULL || new_pattern == NULL || *new_pattern == '\0') return -1; /* Count the overlapping bytes between a) our current node's pattern b) the piece of the PII pattern being added here Additionally, we advance the pattern ptr to the non-matching part, so that only the non-matching part is added to a child node. */ node_pattern_copy = node->pattern; while(*node_pattern_copy != '\0' && *new_pattern != '\0' && *node_pattern_copy == *new_pattern) { /* Handle escape sequences: either the whole thing matches, or not at all */ if (*new_pattern == '\\') { if (*(new_pattern+1) != *(node_pattern_copy+1)) break; /* Don't increment twice if the strings just ended in '\' */ if (*(new_pattern+1) != '\0') { new_pattern++; node_pattern_copy++; overlapping_bytes++; } } new_pattern++; node_pattern_copy++; overlapping_bytes++; } if (*node_pattern_copy == '\0' && *new_pattern == '\0') { /* Patterns completely match */ uint16_t i; int data_added = 0; /* Replace old option_data if the sid & gid match. The OTN has already been freed out from under us. */ for (i = 0; i < node->num_option_data; i++) { if ((node->option_data_list[i]->sid == data->sid) && (node->option_data_list[i]->gid == data->gid)) { free(node->option_data_list[i]->pii); free(node->option_data_list[i]); node->option_data_list[i] = data; data_added = 1; } } /* Otherwise, append the new option_data to the list. */ if (!data_added) { SDFOptionData **tmp_realloc_ptr = NULL; tmp_realloc_ptr = (SDFOptionData **) realloc((void *)node->option_data_list, (node->num_option_data + 1) * sizeof(SDFOptionData *)); if (tmp_realloc_ptr == NULL) DynamicPreprocessorFatalMessage("%s(%d) Could not reallocate " "option_data_list\n", __FILE__, __LINE__); node->option_data_list = tmp_realloc_ptr; node->option_data_list[node->num_option_data] = data; node->num_option_data++; } return 1; } else if (*node_pattern_copy == '\0') { int i; /* Current node holds a subset of the pattern. Recurse to the children. */ for(i = 0; i < node->num_children; i++) { if (AddPiiPiece(node->children[i], new_pattern, data) == 1) return 1; } /* No children matched, or no children existed. Add the child here. */ AddChild(node, data, new_pattern); return 1; } else if (*new_pattern == '\0') { /* pattern is a subset of the current node's pattern */ SplitNode(node, overlapping_bytes); node->num_option_data = 1; node->option_data_list = (SDFOptionData **) calloc(1, sizeof(SDFOptionData *)); if (node->option_data_list == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate option_data_list\n", __FILE__, __LINE__); } node->option_data_list[0] = data; return 1; } else if (overlapping_bytes > 0) { /* Add the child node */ SplitNode(node, overlapping_bytes); AddChild(node, data, new_pattern); return 1; } /* These patterns don't overlap at all! */ return 0; } int SplitNode(sdf_tree_node *node, uint16_t split_index) { sdf_tree_node *new_node = NULL; if (node == NULL) return -1; if (split_index > strlen(node->pattern)) return -1; /* Create new node for second half of split */ new_node = (sdf_tree_node *) calloc(1,sizeof(sdf_tree_node)); if (new_node == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate new_node\n", __FILE__, __LINE__); } /* Fill in the new node with the child pointers, pattern, pii ptr */ new_node->pattern = strdup(node->pattern + split_index); if (new_node->pattern == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate new_node pattern\n", __FILE__, __LINE__); } new_node->children = node->children; new_node->option_data_list = node->option_data_list; new_node->num_children = node->num_children; new_node->num_option_data = node->num_option_data; /* Truncate the pattern of the current node, set child to new node */ node->children = (sdf_tree_node **) calloc(1,sizeof(sdf_tree_node *)); if (node->children == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate node children\n", __FILE__, __LINE__); } node->children[0] = new_node; node->num_children = 1; node->option_data_list = NULL; node->num_option_data = 0; node->pattern[split_index] = '\0'; return 0; } /* Create a new tree node, and add it as a child to the current node. */ sdf_tree_node * AddChild(sdf_tree_node *node, SDFOptionData *data, char *pattern) { sdf_tree_node *new_node = NULL; /* Take care not to step on the other children */ if (node->num_children) { sdf_tree_node **new_child_ptrs = (sdf_tree_node **) calloc(node->num_children+1, sizeof(sdf_tree_node *)); if (new_child_ptrs == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate new child pointers\n", __FILE__, __LINE__); } memcpy(new_child_ptrs, node->children, (node->num_children * sizeof(sdf_tree_node *))); new_node = (sdf_tree_node *) calloc(1,sizeof(sdf_tree_node)); if (new_node == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate new node\n", __FILE__, __LINE__); } new_child_ptrs[node->num_children] = new_node; free(node->children); node->children = new_child_ptrs; node->num_children++; } else { node->children = (sdf_tree_node **)calloc(1,sizeof(sdf_tree_node *)); if (node->children == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate node children\n", __FILE__, __LINE__); } node->children[0] = (sdf_tree_node *)calloc(1,sizeof(sdf_tree_node)); if (node->children[0] == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate node children[0]\n", __FILE__, __LINE__); } node->num_children = 1; new_node = node->children[0]; } new_node->pattern = strdup(pattern); if (new_node->pattern == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate node pattern\n", __FILE__, __LINE__); } new_node->num_option_data = 1; new_node->option_data_list = (SDFOptionData **) calloc(1, sizeof(SDFOptionData *)); if (new_node->option_data_list == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Could not allocate node list\n", __FILE__, __LINE__); } new_node->option_data_list[0] = data; return new_node; } /* Frees an entire PII tree. */ int FreePiiTree(sdf_tree_node *node) { uint16_t i; if (node == NULL) return -1; for (i = 0; i < node->num_children; i++) { FreePiiTree(node->children[i]); } free(node->pattern); free(node->children); for (i = 0; i < node->num_option_data; i++) { free(node->option_data_list[i]->pii); free(node->option_data_list[i]); } free(node->option_data_list); free(node); return 0; } /* Returns an sdf_tree_node that matches the pattern */ sdf_tree_node * FindPiiRecursively(sdf_tree_node *node, char *buf, uint16_t *buf_index, uint16_t buflen, SDFConfig *config, uint16_t *partial_index, sdf_tree_node **partial_node) { uint16_t old_buf_index; uint16_t pattern_index = *partial_index; int node_match = 1; *partial_index = 0; *partial_node = NULL; if (node == NULL || buf == NULL || buflen == 0 || *buf_index >= buflen) return NULL; /* Save the value of buf_index that was passed in. We revert to this value if a pattern is not matched here. Ultimately, it should hold the number of bytes matched against a pattern. */ old_buf_index = *buf_index; /* Match pattern buf against current node. Evaluate escape sequences. NOTE: node->pattern is a NULL-terminated string, but buf is network data and may legitimately contain NULL bytes. */ while (*buf_index < buflen && *(node->pattern + pattern_index) != '\0' && node_match ) { /* Match a byte at a time. */ if ( *(node->pattern + pattern_index) == '\\' && *(node->pattern + pattern_index + 1) != '\0' ) { /* Escape sequence found */ pattern_index++; switch ( *(node->pattern + pattern_index) ) { /* Escaped special character */ case '\\': case '{': case '}': case '?': node_match = (*(buf + *buf_index) == *(node->pattern + pattern_index)); break; /* \d : match digit */ case 'd': node_match = isdigit( (int)(*(buf + *buf_index)) ); break; /* \D : match non-digit */ case 'D': node_match = !isdigit( (int)(*(buf + *buf_index)) ); break; /* \w : match alphanumeric */ case 'w': node_match = isalnum( (int)(*(buf + *buf_index)) ); break; /* \W : match non-alphanumeric */ case 'W': node_match = !isalnum( (int)(*(buf + *buf_index)) ); break; /* \l : match a letter */ case 'l': node_match = isalpha( (int)(*(buf + *buf_index)) ); break; /* \L : match a non-letter */ case 'L': node_match = !isalpha( (int)(*(buf + *buf_index)) ); break; } } else { /* Normal byte */ node_match = (*(buf + *buf_index) == *(node->pattern + pattern_index)); } /* Handle optional characters */ if (*(node->pattern + pattern_index + 1) == '?') { /* Advance past the '?' in the pattern string. Only advance in the buffer if we matched the optional char. */ pattern_index += 2; if (node_match) (*buf_index)++; else node_match = 1; } else { /* Advance to next byte */ (*buf_index)++; pattern_index++; } } if (node_match) { int i = 0; uint16_t j; bool node_contains_matches = false; sdf_tree_node *matched_node = NULL; if(*buf_index == buflen) { if( (*(node->pattern + pattern_index) != '\0') || ((strlen(node->pattern) == pattern_index) && node->num_children)) { if( (pattern_index < strlen(node->pattern) ) && ( (*(node->pattern + pattern_index) == '\\' ) && (*(node->pattern + pattern_index+1) == 'D') ) ) { /* Do nothing here, as we found PII which is not ending with '\D'. * This is not a partial match, we found the full PII, so do not * treat this as partial match. */ } else { *partial_index = pattern_index; *partial_node = node; return NULL; } } } /* Check the children first. Always err on the side of a larger match. */ while (i < node->num_children && (matched_node == NULL) && !(*partial_index) ) { matched_node = FindPiiRecursively(node->children[i], buf, buf_index, buflen, config, partial_index, partial_node); i++; } if ((matched_node != NULL) || *partial_index) return matched_node; /* An sdf_tree_node holds multiple SDFOptionData. It's possible to get some with validation funs and some without. Evaluate them independently. */ for (j = 0; j < node->num_option_data; j++) { SDFOptionData *option_data = node->option_data_list[j]; /* Run eval func, return NULL if it exists but fails */ if (option_data->validate_func != NULL && option_data->validate_func(buf, *buf_index, config) != 1) { *buf_index = old_buf_index; option_data->match_success = 0; } else { /* No eval func necessary, or an eval func existed and returned 1 */ option_data->match_success = 1; node_contains_matches = true; } } if (node_contains_matches) return node; } /* No match here. */ *buf_index = old_buf_index; return NULL; } /* This function takes a head node, and searches the children for PII. * * head - Pointer to head node of SDF patttern tree. This contains no pattern. * buf - Buffer to search for patterns * buf_index - Pointer to store number of bytes that matched a pattern. * buflen - Length of buffer pointed to by buf * config - SDF preprocessor configuration. * * returns: sdf_tree_node ptr for matched pattern, or NULL if no match. */ sdf_tree_node * FindPii(const sdf_tree_node *head, char *buf, uint16_t *buf_index, uint16_t buflen, SDFConfig *config, SDFSessionData *session) { uint16_t i; uint16_t *partial_index = &(session->part_match_index); sdf_tree_node **partial_node = &(session->part_match_node); *partial_index = 0; if (head == NULL) return NULL; for (i = 0; i < head->num_children; i++) { sdf_tree_node * matched_node; matched_node = FindPiiRecursively(head->children[i], buf, buf_index, buflen, config, partial_index, partial_node); if (matched_node || *partial_index) return matched_node; } return NULL; } snort-2.9.20/src/dynamic-preprocessors/sdf/sdf_detection_option.c0000644000175000017500000002265514241076263023354 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_sdf.h" #include "sdf_pattern_match.h" #include "sdf_detection_option.h" #include "sf_snort_plugin_api.h" #include "sdf_us_ssn.h" #include "sdf_credit_card.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "treenodes.h" #ifdef SNORT_RELOAD extern sdf_tree_node *swap_head_node; extern uint32_t swap_num_patterns; #endif void AddPortsToConf(struct _SnortConfig *sc, SDFConfig *config, OptTreeNode *otn); void AddProtocolsToConf(struct _SnortConfig *sc, SDFConfig *config, OptTreeNode *otn); /* Function: SDFOptionInit * Purpose: Parses a SDF rule option. * Arguments: * name => Name of rule option * args => Arguments to rule option * data => Variable to save option data * Returns: 1 if successful * 0 if name is incorrect * Fatal Error if invalid arguments */ int SDFOptionInit(struct _SnortConfig *sc, char *name, char *args, void **data) { char *token, *endptr; unsigned long int tmpcount; SDFOptionData *sdf_data; if (name == NULL || args == NULL || data == NULL) return 0; if (strcasecmp(name, SDF_OPTION_NAME) != 0) return 0; sdf_data = (SDFOptionData *)calloc(1, sizeof(SDFOptionData)); if (sdf_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "SDF pattern data structure.", __FILE__, __LINE__); } /* Parse the count */ if (*args == '-') { free(sdf_data); DynamicPreprocessorFatalMessage("SDF rule cannot have a negative count:" " %s\n", args); } tmpcount = _dpd.SnortStrtoul(args, &endptr, 10); if (*endptr != ',') { free(sdf_data); DynamicPreprocessorFatalMessage("SDF rule configured with invalid " "arguments: %s\n", args); } if (tmpcount == 0 || tmpcount > 255) { free(sdf_data); DynamicPreprocessorFatalMessage("SDF rule needs to have a count between " " 1 - 255: %s\n", args); } sdf_data->count = (uint8_t)tmpcount; /* Take everything after the comma as a pattern. */ token = endptr + 1; if (*token == '\0') { free(sdf_data); DynamicPreprocessorFatalMessage("SDF rule missing pattern: %s ", args); } if (strcasecmp(token, SDF_CREDIT_KEYWORD) == 0) { sdf_data->pii = strdup(SDF_CREDIT_PATTERN_ALL); sdf_data->validate_func = SDFLuhnAlgorithm; } else if (strcasecmp(token, SDF_SOCIAL_KEYWORD) == 0) { sdf_data->pii = strdup(SDF_SOCIAL_PATTERN); sdf_data->validate_func = SDFSocialCheck; } else if (strcasecmp(token, SDF_SOCIAL_NODASHES_KEYWORD) == 0) { sdf_data->pii = strdup(SDF_SOCIAL_NODASHES_PATTERN); sdf_data->validate_func = SDFSocialCheck; } else if (strcasecmp(token, SDF_EMAIL_KEYWORD) == 0) { sdf_data->pii = strdup(SDF_EMAIL_PATTERN); } else { sdf_data->pii = strdup(token); sdf_data->validate_func = NULL; } if (!sdf_data->pii) { free(sdf_data); DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "SDF pattern data.", __FILE__, __LINE__); } *data = (void *)sdf_data; return 1; } /* This function receives the OTN of a fully-parsed rule, checks that it is a SDF rule, then adds pattern & OTN to the SDF pattern-matching tree. */ int SDFOtnHandler(struct _SnortConfig *sc, void *potn) { OptTreeNode *otn = (OptTreeNode *)potn; SDFConfig *config; tSfPolicyId policy_id; SDFOptionData *sdf_data; OptFpList *tmp = otn->opt_func; PreprocessorOptionInfo *preproc_info = NULL; tSfPolicyUserContextId context_to_use = sdf_context->context_id; sdf_tree_node *head_node_to_use = sdf_context->head_node; uint32_t *num_patterns_to_use = &sdf_context->num_patterns; int sdf_option_added = 0; #ifdef SNORT_RELOAD /* If we are reloading, use that context instead. This should work since preprocessors get configured before rule parsing */ SDFContext *sdf_swap_context; sdf_swap_context = (SDFContext *)_dpd.getRelatedReloadData(sc, "sensitive_data"); if (sdf_swap_context != NULL) { context_to_use = sdf_swap_context->context_id; head_node_to_use = sdf_swap_context->head_node; num_patterns_to_use = &sdf_swap_context->num_patterns; } #endif /* Retrieve the current policy being parsed */ policy_id = _dpd.getParserPolicy(sc); sfPolicyUserPolicySet(context_to_use, policy_id); config = (SDFConfig *) sfPolicyUserDataGetCurrent(context_to_use); /* Check that this is a SDF rule, then grab the context data. */ while (tmp != NULL && tmp->type != RULE_OPTION_TYPE_LEAF_NODE) { if (tmp->type == RULE_OPTION_TYPE_PREPROCESSOR) preproc_info = tmp->context; if (preproc_info == NULL || preproc_info->optionEval != (PreprocOptionEval) SDFOptionEval) { DynamicPreprocessorFatalMessage("%s(%d) Rules with SDF options cannot " "have other detection options in the same rule.\n", *_dpd.config_file, *_dpd.config_line); } if (sdf_option_added) { DynamicPreprocessorFatalMessage("A rule may contain only one " "\"%s\" option.\n", SDF_OPTION_NAME); } if (otn->sigInfo.generator != GENERATOR_SPP_SDF_RULES) { DynamicPreprocessorFatalMessage("Rules with SDF options must " "use GID %d.\n", GENERATOR_SPP_SDF_RULES); } sdf_data = (SDFOptionData *)preproc_info->data; sdf_data->otn = otn; sdf_data->sid = otn->sigInfo.id; sdf_data->gid = otn->sigInfo.generator; /* Add the pattern to the SDF pattern-matching tree */ AddPii(head_node_to_use, sdf_data); sdf_data->counter_index = (*num_patterns_to_use)++; AddPortsToConf(sc, config, otn); AddProtocolsToConf(sc, config, otn); sdf_option_added = 1; preproc_info = NULL; tmp = tmp->next; } return 1; } /* Take a port object's ports and add them to the preprocessor's port array. */ void AddPortsToConf(struct _SnortConfig *sc, SDFConfig *config, OptTreeNode *otn) { int i, nports; char *src_parray, *dst_parray; RuleTreeNode *rtn; if (config == NULL || otn == NULL) return; /* RTNs vary based on which policy the rule appears in. */ rtn = otn->proto_nodes[_dpd.getParserPolicy(sc)]; /* Take the source port object and add ports to the preproc's array */ src_parray = _dpd.portObjectCharPortArray(NULL, rtn->src_portobject, &nports); if (src_parray == 0) { /* This is an "any" port object! */ for (i = 0; i < MAX_PORTS/8; i++) { config->src_ports[i] = 0xFF; } } else { /* iterate through an array of ports, add each one. */ for (i = 0; i < MAX_PORTS; i++) { if (src_parray[i] == 1) config->src_ports[PORT_INDEX(i)] |= CONV_PORT(i); } } /* Repeat for destination ports. */ dst_parray = _dpd.portObjectCharPortArray(NULL, rtn->dst_portobject, &nports); if (dst_parray == 0) { /* This is an "any" port object! */ for (i = 0; i < MAX_PORTS/8; i++) { config->dst_ports[i] = 0xFF; } } else { /* iterate through an array of ports, add each one. */ for (i = 0; i < MAX_PORTS; i++) { if (dst_parray[i] == 1) config->dst_ports[PORT_INDEX(i)] |= CONV_PORT(i); } } /* Cleanup */ if (src_parray) free(src_parray); if (dst_parray) free(dst_parray); } void AddProtocolsToConf(struct _SnortConfig *sc, SDFConfig *config, OptTreeNode *otn) { #ifdef TARGET_BASED unsigned int i; int16_t ordinal; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); if (config == NULL || otn == NULL) return; for (i = 0; i < otn->sigInfo.num_services; i++) { ordinal = otn->sigInfo.services[i].service_ordinal; if (ordinal > 0 && ordinal < MAX_PROTOCOL_ORDINAL) config->protocol_ordinals[ordinal] = 1; _dpd.streamAPI->set_service_filter_status( sc, ordinal, PORT_MONITOR_SESSION, policy_id, 1); } #endif } /* Stub function -- We're not evaluating SDF during rule-matching */ int SDFOptionEval(void *p, const uint8_t **cursor, void *data) { return RULE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/sdf/sdf_credit_card.h0000644000175000017500000000237014241076255022247 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef SDF_CREDIT_CARD__H #define SDF_CREDIT_CARD__H #include #include "spp_sdf.h" #define ISSUER_SIZE 4 #define CC_COPY_BUF_LEN 20 /* 16 digits + 3 spaces/dashes + null */ #define MIN_CC_BUF_LEN 15 /* 13 digits + 2 surrounding non-digits */ int SDFLuhnAlgorithm(char *buf, uint32_t buflen, struct _SDFConfig *config); #endif /* SDF_CREDIT_CARD__H */ snort-2.9.20/src/dynamic-preprocessors/sdf/Makefile.in0000644000175000017500000005244414242725547021061 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/dynamic-preprocessors/sdf ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_sdf_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am_libsf_sdf_preproc_la_OBJECTS = spp_sdf.lo sdf_pattern_match.lo \ sdf_credit_card.lo sdf_us_ssn.lo sdf_detection_option.lo @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_sdf_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_sdf_preproc_la_OBJECTS = $(am_libsf_sdf_preproc_la_OBJECTS) \ $(nodist_libsf_sdf_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_sdf_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_sdf_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_sdf_preproc_la_SOURCES) \ $(nodist_libsf_sdf_preproc_la_SOURCES) DIST_SOURCES = $(libsf_sdf_preproc_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_sdf_preproc.la libsf_sdf_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_sdf_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_sdf_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c libsf_sdf_preproc_la_SOURCES = \ spp_sdf.c \ spp_sdf.h \ sdf_pattern_match.c \ sdf_pattern_match.h \ sdf_credit_card.c \ sdf_credit_card.h \ sdf_us_ssn.c \ sdf_us_ssn.h \ sdf_detection_option.c \ sdf_detection_option.h EXTRA_DIST = \ sf_sdf.vcxproj \ sf_sdf.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/sdf/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/sdf/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_sdf_preproc.la: $(libsf_sdf_preproc_la_OBJECTS) $(libsf_sdf_preproc_la_DEPENDENCIES) $(EXTRA_libsf_sdf_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_sdf_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_sdf_preproc_la_OBJECTS) $(libsf_sdf_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/sdf/sdf_us_ssn.h0000644000175000017500000000231414241076274021315 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef SDF_US_SSN__H #define SDF_US_SSN__H struct _SDFConfig; /* Forward declaration of SDFConfig */ int SDFSocialCheck(char *buf, uint32_t buflen, struct _SDFConfig *config); int ParseSSNGroups(char *filename, struct _SDFConfig *config); int SSNSetDefaultGroups(struct _SDFConfig *config); #endif /* SDF_US_SSN__H */ snort-2.9.20/src/dynamic-preprocessors/sdf/sdf_detection_option.h0000644000175000017500000000317214241076265023354 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef SDF_DETECTION_OPTION__H #define SDF_DETECTION_OPTION__H #include #include "treenodes.h" #include "sf_dynamic_engine.h" #include "spp_sdf.h" int SDFOptionInit(struct _SnortConfig *sc, char *name, char *args, void **data); int SDFOptionEval(void *p, const uint8_t **cursor, void *data); int SDFOtnHandler(struct _SnortConfig *sc, void *potn); /* Struct for SDF option data */ typedef struct _SDFOptionData { char *pii; uint32_t counter_index; OptTreeNode *otn; int (*validate_func)(char *buf, uint32_t buflen, struct _SDFConfig *config); uint8_t count; uint8_t match_success; /* These are kept separately in case the OTN reference is freed */ uint32_t sid; uint32_t gid; } SDFOptionData; #endif snort-2.9.20/src/dynamic-preprocessors/sdf/sf_sdf.dsp0000444000175000017500000001274314230012554020744 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_sdf" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_sdf - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_sdf.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_sdf.mak" CFG="sf_sdf - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_sdf - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_sdf - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_sdf - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_sdf - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_sdf - Win32 Release" # Name "sf_sdf - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\spp_sdf.c # End Source File # Begin Source File SOURCE=..\include\strtok_r.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\sdf_credit_card.c # End Source File # Begin Source File SOURCE=.\sdf_credit_card.h # End Source File # Begin Source File SOURCE=.\sdf_detection_option.c # End Source File # Begin Source File SOURCE=.\sdf_detection_option.h # End Source File # Begin Source File SOURCE=.\sdf_pattern_match.c # End Source File # Begin Source File SOURCE=.\sdf_pattern_match.h # End Source File # Begin Source File SOURCE=.\sdf_us_ssn.c # End Source File # Begin Source File SOURCE=.\sdf_us_ssn.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\spp_sdf.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/sdf/spp_sdf.c0000644000175000017500000010173514241076276020611 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include "sf_types.h" /* #include "snort.h" #include "parser.h" #include "util.h" #include "plugbase.h" */ #include "snort_debug.h" #include "stream_api.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "sf_snort_packet.h" /* #ifdef TARGET_BASED #include "sftarget_protocol_reference.h" #endif */ #include "profiler.h" #include "spp_sdf.h" #include "sf_preproc_info.h" #include "sdf_us_ssn.h" #include "sdf_detection_option.h" #include "sdf_pattern_match.h" const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_SDF"; #define SetupSDF DYNAMIC_PREPROC_SETUP /* PROTOTYPES */ static void SDFInit(struct _SnortConfig *, char *args); static void ProcessSDF(void *p, void *context); static SDFConfig * NewSDFConfig(struct _SnortConfig *, tSfPolicyUserContextId); static void ParseSDFArgs(SDFConfig *config, char *args); static void SDFCleanExit(int signal, void *unused); static int SDFFreeConfig(tSfPolicyUserContextId context, tSfPolicyId id, void *pData); static void SDFFillPacket(sdf_tree_node *node, SDFSessionData *session, SFSnortPacket *p, uint16_t *dlen); static void SDFPrintPseudoPacket(SDFConfig *config, SDFSessionData *session, SFSnortPacket *real_packet); #ifdef SNORT_RELOAD static void SDFReload(struct _SnortConfig *, char *, void **); static void * SDFReloadSwap(struct _SnortConfig *, void *); static void SDFReloadSwapFree(void *); #endif /* GLOBALS :( */ SDFContext *sdf_context = NULL; static uint32_t sdf_config_count = 0; #ifdef SNORT_RELOAD sdf_tree_node *swap_head_node = NULL; uint32_t swap_num_patterns = 0; #endif #ifdef PERF_PROFILING PreprocStats sdfPerfStats; #endif #define IPPROTO_SDF 0xFE // TBD - use same for ps? (eg IPPROTO_SNORT?) /* * Function: SetupSDF() * * Purpose: Registers the preprocessor keyword and initialization function * into the preprocessor list. * * Arguments: None. * * Returns: void * */ void SetupSDF(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc("sensitive_data", SDFInit); #else _dpd.registerPreproc("sensitive_data", SDFInit, SDFReload, NULL, SDFReloadSwap, SDFReloadSwapFree); #endif } /* * Function: SDFInit(char *) * * Purpose: Processes the args sent to the preprocessor, sets up the port list, * links the processing function into the preproc function list * * Arguments: args => ptr to argument string * * Returns: void * */ void SDFInit(struct _SnortConfig *sc, char *args) { SDFConfig *config = NULL; /* Check prerequisites */ if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SDFInit(): The Stream preprocessor must be enabled.\n"); } /* Create context id, register callbacks. This is only done once. */ if (sdf_context == NULL) { sdf_context = (SDFContext *)calloc(1, sizeof(*sdf_context)); if (!sdf_context) DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF " "configuration.\n"); sdf_context->context_id = sfPolicyConfigCreate(); if (!sdf_context->context_id) DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF " "configuration.\n"); sdf_context->head_node = (sdf_tree_node *)calloc(1, sizeof(*sdf_context->head_node)); if (!sdf_context->head_node) DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF " "configuration.\n"); _dpd.addPreprocExit(SDFCleanExit, NULL, PRIORITY_LAST, PP_SDF); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("sensitive_data", (void *)&sdfPerfStats, 0, _dpd.totalPerfStats, NULL); #endif } /* Handle configuration. This is done once for each policy. */ config = NewSDFConfig(sc, sdf_context->context_id); ParseSDFArgs(config, args); /* Register callbacks */ _dpd.addDetect(sc, ProcessSDF, PRIORITY_FIRST, PP_SDF, PROTO_BIT__TCP | PROTO_BIT__UDP); _dpd.preprocOptRegister(sc, SDF_OPTION_NAME, SDFOptionInit, SDFOptionEval, NULL, NULL, NULL, SDFOtnHandler, NULL); } /* Check the ports and target-based protocol for a given packet. * * Returns: 0 if the port check fails * 1 if the packet should be inspected */ static int SDFCheckPorts(SDFConfig *config, SFSnortPacket *packet) { #ifdef TARGET_BASED int16_t app_ordinal = SFTARGET_UNKNOWN_PROTOCOL; /* Do port checks */ app_ordinal = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session); if (app_ordinal == SFTARGET_UNKNOWN_PROTOCOL) return 0; if (app_ordinal && (config->protocol_ordinals[app_ordinal] == 0)) return 0; if (app_ordinal == 0) { #endif /* No target-based info for this packet. Check ports. */ if (((config->src_ports[PORT_INDEX(packet->src_port)] & CONV_PORT(packet->src_port)) == 0) || ((config->dst_ports[PORT_INDEX(packet->dst_port)] & CONV_PORT(packet->dst_port)) == 0)) { return 0; } #ifdef TARGET_BASED } #endif return 1; } /* A free function that gets registered along with our Stream session data */ static void FreeSDFSession(void *data) { SDFSessionData *session = (SDFSessionData *)data; if (session == NULL) return; free(session->counters); free(session->rtns_matched); free(session); return; } /* Create a new SDF session data struct. Returns: Fatal Error if allocation fails Valid ptr otherwise */ static SDFSessionData * NewSDFSession(SDFConfig *config, SFSnortPacket *packet) { SDFSessionData *session; /* Allocate new session data. */ session = (SDFSessionData *) malloc(sizeof(SDFSessionData)); if (session == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory for " "SDF preprocessor session data.\n"); } if (packet->stream_session) { _dpd.sessionAPI->set_application_data(packet->stream_session, PP_SDF, session, FreeSDFSession); } session->part_match_node= NULL; session->part_match_index = 0; session->global_counter = 0; session->config_num = config->config_num; session->last_pkt_seq_num = 0; session->last_pkt_data_len = -1; /* Allocate counters in the session data */ session->num_patterns = sdf_context->num_patterns; session->counters = calloc(session->num_patterns, sizeof(uint8_t)); session->rtns_matched = calloc(session->num_patterns, sizeof(int8_t)); if (session->counters == NULL || session->rtns_matched == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory for " "SDF preprocessor session data.\n"); } return session; } static inline bool isBufferInPayload(char *begin, char *end, SFSnortPacket *packet) { if ((end <= (char *) packet->payload + packet->payload_size) && (begin >= (char *) packet->payload)) return true; else return false; } static void SDFSearchRecursively(SDFConfig *config, SFSnortPacket *packet, SDFSessionData *session, sdf_tree_node *matched_node, char **position, uint16_t *buflen, uint16_t match_length, bool *ob_failed) { /* Iterate through the SDFOptionData that matches this pattern. */ uint16_t i; for (i = 0; i < matched_node->num_option_data; i++) { SDFOptionData *found_pattern = matched_node->option_data_list[i]; if (found_pattern->match_success) { int index; /* Reset the match_success flag for subsequent matches */ found_pattern->match_success = 0; /* Check the RTN for the PII we found. The IPs & ports might not match. We only want to do this once per session */ index = found_pattern->counter_index; if (session->rtns_matched[index] == 0) { bool check_ports = true; OptTreeNode *otn = found_pattern->otn; RuleTreeNode *rtn = NULL; #ifdef TARGET_BASED int16_t app_ordinal; #endif if (_dpd.getIpsRuntimePolicy() < otn->proto_node_num) rtn = otn->proto_nodes[_dpd.getIpsRuntimePolicy()]; #ifdef TARGET_BASED /* Check the service against the matched OTN. */ app_ordinal = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session); if( app_ordinal != SFTARGET_UNKNOWN_PROTOCOL ) { unsigned int i; for (i = 0; i < otn->sigInfo.num_services; i++) { if (otn->sigInfo.services[i].service_ordinal == app_ordinal) { check_ports = false; break; } } } #endif if (rtn != NULL && _dpd.fpEvalRTN(rtn, packet, check_ports)) session->rtns_matched[index] = 1; else session->rtns_matched[index] = -1; } if (session->rtns_matched[index] == 1) { /* Increment counters */ session->counters[found_pattern->counter_index]++; /* Obfuscate the data. We do this even if it's not time to alert, to obfuscate each match. Only obfuscate built-in patterns */ if (config->mask_output && found_pattern->validate_func) { if (isBufferInPayload(*position, *position + match_length, packet)) { uint16_t offset, ob_length = 0; offset = (uint16_t) ((*position) - (char *)packet->payload); if (match_length > SDF_OBFUSCATION_DIGITS_SHOWN) ob_length = match_length - SDF_OBFUSCATION_DIGITS_SHOWN; /* Generally, the CC# and SS# patterns contain non-digits on either * side of the actual number. Sometimes, for the patterns from the * first line of the data might start with a digit, instead of a * non-digit. Adjust the mask to match. */ if (isdigit((int)*position[0])) ob_length = ob_length - 1; else { offset = offset + 1; ob_length = ob_length - 2; } _dpd.obApi->addObfuscationEntry(packet, offset, ob_length, SDF_OBFUSCATION_CHAR); } else { *ob_failed = true; } } if (session->counters[found_pattern->counter_index] == found_pattern->count) { /* Raise the alert for this particular pattern */ _dpd.alertAdd(GENERATOR_SPP_SDF_RULES, found_pattern->otn->sigInfo.id, found_pattern->otn->sigInfo.rev, found_pattern->otn->sigInfo.class_id, found_pattern->otn->sigInfo.priority, found_pattern->otn->sigInfo.message, 0); } } } } /* Check the global counter and alert */ session->global_counter++; if (session->global_counter == config->threshold) { /* Do our "combo alert" */ SDFPrintPseudoPacket(config, session, packet); _dpd.genSnortEvent(config->pseudo_packet, GENERATOR_SPP_SDF_PREPROC, SDF_COMBO_ALERT_SID, SDF_COMBO_ALERT_REV, SDF_COMBO_ALERT_CLASS, SDF_COMBO_ALERT_PRIORITY, SDF_COMBO_ALERT_STR); } /* Update position */ if (match_length > 1) { (*position) += match_length - 1; (*buflen) -= match_length - 1; } } /* Search a buffer for PII. Generates alerts when enough PII is found. Returns: void */ static void SDFSearch(SDFConfig *config, SFSnortPacket *packet, SDFSessionData *session, char *position, char *end, uint16_t buflen, bool *ob_failed) { uint16_t match_length = 0; sdf_tree_node *matched_node = NULL; uint16_t *partial_index = &(session->part_match_index); sdf_tree_node **partial_node = &(session->part_match_node); /* Check to see if there was a partial match */ if(*partial_index > 0) { if( position < end ) { sdf_tree_node *node = *partial_node; if(strlen(node->pattern) == *partial_index) { int i = 0; while ((i < node->num_children) && matched_node == NULL) { *partial_index = 0; matched_node = FindPiiRecursively(node->children[i], position, &match_length, buflen, config, partial_index, partial_node); i++; } } else { matched_node = FindPiiRecursively(node, position, &match_length, buflen, config, partial_index, partial_node); } /* only when matched update the position ptr. FindPiiRecursively only checks one node unlike FindPii */ if (matched_node) SDFSearchRecursively(config, packet, session, matched_node, &position, &buflen, match_length, ob_failed); else if (*partial_index) { position += match_length; buflen -= match_length; } } else { return; } } while (position < end) { match_length = 0; matched_node = NULL; /* Traverse the pattern tree and match PII against our data */ matched_node = FindPii(sdf_context->head_node, position, &match_length, buflen, config, session); if (matched_node) SDFSearchRecursively(config, packet, session, matched_node, &position, &buflen, match_length, ob_failed); else if (*partial_index) { position += match_length; buflen -= match_length; } else { position++; buflen--; } } } /* * Function: ProcessSDF(void *, void *) * * Purpose: Inspects a packet's payload for Personally Identifiable Information * * Arguments: p => poitner to the current packet data struct * context => unused void pointer * * Returns: void * */ static void ProcessSDF(void *p, void *context) { tSfPolicyId policy_id; SDFConfig *config = NULL; SFSnortPacket *packet = (SFSnortPacket *)p; SDFSessionData *session; char *begin, *end; uint16_t buflen; bool payload_checked = false; bool ob_failed = false; PROFILE_VARS; // preconditions - what we registered for assert((IsUDP(packet) || IsTCP(packet)) && packet->payload && packet->payload_size); /* Check if we should be working on this packet */ if ( packet->flags & FLAG_STREAM_INSERT && (!(packet->flags & FLAG_PDU_TAIL)) ) return; // Waiting on stream reassembly /* Retrieve the corresponding config for this packet */ policy_id = _dpd.getIpsRuntimePolicy(); sfPolicyUserPolicySet (sdf_context->context_id, policy_id); config = sfPolicyUserDataGetCurrent(sdf_context->context_id); /* Retrieve stream session data. Create one if it doesn't exist. */ session = _dpd.sessionAPI->get_application_data(packet->stream_session, PP_SDF); if (session == NULL) { char pseudo_start[1] = {'0'}; /* Do port checks */ if (SDFCheckPorts(config, packet) == 0) { return; } /* If there's no stream session, we'll just count PII for one packet */ if (packet->stream_session == NULL) { if (config->stateless_session == NULL) config->stateless_session = NewSDFSession(config, packet); session = config->stateless_session; memset(session->counters, 0, session->num_patterns); memset(session->rtns_matched, 0, session->num_patterns); } else session = NewSDFSession(config, packet); /* Add one byte to support sensitive data starts with first byte */ begin = pseudo_start; buflen = 1; end = begin + buflen; SDFSearch(config, packet, session, begin, end, buflen, &ob_failed); } else if( session->config_num != config->config_num ) { /* Config has changed. Don't use rule tree nodes from previous config */ session->part_match_index = 0; session->part_match_node = NULL; /* Update the session's config num */ session->config_num = config->config_num; } PREPROC_PROFILE_START(sdfPerfStats); /* By First checking ports and protocols for a given packet , Snort is able to limit the number of rules that must be evaluated. prmFindRuleGroup() will be called to make sure a match exists for the source and destination ports mentioned in the rule. Sometimes more than one rule group will be matched to the same packet. This is quick way to eliminate the rules without complex pattern matching. Sometimes same packet will be evaluated by Sensitive Data Preprocessor more than once because packet is matched to more than one rule group. When evaluating same packet for each rule group by SDF, PII count will be incremented for the same packet which causes mis-firing alert. Need to avoid evaluating same packet multiple times by SDF. */ if(packet->tcp_header) { if( (session->last_pkt_seq_num == packet->tcp_header->sequence) ) { if( (_dpd.fileDataBuf->len == 0 ) || (session->last_pkt_data_len != _dpd.fileDataBuf->len )) { // Process the same packet if file data buffer len is diffrent or zero. session->last_pkt_data_len = _dpd.fileDataBuf->len; } else { // Do not evaluate the same packet if file data buffer len is same. session->last_pkt_data_len = _dpd.fileDataBuf->len; return; } } else { session->last_pkt_seq_num = packet->tcp_header->sequence; session->last_pkt_data_len = _dpd.fileDataBuf->len; } } /* Inspect HTTP Body or Email attachments. */ if (_dpd.fileDataBuf->len > 0) { begin = (char *) _dpd.fileDataBuf->data; buflen = _dpd.fileDataBuf->len; end = begin + buflen; SDFSearch(config, packet, session, begin, end, buflen, &ob_failed); } else if ( PacketHasPAFPayload(packet) ) { /* SDF already requires stream to be enabled, might as well look * at the rebuilt packet */ begin = (char *)packet->payload; buflen = packet->payload_size; end = begin + buflen; SDFSearch(config, packet, session, begin, end, buflen, &ob_failed); payload_checked = true; } /* If this packet is HTTP, inspect the URI and Client Body while ignoring * headers. */ if (packet->flags & FLAG_HTTP_DECODE) { unsigned len; begin = (char*)_dpd.getHttpBuffer(HTTP_BUFFER_URI, &len); if ( begin ) { buflen = (uint16_t)len; end = begin + buflen; if (!payload_checked || !isBufferInPayload(begin, end, packet)) SDFSearch(config, packet, session, begin, end, buflen, &ob_failed); } begin = (char*)_dpd.getHttpBuffer(HTTP_BUFFER_CLIENT_BODY, &len); if ( begin ) { buflen = (uint16_t)len; end = begin + buflen; if (!payload_checked || !isBufferInPayload(begin, end, packet)) SDFSearch(config, packet, session, begin, end, buflen, &ob_failed); } } /* Found match but not in payload, recheck to mask the rebuilt packet */ if ( !payload_checked && ob_failed && PacketHasPAFPayload(packet) ) { begin = (char *)packet->payload; buflen = packet->payload_size; end = begin + buflen; SDFSearch(config, packet, session, begin, end, buflen, &ob_failed); payload_checked = true; } /* End. */ PREPROC_PROFILE_END(sdfPerfStats); return; } static void DisplaySDFConfig(SDFConfig *config) { if (config == NULL) return; _dpd.logMsg("Sensitive Data preprocessor config: \n"); _dpd.logMsg(" Global Alert Threshold: %d\n", config->threshold); _dpd.logMsg(" Masked Output: %s\n", config->mask_output ? "ENABLED" : "DISABLED" ); } /* * Function: ParseSDFArgs(SDFConfig *, char *) * * Purpose: Parse the arguments to the SDF preprocessor and instantiate a * SDFConfig struct. * * Arguments: config => pointer to a newly-allocated SDFConfig struct, which * will be modified. * args => pointer to string containing SDF preproc arguments. * * Returns: void * */ static void ParseSDFArgs(SDFConfig *config, char *args) { char *argcpy = NULL; char *cur_tokenp = NULL; if (config == NULL || args == NULL) return; /* Set default options */ SSNSetDefaultGroups(config); /* Copy args so that we can break them up wtih strtok */ argcpy = strdup(args); if (argcpy == NULL) DynamicPreprocessorFatalMessage("Could not allocate memory to parse " "SDF options.\n"); cur_tokenp = strtok(argcpy, " "); /* Loop through config options */ while (cur_tokenp) { /* Parse the global PII threshold */ if (!strcmp(cur_tokenp, SDF_THRESHOLD_KEYWORD)) { char *endptr; cur_tokenp = strtok(NULL, " "); if (cur_tokenp == NULL) { DynamicPreprocessorFatalMessage("SDF preprocessor config option " "\"%s\" requires an argument.\n", SDF_THRESHOLD_KEYWORD); } if (*cur_tokenp == '-') { DynamicPreprocessorFatalMessage("SDF preprocessor config option " "\"%s\" cannot take a negative argument.\n", SDF_THRESHOLD_KEYWORD); } config->threshold = _dpd.SnortStrtoul(cur_tokenp, &endptr, 10); if (config->threshold == 0 || config->threshold > USHRT_MAX) { DynamicPreprocessorFatalMessage("SDF preprocessor config option " "\"%s\" must have an argument between 1 - %u.\n", SDF_THRESHOLD_KEYWORD, USHRT_MAX); } if (*endptr != '\0') { DynamicPreprocessorFatalMessage("Invalid argument to SDF config " "option \"%s\": %s", SDF_THRESHOLD_KEYWORD, cur_tokenp); } } /* Parse the output masking option */ else if (!strcmp(cur_tokenp, SDF_MASK_KEYWORD)) { config->mask_output = 1; } /* Parse the file containing new SSN group data */ else if (!strcmp(cur_tokenp, SDF_SSN_FILE_KEYWORD)) { int iRet; cur_tokenp = strtok(NULL, " "); if (cur_tokenp == NULL) { DynamicPreprocessorFatalMessage("SDF preprocessor config option " "\"%s\" requires an argument.\n", SDF_SSN_FILE_KEYWORD); } iRet = ParseSSNGroups(cur_tokenp, config); if (iRet < 0) { DynamicPreprocessorFatalMessage("Error parsing Social Security " "group data from file: %s", cur_tokenp); } } else { DynamicPreprocessorFatalMessage("%s(%d) => Unknown SDF configuration option %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); } cur_tokenp = strtok(NULL, " "); } /* Cleanup */ DisplaySDFConfig(config); free(argcpy); argcpy = NULL; } /* Allocate & Initialize the pseudo-packet used for logging combo alerts. * * Returns: 0 on success, -1 on error. */ static int SDFPacketInit(SDFConfig *config) { config->pseudo_packet = _dpd.encodeNew(); return 0; } /* * Function: NewSDFConfig(void) * * Purpose: Create a new SDFConfig for the current parser policy. * * Arguments: context => context ID to use when creating config * * Returns: Pointer to newly created SDFConfig struct. * */ static SDFConfig * NewSDFConfig(struct _SnortConfig *sc, tSfPolicyUserContextId context) { SDFConfig *config = NULL; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); /* Check for an existing configuration in this policy */ sfPolicyUserPolicySet(context, policy_id); config = (SDFConfig *) sfPolicyUserDataGetCurrent(context); if (config) DynamicPreprocessorFatalMessage("SDF preprocessor can only be " "configured once.\n"); /* Create and store config */ config = (SDFConfig *)calloc(1, sizeof(SDFConfig)); if (!config) DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF " "configuration.\n"); sfPolicyUserDataSetCurrent(context, config); /* Allocate the pseudo-packet used for logging */ SDFPacketInit(config); config->config_num = sdf_config_count++; return config; } /* * Function: SDFCleanExit(int, void *) * * Purpose: Free memory used by the SDF preprocessor before Snort exits. * * Arguments: Signal sent to Snort, unused void pointer * * Returns: void * */ static void SDFCleanExit(int signal, void *unused) { /* Free the individual configs. */ if (sdf_context == NULL) return; sfPolicyUserDataFreeIterate(sdf_context->context_id, SDFFreeConfig); sfPolicyConfigDelete(sdf_context->context_id); FreePiiTree(sdf_context->head_node); free(sdf_context); sdf_context = NULL; } /* * Function: SDFFreeConfig(tSfPolicyUserContextId, tSfPolicyId, void *) * * Purpose: Callback that frees a SDFConfig struct correctly, and clears data * from the policy. * * Arguments: context => context ID for the SDF preprocessor * id => policy ID for the policy being destroyed * pData => pointer to SDFConfig struct that gets freed * * Returns: zero * */ static int SDFFreeConfig(tSfPolicyUserContextId context, tSfPolicyId id, void *pData) { SDFConfig *config = (SDFConfig *)pData; sfPolicyUserDataClear(context, id); _dpd.encodeDelete(config->pseudo_packet); FreeSDFSession(config->stateless_session); free(config); return 0; } #ifdef SNORT_RELOAD static void SDFReload(struct _SnortConfig *sc, char *args, void **new_config) { SDFContext *sdf_swap_context = (SDFContext *)*new_config; SDFConfig *config = NULL; if (sdf_swap_context == NULL) { if (!_dpd.streamAPI) DynamicPreprocessorFatalMessage("SetupSDF(): The Stream preprocessor " "must be enabled.\n"); sdf_swap_context = (SDFContext *)calloc(1, sizeof(*sdf_context)); if (!sdf_swap_context) DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF " "configuration.\n"); sdf_swap_context->context_id = sfPolicyConfigCreate(); if (!sdf_swap_context->context_id) DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF " "configuration.\n"); sdf_swap_context->head_node = (sdf_tree_node *)calloc(1, sizeof(*sdf_swap_context->head_node)); if (!sdf_swap_context->head_node) DynamicPreprocessorFatalMessage("Failed to allocate memory for SDF " "configuration.\n"); *new_config = (void *)sdf_swap_context; } config = NewSDFConfig(sc, sdf_swap_context->context_id); ParseSDFArgs(config, args); _dpd.addDetect(sc, ProcessSDF, PRIORITY_FIRST, PP_SDF, PROTO_BIT__TCP | PROTO_BIT__UDP); _dpd.preprocOptRegister(sc, SDF_OPTION_NAME, SDFOptionInit, SDFOptionEval, NULL, NULL, NULL, SDFOtnHandler, NULL); } static void * SDFReloadSwap(struct _SnortConfig *sc, void *swap_config) { SDFContext *sdf_swap_context = (SDFContext *)swap_config; SDFContext *old_context = sdf_context; if (old_context == NULL || sdf_swap_context == NULL) return NULL; sdf_context = sdf_swap_context; return (void *) old_context; } static void SDFReloadSwapFree(void *data) { SDFContext *context = (SDFContext *) data; if (context == NULL) return; sfPolicyUserDataFreeIterate(context->context_id, SDFFreeConfig); sfPolicyConfigDelete(context->context_id); FreePiiTree(context->head_node); free(context); } #endif static void SDFPrintPseudoPacket(SDFConfig *config, SDFSessionData *session, SFSnortPacket *real_packet) { SFSnortPacket* p; if (config == NULL || session == NULL || real_packet == NULL) return; p = config->pseudo_packet; _dpd.encodeFormat(ENC_DYN_FWD|ENC_DYN_NET, real_packet, config->pseudo_packet, PSEUDO_PKT_SDF); if ( IS_IP4(real_packet) ) { ((IPV4Header *)p->ip4_header)->proto = IPPROTO_SDF; p->inner_ip4h.ip_proto = IPPROTO_SDF; } else if (IS_IP6(p)) { // FIXTHIS assumes there are no ip6 extension headers p->inner_ip6h.next = IPPROTO_SDF; p->ip6h = &p->inner_ip6h; } /* Fill in the payload with SDF alert info */ SDFFillPacket(sdf_context->head_node, session, p, &p->payload_size); _dpd.encodeUpdate(config->pseudo_packet); if (real_packet->family == AF_INET) { p->ip4h->ip_len = p->ip4_header->data_length; } else { IP6RawHdr* ip6h = (IP6RawHdr*)p->raw_ip6_header; if ( ip6h ) p->ip6h->len = ip6h->ip6_payload_len; } } /* This function traverses the pattern tree and prints out the relevant * info into a provided pseudo-packet. */ static void SDFFillPacket(sdf_tree_node *node, SDFSessionData *session, SFSnortPacket *p, uint16_t *dlen) { uint16_t i; if (node == NULL || session == NULL || p == NULL || dlen == NULL) return; /* Recurse to the leaves of the pattern tree */ for (i = 0; i < node->num_children; i++) { SDFFillPacket(node->children[i], session, p, dlen); } for (i = 0; i < node->num_option_data; i++) { SDFOptionData * option_data = node->option_data_list[i]; /* Print the info from leaves */ if (option_data) { uint32_t index = option_data->counter_index; uint8_t counter = session->counters[index]; if (counter > 0) { /* Print line */ const char *sigmessage = option_data->otn->sigInfo.message; uint8_t *dest = (uint8_t*)p->payload + *dlen; size_t siglen = strlen(sigmessage); uint16_t space_left = p->max_payload - *dlen; if (space_left < siglen + SDF_ALERT_LENGTH) return; *dlen += (siglen + SDF_ALERT_LENGTH); snprintf((char *)dest, space_left, "%s: %3d", sigmessage, counter); } } } return; } snort-2.9.20/src/dynamic-preprocessors/sdf/sf_sdf.vcxproj0000444000175000017500000004125414230012554021650 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {F0F86116-A76F-4A8F-B416-9BAA2DA4D16E} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_sdf.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_sdf.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_sdf.bsc true true Console .\Release\sf_sdf.dll .\Release\sf_sdf.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_sdf.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_sdf.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_sdf.bsc true true Console .\Release\sf_sdf.dll .\Release\sf_sdf.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_sdf.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_sdf.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_sdf.bsc true true true Console .\Debug\sf_sdf.dll .\Debug\sf_sdf.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_sdf.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_sdf.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_sdf.bsc true true true Console .\Debug\sf_sdf.dll .\Debug\sf_sdf.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/sdf/sdf_pattern_match.h0000644000175000017500000000332414241076271022633 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef SDF_PATTERN_MATCH__H #define SDF_PATTERN_MATCH__H #include "spp_sdf.h" #include "sdf_detection_option.h" #include "treenodes.h" #include int AddPii(sdf_tree_node *head, SDFOptionData *data); int AddPiiPiece(sdf_tree_node *node, char *new_pattern, SDFOptionData *data); int SplitNode(sdf_tree_node *node, uint16_t split_index); sdf_tree_node * AddChild(sdf_tree_node *node, SDFOptionData *data, char *pattern); int FreePiiTree(sdf_tree_node *head); sdf_tree_node * FindPii(const sdf_tree_node *head, char *buf, uint16_t *buf_index, uint16_t buflen, SDFConfig *config, SDFSessionData *session); sdf_tree_node * FindPiiRecursively(sdf_tree_node *node, char *buf, uint16_t *buf_index, uint16_t buflen, SDFConfig *config, uint16_t *partial_index, sdf_tree_node **partial_node); #endif /* SDF_PATTERN_MATCH__H */ snort-2.9.20/src/dynamic-preprocessors/sdf/sdf_credit_card.c0000644000175000017500000001066114241076254022243 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_sdf.h" #include "sdf_credit_card.h" #include #include #include /* Check the Issuer Identification Number of a CC#. */ static inline int CheckIssuers(char *cardnum, uint32_t buflen) { if (cardnum == NULL || buflen < ISSUER_SIZE) return 0; /* Visa */ if (cardnum[0] == '4') return 1; /* Mastercard */ if ((cardnum[0] == '5') && (cardnum[1] > '0') && (cardnum[1] < '6')) return 1; /* Amex */ if ((cardnum[0] == '3') && (cardnum[1] == '4' || cardnum[1] == '7')) return 1; /* Discover */ if (cardnum[0] == '6' && cardnum[1] == '0' && cardnum[2] == '1' && cardnum[3] == '1') return 1; return 0; } /* This function takes a string representation of a credit card number and * checks that it's a valid number. The number may contain spaces or dashes. * * Returns: 1 on match, 0 otherwise. */ int SDFLuhnAlgorithm(char *buf, uint32_t buflen, struct _SDFConfig *config) { int i, digits, alternate, sum, val; char cc_digits[CC_COPY_BUF_LEN]; /* Normalized CC# string */ uint32_t j; if (buf == NULL || buflen < MIN_CC_BUF_LEN) return 0; /* Generally, the buffer has two non-digits, one on either side. Sometimes, * when the buffer is pointing to the first line of the data, it might * start with a digit, instead of a non-digit. Sometimes, the buffer has only * one non-digit either starting or ending. Strip the non-digits only. */ /* Check the buffer is ending with non-digit or digit. If buffer is ending with digit do not decrement the buflen, * If buffer is ending with non-digit then decrement buflen as non-digit is not part of credit card. */ if(!isdigit((int)buf[buflen-1])) { buflen -= 1; } /* Check start of the buffer, if it is non-digit, decrement buflen by 1 and move pointer to next character/digit in buf. */ if(!isdigit((int)buf[0])) { buf++; buflen -= 1; } /* If the first digit is greater than 6, this isn't one of the major credit cards. */ if (!isdigit((int)buf[0]) || buf[0] > '6') return 0; /* Check the issuer number for Visa, Mastercard, Amex, or Discover. */ if (CheckIssuers(buf, buflen) == 0) return 0; /* Limit to 16 digits + spaces in between */ if (buflen >= CC_COPY_BUF_LEN) buflen = CC_COPY_BUF_LEN - 1; /* Copy the string into cc_digits, stripping out spaces & dashes. */ digits = 0; for (j = 0; j < buflen; j++) { if (isdigit((int)buf[j]) == 0) { if (buf[j] == ' ' || buf[j] == '-') continue; else break; } cc_digits[digits++] = buf[j]; } cc_digits[digits] = '\0'; /* Check if the string was too short, or we broke at an invalid character */ if (digits < 13 || digits > 16 || j < buflen) return 0; /* The Luhn algorithm: 1) Starting at the right-most digit, double every second digit. 2) Sum all the *individual* digits (i.e. 16 => 1+6) 3) If the Sum mod 10 == 0, the CC# is valid. */ alternate = 0; sum = 0; for (i = digits - 1; i >= 0; i--) { val = cc_digits[i] - '0'; if (alternate) { val *= 2; if (val > 9) val -= 9; } alternate = !alternate; sum += val; } if (sum % 10) return 0; return 1; } snort-2.9.20/src/dynamic-preprocessors/sdf/Makefile.am0000444000175000017500000000151014230012554021012 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_sdf_preproc.la libsf_sdf_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_sdf_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_sdf_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c endif libsf_sdf_preproc_la_SOURCES = \ spp_sdf.c \ spp_sdf.h \ sdf_pattern_match.c \ sdf_pattern_match.h \ sdf_credit_card.c \ sdf_credit_card.h \ sdf_us_ssn.c \ sdf_us_ssn.h \ sdf_detection_option.c \ sdf_detection_option.h EXTRA_DIST = \ sf_sdf.vcxproj \ sf_sdf.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/sdf/spp_sdf.h0000644000175000017500000000742114241076277020614 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2009-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * spp_sdf.h: Definitions, prototypes, etc. for the SDF preprocessor. * Author: Ryan Jordan */ #ifndef SPP_SDF_H #define SPP_SDF_H /*#include "sdf_pattern_match.h"*/ #include #include "sfPolicyUserData.h" #include "sdf_us_ssn.h" #include "sdf_detection_option.h" #define GENERATOR_SPP_SDF_RULES 138 #define GENERATOR_SPP_SDF_PREPROC 139 /* This is the maximum defined area number */ #define MAX_AREA 772 #define MAX_PORTS 65536 #define PORT_INDEX(port) port/8 #define CONV_PORT(port) 1 << (port % 8) #define MAX_PROTOCOL_ORDINAL 8192 typedef struct _sdf_tree_node { char *pattern; uint16_t num_children; uint16_t num_option_data; struct _sdf_tree_node **children; SDFOptionData **option_data_list; } sdf_tree_node; typedef struct _SDFSessionData { sdf_tree_node *part_match_node; uint16_t part_match_index; uint32_t num_patterns, global_counter; uint8_t *counters; int8_t *rtns_matched; uint32_t config_num; uint32_t last_pkt_seq_num; int last_pkt_data_len; } SDFSessionData; typedef struct _SDFContext { tSfPolicyUserContextId context_id; sdf_tree_node *head_node; uint32_t num_patterns; } SDFContext; typedef struct _SDFConfig { SFSnortPacket *pseudo_packet; SDFSessionData *stateless_session; uint32_t threshold; uint8_t mask_output; int ssn_max_group[MAX_AREA+1]; unsigned char src_ports[MAX_PORTS/8]; unsigned char dst_ports[MAX_PORTS/8]; unsigned char protocol_ordinals[MAX_PROTOCOL_ORDINAL]; uint32_t config_num; } SDFConfig; /* Definitions of config options */ #define SDF_THRESHOLD_KEYWORD "alert_threshold" #define SDF_MASK_KEYWORD "mask_output" #define SDF_SSN_FILE_KEYWORD "ssn_file" #define SDF_OPTION_NAME "sd_pattern" #define SDF_OPTION_SEPARATORS "," /* Order of SDF options */ #define SDF_OPTION_COUNT_NUM 1 #define SDF_OPTION_PATTERN_NUM 2 /* Keywords for SDF built-in option */ /* This pattern matches Visa/Mastercard/Amex, with & without spaces or dashes. The pattern alone would match other non-credit patterns, but the function SDFLuhnAlgorithm() does stricter checking. */ #define SDF_CREDIT_KEYWORD "credit_card" #define SDF_CREDIT_PATTERN_ALL "\\D\\d{4} ?-?\\d{4} ?-?\\d{2} ?-?\\d{2} ?-?\\d{3}\\d?\\D" #define SDF_SOCIAL_KEYWORD "us_social" #define SDF_SOCIAL_PATTERN "\\D\\d{3}-\\d{2}-\\d{4}\\D" #define SDF_SOCIAL_NODASHES_KEYWORD "us_social_nodashes" #define SDF_SOCIAL_NODASHES_PATTERN "\\D\\d{9}\\D" #define SDF_EMAIL_KEYWORD "email" #define SDF_EMAIL_PATTERN "\\w@\\w" /* Obfuscation constants */ #define SDF_OBFUSCATION_CHAR 'X' #define SDF_OBFUSCATION_DIGITS_SHOWN 4 /* Length of ": 255\0" */ #define SDF_ALERT_LENGTH 6 /* Combo Alert constants */ #define SDF_COMBO_ALERT_SID 1 #define SDF_COMBO_ALERT_REV 1 #define SDF_COMBO_ALERT_CLASS 1 #define SDF_COMBO_ALERT_PRIORITY 1 #define SDF_COMBO_ALERT_STR "(spp_sdf) SDF Combination Alert" extern SDFContext *sdf_context; #endif snort-2.9.20/src/dynamic-preprocessors/sip/0000755000175000017500000000000014242725716017020 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/sip/sf_sip.dsp0000555000175000017500000001403014230012554020774 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_sip" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_sip - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_sip.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_sip.mak" CFG="sf_sip - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_sip - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_sip - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_sip - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_sip - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_sip - Win32 Release" # Name "sf_sip - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\inet_aton.c # End Source File # Begin Source File SOURCE=..\include\inet_pton.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_ip.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\sip_config.c # End Source File # Begin Source File SOURCE=.\sip_dialog.c # End Source File # Begin Source File SOURCE=.\sip_parser.c # End Source File # Begin Source File SOURCE=.\sip_roptions.c # End Source File # Begin Source File SOURCE=.\sip_utils.c # End Source File # Begin Source File SOURCE=.\spp_sip.c # End Source File # Begin Source File SOURCE=.\sip_paf.c # End Source File # Begin Source File SOURCE=..\include\strtok_r.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\sip_config.h # End Source File # Begin Source File SOURCE=.\sip_debug.h # End Source File # Begin Source File SOURCE=.\sip_dialog.h # End Source File # Begin Source File SOURCE=.\sip_parser.h # End Source File # Begin Source File SOURCE=.\sip_roptions.h # End Source File # Begin Source File SOURCE=.\sip_utils.h # End Source File # Begin Source File SOURCE=.\spp_sip.h # End Source File # Begin Source File SOURCE=.\sip_paf.h # End Source File # Begin Source File SOURCE=..\include\sip_common.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/sip/sip_buffer_dump.c0000644000175000017500000000527014241076300022324 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file sip_buffer_dump.c ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during SIP inspection. */ #include "sip_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_SIP_BUFFER_DUMP] = {{"HEADER_DUMP", "", 0}, {"BODY_DUMP", "", 0}, {"STATUS_CODE_DUMP", "", 0}, {"METHOD_DUMP", "", 0}, {"URI_DUMP", "", 0}, {"CALL_ID_DUMP", "", 0}, {"CSEQ_NAME_DUMP", "", 0}, {"FROM_DUMP", "", 0}, {"FROM_TAG_DUMP", "", 0}, {"USER_NAME_DUMP", "", 0}, {"TO_DUMP", "", 0}, {"TO_TAG_DUMP", "", 0}, {"VIA_DUMP", "", 0}, {"CONTACT_DUMP", "", 0}, {"USER_AGENT_DUMP", "", 0}, {"SERVER_DUMP", "", 0}}; void dumpBuffer(SIP_BUFFER_DUMP type, const char *content, uint16_t len){ buf[type].buf_content = (char *)content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_SIP_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getSIPBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/sip/sip_roptions.c0000644000175000017500000003265414241076333021717 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sip_roptions.h" #include "spp_sip.h" #include "sf_types.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "sf_dynamic_engine.h" #include "sf_snort_plugin_api.h" #include "sfhashfcn.h" #include "profiler.h" #include "sip_utils.h" #include "sip_debug.h" #include "sip_config.h" #include "treenodes.h" #define SIP_ROPT__METHOD "sip_method" #define SIP_ROPT__STATUS_CODE "sip_stat_code" #define SIP_ROPT__HEADER "sip_header" #define SIP_ROPT__BODY "sip_body" /******************************************************************** * Private function prototypes ********************************************************************/ static int SIP_MethodInit(struct _SnortConfig *sc, char *, char *, void **); static int SIP_MethodEval(void *, const uint8_t **, void *); static int SIP_HeaderInit(struct _SnortConfig *sc, char *, char *, void **); static int SIP_HeaderEval(void *, const uint8_t **, void *); static int SIP_StatCodeInit(struct _SnortConfig *sc, char *, char *, void **); static int SIP_StatCodeEval(void *, const uint8_t **, void *); static int SIP_BodyInit(struct _SnortConfig *sc, char *, char *, void **); static int SIP_BodyEval(void *, const uint8_t **, void *); static int SIP_MethodAddFastPatterns(void *, int, int, FPContentInfo **); static inline int SIP_RoptDoEval(SFSnortPacket *p) { if ((p->payload_size == 0) || (p->stream_session == NULL) || (!IsTCP(p) && !IsUDP(p))) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "No payload or no " "session pointer or not TCP or UDP - not evaluating.\n")); return 0; } return 1; } static inline int IsRequest(SIP_Roptions *ropts) { if (ropts->status_code) return FALSE; else return TRUE; } /* Parsing for the rule option */ static int SIP_MethodInit(struct _SnortConfig *sc, char *name, char *params, void **data) { int flags = 0, mask = 0; char *end = NULL; char *tok; int negated = 0; int numTokens = 0; SipMethodRuleOptData *sdata; SIPMethodNode *method; SIPConfig * sip_parsing_config; if (strcasecmp(name, SIP_ROPT__METHOD) != 0) return 0; /*Evaluate whether all the methods are in the PP configurations */ sip_parsing_config = getParsingSIPConfig(sc); if (NULL == sip_parsing_config) DynamicPreprocessorFatalMessage("%s(%d) => Configuration error!\n", *(_dpd.config_file), *(_dpd.config_line)); /* Must have arguments */ if (SIP_IsEmptyStr(params)) { DynamicPreprocessorFatalMessage("%s(%d) => missing argument to sip_method keyword\n", *(_dpd.config_file), *(_dpd.config_line)); } tok = strtok_r(params, ",", &end); if(!tok) DynamicPreprocessorFatalMessage("%s(%d) => missing argument to sip_method keyword\n", *(_dpd.config_file), *(_dpd.config_line)); while (NULL != tok) { numTokens++; if (tok[0] == '!') { negated = 1; tok++; } /*Only one method is allowed with !*/ if (negated && (numTokens > 1)) { DynamicPreprocessorFatalMessage("%s(%d) => %s, only one method is allowed with ! for %s.\n", *(_dpd.config_file), *(_dpd.config_line), tok, name); } method = SIP_FindMethod (sip_parsing_config->methods, tok, strlen (tok)); /*if method is not found, add it as a user defined method*/ if (NULL == method) { method = SIP_AddUserDefinedMethod(tok, &sip_parsing_config->methodsConfig, &sip_parsing_config->methods ); if (NULL == method) DynamicPreprocessorFatalMessage("%s(%d) => %s can't add new method to %s.\n", *(_dpd.config_file), *(_dpd.config_line), tok, name); _dpd.logMsg("%s(%d) => Add user defined method: %s to SIP preprocessor through rule.\n", *(_dpd.config_file), *(_dpd.config_line), method->methodName); } flags |= 1 << (method->methodFlag - 1); if (negated) mask |= 1 << (method->methodFlag - 1); tok = strtok_r(NULL, ", ", &end); } sdata = (SipMethodRuleOptData *)calloc(1, sizeof(*sdata)); if (sdata == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "sip preprocessor rule option.\n"); } sdata->flags = flags; sdata->mask = mask; *data = (void *)sdata; return 1; } /* Rule option evaluation */ static int SIP_MethodEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; SIPData *sd; SIP_Roptions *ropts; SipMethodRuleOptData *sdata = (SipMethodRuleOptData *)data; uint32_t methodFlag; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Evaluating \"%s\" rule option.\n", SIP_ROPT__METHOD)); if (!SIP_RoptDoEval(p)) return RULE_NOMATCH; sd = (SIPData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_SIP); if (sd == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Rule Flags: %x Data Flags: %x, Mask: %x \n", sdata->flags, ropts->methodFlag, sdata->mask )); // Not response methodFlag = 1 << (ropts->methodFlag - 1); if (IsRequest(ropts) && ((sdata->flags & methodFlag) ^ sdata->mask)) { return RULE_MATCH; } return RULE_NOMATCH; } static int SIP_MethodAddFastPatterns(void *data, int protocol, int direction, FPContentInfo **info) { char *sip = "SIP"; FPContentInfo *method_fp; SipMethodRuleOptData *sdata = (SipMethodRuleOptData *)data; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Evaluating \"%s\" fast pattern rule option.\n", SIP_ROPT__METHOD)); if ((sdata == NULL) || (info == NULL)) return -1; if ((protocol != IPPROTO_TCP) && (protocol != IPPROTO_UDP)) return -1; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "adding info to \"%s\" fast pattern rule option.\n", SIP_ROPT__METHOD)); method_fp = (FPContentInfo *)calloc(1,sizeof(FPContentInfo)); if (NULL == method_fp) return -1; method_fp->content = (char *)malloc(strlen(sip)); if (NULL == method_fp->content) { free(method_fp); return -1; } memcpy(method_fp->content, sip, strlen(sip)); method_fp->length = strlen(sip); *info = method_fp; return 0; } /* Parsing for the rule option */ static int SIP_HeaderInit(struct _SnortConfig *sc, char *name, char *params, void **data) { if (strcasecmp(name, SIP_ROPT__HEADER) != 0) return 0; /* Must not have arguments */ if (!SIP_IsEmptyStr(params)) { DynamicPreprocessorFatalMessage("%s, %s(%d) => rule option: This option has no arguments.\n", SIP_ROPT__HEADER, *(_dpd.config_file), *(_dpd.config_line)); } return 1; } /* Rule option evaluation */ static int SIP_HeaderEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; SIPData *sd; SIP_Roptions *ropts; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Evaluating \"%s\" rule option.\n", SIP_ROPT__HEADER)); if (!SIP_RoptDoEval(p)) return RULE_NOMATCH; sd = (SIPData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_SIP); if (sd == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if (ropts->header_data != NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Setting cursor to header data: %p.\n", ropts->header_data)); *cursor = ropts->header_data; //Limit the length _dpd.SetAltDetect((uint8_t *)ropts->header_data, ropts->header_len); return RULE_MATCH; } return RULE_NOMATCH; } /* Parsing for the rule option */ static int SIP_StatCodeInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *end = NULL; char *tok; int i_tok = 0; SipStatCodeRuleOptData *sdata; if (strcasecmp(name, SIP_ROPT__STATUS_CODE) != 0) return 0; /* Must have arguments */ if (SIP_IsEmptyStr(params)) { DynamicPreprocessorFatalMessage("%s(%d) => missing argument to sip_stat_code keyword\n", *(_dpd.config_file), *(_dpd.config_line)); } tok = strtok_r(params, ",", &end); if(!tok) DynamicPreprocessorFatalMessage("%s(%d) => missing argument to sip_stat_code keyword\n", *(_dpd.config_file), *(_dpd.config_line)); sdata = (SipStatCodeRuleOptData *)calloc(1, sizeof(*sdata)); if (sdata == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "sip preprocessor rule option.\n"); } while ((NULL != tok) && (i_tok < SIP_NUM_STAT_CODE_MAX)) { unsigned long statCode = _dpd.SnortStrtoul(tok, NULL, 10); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Rule Status code: %d.\n",sdata->stat_codes[i_tok])); if ((statCode > MAX_STAT_CODE) || ((statCode > NUM_OF_RESPONSE_TYPES - 1) && (statCode < MIN_STAT_CODE))) { DynamicPreprocessorFatalMessage("%s(%d) => Status code %u specified is not a 3 digit number or 1 - %d\n ", *(_dpd.config_file), *(_dpd.config_line), statCode, NUM_OF_RESPONSE_TYPES-1); } sdata->stat_codes[i_tok] = (uint16_t)statCode; tok = strtok_r(NULL, ", ", &end); i_tok++; } if (NULL != tok) DynamicPreprocessorFatalMessage("%s(%d) => More than %d argument to sip_stat_code keyword\n", *(_dpd.config_file), *(_dpd.config_line), SIP_NUM_STAT_CODE_MAX); *data = (void *)sdata; return 1; } /* Rule option evaluation */ static int SIP_StatCodeEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; SIPData *sd; SIP_Roptions *ropts; SipStatCodeRuleOptData *sdata = (SipStatCodeRuleOptData *)data; uint16_t short_code; int i_code; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Evaluating \"%s\" rule option.\n", SIP_ROPT__STATUS_CODE)); if (!SIP_RoptDoEval(p)) return RULE_NOMATCH; sd = (SIPData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_SIP); if (sd == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Status code in packet: %d \n", ropts->status_code)); if (0 == ropts->status_code) return RULE_NOMATCH; /*Match the status code*/ short_code = ropts->status_code / 100; for(i_code = 0; i_code < SIP_NUM_STAT_CODE_MAX; i_code++) { if ((sdata->stat_codes[i_code] == short_code)|| (sdata->stat_codes[i_code] == ropts->status_code)) return RULE_MATCH; } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Rule No Match\n")); return RULE_NOMATCH; } /* Parsing for the rule option */ static int SIP_BodyInit(struct _SnortConfig *sc, char *name, char *params, void **data) { if (strcasecmp(name, SIP_ROPT__BODY) != 0) return 0; /* Must not have arguments */ if (!SIP_IsEmptyStr(params)) { DynamicPreprocessorFatalMessage("%s, %s(%d) => rule option: This option has no arguments.\n", SIP_ROPT__BODY, *(_dpd.config_file), *(_dpd.config_line)); } return 1; } /* Rule option evaluation */ static int SIP_BodyEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; SIPData *sd; SIP_Roptions *ropts; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Evaluating \"%s\" rule option.\n", SIP_ROPT__BODY)); if (!SIP_RoptDoEval(p)) return RULE_NOMATCH; sd = (SIPData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_SIP); if (sd == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if (ropts->body_data != NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Setting cursor to body data: %p.\n", ropts->body_data)); *cursor = ropts->body_data; //Limit the length _dpd.SetAltDetect((uint8_t *)ropts->body_data, ropts->body_len); return RULE_MATCH; } return RULE_NOMATCH; } /******************************************************************** * Function: SIP_RegRuleOptions * * Purpose: Register rule options * * Arguments: void * * Returns: void * ********************************************************************/ void SIP_RegRuleOptions(struct _SnortConfig *sc) { _dpd.preprocOptRegister(sc, SIP_ROPT__METHOD, SIP_MethodInit, SIP_MethodEval, free, NULL, NULL, NULL, SIP_MethodAddFastPatterns); _dpd.preprocOptRegister(sc, SIP_ROPT__HEADER, SIP_HeaderInit, SIP_HeaderEval, NULL, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, SIP_ROPT__STATUS_CODE, SIP_StatCodeInit, SIP_StatCodeEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, SIP_ROPT__BODY, SIP_BodyInit, SIP_BodyEval, NULL, NULL, NULL, NULL, NULL); } snort-2.9.20/src/dynamic-preprocessors/sip/sip_config.c0000644000175000017500000006560414241076303021305 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 2/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "sf_types.h" #include "sf_snort_packet.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "sip_config.h" #include "spp_sip.h" #include "sip_debug.h" #define METHOD_NOT_FOUND -1 /* * Default SIP port */ #define SIP_PORT 5060 #define SIPS_PORT 5061 /* * Default values for configurable parameters. */ #define SIP_DEFAULT_MAX_SESSIONS 10000 #define SIP_DEFAULT_MAX_DIALOGS_IN_SESSION 4 #define SIP_DEFAULT_MAX_URI_LEN 256 #define SIP_DEFAULT_MAX_CALL_ID_LEN 256 #define SIP_DEFAULT_MAX_REQUEST_NAME_LEN 20 #define SIP_DEFAULT_MAX_FROM_LEN 256 #define SIP_DEFAULT_MAX_TO_LEN 256 #define SIP_DEFAULT_MAX_VIA_LEN 1024 #define SIP_DEFAULT_MAX_CONTACT_LEN 256 #define SIP_DEFAULT_MAX_CONTENT_LEN 1024 /* * Min/Max values for each configurable parameter. */ #define MIN_MAX_NUM_SESSION 1024 /* * The maximum value that can be used is 4 GB * which 4194304 in KB on product. So will be using * 1 more than this as maximum limit. */ #define MAX_MAX_NUM_SESSION 4194305 #define MIN_MAX_NUM_DIALOG 1 #define MAX_MAX_NUM_DIALOG 4194305 #define MIN_MAX_URI_LEN 0 #define MAX_MAX_URI_LEN 65535 #define MIN_MAX_CALL_ID_LEN 0 #define MAX_MAX_CALL_ID_LEN 65535 #define MIN_MAX_REQUEST_NAME_LEN 0 #define MAX_MAX_REQUEST_NAME_LEN 65535 #define MIN_MAX_FROM_LEN 0 #define MAX_MAX_FROM_LEN 65535 #define MIN_MAX_TO_LEN 0 #define MAX_MAX_TO_LEN 65535 #define MIN_MAX_VIA_LEN 0 #define MAX_MAX_VIA_LEN 65535 #define MIN_MAX_CONTACT_LEN 0 #define MAX_MAX_CONTACT_LEN 65535 #define MIN_MAX_CONTENT_LEN 0 #define MAX_MAX_CONTENT_LEN 65535 /* * Keyword strings for parsing configuration options. */ #define SIP_DISABLED_KEYWORD "disabled" #define SIP_PORTS_KEYWORD "ports" #define SIP_MAX_SESSION_KEYWORD "max_sessions" #define SIP_MAX_DIALOG_KEYWORD "max_dialogs" #define SIP_METHODS_KEYWORD "methods" #define SIP_MAX_URI_LEN_KEYWORD "max_uri_len" #define SIP_MAX_CALL_ID_LEN_KEYWORD "max_call_id_len" #define SIP_MAX_REQUEST_NAME_LEN_KEYWORD "max_requestName_len" #define SIP_MAX_FROM_LEN_KEYWORD "max_from_len" #define SIP_MAX_TO_LEN_KEYWORD "max_to_len" #define SIP_MAX_VIA_LEN_KEYWORD "max_via_len" #define SIP_MAX_CONTACT_LEN_KEYWORD "max_contact_len" #define SIP_MAX_CONTENT_LEN_KEYWORD "max_content_len" #define SIP_IGNORE_CHANNEL_KEYWORD "ignore_call_channel" #define SIP_SEPERATORS "()<>@,;:\\/[]?={}\" " #define SIP_CONFIG_SECTION_SEPERATORS ",;" #define SIP_CONFIG_VALUE_SEPERATORS " " /* * method names defined by standard, 14 methods defined up to Mar. 2011 * The first 6 methods are standard defined by RFC3261 */ SIPMethod StandardMethods[] = { {"invite", SIP_METHOD_INVITE}, {"cancel",SIP_METHOD_CANCEL}, {"ack", SIP_METHOD_ACK}, {"bye", SIP_METHOD_BYE}, {"register", SIP_METHOD_REGISTER}, {"options",SIP_METHOD_OPTIONS}, {"refer", SIP_METHOD_REFER}, {"subscribe", SIP_METHOD_SUBSCRIBE}, {"update", SIP_METHOD_UPDATE}, {"join", SIP_METHOD_JOIN}, {"info", SIP_METHOD_INFO}, {"message", SIP_METHOD_MESSAGE}, {"notify", SIP_METHOD_NOTIFY}, {"prack", SIP_METHOD_PRACK}, {NULL, SIP_METHOD_NULL} }; static SIPMethodsFlag currentUseDefineMethod = SIP_METHOD_USER_DEFINE; /* * Function prototype(s) */ static void DisplaySIPConfig(SIPConfig *); static void SIP_SetDefaultMethods(SIPConfig *); static void SIP_ParsePortList(char **, uint8_t *); static void SIP_ParseMethods(char **, uint32_t *,SIPMethodlist*); static SIPMethodNode* SIP_AddMethodToList(char *, SIPMethodsFlag, SIPMethodlist*); static int SIP_findMethod(char *, SIPMethod *); static int ParseNumInRange(char *token, char *keyword, int min, int max); /* * Find method from the array methods * * PARAMETERS: * char *token: the method token name to be checked * SIPMethod* methods: methods array. * * RETURNS: * the index of the method in the array, -1 if not found */ static int SIP_findMethod(char *token, SIPMethod* methods) { int i = 0; while(NULL != methods[i].name) { if ((strlen(token) == strlen(methods[i].name))&& (strncasecmp(methods[i].name, token, strlen(token)) == 0)) return i; i++; } return METHOD_NOT_FOUND; } /* Display the configuration for the SIP preprocessor. * * PARAMETERS: * * SIPConfig *config: SIP preprocessor configuration. * * RETURNS: Nothing. */ static void DisplaySIPConfig(SIPConfig *config) { int index; int newline; SIPMethodNode *method; if (config == NULL) return; _dpd.logMsg("SIP config: \n"); _dpd.logMsg(" Max number of sessions: %d %s \n", config->maxNumSessions, config->maxNumSessions == SIP_DEFAULT_MAX_SESSIONS ? "(Default)" : "" ); _dpd.logMsg(" Max number of dialogs in a session: %d %s \n", config->maxNumDialogsInSession, config->maxNumDialogsInSession == SIP_DEFAULT_MAX_DIALOGS_IN_SESSION ? "(Default)" : "" ); _dpd.logMsg(" Status: %s\n", config->disabled ? "DISABLED":"ENABLED"); if (config->disabled) return; _dpd.logMsg(" Ignore media channel: %s\n", config->ignoreChannel ? "ENABLED":"DISABLED"); _dpd.logMsg(" Max URI length: %d %s \n", config->maxUriLen, config->maxUriLen == SIP_DEFAULT_MAX_URI_LEN ? "(Default)" : "" ); _dpd.logMsg(" Max Call ID length: %d %s \n", config->maxCallIdLen, config->maxCallIdLen == SIP_DEFAULT_MAX_CALL_ID_LEN ? "(Default)" : "" ); _dpd.logMsg(" Max Request name length: %d %s \n", config->maxRequestNameLen, config->maxRequestNameLen == SIP_DEFAULT_MAX_REQUEST_NAME_LEN ? "(Default)" : "" ); _dpd.logMsg(" Max From length: %d %s \n", config->maxFromLen, config->maxFromLen == SIP_DEFAULT_MAX_FROM_LEN ? "(Default)" : "" ); _dpd.logMsg(" Max To length: %d %s \n", config->maxToLen, config->maxToLen == SIP_DEFAULT_MAX_TO_LEN ? "(Default)" : "" ); _dpd.logMsg(" Max Via length: %d %s \n", config->maxViaLen, config->maxViaLen == SIP_DEFAULT_MAX_VIA_LEN ? "(Default)" : "" ); _dpd.logMsg(" Max Contact length: %d %s \n", config->maxContactLen, config->maxContactLen == SIP_DEFAULT_MAX_CONTACT_LEN ? "(Default)" : "" ); _dpd.logMsg(" Max Content length: %d %s \n", config->maxContentLen, config->maxContentLen == SIP_DEFAULT_MAX_CONTENT_LEN ? "(Default)" : "" ); /* Traverse list, printing ports, 5 per line */ newline = 1; _dpd.logMsg(" Ports:\n"); for(index = 0; index < MAXPORTS; index++) { if( config->ports[ PORT_INDEX(index) ] & CONV_PORT(index) ) { _dpd.logMsg("\t%d", index); if ( !((newline++)% 5) ) { _dpd.logMsg("\n"); } } } _dpd.logMsg("\n"); _dpd.logMsg(" Methods:\n"); _dpd.logMsg("\t%s ", config->methodsConfig == SIP_METHOD_DEFAULT ? "(Default)" : ""); method = config->methods; while(NULL != method) { _dpd.logMsg(" %s", method->methodName); method = method->nextm; } _dpd.logMsg("\n"); } /* * The first 6 methods are standard defined by RFC3261 * We use those first 6 methods as default * */ static void SIP_SetDefaultMethods(SIPConfig *config) { int i; config->methodsConfig = SIP_METHOD_DEFAULT; for (i = 0; i < 6 ; i++) { if (SIP_AddMethodToList(StandardMethods[i].name, StandardMethods[i].methodFlag, &config->methods) == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to add SIP " "default method: %s.\n", *(_dpd.config_file), *(_dpd.config_line), StandardMethods[i].name); } } } /******************************************************************** * Function: SIP_ParsePortList() * * Parses a port list and adds bits associated with the ports * parsed to a bit array. * * Arguments: * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the IP list. * uint8_t * * Pointer to the port array mask to set bits for the ports * parsed. * * Returns: * SIP_Ret * SIP_SUCCESS if we were able to successfully parse the * port list. * SIP_FAILURE if an error occured in parsing the port list. * ********************************************************************/ static void SIP_ParsePortList(char **ptr, uint8_t *port_array) { int port; char* cur_tokenp = *ptr; /* If the user specified ports, remove SIP_PORT for now since * it now needs to be set explicitly. */ port_array[ PORT_INDEX( SIP_PORT ) ] = 0; port_array[ PORT_INDEX( SIPS_PORT ) ] = 0; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Port configurations: %s\n",*ptr );); /* Eat the open brace. */ cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Port token: %s\n",cur_tokenp );); /* Check the space after '{'*/ if (( !cur_tokenp ) || ( 0 != strncmp (cur_tokenp, "{", 2 ))) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s, make sure space before and after '{'.\n", *(_dpd.config_file), *(_dpd.config_line), SIP_PORTS_KEYWORD); } cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); while (( cur_tokenp ) && ( 0 != strncmp (cur_tokenp, "}", 2 ))) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Port token: %s\n",cur_tokenp );); port = ParseNumInRange(cur_tokenp, SIP_PORTS_KEYWORD, 1, MAXPORTS-1); port_array[ PORT_INDEX( port ) ] |= CONV_PORT(port); cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); } if ( NULL == cur_tokenp ) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s, missing '}'.\n", *(_dpd.config_file), *(_dpd.config_line), SIP_PORTS_KEYWORD); } *ptr = cur_tokenp; } /* Parses a single numerical value. * A fatal error is made if the parsed value is out of bounds. * * PARAMETERS: * * token: String containing argument * keyword: String containing option's name. Used when printing an error. * min: Minimum value of argument * max: Maximum value of argument * * RETURNS: bounds-checked integer value of argument. */ static int ParseNumInRange(char *token, char *keyword, int min, int max) { long int value; char *str; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Num token: %s\n",token );); if (( !token ) || !isdigit((int)token[0]) ) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), keyword, min, max); } value = _dpd.SnortStrtol( token, &str, 10); if (0 != strlen(str)) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), keyword, min, max); } if (value < min || value > max) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), keyword, min, max); } return value; } /******************************************************************** * Function: SIP_ParseMethods() * * Parses the methods to detect * * * Arguments: * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the methods list. * SIPMethods* * Flag for the methods. * NULL flag if not a valid method type * Returns: * ********************************************************************/ static void SIP_ParseMethods(char **ptr, uint32_t *methodsConfig, SIPMethodlist* pmethods) { char* cur_tokenp = *ptr; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Method configurations: %s\n",*ptr );); /* If the user specified methods, remove default methods for now since * it now needs to be set explicitly. */ *methodsConfig = SIP_METHOD_NULL; /* Eat the open brace. */ cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Method token: %s\n",cur_tokenp );); /* Check the space after '{'*/ if (( !cur_tokenp ) || ( 0 != strncmp (cur_tokenp, "{", 2 ))) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s, make sure space before and after '{'.\n", *(_dpd.config_file), *(_dpd.config_line), SIP_METHODS_KEYWORD); } cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); while (( cur_tokenp ) && (0 != strncmp (cur_tokenp, "}", 2 ))) { int i_method; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Method token: %s\n",cur_tokenp );); // Check whether this is a standard method i_method = SIP_findMethod(cur_tokenp, StandardMethods); if (METHOD_NOT_FOUND != i_method ) { *methodsConfig |= 1 << (StandardMethods[i_method].methodFlag - 1); if (SIP_AddMethodToList(cur_tokenp, StandardMethods[i_method].methodFlag, pmethods) == NULL) { DynamicPreprocessorFatalMessage( "%s(%d) => Failed to add SIP method: %s.\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); } } else { if (SIP_AddUserDefinedMethod(cur_tokenp, methodsConfig, pmethods) == NULL) { DynamicPreprocessorFatalMessage( "%s(%d) => Failed to add user defined SIP method: %s.\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); } } cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); } if ( NULL == cur_tokenp ) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s, missing '}'.\n", *(_dpd.config_file), *(_dpd.config_line), SIP_METHODS_KEYWORD); } *ptr = cur_tokenp; } static SIPMethodNode* SIP_AddMethodToList(char *methodName, SIPMethodsFlag methodConf, SIPMethodlist* p_methodList) { SIPMethodNode* method; int methodLen; SIPMethodNode* lastMethod; if (NULL == methodName) return NULL; methodLen = strlen(methodName); method =*p_methodList; lastMethod = *p_methodList; while(method) { // Already in the list, return if(strcasecmp(method->methodName, methodName) == 0) return method; lastMethod = method; method = method->nextm; } method = (SIPMethodNode *) _dpd.snortAlloc(1, sizeof(SIPMethodNode), PP_SIP, PP_MEM_CATEGORY_CONFIG); if (NULL == method) return NULL; method->methodName = strdup(methodName); if (NULL == method->methodName) { _dpd.snortFree(method, sizeof(SIPMethodNode), PP_SIP, PP_MEM_CATEGORY_CONFIG); return NULL; } method->methodLen = methodLen; method->methodFlag = methodConf; method->nextm = NULL; // The first method, point to the first created one if (NULL == *p_methodList) { *p_methodList = method; } else { lastMethod->nextm = method; } return method; } /******************************************************************** * Function: SIP_FreeConfig * * Frees a sip configuration * * Arguments: * SIP_Config * * The configuration to free. * * Returns: None * ********************************************************************/ void SIP_FreeConfig (SIPConfig *config) { SIPMethodNode *nextNode; SIPMethodNode *curNode; if (config == NULL) return; curNode = config->methods; while (NULL != curNode) { if (NULL != curNode->methodName) free(curNode->methodName); nextNode = curNode->nextm; _dpd.snortFree(curNode, sizeof(SIPMethodNode), PP_SIP, PP_MEM_CATEGORY_CONFIG); curNode = nextNode; } _dpd.snortFree(config, sizeof(SIPConfig), PP_SIP, PP_MEM_CATEGORY_CONFIG); } /* Parses and processes the configuration arguments * supplied in the SIP preprocessor rule. * * PARAMETERS: * * SIPConfig *config: SIP preprocessor configuration. * argp: Pointer to string containing the config arguments. * * RETURNS: Nothing. */ void ParseSIPArgs(SIPConfig *config, u_char* argp) { char* cur_sectionp = NULL; char* next_sectionp = NULL; char* argcpyp = NULL; if (config == NULL) return; config->maxNumSessions = SIP_DEFAULT_MAX_SESSIONS; config->maxNumDialogsInSession = SIP_DEFAULT_MAX_DIALOGS_IN_SESSION; config->maxUriLen = SIP_DEFAULT_MAX_URI_LEN; config->maxCallIdLen = SIP_DEFAULT_MAX_CALL_ID_LEN; config->maxRequestNameLen = SIP_DEFAULT_MAX_REQUEST_NAME_LEN; config->maxFromLen = SIP_DEFAULT_MAX_FROM_LEN; config->maxToLen = SIP_DEFAULT_MAX_TO_LEN; config->maxViaLen = SIP_DEFAULT_MAX_VIA_LEN; config->maxContactLen = SIP_DEFAULT_MAX_CONTACT_LEN; config->maxContentLen = SIP_DEFAULT_MAX_CONTENT_LEN; /* Set up default port to listen on */ config->ports[ PORT_INDEX( SIP_PORT ) ] |= CONV_PORT(SIP_PORT); config->ports[ PORT_INDEX( SIPS_PORT ) ] |= CONV_PORT(SIPS_PORT); config->methodsConfig = SIP_METHOD_NULL; config->methods = NULL; /* Reset user defined method for every policy*/ currentUseDefineMethod = SIP_METHOD_USER_DEFINE; /* Sanity check(s) */ if ( !argp ) { SIP_SetDefaultMethods(config); DisplaySIPConfig(config); return; } argcpyp = strdup( (char*) argp ); if ( !argcpyp ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse SIP options.\n"); return; } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "SIP configurations: %s\n",argcpyp );); cur_sectionp = strtok_r( argcpyp, SIP_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Arguments token: %s\n",cur_sectionp );); while ( cur_sectionp ) { char* cur_config; char* cur_tokenp = strtok( cur_sectionp, SIP_CONFIG_VALUE_SEPERATORS); if (!cur_tokenp) { cur_sectionp = strtok_r( next_sectionp, SIP_CONFIG_SECTION_SEPERATORS, &next_sectionp); continue; } cur_config = cur_tokenp; if ( !strcmp( cur_tokenp, SIP_PORTS_KEYWORD )) { SIP_ParsePortList(&cur_tokenp, config->ports); } else if ( !strcmp( cur_tokenp, SIP_METHODS_KEYWORD )) { SIP_ParseMethods(&cur_tokenp, &config->methodsConfig, &config->methods ); } else if ( !strcmp( cur_tokenp, SIP_DISABLED_KEYWORD )) { config->disabled = 1; } else if ( !strcmp( cur_tokenp, SIP_MAX_SESSION_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxNumSessions = (uint32_t)ParseNumInRange(cur_tokenp, SIP_MAX_SESSION_KEYWORD, MIN_MAX_NUM_SESSION, MAX_MAX_NUM_SESSION); } else if ( !strcmp( cur_tokenp, SIP_MAX_DIALOG_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxNumDialogsInSession = (uint32_t)ParseNumInRange(cur_tokenp, SIP_MAX_DIALOG_KEYWORD, MIN_MAX_NUM_DIALOG, MAX_MAX_NUM_DIALOG); } else if ( !strcmp( cur_tokenp, SIP_MAX_URI_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxUriLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_URI_LEN_KEYWORD, MIN_MAX_URI_LEN, MAX_MAX_URI_LEN); } else if ( !strcmp( cur_tokenp, SIP_MAX_CALL_ID_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxCallIdLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_CALL_ID_LEN_KEYWORD, MIN_MAX_CALL_ID_LEN, MAX_MAX_CALL_ID_LEN); } else if ( !strcmp( cur_tokenp, SIP_MAX_REQUEST_NAME_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxRequestNameLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_REQUEST_NAME_LEN_KEYWORD, MIN_MAX_REQUEST_NAME_LEN, MAX_MAX_REQUEST_NAME_LEN); } else if ( !strcmp( cur_tokenp, SIP_MAX_FROM_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxFromLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_FROM_LEN_KEYWORD, MIN_MAX_FROM_LEN, MAX_MAX_FROM_LEN); } else if ( !strcmp( cur_tokenp, SIP_MAX_TO_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxToLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_TO_LEN_KEYWORD, MIN_MAX_TO_LEN, MAX_MAX_TO_LEN); } else if ( !strcmp( cur_tokenp, SIP_MAX_VIA_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxViaLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_VIA_LEN_KEYWORD, MIN_MAX_VIA_LEN, MAX_MAX_VIA_LEN); } else if ( !strcmp( cur_tokenp, SIP_MAX_CONTACT_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxContactLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_CONTACT_LEN_KEYWORD, MIN_MAX_CONTACT_LEN, MAX_MAX_CONTACT_LEN); } else if ( !strcmp( cur_tokenp, SIP_MAX_CONTENT_LEN_KEYWORD )) { cur_tokenp = strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS); config->maxContentLen = (uint16_t)ParseNumInRange(cur_tokenp, SIP_MAX_CONTENT_LEN_KEYWORD, MIN_MAX_CONTENT_LEN, MAX_MAX_CONTENT_LEN); } else if ( !strcmp( cur_tokenp, SIP_IGNORE_CHANNEL_KEYWORD )) { config->ignoreChannel = 1; } else { DynamicPreprocessorFatalMessage(" %s(%d) => Invalid argument: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); return; } /*Check whether too many parameters*/ if (NULL != strtok( NULL, SIP_CONFIG_VALUE_SEPERATORS)) { DynamicPreprocessorFatalMessage("%s(%d) => To many arguments: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_config); } cur_sectionp = strtok_r( next_sectionp, SIP_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Arguments token: %s\n",cur_sectionp );); } /*If no methods defined, use the default*/ if (SIP_METHOD_NULL == config->methodsConfig) { SIP_SetDefaultMethods(config); } DisplaySIPConfig(config); free(argcpyp); } /******************************************************************** * Function: SIP_AddUserDefinedMethod * * Add a user defined method * * Arguments: * char *: the method name * SIPMethodlist *: the list to be added * * Returns: user defined method * ********************************************************************/ SIPMethodNode* SIP_AddUserDefinedMethod(char *methodName, uint32_t *methodsConfig, SIPMethodlist* pmethods) { int i = 0; SIPMethodNode* method; /*Check whether all the chars are defined by RFC2616*/ while(methodName[i]) { if (iscntrl(methodName[i])|(NULL != strchr(SIP_SEPERATORS,methodName[i]))| (methodName[i] < 0) ) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad character included in the User defined method: %s." "Make sure space before and after '}'. \n", *(_dpd.config_file), *(_dpd.config_line), methodName ); return NULL; } i++; } if (currentUseDefineMethod > SIP_METHOD_USER_DEFINE_MAX) { DynamicPreprocessorFatalMessage(" %s(%d) => Exceeded max number of user defined methods (%d), can't add %s.\n", *(_dpd.config_file), *(_dpd.config_line), SIP_METHOD_USER_DEFINE_MAX - SIP_METHOD_USER_DEFINE + 1, methodName ); return NULL; } *methodsConfig |= 1 << (currentUseDefineMethod - 1); method = SIP_AddMethodToList(methodName, currentUseDefineMethod, pmethods); currentUseDefineMethod = (SIPMethodsFlag) (currentUseDefineMethod + 1); return method; } snort-2.9.20/src/dynamic-preprocessors/sip/sip_debug.h0000644000175000017500000000340114241076305021120 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides macros and functions for debugging the preprocessor. * If Snort is not configured to do debugging, macros are empty. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _SIP_DEBUG_H_ #define _SIP_DEBUG_H_ #include #include "snort_debug.h" /******************************************************************** * Macros ********************************************************************/ #define SIP_DEBUG__START_MSG "SIP Start ********************************************" #define SIP_DEBUG__END_MSG "SIP End **********************************************" #endif /* _SIP_DEBUG_H_ */ snort-2.9.20/src/dynamic-preprocessors/sip/sip_parser.c0000644000175000017500000012407314241076322021331 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 2/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HAVE_PARSER_H #include #include "sf_types.h" #include "sf_snort_packet.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "sip_parser.h" #include "spp_sip.h" #include "sip_config.h" #include "sip_utils.h" #include "sf_ip.h" #ifdef DUMP_BUFFER #include "sip_buffer_dump.h" #endif #define MAX_NUM_32BIT 2147483647 #define SIP_PARSE_NOFOLDING (-2) #define SIP_PARSE_ERROR (-1) #define SIP_PARSE_SUCCESS (1) /*Should at least have SIP/2.0 */ #define SIP_KEYWORD "SIP/" #define SIP_KEYWORD_LEN 4 #define SIP_VERSION_NUM_LEN 3 /*2.0 or 1.0 or 1.1*/ #define SIP_VERSION_LEN SIP_KEYWORD_LEN + SIP_VERSION_NUM_LEN #define SIP_MIN_MSG_LEN SIP_VERSION_LEN #define SIP_TAG_KEYWORD "tag=" #define SIP_TAG_KEYWORD_LEN 4 static int sip_headers_parse(SIPMsg *, const char *, char *,char **); static int sip_startline_parse(SIPMsg *, const char *, char *,char **); static int sip_body_parse(SIPMsg *, const char *, char *, char **); static int sip_check_headers(SIPMsg *); static int sip_parse_via(SIPMsg *, const char *, const char *); static int sip_parse_from(SIPMsg *, const char *, const char *); static int sip_parse_to(SIPMsg *, const char *, const char *); static int sip_parse_call_id(SIPMsg *, const char *, const char *); static int sip_parse_user_agent(SIPMsg *, const char *, const char *); static int sip_parse_server(SIPMsg *, const char *, const char *); static int sip_parse_cseq(SIPMsg *, const char *, const char *); static int sip_parse_contact(SIPMsg *, const char *, const char *); static int sip_parse_authorization(SIPMsg *, const char *, const char *); static int sip_parse_content_type(SIPMsg *, const char *, const char *); static int sip_parse_content_len(SIPMsg *, const char *, const char *); static int sip_parse_content_encode(SIPMsg *, const char *, const char *); static int sip_process_headField(SIPMsg *, const char *, const char *, int *); static int sip_process_bodyField(SIPMsg *, const char *, const char *); static int sip_parse_sdp_o(SIPMsg *, const char *, const char *); static int sip_parse_sdp_c(SIPMsg *, const char *, const char *); static int sip_parse_sdp_m(SIPMsg *, const char *, const char *); static int sip_find_linebreak(const char *, char *, char **); /* * Header fields and processing functions */ typedef struct _SIPheaderField { char *fname; int fnameLen; char *shortName; int (*setfield) (SIPMsg *, const char *,const char *); } SIPheaderField; /* * Body fields and processing functions */ typedef struct _SIPbodyField { char *fname; int fnameLen; int (*setfield) (SIPMsg *, const char *,const char *); } SIPbodyField; /* * header field name, short form field name, and field processing function */ SIPheaderField headerFields[] = { {"Via", 3, "v", &sip_parse_via}, {"From", 4,"f", &sip_parse_from}, {"To", 2, "t", &sip_parse_to}, {"Call-ID", 7, "i", &sip_parse_call_id}, {"CSeq", 4, NULL, &sip_parse_cseq}, {"Contact", 7, "m", &sip_parse_contact}, {"Authorization", 13, NULL, &sip_parse_authorization}, {"Content-Type", 12, "c", &sip_parse_content_type}, {"Content-Length", 14, "l", &sip_parse_content_len}, {"Content-Encoding", 16, "e", &sip_parse_content_encode}, {"User-Agent", 10, NULL, &sip_parse_user_agent}, {"Server", 6, NULL, &sip_parse_server}, {NULL, 0, NULL, NULL} }; /* * body field name, field processing function */ SIPbodyField bodyFields[] = { {"o=", 2, &sip_parse_sdp_o}, {"c=", 2, &sip_parse_sdp_c}, {"m=", 2, &sip_parse_sdp_m}, {NULL, 0, NULL} }; /******************************************************************** * Function: sip_process_headField() * * Process the header fields (lines). This also deals with folding. * * Arguments: * SIPMsg * - sip message * char* start - start of the header line * char* end - end of the header line * int* - index of last field processed. Used for folding processing * This value will be updated after current field been processed * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_process_headField(SIPMsg *msg, const char *start, const char *end, int *lastFieldIndex) { int findex =0; int length = end -start; char *colonIndex; char *newStart, *newEnd, newLength; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "process line: %.*s\n", length, start)); // If this is folding if((' ' == start[0]) || ('\t' == start[0])) { if(SIP_PARSE_NOFOLDING != *lastFieldIndex) { SIP_TrimSP(start, end, &newStart, &newEnd); return(headerFields[*lastFieldIndex].setfield(msg, newStart, newEnd)); } } // Otherwise, continue normal processing colonIndex = memchr(start, ':', length); if (!colonIndex || (colonIndex < start + 1)) return SIP_PARSE_ERROR; if (!SIP_TrimSP(start, colonIndex, &newStart, &newEnd)) return SIP_PARSE_ERROR; newLength = newEnd - newStart; /*Find out whether the field name needs to process*/ while (NULL != headerFields[findex].fname) { //Use the full name to check if ((headerFields[findex].fnameLen == newLength)&& (0 == strncasecmp(headerFields[findex].fname, newStart, newLength))) { break; } //Use short name to check else if ((NULL != headerFields[findex].shortName) && ( 1 == newLength)&& (0 == strncasecmp(headerFields[findex].shortName, newStart, newLength))) { break; } findex++; } if (NULL != headerFields[findex].fname) { // Found the field name, evaluate the value SIP_TrimSP(colonIndex + 1, end, &newStart, &newEnd); *lastFieldIndex = findex; return (headerFields[findex].setfield(msg, newStart, newEnd)); } *lastFieldIndex = SIP_PARSE_NOFOLDING; return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_process_bodyField() * * Process the body fields. * * Arguments: * SIPMsg * - sip message * char* start - start of the line * char* end - end of the line * * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_process_bodyField(SIPMsg *msg, const char *start, const char *end) { int findex =0; if (start == end) return SIP_PARSE_SUCCESS; /*Find out whether the field name needs to process*/ while (NULL != bodyFields[findex].fname) { int length = bodyFields[findex].fnameLen; if (0 == strncasecmp(bodyFields[findex].fname, start,length)) { return (bodyFields[findex].setfield(msg,start + length, end)); } findex++; } return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_find_linebreak() * * Find the line break \r \n in the current buffer * * Arguments: * char* start - start of the buffer * char* end - end of the buffer * char **lineEnd - output, point to the end of the line defined by line breaks * Returns: * int - number of line breaks found in the line found. ********************************************************************/ static int sip_find_linebreak(const char *start, char *end, char **lineEnd) { int numCRLF; char *s = (char *)start; *lineEnd = NULL; numCRLF = 0; if (start >= end) return 0; while ((s < end) && !('\r' ==*s || '\n' == *s)) { s++; } if (s == end) return 0; s++; numCRLF = 1; if ((s < end) && ('\r' == s[-1]) && ('\n' == s[0])) { s++; numCRLF = 2; } *lineEnd= s; return numCRLF; } /******************************************************************** * Function: sip_is_valid_version() * * Check whether the version is a valid version (2.0, 1.1, 1.0) * * Arguments: * char* start - start of the version * * Returns: * SIP_TRUE * SIP_FALSE ********************************************************************/ static inline int sip_is_valid_version(const char *start) { if (!strncmp(start, "1.", 2)) { if ((*(start+2) == '1') || (*(start+2) == '0')) return SIP_TRUE; } else if (!strncmp(start, "2.0", 3)) return SIP_TRUE; return SIP_FALSE; } /******************************************************************** * Function: sip_startline_parse() * * Parse the start line: request and response are different * * Arguments: * SIPMsg * - sip message * char* buff - start of the sip message buffer * char* end - end of the buffer * char**lineEnd - output, the found end of start line * Returns: * SIP_FAILURE * SIP_SUCCESS ********************************************************************/ static int sip_startline_parse(SIPMsg *msg, const char *buff, char *end, char **lineEnd) { char *next; char *start; int length; int numOfLineBreaks; start = (char *) buff; numOfLineBreaks = sip_find_linebreak(start, end, &next); if (numOfLineBreaks < 1) { /*No CRLF */ DEBUG_WRAP(DebugMessage(DEBUG_SIP, "No CRLF, check failed\n")); return SIP_FAILURE; } /*Exclude CRLF from start line*/ length = next - start - numOfLineBreaks; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Start line: %.*s \n", length, start)); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "End of Start line \n")); /*Should at least have SIP/2.0 */ if (length < SIP_MIN_MSG_LEN) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Message too short, check failed\n")); return SIP_FAILURE; } *lineEnd = next; // This is a response if (0 == strncmp((const char *) buff, (const char *) SIP_KEYWORD, SIP_KEYWORD_LEN)) { char *space; unsigned long statusCode; /*Process response*/ msg->method = NULL; msg->uri = NULL; /*Check SIP version number, end with SP*/ if (!(sip_is_valid_version(buff + SIP_KEYWORD_LEN) && (*(buff + SIP_VERSION_LEN) == ' '))) { ALERT(SIP_EVENT_INVALID_VERSION,SIP_EVENT_INVALID_VERSION_STR); } space = strchr(buff, ' '); if (space == NULL) return SIP_FAILURE; statusCode = _dpd.SnortStrtoul(space + 1, NULL, 10); if (( statusCode > MAX_STAT_CODE) || (statusCode < MIN_STAT_CODE )) { ALERT(SIP_EVENT_BAD_STATUS_CODE,SIP_EVENT_BAD_STATUS_CODE_STR) msg->status_code = MAX_STAT_CODE + 1; } else msg->status_code = (uint16_t)statusCode; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Status code: %d \n", msg->status_code)); } else /* This might be a request*/ { char *space; char *version; int length; SIPMethodNode *method; /*Process request*/ if (NULL ==sip_eval_config) return SIP_FAILURE; msg->status_code = 0; // Parse the method space = memchr(buff, ' ', end - buff); if (space == NULL) return SIP_FAILURE; length = space - buff; msg->method = (char*)buff; msg->methodLen = length; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "method: %.*s\n", msg->methodLen, msg->method)); #ifdef DUMP_BUFFER dumpBuffer(METHOD_DUMP,msg->method,msg->methodLen); #endif method = SIP_FindMethod (sip_eval_config->methods, msg->method, msg->methodLen); if (method) { msg->methodFlag = method->methodFlag; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Found the method: %s, Flag: 0x%x\n", method->methodName, method->methodFlag)); } // parse the uri if (space + 1 > end) return SIP_FAILURE; msg->uri = space + 1; space = memchr(space + 1, ' ', end - msg->uri); if (space == NULL) return SIP_FAILURE; msg->uriLen = space - msg->uri; #ifdef DUMP_BUFFER dumpBuffer(URI_DUMP,msg->uri,msg->uriLen); #endif DEBUG_WRAP(DebugMessage(DEBUG_SIP, "uri: %.*s, length: %u\n", msg->uriLen, msg->uri, msg->uriLen)); if(0 == msg->uriLen) ALERT(SIP_EVENT_EMPTY_REQUEST_URI,SIP_EVENT_EMPTY_REQUEST_URI_STR) else if (sip_eval_config->maxUriLen && (msg->uriLen > sip_eval_config->maxUriLen)) ALERT(SIP_EVENT_BAD_URI,SIP_EVENT_BAD_URI_STR); version = space + 1; if (version + SIP_VERSION_LEN > end) return SIP_FAILURE; if (0 != strncmp((const char *) version, (const char *) SIP_KEYWORD, SIP_KEYWORD_LEN)) return SIP_FAILURE; /*Check SIP version number, end with CRLF*/ if (!sip_is_valid_version(*lineEnd - SIP_VERSION_NUM_LEN - numOfLineBreaks)) { ALERT(SIP_EVENT_INVALID_VERSION,SIP_EVENT_INVALID_VERSION_STR); } if (NULL == method) { ALERT(SIP_EVENT_UNKOWN_METHOD, SIP_EVENT_UNKOWN_METHOD_STR); return SIP_FAILURE; } } return SIP_SUCCESS; } /******************************************************************** * Function: sip_headers_parse() * * Parse the SIP header: request and response are the same * * Arguments: * SIPMsg * - sip message * char* buff - start of the header * char* end - end of the buffer * char**lineEnd - output, the found end of header * Returns: * SIP_FAILURE * SIP_SUCCESS ********************************************************************/ static int sip_headers_parse(SIPMsg *msg, const char *buff, char *end, char **headEnd) { char *next; char *start; int length; int numOfLineBreaks; int lastFieldIndex = SIP_PARSE_NOFOLDING ; start = (char *) buff; /* * The end of header is defined by two CRLFs, or CRCR, or LFLF */ numOfLineBreaks = sip_find_linebreak(start, end, &next); while (numOfLineBreaks > 0) { /*Processing this line*/ length = next - start - numOfLineBreaks; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Header line: %.*s\n", length, start)); /*Process headers*/ sip_process_headField(msg, start, start + length, &lastFieldIndex); /*check the end of header*/ if ((1 == numOfLineBreaks) && ( start[0] == start[-1])) { /*Either CRCR or LFLF*/ *headEnd = next ; return SIP_SUCCESS; } else if ( (2 == numOfLineBreaks) && ('\r' == start[0])&&('\n' == start[1])) { *headEnd = next; return SIP_SUCCESS; } start = next; numOfLineBreaks = sip_find_linebreak(start, end, &next); } return SIP_SUCCESS; } /******************************************************************** * Function: sip_body_parse() * * Parse the SIP body: request and response are the same * * Arguments: * SIPMsg * - sip message * char* buff - start of the body * char* end - end of the buffer * char**lineEnd - output, the found end of body * Returns: * SIP_FAILURE * SIP_SUCCESS ********************************************************************/ static int sip_body_parse(SIPMsg *msg, const char *buff, char *end, char **bodyEnd) { int length; char *next; char *start; int numOfLineBreaks; length = end - buff; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Body length: %d\n", length);); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Body line: %.*s\n", length, buff);); // Initialize it *bodyEnd = end; if (buff == end) return SIP_SUCCESS; msg->body_data = (uint8_t *)buff; // Create a media session msg->mediaSession = (SIP_MediaSession *)_dpd.snortAlloc(1, sizeof(SIP_MediaSession), PP_SIP, PP_MEM_CATEGORY_SESSION); if (NULL == msg->mediaSession) return SIP_FAILURE; start = (char *) buff; /* * The end of body is defined by two CRLFs or CRCR or LFLF */ numOfLineBreaks = sip_find_linebreak(start, end, &next); while (numOfLineBreaks > 0) { /*Processing this line*/ length = next - start - numOfLineBreaks; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Body line: %.*s\n", length, start)); /*Process body fields*/ sip_process_bodyField(msg, start, start + length); start = next; numOfLineBreaks = sip_find_linebreak(start, end, &next); } *bodyEnd = start; return SIP_SUCCESS; } /******************************************************************** * Function: sip_check_headers() * * Check whether the headers are mal-formed. * Most checks are here, except some need context information are scattered * in the parsing. * * Arguments: * SIPMsg * - sip message * * Returns: * SIP_FAILURE * SIP_SUCCESS ********************************************************************/ static int sip_check_headers(SIPMsg *msg) { int ret = SIP_SUCCESS; if(0 == msg->fromLen) { ALERT(SIP_EVENT_EMPTY_FROM,SIP_EVENT_EMPTY_FROM_STR) ret = SIP_FAILURE; } else if (sip_eval_config->maxFromLen && (msg->fromLen > sip_eval_config->maxFromLen)) { ALERT(SIP_EVENT_BAD_FROM,SIP_EVENT_BAD_FROM_STR); ret = SIP_FAILURE; } if(0 == msg->toLen) { ALERT(SIP_EVENT_EMPTY_TO,SIP_EVENT_EMPTY_TO_STR) ret = SIP_FAILURE; } else if (sip_eval_config->maxToLen && (msg->toLen > sip_eval_config->maxToLen)) { ALERT(SIP_EVENT_BAD_TO,SIP_EVENT_BAD_TO_STR); ret = SIP_FAILURE; } if(0 == msg->callIdLen) { ALERT(SIP_EVENT_EMPTY_CALL_ID,SIP_EVENT_EMPTY_CALL_ID_STR) ret = SIP_FAILURE; } else if ( sip_eval_config->maxCallIdLen && (msg->callIdLen > sip_eval_config->maxCallIdLen)) { ALERT(SIP_EVENT_BAD_CALL_ID,SIP_EVENT_BAD_CALL_ID_STR); ret = SIP_FAILURE; } if(msg->cseqnum > MAX_NUM_32BIT) { ALERT(SIP_EVENT_BAD_CSEQ_NUM,SIP_EVENT_BAD_CSEQ_NUM_STR); ret = SIP_FAILURE; } if ( sip_eval_config->maxRequestNameLen && (msg->cseqNameLen > sip_eval_config->maxRequestNameLen)) { ALERT(SIP_EVENT_BAD_CSEQ_NAME,SIP_EVENT_BAD_CSEQ_NAME_STR); ret = SIP_FAILURE; } /*Alert here after parsing*/ if(0 == msg->viaLen) { ALERT(SIP_EVENT_EMPTY_VIA,SIP_EVENT_EMPTY_VIA_STR) ret = SIP_FAILURE; } else if (sip_eval_config->maxViaLen && (msg->viaLen > sip_eval_config->maxViaLen)) { ALERT(SIP_EVENT_BAD_VIA,SIP_EVENT_BAD_VIA_STR); ret = SIP_FAILURE; } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Method flag: %d\n", msg->methodFlag)); // Contact is required for invite message if((0 == msg->contactLen)&&(msg->methodFlag == SIP_METHOD_INVITE)&&(0 == msg->status_code)) { ALERT(SIP_EVENT_EMPTY_CONTACT,SIP_EVENT_EMPTY_CONTACT_STR) ret = SIP_FAILURE; } else if (sip_eval_config->maxContactLen && (msg->contactLen > sip_eval_config->maxContactLen)) { ALERT(SIP_EVENT_BAD_CONTACT,SIP_EVENT_BAD_CONTACT_STR); ret = SIP_FAILURE; } if((0 == msg->contentTypeLen) && (msg->content_len > 0)) { ALERT(SIP_EVENT_EMPTY_CONTENT_TYPE,SIP_EVENT_EMPTY_CONTENT_TYPE_STR) ret = SIP_FAILURE; } return ret; } /******************************************************************** * Function: sip_parse_via() * * Parse the via field: Via can have multiple header * * Arguments: * SIPMsg * - sip message * char* start - start of the via filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_via(SIPMsg *msg, const char *start, const char *end) { int length = end -start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Via value: %.*s\n", length, start);); msg->viaLen = msg->viaLen + length; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Via length: %d\n", msg->viaLen);); #ifdef DUMP_BUFFER dumpBuffer(VIA_DUMP,start,msg->viaLen); #endif return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_from() * * Parse the from field and get from tag * Note: From has no multiple header * * Arguments: * SIPMsg * - sip message * char* start - start of the from filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_from(SIPMsg *msg, const char *start, const char *end) { DEBUG_WRAP(int length = end -start;) char *buff; char *userEnd; char *userStart; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "From value: %.*s\n", length, start);); msg->from = (char *)start; msg->fromLen = end - start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "From length: %d , content: %.*s\n", msg->fromLen, msg->fromLen, msg->from);); #ifdef DUMP_BUFFER dumpBuffer(FROM_DUMP,msg->from,msg->fromLen); #endif /*Get the from tag*/ msg->fromTagLen = 0; buff = memchr(start, ';', msg->fromLen); while ((NULL != buff)&& (buff < end)) { if (0 == strncmp(buff + 1, SIP_TAG_KEYWORD, SIP_TAG_KEYWORD_LEN)) { msg->from_tag = buff + SIP_TAG_KEYWORD_LEN + 1; msg->fromTagLen = end - msg->from_tag; msg->dlgID.fromTagHash = strToHash(msg->from_tag,msg->fromTagLen); break; } buff = memchr(buff + 1, ';', msg->fromLen); } #ifdef DUMP_BUFFER dumpBuffer(FROM_TAG_DUMP,msg->from_tag,msg->fromTagLen); #endif userStart = memchr(msg->from, ':', msg->fromLen); userEnd = memchr(msg->from, '>', msg->fromLen); if (userStart && userEnd && (userEnd > userStart)) { /*strndup here */ msg->userName = userStart+1; msg->userNameLen = userEnd - userStart - 1; } else { msg->userName = NULL; msg->userNameLen = 0; } #ifdef DUMP_BUFFER dumpBuffer(USER_NAME_DUMP,msg->userName,msg->userNameLen); #endif DEBUG_WRAP(DebugMessage(DEBUG_SIP, "From tag length: %d , hash: %u, content: %.*s\n", msg->fromTagLen, msg->dlgID.fromTagHash, msg->fromTagLen, msg->from_tag);); return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_to() * * Parse the to field and get to tag information * Note: To has no multiple header * * Arguments: * SIPMsg * - sip message * char* start - start of the to filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_to(SIPMsg *msg, const char *start, const char *end) { DEBUG_WRAP(int length = end -start;) char *buff; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "To value: %.*s\n", length, start);); msg->to = (char *)start; msg->toLen = end - start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "To length: %d , content: %.*s\n", msg->toLen, msg->toLen, msg->to);); #ifdef DUMP_BUFFER dumpBuffer(TO_DUMP,msg->to,msg->toLen); #endif /*Processing tag information*/ msg->toTagLen = 0; buff = memchr(start, ';', msg->toLen); while ((NULL != buff)&& (buff < end)) { if (0 == strncmp(buff + 1, SIP_TAG_KEYWORD, SIP_TAG_KEYWORD_LEN)) { msg->to_tag = buff + SIP_TAG_KEYWORD_LEN + 1; msg->toTagLen = end - msg->to_tag; msg->dlgID.toTagHash = strToHash(msg->to_tag,msg->toTagLen); break; } buff = memchr(buff + 1, ';', msg->toLen); } #ifdef DUMP_BUFFER dumpBuffer(TO_TAG_DUMP,msg->to_tag,msg->toTagLen); #endif DEBUG_WRAP(DebugMessage(DEBUG_SIP, "To tag length: %d , Hash: %u, content: %.*s\n", msg->toTagLen, msg->dlgID.toTagHash, msg->toTagLen, msg->to_tag);); return SIP_PARSE_SUCCESS; } static inline bool is_valid_ip(const char *start, const char *end) { sfaddr_t ip; char ipStr[INET6_ADDRSTRLEN]; int length = end - start; /*Get the IP address*/ if(length > INET6_ADDRSTRLEN - 1) { length = INET6_ADDRSTRLEN - 1; } memcpy(ipStr, start, length); ipStr[length] = '\0'; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "IP data: %s\n", ipStr);); if( (sfaddr_pton(ipStr, &ip)) != SFIP_SUCCESS) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Not valid IP\n");); return false; } return true; } /******************************************************************** * Function: sip_parse_call_id() * * Parse the call-id field * Note: call-id has no multiple header * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_call_id(SIPMsg *msg, const char *start, const char *end) { char* at; int length = end -start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Call-Id value: %.*s\n", length, start);); msg->call_id = (char *) start; /*ignore ip address in call id by adjusting length*/ at = memchr(start, '@', length); if(at && (at < end) && is_valid_ip(at+1, end)) { length = at - start; } msg->callIdLen = end - start; #ifdef DUMP_BUFFER dumpBuffer(CALL_ID_DUMP,msg->call_id, length); #endif msg->dlgID.callIdHash = strToHash(msg->call_id, length); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Call-Id length: %d, Hash: %u\n", msg->callIdLen, msg->dlgID.callIdHash);); return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_user_agent() * * Parse the user_agent field * * Arguments: * SIPMsg * - sip message * char* start - start of the field line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_user_agent(SIPMsg *msg, const char *start, const char *end) { DEBUG_WRAP(int length = end -start;) DEBUG_WRAP(DebugMessage(DEBUG_SIP, "User-Agent value: %.*s\n", length, start);); msg->userAgent = (char *)start; msg->userAgentLen = end - start; #ifdef DUMP_BUFFER dumpBuffer(USER_AGENT_DUMP,msg->userAgent,msg->userAgentLen); #endif return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_server() * * Parse the server field * * Arguments: * SIPMsg * - sip message * char* start - start of the field line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_server(SIPMsg *msg, const char *start, const char *end) { DEBUG_WRAP(int length = end -start;) DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Server value: %.*s\n", length, start);); msg->server = (char *)start; msg->serverLen = end - start; #ifdef DUMP_BUFFER dumpBuffer(SERVER_DUMP,msg->server,msg->serverLen); #endif return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_cseq() * * Parse the cseq field: get sequence number and request name * Note: Cseq has no multiple header * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_cseq(SIPMsg *msg, const char *start, const char *end) { char *next = NULL; DEBUG_WRAP(int length = end -start;) SIPMethodNode* method = NULL; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "CSeq value: %.*s\n", length, start);); msg->cseqnum = _dpd.SnortStrtoul(start, &next, 10); if ((NULL != next )&&(next < end)) { msg->cseqName = next + 1; msg->cseqNameLen = end - msg->cseqName; method = SIP_FindMethod (sip_eval_config->methods, msg->cseqName, msg->cseqNameLen); } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "CSeq number: %d, CSeqName: %.*s\n", msg->cseqnum, msg->cseqNameLen, msg->cseqName);); #ifdef DUMP_BUFFER dumpBuffer(CSEQ_NAME_DUMP,msg->cseqName,msg->cseqNameLen); #endif if (NULL == method) { ALERT(SIP_EVENT_INVALID_CSEQ_NAME,SIP_EVENT_INVALID_CSEQ_NAME_STR) return SIP_PARSE_ERROR; } else { /*Use request name only for response message*/ if ((SIP_METHOD_NULL == msg->methodFlag)&&( msg->status_code > 0)) msg->methodFlag = method->methodFlag; else if ( method->methodFlag != msg->methodFlag) { ALERT(SIP_EVENT_MISMATCH_METHOD,SIP_EVENT_MISMATCH_METHOD_STR) } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Found the method: %s, Flag: 0x%x\n", method->methodName, method->methodFlag)); } return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_contact() * * Parse the to contact field * Note: Contact has multiple header * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_contact(SIPMsg *msg, const char *start, const char *end) { int length = end -start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Contact value: %.*s\n", length, start);); msg->contact = (char *) start; msg->contactLen = msg->contactLen + length; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Contact length: %d\n", msg->contactLen);); #ifdef DUMP_BUFFER dumpBuffer(CONTACT_DUMP,msg->contact,msg->contactLen); #endif return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_authorization() * * Parse the to authorization field * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_authorization(SIPMsg *msg, const char *start, const char *end) { DEBUG_WRAP(int length = end -start;) DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Authorization value: %.*s\n", length, start);); msg->authorization = (char *) start; return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_content_type() * * Parse the to content type field * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_content_type(SIPMsg *msg, const char *start, const char *end) { DEBUG_WRAP(int length = end -start;) DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Content type value: %.*s\n", length, start);); msg->contentTypeLen = end - start; msg->content_type = (char *) start; return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_content_len() * * Parse the to content length field * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_content_len(SIPMsg *msg, const char *start, const char *end) { char *next = NULL; #ifdef DEBUG DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Content length value: %.*s\n", (int)(end - start), start);); #endif msg->content_len = _dpd.SnortStrtoul(start, &next, 10); if ( sip_eval_config->maxContentLen && (msg->content_len > sip_eval_config->maxContentLen)) ALERT(SIP_EVENT_BAD_CONTENT_LEN,SIP_EVENT_BAD_CONTENT_LEN_STR); /*Check the length of the value*/ if (next > start + SIP_CONTENT_LEN) // This check is to prevent overflow { if (sip_eval_config->maxContentLen) ALERT(SIP_EVENT_BAD_CONTENT_LEN,SIP_EVENT_BAD_CONTENT_LEN_STR); return SIP_PARSE_ERROR; } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Content length: %u\n", msg->content_len);); return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_content_encode() * * Parse the to content encode field * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_content_encode(SIPMsg *msg, const char *start, const char *end) { DEBUG_WRAP(int length = end -start;) DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Content encode value: %.*s\n", length, start);); msg->content_encode = (char *) start; return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_sdp_o() * * Parse SDP origination information * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_sdp_o(SIPMsg *msg, const char *start, const char *end) { int length; char *spaceIndex = NULL; char *spaceIndex2 = NULL; if (NULL == msg->mediaSession) return SIP_PARSE_ERROR; length = end - start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Origination information: %.*s\n", length, start);); // Get username and session ID information (before second space) spaceIndex = memchr(start, ' ', length); // first space if ((NULL == spaceIndex)||(spaceIndex == end)) return SIP_PARSE_ERROR; spaceIndex = memchr(spaceIndex + 1, ' ', end - spaceIndex -1 ); // second space if (NULL == spaceIndex) return SIP_PARSE_ERROR; spaceIndex2 = memchr(spaceIndex + 1, ' ', end - spaceIndex -1 ); // third space if (NULL == spaceIndex2) return SIP_PARSE_ERROR; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Session information: %.*s\n", spaceIndex - start, start);); //sessionId uses all elements from o: line except sessionId version msg->mediaSession->sessionID = strToHash(start, spaceIndex - start); msg->mediaSession->sessionID += strToHash(spaceIndex2+1, end - (spaceIndex2+1)); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Session ID: %u\n", msg->mediaSession->sessionID);); return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_sdp_c() * * Parse SDP connection data * * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_sdp_c(SIPMsg *msg, const char *start, const char *end) { int length; sfaddr_t *ip; char ipStr[INET6_ADDRSTRLEN]; char *spaceIndex = NULL; if (NULL == msg->mediaSession) return SIP_PARSE_ERROR; length = end - start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Connection data: %.*s\n", length, start);); /*Get the IP address*/ spaceIndex = memchr(start, ' ', length); // first space if ((NULL == spaceIndex)||(spaceIndex == end)) return SIP_PARSE_ERROR; spaceIndex = memchr(spaceIndex + 1, ' ', end - spaceIndex -1 ); // second space if (NULL == spaceIndex) return SIP_PARSE_ERROR; length = end - spaceIndex; if(length > INET6_ADDRSTRLEN - 1) { length = INET6_ADDRSTRLEN - 1; } memcpy(ipStr, spaceIndex, length); ipStr[length] = '\0'; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "IP data: %s\n", ipStr);); // If no default session connect information, add it if(NULL == msg->mediaSession->medias) { ip = &(msg->mediaSession->maddress_default); } else // otherwise, update the latest media data (header of media list) { ip = &(msg->mediaSession->medias->maddress); } if( (sfaddr_pton(ipStr, ip)) != SFIP_SUCCESS) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Parsed error! \n");); return SIP_PARSE_ERROR; } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Parsed Connection data: %s\n", sfip_to_str (ip));); return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse_sdp_c() * * Parse media type information * Note: to make it easier update the media address, media data are added to the header of media list * Arguments: * SIPMsg * - sip message * char* start - start of the filed line * char* end - end of the line * Returns: * SIP_PARSE_ERROR * SIP_PARSE_SUCCESS ********************************************************************/ static int sip_parse_sdp_m(SIPMsg *msg, const char *start, const char *end) { int length; char *spaceIndex = NULL; char *next; SIP_MediaData *mdata; if (NULL == msg->mediaSession) return SIP_PARSE_ERROR; length = end - start; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Media information: %.*s\n", length, start);); spaceIndex = memchr(start, ' ', length); // first space if ((NULL == spaceIndex)||(spaceIndex == end)) return SIP_PARSE_ERROR; mdata = (SIP_MediaData *) _dpd.snortAlloc(1, sizeof(SIP_MediaData), PP_SIP, PP_MEM_CATEGORY_SESSION); if (NULL == mdata) return SIP_PARSE_ERROR; mdata->mport = (uint16_t) _dpd.SnortStrtoul(spaceIndex + 1, &next, 10); if ((NULL != next)&&('/'==next[0])) mdata->numPort = (uint8_t)_dpd.SnortStrtoul(spaceIndex + 1, &next, 10); // Put mdata->nextM = msg->mediaSession->medias; mdata->maddress = msg->mediaSession->maddress_default; msg->mediaSession->medias = mdata; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Media IP: %s, Media port %u, number of media: %d\n", sfip_to_str(&mdata->maddress), mdata->mport, mdata->numPort);); return SIP_PARSE_SUCCESS; } /******************************************************************** * Function: sip_parse() * * The main entry for parser: process the sip messages. * * Arguments: * SIPMsg * - sip message * char* buff - start of the sip message buffer * char* end - end of the buffer * * Returns: * SIP_FAILURE * SIP_SUCCESS ********************************************************************/ int sip_parse(SIPMsg *msg, const char *buff, char *end) { char *nextIndex; char *start; int status; #ifdef DUMP_BUFFER dumpBufferInit(); #endif /*Initialize key values*/ msg->methodFlag = SIP_METHOD_NULL; msg->status_code = 0; /*Parse the start line*/ start = (char *) buff; nextIndex = NULL; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Start parsing...\n")); msg->header = (uint8_t *) buff; status = sip_startline_parse(msg, start, end, &nextIndex); if(SIP_FAILURE == status ) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Start line parsing failed...\n")); return status; } /*Parse the headers*/ start = nextIndex; status = sip_headers_parse(msg, start, end, &nextIndex); msg->headerLen = nextIndex - buff; #ifdef DUMP_BUFFER dumpBuffer(HEADER_DUMP, (const char *) msg->header, msg->headerLen); #endif if(SIP_FAILURE == status ) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Header parsing failed...\n")); } status = sip_check_headers(msg); if(SIP_FAILURE == status ) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Headers validation failed...\n")); } /*Parse the body*/ start = nextIndex; msg->bodyLen = end - start; /* Disable this check for TCP. */ if((!msg->isTcp)&&(msg->content_len > msg->bodyLen)) ALERT(SIP_EVENT_MISMATCH_CONTENT_LEN,SIP_EVENT_MISMATCH_CONTENT_LEN_STR); if (msg->content_len < msg->bodyLen) status = sip_body_parse(msg, start, start + msg->content_len, &nextIndex); else status = sip_body_parse(msg, start, end, &nextIndex); #ifdef DUMP_BUFFER dumpBuffer(BODY_DUMP, (const char *) msg->body_data,msg->bodyLen); #endif if(SIP_FAILURE == status ) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Headers validation failed...\n")); } // Find out whether multiple SIP messages in this packet /* Disable this check for TCP. */ if ((!msg->isTcp) && (msg->content_len < msg->bodyLen)) { if (SIP_SUCCESS == sip_startline_parse(msg, start + msg->content_len, end, &nextIndex)) { ALERT(SIP_EVENT_MULTI_MSGS,SIP_EVENT_MULTI_MSGS_STR); } else { ALERT(SIP_EVENT_MISMATCH_CONTENT_LEN,SIP_EVENT_MISMATCH_CONTENT_LEN_STR); } } return status; } /******************************************************************** * Function: sip_freeMsg * * Frees a sip msg. * Media session information will be release if they are not used by dialog. * * Arguments: * SIPMsg * * The sip message to free. * * Returns: None * ********************************************************************/ void sip_freeMsg (SIPMsg *msg) { if (NULL == msg) return; if (NULL != msg->mediaSession) { if (SIP_SESSION_SAVED != msg->mediaSession->savedFlag) sip_freeMediaSession(msg->mediaSession); } } /******************************************************************** * Function: sip_freeMediaSession * * Frees a sip media session * * Arguments: * SIP_MediaSession * * The media session to free. * * Returns: None * ********************************************************************/ void sip_freeMediaSession (SIP_MediaSession *mediaSession) { SIP_MediaData *nextNode; SIP_MediaData *curNode = NULL; if (NULL != mediaSession) { curNode = mediaSession->medias; } while (NULL != curNode) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Clear media ip: %s, port: %d, number of port: %d\n", sfip_to_str(&curNode->maddress), curNode->mport, curNode->numPort )); nextNode = curNode->nextM; _dpd.snortFree(curNode, sizeof(SIP_MediaData), PP_SIP, PP_MEM_CATEGORY_SESSION); curNode = nextNode; } if (NULL != mediaSession) _dpd.snortFree(mediaSession, sizeof(SIP_MediaSession), PP_SIP, PP_MEM_CATEGORY_SESSION); } /******************************************************************** * Function: sip_freeMediaList * * Frees a sip media session list * * Arguments: * SIP_MediaList * The media session list to free. * * Returns: None * ********************************************************************/ void sip_freeMediaList (SIP_MediaList medias) { SIP_MediaSession *nextNode; SIP_MediaSession *curNode = medias; while (NULL != curNode) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Clean Media session default IP: %s, session ID: %u\n", sfip_to_str(&curNode->maddress_default), curNode->sessionID)); nextNode = curNode->nextS; sip_freeMediaSession(curNode); curNode = nextNode; } } #endif snort-2.9.20/src/dynamic-preprocessors/sip/sip_utils.c0000644000175000017500000001051414241076335021173 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions. * * 2/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sip_utils.h" /******************************************************************** * Function: SIP_IsEmptyStr() * * Checks if string is NULL, empty or just spaces. * String must be 0 terminated. * * Arguments: * char * - string to check * * Returns: * 1 if string is NULL, empty or just spaces * 0 otherwise * ********************************************************************/ int SIP_IsEmptyStr(char *str) { char *end; if (str == NULL) return 1; end = str + strlen(str); while ((str < end) && isspace((int)*str)) str++; if (str == end) return 1; return 0; } /* * Trim spaces non-destructively on both sides of string : '', \t, \n, \r * If string is empty return 0, otherwise 1 * Note: end point to the location start + length, * not necessary the real end of string if not end with \0 */ int SIP_TrimSP(const char *start, const char *end, char **new_start, char** new_end) { char *before; char *after; if (start >= end ) { *new_start = (char *)start; *new_end = *new_start; return 0; } before = (char *) start; // Trim the starting spaces while((before < end) && isspace((int)*before)) { before++; } // This is an empty string if (before == end) { *new_start = (char *)end; *new_end = *new_start; return 0; } // Trim the ending spaces after = (char *) end - 1; while((before < after) && isspace((int)*after)) { after--; } *new_start = before; *new_end = after + 1; return 1; } /******************************************************************** * Function: SIP_FindMethod() * * Find method in the method list by name * * Arguments: * SIPMethodlist - methods list to be searched, * char * - method name, * int - length of the method name * * Returns: * SIPMethodNode*- the founded method node, or NULL if not founded * ********************************************************************/ SIPMethodNode* SIP_FindMethod(SIPMethodlist methods, char* methodName, unsigned int length) { SIPMethodNode* method = NULL; method = methods; while (NULL != method) { if ((length == strlen(method->methodName))&& (strncasecmp(method->methodName, methodName, length) == 0)) { return method; } method = method->nextm; } return method; } /******************************************************************** * Function: strToHash() * * Calculate the hash value of a string * * Arguments: * char * - string to be hashed * int: length of the string * * Returns: * 1 if string is NULL, empty or just spaces * 0 otherwise * ********************************************************************/ uint32_t strToHash(const char *str, int length ) { uint32_t a,b,c,tmp; int i,j,k,l; a = b = c = 0; for (i=0,j=0;i 4) k=4; for (l=0;l&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@am__append_1 = ../libsf_dynamic_utils.la @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@am__append_2 = ../include/appdata_adjuster.c ../include/sfxhash.c ../include/sfhashfcn.c ../include/sfmemcap.c ../include/sfprimetable.c ../include/reg_test.h ../include/reg_test.c @BUILD_BUFFER_DUMP_TRUE@am__append_3 = \ @BUILD_BUFFER_DUMP_TRUE@sip_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@sip_buffer_dump.h subdir = src/dynamic-preprocessors/sip ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_sip_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ $(am__append_1) am__libsf_sip_preproc_la_SOURCES_DIST = spp_sip.c spp_sip.h \ sip_config.c sip_config.h sip_parser.c sip_parser.h \ sip_dialog.c sip_dialog.h sip_roptions.c sip_roptions.h \ sip_utils.c sip_utils.h sip_debug.h sip_paf.c sip_paf.h \ sip_buffer_dump.c sip_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = sip_buffer_dump.lo am_libsf_sip_preproc_la_OBJECTS = spp_sip.lo sip_config.lo \ sip_parser.lo sip_dialog.lo sip_roptions.lo sip_utils.lo \ sip_paf.lo $(am__objects_1) @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@am__objects_2 = appdata_adjuster.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfxhash.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfhashfcn.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfmemcap.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ sfprimetable.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_FALSE@ reg_test.lo @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_sip_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo sf_ip.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo $(am__objects_2) libsf_sip_preproc_la_OBJECTS = $(am_libsf_sip_preproc_la_OBJECTS) \ $(nodist_libsf_sip_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_sip_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_sip_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_sip_preproc_la_SOURCES) \ $(nodist_libsf_sip_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_sip_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_sip_preproc.la libsf_sip_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_sip_preproc_la_LIBADD = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ $(am__append_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_sip_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sf_ip.c \ @SO_WITH_STATIC_LIB_FALSE@ ../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@ $(am__append_2) libsf_sip_preproc_la_SOURCES = spp_sip.c spp_sip.h sip_config.c \ sip_config.h sip_parser.c sip_parser.h sip_dialog.c \ sip_dialog.h sip_roptions.c sip_roptions.h sip_utils.c \ sip_utils.h sip_debug.h sip_paf.c sip_paf.h $(am__append_3) EXTRA_DIST = \ sf_sip.vcxproj \ sf_sip.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/sip/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/sip/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_sip_preproc.la: $(libsf_sip_preproc_la_OBJECTS) $(libsf_sip_preproc_la_DEPENDENCIES) $(EXTRA_libsf_sip_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_sip_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_sip_preproc_la_OBJECTS) $(libsf_sip_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sf_ip.lo: ../include/sf_ip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_ip.lo `test -f '../include/sf_ip.c' || echo '$(srcdir)/'`../include/sf_ip.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c appdata_adjuster.lo: ../include/appdata_adjuster.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o appdata_adjuster.lo `test -f '../include/appdata_adjuster.c' || echo '$(srcdir)/'`../include/appdata_adjuster.c sfxhash.lo: ../include/sfxhash.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfxhash.lo `test -f '../include/sfxhash.c' || echo '$(srcdir)/'`../include/sfxhash.c sfhashfcn.lo: ../include/sfhashfcn.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfhashfcn.lo `test -f '../include/sfhashfcn.c' || echo '$(srcdir)/'`../include/sfhashfcn.c sfmemcap.lo: ../include/sfmemcap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfmemcap.lo `test -f '../include/sfmemcap.c' || echo '$(srcdir)/'`../include/sfmemcap.c sfprimetable.lo: ../include/sfprimetable.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfprimetable.lo `test -f '../include/sfprimetable.c' || echo '$(srcdir)/'`../include/sfprimetable.c reg_test.lo: ../include/reg_test.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o reg_test.lo `test -f '../include/reg_test.c' || echo '$(srcdir)/'`../include/reg_test.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/sip/sf_sip.vcxproj0000444000175000017500000004203214230012554021701 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {E8B8A0A4-51A2-4D42-87C9-8AA65BEA6940} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_sip.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_sip.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_sip.bsc true true Console .\Release\sf_sip.dll .\Release\sf_sip.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_sip.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_sip.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_sip.bsc true true Console .\Release\sf_sip.dll .\Release\sf_sip.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_sip.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_sip.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_sip.bsc true true true Console .\Debug\sf_sip.dll .\Debug\sf_sip.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_sip.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_sip.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_sip.bsc true true true Console .\Debug\sf_sip.dll .\Debug\sf_sip.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/sip/sip_dialog.c0000644000175000017500000006666114241076307021307 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for dialog management * Dialog management is the central part of SIP call flow analysis * * 3/15/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sip_dialog.h" #include "sip_parser.h" #include "sip_debug.h" #include "sf_ip.h" #include "spp_sip.h" #include "session_api.h" #include "stream_api.h" #include static void SIP_updateMedias(SIP_MediaSession *, SIP_MediaList *); static int SIP_compareMedias(SIP_MediaDataList , SIP_MediaDataList ); static int SIP_checkMediaChange(SIPMsg *sipMsg, SIP_DialogData *dialog); static int SIP_processRequest(SIPMsg *, SIP_DialogData *, SIP_DialogList *, SFSnortPacket *); static int SIP_processInvite(SIPMsg *, SIP_DialogData *, SIP_DialogList *); static int SIP_processACK(SIPMsg *, SIP_DialogData *, SIP_DialogList *, SFSnortPacket *); static int SIP_processResponse(SIPMsg *, SIP_DialogData *, SIP_DialogList *, SFSnortPacket *); static int SIP_ignoreChannels( SIP_DialogData *, SFSnortPacket *p); static SIP_DialogData* SIP_addDialog(SIPMsg *, SIP_DialogData *, SIP_DialogList *); static int SIP_deleteDialog(SIP_DialogData *, SIP_DialogList *); #ifdef DEBUG_MSGS void SIP_displayMedias(SIP_MediaList *dList); #endif /******************************************************************** * Function: SIP_processRequest() * * Based on the new received sip request message, update the dialog information. * Note: dialog is created through dialog * Arguments: * SIPMsg * - sip request message * SIP_DialogData* - dialog to be updated, * SFSnortPacket* - the packet * * Returns: * SIP_SUCCESS: request message has been processed correctly * SIP_FAILURE: request message has not been processed correctly ********************************************************************/ static int SIP_processRequest(SIPMsg *sipMsg, SIP_DialogData *dialog, SIP_DialogList *dList, SFSnortPacket *p) { SIPMethodsFlag methodFlag; int ret = SIP_SUCCESS; assert (NULL != sipMsg); /*If dialog not exist, create one */ if((NULL == dialog)&&(SIP_METHOD_CANCEL != sipMsg->methodFlag)) { dialog = SIP_addDialog(sipMsg, dList->head, dList); } methodFlag = sipMsg->methodFlag; sip_stats.requests[TOTAL_REQUESTS]++; if (methodFlag > 0) sip_stats.requests[methodFlag]++; switch (methodFlag) { case SIP_METHOD_INVITE: ret = SIP_processInvite(sipMsg, dialog, dList); break; case SIP_METHOD_CANCEL: if (NULL == dialog) return SIP_FAILURE; /*dialog can be deleted in the early state*/ if((SIP_DLG_EARLY == dialog->state)||(SIP_DLG_INVITING == dialog->state) || (SIP_DLG_CREATE == dialog->state)) SIP_deleteDialog(dialog, dList); break; case SIP_METHOD_ACK: SIP_processACK(sipMsg, dialog, dList, p); break; case SIP_METHOD_BYE: if(SIP_DLG_ESTABLISHED == dialog->state) dialog->state = SIP_DLG_TERMINATING; break; default: break; } return ret; } /******************************************************************** * Function: SIP_processInvite() * * Based on the new received sip invite request message, update the dialog information. * Note: dialog is created through dialog * Arguments: * SIPMsg * - sip request message * SIP_DialogData* - dialog to be updated, * SIP_DialogList*- dialog list * Returns: * SIP_SUCCESS: * SIP_FAILURE: ********************************************************************/ static int SIP_processInvite(SIPMsg *sipMsg, SIP_DialogData *dialog, SIP_DialogList *dList) { int ret = SIP_SUCCESS; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Processing invite, dialog state %d \n", dialog->state );); if (NULL == dialog) return SIP_FAILURE; /*Check for the invite replay attack: authenticated invite without challenge*/ // check whether this invite has authorization information if ((SIP_DLG_AUTHENCATING != dialog->state) && (NULL != sipMsg ->authorization)) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Dialog state code: %u\n", dialog->status_code)); ALERT(SIP_EVENT_AUTH_INVITE_REPLAY_ATTACK,SIP_EVENT_AUTH_INVITE_REPLAY_ATTACK_STR); return SIP_FAILURE; } if (SIP_DLG_ESTABLISHED == dialog->state) { /* this is the case of re-INVITE*/ // create a temporary new dialog before the current dialog dialog = SIP_addDialog(sipMsg, dialog, dList); dialog->state = SIP_DLG_REINVITING; return SIP_SUCCESS; } /*Check for the fake busy attack: change media session before dialog established*/ else if((SIP_DLG_INVITING == dialog->state) || (SIP_DLG_EARLY == dialog->state) || (SIP_DLG_REINVITING == dialog->state)|| (SIP_DLG_AUTHENCATING == dialog->state)) { ret = SIP_checkMediaChange(sipMsg, dialog); if (SIP_FAILURE == ret) ALERT(SIP_EVENT_AUTH_INVITE_DIFF_SESSION,SIP_EVENT_AUTH_INVITE_DIFF_SESSION_STR); SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); } else if (SIP_DLG_TERMINATED == dialog->state) { SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); } dialog->state = SIP_DLG_INVITING; return ret; } /******************************************************************** * Function: SIP_processACK() * * Based on the new received sip ACK request message, update the dialog information. * Note: dialog is created through dialog * Arguments: * SIPMsg * - sip request message * SIP_DialogData* - dialog to be updated, * SIP_DialogList* - dialog list * SFSnortPacket* - the packet * Returns: * SIP_SUCCESS: * SIP_FAILURE: ********************************************************************/ static int SIP_processACK(SIPMsg *sipMsg, SIP_DialogData *dialog, SIP_DialogList *dList, SFSnortPacket *p) { if (NULL == dialog) return SIP_FAILURE; if (SIP_DLG_ESTABLISHED == dialog->state) { if ((SIP_METHOD_INVITE == dialog->creator)&&(SIP_checkMediaChange(sipMsg, dialog) == SIP_FAILURE)) { SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); SIP_ignoreChannels(dialog, p); sipMsg->mediaUpdated = 1; } } return SIP_SUCCESS; } /******************************************************************** * Function: SIP_processResponse() * * Based on the new received sip response message, update the dialog information. * * Arguments: * SIPMsg * - sip response message * SIP_DialogData* - dialog to be updated, * SFSnortPacket* - the packet * * Returns: * SIP_SUCCESS: * SIP_FAILURE: ********************************************************************/ static int SIP_processResponse(SIPMsg *sipMsg, SIP_DialogData *dialog, SIP_DialogList *dList, SFSnortPacket *p) { int statusType; SIP_DialogData *currDialog = dialog; assert (NULL != sipMsg); statusType = sipMsg->status_code / 100; sip_stats.responses[TOTAL_RESPONSES]++; if (statusType < NUM_OF_RESPONSE_TYPES) sip_stats.responses[statusType]++; if(NULL == dialog) return SIP_FAILURE; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Processing response, dialog state %d \n", dialog->state );); if(sipMsg->status_code > 0) dialog->status_code = sipMsg->status_code; switch (statusType) { case 0: break; case RESPONSE1XX: if (SIP_DLG_CREATE == currDialog->state) currDialog->state = SIP_DLG_EARLY; /*183 session progress can have SDP body for which we need to set mediaUpdated flag so that AppId can create pinhole for RTP/RTCP media session*/ if ((183 == sipMsg->status_code) && (SIP_METHOD_INVITE == currDialog->creator) && (SIP_checkMediaChange(sipMsg, dialog) == SIP_FAILURE)) { SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); SIP_ignoreChannels(currDialog, p); sipMsg->mediaUpdated = 1; } else { SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); } break; case RESPONSE2XX: if (SIP_DLG_REINVITING == currDialog->state) { SIP_deleteDialog(currDialog->nextD, dList); if (SIP_checkMediaChange(sipMsg, dialog) == SIP_FAILURE) { SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); SIP_ignoreChannels(currDialog, p); sipMsg->mediaUpdated = 1; } currDialog->state = SIP_DLG_ESTABLISHED; } else if (SIP_DLG_TERMINATING == currDialog->state) { SIP_deleteDialog(currDialog, dList); return SIP_SUCCESS; } else { if ((SIP_METHOD_INVITE == currDialog->creator)&& (SIP_checkMediaChange(sipMsg, dialog) == SIP_FAILURE)) { SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); SIP_ignoreChannels(currDialog, p); sipMsg->mediaUpdated = 1; } currDialog->state = SIP_DLG_ESTABLISHED; } break; case RESPONSE3XX: case RESPONSE4XX: case RESPONSE5XX: case RESPONSE6XX: // If authentication is required if((401 == sipMsg->status_code) || (407 == sipMsg->status_code)) { currDialog->state = SIP_DLG_AUTHENCATING; } /*Failed re-Invite will resume to the original state*/ else if(SIP_DLG_REINVITING == currDialog->state) { SIP_deleteDialog(currDialog, dList); } else currDialog->state = SIP_DLG_TERMINATED; break; default: break; } return SIP_SUCCESS; } /******************************************************************** * Function: SIP_checkMediaChange() * * Based on the new received sip invite request message, check whether SDP has been changed * * Arguments: * SIPMsg * - sip request message * SIP_DialogData* - dialog to be updated, * * Returns: * SIP_SUCCESS: media not changed * SIP_FAILURE: media changed ********************************************************************/ static int SIP_checkMediaChange(SIPMsg *sipMsg, SIP_DialogData *dialog) { SIP_MediaSession *medias; // Compare the medias (SDP part) if (NULL == sipMsg->mediaSession) return SIP_SUCCESS; medias = dialog->mediaSessions; while(NULL != medias) { if (sipMsg->mediaSession->sessionID == medias->sessionID) break; medias = medias->nextS; } if (NULL == medias) { // Can't find the media session by ID, SDP has been changed. DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Can't find the media data, ID: %u\n", sipMsg->mediaSession->sessionID );); return SIP_FAILURE; } // The media content has been changed if (0 != SIP_compareMedias(medias->medias, sipMsg->mediaSession->medias)) { // Can't find the media session by ID, SDP has been changed. DEBUG_WRAP(DebugMessage(DEBUG_SIP, "The media data is different!\n");); return SIP_FAILURE; } return SIP_SUCCESS; } /******************************************************************** * Function: SIP_ignoreChannels * * Ignore the channels in the current dialog: for a dialog,there will be media * sessions, one from each side of conversation * * Arguments: * SIP_DialogData * - the current dialog * * * Returns: * SIP_SUCCESS: the channel has been ignored * SIP_FAILURE: the channel has not been ignored * ********************************************************************/ static int SIP_ignoreChannels( SIP_DialogData *dialog, SFSnortPacket *p) { SIP_MediaData *mdataA,*mdataB; if (0 == sip_eval_config->ignoreChannel) return SIP_FAILURE; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Ignoring the media data in Dialog: %u\n", dialog->dlgID.callIdHash);); // check the first media session if (NULL == dialog->mediaSessions) return SIP_FAILURE; // check the second media session if (NULL == dialog->mediaSessions->nextS) return SIP_FAILURE; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Ignoring the media sessions ID: %u and %u\n", dialog->mediaSessions->sessionID, dialog->mediaSessions->nextS->sessionID);); mdataA = dialog->mediaSessions->medias; mdataB = dialog->mediaSessions->nextS->medias; sip_stats.ignoreSessions++; while((NULL != mdataA)&&(NULL != mdataB)) { void *ssn; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Ignoring channels Source IP: %s Port: %u\n", sfip_to_str(&mdataA->maddress), mdataA->mport);); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Ignoring channels Destine IP: %s Port: %u\n", sfip_to_str(&mdataB->maddress), mdataB->mport);); /* Call into Streams to mark data channel as something to ignore. */ #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t cid = GET_SFOUTER_IPH_PROTOID(p, pkt_header); #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t sAsId = p->pkt_header->address_space_id_src; uint16_t dAsId = p->pkt_header->address_space_id_dst; ssn = _dpd.sessionAPI->get_session_ptr_from_ip_port(&mdataA->maddress,mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, 0, 0, sAsId, dAsId, cid); #else ssn = _dpd.sessionAPI->get_session_ptr_from_ip_port(&mdataA->maddress,mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, 0, 0, p->pkt_header->address_space_id, cid); #endif #else ssn = _dpd.sessionAPI->get_session_ptr_from_ip_port(&mdataA->maddress,mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, 0, 0, 0, cid); #endif #else /* No carrier id support */ #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t sAsId = p->pkt_header->address_space_id_src; uint16_t dAsId = p->pkt_header->address_space_id_dst; ssn = _dpd.sessionAPI->get_session_ptr_from_ip_port(&mdataA->maddress,mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, 0, 0, sAsId, dAsId); #else ssn = _dpd.sessionAPI->get_session_ptr_from_ip_port(&mdataA->maddress,mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, 0, 0, p->pkt_header->address_space_id); #endif #else ssn = _dpd.sessionAPI->get_session_ptr_from_ip_port(&mdataA->maddress,mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, 0, 0, 0); #endif #endif if ( _dpd.sessionAPI->is_session_verified( ssn ) ) { _dpd.sessionAPI->set_ignore_direction(ssn, SSN_DIR_BOTH); } else { _dpd.sessionAPI->ignore_session(p, &mdataA->maddress, mdataA->mport, &mdataB->maddress, mdataB->mport, IPPROTO_UDP, PP_SIP, SSN_DIR_BOTH, 0 /* Not permanent */, &p->expectedSession ); } sip_stats.ignoreChannels++; mdataA = mdataA->nextM; mdataB = mdataB->nextM; } return SIP_SUCCESS; } /******************************************************************** * Function: SIP_compareMedias * * Compare two media list * * Arguments: * SIPMsg * - the message used to create a dialog * SIP_DialogData * - the current dialog location * SIP_DialogList * - the dialogs to be added. * * * Returns: * 1: not the same * 0: the same * ********************************************************************/ static int SIP_compareMedias(SIP_MediaDataList mlistA, SIP_MediaDataList mlistB ) { SIP_MediaData *mdataA,*mdataB; mdataA = mlistA; mdataB = mlistB; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Compare the media data \n");); while((NULL != mdataA) && (NULL != mdataB)) { if(sfip_compare(&mdataA->maddress, &mdataB->maddress) != SFIP_EQUAL) break; if((mdataA->mport != mdataB->mport)|| (mdataA->numPort != mdataB->numPort)) break; mdataA = mdataA->nextM; mdataB = mdataB->nextM; } if((NULL == mdataA) && (NULL == mdataB)) return 0; else return 1; } /******************************************************************** * Function: SIP_updateMedias() * * Based on the new received media session information, update the media list. * If not in the current list, created one and add it to the head. * * Arguments: * SIP_MediaSession* - media session * SIP_MediaList* - media session list to be updated, * * Returns: * ********************************************************************/ static void SIP_updateMedias(SIP_MediaSession *mSession, SIP_MediaList *dList) { SIP_MediaSession *currSession, *preSession = NULL; if(NULL == mSession) return; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Updating session id: %u\n", mSession->sessionID)); mSession->savedFlag = SIP_SESSION_SAVED; // Find out the media session based on session id currSession = *dList; while(NULL != currSession) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Session id: %u\n", currSession->sessionID)); if(currSession->sessionID == mSession->sessionID) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Found Session id: %u\n", currSession->sessionID)); break; } preSession = currSession; currSession = currSession->nextS; } // if this is a new session data, add to the list head if (NULL == currSession) { mSession->nextS = *dList; *dList = mSession; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Add Session id: %u\n", mSession->sessionID)); } else { // if this session needs to be updated DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Update Session id: %u\n", mSession->sessionID)); mSession->nextS = currSession->nextS; // if this is the header, update the new header if (NULL == preSession) *dList = mSession; else preSession->nextS = mSession; // Clear the old session currSession->nextS = NULL; sip_freeMediaSession(currSession); } // Display the final media session #ifdef DEBUG_MSGS SIP_displayMedias(dList); #endif return; } #ifdef DEBUG_MSGS void SIP_displayMedias(SIP_MediaList *dList) { SIP_MediaSession *currSession; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Updated Session information------------\n")); currSession = *dList; while(NULL != currSession) { SIP_MediaData *mdata; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Session id: %u\n", currSession->sessionID)); mdata = currSession->medias; while(NULL != mdata) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Media IP: %s, port: %u, number of ports %u\n", sfip_to_str(&mdata->maddress), mdata->mport, mdata->numPort)); mdata = mdata->nextM; } currSession = currSession->nextS; } DEBUG_WRAP(DebugMessage(DEBUG_SIP, "End of Session information------------\n")); } #endif /******************************************************************** * Function: SIP_addDialog * * Add a sip dialog before the current dialog * * Arguments: * SIPMsg * - the message used to create a dialog * SIP_DialogData * - the current dialog location * SIP_DialogList * - the dialogs to be added. * * * Returns: None * ********************************************************************/ static SIP_DialogData* SIP_addDialog(SIPMsg *sipMsg, SIP_DialogData *currDialog, SIP_DialogList *dList) { SIP_DialogData* dialog; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Add Dialog id: %u, From: %u, To: %u, status code: %u\n", sipMsg->dlgID.callIdHash,sipMsg->dlgID.fromTagHash,sipMsg->dlgID.toTagHash, sipMsg->status_code)); sip_stats.dialogs++; dialog = (SIP_DialogData *) _dpd.snortAlloc(1, sizeof(SIP_DialogData), PP_SIP, PP_MEM_CATEGORY_SESSION); if (NULL == dialog) return NULL; // Add to the head dialog->nextD = currDialog; if(NULL != currDialog) { dialog->prevD = currDialog->prevD; if (NULL != currDialog->prevD) currDialog->prevD->nextD = dialog; else dList->head = dialog; // become the head currDialog->prevD = dialog; } else { // The first dialog dialog->prevD = NULL; dList->head = dialog; } dialog->dlgID = sipMsg->dlgID; dialog->creator = sipMsg->methodFlag; dialog->state = SIP_DLG_CREATE; SIP_updateMedias(sipMsg->mediaSession, &dialog->mediaSessions); dList->num_dialogs++; return dialog; } /******************************************************************** * Function: SIP_deleteDialog * * Delete a sip dialog from the list * * Arguments: * SIP_DialogData * - the current dialog to be deleted * SIP_DialogList * - the dialog list. * * Returns: None * ********************************************************************/ static int SIP_deleteDialog(SIP_DialogData *currDialog, SIP_DialogList *dList) { if ((NULL == currDialog)||(NULL == dList)) return SIP_FAILURE; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Delete Dialog id: %u, From: %u, To: %u \n", currDialog->dlgID.callIdHash,currDialog->dlgID.fromTagHash,currDialog->dlgID.toTagHash)); // If this is the header if(NULL == currDialog->prevD) { if(NULL != currDialog->nextD) currDialog->nextD->prevD = NULL; dList->head = currDialog->nextD; } else { currDialog->prevD->nextD = currDialog->nextD; if(NULL != currDialog->nextD) currDialog->nextD->prevD = currDialog->prevD; } sip_freeMediaList(currDialog->mediaSessions); _dpd.snortFree(currDialog, sizeof(SIP_DialogData), PP_SIP, PP_MEM_CATEGORY_SESSION); if( dList->num_dialogs > 0) dList->num_dialogs--; return SIP_SUCCESS; } /********************************************************************* * Update appId sip detector with parsed SIP message and dialog * * Arguments: * SFSnortPacket * - pointer to packet structure * SIPMsg * - pointer to parserd SIP messgage * SIPData * - pointer to SIP session * * Returns: * None * *********************************************************************/ static void sip_update_appid(SFSnortPacket *p, const SIPMsg *sipMsg, const SIP_DialogData *dialog) { SipHeaders hdrs; SipDialog dlg; SipEventData sipEventData; hdrs.callid = sipMsg->call_id; hdrs.callidLen = sipMsg->callIdLen; hdrs.methodFlag = sipMsg->methodFlag; hdrs.userAgent = sipMsg->userAgent; hdrs.userAgentLen = sipMsg->userAgentLen; hdrs.server = sipMsg->server; hdrs.serverLen = sipMsg->serverLen; hdrs.userName = sipMsg->userName; hdrs.userNameLen = sipMsg->userNameLen; hdrs.from = sipMsg->from; hdrs.fromLen= sipMsg->fromLen; sipEventData.headers = &hdrs; if (dialog) { dlg.state = dialog->state; dlg.mediaSessions = dialog->mediaSessions; dlg.mediaUpdated = sipMsg->mediaUpdated; sipEventData.dialog = &dlg; } else { sipEventData.dialog = NULL; } sipEventData.packet = p; if (_dpd.streamAPI->service_event_publish(PP_SIP, p->stream_session, SIP_EVENT_TYPE_SIP_DIALOG, &sipEventData) == false) _dpd.errMsg("failed to publish to SIP_DIALOG\n"); } /******************************************************************** * Function: SIP_updateDialog() * * Based on the new received sip message, update the dialog information. * If not in the current list, created one and add it to the head. * * Arguments: * SIPMsg * - sip message * SIP_DialogList* - dialog list to be updated, * * Returns: * SIP_SUCCESS: dialog has been updated * SIP_FAILURE: dialog has not been updated ********************************************************************/ int SIP_updateDialog(SIPMsg *sipMsg, SIP_DialogList *dList, SFSnortPacket *p) { SIP_DialogData* dialog; SIP_DialogData* oldDialog = NULL; int ret; if ((NULL == sipMsg)||(0 == sipMsg->dlgID.callIdHash)) return SIP_FAILURE; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Updating Dialog id: %u, From: %u, To: %u\n", sipMsg->dlgID.callIdHash,sipMsg->dlgID.fromTagHash,sipMsg->dlgID.toTagHash)); dialog = dList->head; /*Find out the dialog in the dialog list*/ while(NULL != dialog) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Dialog id: %u, From: %u, To: %u\n", dialog->dlgID.callIdHash,dialog->dlgID.fromTagHash,dialog->dlgID.toTagHash)); if (sipMsg->dlgID.callIdHash == dialog->dlgID.callIdHash) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Found Dialog id: %u, From: %u, To: %u\n", dialog->dlgID.callIdHash,dialog->dlgID.fromTagHash,dialog->dlgID.toTagHash)); break; } oldDialog = dialog; dialog = dialog->nextD; } /*If the number of dialogs exceeded, release the oldest one*/ if((dList->num_dialogs >= sip_eval_config->maxNumDialogsInSession) && (!dialog)) { ALERT(SIP_EVENT_MAX_DIALOGS_IN_A_SESSION, SIP_EVENT_MAX_DIALOGS_IN_A_SESSION_STR); SIP_deleteDialog(oldDialog, dList); } /*Update the dialog information*/ if (sipMsg->status_code == 0) ret = SIP_processRequest(sipMsg, dialog, dList, p); else if (sipMsg->status_code > 0) ret = SIP_processResponse(sipMsg, dialog, dList, p); else ret = SIP_FAILURE; for (dialog = dList->head; dialog; dialog = dialog->nextD) { if (sipMsg->dlgID.callIdHash == dialog->dlgID.callIdHash) break; } sip_update_appid(p, sipMsg, dialog); return ret; } /******************************************************************** * Function: sip_freeDialogs * * Frees a sip dialog * * Arguments: * SIP_DialogList * The dialogs to free. * * Returns: None * ********************************************************************/ void sip_freeDialogs (SIP_DialogList *list) { SIP_DialogData *nextNode; SIP_DialogData *curNode = list->head; while (NULL != curNode) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "*Clean Dialog creator: 0x%x, id: %u, From: %u, To: %u, State: %d\n", curNode->creator, curNode->dlgID.callIdHash,curNode->dlgID.fromTagHash,curNode->dlgID.toTagHash,curNode->state)); nextNode = curNode->nextD; sip_freeMediaList(curNode->mediaSessions); _dpd.snortFree(curNode, sizeof(SIP_DialogData), PP_SIP, PP_MEM_CATEGORY_SESSION); curNode = nextNode; } } snort-2.9.20/src/dynamic-preprocessors/sip/sip_paf.h0000644000175000017500000000252714241076321020606 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __SIP_PAF_H__ #define __SIP_PAF_H__ #include "sfPolicy.h" #include "sfPolicyUserData.h" #ifdef TARGET_BASED void register_sip_paf_service(struct _SnortConfig *sc, int16_t app, tSfPolicyId policy); #endif void register_sip_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy); #endif snort-2.9.20/src/dynamic-preprocessors/sip/sip_config.h0000644000175000017500000000663614241076304021313 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 2/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef _SIP_CONFIG_H_ #define _SIP_CONFIG_H_ #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "sip_debug.h" #include "sip_common.h" #define SIP_NAME "sip" #define SIP_METHOD_DEFAULT 0x003f #define SIP_METHOD_ALL 0xffffffff /* * Header fields and processing functions */ typedef struct _SIPMethod { char *name; SIPMethodsFlag methodFlag; }SIPMethod; extern SIPMethod StandardMethods[]; typedef struct _sipMethodlistNode { char *methodName; int methodLen; SIPMethodsFlag methodFlag; struct _sipMethodlistNode* nextm; } SIPMethodNode; typedef SIPMethodNode * SIPMethodlist; /* * One of these structures is kept for each configured * server port. */ typedef struct _sipPortlistNode { uint16_t server_port; struct _sipPortlistNode* nextp; } SIPPortNode; /* * SIP preprocessor configuration. * * disabled: Whether or not to disable SIP PP. * maxNumSessions: Maximum amount of run-time memory * ports: Which ports to check for SIP messages * methods: Which methods to check * maxUriLen: Maximum requst_URI size * maxCallIdLen: Maximum call_ID size. * maxRequestNameLen: Maximum length of request name in the CSeqID. * maxFromLen: Maximum From field size * maxToLen: Maximum To field size * maxViaLen: Maximum Via field size * maxContactLen: Maximum Contact field size * maxContentLen: Maximum Content length * ignoreChannel: Whether to ignore media channels found by SIP PP */ typedef struct _sipConfig { uint8_t disabled; uint32_t maxNumSessions; uint32_t maxNumDialogsInSession; uint8_t ports[MAXPORTS/8]; uint32_t methodsConfig; SIPMethodlist methods; uint16_t maxUriLen; uint16_t maxCallIdLen; uint16_t maxRequestNameLen; uint16_t maxFromLen; uint16_t maxToLen; uint16_t maxViaLen; uint16_t maxContactLen; uint16_t maxContentLen; uint8_t ignoreChannel; int ref_count; } SIPConfig; /******************************************************************** * Public function prototypes ********************************************************************/ void SIP_FreeConfig(SIPConfig *); void ParseSIPArgs(SIPConfig *, u_char*); SIPMethodNode* SIP_AddUserDefinedMethod(char *, uint32_t *, SIPMethodlist*); #endif snort-2.9.20/src/dynamic-preprocessors/sip/sip_buffer_dump.h0000644000175000017500000000347014241076301022332 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file sip_buffer_dump.h ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during SIP inspection. */ #ifndef __SIP_BUFFER_DUMP_H__ #define __SIP_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { HEADER_DUMP, BODY_DUMP, STATUS_CODE_DUMP, METHOD_DUMP, URI_DUMP, CALL_ID_DUMP, CSEQ_NAME_DUMP, FROM_DUMP, FROM_TAG_DUMP, USER_NAME_DUMP, TO_DUMP, TO_TAG_DUMP, VIA_DUMP, CONTACT_DUMP, USER_AGENT_DUMP, SERVER_DUMP, } SIP_BUFFER_DUMP; void dumpBuffer(SIP_BUFFER_DUMP type, const char *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getSIPBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/sip/sip_parser.h0000644000175000017500000000314314241076323021331 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 2/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef _SIP_PARSER_H_ #define _SIP_PARSER_H_ #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "sip_debug.h" #include "spp_sip.h" #include "sf_ip.h" int sip_parse(SIPMsg *, const char *, char *); void sip_freeMsg (SIPMsg *msg); void sip_freeMediaSession (SIP_MediaSession*); void sip_freeMediaList (SIP_MediaList medias); #endif snort-2.9.20/src/dynamic-preprocessors/sip/spp_sip.h0000644000175000017500000002012414241076346020642 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * spp_sip.h: Definitions, structs, function prototype(s) for * the SIP preprocessor. * Author: Hui Cao */ #ifndef SPP_SIP_H #define SPP_SIP_H #include #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "sip_roptions.h" #include "sf_ip.h" /* Convert port value into an index for the sip_config->ports array */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) /* * Boolean values. */ #define SIP_TRUE (1) #define SIP_FALSE (0) #define SIP_STATUS_CODE_LEN (3) #define SIP_CONTENT_LEN (5) /* * Error codes. */ #define SIP_SUCCESS (1) #define SIP_FAILURE (0) #define SIP_SESSION_SAVED (1) #define SIP_SESSION_INIT (0) typedef struct _SIP_DialogID { uint32_t callIdHash; uint32_t fromTagHash; uint32_t toTagHash; } SIP_DialogID; typedef struct _SIP_DialogData { SIP_DialogID dlgID; SIP_DialogState state; SIPMethodsFlag creator; uint16_t status_code; SIP_MediaList mediaSessions; struct _SIP_DialogData *nextD; struct _SIP_DialogData *prevD; } SIP_DialogData; typedef struct _SIP_DialogList { SIP_DialogData* head; uint32_t num_dialogs; }SIP_DialogList; /* * Per-session data block containing current state * of the SIP preprocessor for the session. * * state_flags: Bit vector describing the current state of the * session. */ typedef struct _sipData { uint32_t state_flags; tSfPolicyId policy_id; SIP_DialogList dialogs; SIP_Roptions ropts; tSfPolicyUserContextId config; } SIPData; typedef struct _SIPMsg { uint16_t headerLen; uint16_t methodLen; SIPMethodsFlag methodFlag; uint16_t status_code; uint16_t uriLen; uint16_t callIdLen; uint16_t cseqNameLen; uint16_t fromLen; uint16_t fromTagLen; uint16_t toLen; uint16_t toTagLen; uint16_t viaLen; uint16_t contactLen; uint16_t bodyLen; uint16_t contentTypeLen; uint32_t content_len; SIP_DialogID dlgID; SIP_MediaSession *mediaSession; char *authorization; const uint8_t *header; const uint8_t *body_data; /* Set to NULL if not applicable */ uint64_t cseqnum; uint16_t userNameLen; uint16_t userAgentLen; uint16_t serverLen; bool mediaUpdated; /* nothing after this point is zeroed ...*/ /*Input parameters*/ unsigned char isTcp; char *method; char *uri; char *call_id; char *cseqName; char *from; char *from_tag; char *to; char *to_tag; char *via; char *contact; char *content_type; char *content_encode; const char *userAgent; const char *userName; const char *server; } SIPMsg; #define SIPMSG_ZERO_LEN offsetof(SIPMsg, isTcp) /* * Generator id. Define here the same as the official registry * in generators.h */ #define GENERATOR_SPP_SIP 140 /* Ultimately calls SnortEventqAdd */ /* Arguments are: gid, sid, rev, classification, priority, message, rule_info */ #define ALERT(x,y) { _dpd.alertAdd(GENERATOR_SPP_SIP, x, 1, 0, 3, y, 0 ); sip_stats.events++; } /* * SIP preprocessor alert types. */ #define SIP_EVENT_MAX_SESSIONS 1 #define SIP_EVENT_EMPTY_REQUEST_URI 2 #define SIP_EVENT_BAD_URI 3 #define SIP_EVENT_EMPTY_CALL_ID 4 #define SIP_EVENT_BAD_CALL_ID 5 #define SIP_EVENT_BAD_CSEQ_NUM 6 #define SIP_EVENT_BAD_CSEQ_NAME 7 #define SIP_EVENT_EMPTY_FROM 8 #define SIP_EVENT_BAD_FROM 9 #define SIP_EVENT_EMPTY_TO 10 #define SIP_EVENT_BAD_TO 11 #define SIP_EVENT_EMPTY_VIA 12 #define SIP_EVENT_BAD_VIA 13 #define SIP_EVENT_EMPTY_CONTACT 14 #define SIP_EVENT_BAD_CONTACT 15 #define SIP_EVENT_BAD_CONTENT_LEN 16 #define SIP_EVENT_MULTI_MSGS 17 #define SIP_EVENT_MISMATCH_CONTENT_LEN 18 #define SIP_EVENT_INVALID_CSEQ_NAME 19 #define SIP_EVENT_AUTH_INVITE_REPLAY_ATTACK 20 #define SIP_EVENT_AUTH_INVITE_DIFF_SESSION 21 #define SIP_EVENT_BAD_STATUS_CODE 22 #define SIP_EVENT_EMPTY_CONTENT_TYPE 23 #define SIP_EVENT_INVALID_VERSION 24 #define SIP_EVENT_MISMATCH_METHOD 25 #define SIP_EVENT_UNKOWN_METHOD 26 #define SIP_EVENT_MAX_DIALOGS_IN_A_SESSION 27 /* * SIP preprocessor alert strings. */ #define SIP_EVENT_MAX_SESSIONS_STR "(spp_sip) Maximum sessions reached" #define SIP_EVENT_EMPTY_REQUEST_URI_STR "(spp_sip) Empty request URI" #define SIP_EVENT_BAD_URI_STR "(spp_sip) URI is too long" #define SIP_EVENT_EMPTY_CALL_ID_STR "(spp_sip) Empty call-Id" #define SIP_EVENT_BAD_CALL_ID_STR "(spp_sip) Call-Id is too long" #define SIP_EVENT_BAD_CSEQ_NUM_STR "(spp_sip) CSeq number is too large or negative" #define SIP_EVENT_BAD_CSEQ_NAME_STR "(spp_sip) Request name in CSeq is too long" #define SIP_EVENT_EMPTY_FROM_STR "(spp_sip) Empty From header" #define SIP_EVENT_BAD_FROM_STR "(spp_sip) From header is too long" #define SIP_EVENT_EMPTY_TO_STR "(spp_sip) Empty To header" #define SIP_EVENT_BAD_TO_STR "(spp_sip) To header is too long" #define SIP_EVENT_EMPTY_VIA_STR "(spp_sip) Empty Via header" #define SIP_EVENT_BAD_VIA_STR "(spp_sip) Via header is too long" #define SIP_EVENT_EMPTY_CONTACT_STR "(spp_sip) Empty Contact" #define SIP_EVENT_BAD_CONTACT_STR "(spp_sip) Contact is too long" #define SIP_EVENT_BAD_CONTENT_LEN_STR "(spp_sip) Content length is too large or negative" #define SIP_EVENT_MULTI_MSGS_STR "(spp_sip) Multiple SIP messages in a packet" #define SIP_EVENT_MISMATCH_CONTENT_LEN_STR "(spp_sip) Content length mismatch" #define SIP_EVENT_INVALID_CSEQ_NAME_STR "(spp_sip) Request name is invalid" #define SIP_EVENT_AUTH_INVITE_REPLAY_ATTACK_STR "(spp_sip) Invite replay attack" #define SIP_EVENT_AUTH_INVITE_DIFF_SESSION_STR "(spp_sip) Illegal session information modification" #define SIP_EVENT_BAD_STATUS_CODE_STR "(spp_sip) Response status code is not a 3 digit number" #define SIP_EVENT_EMPTY_CONTENT_TYPE_STR "(spp_sip) Empty Content-type header" #define SIP_EVENT_INVALID_VERSION_STR "(spp_sip) SIP version is invalid" #define SIP_EVENT_MISMATCH_METHOD_STR "(spp_sip) Mismatch in METHOD of request and the CSEQ header" #define SIP_EVENT_UNKOWN_METHOD_STR "(spp_sip) Method is unknown" #define SIP_EVENT_MAX_DIALOGS_IN_A_SESSION_STR "(spp_sip) Maximum dialogs within a session reached" #define MAX_STAT_CODE 999 #define MIN_STAT_CODE 100 #define TOTAL_RESPONSES 0 #define RESPONSE1XX 1 #define RESPONSE2XX 2 #define RESPONSE3XX 3 #define RESPONSE4XX 4 #define RESPONSE5XX 5 #define RESPONSE6XX 6 #define NUM_OF_RESPONSE_TYPES 10 #define TOTAL_REQUESTS 0 #define NUM_OF_REQUEST_TYPES SIP_METHOD_USER_DEFINE_MAX typedef struct _SIP_Stats { uint64_t sessions; uint64_t events; uint64_t dialogs; uint64_t requests[NUM_OF_REQUEST_TYPES]; uint64_t responses[NUM_OF_RESPONSE_TYPES]; uint64_t ignoreChannels; uint64_t ignoreSessions; } SIP_Stats; extern SIP_Stats sip_stats; extern SIPConfig *sip_eval_config; extern tSfPolicyUserContextId sip_config; /* Prototypes for public interface */ void SetupSIP(void); SIPConfig *getParsingSIPConfig(struct _SnortConfig *); #endif /* SPP_SIP_H */ snort-2.9.20/src/dynamic-preprocessors/sip/Makefile.am0000555000175000017500000000245614230012554021046 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_sip_preproc.la libsf_sip_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_sip_preproc_la_LIBADD = ../libsf_dynamic_preproc.la if BUILD_SNORT_RELOAD libsf_sip_preproc_la_LIBADD += ../libsf_dynamic_utils.la endif else nodist_libsf_sip_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sf_ip.c \ ../include/sfPolicyUserData.c if BUILD_SNORT_RELOAD nodist_libsf_sip_preproc_la_SOURCES += ../include/appdata_adjuster.c ../include/sfxhash.c ../include/sfhashfcn.c ../include/sfmemcap.c ../include/sfprimetable.c ../include/reg_test.h ../include/reg_test.c endif endif libsf_sip_preproc_la_SOURCES = \ spp_sip.c \ spp_sip.h \ sip_config.c \ sip_config.h \ sip_parser.c \ sip_parser.h \ sip_dialog.c \ sip_dialog.h \ sip_roptions.c \ sip_roptions.h \ sip_utils.c \ sip_utils.h \ sip_debug.h \ sip_paf.c \ sip_paf.h if BUILD_BUFFER_DUMP libsf_sip_preproc_la_SOURCES += \ sip_buffer_dump.c \ sip_buffer_dump.h endif EXTRA_DIST = \ sf_sip.vcxproj \ sf_sip.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/sip/sip_utils.h0000644000175000017500000000306614241076344021204 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions. * * 2/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef SIP_UTILS_H_ #define SIP_UTILS_H_ #include "sf_snort_packet.h" #include "sip_config.h" #include "sfhashfcn.h" int SIP_IsEmptyStr(char *); int SIP_TrimSP(const char *, const char *, char **, char** ); SIPMethodNode * SIP_FindMethod(SIPMethodlist, char*, unsigned int); uint32_t strToHash(const char *, int ); #endif /* SIP_UTILS_H_ */ snort-2.9.20/src/dynamic-preprocessors/sip/sip_dialog.h0000644000175000017500000000267214241076315021303 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions. * * 3/15/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef SIP_DIALOG_H_ #define SIP_DIALOG_H_ #include "spp_sip.h" int SIP_updateDialog(SIPMsg *sipMsg, SIP_DialogList *dList, SFSnortPacket *p); void sip_freeDialogs (SIP_DialogList *list); #endif /* SIP_DIALOG_H_ */ snort-2.9.20/src/dynamic-preprocessors/sip/sip_paf.c0000644000175000017500000002553114241076317020606 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include #include "sf_types.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "sip_paf.h" #include /* State tracker for SIP PAF */ typedef enum _SIPPafState { SIP_PAF_START_STATE, /* default state. continue until LF */ SIP_PAF_CONTENT_LEN_CMD, /* searching Content-Length header */ SIP_PAF_CONTENT_LEN_CONVERT, /* parse the literal content length */ SIP_PAF_BODY_SEARCH, /* Check SIP body start */ SIP_PAF_FLUSH_STATE /* flush if Content-Length is reached */ } SIPPafState; /* State tracker for SIP Content Length */ typedef enum _SIPPafDataLenStatus { SIP_PAF_LENGTH_INVALID, SIP_PAF_LENGTH_CONTINUE, SIP_PAF_LENGTH_DONE } SIPPafDataLenStatus; /* State tracker for SIP Body Boundary*/ typedef enum _SIPPafBodyStatus { SIP_PAF_BODY_UNKNOWN, SIP_PAF_BODY_START_FIRST_CR, /* Check SIP body start - first CR */ SIP_PAF_BODY_START_FIRST_LF, /* Check SIP body start - first LF */ SIP_PAF_BODY_START_SECOND_CR, /* Check SIP body start - second CR */ SIP_PAF_BODY_START_SECOND_LF /* Check SIP body start - second LF */ } SIPPafBodyStatus; /* State tracker for POP PAF */ typedef struct _sipPafData { SIPPafState sip_state; /* The current sip paf state */ SIPPafBodyStatus body_state; /* State to find sip body */ char *next_letter; /* The current character in Content-Length */ bool found_len; uint32_t length; } SIPPafData; static char* content_len_key = "Content-Length"; static char* content_len_key_compact = "l"; #define UNKNOWN_CONTENT_LENGTH UINT32_MAX static uint8_t sip_paf_id = 0; static inline void reset_data_states(SIPPafData *pfdata) { /* reset data information information */ pfdata->next_letter = 0; pfdata->length = UNKNOWN_CONTENT_LENGTH; pfdata->sip_state = SIP_PAF_START_STATE; pfdata->body_state = SIP_PAF_BODY_UNKNOWN; } /* * Search for the single line termination sequence LF ("\n"). * * PARAMS: * const uint8_t ch - the next character to analyze. * SIPPafData *pfdata - the struct containing all sip paf information * * RETURNS: * false - if termination sequence not found * true - if termination sequence found */ static bool find_data_end_single_line(const uint8_t ch, SIPPafData *pfdata) { if (ch == '\n') { pfdata->sip_state = SIP_PAF_CONTENT_LEN_CMD; return true; } return false; } /* * Confirms every character in the current sequence is part of the expected * header. After confirmation is complete, SIP PAF will begin searching * for content length. If any character is unexpected, searches for the default * termination sequence. * * PARAMS: * const uint8_t ch - the next character to analyze. * SIPPafData *pfdata - the struct containing all sip paf information */ static inline void process_command(const uint8_t ch, SIPPafData *pfdata) { char val; if (!pfdata->next_letter) { /* avoid leading space */ if (isspace(ch)) return; if (toupper(ch) == toupper(content_len_key[0])) { pfdata->next_letter = &content_len_key[1]; return; } else pfdata->next_letter = content_len_key_compact; } val = *(pfdata->next_letter); if (val == '\0') { /* end of content header */ if (ch == ':') pfdata->sip_state = SIP_PAF_CONTENT_LEN_CONVERT; else if (!isblank(ch)) { reset_data_states(pfdata); find_data_end_single_line(ch, pfdata); } } else if (toupper(ch) == toupper(val)) pfdata->next_letter++; else { reset_data_states(pfdata); find_data_end_single_line(ch, pfdata); } } /* Get the length of SIP body from Content-Length header */ static SIPPafDataLenStatus get_length(char c, uint32_t *len ) { uint64_t length = *len; if (isspace(c)) { if (length != UNKNOWN_CONTENT_LENGTH) { *len = length; return SIP_PAF_LENGTH_DONE; } } else if (isdigit(c)) { uint64_t tmp_len; if (length == UNKNOWN_CONTENT_LENGTH) length = 0; tmp_len = (10 * length) + (c - '0'); if (tmp_len < UINT32_MAX) length = (uint32_t) tmp_len; else { *len = 0; return SIP_PAF_LENGTH_INVALID; } } else { *len = 0; return SIP_PAF_LENGTH_INVALID; } *len = length; return SIP_PAF_LENGTH_CONTINUE; } /* * Find the start of SIP body, "\r\n\r\n" or "\n\n" * * PARAMS: * const uint8_t ch - the next character to analyze. * SIPPafData *pfdata - the struct containing all sip paf information * Returns: * true: found body; * false: not found yet */ static inline bool find_body(const uint8_t ch, SIPPafData *pfdata) { switch (pfdata->body_state) { case SIP_PAF_BODY_UNKNOWN: if (ch == '\r') pfdata->body_state = SIP_PAF_BODY_START_FIRST_CR; else if (ch == '\n') pfdata->body_state = SIP_PAF_BODY_START_FIRST_LF; break; case SIP_PAF_BODY_START_FIRST_CR: if (ch == '\n') pfdata->body_state = SIP_PAF_BODY_START_SECOND_CR; else if (ch != '\r') pfdata->body_state = SIP_PAF_BODY_UNKNOWN; break; case SIP_PAF_BODY_START_FIRST_LF: if (ch == '\n') return true; else if (ch == '\r') pfdata->body_state = SIP_PAF_BODY_START_FIRST_CR; else pfdata->body_state = SIP_PAF_BODY_UNKNOWN; break; case SIP_PAF_BODY_START_SECOND_CR: if (ch == '\r') pfdata->body_state = SIP_PAF_BODY_START_SECOND_LF; else if (ch == '\n') return true; else pfdata->body_state = SIP_PAF_BODY_UNKNOWN; break; case SIP_PAF_BODY_START_SECOND_LF: if (ch == '\n') return true; else if (ch == '\r') pfdata->body_state = SIP_PAF_BODY_START_FIRST_CR; else pfdata->body_state = SIP_PAF_BODY_UNKNOWN; break; } return false; } /* Function: sip_paf() * * Purpose: sip PAF callback. * Inspects sip traffic. * * Arguments: * void *ssn - stream session pointer * void **ps - sipPaf state tracking structure * const uint8_t *data - payload data to inspect * uint32_t len - length of payload data * uint32_t flags - flags to check whether client or server * uint32_t * fp - pointer to set flush point * uint32_t * fp_eoh - pointer to set header flush point * * Returns: * PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise */ static PAF_Status sip_paf(void* ssn, void** ps, const uint8_t* data, uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh) { uint32_t i; SIPPafData *pfdata = *(SIPPafData **)ps; if (pfdata == NULL) { pfdata = _dpd.snortAlloc(1, sizeof(*pfdata), PP_SIP, PP_MEM_CATEGORY_SESSION); if (pfdata == NULL) { return PAF_ABORT; } reset_data_states(pfdata); *ps = pfdata; } for (i = 0; i < len; i++) { uint8_t ch = data[i]; const uint8_t *next; SIPPafDataLenStatus status; switch(pfdata->sip_state) { case SIP_PAF_START_STATE: next = (const uint8_t *) memchr(&data[i], '\n', (len - i)); if (next) { i = (uint32_t)(next - data); pfdata->sip_state = SIP_PAF_CONTENT_LEN_CMD; } else return PAF_SEARCH; break; case SIP_PAF_CONTENT_LEN_CMD: process_command(ch, pfdata); break; case SIP_PAF_CONTENT_LEN_CONVERT: status = get_length(ch, &pfdata->length); if ( status == SIP_PAF_LENGTH_DONE) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Find data length: %d\n", pfdata->length);); pfdata->sip_state = SIP_PAF_BODY_SEARCH; find_body(ch, pfdata); } else if (status == SIP_PAF_LENGTH_INVALID) { reset_data_states(pfdata); find_data_end_single_line(ch, pfdata); } break; case SIP_PAF_BODY_SEARCH: if (find_body(ch, pfdata)) { pfdata->sip_state = SIP_PAF_FLUSH_STATE; } else break; case SIP_PAF_FLUSH_STATE: if (pfdata->length == 0) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "SIP PAF: flushing data!\n");); *fp = i + 1; reset_data_states(pfdata); return PAF_FLUSH; } pfdata->length--; break; } } return PAF_SEARCH; } static void sip_paf_cleanup(void *pafData) { if (pafData) _dpd.snortFree(pafData, sizeof(SIPPafData), PP_SIP, PP_MEM_CATEGORY_SESSION); } #ifdef TARGET_BASED void register_sip_paf_service (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { sip_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, sip_paf, true); sip_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false, sip_paf, true); _dpd.streamAPI->register_paf_free(sip_paf_id, sip_paf_cleanup); } } #endif void register_sip_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { sip_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, true, sip_paf, true); sip_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, false, sip_paf, true); } } snort-2.9.20/src/dynamic-preprocessors/sip/sip_roptions.h0000644000175000017500000000427314241076334021721 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ #ifndef _SIP_ROPTIONS_H_ #define _SIP_ROPTIONS_H_ #include "sip_config.h" #define SIP_NUM_STAT_CODE_MAX 20 /******************************************************************** * Structures ********************************************************************/ typedef struct _SIP_Roptions { /* sip_method data*/ SIPMethodsFlag methodFlag; /* sip_stat_code data*/ uint16_t status_code; /* sip header data */ const uint8_t *header_data; /* Set to NULL if not applicable */ uint16_t header_len; /* sip body data */ const uint8_t *body_data; /* Set to NULL if not applicable */ uint16_t body_len; } SIP_Roptions; typedef struct _SipMethodRuleOptData { int flags; int mask; } SipMethodRuleOptData; typedef struct _SipStatCodeRuleOptData { uint16_t stat_codes[SIP_NUM_STAT_CODE_MAX]; } SipStatCodeRuleOptData; /******************************************************************** * Public function prototypes ********************************************************************/ void SIP_RegRuleOptions(struct _SnortConfig *); #endif /* _SIP_ROPTIONS_H_ */ snort-2.9.20/src/dynamic-preprocessors/sip/spp_sip.c0000644000175000017500000010403114241076345020634 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * SIP preprocessor * * This is the main entry point for this preprocessor * * Author: Hui Cao * Date: 03-15-2011 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #include "spp_sip.h" #include "sip_config.h" #include "sip_roptions.h" #include "sip_parser.h" #include "sip_dialog.h" #include "sip_paf.h" #include #include #include #include #include #ifndef WIN32 #include #include #endif #include #include #ifdef REG_TEST #include "reg_test.h" #endif #include "profiler.h" #ifdef PERF_PROFILING PreprocStats sipPerfStats; #endif #include "sf_types.h" #ifdef DUMP_BUFFER #include "sip_buffer_dump.h" #endif #ifdef SNORT_RELOAD #include "appdata_adjuster.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_SIP"; #define SetupSIP DYNAMIC_PREPROC_SETUP #ifdef TARGET_BASED int16_t sip_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif /* * Session state flags for SIPData::state_flags */ #define SIP_FLG_MISSED_PACKETS (0x10000) /* * Function prototype(s) */ SIPData * SIPGetNewSession(SFSnortPacket *, tSfPolicyId); static void SIPInit( struct _SnortConfig *, char* ); static bool SIPGlobalIsEnabled(struct _SnortConfig *sc, tSfPolicyUserContextId sip_config); static int SIPPolicyIsEnabled(struct _SnortConfig *sc, tSfPolicyUserContextId pContext, tSfPolicyId policyId, void* config); static int SIPCheckConfig(struct _SnortConfig *); static void FreeSIPData( void* ); static inline int SIP_Process(SFSnortPacket *, SIPData*); static void SIPmain( void*, void* ); static inline int CheckSIPPort( uint16_t ); static void SIPFreeConfig(tSfPolicyUserContextId); static void registerPortsForDispatch( struct _SnortConfig *sc, SIPConfig *policy ); static void registerPortsForReassembly( SIPConfig *policy, int direction ); static void _addPortsToStreamFilter(struct _SnortConfig *, SIPConfig *, tSfPolicyId); static void SIP_PrintStats(int); static void DisplaySIPStats (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static void SIPCleanExit(int, void *); /******************************************************************** * Global variables ********************************************************************/ uint32_t numSessions = 0; SIP_Stats sip_stats; SIPConfig *sip_eval_config; tSfPolicyUserContextId sip_config; #ifdef SNORT_RELOAD static APPDATA_ADJUSTER *ada; #endif static size_t SIP_NumSessions() { return (size_t) numSessions; } #ifdef SNORT_RELOAD static void SIPReload(struct _SnortConfig *, char *, void **); static int SIPReloadVerify(struct _SnortConfig *, void *); static void * SIPReloadSwap(struct _SnortConfig *, void *); static void SIPReloadSwapFree(void *); #endif static SIPMsg sipMsg; int SIPPrintMemStats(FILE *fd, char *buffer, PreprocMemInfo *meminfo) { int len = 0; time_t curr_time; if (fd) { len = fprintf(fd, ",%lu,%u" ",%lu,%u,%u" ",%lu,%u,%u,%lu" , sip_stats.sessions , numSessions , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); return len; } curr_time = time(NULL); if (buffer) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics of SIP on: %s\n" " Total Sessions : %lu\n" " Current Active Sessions : %u\n\n" , ctime(&curr_time) , sip_stats.sessions , numSessions); } else { _dpd.logMsg("\n"); _dpd.logMsg("Memory Statistics of SIP on: %s\n", ctime(&curr_time)); _dpd.logMsg(" Total Sessions : %lu\n", sip_stats.sessions); _dpd.logMsg(" Current Active Sessions : %u\n\n", numSessions); } return len; } /* Called at preprocessor setup time. Links preprocessor keyword * to corresponding preprocessor initialization function. * * PARAMETERS: None. * * RETURNS: Nothing. * */ void SetupSIP(void) { _dpd.registerMemoryStatsFunc(PP_SIP, SIPPrintMemStats); /* Link preprocessor keyword to initialization function * in the preprocessor list. */ #ifndef SNORT_RELOAD _dpd.registerPreproc( "sip", SIPInit ); #else _dpd.registerPreproc("sip", SIPInit, SIPReload, SIPReloadVerify, SIPReloadSwap, SIPReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getSIPBuffers, SIP_BUFFER_DUMP_FUNC); #endif } SIPConfig *getParsingSIPConfig(struct _SnortConfig *sc) { SIPConfig * sip_parsing_config; #ifdef SNORT_RELOAD tSfPolicyUserContextId sip_swap_config = (tSfPolicyUserContextId)_dpd.getRelatedReloadData(sc, "sip"); if (sip_swap_config) sip_parsing_config = sfPolicyUserDataGetCurrent(sip_swap_config); else #endif sip_parsing_config = sfPolicyUserDataGetCurrent(sip_config); return sip_parsing_config; } #ifdef REG_TEST static inline void PrintSIPSize(void) { _dpd.logMsg("\nSIP Session Size: %lu\n", (long unsigned int)sizeof(SIPData)); } #endif /* Initializes the SIP preprocessor module and registers * it in the preprocessor list. * * PARAMETERS: * * argp: Pointer to argument string to process for config * data. * * RETURNS: Nothing. */ static void SIPInit(struct _SnortConfig *sc, char *argp) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); SIPConfig *pDefaultPolicyConfig = NULL; SIPConfig *pPolicyConfig = NULL; #ifdef REG_TEST PrintSIPSize(); #endif /* For SFR CLI */ _dpd.controlSocketRegisterHandler(CS_TYPE_SIP_STATS, NULL, NULL, &DisplaySIPStats); if (sip_config == NULL) { //create a context sip_config = sfPolicyConfigCreate(); if (sip_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for SIP config.\n"); } _dpd.addPreprocConfCheck(sc, SIPCheckConfig); _dpd.registerPreprocStats(SIP_NAME, SIP_PrintStats); _dpd.addPreprocExit(SIPCleanExit, NULL, PRIORITY_LAST, PP_SIP); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("sip", (void *)&sipPerfStats, 0, _dpd.totalPerfStats, NULL); #endif #ifdef TARGET_BASED sip_app_id = _dpd.findProtocolReference("sip"); if (sip_app_id == SFTARGET_UNKNOWN_PROTOCOL) sip_app_id = _dpd.addProtocolReference("sip"); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_SIP, sip_app_id ); #endif } sfPolicyUserPolicySet (sip_config, policy_id); pDefaultPolicyConfig = (SIPConfig *)sfPolicyUserDataGetDefault(sip_config); pPolicyConfig = (SIPConfig *)sfPolicyUserDataGetCurrent(sip_config); if ((pPolicyConfig != NULL) && (pDefaultPolicyConfig == NULL)) { DynamicPreprocessorFatalMessage("SIP preprocessor can only be " "configured once.\n"); } pPolicyConfig = (SIPConfig *)_dpd.snortAlloc(1, sizeof(SIPConfig), PP_SIP, PP_MEM_CATEGORY_CONFIG); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "SIP preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(sip_config, pPolicyConfig); SIP_RegRuleOptions(sc); ParseSIPArgs(pPolicyConfig, (u_char *)argp); #ifdef SNORT_RELOAD pDefaultPolicyConfig = (SIPConfig *)sfPolicyUserDataGetDefault(sip_config); //we don't know the order in which policies are init //maybe default (policy 0) isn't init until after another policy is init //however a default policy is guranteed //avoid core //Also, if SIP isn't enabled, then why waste memory? #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("SIP-reload SIPInit-before : %p\n", ada); } #endif if (pDefaultPolicyConfig != NULL && ada == NULL && SIPGlobalIsEnabled(sc, sip_config)) { ada = ada_init(SIP_NumSessions, PP_SIP, (size_t)pDefaultPolicyConfig->maxNumSessions); if (ada == NULL) DynamicPreprocessorFatalMessage("Could not allocate memory for SIP ada\n"); } #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("SIP-reload SIPInit-after : %p\n", ada); } #endif #endif } /********************************************************************* * Overload PCRE options: this is to support the "H" * * For SIP messages, uri Buffers will point to SIP instead of HTTP * * Arguments: * SFSnortPacket * - pointer to packet structure * * Returns: * None * *********************************************************************/ static inline void SIP_overloadURI(SFSnortPacket *p, SIPMsg *sipMsg) { if ( sipMsg->header ) _dpd.setHttpBuffer(HTTP_BUFFER_HEADER, sipMsg->header, sipMsg->headerLen); if ( sipMsg->body_data ) _dpd.setHttpBuffer(HTTP_BUFFER_CLIENT_BODY, sipMsg->body_data, sipMsg->bodyLen); } /********************************************************************* * Main entry point for SIP processing. * * Arguments: * SFSnortPacket * - pointer to packet structure * * Returns: * int - SIP_SUCCESS * SIP_FAILURE * *********************************************************************/ static inline int SIP_Process(SFSnortPacket *p, SIPData* sessp) { int status; char* sip_buff = (char*) p->payload; char* end; SIP_Roptions *pRopts; memset(&sipMsg, 0, SIPMSG_ZERO_LEN); /*Input parameters*/ sipMsg.isTcp = IsTCP(p); end = sip_buff + p->payload_size; status = sip_parse(&sipMsg, sip_buff, end); if (SIP_SUCCESS == status) { SIP_overloadURI(p, &sipMsg); /*Update the dialog state*/ SIP_updateDialog(&sipMsg, &(sessp->dialogs), p); } /*Update the session data*/ pRopts = &(sessp->ropts); pRopts->methodFlag = sipMsg.methodFlag; pRopts->header_data = sipMsg.header; pRopts->header_len = sipMsg.headerLen; pRopts->body_len = sipMsg.bodyLen; pRopts->body_data = sipMsg.body_data; pRopts->status_code = sipMsg.status_code; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "SIP message header length: %d\n", sipMsg.headerLen)); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Parsed method: %.*s, Flag: 0x%x\n", sipMsg.methodLen, sipMsg.method, sipMsg.methodFlag)); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Parsed status code: %d\n", sipMsg.status_code)); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Parsed header address: %p.\n", sipMsg.header)); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Parsed body address: %p.\n", sipMsg.body_data)); sip_freeMsg(&sipMsg); return status; } /* Main runtime entry point for SIP preprocessor. * Analyzes SIP packets for anomalies/exploits. * * PARAMETERS: * * packetp: Pointer to current packet to process. * contextp: Pointer to context block, not used. * * RETURNS: Nothing. */ static void SIPmain( void* ipacketp, void* contextp ) { SIPData* sessp = NULL; uint8_t source = 0; uint8_t dest = 0; SFSnortPacket* packetp; #ifdef TARGET_BASED int16_t app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); PROFILE_VARS; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__START_MSG)); packetp = (SFSnortPacket*) ipacketp; sfPolicyUserPolicySet (sip_config, policy_id); // preconditions - what we registered for assert((IsUDP(packetp) || IsTCP(packetp)) && packetp->payload && packetp->payload_size); if (IsTCP(packetp)) { if (!_dpd.readyForProcess(packetp)) { /* Packet will be rebuilt, so wait for it */ DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Packet will be reassembled\n")); return; } if (_dpd.sessionAPI->get_application_data(packetp->stream_session, PP_SSL) && !_dpd.streamAPI->is_session_decrypted(packetp->stream_session)) { /* Packet is a non-SIP/encrypted SIP one, skip those */ DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Packet is encrypted or not a SIP packet\n")); return; } } PREPROC_PROFILE_START(sipPerfStats); sip_eval_config = sfPolicyUserDataGetCurrent(sip_config); /* Attempt to get a previously allocated SIP block. */ sessp = _dpd.sessionAPI->get_application_data(packetp->stream_session, PP_SIP); if (sessp != NULL) { sip_eval_config = sfPolicyUserDataGet(sessp->config, sessp->policy_id); } if (sessp == NULL) { /* If not doing autodetection, check the ports to make sure this is * running on an SIP port, otherwise no need to examine the traffic. */ #ifdef TARGET_BASED app_id = _dpd.sessionAPI->get_application_protocol_id(packetp->stream_session); if (app_id == SFTARGET_UNKNOWN_PROTOCOL) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Unknown protocol - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__END_MSG)); PREPROC_PROFILE_END(sipPerfStats); return; } else if (app_id && (app_id != sip_app_id)) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Not SIP - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__END_MSG)); PREPROC_PROFILE_END(sipPerfStats); return; } else if (!app_id) { #endif source = (uint8_t)CheckSIPPort( packetp->src_port ); dest = (uint8_t)CheckSIPPort( packetp->dst_port ); if ( !source && !dest ) { /* Not one of the ports we care about. */ DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Not SIP ports - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__END_MSG)); PREPROC_PROFILE_END(sipPerfStats); return; } #ifdef TARGET_BASED } #endif /* Check the stream session. If it does not currently * have our SIP data-block attached, create one. */ sessp = SIPGetNewSession(packetp, policy_id); if ( !sessp ) { /* Could not get/create the session data for this packet. */ DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Create session error - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__END_MSG)); PREPROC_PROFILE_END(sipPerfStats); return; } } /* Don't process if we've missed packets */ if (sessp->state_flags & SIP_FLG_MISSED_PACKETS) { DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Missed packets - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__END_MSG)); PREPROC_PROFILE_END(sipPerfStats); return; } /* If we picked up mid-stream or missed any packets (midstream pick up * means we've already missed packets) set missed packets flag and make * sure we don't do any more reassembly on this session */ if (IsTCP(packetp)) { if ((_dpd.sessionAPI->get_session_flags(packetp->stream_session) & SSNFLAG_MIDSTREAM) || _dpd.streamAPI->missed_packets(packetp->stream_session, SSN_DIR_BOTH)) { _dpd.streamAPI->set_reassembly(packetp->stream_session, STREAM_FLPOLICY_IGNORE, SSN_DIR_BOTH, STREAM_FLPOLICY_SET_ABSOLUTE); sessp->state_flags |= SIP_FLG_MISSED_PACKETS; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Missed packets - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__END_MSG)); PREPROC_PROFILE_END(sipPerfStats); return; } } /* * Start process PAYLOAD */ SIP_Process(packetp,sessp); DEBUG_WRAP(DebugMessage(DEBUG_SIP, "%s\n", SIP_DEBUG__END_MSG)); PREPROC_PROFILE_END(sipPerfStats); } /********************************************************************** * Retrieves the SIP data block registered with the stream * session associated w/ the current packet. If none exists, * allocates it and registers it with the stream API. * * Arguments: * * packetp: Pointer to the packet from which/in which to * retrieve/store the SIP data block. * * RETURNS: Pointer to an SIP data block, upon success. * NULL, upon failure. **********************************************************************/ SIPData * SIPGetNewSession(SFSnortPacket *packetp, tSfPolicyId policy_id) { SIPData* datap = NULL; static int MaxSessionsAlerted = 0; /* Sanity check(s) */ assert( packetp ); if ( !packetp->stream_session ) { return NULL; } if(numSessions > ((SIPConfig *)sfPolicyUserDataGetCurrent(sip_config))->maxNumSessions) { if (!MaxSessionsAlerted) ALERT(SIP_EVENT_MAX_SESSIONS,SIP_EVENT_MAX_SESSIONS_STR); MaxSessionsAlerted = 1; return NULL; } else { MaxSessionsAlerted = 0; } datap = (SIPData *)_dpd.snortAlloc(1, sizeof(SIPData), PP_SIP, PP_MEM_CATEGORY_SESSION); if ( !datap ) return NULL; /*Register the new SIP data block in the stream session. */ _dpd.sessionAPI->set_application_data( packetp->stream_session, PP_SIP, datap, FreeSIPData ); /* We're interested in this session. Turn on stream reassembly. */ if ( !(_dpd.streamAPI->get_reassembly_direction(packetp->stream_session) & SSN_DIR_BOTH )) { _dpd.streamAPI->set_reassembly(packetp->stream_session, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_BOTH, STREAM_FLPOLICY_SET_ABSOLUTE); } #ifdef SNORT_RELOAD ada_add(ada, (void *)datap, packetp->stream_session); #endif datap->policy_id = policy_id; datap->config = sip_config; ((SIPConfig *)sfPolicyUserDataGetCurrent(sip_config))->ref_count++; numSessions++; sip_stats.sessions++; DEBUG_WRAP(DebugMessage(DEBUG_SIP, "Number of sessions created: %u\n", numSessions)); return datap; } /*********************************************************************** * Registered as a callback with our SIP data blocks when * they are added to the underlying stream session. Called * by the stream preprocessor when a session is about to be * destroyed. * * PARAMETERS: * * idatap: Pointer to the moribund data. * * RETURNS: Nothing. ***********************************************************************/ static void FreeSIPData( void* idatap ) { SIPData *ssn = (SIPData *)idatap; SIPConfig *config = NULL; if (ssn == NULL) return; if (numSessions > 0) numSessions--; #ifdef SNORT_RELOAD ada_appdata_freed( ada, idatap ); #endif /*Free all the dialog data*/ sip_freeDialogs(&ssn->dialogs); /*Clean the configuration data*/ if (ssn->config != NULL) { config = (SIPConfig *)sfPolicyUserDataGet(ssn->config, ssn->policy_id); } if (config == NULL) { _dpd.snortFree(ssn, sizeof(SIPData), PP_SIP, PP_MEM_CATEGORY_SESSION); return; } config->ref_count--; if ((config->ref_count == 0) && (ssn->config != sip_config)) { sfPolicyUserDataClear (ssn->config, ssn->policy_id); _dpd.snortFree(config, sizeof(SIPConfig), PP_SIP, PP_MEM_CATEGORY_CONFIG); if (sfPolicyUserPolicyGetActive(ssn->config) == 0) { /* No more outstanding configs - free the config array */ SIPFreeConfig(ssn->config); } } _dpd.snortFree(ssn, sizeof(SIPData), PP_SIP, PP_MEM_CATEGORY_SESSION); } /* ********************************************************************** * Validates given port as an SIP server port. * * PARAMETERS: * * port: Port to validate. * * RETURNS: SIP_TRUE, if the port is indeed an SIP server port. * SIP_FALSE, otherwise. ***********************************************************************/ static inline int CheckSIPPort( uint16_t port ) { if ( sip_eval_config->ports[ PORT_INDEX(port) ] & CONV_PORT( port ) ) { return SIP_TRUE; } return SIP_FALSE; } static void registerPortsForDispatch( struct _SnortConfig *sc, SIPConfig *policy ) { if ( _dpd.isPreprocEnabled( sc, PP_APP_ID ) ) { _dpd.sessionAPI->enable_preproc_all_ports( sc, PP_SIP, PROTO_BIT__UDP | PROTO_BIT__TCP ); } else { int port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_SIP, PROTO_BIT__UDP | PROTO_BIT__TCP, port ); } } } static void registerPortsForReassembly( SIPConfig *policy, int direction ) { uint32_t port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.streamAPI->register_reassembly_port( NULL, port, direction ); } } static void _addPortsToStreamFilter(struct _SnortConfig *sc, SIPConfig *config, tSfPolicyId policy_id) { uint32_t portNum; assert(config); assert(_dpd.streamAPI); for (portNum = 0; portNum < MAXPORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_UDP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); register_sip_paf_port(sc, portNum, policy_id); } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, sip_app_id, PORT_MONITOR_SESSION, policy_id, 1); register_sip_paf_service(sc, sip_app_id, policy_id); } #endif static int SIPCheckPolicyConfig(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policy_id, void* pData) { SIPConfig *sip_policy = ( SIPConfig * ) pData; if ( sip_policy->disabled ) return 0; if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("SIPCheckPolicyConfig(): The Stream preprocessor must be enabled.\n"); return -1; } if (policy_id != 0) { SIPConfig *default_sip_policy = ( SIPConfig * ) sfPolicyUserDataGetDefault( config ); if(default_sip_policy == NULL) { _dpd.errMsg("SIPCheckPolicyConfig(): SIP default policy must be configured\n"); return -1; } sip_policy->maxNumSessions = default_sip_policy->maxNumSessions; } _dpd.setParserPolicy( sc, policy_id ); _dpd.addPreproc( sc, SIPmain, PRIORITY_APPLICATION, PP_SIP, PROTO_BIT__UDP|PROTO_BIT__TCP ); // register ports with session and stream registerPortsForDispatch( sc, sip_policy ); registerPortsForReassembly( sip_policy, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStreamFilter(sc, sip_policy, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif return 0; } static bool SIPGlobalIsEnabled(struct _SnortConfig *sc, tSfPolicyUserContextId config) { return sfPolicyUserDataIterate(sc, config, SIPPolicyIsEnabled) != 0; } static int SIPPolicyIsEnabled(struct _SnortConfig *sc, tSfPolicyUserContextId pContext, tSfPolicyId policyId, void* config) { SIPConfig *sip_policy_config = (SIPConfig *) config; if (sip_policy_config == NULL || sip_policy_config->disabled) return 0; return 1; } int SIPCheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate (sc, sip_config, SIPCheckPolicyConfig))) return rval; return 0; } static void SIPCleanExit(int signal, void *data) { if (sip_config != NULL) { SIPFreeConfig(sip_config); sip_config = NULL; #ifdef SNORT_RELOAD ada_delete(ada); ada = NULL; #endif } } static int SIPFreeConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SIPConfig *pPolicyConfig = (SIPConfig *)pData; //do any housekeeping before freeing SIPConfig sfPolicyUserDataClear (config, policyId); SIP_FreeConfig(pPolicyConfig); return 0; } void SIPFreeConfig(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, SIPFreeConfigPolicy); sfPolicyConfigDelete(config); } static void DisplaySIPStats (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f) { char buffer[CS_STATS_BUF_SIZE + 1]; int i = 0; int len = 0; if (sip_stats.sessions) { len += snprintf(buffer, CS_STATS_BUF_SIZE, "SIP Preprocessor Statistics\n" " Total sessions: "STDu64"\n", sip_stats.sessions); if (sip_stats.events) len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, " SIP anomalies : "STDu64"\n", sip_stats.events); if (sip_stats.dialogs) len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, " Total dialogs: "STDu64"\n", sip_stats.dialogs); len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, " Requests: "STDu64"\n", sip_stats.requests[0]); while (NULL != StandardMethods[i].name && len < CS_STATS_BUF_SIZE) { len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, "%16s: "STDu64"\n", StandardMethods[i].name, sip_stats.requests[StandardMethods[i].methodFlag]); i++; } len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, " Responses: "STDu64"\n", sip_stats.responses[TOTAL_RESPONSES]); for (i = 1; i 0) { if (sip_stats.events > 0) _dpd.logMsg(" SIP anomalies : "STDu64"\n", sip_stats.events); if (sip_stats.dialogs > 0) _dpd.logMsg(" Total dialogs: "STDu64"\n", sip_stats.dialogs); _dpd.logMsg(" Requests: "STDu64"\n", sip_stats.requests[0]); i = 0; while (NULL != StandardMethods[i].name) { _dpd.logMsg("%16s: "STDu64"\n", StandardMethods[i].name, sip_stats.requests[StandardMethods[i].methodFlag]); i++; } _dpd.logMsg(" Responses: "STDu64"\n", sip_stats.responses[TOTAL_RESPONSES]); for (i = 1; i maxNumSessions); if (ada == NULL) DynamicPreprocessorFatalMessage("Could not allocate memory for SIP ada\n"); } #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("SIP-reload SIPReload-after : %p\n", ada); } #endif } static int SIPReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId sip_swap_config = (tSfPolicyUserContextId)swap_config; SIPConfig * default_swap_config = NULL; SIPConfig * current_default_config = NULL; int rval; if (sip_swap_config == NULL) return 0; // validate each policy and do per policy initialization processing if ((rval = sfPolicyUserDataIterate (sc, sip_swap_config, SIPCheckPolicyConfig))) return rval; default_swap_config = (SIPConfig *)sfPolicyUserDataGet(sip_swap_config, _dpd.getDefaultPolicy()); if (sip_config != NULL) { current_default_config = (SIPConfig *)sfPolicyUserDataGet(sip_config, _dpd.getDefaultPolicy()); //not possible if (!current_default_config) return 0; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); if (!SIPGlobalIsEnabled(sc, sip_swap_config)) { ada_reload_disable(&ada, sc, "sip-disable-mem-dump", policy_id); } else if (SIPGlobalIsEnabled(sc, sip_config) && default_swap_config->maxNumSessions < current_default_config->maxNumSessions) { ada_reload_adjust_register(ada, policy_id, sc, "sip-mem-reloader", (size_t) default_swap_config->maxNumSessions); } } return 0; } static int SIPFreeUnusedConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SIPConfig *pPolicyConfig = (SIPConfig *)pData; //do any housekeeping before freeing SIPConfig if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); SIP_FreeConfig(pPolicyConfig); } return 0; } static void * SIPReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId sip_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = sip_config; if (sip_swap_config == NULL) return NULL; sip_config = sip_swap_config; sfPolicyUserDataFreeIterate (old_config, SIPFreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_config; } return NULL; } static void SIPReloadSwapFree(void *data) { if (data == NULL) return; SIPFreeConfig((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/Makefile.in0000644000175000017500000021663114242725546020304 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@am__append_1 = include/appId.h include/appIdApi.h include/thirdparty_appid_types.h \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/thirdparty_appid_api.h include/dns_defs.h #appdata_adjuster @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@am__append_2 = include/appdata_adjuster.c include/sfxhash.c include/sfhashfcn.c include/sfmemcap.c include/sfprimetable.c include/reg_test.h include/reg_test.c @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@am__append_3 = include/appdata_adjuster.h @FEAT_OPEN_APPID_TRUE@am__append_4 = include/appId.h include/appIdApi.h include/thirdparty_appid_types.h \ @FEAT_OPEN_APPID_TRUE@ include/thirdparty_appid_api.h \ @FEAT_OPEN_APPID_TRUE@ include/sfprimetable.h include/sfprimetable.c include/sfxhash.h \ @FEAT_OPEN_APPID_TRUE@ include/sfxhash.c \ @FEAT_OPEN_APPID_TRUE@ include/sfghash.c include/sfhashfcn.c include/sflsq.h include/sflsq.c \ @FEAT_OPEN_APPID_TRUE@ include/md5.h include/md5.c \ @FEAT_OPEN_APPID_TRUE@ include/dns_defs.h @BUILD_SNORT_RELOAD_TRUE@am__append_5 = include/sfprimetable.h \ @BUILD_SNORT_RELOAD_TRUE@ include/sfprimetable.c \ @BUILD_SNORT_RELOAD_TRUE@ include/sfmemcap.h \ @BUILD_SNORT_RELOAD_TRUE@ include/sfmemcap.c \ @BUILD_SNORT_RELOAD_TRUE@ include/sfhashfcn.h \ @BUILD_SNORT_RELOAD_TRUE@ include/sfhashfcn.c \ @BUILD_SNORT_RELOAD_TRUE@ include/sfxhash.h \ @BUILD_SNORT_RELOAD_TRUE@ include/sfxhash.c \ @BUILD_SNORT_RELOAD_TRUE@ include/appdata_adjuster.h \ @BUILD_SNORT_RELOAD_TRUE@ include/appdata_adjuster.c @FEAT_OPEN_APPID_TRUE@am__append_6 = appid @FEAT_OPEN_APPID_TRUE@am__append_7 = include/appId.h include/appIdApi.h include/thirdparty_appid_types.h \ @FEAT_OPEN_APPID_TRUE@ include/thirdparty_appid_api.h \ @FEAT_OPEN_APPID_TRUE@ include/sfprimetable.h include/sfxhash.h include/sfhashfcn.h \ @FEAT_OPEN_APPID_TRUE@ include/md5.h \ @FEAT_OPEN_APPID_TRUE@ include/dns_defs.h @BUILD_SNORT_RELOAD_TRUE@am__append_8 = include/sfprimetable.h \ @BUILD_SNORT_RELOAD_TRUE@include/sfprimetable.c \ @BUILD_SNORT_RELOAD_TRUE@include/sfmemcap.h \ @BUILD_SNORT_RELOAD_TRUE@include/sfmemcap.c \ @BUILD_SNORT_RELOAD_TRUE@include/sfhashfcn.h \ @BUILD_SNORT_RELOAD_TRUE@include/sfhashfcn.c \ @BUILD_SNORT_RELOAD_TRUE@include/sfxhash.h \ @BUILD_SNORT_RELOAD_TRUE@include/sfxhash.c \ @BUILD_SNORT_RELOAD_TRUE@include/appdata_adjuster.h \ @BUILD_SNORT_RELOAD_TRUE@include/appdata_adjuster.c subdir = src/dynamic-preprocessors ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__preproc_HEADERS_DIST) \ $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(preproclibdir)" \ "$(DESTDIR)$(preprocdir)" "$(DESTDIR)$(preprocdir)" LTLIBRARIES = $(preproclib_LTLIBRARIES) libsf_dynamic_preproc_la_LIBADD = am__libsf_dynamic_preproc_la_SOURCES_DIST = ssl_common/ssl.c \ ssl_common/ssl_config.c ssl_common/ssl_inspect.c \ ssl_common/ssl_ha.c @SO_WITH_STATIC_LIB_TRUE@am_libsf_dynamic_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-ssl.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-ssl_config.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-ssl_inspect.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-ssl_ha.lo @SO_WITH_STATIC_LIB_TRUE@nodist_libsf_dynamic_preproc_la_OBJECTS = libsf_dynamic_preproc_la-sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sf_ip.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sfrt.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sfrt_dir.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sfrt_flat.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sfrt_flat_dir.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-segment_mem.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-mempool.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sf_sdlist.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sfPolicyUserData.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-util_unfold.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sf_base64decode.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sf_email_attach_decode.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-reg_test.lo \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc_la-sfparser.lo libsf_dynamic_preproc_la_OBJECTS = \ $(am_libsf_dynamic_preproc_la_OBJECTS) \ $(nodist_libsf_dynamic_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_dynamic_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) \ $(libsf_dynamic_preproc_la_LDFLAGS) $(LDFLAGS) -o $@ @SO_WITH_STATIC_LIB_TRUE@am_libsf_dynamic_preproc_la_rpath = -rpath \ @SO_WITH_STATIC_LIB_TRUE@ $(preproclibdir) libsf_dynamic_utils_la_LIBADD = @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@am__objects_1 = libsf_dynamic_utils_la-appdata_adjuster.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfxhash.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfhashfcn.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfmemcap.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfprimetable.lo \ @BUILD_SNORT_RELOAD_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-reg_test.lo @FEAT_OPEN_APPID_FALSE@@SO_WITH_STATIC_LIB_TRUE@nodist_libsf_dynamic_utils_la_OBJECTS = libsf_dynamic_utils_la-sfmemcap.lo \ @FEAT_OPEN_APPID_FALSE@@SO_WITH_STATIC_LIB_TRUE@ $(am__objects_1) @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@nodist_libsf_dynamic_utils_la_OBJECTS = libsf_dynamic_utils_la-sfprimetable.lo \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfxhash.lo \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfmemcap.lo \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfghash.lo \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sfhashfcn.lo \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-sflsq.lo \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils_la-md5.lo \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ $(am__objects_1) libsf_dynamic_utils_la_OBJECTS = \ $(nodist_libsf_dynamic_utils_la_OBJECTS) libsf_dynamic_utils_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) \ $(libsf_dynamic_utils_la_LDFLAGS) $(LDFLAGS) -o $@ @SO_WITH_STATIC_LIB_TRUE@am_libsf_dynamic_utils_la_rpath = -rpath \ @SO_WITH_STATIC_LIB_TRUE@ $(preproclibdir) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_dynamic_preproc_la_SOURCES) \ $(nodist_libsf_dynamic_preproc_la_SOURCES) \ $(nodist_libsf_dynamic_utils_la_SOURCES) DIST_SOURCES = $(am__libsf_dynamic_preproc_la_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__preproc_HEADERS_DIST = ssl_common/ssl.h ssl_common/ssl_include.h \ ssl_common/ssl_session.h ssl_common/ssl_config.h \ ssl_common/ssl_ha.h ssl_common/ssl_inspect.h HEADERS = $(nodist_preproc_HEADERS) $(preproc_HEADERS) RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = . libs ftptelnet pop imap smtp ssh dns ssl dcerpc2 sdf \ sip reputation gtp modbus dnp3 s7commplus file appid am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I${top_builddir}/src/dynamic-preprocessors/include -I${top_builddir}/src/dynamic-preprocessors/ssl_common -I${top_srcdir}/src/dynamic-preprocessors/libs -I${top_builddir} INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies @SO_WITH_STATIC_LIB_TRUE@preproclibdir = $(pkglibdir)/dynamic_preproc @SO_WITH_STATIC_LIB_TRUE@preproclib_LTLIBRARIES = \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ libsf_dynamic_utils.la @SO_WITH_STATIC_LIB_TRUE@libsf_dynamic_preproc_la_CFLAGS = -fPIC -DPIC -DDYNAMIC_PREPROC_CONTEXT @SO_WITH_STATIC_LIB_TRUE@libsf_dynamic_preproc_la_LDFLAGS = -static @SO_WITH_STATIC_LIB_TRUE@libsf_dynamic_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl.c \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_config.c \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_inspect.c \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_ha.c @SO_WITH_STATIC_LIB_TRUE@nodist_libsf_dynamic_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_TRUE@include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_TRUE@include/sf_ip.c \ @SO_WITH_STATIC_LIB_TRUE@include/sfrt.c \ @SO_WITH_STATIC_LIB_TRUE@include/sfrt_dir.c \ @SO_WITH_STATIC_LIB_TRUE@include/sfrt_flat.c \ @SO_WITH_STATIC_LIB_TRUE@include/sfrt_flat_dir.c \ @SO_WITH_STATIC_LIB_TRUE@include/segment_mem.c \ @SO_WITH_STATIC_LIB_TRUE@include/mempool.c \ @SO_WITH_STATIC_LIB_TRUE@include/sf_sdlist.c \ @SO_WITH_STATIC_LIB_TRUE@include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_TRUE@include/util_unfold.c \ @SO_WITH_STATIC_LIB_TRUE@include/sf_base64decode.c \ @SO_WITH_STATIC_LIB_TRUE@include/sf_email_attach_decode.c \ @SO_WITH_STATIC_LIB_TRUE@include/reg_test.c \ @SO_WITH_STATIC_LIB_TRUE@libs/sfparser.c @SO_WITH_STATIC_LIB_TRUE@preprocdir = $(pkgincludedir)/dynamic_preproc @SO_WITH_STATIC_LIB_TRUE@preproc_HEADERS = \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl.h \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_include.h \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_session.h \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_config.h \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_ha.h \ @SO_WITH_STATIC_LIB_TRUE@ssl_common/ssl_inspect.h @SO_WITH_STATIC_LIB_TRUE@nodist_preproc_HEADERS = libs/sfcommon.h \ @SO_WITH_STATIC_LIB_TRUE@ libs/sf_preproc_info.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_snort_packet.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_protocols.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_snort_plugin_api.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_decompression.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_decompression_define.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sfPolicyUserData.h \ @SO_WITH_STATIC_LIB_TRUE@ include/snort_debug.h \ @SO_WITH_STATIC_LIB_TRUE@ include/snort_bounds.h \ @SO_WITH_STATIC_LIB_TRUE@ include/cpuclock.h include/profiler.h \ @SO_WITH_STATIC_LIB_TRUE@ include/bitop.h include/mempool.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_sdlist_types.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_ip.h include/sfrt_flat.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sfrt_flat_dir.h \ @SO_WITH_STATIC_LIB_TRUE@ include/segment_mem.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_dynamic_common.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_dynamic_engine.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_dynamic_define.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_dynamic_meta.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_dynamic_preprocessor.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_dynamic_preproc_lib.h \ @SO_WITH_STATIC_LIB_TRUE@ include/ipv6_port.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sfPolicy.h include/sfrt.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sfrt_dir.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sfrt_trie.h \ @SO_WITH_STATIC_LIB_TRUE@ include/obfuscation.h \ @SO_WITH_STATIC_LIB_TRUE@ include/packet_time.h \ @SO_WITH_STATIC_LIB_TRUE@ include/session_api.h \ @SO_WITH_STATIC_LIB_TRUE@ include/stream_api.h \ @SO_WITH_STATIC_LIB_TRUE@ include/str_search.h \ @SO_WITH_STATIC_LIB_TRUE@ include/preprocids.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sfcontrol.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sidechannel_define.h \ @SO_WITH_STATIC_LIB_TRUE@ include/idle_processing.h \ @SO_WITH_STATIC_LIB_TRUE@ include/sf_seqnums.h \ @SO_WITH_STATIC_LIB_TRUE@ include/perf_indicators.h \ @SO_WITH_STATIC_LIB_TRUE@ include/mpse_methods.h \ @SO_WITH_STATIC_LIB_TRUE@ include/file_api.h \ @SO_WITH_STATIC_LIB_TRUE@ include/reload_api.h \ @SO_WITH_STATIC_LIB_TRUE@ include/smtp_api.h include/reg_test.h \ @SO_WITH_STATIC_LIB_TRUE@ include/memory_stats.h \ @SO_WITH_STATIC_LIB_TRUE@ $(am__append_1) $(am__append_3) @SO_WITH_STATIC_LIB_TRUE@libsf_dynamic_utils_la_CFLAGS = -fPIC -DPIC -DDYNAMIC_PREPROC_CONTEXT @SO_WITH_STATIC_LIB_TRUE@libsf_dynamic_utils_la_LDFLAGS = -static @FEAT_OPEN_APPID_FALSE@@SO_WITH_STATIC_LIB_TRUE@nodist_libsf_dynamic_utils_la_SOURCES = include/sfmemcap.c \ @FEAT_OPEN_APPID_FALSE@@SO_WITH_STATIC_LIB_TRUE@ include/sfmemcap.h \ @FEAT_OPEN_APPID_FALSE@@SO_WITH_STATIC_LIB_TRUE@ $(am__append_2) @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@nodist_libsf_dynamic_utils_la_SOURCES = include/sfprimetable.c \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/sfxhash.c \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/sfmemcap.c \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/sfmemcap.h \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/sfghash.c \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/sfhashfcn.c \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/sflsq.c \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ include/md5.c \ @FEAT_OPEN_APPID_TRUE@@SO_WITH_STATIC_LIB_TRUE@ $(am__append_2) BUILT_SOURCES = include/snort_bounds.h include/snort_debug.h \ include/preprocids.h include/profiler.h include/cpuclock.h \ include/sf_dynamic_common.h include/sf_dynamic_engine.h \ include/sf_dynamic_define.h include/sf_dynamic_meta.h \ include/sf_dynamic_preprocessor.h \ include/sf_dynamic_preproc_lib.c \ include/sf_dynamic_preproc_lib.h include/sfghash.h \ include/sfhashfcn.h include/bitop.h include/sf_ip.h \ include/sf_ip.c include/sf_ipvar.h include/sf_vartable.h \ include/ipv6_port.h include/sfsnort_dynamic_detection_lib.c \ include/sfsnort_dynamic_detection_lib.h \ include/sf_snort_packet.h include/sf_protocols.h \ include/sf_snort_plugin_api.h include/sf_decompression.h \ include/sf_decompression_define.h include/pcap_pkthdr32.h \ include/session_api.h include/stream_api.h \ include/str_search.h include/sf_types.h include/sfrt.h \ include/sfrt.c include/sfrt_dir.h include/sfrt_dir.c \ include/sfrt_flat.h include/sfrt_flat.c \ include/sfrt_flat_dir.h include/sfrt_flat_dir.c \ include/sfrt_trie.h include/segment_mem.h \ include/segment_mem.c include/mempool.h include/mempool.c \ include/sfmemcap.h include/sfmemcap.c include/sf_sdlist.h \ include/sf_sdlist_types.h include/sf_sdlist.c \ include/sfPolicyUserData.c include/sfPolicyUserData.h \ include/sfPolicy.h include/util_unfold.h include/util_unfold.c \ include/sf_base64decode.h include/sf_base64decode.c \ include/sf_email_attach_decode.h \ include/sf_email_attach_decode.c include/treenodes.h \ include/signature.h include/plugin_enum.h \ include/obfuscation.h include/packet_time.h \ include/rule_option_types.h include/event.h \ include/Unified2_common.h include/sfcontrol.h \ include/sidechannel_define.h include/idle_processing.h \ include/sf_seqnums.h include/perf_indicators.h \ include/file_api.h include/file_mail_common.h \ include/mpse_methods.h include/sfdebug.h include/sip_common.h \ include/cip_common.h include/reload_api.h include/smtp_api.h \ include/reg_test.h include/reg_test.c ssl_common/ssl.h \ ssl_common/ssl.c ssl_common/ssl_include.h \ ssl_common/ssl_config.h ssl_common/ssl_config.c \ ssl_common/ssl_session.h ssl_common/ssl_inspect.h \ ssl_common/ssl_inspect.c ssl_common/ssl_ha.h \ ssl_common/ssl_ha.c libs/sfparser.c libs/sfcommon.h \ include/memory_stats.h $(am__append_4) $(am__append_5) sed_ipv6_headers = \ sed -e "s/->iph->ip_src/->ip4_header->source/" \ -e "s/->iph->ip_dst/->ip4_header->destination/" \ -e "s/->iph->/->ip4_header->/" \ -e "s/->iph$$/->ip4_header/" \ -e "s/orig_iph/orig_ipv4h/" \ -e "s/ip_verhl/version_headerlength/" \ -e "s/ip_tos/type_service/" \ -e "s/ip_len/data_length/" \ -e "s/ip_id/identifier/" \ -e "s/ip_off/offset/" \ -e "s/ip_ttl/time_to_live/" \ -e "s/ip_proto/proto/" \ -e "s/ip_csum/checksum/" \ $$dst_header.new > $$dst_header massage_ipv6_headers = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_ipv6_headers); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_ipv6_headers); \ fi sed_headers = \ sed -e "s/Packet /SFSnortPacket /" \ -e "s/SnortPktHdr /SFSnortPktHdr /" \ -e "s/decode\.h/sf_snort_packet.h/" \ -e "/sfportobject\.h/d" \ -e "s/PortObject \*/void */g" \ $$dst_header.new > $$dst_header massage_headers = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_headers); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_headers); \ fi sed_debug_header = \ sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" \ -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" \ -e "s/; DebugMessageFunc$$/; _dpd.debugMsg/" \ -e "s/; DebugWideMessageFunc$$/; _dpd.debugWideMsg/" \ $$dst_header.new > $$dst_header copy_debug_header = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_debug_header); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_debug_header); \ fi copy_error_message = \ if test -f $$dst_header; then \ sed -e "s/ErrorMessage/_dpd.errMsg/" \ -e "s/LogMessage/_dpd.logMsg/" \ -e "s/FatalError/_dpd.fatalMsg/" \ -e "/util.h/d" \ -e "/snort.h/d" \ $$dst_header > $$dst_header.new; \ mv -f $$dst_header.new $$dst_header; \ fi copy_no_static_hash = \ if test -f $$dst_header; then \ echo "Updating " $$dst_header; \ sed -e "s/\#ifndef MODULUS_HASH/\#ifdef STATIC_HASH/" \ $$dst_header > $$dst_header.new; \ mv -f $$dst_header.new $$dst_header; \ fi replace_policy_globals = \ if test -f $$dst_header; then \ sed -e "/SharedObjectAddStarts/d" \ -e "/SharedObjectAddEnds/d" \ -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" \ -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" \ -e "s/SnortStrnStr/_dpd.SnortStrnStr/" \ -e "s/SnortStrncpy/_dpd.SnortStrncpy/" \ -e "s/ReloadAdjustRegister/_dpd.reloadAdjustRegister/" \ -e "s/session_api/_dpd.sessionAPI/" \ $$dst_header > $$dst_header.new; \ mv -f $$dst_header.new $$dst_header; \ fi copy_headers = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi sed_treenode_header = \ sed -f $(srcdir)/treenodes.sed $$dst_header.new > $$dst_header copy_treenode_header = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_treenode_header); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_treenode_header); \ fi @FEAT_FILE_INSPECT_TRUE@FILE_INSPECT_DIR = file SUBDIRS = . libs ftptelnet pop imap smtp ssh dns ssl dcerpc2 sdf sip \ reputation gtp modbus dnp3 s7commplus $(FILE_INSPECT_DIR) \ $(am__append_6) EXTRA_DIST = \ dynamic_preprocessors.vcxproj \ dynamic_preprocessors.dsp \ sf_dynamic_initialize/sf_dynamic_initialize.vcxproj \ sf_dynamic_initialize/sf_dynamic_initialize.dsp \ treenodes.sed srcinstdir = $(exec_prefix)/src/snort_dynamicsrc exported_files = include/sf_dynamic_common.h \ include/sf_dynamic_define.h include/sf_dynamic_engine.h \ include/sf_dynamic_meta.h include/sf_dynamic_preprocessor.h \ include/sf_dynamic_preproc_lib.h \ include/sf_dynamic_preproc_lib.c include/sf_ip.h \ include/sf_snort_packet.h include/sf_protocols.h \ include/sf_snort_plugin_api.h include/sf_decompression.h \ include/sf_decompression_define.h include/sf_types.h \ include/sfsnort_dynamic_detection_lib.h \ include/sfsnort_dynamic_detection_lib.c \ include/pcap_pkthdr32.h include/str_search.h \ include/session_api.h include/stream_api.h \ include/snort_debug.h include/profiler.h include/sfghash.h \ include/sfhashfcn.h include/sfmemcap.h include/bitop.h \ include/preprocids.h include/sfPolicyUserData.h \ include/util_unfold.h include/util_unfold.c \ include/sf_base64decode.h include/sf_base64decode.c \ include/sf_email_attach_decode.h \ include/sf_email_attach_decode.c include/treenodes.h \ include/signature.h include/plugin_enum.h \ include/sfPolicyUserData.c include/obfuscation.h \ include/sidechannel_define.h include/rule_option_types.h \ include/event.h include/Unified2_common.h include/sfcontrol.h \ include/idle_processing.h include/sf_seqnums.h \ include/perf_indicators.h include/file_api.h \ include/file_mail_common.h include/mpse_methods.h \ include/sfdebug.h include/sip_common.h include/cip_common.h \ include/reload_api.h include/reg_test.h include/reg_test.c \ ssl_common/ssl.h ssl_common/ssl.c ssl_common/ssl_include.h \ ssl_common/ssl_config.h ssl_common/ssl_config.c \ ssl_common/ssl_session.h ssl_common/ssl_inspect.h \ ssl_common/ssl_inspect.c ssl_common/ssl_ha.h \ ssl_common/ssl_ha.c libs/sfparser.c include/memory_stats.h \ $(am__append_7) $(am__append_8) all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-preproclibLTLIBRARIES: $(preproclib_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(preproclib_LTLIBRARIES)'; test -n "$(preproclibdir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(preproclibdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(preproclibdir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(preproclibdir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(preproclibdir)"; \ } uninstall-preproclibLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(preproclib_LTLIBRARIES)'; test -n "$(preproclibdir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(preproclibdir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(preproclibdir)/$$f"; \ done clean-preproclibLTLIBRARIES: -test -z "$(preproclib_LTLIBRARIES)" || rm -f $(preproclib_LTLIBRARIES) @list='$(preproclib_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_dynamic_preproc.la: $(libsf_dynamic_preproc_la_OBJECTS) $(libsf_dynamic_preproc_la_DEPENDENCIES) $(EXTRA_libsf_dynamic_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_dynamic_preproc_la_LINK) $(am_libsf_dynamic_preproc_la_rpath) $(libsf_dynamic_preproc_la_OBJECTS) $(libsf_dynamic_preproc_la_LIBADD) $(LIBS) libsf_dynamic_utils.la: $(libsf_dynamic_utils_la_OBJECTS) $(libsf_dynamic_utils_la_DEPENDENCIES) $(EXTRA_libsf_dynamic_utils_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_dynamic_utils_la_LINK) $(am_libsf_dynamic_utils_la_rpath) $(libsf_dynamic_utils_la_OBJECTS) $(libsf_dynamic_utils_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< libsf_dynamic_preproc_la-ssl.lo: ssl_common/ssl.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-ssl.lo `test -f 'ssl_common/ssl.c' || echo '$(srcdir)/'`ssl_common/ssl.c libsf_dynamic_preproc_la-ssl_config.lo: ssl_common/ssl_config.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-ssl_config.lo `test -f 'ssl_common/ssl_config.c' || echo '$(srcdir)/'`ssl_common/ssl_config.c libsf_dynamic_preproc_la-ssl_inspect.lo: ssl_common/ssl_inspect.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-ssl_inspect.lo `test -f 'ssl_common/ssl_inspect.c' || echo '$(srcdir)/'`ssl_common/ssl_inspect.c libsf_dynamic_preproc_la-ssl_ha.lo: ssl_common/ssl_ha.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-ssl_ha.lo `test -f 'ssl_common/ssl_ha.c' || echo '$(srcdir)/'`ssl_common/ssl_ha.c libsf_dynamic_preproc_la-sf_dynamic_preproc_lib.lo: include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sf_dynamic_preproc_lib.lo `test -f 'include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`include/sf_dynamic_preproc_lib.c libsf_dynamic_preproc_la-sf_ip.lo: include/sf_ip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sf_ip.lo `test -f 'include/sf_ip.c' || echo '$(srcdir)/'`include/sf_ip.c libsf_dynamic_preproc_la-sfrt.lo: include/sfrt.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sfrt.lo `test -f 'include/sfrt.c' || echo '$(srcdir)/'`include/sfrt.c libsf_dynamic_preproc_la-sfrt_dir.lo: include/sfrt_dir.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sfrt_dir.lo `test -f 'include/sfrt_dir.c' || echo '$(srcdir)/'`include/sfrt_dir.c libsf_dynamic_preproc_la-sfrt_flat.lo: include/sfrt_flat.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sfrt_flat.lo `test -f 'include/sfrt_flat.c' || echo '$(srcdir)/'`include/sfrt_flat.c libsf_dynamic_preproc_la-sfrt_flat_dir.lo: include/sfrt_flat_dir.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sfrt_flat_dir.lo `test -f 'include/sfrt_flat_dir.c' || echo '$(srcdir)/'`include/sfrt_flat_dir.c libsf_dynamic_preproc_la-segment_mem.lo: include/segment_mem.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-segment_mem.lo `test -f 'include/segment_mem.c' || echo '$(srcdir)/'`include/segment_mem.c libsf_dynamic_preproc_la-mempool.lo: include/mempool.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-mempool.lo `test -f 'include/mempool.c' || echo '$(srcdir)/'`include/mempool.c libsf_dynamic_preproc_la-sf_sdlist.lo: include/sf_sdlist.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sf_sdlist.lo `test -f 'include/sf_sdlist.c' || echo '$(srcdir)/'`include/sf_sdlist.c libsf_dynamic_preproc_la-sfPolicyUserData.lo: include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sfPolicyUserData.lo `test -f 'include/sfPolicyUserData.c' || echo '$(srcdir)/'`include/sfPolicyUserData.c libsf_dynamic_preproc_la-util_unfold.lo: include/util_unfold.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-util_unfold.lo `test -f 'include/util_unfold.c' || echo '$(srcdir)/'`include/util_unfold.c libsf_dynamic_preproc_la-sf_base64decode.lo: include/sf_base64decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sf_base64decode.lo `test -f 'include/sf_base64decode.c' || echo '$(srcdir)/'`include/sf_base64decode.c libsf_dynamic_preproc_la-sf_email_attach_decode.lo: include/sf_email_attach_decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sf_email_attach_decode.lo `test -f 'include/sf_email_attach_decode.c' || echo '$(srcdir)/'`include/sf_email_attach_decode.c libsf_dynamic_preproc_la-reg_test.lo: include/reg_test.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-reg_test.lo `test -f 'include/reg_test.c' || echo '$(srcdir)/'`include/reg_test.c libsf_dynamic_preproc_la-sfparser.lo: libs/sfparser.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_preproc_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_preproc_la-sfparser.lo `test -f 'libs/sfparser.c' || echo '$(srcdir)/'`libs/sfparser.c libsf_dynamic_utils_la-sfmemcap.lo: include/sfmemcap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-sfmemcap.lo `test -f 'include/sfmemcap.c' || echo '$(srcdir)/'`include/sfmemcap.c libsf_dynamic_utils_la-appdata_adjuster.lo: include/appdata_adjuster.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-appdata_adjuster.lo `test -f 'include/appdata_adjuster.c' || echo '$(srcdir)/'`include/appdata_adjuster.c libsf_dynamic_utils_la-sfxhash.lo: include/sfxhash.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-sfxhash.lo `test -f 'include/sfxhash.c' || echo '$(srcdir)/'`include/sfxhash.c libsf_dynamic_utils_la-sfhashfcn.lo: include/sfhashfcn.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-sfhashfcn.lo `test -f 'include/sfhashfcn.c' || echo '$(srcdir)/'`include/sfhashfcn.c libsf_dynamic_utils_la-sfprimetable.lo: include/sfprimetable.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-sfprimetable.lo `test -f 'include/sfprimetable.c' || echo '$(srcdir)/'`include/sfprimetable.c libsf_dynamic_utils_la-reg_test.lo: include/reg_test.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-reg_test.lo `test -f 'include/reg_test.c' || echo '$(srcdir)/'`include/reg_test.c libsf_dynamic_utils_la-sfghash.lo: include/sfghash.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-sfghash.lo `test -f 'include/sfghash.c' || echo '$(srcdir)/'`include/sfghash.c libsf_dynamic_utils_la-sflsq.lo: include/sflsq.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-sflsq.lo `test -f 'include/sflsq.c' || echo '$(srcdir)/'`include/sflsq.c libsf_dynamic_utils_la-md5.lo: include/md5.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libsf_dynamic_utils_la_CFLAGS) $(CFLAGS) -c -o libsf_dynamic_utils_la-md5.lo `test -f 'include/md5.c' || echo '$(srcdir)/'`include/md5.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-nodist_preprocHEADERS: $(nodist_preproc_HEADERS) @$(NORMAL_INSTALL) @list='$(nodist_preproc_HEADERS)'; test -n "$(preprocdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(preprocdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(preprocdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(preprocdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(preprocdir)" || exit $$?; \ done uninstall-nodist_preprocHEADERS: @$(NORMAL_UNINSTALL) @list='$(nodist_preproc_HEADERS)'; test -n "$(preprocdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(preprocdir)'; $(am__uninstall_files_from_dir) install-preprocHEADERS: $(preproc_HEADERS) @$(NORMAL_INSTALL) @list='$(preproc_HEADERS)'; test -n "$(preprocdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(preprocdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(preprocdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(preprocdir)'"; \ $(INSTALL_HEADER) $$files "$(DESTDIR)$(preprocdir)" || exit $$?; \ done uninstall-preprocHEADERS: @$(NORMAL_UNINSTALL) @list='$(preproc_HEADERS)'; test -n "$(preprocdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(preprocdir)'; $(am__uninstall_files_from_dir) # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-recursive @SO_WITH_STATIC_LIB_FALSE@all-local: all-am: Makefile $(LTLIBRARIES) $(HEADERS) all-local installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(preproclibdir)" "$(DESTDIR)$(preprocdir)" "$(DESTDIR)$(preprocdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) clean: clean-recursive clean-am: clean-generic clean-libtool clean-local \ clean-preproclibLTLIBRARIES mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-data-local install-nodist_preprocHEADERS \ install-preprocHEADERS install-preproclibLTLIBRARIES install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-local uninstall-nodist_preprocHEADERS \ uninstall-preprocHEADERS uninstall-preproclibLTLIBRARIES .MAKE: $(am__recursive_targets) all check install install-am \ install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am all-local \ check check-am clean clean-generic clean-libtool clean-local \ clean-preproclibLTLIBRARIES cscopelist-am ctags ctags-am \ distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-data-local install-dvi install-dvi-am \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man \ install-nodist_preprocHEADERS install-pdf install-pdf-am \ install-preprocHEADERS install-preproclibLTLIBRARIES \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am uninstall-local \ uninstall-nodist_preprocHEADERS uninstall-preprocHEADERS \ uninstall-preproclibLTLIBRARIES .PRECIOUS: Makefile @SO_WITH_STATIC_LIB_TRUE@all-local: $(LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@ $(MAKE) DESTDIR=`pwd`/build install-preproclibLTLIBRARIES # From main src tree include/snort_debug.h: $(srcdir)/../snort_debug.h @src_header=$?; dst_header=$@; $(copy_debug_header) include/preprocids.h: $(srcdir)/../preprocids.h @src_header=$?; dst_header=$@; $(copy_headers) include/profiler.h: $(srcdir)/../profiler.h @src_header=$?; dst_header=$@; $(copy_headers) include/cpuclock.h: $(srcdir)/../cpuclock.h @src_header=$?; dst_header=$@; $(copy_headers) include/pcap_pkthdr32.h: $(srcdir)/../pcap_pkthdr32.h @src_header=$?; dst_header=$@; $(copy_headers) include/snort_bounds.h: $(srcdir)/../snort_bounds.h @src_header=$?; dst_header=$@; $(copy_headers) include/ipv6_port.h: $(srcdir)/../ipv6_port.h @src_header=$?; dst_header=$@; $(massage_ipv6_headers) include/sf_types.h: $(srcdir)/../sf_types.h @src_header=$?; dst_header=$@; $(copy_headers) include/obfuscation.h: $(srcdir)/../obfuscation.h @src_header=$?; dst_header=$@; $(massage_headers) include/packet_time.h: $(srcdir)/../packet_time.h @src_header=$?; dst_header=$@; $(massage_headers) include/rule_option_types.h: $(srcdir)/../rule_option_types.h @src_header=$?; dst_header=$@; $(copy_headers) include/event.h: $(srcdir)/../event.h @src_header=$?; dst_header=$@; $(copy_headers) include/sidechannel_define.h: $(srcdir)/../side-channel/sidechannel_define.h @src_header=$?; dst_header=$@; $(massage_headers) include/reload_api.h: $(srcdir)/../reload_api.h @src_header=$?; dst_header=$@; $(massage_headers) include/smtp_api.h: $(srcdir)/../smtp_api.h @src_header=$?; dst_header=$@; $(massage_headers) include/reg_test.h: $(srcdir)/../reg_test.h @src_header=$?; dst_header=$@; $(copy_headers) include/reg_test.c: $(srcdir)/../reg_test.c @src_header=$?; dst_header=$@; $(copy_headers) # From dynamic-plugins include/sf_dynamic_common.h: $(srcdir)/../dynamic-plugins/sf_dynamic_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_engine.h: $(srcdir)/../dynamic-plugins/sf_dynamic_engine.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_define.h: $(srcdir)/../dynamic-plugins/sf_dynamic_define.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_meta.h: $(srcdir)/../dynamic-plugins/sf_dynamic_meta.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_preprocessor.h: $(srcdir)/../dynamic-plugins/sf_dynamic_preprocessor.h @src_header=$?; dst_header=$@; $(massage_headers) # From dynamic-plugins/sf_preproc_example include/sf_dynamic_preproc_lib.c: $(srcdir)/../dynamic-plugins/sf_preproc_example/sf_dynamic_preproc_lib.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_preproc_lib.h: $(srcdir)/../dynamic-plugins/sf_preproc_example/sf_dynamic_preproc_lib.h @src_header=$?; dst_header=$@; $(copy_headers) # From Utils include/sfghash.h: $(srcdir)/../sfutil/sfghash.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfhashfcn.h: $(srcdir)/../sfutil/sfhashfcn.h @src_header=$?; dst_header=$@; $(copy_headers) include/bitop.h: $(srcdir)/../sfutil/bitop.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_ip.h: $(srcdir)/../sfutil/sf_ip.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_ip.c: $(srcdir)/../sfutil/sf_ip.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_ipvar.h: $(srcdir)/../sfutil/sf_ipvar.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_vartable.h: $(srcdir)/../sfutil/sf_vartable.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt.h: $(srcdir)/../sfutil/sfrt.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt.c: $(srcdir)/../sfutil/sfrt.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_dir.h: $(srcdir)/../sfutil/sfrt_dir.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_dir.c: $(srcdir)/../sfutil/sfrt_dir.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat.h: $(srcdir)/../sfutil/sfrt_flat.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat.c: $(srcdir)/../sfutil/sfrt_flat.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat_dir.h: $(srcdir)/../sfutil/sfrt_flat_dir.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat_dir.c: $(srcdir)/../sfutil/sfrt_flat_dir.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_trie.h: $(srcdir)/../sfutil/sfrt_trie.h @src_header=$?; dst_header=$@; $(copy_headers) include/segment_mem.c: $(srcdir)/../sfutil/segment_mem.c @src_header=$?; dst_header=$@; $(copy_headers) include/segment_mem.h: $(srcdir)/../sfutil/segment_mem.h @src_header=$?; dst_header=$@; $(copy_headers) include/mempool.h: $(srcdir)/../mempool.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/mempool.c: $(srcdir)/../mempool.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfmemcap.h: $(srcdir)/../sfutil/sfmemcap.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfmemcap.c: $(srcdir)/../sfutil/sfmemcap.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sf_sdlist.h: $(srcdir)/../sf_sdlist.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sf_sdlist_types.h: $(srcdir)/../sf_sdlist_types.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sf_sdlist.c: $(srcdir)/../sf_sdlist.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfPolicyUserData.c: $(srcdir)/../sfutil/sfPolicyUserData.c @src_header=$?; dst_header=$@; $(copy_headers); $(replace_policy_globals) include/sfPolicyUserData.h: $(srcdir)/../sfutil/sfPolicyUserData.h @src_header=$?; dst_header=$@; $(copy_headers); $(replace_policy_globals) include/sfPolicy.h: $(srcdir)/../sfutil/sfPolicy.h @src_header=$?; dst_header=$@; $(copy_headers); $(replace_policy_globals) include/util_unfold.h: $(srcdir)/../sfutil/util_unfold.h @src_header=$?; dst_header=$@; $(copy_headers) include/util_unfold.c: $(srcdir)/../sfutil/util_unfold.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_base64decode.h: $(srcdir)/../sfutil/sf_base64decode.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_base64decode.c: $(srcdir)/../sfutil/sf_base64decode.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_email_attach_decode.h: $(srcdir)/../sfutil/sf_email_attach_decode.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_email_attach_decode.c: $(srcdir)/../sfutil/sf_email_attach_decode.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/Unified2_common.h: $(srcdir)/../sfutil/Unified2_common.h @src_header=$?; dst_header=$@; $(copy_headers) # From dynamic-plugins/sf_engine/examples include/sfsnort_dynamic_detection_lib.c: $(srcdir)/../dynamic-plugins/sf_engine/examples/sfsnort_dynamic_detection_lib.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfsnort_dynamic_detection_lib.h: $(srcdir)/../dynamic-plugins/sf_engine/examples/sfsnort_dynamic_detection_lib.h @src_header=$?; dst_header=$@; $(copy_headers) # From dynamic-plugins/sf_engine include/sf_snort_packet.h: $(srcdir)/../dynamic-plugins/sf_engine/sf_snort_packet.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_protocols.h: $(srcdir)/../sf_protocols.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_snort_plugin_api.h: $(srcdir)/../dynamic-plugins/sf_engine/sf_snort_plugin_api.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_decompression.h: $(srcdir)/../dynamic-plugins/sf_engine/sf_decompression.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_decompression_define.h: $(srcdir)/../dynamic-plugins/sf_decompression_define.h @src_header=$?; dst_header=$@; $(copy_headers) # Session API/String Searching, massage it to use SFSnortPacket include/session_api.h: $(srcdir)/../preprocessors/session_api.h @src_header=$?; dst_header=$@; $(massage_headers) # Stream API/String Searching, massage it to use SFSnortPacket include/stream_api.h: $(srcdir)/../preprocessors/stream_api.h @src_header=$?; dst_header=$@; $(massage_headers) include/str_search.h: $(srcdir)/../preprocessors/str_search.h @src_header=$?; dst_header=$@; $(massage_headers) include/treenodes.h: $(srcdir)/../treenodes.h @src_header=$?; dst_header=$@; $(copy_treenode_header) include/signature.h: $(srcdir)/../signature.h @src_header=$?; dst_header=$@; $(copy_treenode_header) include/plugin_enum.h: $(srcdir)/../plugin_enum.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfcontrol.h: $(top_srcdir)/src/control/sfcontrol.h @src_header=$?; dst_header=$@; $(copy_headers) include/idle_processing.h: $(top_srcdir)/src/idle_processing.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_seqnums.h: $(top_srcdir)/src/sfutil/sf_seqnums.h @src_header=$?; dst_header=$@; $(copy_headers) include/perf_indicators.h: $(srcdir)/../preprocessors/perf_indicators.h @src_header=$?; dst_header=$@; $(copy_headers) include/file_api.h: $(top_srcdir)/src/file-process/file_api.h @src_header=$?; dst_header=$@; $(copy_headers) include/file_mail_common.h: $(top_srcdir)/src/file-process/file_mail_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfdebug.h: $(srcdir)/../sfutil/sfdebug.h @src_header=$?; dst_header=$@; $(copy_headers) include/mpse_methods.h: $(srcdir)/../sfutil/mpse_methods.h @src_header=$?; dst_header=$@; $(copy_headers) include/sip_common.h: $(srcdir)/../preprocessors/sip_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/cip_common.h: $(srcdir)/../preprocessors/cip_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/appId.h: $(srcdir)/appid/appId.h @src_header=$?; dst_header=$@; $(copy_headers) @FEAT_OPEN_APPID_TRUE@include/appIdApi.h: $(srcdir)/../appIdApi.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers) @FEAT_OPEN_APPID_TRUE@include/thirdparty_appid_types.h: $(srcdir)/appid/thirdparty_appid_types.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers) @FEAT_OPEN_APPID_TRUE@include/thirdparty_appid_api.h: $(srcdir)/appid/thirdparty_appid_api.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers) @FEAT_OPEN_APPID_TRUE@include/dns_defs.h: $(srcdir)/appid/dns_defs.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers) @FEAT_OPEN_APPID_TRUE@include/md5.c: $(srcdir)/../sfutil/md5.c @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers) @FEAT_OPEN_APPID_TRUE@include/md5.h: $(srcdir)/../sfutil/md5.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers) @FEAT_OPEN_APPID_TRUE@include/sfprimetable.h: $(srcdir)/../sfutil/sfprimetable.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @FEAT_OPEN_APPID_TRUE@include/sfprimetable.c: $(srcdir)/../sfutil/sfprimetable.c @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @FEAT_OPEN_APPID_TRUE@include/sfxhash.h: $(srcdir)/../sfutil/sfxhash.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @FEAT_OPEN_APPID_TRUE@include/sfxhash.c: $(srcdir)/../sfutil/sfxhash.c @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @FEAT_OPEN_APPID_TRUE@include/sfghash.c: $(srcdir)/../sfutil/sfghash.c @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @FEAT_OPEN_APPID_TRUE@include/sfhashfcn.c: $(srcdir)/../sfutil/sfhashfcn.c @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals); $(copy_no_static_hash) @FEAT_OPEN_APPID_TRUE@include/sflsq.h: $(srcdir)/../sfutil/sflsq.h @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @FEAT_OPEN_APPID_TRUE@include/sflsq.c: $(srcdir)/../sfutil/sflsq.c @FEAT_OPEN_APPID_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@include/sfxhash.h: $(srcdir)/../sfutil/sfxhash.h @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@include/sfxhash.c: $(srcdir)/../sfutil/sfxhash.c @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@include/sfprimetable.h: $(srcdir)/../sfutil/sfprimetable.h @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@include/sfprimetable.c: $(srcdir)/../sfutil/sfprimetable.c @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@include/sfhashfcn.c: $(srcdir)/../sfutil/sfhashfcn.c @BUILD_SNORT_RELOAD_TRUE@@FEAT_OPEN_APPID_FALSE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals); $(copy_no_static_hash) @BUILD_SNORT_RELOAD_TRUE@include/appdata_adjuster.c: $(srcdir)/../reload-adjust/appdata_adjuster.c @BUILD_SNORT_RELOAD_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) @BUILD_SNORT_RELOAD_TRUE@include/appdata_adjuster.h: $(srcdir)/../reload-adjust/appdata_adjuster.h @BUILD_SNORT_RELOAD_TRUE@ @src_header=$?; dst_header=$@; $(copy_headers) include/memory_stats.h: $(srcdir)/../memory_stats.h @src_header=$?; dst_header=$@; $(copy_headers) clean-local: rm -rf include build install-data-local: @for f in $(exported_files); do \ truefile=`echo $$f | sed -e "s/.*\///"`; \ $(mkinstalldirs) $(DESTDIR)$(srcinstdir); \ if test -f $(srcdir)/$$f; then p=$(srcdir)/$$f; else p=$$f; fi; \ $(INSTALL_DATA) $$p $(DESTDIR)$(srcinstdir)/$$truefile; \ done uninstall-local: @for f in $(exported_files); do \ truefile=`echo $$f | sed -e "s/.*\///"`; \ $(mkinstalldirs) $(DESTDIR)$(srcinstdir); \ rm -f $(DESTDIR)$(srcinstdir)/$$truefile; \ done # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/smtp/0000755000175000017500000000000014242725715017207 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/smtp/smtp_xlink2state.c0000644000175000017500000002007414241076402022660 0ustar apoapo/*************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************ * * smtp_xlink2state.c * * Author: Andy Mullican * * Description: * * This file handles the X-Link2State vulnerability. * * Entry point function: * * ParseXLink2State() * * ************************************************************************/ #ifndef WIN32 #include #endif #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_smtp.h" #include "smtp_config.h" #include "smtp_util.h" #include "smtp_log.h" #include "smtp_xlink2state.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_packet.h" #define XLINK_OTHER 1 #define XLINK_FIRST 2 #define XLINK_CHUNK 3 #define XLINK_LEN 12 /* strlen("X-LINK2STATE") */ /* X-Link2State overlong length */ #define XLINK2STATE_MAX_LEN 520 extern SMTP *smtp_ssn; extern SMTPConfig *smtp_eval_config; /* Prototypes */ static uint32_t get_xlink_hex_value(const uint8_t *, const uint8_t *); static char get_xlink_keyword(const uint8_t *, const uint8_t *); /* * Extract a number from a string * * @param buf pointer to beginning of buffer to parse * @param end end pointer of buffer to parse * * @return unsigned long value of number extracted * * @note this could be more efficient, but the search buffer should be pretty short */ static uint32_t get_xlink_hex_value(const uint8_t *buf, const uint8_t *end) { char c; uint32_t value = 0; const uint8_t *hex_end; if ((end - buf) < 8) return 0; hex_end = buf + 8; while (buf < hex_end) { c = toupper((int)*buf); /* Make sure it is a number or hex char; if not return with what we have */ if (isdigit((int)c)) { c = c - '0'; } else if (c >= 'A' && c <= 'F') { c = (c - 'A') + 10; } else { return value; } value = (value * 16) + c; buf++; } return value; } /* * Check for X-LINK2STATE keywords FIRST or CHUNK * * * @param x pointer to "X-LINK2STATE" in buffer * @param x_len length of buffer after x * * @retval int identifies which keyword found, if any */ static char get_xlink_keyword(const uint8_t *ptr, const uint8_t *end) { int len; if (ptr == NULL || end == NULL) return XLINK_OTHER; ptr += XLINK_LEN; if (ptr >= end) return XLINK_OTHER; /* Skip over spaces */ while (ptr < end && isspace((int)*ptr)) { ptr++; } len = end - ptr; if (len > 5 && strncasecmp((const char *)ptr, "FIRST", 5) == 0) { return XLINK_FIRST; } else if (len > 5 && strncasecmp((const char *)ptr, "CHUNK", 5) == 0) { return XLINK_CHUNK; } return XLINK_OTHER; } /* * Handle X-Link2State vulnerability * * From Lurene Grenier: The X-LINK2STATE command always takes the following form: X-LINK2STATE [FIRST|NEXT|LAST] CHUNK= The overwrite occurs when three criteria are met: No chunk identifier exists - ie neither FIRST, NEXT, or LAST are specified No previous FIRST chunk was sent has a length greater than 520 bytes Normally you send a FIRST chunk, then some intermediary chunks marked with either NEXT or not marked, then finally a LAST chunk. If no first chunk is sent, and a chunk with no specifier is sent, it assumes it must append to something, but it has nothing to append to, so an overwrite occurs. Sending out of order chunks WITH specifiers results in an exception. So simply: if (gotFirstChunk) next; # chunks came with proper first chunk specified if (/X-LINK2STATE [FIRST|NEXT|LAST] CHUNK/) { if (/X-LINK2STATE FIRST CHUNK/) gotFirstChunk = TRUE; next; # some specifier is marked } if (chunkLen > 520) attempt = TRUE; # Gotcha! Usually it takes more than one unspecified packet in a row, but I think this is just a symptom of the fact that we're triggering a heap overwrite, and not a condition of the bug. However, if we're still getting FPs this might be an avenue to try. * * @param p standard Packet structure * @param x pointer to "X-LINK2STATE" in buffer * * @retval 1 if alert raised * @retval 0 if no alert raised */ int ParseXLink2State(SFSnortPacket *p, const uint8_t *ptr) { uint8_t *lf = NULL; uint32_t len = 0; char x_keyword; const uint8_t *end; if (p == NULL || ptr == NULL) return 0; /* If we got a FIRST chunk on this stream, this is not an exploit */ if (smtp_ssn->session_flags & SMTP_FLAG_XLINK2STATE_GOTFIRSTCHUNK) return 0; /* Calculate length from pointer to end of packet data */ end = p->payload + p->payload_size; if (ptr >= end) return 0; /* Check for "FIRST" or "CHUNK" after X-LINK2STATE */ x_keyword = get_xlink_keyword(ptr, end); if (x_keyword != XLINK_CHUNK) { if (x_keyword == XLINK_FIRST) smtp_ssn->session_flags |= SMTP_FLAG_XLINK2STATE_GOTFIRSTCHUNK; return 0; } ptr = (uint8_t *)memchr((char *)ptr, '=', end - ptr); if (ptr == NULL) return 0; /* move past '=' and make sure we're within bounds */ ptr++; if (ptr >= end) return 0; /* Look for one of two patterns: * * ... CHUNK={0000006d} MULTI (5) ({00000000051} ... * ... CHUNK=AAAAAAAAAAAAAAAAAAAAAAAAAAAAA\n */ if (*ptr == '{') { /* move past '{' and make sure we're within bounds */ ptr++; if ((ptr + 8) >= end) return 0; /* Get length - can we always trust it? */ len = get_xlink_hex_value(ptr, end); } else { lf = (uint8_t *)memchr((char *)ptr, '\n', end - ptr); if (lf == NULL) return 0; len = lf - ptr; } if (len > XLINK2STATE_MAX_LEN) { /* Need to drop the packet if we're told to * (outside of whether its thresholded). */ if (smtp_eval_config->drop_xlink2state) { _dpd.inlineDropSessionAndReset(p); if (*_dpd.pkt_tracer_enabled) { _dpd.addPktTrace(VERDICT_REASON_XLINK2STATE, snprintf(_dpd.trace, _dpd.traceMax, "X-Link2State: buffer overflow vulnerability detected in SMTP, gid %u, sid %u, drop\n", GENERATOR_SMTP, SMTP_XLINK2STATE_OVERFLOW)); } else _dpd.addPktTrace(VERDICT_REASON_XLINK2STATE, 0); } SMTP_GenerateAlert(SMTP_XLINK2STATE_OVERFLOW, "%s", SMTP_XLINK2STATE_OVERFLOW_STR); smtp_ssn->session_flags |= SMTP_FLAG_XLINK2STATE_ALERTED; return 1; } /* Check for more than one command in packet */ ptr = (uint8_t *)memchr((char *)ptr, '\n', end - ptr); if (ptr == NULL) return 0; /* move past '\n' */ ptr++; if (ptr < end) { ParseXLink2State(p, ptr); } return 0; } snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_xlink2state.h0000644000175000017500000000265414241076403022672 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************* * smtp_xlink2state.h * * Author: Andy Mullican * *************************************************************************/ #ifndef __SMTP_XLINK2STATE_H__ #define __SMTP_XLINK2STATE_H__ #include "sf_snort_packet.h" int ParseXLink2State(SFSnortPacket *, const uint8_t *); #endif /* __SMTP_XLINK2STATE_H__ */ snort-2.9.20/src/dynamic-preprocessors/smtp/snort_smtp.h0000644000175000017500000001650214241076406021567 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * **************************************************************************/ /************************************************************************** * * snort_smtp.h * * Author: Andy Mullican * Author: Todd Wease * * Description: * * This file defines everything specific to the SMTP preprocessor. * **************************************************************************/ #ifndef __SMTP_H__ #define __SMTP_H__ /* Includes ***************************************************************/ #include #include "sf_snort_packet.h" #include "ssl.h" #include "smtp_config.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "mempool.h" #include "sf_email_attach_decode.h" #include "file_mail_common.h" #include "file_api.h" #ifdef DEBUG #include "sf_types.h" #endif /**************************************************************************/ /* Defines ****************************************************************/ /* Direction packet is coming from, if we can figure it out */ #define SMTP_PKT_FROM_UNKNOWN 0 #define SMTP_PKT_FROM_CLIENT 1 #define SMTP_PKT_FROM_SERVER 2 /* Inspection type */ #define SMTP_STATELESS 0 #define SMTP_STATEFUL 1 #define SEARCH_CMD 0 #define SEARCH_RESP 1 #define SEARCH_HDR 2 #define SEARCH_DATA_END 3 #define NUM_SEARCHES 4 #define BOUNDARY 0 #define STATE_CONNECT 0 #define STATE_COMMAND 1 /* Command state of SMTP transaction */ #define STATE_DATA 2 /* Data state */ #define STATE_BDATA 3 /* Binary data state */ #define STATE_TLS_CLIENT_PEND 4 /* Got STARTTLS */ #define STATE_TLS_SERVER_PEND 5 /* Got STARTTLS */ #define STATE_TLS_DATA 6 /* Successful handshake, TLS encrypted data */ #define STATE_AUTH 7 #define STATE_XEXCH50 8 #define STATE_UNKNOWN 9 #define STATE_DATA_INIT 0 #define STATE_DATA_HEADER 1 /* Data header section of data state */ #define STATE_DATA_BODY 2 /* Data body section of data state */ #define STATE_MIME_HEADER 3 /* MIME header section within data section */ #define STATE_DATA_UNKNOWN 4 /* state flags */ #define SMTP_FLAG_GOT_MAIL_CMD 0x00000001 #define SMTP_FLAG_GOT_RCPT_CMD 0x00000002 #define SMTP_FLAG_BDAT 0x00001000 #define SMTP_FLAG_ABORT 0x00002000 /* session flags */ #define SMTP_FLAG_XLINK2STATE_GOTFIRSTCHUNK 0x00000001 #define SMTP_FLAG_XLINK2STATE_ALERTED 0x00000002 #define SMTP_FLAG_NEXT_STATE_UNKNOWN 0x00000004 #define SMTP_FLAG_GOT_NON_REBUILT 0x00000008 #define SMTP_FLAG_CHECK_SSL 0x00000010 #define SMTP_SSL_ERROR_FLAGS (SSL_BOGUS_HS_DIR_FLAG | \ SSL_BAD_VER_FLAG | \ SSL_BAD_TYPE_FLAG | \ SSL_UNKNOWN_FLAG) /* Maximum length of header chars before colon, based on Exim 4.32 exploit */ #define MAX_HEADER_NAME_LEN 64 #define SMTP_PROTO_REF_STR "smtp" #define MAX_AUTH_NAME_LEN 20 /* Max length of SASL mechanisms, defined in RFC 4422 */ /**************************************************************************/ /* Data structures ********************************************************/ typedef enum _SMTPCmdEnum { CMD_ATRN = 0, CMD_AUTH, CMD_BDAT, CMD_DATA, CMD_DEBUG, CMD_EHLO, CMD_EMAL, CMD_ESAM, CMD_ESND, CMD_ESOM, CMD_ETRN, CMD_EVFY, CMD_EXPN, CMD_HELO, CMD_HELP, CMD_IDENT, CMD_MAIL, CMD_NOOP, CMD_ONEX, CMD_QUEU, CMD_QUIT, CMD_RCPT, CMD_RSET, CMD_SAML, CMD_SEND, CMD_SIZE, CMD_STARTTLS, CMD_SOML, CMD_TICK, CMD_TIME, CMD_TURN, CMD_TURNME, CMD_VERB, CMD_VRFY, CMD_X_EXPS, CMD_XADR, CMD_XAUTH, CMD_XCIR, CMD_XEXCH50, CMD_XGEN, CMD_XLICENSE, CMD_X_LINK2STATE, CMD_XQUE, CMD_XSTA, CMD_XTRN, CMD_XUSR, CMD_ABORT, CMD_LAST } SMTPCmdEnum; typedef enum _SMTPRespEnum { RESP_220 = 0, RESP_221, RESP_235, RESP_250, RESP_334, RESP_354, RESP_421, RESP_450, RESP_451, RESP_452, RESP_500, RESP_501, RESP_502, RESP_503, RESP_504, RESP_535, RESP_550, RESP_551, RESP_552, RESP_553, RESP_554, RESP_LAST } SMTPRespEnum; typedef enum _SMTPHdrEnum { HDR_CONTENT_TYPE = 0, HDR_CONT_TRANS_ENC, HDR_CONT_DISP, HDR_LAST } SMTPHdrEnum; typedef enum _SMTPDataEndEnum { DATA_END_1 = 0, DATA_END_2, DATA_END_3, DATA_END_4, DATA_END_LAST } SMTPDataEndEnum; typedef struct _SMTPSearchInfo { int id; int index; int length; } SMTPSearchInfo; typedef struct _SMTPAuthName { int length; char name[MAX_AUTH_NAME_LEN]; } SMTPAuthName; typedef struct _SMTP { int state; int state_flags; int session_flags; int alert_mask; int reassembling; uint32_t dat_chunk; #ifdef DEBUG_MSGS uint64_t session_number; #endif /* may want to keep track where packet didn't end with end of line marker int cur_client_line_len; int cur_server_line_len; */ MimeState mime_ssn; SMTPAuthName *auth_name; /* In future if we look at forwarded mail (message/rfc822) we may * need to keep track of additional mime boundaries * SMTPMimeBoundary mime_boundary[8]; * int current_mime_boundary; */ tSfPolicyId policy_id; uint32_t flow_id; tSfPolicyUserContextId config; } SMTP; /**************************************************************************/ /* Function prototypes ****************************************************/ void SMTP_InitCmds(SMTPConfig *config); void SMTP_SearchInit(void); void SMTP_Free(void); void SnortSMTP(SFSnortPacket *); int SMTP_IsServer(uint16_t); void SMTP_FreeConfig(SMTPConfig *); void SMTP_FreeConfigs(tSfPolicyUserContextId); int SMTP_GetFilename(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int SMTP_GetMailFrom(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int SMTP_GetRcptTo(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int SMTP_GetEmailHdrs(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int SMTP_SessionExist(void *data); void SMTP_MempoolInit(uint32_t, uint32_t); /**************************************************************************/ #endif /* __SMTP_H__ */ snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_paf.c0000644000175000017500000003265514241076373021175 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include #include "sf_types.h" #include "smtp_paf.h" #include "spp_smtp.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "snort_smtp.h" #include "file_api.h" #include "smtp_log.h" static uint8_t smtp_paf_id = 0; extern tSfPolicyUserContextId smtp_config; /* State tracker for MIME PAF */ typedef enum _SmtpDataCMD { SMTP_PAF_BDAT_CMD, SMTP_PAF_DATA_CMD, SMTP_PAF_XEXCH50_CMD, SMTP_PAF_STRARTTLS_CMD, SMTP_PAF_AUTH_CMD } SmtpDataCMD; typedef struct _PAFToken { char *name; int name_len; int search_id; bool has_length; } SmtpPAFToken; const SmtpPAFToken smtp_paf_tokens[] = { {"BDAT", 4, SMTP_PAF_BDAT_CMD, true}, {"DATA", 4, SMTP_PAF_DATA_CMD, true}, {"XEXCH50", 7, SMTP_PAF_XEXCH50_CMD, true}, {"STRARTTLS", 9, SMTP_PAF_STRARTTLS_CMD, false}, {"AUTH", 4, SMTP_PAF_AUTH_CMD, false}, {NULL, 0, 0, false} }; /* State tracker for data command */ typedef enum _SmtpPafCmdState { SMTP_PAF_CMD_UNKNOWN, SMTP_PAF_CMD_START, SMTP_PAF_CMD_DETECT, SMTP_PAF_CMD_DATA_LENGTH_STATE, SMTP_PAF_CMD_DATA_END_STATE } SmtpPafCmdState; /* State tracker for SMTP PAF */ typedef enum _SmtpPafState { SMTP_PAF_CMD_STATE, SMTP_PAF_DATA_STATE } SmtpPafState; typedef struct _SmtpCmdSearchInfo { SmtpPafCmdState cmd_state; int search_id; char *search_state; } SmtpCmdSearchInfo; /* State tracker for SMTP PAF */ typedef struct _SmtpPafData { DataEndState data_end_state; uint32_t length; SmtpPafState smtp_state; bool end_of_data; bool alert_generated; SmtpCmdSearchInfo cmd_info; MimeDataPafInfo data_info; } SmtpPafData; /* State tracker for SMTP PAF */ typedef enum _SmtpPafDataLenStatus { SMTP_PAF_LENGTH_INVALID, SMTP_PAF_LENGTH_CONTINUE, SMTP_PAF_LENGTH_DONE } SmtpPafDataLenStatus; /* Process responses from server, flushed at EOL*/ static inline PAF_Status smtp_paf_server(SmtpPafData *pfdata, const uint8_t* data, uint32_t len, uint32_t* fp) { const char *pch; pfdata->smtp_state = SMTP_PAF_CMD_STATE; pch = memchr (data, '\n', len); if (pch != NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Find end of line!\n");); *fp = (uint32_t)(pch - (const char*)data) + 1; return PAF_FLUSH; } return PAF_SEARCH; } /* Initialize command search based on first byte of command*/ static inline char* init_cmd_search(SmtpCmdSearchInfo *search_info, uint8_t ch) { /* Use the first byte to choose data command)*/ switch (ch) { case 'b': case 'B': search_info->search_state = &smtp_paf_tokens[SMTP_PAF_BDAT_CMD].name[1]; search_info->search_id = SMTP_PAF_BDAT_CMD; break; case 'd': case 'D': search_info->search_state = &smtp_paf_tokens[SMTP_PAF_DATA_CMD].name[1]; search_info->search_id = SMTP_PAF_DATA_CMD; break; case 'x': case 'X': search_info->search_state = &smtp_paf_tokens[SMTP_PAF_XEXCH50_CMD].name[1]; search_info->search_id = SMTP_PAF_XEXCH50_CMD; break; case 's': case 'S': search_info->search_state = &smtp_paf_tokens[SMTP_PAF_STRARTTLS_CMD].name[1]; search_info->search_id = SMTP_PAF_STRARTTLS_CMD; break; case 'a': case 'A': search_info->search_state = &smtp_paf_tokens[SMTP_PAF_AUTH_CMD].name[1]; search_info->search_id = SMTP_PAF_AUTH_CMD; break; default: search_info->search_state = NULL; break; } return search_info->search_state; } /* Validate whether the command is a data command*/ static inline void validate_command(SmtpCmdSearchInfo *search_info, uint8_t val) { if (search_info->search_state ) { uint8_t expected = *(search_info->search_state); if (toupper(val) == toupper(expected)) { search_info->search_state++; /* Found data command, change to SMTP_PAF_CMD_DATA_LENGTH_STATE */ if (*(search_info->search_state) == '\0') { search_info->search_state = NULL; search_info->cmd_state = SMTP_PAF_CMD_DATA_LENGTH_STATE; return; } } else { search_info->search_state = NULL; search_info->cmd_state = SMTP_PAF_CMD_UNKNOWN; return; } } } /* Get the length of data from data command */ static SmtpPafDataLenStatus get_length(char c, uint32_t *len ) { uint32_t length = *len; if (isblank(c)) { if (length) { *len = length; return SMTP_PAF_LENGTH_DONE; } } else if (isdigit(c)) { uint64_t tmp_len = (10 * length) + (c - '0'); if (tmp_len < UINT32_MAX) length = (uint32_t) tmp_len; else { *len = 0; return SMTP_PAF_LENGTH_INVALID; } } else { *len = 0; return SMTP_PAF_LENGTH_INVALID; } *len = length; return SMTP_PAF_LENGTH_CONTINUE; } /* Reset data states*/ static inline void reset_data_states(SmtpPafData *pfdata) { _dpd.fileAPI->reset_mime_paf_state(&(pfdata->data_info)); pfdata->length = 0; } /* Currently, we support "BDAT", "DATA", "XEXCH50", "STRARTTLS" * Each data command should start from offset 0, * since previous data have been flushed */ static inline bool process_command(SmtpPafData *pfdata, uint8_t val) { /*State unknown, start cmd search start from EOL, flush on EOL*/ if (val == '\n') { if (pfdata->cmd_info.cmd_state == SMTP_PAF_CMD_DATA_END_STATE) { pfdata->smtp_state = SMTP_PAF_DATA_STATE; reset_data_states(pfdata); pfdata->end_of_data = false; } pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_START; return 1; } switch (pfdata->cmd_info.cmd_state) { case SMTP_PAF_CMD_UNKNOWN: break; case SMTP_PAF_CMD_START: if (init_cmd_search(&(pfdata->cmd_info), val)) pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_DETECT; else pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_UNKNOWN; break; case SMTP_PAF_CMD_DETECT: /* Search for data command */ validate_command(&(pfdata->cmd_info), val); break; case SMTP_PAF_CMD_DATA_LENGTH_STATE: /* Continue finding the data length ...*/ if (get_length(val, &pfdata->length) != SMTP_PAF_LENGTH_CONTINUE) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Find data length: %d\n", pfdata->length);); pfdata->cmd_info.cmd_state = SMTP_PAF_CMD_DATA_END_STATE; } break; case SMTP_PAF_CMD_DATA_END_STATE: /* Change to Data state at EOL*/ break; default: break; } return 0; } /* Flush based on data length*/ static inline bool flush_based_length(SmtpPafData *pfdata) { if (pfdata->length) { pfdata->length--; if (!pfdata->length) return true; } return false; } /* Process data length if specified, or end of data marker, flush at the end * or * Process data boundary and flush each file based on boundary*/ static inline bool process_data(SmtpPafData *pfdata, uint8_t data) { if (flush_based_length(pfdata)|| _dpd.fileAPI->check_data_end(&(pfdata->data_end_state), data)) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "End of data\n");); /*Clean up states*/ pfdata->smtp_state = SMTP_PAF_CMD_STATE; pfdata->end_of_data = true; reset_data_states(pfdata); return true; } return _dpd.fileAPI->process_mime_paf_data(&(pfdata->data_info), data); } /* Process commands/data from client * For command, flush at EOL * For data, flush at boundary */ static inline PAF_Status smtp_paf_client(SmtpPafData *pfdata, const uint8_t* data, uint32_t len, uint32_t* fp) { uint32_t i; uint32_t boundary_start = 0; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); SMTPConfig* smtp_current_config = (SMTPConfig *)sfPolicyUserDataGet(smtp_config, policy_id); DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "From client: %s \n", data);); for (i = 0; i < len; i++) { uint8_t ch = data[i]; switch (pfdata->smtp_state) { case SMTP_PAF_CMD_STATE: if (process_command(pfdata, ch)) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Flush command: %s \n", data);); *fp = i + 1; return PAF_FLUSH; } break; case SMTP_PAF_DATA_STATE: if (pfdata->cmd_info.search_id == SMTP_PAF_AUTH_CMD) { if ( smtp_current_config && (smtp_current_config->max_auth_command_line_len != 0) && (i + pfdata->data_info.boundary_len) > smtp_current_config->max_auth_command_line_len && !pfdata->alert_generated) { if( !smtp_current_config->no_alerts ) { _dpd.alertAdd(GENERATOR_SMTP, SMTP_AUTH_COMMAND_OVERFLOW, 1, 0, 3, SMTP_AUTH_COMMAND_OVERFLOW_STR, 0); pfdata->alert_generated = true; } } if (ch == '\n') { pfdata->smtp_state = SMTP_PAF_CMD_STATE; pfdata->end_of_data = true; reset_data_states(pfdata); *fp = i + 1; return PAF_FLUSH; } else if (i == len-1) pfdata->data_info.boundary_len += len; } else if (process_data(pfdata, ch)) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Flush data!\n");); *fp = i + 1; return PAF_FLUSH; } if (pfdata->data_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN) boundary_start = i; break; default: break; } } if ( scanning_boundary(&pfdata->data_info, boundary_start, fp) ) return PAF_LIMIT; return PAF_SEARCH; } /* Main PAF function*/ static PAF_Status smtp_paf_eval(void* ssn, void** ps, const uint8_t* data, uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh) { SmtpPafData *pfdata = *(SmtpPafData **)ps; if (pfdata == NULL) { if (_dpd.fileAPI->check_paf_abort(ssn)) return PAF_ABORT; pfdata = _dpd.snortAlloc(1, sizeof(*pfdata), PP_SMTP, PP_MEM_CATEGORY_SESSION); if (pfdata == NULL) { return PAF_ABORT; } *ps = pfdata; pfdata->data_end_state = PAF_DATA_END_UNKNOWN; pfdata->smtp_state = SMTP_PAF_CMD_STATE; pfdata->alert_generated = false; } /*From server side (responses)*/ if (*flags & FLAG_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "From server!\n");); return smtp_paf_server(pfdata, data, len, fp); } else /*From client side (requests)*/ { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "From client!\n");); return smtp_paf_client(pfdata, data, len, fp); } return PAF_SEARCH; } bool is_data_end (void* ssn) { if ( ssn ) { SmtpPafData** s = (SmtpPafData **)_dpd.streamAPI->get_paf_user_data(ssn, 1, smtp_paf_id); if ( s && (*s) ) return ((*s)->end_of_data); } return false; } void smtp_paf_cleanup(void *pafData) { if (pafData) { _dpd.snortFree(pafData, sizeof(SmtpPafData), PP_SMTP, PP_MEM_CATEGORY_SESSION); } } #ifdef TARGET_BASED void register_smtp_paf_service (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { smtp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, smtp_paf_eval, true); smtp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false,smtp_paf_eval, true); _dpd.streamAPI->register_paf_free(smtp_paf_id, smtp_paf_cleanup); } } #endif void register_smtp_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { smtp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, true, smtp_paf_eval, true); smtp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, false, smtp_paf_eval, true); _dpd.streamAPI->register_paf_free(smtp_paf_id, smtp_paf_cleanup); } } snort-2.9.20/src/dynamic-preprocessors/smtp/snort_smtp.c0000644000175000017500000016551414241076405021571 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * snort_smtp.c * * Author: Andy Mullican * Author: Todd Wease * * Description: * * This file handles SMTP protocol checking and normalization. * * Entry point functions: * * SnortSMTP() * SMTP_Init() * SMTP_Free() * **************************************************************************/ /* Includes ***************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sf_types.h" #include "snort_smtp.h" #include "smtp_config.h" #include "smtp_normalize.h" #include "smtp_util.h" #include "smtp_log.h" #include "smtp_xlink2state.h" #include "sf_snort_packet.h" #include "stream_api.h" #include "snort_debug.h" #include "profiler.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" #include "ssl.h" #include "ssl_include.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "Unified2_common.h" #include "file_api.h" #ifdef DEBUG_MSGS #include "sf_types.h" #endif #include "smtp_paf.h" #ifdef DUMP_BUFFER #include "smtp_buffer_dump.h" #endif /**************************************************************************/ /* Externs ****************************************************************/ #ifdef PERF_PROFILING extern PreprocStats smtpDetectPerfStats; extern int smtpDetectCalled; #endif extern tSfPolicyUserContextId smtp_config; extern SMTPConfig *smtp_eval_config; extern MemPool *smtp_mime_mempool; extern MemPool *smtp_mempool; #ifdef DEBUG_MSGS extern char smtp_print_buffer[]; #endif /**************************************************************************/ /* Globals ****************************************************************/ const SMTPToken smtp_known_cmds[] = { {"ATRN", 4, CMD_ATRN, SMTP_CMD_TYPE_NORMAL}, {"AUTH", 4, CMD_AUTH, SMTP_CMD_TYPE_AUTH}, {"BDAT", 4, CMD_BDAT, SMTP_CMD_TYPE_BDATA}, {"DATA", 4, CMD_DATA, SMTP_CMD_TYPE_DATA}, {"DEBUG", 5, CMD_DEBUG, SMTP_CMD_TYPE_NORMAL}, {"EHLO", 4, CMD_EHLO, SMTP_CMD_TYPE_NORMAL}, {"EMAL", 4, CMD_EMAL, SMTP_CMD_TYPE_NORMAL}, {"ESAM", 4, CMD_ESAM, SMTP_CMD_TYPE_NORMAL}, {"ESND", 4, CMD_ESND, SMTP_CMD_TYPE_NORMAL}, {"ESOM", 4, CMD_ESOM, SMTP_CMD_TYPE_NORMAL}, {"ETRN", 4, CMD_ETRN, SMTP_CMD_TYPE_NORMAL}, {"EVFY", 4, CMD_EVFY, SMTP_CMD_TYPE_NORMAL}, {"EXPN", 4, CMD_EXPN, SMTP_CMD_TYPE_NORMAL}, {"HELO", 4, CMD_HELO, SMTP_CMD_TYPE_NORMAL}, {"HELP", 4, CMD_HELP, SMTP_CMD_TYPE_NORMAL}, {"IDENT", 5, CMD_IDENT, SMTP_CMD_TYPE_NORMAL}, {"MAIL", 4, CMD_MAIL, SMTP_CMD_TYPE_NORMAL}, {"NOOP", 4, CMD_NOOP, SMTP_CMD_TYPE_NORMAL}, {"ONEX", 4, CMD_ONEX, SMTP_CMD_TYPE_NORMAL}, {"QUEU", 4, CMD_QUEU, SMTP_CMD_TYPE_NORMAL}, {"QUIT", 4, CMD_QUIT, SMTP_CMD_TYPE_NORMAL}, {"RCPT", 4, CMD_RCPT, SMTP_CMD_TYPE_NORMAL}, {"RSET", 4, CMD_RSET, SMTP_CMD_TYPE_NORMAL}, {"SAML", 4, CMD_SAML, SMTP_CMD_TYPE_NORMAL}, {"SEND", 4, CMD_SEND, SMTP_CMD_TYPE_NORMAL}, {"SIZE", 4, CMD_SIZE, SMTP_CMD_TYPE_NORMAL}, {"STARTTLS", 8, CMD_STARTTLS, SMTP_CMD_TYPE_NORMAL}, {"SOML", 4, CMD_SOML, SMTP_CMD_TYPE_NORMAL}, {"TICK", 4, CMD_TICK, SMTP_CMD_TYPE_NORMAL}, {"TIME", 4, CMD_TIME, SMTP_CMD_TYPE_NORMAL}, {"TURN", 4, CMD_TURN, SMTP_CMD_TYPE_NORMAL}, {"TURNME", 6, CMD_TURNME, SMTP_CMD_TYPE_NORMAL}, {"VERB", 4, CMD_VERB, SMTP_CMD_TYPE_NORMAL}, {"VRFY", 4, CMD_VRFY, SMTP_CMD_TYPE_NORMAL}, {"X-EXPS", 6, CMD_X_EXPS, SMTP_CMD_TYPE_AUTH}, {"XADR", 4, CMD_XADR, SMTP_CMD_TYPE_NORMAL}, {"XAUTH", 5, CMD_XAUTH, SMTP_CMD_TYPE_AUTH}, {"XCIR", 4, CMD_XCIR, SMTP_CMD_TYPE_NORMAL}, {"XEXCH50", 7, CMD_XEXCH50, SMTP_CMD_TYPE_BDATA}, {"XGEN", 4, CMD_XGEN, SMTP_CMD_TYPE_NORMAL}, {"XLICENSE", 8, CMD_XLICENSE, SMTP_CMD_TYPE_NORMAL}, {"X-LINK2STATE", 12, CMD_X_LINK2STATE, SMTP_CMD_TYPE_NORMAL}, {"XQUE", 4, CMD_XQUE, SMTP_CMD_TYPE_NORMAL}, {"XSTA", 4, CMD_XSTA, SMTP_CMD_TYPE_NORMAL}, {"XTRN", 4, CMD_XTRN, SMTP_CMD_TYPE_NORMAL}, {"XUSR", 4, CMD_XUSR, SMTP_CMD_TYPE_NORMAL}, {"*", 1, CMD_ABORT, SMTP_CMD_TYPE_NORMAL}, {NULL, 0, 0, SMTP_CMD_TYPE_NORMAL} }; const SMTPToken smtp_resps[] = { {"220", 3, RESP_220, SMTP_CMD_TYPE_NORMAL}, /* Service ready - initial response and STARTTLS response */ {"221", 3, RESP_221, SMTP_CMD_TYPE_NORMAL}, /* Goodbye - response to QUIT */ {"235", 3, RESP_235, SMTP_CMD_TYPE_NORMAL}, /* Auth done response */ {"250", 3, RESP_250, SMTP_CMD_TYPE_NORMAL}, /* Requested mail action okay, completed */ {"334", 3, RESP_334, SMTP_CMD_TYPE_NORMAL}, /* Auth intermediate response */ {"354", 3, RESP_354, SMTP_CMD_TYPE_NORMAL}, /* Start mail input - data response */ {"421", 3, RESP_421, SMTP_CMD_TYPE_NORMAL}, /* Service not availiable - closes connection */ {"450", 3, RESP_450, SMTP_CMD_TYPE_NORMAL}, /* Mailbox unavailable */ {"451", 3, RESP_451, SMTP_CMD_TYPE_NORMAL}, /* Local error in processing */ {"452", 3, RESP_452, SMTP_CMD_TYPE_NORMAL}, /* Insufficient system storage */ {"500", 3, RESP_500, SMTP_CMD_TYPE_NORMAL}, /* Command unrecognized */ {"501", 3, RESP_501, SMTP_CMD_TYPE_NORMAL}, /* Syntax error in parameters or arguments */ {"502", 3, RESP_502, SMTP_CMD_TYPE_NORMAL}, /* Command not implemented */ {"503", 3, RESP_503, SMTP_CMD_TYPE_NORMAL}, /* Bad sequence of commands */ {"504", 3, RESP_504, SMTP_CMD_TYPE_NORMAL}, /* Command parameter not implemented */ {"535", 3, RESP_535, SMTP_CMD_TYPE_NORMAL}, /* Authentication failed */ {"550", 3, RESP_550, SMTP_CMD_TYPE_NORMAL}, /* Action not taken - mailbox unavailable */ {"551", 3, RESP_551, SMTP_CMD_TYPE_NORMAL}, /* User not local; please try */ {"552", 3, RESP_552, SMTP_CMD_TYPE_NORMAL}, /* Mail action aborted: exceeded storage allocation */ {"553", 3, RESP_553, SMTP_CMD_TYPE_NORMAL}, /* Action not taken: mailbox name not allowed */ {"554", 3, RESP_554, SMTP_CMD_TYPE_NORMAL}, /* Transaction failed */ {NULL, 0, 0, SMTP_CMD_TYPE_NORMAL} }; typedef struct _SMTPAuth { char *name; int name_len; } SMTPAuth; /* Cyrus SASL authentication mechanisms ANONYMOUS, PLAIN and LOGIN * does not have context */ const SMTPAuth smtp_auth_no_ctx[] = { { "ANONYMOUS", 9 }, { "PLAIN", 5 }, { "LOGIN", 5 }, {NULL, 0} }; SMTP *smtp_ssn = NULL; SMTP smtp_no_session; char smtp_normalizing; SMTPSearchInfo smtp_search_info; #ifdef DEBUG_MSGS uint64_t smtp_session_counter = 0; #endif #ifdef TARGET_BASED int16_t smtp_proto_id; #endif void *smtp_resp_search_mpse = NULL; SMTPSearch smtp_resp_search[RESP_LAST]; SMTPSearch *smtp_current_search = NULL; /**************************************************************************/ /* Private functions ******************************************************/ static int SMTP_Setup(SFSnortPacket *p, SMTP *ssn); static void SMTP_ResetState(void); static void SMTP_SessionFree(void *); static int SMTP_GetPacketDirection(SFSnortPacket *, int); static void SMTP_ProcessClientPacket(SFSnortPacket *); static void SMTP_ProcessServerPacket(SFSnortPacket *, int *); static void SMTP_DisableDetect(SFSnortPacket *); static const uint8_t * SMTP_HandleCommand(SFSnortPacket *, const uint8_t *, const uint8_t *); static int SMTP_SearchStrFound(void *, void *, int, void *, void *); static int SMTP_Inspect(SFSnortPacket *); void SMTP_Set_flow_id( void *app_data, uint32_t fid ); static int SMTP_HandleHeaderLine(void *pkt, const uint8_t *ptr, const uint8_t *eol, int max_header_len, void *mime_ssn); static int SMTP_NormalizeData(void *pkt, const uint8_t *ptr, const uint8_t *data_end); MimeMethods mime_methods = {SMTP_HandleHeaderLine, SMTP_NormalizeData, SMTP_DecodeAlert, SMTP_ResetState, is_data_end}; /**************************************************************************/ void SMTP_InitCmds(SMTPConfig *config) { const SMTPToken *tmp; if (config == NULL) return; /* add one to CMD_LAST for NULL entry */ config->cmds = (SMTPToken *)_dpd.snortAlloc(CMD_LAST + 1, sizeof(SMTPToken), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (config->cmds == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for smtp " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } for (tmp = &smtp_known_cmds[0]; tmp->name != NULL; tmp++) { config->cmds[tmp->search_id].name_len = tmp->name_len; config->cmds[tmp->search_id].search_id = tmp->search_id; config->cmds[tmp->search_id].name = strdup(tmp->name); config->cmds[tmp->search_id].type = tmp->type; if (config->cmds[tmp->search_id].name == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for smtp " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } } /* initialize memory for command searches */ config->cmd_search = (SMTPSearch *)_dpd.snortAlloc(CMD_LAST, sizeof(SMTPSearch), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (config->cmd_search == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for smtp " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } config->num_cmds = CMD_LAST; } /* * Initialize SMTP searches * * @param none * * @return none */ void SMTP_SearchInit(void) { const SMTPToken *tmp; /* Response search */ smtp_resp_search_mpse = _dpd.searchAPI->search_instance_new(); if (smtp_resp_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate SMTP " "response search.\n"); } for (tmp = &smtp_resps[0]; tmp->name != NULL; tmp++) { smtp_resp_search[tmp->search_id].name = tmp->name; smtp_resp_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(smtp_resp_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(smtp_resp_search_mpse); } /* * Reset SMTP session state * * @param none * * @return none */ static void SMTP_ResetState(void) { smtp_ssn->state = STATE_COMMAND; smtp_ssn->state_flags = 0; } /* * Given a server configuration and a port number, we decide if the port is * in the SMTP server port list. * * @param port the port number to compare with the configuration * * @return integer * @retval 0 means that the port is not a server port * @retval !0 means that the port is a server port */ int SMTP_IsServer(uint16_t port) { if( isPortEnabled( smtp_eval_config->ports, port ) ) return 1; return 0; } static SMTP * SMTP_GetNewSession(SFSnortPacket *p, tSfPolicyId policy_id) { SMTP *ssn; SMTPConfig *pPolicyConfig = NULL; int ret = 0; pPolicyConfig = (SMTPConfig *)sfPolicyUserDataGetCurrent(smtp_config); if ((p->stream_session == NULL) || (pPolicyConfig->inspection_type == SMTP_STATELESS)) { #ifdef DEBUG_MSGS if (p->stream_session == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Stream session pointer is NULL - " "treating packet as stateless\n");); } #endif memset(&smtp_no_session, 0, sizeof(SMTP)); ssn = &smtp_no_session; ssn->session_flags |= SMTP_FLAG_CHECK_SSL; ssn->mime_ssn.log_config = &(smtp_eval_config->log_config); ssn->mime_ssn.decode_conf = &(smtp_eval_config->decode_conf); ssn->mime_ssn.mime_mempool = smtp_mime_mempool; ssn->mime_ssn.log_mempool = smtp_mempool; ssn->mime_ssn.mime_stats = &(smtp_stats.mime_stats); ssn->mime_ssn.methods = &(mime_methods); ssn->state = STATE_UNKNOWN; return ssn; } DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Creating new session data structure\n");); ssn = (SMTP *)_dpd.snortAlloc(1, sizeof(SMTP), PP_SMTP, PP_MEM_CATEGORY_SESSION); if (ssn == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate SMTP session data\n"); } smtp_ssn = ssn; smtp_ssn->mime_ssn.log_config = &(smtp_eval_config->log_config); smtp_ssn->mime_ssn.decode_conf = &(smtp_eval_config->decode_conf); smtp_ssn->mime_ssn.mime_mempool = smtp_mime_mempool; smtp_ssn->mime_ssn.log_mempool = smtp_mempool; smtp_ssn->mime_ssn.mime_stats = &(smtp_stats.mime_stats); smtp_ssn->mime_ssn.methods = &(mime_methods); if ((ret=_dpd.fileAPI->set_log_buffers(&(smtp_ssn->mime_ssn.log_state), &(pPolicyConfig->log_config),smtp_mempool, p->stream_session, PP_SMTP)) < 0) { if( ret == -1 ) { if(smtp_stats.log_memcap_exceeded % 10000 == 0) { _dpd.logMsg("WARNING: SMTP memcap exceeded.\n"); } smtp_stats.log_memcap_exceeded++; } _dpd.snortFree(ssn, sizeof(*ssn), PP_SMTP, PP_MEM_CATEGORY_SESSION); return NULL; } _dpd.sessionAPI->set_application_data(p->stream_session, PP_SMTP, ssn, &SMTP_SessionFree); if (p->flags & SSNFLAG_MIDSTREAM) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Got midstream packet - " "setting state to unknown\n");); ssn->state = STATE_UNKNOWN; } #ifdef DEBUG_MSGS smtp_session_counter++; ssn->session_number = smtp_session_counter; #endif if (p->stream_session != NULL) { /* check to see if we're doing client reassembly in stream */ if (_dpd.streamAPI->get_reassembly_direction(p->stream_session) & SSN_DIR_FROM_CLIENT) ssn->reassembling = 1; } ssn->policy_id = policy_id; ssn->config = smtp_config; ssn->flow_id = 0; pPolicyConfig->ref_count++; smtp_stats.sessions++; smtp_stats.conc_sessions++; smtp_stats.cur_sessions++; if(smtp_stats.max_conc_sessions < smtp_stats.conc_sessions) smtp_stats.max_conc_sessions = smtp_stats.conc_sessions; return ssn; } /* * Do first-packet setup * * @param p standard Packet structure * * @return none */ static int SMTP_Setup(SFSnortPacket *p, SMTP *ssn) { int flags = 0; int pkt_dir; if (p->stream_session != NULL) { /* set flags to session flags */ flags = _dpd.sessionAPI->get_session_flags(p->stream_session); } /* Figure out direction of packet */ pkt_dir = SMTP_GetPacketDirection(p, flags); DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Session number: "STDu64"\n", ssn->session_number);); /* reset check ssl flag for new packet */ if (!(ssn->session_flags & SMTP_FLAG_CHECK_SSL)) ssn->session_flags |= SMTP_FLAG_CHECK_SSL; /* Check to see if there is a reassembly gap. If so, we won't know * what state we're in when we get the _next_ reassembled packet */ if ((pkt_dir != SMTP_PKT_FROM_SERVER) && (p->flags & FLAG_REBUILT_STREAM)) { int missing_in_rebuilt = _dpd.streamAPI->missing_in_reassembled(p->stream_session, SSN_DIR_FROM_CLIENT); if (ssn->session_flags & SMTP_FLAG_NEXT_STATE_UNKNOWN) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Found gap in previous reassembly buffer - " "set state to unknown\n");); ssn->state = STATE_UNKNOWN; ssn->session_flags &= ~SMTP_FLAG_NEXT_STATE_UNKNOWN; } if (missing_in_rebuilt == SSN_MISSING_BEFORE) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Found missing packets before " "in reassembly buffer - set state to unknown\n");); ssn->state = STATE_UNKNOWN; } } return pkt_dir; } /* * Determine packet direction * * @param p standard Packet structure * * @return none */ static int SMTP_GetPacketDirection(SFSnortPacket *p, int flags) { int pkt_direction = SMTP_PKT_FROM_UNKNOWN; if (flags & SSNFLAG_MIDSTREAM) { if (SMTP_IsServer(p->src_port) && !SMTP_IsServer(p->dst_port)) { pkt_direction = SMTP_PKT_FROM_SERVER; } else if (!SMTP_IsServer(p->src_port) && SMTP_IsServer(p->dst_port)) { pkt_direction = SMTP_PKT_FROM_CLIENT; } } else { if (p->flags & FLAG_FROM_SERVER) { pkt_direction = SMTP_PKT_FROM_SERVER; } else if (p->flags & FLAG_FROM_CLIENT) { pkt_direction = SMTP_PKT_FROM_CLIENT; } /* if direction is still unknown ... */ if (pkt_direction == SMTP_PKT_FROM_UNKNOWN) { if (SMTP_IsServer(p->src_port) && !SMTP_IsServer(p->dst_port)) { pkt_direction = SMTP_PKT_FROM_SERVER; } else if (!SMTP_IsServer(p->src_port) && SMTP_IsServer(p->dst_port)) { pkt_direction = SMTP_PKT_FROM_CLIENT; } } } return pkt_direction; } /* * Free SMTP-specific related to this session * * @param v pointer to SMTP session structure * * * @return none */ static void SMTP_SessionFree(void *session_data) { SMTP *smtp = (SMTP *)session_data; #ifdef SNORT_RELOAD SMTPConfig *pPolicyConfig = NULL; #endif ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if (smtp == NULL) return; #ifdef SNORT_RELOAD pPolicyConfig = (SMTPConfig *)sfPolicyUserDataGet(smtp->config, smtp->policy_id); if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (smtp->config != smtp_config)) { sfPolicyUserDataClear (smtp->config, smtp->policy_id); SMTP_FreeConfig(pPolicyConfig); /* No more outstanding policies for this config */ if (sfPolicyUserPolicyGetActive(smtp->config) == 0) SMTP_FreeConfigs(smtp->config); } } #endif if(smtp->mime_ssn.decode_state != NULL) { mempool_free(smtp_mime_mempool, smtp->mime_ssn.decode_bkt); _dpd.snortFree(smtp->mime_ssn.decode_state, sizeof(Email_DecodeState), PP_SMTP, PP_MEM_CATEGORY_SESSION); } if(smtp->mime_ssn.log_state != NULL) { mempool_free(smtp_mempool, smtp->mime_ssn.log_state->log_hdrs_bkt); _dpd.snortFree(smtp->mime_ssn.log_state, sizeof(MAIL_LogState), PP_SMTP, PP_MEM_CATEGORY_SESSION); } if(smtp->auth_name != NULL) { _dpd.snortFree(smtp->auth_name, sizeof(*(smtp->auth_name)), PP_SMTP, PP_MEM_CATEGORY_SESSION); } if ( ssl_cb ) ssl_cb->session_free(smtp->flow_id); _dpd.snortFree(smtp, sizeof(*smtp), PP_SMTP, PP_MEM_CATEGORY_SESSION); if(smtp_stats.conc_sessions) smtp_stats.conc_sessions--; if(smtp_stats.cur_sessions) smtp_stats.cur_sessions--; } static int SMTP_FreeConfigsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SMTPConfig *pPolicyConfig = (SMTPConfig *)pData; //do any housekeeping before freeing SMTPConfig sfPolicyUserDataClear (config, policyId); SMTP_FreeConfig(pPolicyConfig); return 0; } void SMTP_FreeConfigs(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, SMTP_FreeConfigsPolicy); sfPolicyConfigDelete(config); } void SMTP_FreeConfig(SMTPConfig *config) { if (config == NULL) return; if (config->cmds != NULL) { SMTPToken *tmp = config->cmds; for (; tmp->name != NULL; tmp++) _dpd.snortFree(tmp->name, sizeof(*(tmp->name)), PP_SMTP, PP_MEM_CATEGORY_CONFIG); _dpd.snortFree(config->cmds, sizeof(*(config->cmds)), PP_SMTP, PP_MEM_CATEGORY_CONFIG); } if (config->cmd_config != NULL) _dpd.snortFree(config->cmd_config, sizeof(*(config->cmd_config)), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (config->cmd_search_mpse != NULL) _dpd.searchAPI->search_instance_free(config->cmd_search_mpse); if (config->cmd_search != NULL) _dpd.snortFree(config->cmd_search, sizeof(*(config->cmd_search)), PP_SMTP, PP_MEM_CATEGORY_CONFIG); _dpd.snortFree(config, sizeof(*config), PP_SMTP, PP_MEM_CATEGORY_CONFIG); } /* * Free anything that needs it before shutting down preprocessor * * @param none * * @return none */ void SMTP_Free(void) { SMTP_FreeConfigs(smtp_config); smtp_config = NULL; if (smtp_resp_search_mpse != NULL) _dpd.searchAPI->search_instance_free(smtp_resp_search_mpse); } /* * Callback function for string search * * @param id id in array of search strings from smtp_config.cmds * @param index index in array of search strings from smtp_config.cmds * @param data buffer passed in to search function * * @return response * @retval 1 commands caller to stop searching */ static int SMTP_SearchStrFound(void *id, void *unused, int index, void *data, void *unused2) { int search_id = (int)(uintptr_t)id; smtp_search_info.id = search_id; smtp_search_info.index = index; smtp_search_info.length = smtp_current_search[search_id].name_len; /* Returning non-zero stops search, which is okay since we only look for one at a time */ return 1; } static bool SMTP_IsAuthCtxIgnored(const uint8_t *start, int length) { const SMTPAuth *tmp; for (tmp = &smtp_auth_no_ctx[0]; tmp->name != NULL; tmp++) { if ((tmp->name_len == length) && (!memcmp(start, tmp->name, length))) return true; } return false; } static bool SMTP_IsAuthChanged(const uint8_t *start_ptr, const uint8_t *end_ptr) { int length; bool auth_changed = false; uint8_t *start = (uint8_t *)start_ptr; uint8_t *end = (uint8_t *) end_ptr; while ((start < end) && isspace(*start)) start++; while ((start < end) && isspace(*(end-1))) end--; if (start >= end) return auth_changed; length = end - start; if (length > MAX_AUTH_NAME_LEN) return auth_changed; if (SMTP_IsAuthCtxIgnored(start, length)) return auth_changed; /* if authentication mechanism is set, compare it with current one*/ if (smtp_ssn->auth_name) { if (smtp_ssn->auth_name->length != length) auth_changed = true; else if (memcmp(start, smtp_ssn->auth_name->name, length)) auth_changed = true; } else smtp_ssn->auth_name = _dpd.snortAlloc(1, sizeof(*(smtp_ssn->auth_name)), PP_SMTP, PP_MEM_CATEGORY_SESSION); /* save the current authentication mechanism*/ if (!smtp_ssn->auth_name) return auth_changed; if (auth_changed || (!smtp_ssn->auth_name->length)) { memcpy(smtp_ssn->auth_name->name, start, length); smtp_ssn->auth_name->length = length; } return auth_changed; } /* * Handle COMMAND state * * @param p standard Packet structure * @param ptr pointer into p->payload buffer to start looking at data * @param end points to end of p->payload buffer * * @return pointer into p->payload where we stopped looking at data * will be end of line or end of packet */ static const uint8_t * SMTP_HandleCommand(SFSnortPacket *p, const uint8_t *ptr, const uint8_t *end) { const uint8_t *eol; /* end of line */ const uint8_t *eolm; /* end of line marker */ int cmd_line_len; int ret; int cmd_found; char alert_long_command_line = 0; /* get end of line and end of line marker */ SMTP_GetEOL(ptr, end, &eol, &eolm); /* calculate length of command line */ cmd_line_len = eol - ptr; /* check for command line exceeding maximum * do this before checking for a command since this could overflow * some server's buffers without the presence of a known command */ if ((smtp_eval_config->max_command_line_len != 0) && (cmd_line_len > smtp_eval_config->max_command_line_len)) { alert_long_command_line = 1; } /* TODO If the end of line marker coincides with the end of payload we can't be * sure that we got a command and not a substring which we could tell through * inspection of the next packet. Maybe a command pending state where the first * char in the next packet is checked for a space and end of line marker */ /* do not confine since there could be space chars before command */ smtp_current_search = &smtp_eval_config->cmd_search[0]; cmd_found = _dpd.searchAPI->search_instance_find (smtp_eval_config->cmd_search_mpse, (const char *)ptr, eolm - ptr, 0, SMTP_SearchStrFound); /* see if we actually found a command and not a substring */ if (cmd_found > 0) { const uint8_t *tmp = ptr; const uint8_t *cmd_start = ptr + smtp_search_info.index; const uint8_t *cmd_end = cmd_start + smtp_search_info.length; /* move past spaces up until start of command */ while ((tmp < cmd_start) && isspace((int)*tmp)) tmp++; /* if not all spaces before command, we found a * substring */ if (tmp != cmd_start) cmd_found = 0; /* if we're before the end of line marker and the next * character is not whitespace, we found a substring */ if ((cmd_end < eolm) && !isspace((int)*cmd_end)) cmd_found = 0; /* there is a chance that end of command coincides with the end of payload * in which case, it could be a substring, but for now, we will treat it as found */ } /* if command not found, alert and move on */ if (!cmd_found) { /* If we missed one or more packets we might not actually be in the command * state. Check to see if we're encrypted */ if (smtp_ssn->state == STATE_UNKNOWN) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Command not found, but state is " "unknown - checking for SSL\n");); /* check for encrypted */ if ((smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL) && (IsSSL(ptr, end - ptr, p->flags))) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Packet is SSL encrypted\n");); smtp_ssn->state = STATE_TLS_DATA; /* Ignore data */ if (smtp_eval_config->ignore_tls_data) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Ignoring encrypted data\n");); _dpd.SetAltDecode(0); } return end; } else { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Not SSL - try data state\n");); /* don't check for ssl again in this packet */ if (smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL) smtp_ssn->session_flags &= ~SMTP_FLAG_CHECK_SSL; smtp_ssn->state = STATE_DATA; smtp_ssn->mime_ssn.data_state = STATE_DATA_UNKNOWN; return ptr; } } else { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "No known command found\n");); if (smtp_ssn->state != STATE_AUTH) { if (smtp_eval_config->alert_unknown_cmds) { SMTP_GenerateAlert(SMTP_UNKNOWN_CMD, "%s", SMTP_UNKNOWN_CMD_STR); } if (alert_long_command_line) { SMTP_GenerateAlert(SMTP_COMMAND_OVERFLOW, "%s: more than %d chars", SMTP_COMMAND_OVERFLOW_STR, smtp_eval_config->max_command_line_len); } } /* if normalizing, copy line to alt buffer */ if (smtp_normalizing) { ret = SMTP_CopyToAltBuffer(p, ptr, eol - ptr); if (ret == -1) return NULL; } return eol; } } /* At this point we have definitely found a legitimate command */ DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "%s\n", smtp_eval_config->cmds[smtp_search_info.id].name);); /* check if max command line length for a specific command is exceeded */ if (smtp_eval_config->cmd_config[smtp_search_info.id].max_line_len != 0) { if (cmd_line_len > smtp_eval_config->cmd_config[smtp_search_info.id].max_line_len) { SMTP_GenerateAlert(SMTP_SPECIFIC_CMD_OVERFLOW, "%s: %s, %d chars", SMTP_SPECIFIC_CMD_OVERFLOW_STR, smtp_eval_config->cmd_search[smtp_search_info.id].name, cmd_line_len); } } else if (alert_long_command_line) { SMTP_GenerateAlert(SMTP_COMMAND_OVERFLOW, "%s: more than %d chars", SMTP_COMMAND_OVERFLOW_STR, smtp_eval_config->max_command_line_len); } /* Are we alerting on this command? */ if (smtp_eval_config->cmd_config[smtp_search_info.id].alert) { SMTP_GenerateAlert(SMTP_ILLEGAL_CMD, "%s: %s", SMTP_ILLEGAL_CMD_STR, smtp_eval_config->cmds[smtp_search_info.id].name); } switch (smtp_search_info.id) { /* unless we do our own parsing of MAIL and RCTP commands we have to assume they * are ok unless we got a server error in which case we flush and if this is a * reassembled packet, the last command in this packet will be the command that * caused the error */ case CMD_MAIL: smtp_ssn->state_flags |= SMTP_FLAG_GOT_MAIL_CMD; if( smtp_eval_config->log_config.log_mailfrom ) { if(!SMTP_CopyEmailID(ptr, eolm - ptr, CMD_MAIL, smtp_ssn->mime_ssn.log_state)) smtp_ssn->mime_ssn.log_flags |= FLAG_MAIL_FROM_PRESENT; } break; case CMD_RCPT: if ((smtp_ssn->state_flags & SMTP_FLAG_GOT_MAIL_CMD) || smtp_ssn->state == STATE_UNKNOWN) { smtp_ssn->state_flags |= SMTP_FLAG_GOT_RCPT_CMD; } if( smtp_eval_config->log_config.log_rcptto) { if(!SMTP_CopyEmailID(ptr, eolm - ptr, CMD_RCPT, smtp_ssn->mime_ssn.log_state)) smtp_ssn->mime_ssn.log_flags |= FLAG_RCPT_TO_PRESENT; } break; case CMD_RSET: case CMD_HELO: case CMD_EHLO: case CMD_QUIT: smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD); break; case CMD_STARTTLS: /* if reassembled we flush after seeing a 220 so this should be the last * command in reassembled packet and if not reassembled it should be the * last line in the packet as you can't pipeline the tls hello */ if (eol == end) smtp_ssn->state = STATE_TLS_CLIENT_PEND; break; case CMD_X_LINK2STATE: if (smtp_eval_config->alert_xlink2state) ParseXLink2State(p, ptr + smtp_search_info.index); break; case CMD_AUTH: smtp_ssn->state = STATE_AUTH; if (SMTP_IsAuthChanged(ptr + smtp_search_info.index + smtp_search_info.length, eolm) && (smtp_ssn->state_flags & SMTP_FLAG_ABORT)) { SMTP_GenerateAlert(SMTP_AUTH_ABORT_AUTH, "%s", SMTP_AUTH_ABORT_AUTH_STR); } smtp_ssn->state_flags &= ~(SMTP_FLAG_ABORT); break; case CMD_ABORT: smtp_ssn->state_flags |= SMTP_FLAG_ABORT; break; default: switch (smtp_eval_config->cmds[smtp_search_info.id].type) { case SMTP_CMD_TYPE_DATA: if ((smtp_ssn->state_flags & SMTP_FLAG_GOT_RCPT_CMD) || smtp_ssn->state == STATE_UNKNOWN) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Set to data state.\n");); smtp_ssn->state = STATE_DATA; smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD); } else { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Didn't get MAIL -> RCPT command sequence - " "stay in command state.\n");); } break; case SMTP_CMD_TYPE_BDATA: if ((smtp_ssn->state_flags & (SMTP_FLAG_GOT_RCPT_CMD | SMTP_FLAG_BDAT)) || (smtp_ssn->state == STATE_UNKNOWN)) { const uint8_t *begin_chunk; const uint8_t *end_chunk; const uint8_t *tmp; int num_digits; int ten_power; uint32_t dat_chunk = 0; begin_chunk = ptr + smtp_search_info.index + smtp_search_info.length; while ((begin_chunk < eolm) && isspace((int)*begin_chunk)) begin_chunk++; /* bad BDAT command - needs chunk argument */ if (begin_chunk == eolm) break; end_chunk = begin_chunk; while ((end_chunk < eolm) && isdigit((int)*end_chunk)) end_chunk++; /* didn't get all digits */ if ((end_chunk < eolm) && !isspace((int)*end_chunk)) break; /* get chunk size */ num_digits = end_chunk - begin_chunk; /* more than 9 digits could potentially overflow a 32 bit integer * most servers won't accept this much in a chunk */ if (num_digits > 9) break; tmp = end_chunk; for (ten_power = 1, tmp--; tmp >= begin_chunk; ten_power *= 10, tmp--) dat_chunk += (*tmp - '0') * ten_power; if (smtp_search_info.id == CMD_BDAT) { /* got a valid chunk size - check to see if this is the last chunk */ const uint8_t *last = end_chunk; bool bdat_last = false; while ((last < eolm) && isspace((int)*last)) last++; if (((eolm - last) >= 4) && (strncasecmp("LAST", (const char *)last, 4) == 0)) { bdat_last = true; } if (bdat_last || (dat_chunk == 0)) smtp_ssn->state_flags &= ~(SMTP_FLAG_BDAT); else smtp_ssn->state_flags |= SMTP_FLAG_BDAT; smtp_ssn->state = STATE_BDATA; smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD); } else if (smtp_search_info.id == CMD_XEXCH50) { smtp_ssn->state = STATE_XEXCH50; } else { smtp_ssn->state = STATE_BDATA; smtp_ssn->state_flags &= ~(SMTP_FLAG_GOT_MAIL_CMD | SMTP_FLAG_GOT_RCPT_CMD); } smtp_ssn->dat_chunk = dat_chunk; } break; case SMTP_CMD_TYPE_AUTH: smtp_ssn->state = STATE_AUTH; break; default: break; } break; } /* Since we found a command, if state is still unknown, * set to command state */ if (smtp_ssn->state == STATE_UNKNOWN) smtp_ssn->state = STATE_COMMAND; /* normalize command line */ if (smtp_eval_config->normalize == NORMALIZE_ALL || smtp_eval_config->cmd_config[smtp_search_info.id].normalize) { ret = SMTP_NormalizeCmd(p, ptr, eolm, eol); if (ret == -1) return NULL; } else if (smtp_normalizing) /* Already normalizing */ { ret = SMTP_CopyToAltBuffer(p, ptr, eol - ptr); if (ret == -1) return NULL; } return eol; } static int SMTP_NormalizeData(void *pkt, const uint8_t *ptr, const uint8_t *data_end) { SFSnortPacket *p = (SFSnortPacket *)pkt; /* if we're ignoring data and not already normalizing, copy everything * up to here into alt buffer so detection engine doesn't have * to look at the data; otherwise, if we're normalizing and not * ignoring data, copy all of the data into the alt buffer */ if (smtp_eval_config->decode_conf.ignore_data && !smtp_normalizing) { return SMTP_CopyToAltBuffer(p, p->payload, ptr - p->payload); } else if (!smtp_eval_config->decode_conf.ignore_data && smtp_normalizing) { return SMTP_CopyToAltBuffer(p, ptr, data_end - ptr); } return 0; } static int SMTP_HandleHeaderLine(void *pkt, const uint8_t *ptr, const uint8_t *eol, int max_header_len, void *ssn) { int ret; int header_line_len; SFSnortPacket *p = (SFSnortPacket *)pkt; MimeState *mime_ssn = (MimeState *)ssn; /* get length of header line */ header_line_len = eol - ptr; if (max_header_len) SMTP_GenerateAlert(SMTP_HEADER_NAME_OVERFLOW, "%s: %d chars before colon", SMTP_HEADER_NAME_OVERFLOW_STR, max_header_len); if ((smtp_eval_config->max_header_line_len != 0) && (header_line_len > smtp_eval_config->max_header_line_len)) { if (mime_ssn->data_state != STATE_DATA_UNKNOWN) { SMTP_GenerateAlert(SMTP_DATA_HDR_OVERFLOW, "%s: %d chars", SMTP_DATA_HDR_OVERFLOW_STR, header_line_len); } else { /* assume we guessed wrong and are in the body */ return 1; } } /* XXX Does VRT want data headers normalized? * currently the code does not normalize headers */ if (smtp_normalizing) { ret = SMTP_CopyToAltBuffer(p, ptr, eol - ptr); if (ret == -1) return (-1); } if(smtp_eval_config->log_config.log_email_hdrs) { if(mime_ssn->data_state == STATE_DATA_HEADER) { ret = SMTP_CopyEmailHdrs(ptr, eol - ptr, mime_ssn->log_state); if (ret == 0) mime_ssn->log_flags |= FLAG_EMAIL_HDRS_PRESENT; } } return 0; } /* * Process client packet * * @param packet standard Packet structure * * @return none */ static void SMTP_ProcessClientPacket(SFSnortPacket *p) { const uint8_t *ptr = p->payload; const uint8_t *end = p->payload + p->payload_size; if (smtp_ssn->state == STATE_CONNECT) { smtp_ssn->state = STATE_COMMAND; } while ((ptr != NULL) && (ptr < end)) { switch (smtp_ssn->state) { case STATE_COMMAND: DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "COMMAND STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~\n");); ptr = SMTP_HandleCommand(p, ptr, end); #ifdef DUMP_BUFFER dumpBuffer(STATE_COMMAND_DUMP,p->payload,p->payload_size); #endif break; case STATE_DATA: #ifdef DUMP_BUFFER dumpBuffer(STATE_DATA_DUMP,p->payload,p->payload_size); #endif case STATE_BDATA: DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");); ptr = _dpd.fileAPI->process_mime_data(p, ptr, end, &(smtp_ssn->mime_ssn), 1, true, "SMTP", PP_SMTP); //ptr = SMTP_HandleData(p, ptr, end, &(smtp_ssn->mime_ssn)); #ifdef DUMP_BUFFER dumpBuffer(STATE_BDATA_DUMP,p->payload,p->payload_size); #endif break; case STATE_XEXCH50: #ifdef DUMP_BUFFER dumpBuffer(STATE_XEXCH50_DUMP,p->payload,p->payload_size); #endif if (smtp_normalizing) { SMTP_CopyToAltBuffer(p, ptr, end - ptr); #ifdef DUMP_BUFFER dumpBuffer(STATE_XEXCH50_DUMP,_dpd.altBuffer->data,_dpd.altBuffer->len); #endif } if (is_data_end (p->stream_session)) smtp_ssn->state = STATE_COMMAND; return; case STATE_AUTH: ptr = SMTP_HandleCommand(p, ptr, end); #ifdef DUMP_BUFFER dumpBuffer(STATE_AUTH_DUMP,p->payload,p->payload_size); #endif break; case STATE_UNKNOWN: DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "UNKNOWN STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~\n");); /* If state is unknown try command state to see if we can * regain our bearings */ ptr = SMTP_HandleCommand(p, ptr, end); #ifdef DUMP_BUFFER dumpBuffer(STATE_UNKNOWN_DUMP,p->payload,p->payload_size); #endif break; default: DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Bad SMTP state\n");); return; } } #ifdef DEBUG_MSGS if (smtp_normalizing) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Normalized payload\n%s\n", SMTP_PrintBuffer(p));); } #endif } /* * Process server packet * * @param packet standard Packet structure * * @return None */ static void SMTP_ProcessServerPacket(SFSnortPacket *p, int *next_state) { int resp_found; const uint8_t *ptr; const uint8_t *end; const uint8_t *eolm; const uint8_t *eol; int resp_line_len; #ifdef DEBUG_MSGS const uint8_t *dash; #endif *next_state = 0; ptr = p->payload; end = p->payload + p->payload_size; #ifdef DUMP_BUFFER dumpBuffer(STATE_RESP_DUMP,p->payload,p->payload_size); #endif if (smtp_ssn->state == STATE_TLS_SERVER_PEND) { if (IsTlsServerHello(ptr, end)) { smtp_ssn->state = STATE_TLS_DATA; } else if (!(_dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM) && !_dpd.streamAPI->missed_packets(p->stream_session, SSN_DIR_BOTH)) { /* Check to see if the raw packet is in order */ if(p->flags & FLAG_STREAM_ORDER_OK) { /* revert back to command state - assume server didn't accept STARTTLS */ smtp_ssn->state = STATE_COMMAND; } else return; } } if (smtp_ssn->state == STATE_TLS_DATA) { if(!( _dpd.streamAPI->is_session_decrypted(p->stream_session))) return; else smtp_ssn->state = STATE_COMMAND; } while (ptr < end) { SMTP_GetEOL(ptr, end, &eol, &eolm); resp_line_len = eol - ptr; /* Check for response code */ smtp_current_search = &smtp_resp_search[0]; resp_found = _dpd.searchAPI->search_instance_find (smtp_resp_search_mpse, (const char *)ptr, resp_line_len, 1, SMTP_SearchStrFound); if (resp_found > 0) { switch (smtp_search_info.id) { case RESP_220: /* This is either an initial server response or a STARTTLS response */ if (smtp_ssn->state == STATE_CONNECT) smtp_ssn->state = STATE_COMMAND; break; case RESP_250: case RESP_221: case RESP_334: case RESP_354: break; case RESP_235: // Auth done *next_state = STATE_COMMAND; break; default: if (smtp_ssn->state != STATE_COMMAND) { *next_state = STATE_COMMAND; } break; } #ifdef DEBUG_MSGS dash = ptr + smtp_search_info.index + smtp_search_info.length; /* only add response if not a dash after response code */ if ((dash == eolm) || ((dash < eolm) && (*dash != '-'))) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Server sent %s response\n", smtp_resps[smtp_search_info.id].name);); } #endif } else { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Server response not found - see if it's SSL data\n");); if ((smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL) && (IsSSL(ptr, end - ptr, p->flags))) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Server response is an SSL packet\n");); smtp_ssn->state = STATE_TLS_DATA; /* Ignore data */ if (smtp_eval_config->ignore_tls_data) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Ignoring Server TLS encrypted data\n");); _dpd.SetAltDecode(0); } return; } else if (smtp_ssn->session_flags & SMTP_FLAG_CHECK_SSL) { smtp_ssn->session_flags &= ~SMTP_FLAG_CHECK_SSL; } } if ((smtp_eval_config->max_response_line_len != 0) && (resp_line_len > smtp_eval_config->max_response_line_len)) { SMTP_GenerateAlert(SMTP_RESPONSE_OVERFLOW, "%s: %d chars", SMTP_RESPONSE_OVERFLOW_STR, resp_line_len); } ptr = eol; } return; } /* For Target based * If a protocol for the session is already identified and not one SMTP is * interested in, SMTP should leave it alone and return without processing. * If a protocol for the session is already identified and is one that SMTP is * interested in, decode it. * If the protocol for the session is not already identified and the preprocessor * is configured to detect on one of the packet ports, detect. * Returns 0 if we should not inspect * 1 if we should continue to inspect */ static int SMTP_Inspect(SFSnortPacket *p) { #ifdef TARGET_BASED /* SMTP could be configured to be stateless. If stream isn't configured, assume app id * will never be set and just base inspection on configuration */ if (p->stream_session == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP: Target-based: No stream session.\n");); if ((SMTP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (SMTP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP: Target-based: Configured for this " "traffic, so let's inspect.\n");); return 1; } } else { int16_t app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if (app_id != 0) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP: Target-based: App id: %u.\n", app_id);); if (app_id == smtp_proto_id) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP: Target-based: App id is " "set to \"%s\".\n", SMTP_PROTO_REF_STR);); return 1; } } else { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP: Target-based: Unknown protocol for " "this session. See if we're configured.\n");); if ((SMTP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (SMTP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP: Target-based: SMTP port is configured.");); return 1; } } } DEBUG_WRAP(DebugMessage(DEBUG_SMTP,"SMTP: Target-based: Not inspecting ...\n");); #else /* Make sure it's traffic we're interested in */ if ((SMTP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (SMTP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) return 1; #endif /* TARGET_BASED */ return 0; } /* * Entry point to snort preprocessor for each packet * * @param packet standard Packet structure * * @return none */ void SnortSMTP(SFSnortPacket *p) { int detected = 0; int pkt_dir; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); PROFILE_VARS; #ifdef DUMP_BUFFER dumpBufferInit(); #endif smtp_ssn = (SMTP *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_SMTP); if (smtp_ssn != NULL) smtp_eval_config = (SMTPConfig *)sfPolicyUserDataGet(smtp_ssn->config, smtp_ssn->policy_id); else smtp_eval_config = (SMTPConfig *)sfPolicyUserDataGetCurrent(smtp_config); if (smtp_eval_config == NULL) return; if (smtp_ssn == NULL) { if (!SMTP_Inspect(p)) return; smtp_ssn = SMTP_GetNewSession(p, policy_id); if (smtp_ssn == NULL) return; } pkt_dir = SMTP_Setup(p, smtp_ssn); /* reset normalization stuff */ smtp_normalizing = 0; _dpd.DetectFlag_Disable(SF_FLAG_ALT_DECODE); p->normalized_payload_size = 0; if (pkt_dir == SMTP_PKT_FROM_SERVER) { int next_state = 0; DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP server packet\n");); /* Process as a server packet */ SMTP_ProcessServerPacket(p, &next_state); if (next_state) smtp_ssn->state = next_state; } else { #ifdef DEBUG_MSGS if (pkt_dir == SMTP_PKT_FROM_CLIENT) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP client packet\n");); } else { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP packet NOT from client or server! " "Processing as a client packet\n");); } #endif /* This packet should be a tls client hello */ if (smtp_ssn->state == STATE_TLS_CLIENT_PEND) { if (IsTlsClientHello(p->payload, p->payload + p->payload_size)) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "TLS DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~\n");); smtp_ssn->state = STATE_TLS_SERVER_PEND; if(ssl_cb) { ssl_cb->session_initialize(p, smtp_ssn, SMTP_Set_flow_id); } } else if(p->flags & FLAG_STREAM_ORDER_OK) { /* reset state - server may have rejected STARTTLS command */ smtp_ssn->state = STATE_COMMAND; } } if (smtp_ssn->state == STATE_TLS_DATA) { if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) smtp_ssn->state = STATE_COMMAND; } if ((smtp_ssn->state == STATE_TLS_DATA) || (smtp_ssn->state == STATE_TLS_SERVER_PEND)) { /* if we're ignoring tls data, set a zero length alt buffer */ if (smtp_eval_config->ignore_tls_data) { _dpd.SetAltDecode(0); _dpd.sessionAPI->stop_inspection( p->stream_session, p, SSN_DIR_BOTH, -1, 0 ); return; } } else { if (!_dpd.readyForProcess(p)) { /* Packet will be rebuilt, so wait for it */ DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Client packet will be reassembled\n")); return; } else if (smtp_ssn->reassembling && !(p->flags & FLAG_REBUILT_STREAM)) { /* If this isn't a reassembled packet and didn't get * inserted into reassembly buffer, there could be a * problem. If we miss syn or syn-ack that had window * scaling this packet might not have gotten inserted * into reassembly buffer because it fell outside of * window, because we aren't scaling it */ smtp_ssn->session_flags |= SMTP_FLAG_GOT_NON_REBUILT; smtp_ssn->state = STATE_UNKNOWN; } else if (smtp_ssn->reassembling && (smtp_ssn->session_flags & SMTP_FLAG_GOT_NON_REBUILT)) { /* This is a rebuilt packet. If we got previous packets * that were not rebuilt, state is going to be messed up * so set state to unknown. It's likely this was the * beginning of the conversation so reset state */ DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Got non-rebuilt packets before " "this rebuilt packet\n");); smtp_ssn->state = STATE_UNKNOWN; smtp_ssn->session_flags &= ~SMTP_FLAG_GOT_NON_REBUILT; } #ifdef DEBUG_MSGS /* Interesting to see how often packets are rebuilt */ DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Payload: %s\n%s\n", (p->flags & FLAG_REBUILT_STREAM) ? "reassembled" : "not reassembled", SMTP_PrintBuffer(p));); #endif SMTP_ProcessClientPacket(p); } } PREPROC_PROFILE_START(smtpDetectPerfStats); SMTP_LogFuncs(smtp_eval_config, p, &(smtp_ssn->mime_ssn)); detected = _dpd.detect(p); #ifdef PERF_PROFILING smtpDetectCalled = 1; #endif PREPROC_PROFILE_END(smtpDetectPerfStats); if( &smtp_no_session == smtp_ssn ) { if(smtp_ssn->mime_ssn.decode_state != NULL) { mempool_free(smtp_mime_mempool, smtp_ssn->mime_ssn.decode_bkt); _dpd.snortFree(smtp_ssn->mime_ssn.decode_state, sizeof(Email_DecodeState), PP_SMTP, PP_MEM_CATEGORY_SESSION); smtp_ssn->mime_ssn.decode_state = NULL; } if(smtp_ssn->mime_ssn.log_state != NULL) { mempool_free(smtp_mempool, smtp_ssn->mime_ssn.log_state->log_hdrs_bkt); _dpd.snortFree(smtp_ssn->mime_ssn.log_state, sizeof(MAIL_LogState), PP_SMTP, PP_MEM_CATEGORY_SESSION); smtp_ssn->mime_ssn.log_state = NULL; } if(smtp_ssn->auth_name != NULL) { _dpd.snortFree(smtp_ssn->auth_name, sizeof(*(smtp_ssn->auth_name)), PP_SMTP, PP_MEM_CATEGORY_SESSION); smtp_ssn->auth_name = NULL; } } /* Turn off detection since we've already done it. */ SMTP_DisableDetect(p); if (detected) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP vulnerability detected\n");); } } static void SMTP_DisableDetect(SFSnortPacket *p) { _dpd.disableAllDetect(p); _dpd.enablePreprocessor(p, PP_SDF); } static inline SMTP *SMTP_GetSession(void *data) { if(data) return (SMTP *)_dpd.sessionAPI->get_application_data(data, PP_SMTP); return NULL; } /* Callback to return the MIME attachment filenames accumulated */ int SMTP_GetFilename(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { SMTP *ssn = SMTP_GetSession(data); if(ssn == NULL) return 0; if(ssn->mime_ssn.log_state == NULL) return 0; *buf = ssn->mime_ssn.log_state->file_log.filenames; *len = ssn->mime_ssn.log_state->file_log.file_logged; *type = EVENT_INFO_SMTP_FILENAME; return 1; } /* Callback to return the email addresses accumulated from the MAIL FROM command */ int SMTP_GetMailFrom(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { SMTP *ssn = SMTP_GetSession(data); if(ssn == NULL) return 0; if(ssn->mime_ssn.log_state == NULL) return 0; *buf = ssn->mime_ssn.log_state->senders; *len = ssn->mime_ssn.log_state->snds_logged; *type = EVENT_INFO_SMTP_MAILFROM; return 1; } /* Callback to return the email addresses accumulated from the RCP TO command */ int SMTP_GetRcptTo(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { SMTP *ssn = SMTP_GetSession(data); if(ssn == NULL) return 0; if(ssn->mime_ssn.log_state == NULL) return 0; *buf = ssn->mime_ssn.log_state->recipients; *len = ssn->mime_ssn.log_state->rcpts_logged; *type = EVENT_INFO_SMTP_RCPTTO; return 1; } /* Calback to return the email headers */ int SMTP_GetEmailHdrs(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { SMTP *ssn = SMTP_GetSession(data); if(ssn == NULL) return 0; if(ssn->mime_ssn.log_state == NULL) return 0; *buf = ssn->mime_ssn.log_state->emailHdrs; *len = ssn->mime_ssn.log_state->hdrs_logged; *type = EVENT_INFO_SMTP_EMAIL_HDRS; return 1; } int SMTP_SessionIfExists(void *data) { SMTP *ssn = SMTP_GetSession(data); if(ssn == NULL) return 0; return 1; } static SmtpAPI smtpApiTable = { SMTP_SessionIfExists, SMTP_GetFilename, SMTP_GetMailFrom, SMTP_GetRcptTo, SMTP_GetEmailHdrs }; void SmtpApiInit(SmtpAPI *api) { *api = smtpApiTable; } void SMTP_Set_flow_id( void *app_data, uint32_t fid ) { SMTP *ssn = (SMTP *)app_data; if( ssn ) ssn->flow_id = fid; } snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_normalize.c0000644000175000017500000001243614241076370022417 0ustar apoapo/* * smtp_normalize.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * Author: Andy Mullican * * Description: * * This file handles normalizing SMTP traffic into the alternate buffer. * * Entry point functions: * * SMTP_NeedNormalize() * SMTP_Normalize() * * */ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_smtp.h" #include "smtp_util.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_packet.h" extern SMTP *smtp_ssn; extern char smtp_normalizing; /* * SMTP_NormalizeCmd * * If command doesn't need normalizing it will do nothing, except in * the case where we are already normalizing in which case the line * will get copied to the alt buffer. * If the command needs normalizing the normalized data will be copied * to the alt buffer. If we are not already normalizing, all of the * data up to this point will be copied into the alt buffer first. * * XXX This may copy unwanted data if we are ignoring the data in the * message and there was data that came before the command in the * packet, for example if there are multiple transactions on the * session or if we're normalizing QUIT. * * @param p pointer to packet structure * @param ptr pointer to beginning of command line * @param eolm start of end of line marker * @param eol end of end of line marker * * @return response * @retval 0 function succeded without error * @retval -1 there were errors */ int SMTP_NormalizeCmd(SFSnortPacket *p, const uint8_t *ptr, const uint8_t *eolm, const uint8_t *eol) { const uint8_t *tmp; const uint8_t *cmd_start; const uint8_t *cmd_end; const uint8_t *args_start; const uint8_t *args_end; const uint8_t *space = (uint8_t *)" "; int need_normalize = 0; int ret; tmp = ptr; /* move past initial whitespace */ while ((tmp < eolm) && isspace((int)*tmp)) tmp++; /* we got whitespace before command */ if (tmp > ptr) need_normalize = 1; /* move past the command */ cmd_start = cmd_end = tmp; while ((cmd_end < eolm) && !isspace((int)*cmd_end)) cmd_end++; args_start = cmd_end; while ((args_start < eolm) && isspace((int)*args_start)) args_start++; if (args_start == eolm) { /* nothing but space after command - normalize if we got any * spaces since there is not an argument */ if (args_start > cmd_end) need_normalize = 1; args_end = args_start; } else { /* more than one space between command and argument or * whitespace between command and argument is not a regular space character */ if ((args_start > (cmd_end + 1)) || (*cmd_end != ' ')) need_normalize = 1; /* see if there is any dangling space at end of argument */ args_end = eolm; while (isspace((int)*(args_end - 1))) args_end--; if (args_end != eolm) need_normalize = 1; } if (need_normalize) { DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Command needs normalizing\n");); /* if we're not yet normalizing copy everything in the payload up to this * line into the alt buffer */ if (!smtp_normalizing) { ret = SMTP_CopyToAltBuffer(p, p->payload, ptr - p->payload); if (ret == -1) return -1; } /* copy the command into the alt buffer */ ret = SMTP_CopyToAltBuffer(p, cmd_start, cmd_end - cmd_start); if (ret == -1) return -1; /* if we actually have an argument, copy it into the alt buffer */ if (args_start != args_end) { /* copy a 'pure' space */ ret = SMTP_CopyToAltBuffer(p, space, 1); if (ret == -1) return -1; ret = SMTP_CopyToAltBuffer(p, args_start, args_end - args_start); if (ret == -1) return -1; } /* copy the end of line marker into the alt buffer */ ret = SMTP_CopyToAltBuffer(p, eolm, eol - eolm); if (ret == -1) return -1; } else if (smtp_normalizing) { /* if we're already normalizing and didn't need to normalize this line, just * copy it into the alt buffer */ ret = SMTP_CopyToAltBuffer(p, ptr, eol - ptr); if (ret == -1) return -1; } return 0; } snort-2.9.20/src/dynamic-preprocessors/smtp/Makefile.in0000644000175000017500000006374114242725547021272 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@smtp_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@smtp_buffer_dump.h subdir = src/dynamic-preprocessors/smtp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_smtp_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_smtp_preproc_la_SOURCES_DIST = smtp_config.c smtp_config.h \ smtp_log.c smtp_log.h smtp_normalize.c smtp_normalize.h \ smtp_util.c smtp_util.h smtp_xlink2state.c smtp_xlink2state.h \ smtp_paf.c smtp_paf.h snort_smtp.c snort_smtp.h spp_smtp.c \ spp_smtp.h smtp_buffer_dump.c smtp_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = smtp_buffer_dump.lo am_libsf_smtp_preproc_la_OBJECTS = smtp_config.lo smtp_log.lo \ smtp_normalize.lo smtp_util.lo smtp_xlink2state.lo smtp_paf.lo \ snort_smtp.lo spp_smtp.lo $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_smtp_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo mempool.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_sdlist.lo sf_base64decode.lo \ @SO_WITH_STATIC_LIB_FALSE@ util_unfold.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_email_attach_decode.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo ssl.lo ssl_ha.lo \ @SO_WITH_STATIC_LIB_FALSE@ ssl_config.lo ssl_inspect.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfparser.lo libsf_smtp_preproc_la_OBJECTS = $(am_libsf_smtp_preproc_la_OBJECTS) \ $(nodist_libsf_smtp_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_smtp_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_smtp_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_smtp_preproc_la_SOURCES) \ $(nodist_libsf_smtp_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_smtp_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_smtp_preproc.la libsf_smtp_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_smtp_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_smtp_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/mempool.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_sdlist.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_base64decode.c \ @SO_WITH_STATIC_LIB_FALSE@../include/util_unfold.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_email_attach_decode.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_ha.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_config.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_inspect.c \ @SO_WITH_STATIC_LIB_FALSE@../libs/sfparser.c libsf_smtp_preproc_la_SOURCES = smtp_config.c smtp_config.h smtp_log.c \ smtp_log.h smtp_normalize.c smtp_normalize.h smtp_util.c \ smtp_util.h smtp_xlink2state.c smtp_xlink2state.h smtp_paf.c \ smtp_paf.h snort_smtp.c snort_smtp.h spp_smtp.c spp_smtp.h \ $(am__append_1) EXTRA_DIST = \ sf_smtp.vcxproj \ sf_smtp.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/smtp/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/smtp/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_smtp_preproc.la: $(libsf_smtp_preproc_la_OBJECTS) $(libsf_smtp_preproc_la_DEPENDENCIES) $(EXTRA_libsf_smtp_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_smtp_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_smtp_preproc_la_OBJECTS) $(libsf_smtp_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c mempool.lo: ../include/mempool.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mempool.lo `test -f '../include/mempool.c' || echo '$(srcdir)/'`../include/mempool.c sf_sdlist.lo: ../include/sf_sdlist.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_sdlist.lo `test -f '../include/sf_sdlist.c' || echo '$(srcdir)/'`../include/sf_sdlist.c sf_base64decode.lo: ../include/sf_base64decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_base64decode.lo `test -f '../include/sf_base64decode.c' || echo '$(srcdir)/'`../include/sf_base64decode.c util_unfold.lo: ../include/util_unfold.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o util_unfold.lo `test -f '../include/util_unfold.c' || echo '$(srcdir)/'`../include/util_unfold.c sf_email_attach_decode.lo: ../include/sf_email_attach_decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_email_attach_decode.lo `test -f '../include/sf_email_attach_decode.c' || echo '$(srcdir)/'`../include/sf_email_attach_decode.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c ssl.lo: ../ssl_common/ssl.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl.lo `test -f '../ssl_common/ssl.c' || echo '$(srcdir)/'`../ssl_common/ssl.c ssl_ha.lo: ../ssl_common/ssl_ha.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_ha.lo `test -f '../ssl_common/ssl_ha.c' || echo '$(srcdir)/'`../ssl_common/ssl_ha.c ssl_config.lo: ../ssl_common/ssl_config.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_config.lo `test -f '../ssl_common/ssl_config.c' || echo '$(srcdir)/'`../ssl_common/ssl_config.c ssl_inspect.lo: ../ssl_common/ssl_inspect.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_inspect.lo `test -f '../ssl_common/ssl_inspect.c' || echo '$(srcdir)/'`../ssl_common/ssl_inspect.c sfparser.lo: ../libs/sfparser.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfparser.lo `test -f '../libs/sfparser.c' || echo '$(srcdir)/'`../libs/sfparser.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/smtp/sf_smtp.vcxproj0000444000175000017500000004541414230012554022270 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {1B63B2E7-AF61-4248-83C5-1B1F0D237D2D} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Debug\ .\Debug\ true true .\Release\ .\Release\ false false .\Release\ .\Release\ MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_smtp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_smtp.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_smtp.bsc true true true Console .\Debug\sf_smtp.dll .\Debug\sf_smtp.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_smtp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_smtp.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_smtp.bsc true true true Console .\Debug\sf_smtp.dll .\Debug\sf_smtp.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_smtp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_smtp.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_smtp.bsc true true Console .\Release\sf_smtp.dll .\Release\sf_smtp.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_smtp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_smtp.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_smtp.bsc true true Console .\Release\sf_smtp.dll .\Release\sf_smtp.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) {3b06c0ca-3cc0-4514-9737-69a5d41e610d} false {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/smtp/sf_smtp.dsp0000444000175000017500000001636714230012554021370 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_smtp" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_smtp - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_smtp.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_smtp.mak" CFG="sf_smtp - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_smtp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_smtp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_smtp - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "SF_SNORT_PREPROC_DLL" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 pcre.lib ws2_32.lib ../libs/Release/sfdynamic_preproc_libs.lib /nologo /dll /machine:I386 /libpath:"../../../src/win32/WIN32-Libraries" !ELSEIF "$(CFG)" == "sf_smtp - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "SF_SNORT_PREPROC_DLL" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 pcre.lib ws2_32.lib ../libs/Debug/sfdynamic_preproc_libs.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"../../../src/win32/WIN32-Libraries" !ENDIF # Begin Target # Name "sf_smtp - Win32 Release" # Name "sf_smtp - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\mempool.c # End Source File # Begin Source File SOURCE=..\include\sf_base64decode.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_email_attach_decode.c # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=..\libs\sfparser.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_ha.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_inspect.c # End Source File # Begin Source File SOURCE=.\smtp_config.c # End Source File # Begin Source File SOURCE=.\smtp_log.c # End Source File # Begin Source File SOURCE=.\smtp_normalize.c # End Source File # Begin Source File SOURCE=.\smtp_util.c # End Source File # Begin Source File SOURCE=.\smtp_paf.c # End Source File # Begin Source File SOURCE=.\smtp_xlink2state.c # End Source File # Begin Source File SOURCE=.\snort_smtp.c # End Source File # Begin Source File SOURCE=.\spp_smtp.c # End Source File # Begin Source File SOURCE=..\include\util_unfold.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\ssl_common\ssl.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_include.h # End Source File # Begin Source File SOURCE=..\include\mempool.h # End Source File # Begin Source File SOURCE=..\include\sf_base64decode.h # End Source File # Begin Source File SOURCE=..\include\sf_email_attach_decode.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.h # End Source File # Begin Source File SOURCE=.\smtp_config.h # End Source File # Begin Source File SOURCE=.\smtp_log.h # End Source File # Begin Source File SOURCE=.\smtp_normalize.h # End Source File # Begin Source File SOURCE=.\smtp_util.h # End Source File # Begin Source File SOURCE=.\smtp_paf.h # End Source File # Begin Source File SOURCE=.\smtp_xlink2state.h # End Source File # Begin Source File SOURCE=.\snort_smtp.h # End Source File # Begin Source File SOURCE=.\spp_smtp.h # End Source File # Begin Source File SOURCE=..\include\util_unfold.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_log.c0000644000175000017500000000723114241076364021200 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * smtp_log.c * * Author: Andy Mullican * * Description: * * This file handles SMTP alerts. * * Entry point functions: * * SMTP_GenerateAlert() * * **************************************************************************/ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "smtp_config.h" #include "smtp_log.h" #include "snort_smtp.h" #include "sf_dynamic_preprocessor.h" extern SMTPConfig *smtp_eval_config; extern SMTP *smtp_ssn; char smtp_event[SMTP_EVENT_MAX][EVENT_STR_LEN]; void SMTP_GenerateAlert(int event, char *format, ...) { va_list ap; /* Only log a specific alert once per session */ if (smtp_ssn->alert_mask & (1 << event)) { #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Already alerted on: %s - " "ignoring event.\n", smtp_event[event]);); #endif return; } /* set bit for this alert so we don't alert on again * in this session */ smtp_ssn->alert_mask |= (1 << event); if (smtp_eval_config->no_alerts) { #ifdef DEBUG_MSGS va_start(ap, format); smtp_event[event][0] = '\0'; vsnprintf(&smtp_event[event][0], EVENT_STR_LEN - 1, format, ap); smtp_event[event][EVENT_STR_LEN - 1] = '\0'; DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "Ignoring alert: %s\n", smtp_event[event]);); va_end(ap); #endif return; } va_start(ap, format); smtp_event[event][0] = '\0'; vsnprintf(&smtp_event[event][0], EVENT_STR_LEN - 1, format, ap); smtp_event[event][EVENT_STR_LEN - 1] = '\0'; _dpd.alertAdd(GENERATOR_SMTP, event, 1, 0, 3, &smtp_event[event][0], 0); DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP Alert generated: %s\n", smtp_event[event]);); va_end(ap); } void SMTP_DecodeAlert(void *ds) { Email_DecodeState *decode_state = (Email_DecodeState *)ds; switch( decode_state->decode_type ) { case DECODE_B64: if (smtp_eval_config->decode_conf.b64_depth > -1) SMTP_GenerateAlert(SMTP_B64_DECODING_FAILED, "%s", SMTP_B64_DECODING_FAILED_STR); break; case DECODE_QP: if (smtp_eval_config->decode_conf.qp_depth > -1) SMTP_GenerateAlert(SMTP_QP_DECODING_FAILED, "%s", SMTP_QP_DECODING_FAILED_STR); break; case DECODE_UU: if (smtp_eval_config->decode_conf.uu_depth > -1) SMTP_GenerateAlert(SMTP_UU_DECODING_FAILED, "%s", SMTP_UU_DECODING_FAILED_STR); break; default: break; } } snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_buffer_dump.c0000644000175000017500000000425114241076350022707 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file smtp_buffer_dump.c ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during SMTP inspection. */ #include "smtp_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_SMTP_BUFFER_DUMP] = {{"STATE_COMMAND_DUMP", "", 0}, {"STATE_DATA_DUMP", "", 0}, {"STATE_BDATA_DUMP", "", 0}, {"STATE_XEXCH50_DUMP", "", 0}, {"STATE_AUTH_DUMP", "", 0}, {"STATE_UNKNOWN_DUMP", "", 0}, {"STATE_RESP_DUMP", "", 0}}; void dumpBuffer(SMTP_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *)content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_SMTP_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getSMTPBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/smtp/Makefile.am0000444000175000017500000000243414230012554021227 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_smtp_preproc.la libsf_smtp_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_smtp_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_smtp_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/mempool.c \ ../include/sf_sdlist.c \ ../include/sf_base64decode.c \ ../include/util_unfold.c \ ../include/sf_email_attach_decode.c \ ../include/sfPolicyUserData.c \ ../ssl_common/ssl.c \ ../ssl_common/ssl_ha.c \ ../ssl_common/ssl_config.c \ ../ssl_common/ssl_inspect.c \ ../libs/sfparser.c endif libsf_smtp_preproc_la_SOURCES = \ smtp_config.c \ smtp_config.h \ smtp_log.c \ smtp_log.h \ smtp_normalize.c \ smtp_normalize.h \ smtp_util.c \ smtp_util.h \ smtp_xlink2state.c \ smtp_xlink2state.h \ smtp_paf.c \ smtp_paf.h \ snort_smtp.c \ snort_smtp.h \ spp_smtp.c \ spp_smtp.h if BUILD_BUFFER_DUMP libsf_smtp_preproc_la_SOURCES += \ smtp_buffer_dump.c \ smtp_buffer_dump.h endif EXTRA_DIST = \ sf_smtp.vcxproj \ sf_smtp.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/smtp/spp_smtp.c0000644000175000017500000010021714241076407021215 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * spp_smtp.c * * Author: Andy Mullican * * Description: * * This file initializes SMTP as a Snort preprocessor. * * This file registers the SMTP initialization function, * adds the SMTP function into the preprocessor list. * * In general, this file is a wrapper to SMTP functionality, * by interfacing with the Snort preprocessor functions. The rest * of SMTP should be separate from the preprocessor hooks. * **************************************************************************/ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_smtp.h" #include "sf_preproc_info.h" #include "snort_smtp.h" #include "smtp_util.h" #include "smtp_config.h" #include "smtp_log.h" #include "smtp_paf.h" #include "preprocids.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "snort_debug.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "smtp_api.h" #include "profiler.h" #ifdef PERF_PROFILING PreprocStats smtpPerfStats; PreprocStats smtpDetectPerfStats; int smtpDetectCalled = 0; #endif #include "sf_types.h" #include "mempool.h" #include "snort_bounds.h" #include "file_api.h" #ifdef REG_TEST #include "reg_test.h" #endif #ifdef DUMP_BUFFER #include "smtp_buffer_dump.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 9; const char *PREPROC_NAME = "SF_SMTP"; const char *PROTOCOL_NAME = "SMTP"; #define SetupSMTP DYNAMIC_PREPROC_SETUP MemPool *smtp_mime_mempool = NULL; SMTP_Stats smtp_stats; MemPool *smtp_mempool = NULL; tSfPolicyUserContextId smtp_config = NULL; SMTPConfig *smtp_eval_config = NULL; extern SMTP smtp_no_session; extern int16_t smtp_proto_id; static void SMTPInit(struct _SnortConfig *, char *); static void SMTPDetect(void *, void *context); static void SMTPCleanExitFunction(int, void *); static void SMTPResetFunction(int, void *); static void SMTPResetStatsFunction(int, void *); static void enablePortStreamServices(struct _SnortConfig *, SMTPConfig *, tSfPolicyId); static void SMTP_RegXtraDataFuncs(SMTPConfig *config); static void SMTP_PrintStats(int); static void DisplaySMTPStats (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static int SMTPCheckConfig(struct _SnortConfig *); #ifdef SNORT_RELOAD static int SMTPMempoolFreeUsedBucket(MemPool *memory_pool); static unsigned SMTPReloadMimeMempoolAdjust(unsigned smtpMaxWork); static unsigned SMTPReloadLogMempoolAdjust(unsigned smtpMaxWork); static bool SMTPMimeReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData); static bool SMTPLogReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData); static void SMTPReload(struct _SnortConfig *, char *, void **); static int SMTPReloadVerify(struct _SnortConfig *, void *); static void * SMTPReloadSwap(struct _SnortConfig *, void *); static void SMTPReloadSwapFree(void *); #endif void SmtpApiInit(SmtpAPI *api); /* * Function: SetupSMTP() * * Purpose: Registers the preprocessor keyword and initialization * function into the preprocessor list. This is the function that * gets called from InitPreprocessors() in plugbase.c. * * Arguments: None. * * Returns: void function * */ void SetupSMTP(void) { /* link the preprocessor keyword to the init function in the preproc list */ #ifndef SNORT_RELOAD _dpd.registerPreproc("smtp", SMTPInit); #else _dpd.registerPreproc("smtp", SMTPInit, SMTPReload, SMTPReloadVerify, SMTPReloadSwap, SMTPReloadSwapFree); #endif } #ifdef REG_TEST static inline void PrintSMTPSize(void) { _dpd.logMsg("\nSMTP Session Size: %lu\n", (long unsigned int)sizeof(SMTP)); } #endif /* * Function: SMTPInit(char *) * * Purpose: Calls the argument parsing function, performs final setup on data * structs, links the preproc function into the function list. * * Arguments: args => ptr to argument string * * Returns: void function * */ static void SMTPInit(struct _SnortConfig *sc, char *args) { SMTPToken *tmp; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); SMTPConfig * pPolicyConfig = NULL; #ifdef REG_TEST PrintSMTPSize(); #endif _dpd.registerMemoryStatsFunc(PP_SMTP, SMTP_Print_Mem_Stats); if (smtp_config == NULL) { //create a context smtp_config = sfPolicyConfigCreate(); if (smtp_config == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create SMTP " "configuration.\n"); } /* Initialize the searches not dependent on configuration. * headers, reponsed, data, mime boundary regular expression */ SMTP_SearchInit(); /* zero out static SMTP global used for stateless SMTP or if there * is no session pointer */ memset(&smtp_no_session, 0, sizeof(SMTP)); /* Put the preprocessor function into the function list */ /* _dpd.addPreproc(SMTPDetect, PRIORITY_APPLICATION, PP_SMTP, PROTO_BIT__TCP);*/ _dpd.addPreprocExit(SMTPCleanExitFunction, NULL, PRIORITY_LAST, PP_SMTP); _dpd.addPreprocReset(SMTPResetFunction, NULL, PRIORITY_LAST, PP_SMTP); _dpd.registerPreprocStats(SMTP_PROTO_REF_STR, SMTP_PrintStats); _dpd.addPreprocResetStats(SMTPResetStatsFunction, NULL, PRIORITY_LAST, PP_SMTP); _dpd.addPreprocConfCheck(sc, SMTPCheckConfig); _dpd.controlSocketRegisterHandler(CS_TYPE_SMTP_STATS, NULL, NULL, &DisplaySMTPStats); #ifdef TARGET_BASED smtp_proto_id = _dpd.findProtocolReference(SMTP_PROTO_REF_STR); if (smtp_proto_id == SFTARGET_UNKNOWN_PROTOCOL) smtp_proto_id = _dpd.addProtocolReference(SMTP_PROTO_REF_STR); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_SMTP, smtp_proto_id ); DEBUG_WRAP(DebugMessage(DEBUG_SMTP,"SMTP: Target-based: Proto id for %s: %u.\n", SMTP_PROTO_REF_STR, smtp_proto_id);); #endif #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("smtp", (void*)&smtpPerfStats, 0, _dpd.totalPerfStats, NULL); #endif } sfPolicyUserPolicySet (smtp_config, policy_id); pPolicyConfig = (SMTPConfig *)sfPolicyUserDataGetCurrent(smtp_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("Can only configure SMTP preprocessor once.\n"); } pPolicyConfig = (SMTPConfig *)_dpd.snortAlloc(1, sizeof(SMTPConfig), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create SMTP " "configuration.\n"); } sfPolicyUserDataSetCurrent(smtp_config, pPolicyConfig); SMTP_RegXtraDataFuncs(pPolicyConfig); SMTP_InitCmds(pPolicyConfig); SMTP_ParseArgs(pPolicyConfig, args); SMTP_CheckConfig(pPolicyConfig, smtp_config); SMTP_PrintConfig(pPolicyConfig); if(pPolicyConfig->disabled) return; _dpd.addPreproc(sc, SMTPDetect, PRIORITY_APPLICATION, PP_SMTP, PROTO_BIT__TCP); if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("Streaming & reassembly must be enabled " "for SMTP preprocessor\n"); } /* Command search - do this here because it's based on configuration */ pPolicyConfig->cmd_search_mpse = _dpd.searchAPI->search_instance_new(); if (pPolicyConfig->cmd_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate SMTP " "command search.\n"); } for (tmp = pPolicyConfig->cmds; tmp->name != NULL; tmp++) { pPolicyConfig->cmd_search[tmp->search_id].name = tmp->name; pPolicyConfig->cmd_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(pPolicyConfig->cmd_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(pPolicyConfig->cmd_search_mpse); enablePortStreamServices(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getSMTPBuffers, SMTP_BUFFER_DUMP_FUNC); #endif SmtpApiInit(_dpd.smtpApi); } /* * Function: SMTPDetect(void *, void *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ static void SMTPDetect(void *pkt, void *context) { SFSnortPacket *p = (SFSnortPacket *)pkt; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); PROFILE_VARS; // preconditions - what we registered for assert(IsTCP(p) && p->payload && p->payload_size); PREPROC_PROFILE_START(smtpPerfStats); DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP Start (((((((((((((((((((((((((((((((((((((((\n");); sfPolicyUserPolicySet (smtp_config, policy_id); SnortSMTP(p); DEBUG_WRAP(DebugMessage(DEBUG_SMTP, "SMTP End )))))))))))))))))))))))))))))))))))))))))\n\n");); PREPROC_PROFILE_END(smtpPerfStats); #ifdef PERF_PROFILING if (PROFILING_PREPROCS && smtpDetectCalled) { smtpPerfStats.ticks -= smtpDetectPerfStats.ticks; /* And Reset ticks to 0 */ smtpDetectPerfStats.ticks = 0; smtpDetectCalled = 0; } #endif } /* * Function: SMTPCleanExitFunction(int, void *) * * Purpose: This function gets called when Snort is exiting, if there's * any cleanup that needs to be performed (e.g. closing files) * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * function when it was registered, may be * needed to properly exit * * Returns: void function */ static void SMTPCleanExitFunction(int signal, void *data) { SMTP_Free(); if (mempool_destroy(smtp_mime_mempool) == 0) { free(smtp_mime_mempool); smtp_mime_mempool = NULL; } if (mempool_destroy(smtp_mempool) == 0) { free(smtp_mempool); smtp_mempool = NULL; } } static void SMTPResetFunction(int signal, void *data) { return; } static void SMTPResetStatsFunction(int signal, void *data) { return; } static void enablePortStreamServices(struct _SnortConfig *sc, SMTPConfig *config, tSfPolicyId policy_id) { uint32_t portNum; if (config == NULL) return; for (portNum = 0; portNum < MAXPORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); register_smtp_paf_port(sc, portNum, policy_id); _dpd.streamAPI->register_reassembly_port( NULL, (uint16_t) portNum, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _dpd.sessionAPI->enable_preproc_for_port( sc, PP_SMTP, PROTO_BIT__TCP, (uint16_t) portNum ); } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, smtp_proto_id, PORT_MONITOR_SESSION, policy_id, 1); register_smtp_paf_service(sc, smtp_proto_id, policy_id); } #endif static int SMTPEnableDecoding(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void *pData) { SMTPConfig *context = (SMTPConfig *)pData; if (pData == NULL) return 0; if(context->disabled) return 0; if(_dpd.fileAPI->is_decoding_enabled(&(context->decode_conf))) return 1; return 0; } static int SMTPLogExtraData(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void *pData) { SMTPConfig *context = (SMTPConfig *)pData; if (pData == NULL) return 0; if(context->disabled) return 0; if(context->log_config.log_email_hdrs || context->log_config.log_filename || context->log_config.log_mailfrom || context->log_config.log_rcptto) return 1; return 0; } static int CheckFilePolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SMTPConfig *context = (SMTPConfig *)pData; /* Use new Snort config to get the max file depth */ context->decode_conf.file_depth = _dpd.fileAPI->get_max_file_depth(sc, true); if (context->decode_conf.file_depth > -1) context->log_config.log_filename = 1; updateMaxDepth(context->decode_conf.file_depth, &context->decode_conf.max_depth); return 0; } static int SMTPCheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SMTPConfig *context = (SMTPConfig *)pData; _dpd.setParserPolicy(sc, policyId); /* In a multiple-policy setting, the SMTP preproc can be turned on in a "disabled" state. In this case, we don't require Stream5. */ if (context->disabled) return 0; if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("Streaming & reassembly must be enabled " "for SMTP preprocessor\n"); } return 0; } static void SMTP_RegXtraDataFuncs(SMTPConfig *config) { if ((_dpd.streamAPI == NULL) || !config) return; config->xtra_filename_id = _dpd.streamAPI->reg_xtra_data_cb(SMTP_GetFilename); config->xtra_mfrom_id = _dpd.streamAPI->reg_xtra_data_cb(SMTP_GetMailFrom); config->xtra_rcptto_id = _dpd.streamAPI->reg_xtra_data_cb(SMTP_GetRcptTo); config->xtra_ehdrs_id = _dpd.streamAPI->reg_xtra_data_cb(SMTP_GetEmailHdrs); } static int SMTPCheckConfig(struct _SnortConfig *sc) { sfPolicyUserDataIterate (sc, smtp_config, SMTPCheckPolicyConfig); sfPolicyUserDataIterate (sc, smtp_config, CheckFilePolicyConfig); { SMTPConfig *defaultConfig = (SMTPConfig *)sfPolicyUserDataGetDefault(smtp_config); if (defaultConfig == NULL) { _dpd.errMsg( "SMTP: Must configure a default configuration if you " "want to enable smtp decoding.\n"); return -1; } if (sfPolicyUserDataIterate(sc, smtp_config, SMTPEnableDecoding) != 0) { smtp_mime_mempool = (MemPool *) _dpd.fileAPI->init_mime_mempool(defaultConfig->decode_conf.max_mime_mem, defaultConfig->decode_conf.max_depth, smtp_mime_mempool, PROTOCOL_NAME); } if (sfPolicyUserDataIterate(sc, smtp_config, SMTPLogExtraData) != 0) { smtp_mempool = (MemPool *)_dpd.fileAPI->init_log_mempool(defaultConfig->log_config.email_hdrs_log_depth, defaultConfig->memcap, smtp_mempool, PROTOCOL_NAME); } } return 0; } static void DisplaySMTPStats (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f) { char buffer[CS_STATS_BUF_SIZE + 1]; int len = 0; if (smtp_stats.sessions) { len += snprintf(buffer, CS_STATS_BUF_SIZE, "SMTP Preprocessor Statistics\n" " Total sessions : " STDu64 "\n" " Max concurrent sessions : " STDu64 "\n" " Base64 attachments decoded : " STDu64 "\n" " Total Base64 decoded bytes : " STDu64 "\n" " Quoted-Printable attachments decoded : " STDu64 "\n" " Total Quoted decoded bytes : " STDu64 "\n" " UU attachments decoded : " STDu64 "\n" " Total UU decoded bytes : " STDu64 "\n" " Non-Encoded MIME attachments extracted : " STDu64 "\n" " Total Non-Encoded MIME bytes extracted : " STDu64 "\n" , smtp_stats.sessions , smtp_stats.max_conc_sessions , smtp_stats.mime_stats.attachments[DECODE_B64] , smtp_stats.mime_stats.decoded_bytes[DECODE_B64] , smtp_stats.mime_stats.attachments[DECODE_QP] , smtp_stats.mime_stats.decoded_bytes[DECODE_QP] , smtp_stats.mime_stats.attachments[DECODE_UU] , smtp_stats.mime_stats.decoded_bytes[DECODE_UU] , smtp_stats.mime_stats.attachments[DECODE_BITENC] , smtp_stats.mime_stats.decoded_bytes[DECODE_BITENC]); if ( smtp_stats.mime_stats.memcap_exceeded ) len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, " Sessions not decoded due to memory unavailability : " STDu64 "\n", smtp_stats.mime_stats.memcap_exceeded); if ( smtp_stats.log_memcap_exceeded ) len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, " SMTP Sessions fastpathed due to memcap exceeded: " STDu64 "\n", smtp_stats.log_memcap_exceeded); } else { len = snprintf(buffer, CS_STATS_BUF_SIZE, "No available SMTP Sessions\n Total sessions : " STDu64 "\n", smtp_stats.sessions); } if (-1 == f(te, (const uint8_t *)buffer, len)) { _dpd.logMsg("Unable to send data to the frontend\n"); } } static void SMTP_PrintStats(int exiting) { _dpd.logMsg("SMTP Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions : " STDu64 "\n", smtp_stats.sessions); _dpd.logMsg(" Max concurrent sessions : " STDu64 "\n", smtp_stats.max_conc_sessions); if (smtp_stats.sessions > 0) { _dpd.logMsg(" Base64 attachments decoded : " STDu64 "\n", smtp_stats.mime_stats.attachments[DECODE_B64]); _dpd.logMsg(" Total Base64 decoded bytes : " STDu64 "\n", smtp_stats.mime_stats.decoded_bytes[DECODE_B64]); _dpd.logMsg(" Quoted-Printable attachments decoded : " STDu64 "\n", smtp_stats.mime_stats.attachments[DECODE_QP]); _dpd.logMsg(" Total Quoted decoded bytes : " STDu64 "\n", smtp_stats.mime_stats.decoded_bytes[DECODE_QP]); _dpd.logMsg(" UU attachments decoded : " STDu64 "\n", smtp_stats.mime_stats.attachments[DECODE_UU]); _dpd.logMsg(" Total UU decoded bytes : " STDu64 "\n", smtp_stats.mime_stats.decoded_bytes[DECODE_UU]); _dpd.logMsg(" Non-Encoded MIME attachments extracted : " STDu64 "\n", smtp_stats.mime_stats.attachments[DECODE_BITENC]); _dpd.logMsg(" Total Non-Encoded MIME bytes extracted : " STDu64 "\n", smtp_stats.mime_stats.decoded_bytes[DECODE_BITENC]); if ( smtp_stats.mime_stats.memcap_exceeded ) _dpd.logMsg(" Sessions not decoded due to memory unavailability : " STDu64 "\n", smtp_stats.mime_stats.memcap_exceeded); if ( smtp_stats.log_memcap_exceeded ) _dpd.logMsg(" SMTP Sessions fastpathed due to memcap exceeded: " STDu64 "\n", smtp_stats.log_memcap_exceeded); } } #ifdef SNORT_RELOAD static void SMTPReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId smtp_swap_config = (tSfPolicyUserContextId)*new_config; SMTPToken *tmp; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); SMTPConfig *pPolicyConfig = NULL; if (smtp_swap_config == NULL) { //create a context smtp_swap_config = sfPolicyConfigCreate(); if (smtp_swap_config == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create SMTP " "configuration.\n"); } *new_config = (void *)smtp_swap_config; } sfPolicyUserPolicySet (smtp_swap_config, policy_id); pPolicyConfig = (SMTPConfig *)sfPolicyUserDataGetCurrent(smtp_swap_config); if (pPolicyConfig != NULL) DynamicPreprocessorFatalMessage("Can only configure SMTP preprocessor once.\n"); pPolicyConfig = (SMTPConfig *)_dpd.snortAlloc(1, sizeof(SMTPConfig), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create SMTP " "configuration.\n"); } sfPolicyUserDataSetCurrent(smtp_swap_config, pPolicyConfig); SMTP_RegXtraDataFuncs(pPolicyConfig); SMTP_InitCmds(pPolicyConfig); SMTP_ParseArgs(pPolicyConfig, args); SMTP_CheckConfig(pPolicyConfig, smtp_swap_config); SMTP_PrintConfig(pPolicyConfig); if( pPolicyConfig->disabled ) return; if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("Streaming & reassembly must be enabled " "for SMTP preprocessor\n"); } /* Command search - do this here because it's based on configuration */ pPolicyConfig->cmd_search_mpse = _dpd.searchAPI->search_instance_new(); if (pPolicyConfig->cmd_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate SMTP " "command search.\n"); } for (tmp = pPolicyConfig->cmds; tmp->name != NULL; tmp++) { pPolicyConfig->cmd_search[tmp->search_id].name = tmp->name; pPolicyConfig->cmd_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(pPolicyConfig->cmd_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(pPolicyConfig->cmd_search_mpse); _dpd.addPreproc(sc, SMTPDetect, PRIORITY_APPLICATION, PP_SMTP, PROTO_BIT__TCP); enablePortStreamServices(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } static int SMTPMempoolFreeUsedBucket(MemPool *memory_pool) { MemBucket *lru_bucket = NULL; lru_bucket = mempool_get_lru_bucket(memory_pool); if(lru_bucket) { /* Deleting least recently used SMTP session data here to adjust to new max_memory */ _dpd.sessionAPI->set_application_data(lru_bucket->scbPtr, PP_SMTP, NULL, NULL); return 1; } return 0; } static unsigned SMTPReloadMimeMempoolAdjust(unsigned smtpMaxWork) { int retVal; /* deleting MemBucket from free list in SMTP Mime Mempool */ smtpMaxWork = mempool_prune_freelist(smtp_mime_mempool, smtp_mime_mempool->max_memory, smtpMaxWork); if(!smtpMaxWork) return 0; for( ; smtpMaxWork && ((smtp_mime_mempool->used_memory + smtp_mime_mempool->free_memory) > smtp_mime_mempool->max_memory); smtpMaxWork--) { /* deleting least recently used MemBucket from Used list in SMTP Mime Mempool */ retVal = SMTPMempoolFreeUsedBucket(smtp_mime_mempool); if(!retVal) break; } return smtpMaxWork; } static unsigned SMTPReloadLogMempoolAdjust(unsigned smtpMaxWork) { int retVal; /* deleting MemBucket from free list in SMTP Log Mempool */ smtpMaxWork = mempool_prune_freelist(smtp_mempool, smtp_mempool->max_memory, smtpMaxWork); if(!smtpMaxWork) return 0; for( ; smtpMaxWork && ((smtp_mempool->used_memory + smtp_mempool->free_memory) > smtp_mempool->max_memory); smtpMaxWork--) { /* deleting least recently used MemBucket from Used list in SMTP Log Mempool */ retVal = SMTPMempoolFreeUsedBucket(smtp_mempool); if(!retVal) break; } return smtpMaxWork; } static bool SMTPMimeReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned initialMaxWork = idle ? 512 : 5; unsigned maxWork; /* If new max_mime_mem is less than old configured max_mime_mem, need to adjust SMTP Mime Mempool. * In order to adjust to new max_memory of mime mempool, delete buckets from free list. * After deleting buckets from free list, still new max_memory is less than old value , delete buckets * (least recently used i.e head node of used list )from used list till total memory reaches to new max_memory. */ maxWork = SMTPReloadMimeMempoolAdjust(initialMaxWork); if (maxWork == initialMaxWork) { smtp_stats.max_conc_sessions = smtp_stats.conc_sessions; smtp_stats.mime_stats.memcap_exceeded = 0; return true; } else return false; } static bool SMTPLogReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned initialMaxWork = idle ? 512 : 5; unsigned maxWork; /* If new memcap is less than old configured memcap, need to adjust SMTP Log Mempool. * In order to adjust to new max_memory of log mempool, delete buckets from free list. * After deleting buckets from free list, still new max_memory is less than old value , delete buckets * (least recently used i.e head node of used list )from used list till total memory reaches to new max_memory. */ maxWork = SMTPReloadLogMempoolAdjust(initialMaxWork); if (maxWork == initialMaxWork) { smtp_stats.max_conc_sessions = smtp_stats.conc_sessions; smtp_stats.mime_stats.memcap_exceeded = 0; return true; } else return false; } static int SMTPReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId smtp_swap_config = (tSfPolicyUserContextId)swap_config; SMTPConfig *config = NULL; SMTPConfig *configNext = NULL; tSfPolicyId policy_id = 0; if (smtp_swap_config == NULL) return 0; if (smtp_config != NULL) { config = (SMTPConfig *)sfPolicyUserDataGet(smtp_config, _dpd.getDefaultPolicy()); } configNext = (SMTPConfig *)sfPolicyUserDataGet(smtp_swap_config, _dpd.getDefaultPolicy()); if (config == NULL) { return 0; } sfPolicyUserDataIterate (sc, smtp_swap_config, SMTPCheckPolicyConfig); sfPolicyUserDataIterate (sc, smtp_swap_config, CheckFilePolicyConfig); policy_id = _dpd.getParserPolicy(sc); if (smtp_mime_mempool != NULL) { /* If max_mime_mem changes, mime mempool need to be adjusted bcz mempool max_memory will be changed. * Registering here to adjust Mime memory Pool when max_mime_mem changes. */ if( configNext->decode_conf.max_mime_mem < config->decode_conf.max_mime_mem ) _dpd.reloadAdjustRegister(sc, "SMTP-MIME-MEMPOOL", policy_id, &SMTPMimeReloadAdjust, NULL, NULL); } if (smtp_mempool != NULL) { if(configNext) { /* If memcap cahnges, log mempool need to be adjusted bcz mempool max_mempory will be changed. * Registering here to adjust Log memory Pool when memcap changes. */ if (configNext->memcap < config->memcap) _dpd.reloadAdjustRegister(sc, "SMTP-MEMPOOL", policy_id, &SMTPLogReloadAdjust, NULL, NULL); } } else if(configNext != NULL) { if (sfPolicyUserDataIterate(sc, smtp_swap_config, SMTPEnableDecoding) != 0) smtp_mime_mempool = (MemPool *)_dpd.fileAPI->init_mime_mempool(configNext->decode_conf.max_mime_mem, configNext->decode_conf.max_depth, smtp_mime_mempool, PROTOCOL_NAME); if (sfPolicyUserDataIterate(sc, smtp_swap_config, SMTPLogExtraData) != 0) smtp_mempool = (MemPool *)_dpd.fileAPI->init_log_mempool(configNext->log_config.email_hdrs_log_depth, configNext->memcap, smtp_mempool, PROTOCOL_NAME); if ( configNext->disabled ) return 0; } return 0; } static int SMTPReloadSwapPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SMTPConfig *pPolicyConfig = (SMTPConfig *)pData; if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); SMTP_FreeConfig(pPolicyConfig); } return 0; } static void * SMTPReloadSwap(struct _SnortConfig *sc, void *swap_config) { SMTPConfig *configNew = NULL, *configOld = NULL; tSfPolicyUserContextId smtp_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = smtp_config; if (smtp_swap_config == NULL) return NULL; smtp_config = smtp_swap_config; configOld = (SMTPConfig *)sfPolicyUserDataGet(old_config, _dpd.getDefaultPolicy()); configNew = (SMTPConfig *)sfPolicyUserDataGet(smtp_config, _dpd.getDefaultPolicy()); if(configNew) { if(smtp_mime_mempool) { if( (configOld->decode_conf.max_mime_mem != configNew->decode_conf.max_mime_mem) || (configOld->decode_conf.max_depth != configNew->decode_conf.max_depth) ) { #ifdef REG_TEST _dpd.fileAPI->displayMimeMempool(smtp_mime_mempool,&(configOld->decode_conf), &(configNew->decode_conf)); #endif /* Update the smtp_mime_mempool with new max_memmory and object size when max_mime_mem changes. */ _dpd.fileAPI->update_mime_mempool(smtp_mime_mempool, configNew->decode_conf.max_mime_mem, configNew->decode_conf.max_depth); } } if(smtp_mempool) { if( ( configOld->memcap != configNew->memcap) || ( configOld->log_config.email_hdrs_log_depth != configNew->log_config.email_hdrs_log_depth ) ) { #ifdef REG_TEST if (REG_TEST_EMAIL_FLAG_LOG_MEMPOOL_ADJUST & getRegTestFlagsForEmail()) { printf("[SMTP] Email Headers Log depth is: OLD VALUE # %u \n",configOld->log_config.email_hdrs_log_depth); printf("[SMTP] Setting Email Headers Log depth to # (NEW VALUE) %u \n",configNew->log_config.email_hdrs_log_depth); fflush(stdout); } _dpd.fileAPI->displayLogMempool(smtp_mempool, configOld->memcap, configNew->memcap); #endif /* Update the smtp_mempool with new max_memory and objest size when memcap changes. */ _dpd.fileAPI->update_log_mempool(smtp_mempool, configNew->memcap, configNew->log_config.email_hdrs_log_depth); smtp_stats.log_memcap_exceeded = 0; } } #ifdef REG_TEST _dpd.fileAPI->displayDecodeDepth(&(configOld->decode_conf), &(configNew->decode_conf)); #endif } sfPolicyUserDataFreeIterate (old_config, SMTPReloadSwapPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) SMTP_FreeConfigs(old_config); return NULL; } static void SMTPReloadSwapFree(void *data) { if (data == NULL) return; SMTP_FreeConfigs((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_config.c0000644000175000017500000011540614241076360021664 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*************************************************************************** * smtp_config.c * * Author: Andy Mullican * Author: Todd Wease * * Description: * * Handle configuration of the SMTP preprocessor * * Entry point functions: * * SMTP_ParseArgs() * ***************************************************************************/ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_smtp.h" #include "smtp_config.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" #include "sfPolicy.h" /* Private functions */ static int ProcessPorts(SMTPConfig *, char *, int, char **); static int ProcessCmds(SMTPConfig *, char *, int, char **, int, SMTPCmdTypeEnum); static int GetCmdId(SMTPConfig *, char *, SMTPCmdTypeEnum); static int AddCmd(SMTPConfig *, char *, SMTPCmdTypeEnum); static int ProcessAltMaxCmdLen(SMTPConfig *, char *, int, char **); static int ProcessMaxMimeDepth(SMTPConfig *, char *, int, char **); static int ProcessLogDepth(SMTPConfig *, char *, int, char **); static int ProcessXlink2State(SMTPConfig *, char *, int, char **); /* * Function: SMTP_ParseArgs(char *) * * Purpose: Process the preprocessor arguments from the rules file and * initialize the preprocessor's data struct. This function doesn't * have to exist if it makes sense to parse the args in the init * function. * * Arguments: args => argument list * * Returns: void function * */ void SMTP_ParseArgs(SMTPConfig *config, char *args) { int ret = 0; char *arg; char *value; char errStr[ERRSTRLEN]; char *saveptr; int errStrLen = ERRSTRLEN; int deprecated_options = 0; if ((config == NULL) || (args == NULL)) return; enablePort( config->ports, SMTP_DEFAULT_SERVER_PORT ); enablePort( config->ports, XLINK2STATE_DEFAULT_PORT ); enablePort( config->ports, SMTP_DEFAULT_SUBMISSION_PORT ); config->inspection_type = SMTP_STATELESS; config->max_command_line_len = DEFAULT_MAX_COMMAND_LINE_LEN; config->max_header_line_len = DEFAULT_MAX_HEADER_LINE_LEN; config->max_response_line_len = DEFAULT_MAX_RESPONSE_LINE_LEN; config->max_mime_depth = DEFAULT_MAX_MIME_DEPTH; config->memcap = DEFAULT_SMTP_MEMCAP; config->alert_xlink2state = 1; config->print_cmds = 1; config->enable_mime_decoding = 0; config->max_auth_command_line_len = DEFAULT_AUTH_MAX_COMMAND_LINE_LEN; _dpd.fileAPI->set_mime_decode_config_defauts(&(config->decode_conf)); _dpd.fileAPI->set_mime_log_config_defauts(&(config->log_config)); config->log_config.email_hdrs_log_depth = DEFAULT_LOG_DEPTH; config->cmd_config = (SMTPCmdConfig *)_dpd.snortAlloc(CMD_LAST, sizeof(SMTPCmdConfig), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (config->cmd_config == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory for SMTP " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } *errStr = '\0'; arg = strtok_r(args, CONF_SEPARATORS, &saveptr); while ( arg != NULL ) { unsigned long val = 0; if ( !strcasecmp(CONF_PORTS, arg) ) { ret = ProcessPorts(config, errStr, errStrLen, &saveptr); } else if ( !strcasecmp(CONF_INSPECTION_TYPE, arg) ) { value = strtok_r(NULL, CONF_SEPARATORS, &saveptr); if ( value == NULL ) { return; } if ( !strcasecmp(CONF_STATEFUL, value) ) { config->inspection_type = SMTP_STATEFUL; } else { config->inspection_type = SMTP_STATELESS; } } else if ( !strcasecmp(CONF_NORMALIZE, arg) ) { value = strtok_r(NULL, CONF_SEPARATORS, &saveptr); if ( value == NULL ) { return; } if ( !strcasecmp(CONF_NONE, value) ) { config->normalize = NORMALIZE_NONE; } else if ( !strcasecmp(CONF_ALL, value) ) { config->normalize = NORMALIZE_ALL; } else { config->normalize = NORMALIZE_CMDS; } } else if ( !strcasecmp(CONF_IGNORE_DATA, arg) ) { config->decode_conf.ignore_data = 1; } else if ( !strcasecmp(CONF_IGNORE_TLS_DATA, arg) ) { config->ignore_tls_data = 1; } else if ( !strcasecmp(CONF_MAX_COMMAND_LINE_LEN, arg) ) { char *endptr; value = strtok_r(NULL, CONF_SEPARATORS, &saveptr); if ( value == NULL ) return; config->max_command_line_len = strtol(value, &endptr, 10); } else if ( !strcasecmp(CONF_MAX_AUTH_COMMAND_LINE_LEN, arg) ) { char *endptr; value = strtok_r(NULL, CONF_SEPARATORS, &saveptr); if ( value == NULL ) return; config->max_auth_command_line_len = strtol(value, &endptr, 10); } else if ( !strcasecmp(CONF_MAX_HEADER_LINE_LEN, arg) ) { char *endptr; value = strtok_r(NULL, CONF_SEPARATORS, &saveptr); if ( value == NULL ) return; config->max_header_line_len = strtol(value, &endptr, 10); } else if ( !strcasecmp(CONF_MAX_RESPONSE_LINE_LEN, arg) ) { char *endptr; value = strtok_r(NULL, CONF_SEPARATORS, &saveptr); if ( value == NULL ) return; config->max_response_line_len = strtol(value, &endptr, 10); } else if ( !strcasecmp(CONF_NO_ALERTS, arg) ) { config->no_alerts = 1; } else if ( !strcasecmp(CONF_ALERT_UNKNOWN_CMDS, arg) ) { config->alert_unknown_cmds = 1; } else if ( !strcasecmp(CONF_INVALID_CMDS, arg) ) { /* Parse disallowed commands */ ret = ProcessCmds(config, errStr, errStrLen, &saveptr, ACTION_ALERT, SMTP_CMD_TYPE_NORMAL); } else if ( !strcasecmp(CONF_VALID_CMDS, arg) ) { /* Parse allowed commands */ ret = ProcessCmds(config, errStr, errStrLen, &saveptr, ACTION_NO_ALERT, SMTP_CMD_TYPE_NORMAL); } else if ( !strcasecmp(CONF_AUTH_CMDS, arg) ) { ret = ProcessCmds(config, errStr, errStrLen, &saveptr, ACTION_NO_ALERT, SMTP_CMD_TYPE_AUTH); } else if ( !strcasecmp(CONF_DATA_CMDS, arg) ) { ret = ProcessCmds(config, errStr, errStrLen, &saveptr, ACTION_NO_ALERT, SMTP_CMD_TYPE_DATA); } else if ( !strcasecmp(CONF_BDATA_CMDS, arg) ) { ret = ProcessCmds(config, errStr, errStrLen, &saveptr, ACTION_NO_ALERT, SMTP_CMD_TYPE_BDATA); } else if ( !strcasecmp(CONF_NORMALIZE_CMDS, arg) ) { /* Parse normalized commands */ ret = ProcessCmds(config, errStr, errStrLen, &saveptr, ACTION_NORMALIZE, SMTP_CMD_TYPE_NORMAL); } else if ( !strcasecmp(CONF_ALT_MAX_COMMAND_LINE_LEN, arg) ) { /* Parse max line len for commands */ ret = ProcessAltMaxCmdLen(config, errStr, errStrLen, &saveptr); } else if ( !strcasecmp(CONF_SMTP_MEMCAP, arg) ) { ret = _dpd.checkValueInRange(strtok_r(NULL, CONF_SEPARATORS, &saveptr), CONF_SMTP_MEMCAP, MIN_SMTP_MEMCAP, MAX_SMTP_MEMCAP, &val); config->memcap = (uint32_t)val; } else if ( !strcasecmp(CONF_MAX_MIME_MEM, arg) ) { ret = _dpd.checkValueInRange(strtok_r(NULL, CONF_SEPARATORS, &saveptr), CONF_MAX_MIME_MEM, MIN_MIME_MEM, MAX_MIME_MEM, &val); config->decode_conf.max_mime_mem = (int)val; } else if ( !strcasecmp(CONF_MAX_MIME_DEPTH, arg) ) { deprecated_options = 1; _dpd.logMsg("WARNING: %s(%d) => The SMTP config option 'max_mime_depth' is deprecated.\n", *(_dpd.config_file), *(_dpd.config_line)); ret = ProcessMaxMimeDepth(config, errStr, errStrLen, &saveptr); } else if ( !strcasecmp(CONF_ENABLE_MIME_DECODING, arg) ) { deprecated_options = 1; _dpd.logMsg("WARNING: %s(%d) => The SMTP config option 'enable_mime_decoding' is deprecated.\n", *(_dpd.config_file), *(_dpd.config_line)); config->enable_mime_decoding = 1; } else if ( !strcasecmp(CONF_DISABLED, arg) ) { config->disabled = 1; } else if ( !strcasecmp(CONF_XLINK2STATE, arg) ) { ret = ProcessXlink2State(config, errStr, errStrLen, &saveptr); } else if ( !strcasecmp(CONF_LOG_FILENAME, arg) ) { config->log_config.log_filename = 1; } else if ( !strcasecmp(CONF_LOG_MAIL_FROM, arg) ) { config->log_config.log_mailfrom = 1; } else if ( !strcasecmp(CONF_LOG_RCPT_TO, arg) ) { config->log_config.log_rcptto = 1; } else if ( !strcasecmp(CONF_LOG_EMAIL_HDRS, arg) ) { config->log_config.log_email_hdrs = 1; } else if ( !strcasecmp(CONF_EMAIL_HDRS_LOG_DEPTH, arg) ) { ret = ProcessLogDepth(config, errStr, errStrLen, &saveptr); } else if ( !strcasecmp(CONF_PRINT_CMDS, arg) ) { config->print_cmds = 1; } else if(!_dpd.fileAPI->parse_mime_decode_args(&(config->decode_conf), arg, "SMTP", &saveptr)) { ret = 0; } else { DynamicPreprocessorFatalMessage("%s(%d) => Unknown SMTP configuration option %s\n", *(_dpd.config_file), *(_dpd.config_line), arg); } if (ret == -1) { /* ** Fatal Error, log error and exit. */ if (*errStr) { DynamicPreprocessorFatalMessage("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), errStr); } else { DynamicPreprocessorFatalMessage("%s(%d) => Undefined Error.\n", *(_dpd.config_file), *(_dpd.config_line)); } } /* Get next token */ arg = strtok_r(NULL, CONF_SEPARATORS, &saveptr); } // NOTE: the default b64_depth is not defined in this file // but is equal to DEFAULT_MAX_MIME_DEPTH if(config->decode_conf.b64_depth == DEFAULT_MAX_MIME_DEPTH) { if(config->enable_mime_decoding) config->decode_conf.b64_depth = config->max_mime_depth; } else if(deprecated_options) { DynamicPreprocessorFatalMessage("%s(%d) => Cannot specify 'enable_mime_decoding' or 'max_mime_depth' with " "'b64_decode_depth'\n", *(_dpd.config_file), *(_dpd.config_line), arg); } if(!config->log_config.email_hdrs_log_depth) { if(config->log_config.log_email_hdrs) { _dpd.logMsg("WARNING: %s(%d) => 'log_email_hdrs' enabled with 'email_hdrs_log_depth' = 0." "Email headers won't be logged. Please set 'email_hdrs_log_depth' > 0 to enable logging.\n", *(_dpd.config_file), *(_dpd.config_line)); } config->log_config.log_email_hdrs = 0; } } void SMTP_CheckConfig(SMTPConfig *pPolicyConfig, tSfPolicyUserContextId context) { SMTPConfig *defaultConfig = (SMTPConfig *)sfPolicyUserDataGetDefault(context); if (pPolicyConfig == defaultConfig) { if (!_dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), &(defaultConfig->decode_conf), "SMTP")) return; if (!pPolicyConfig->memcap) pPolicyConfig->memcap = DEFAULT_SMTP_MEMCAP; if(pPolicyConfig->disabled && !pPolicyConfig->log_config.email_hdrs_log_depth) pPolicyConfig->log_config.email_hdrs_log_depth = DEFAULT_LOG_DEPTH; } else if (defaultConfig == NULL) { _dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), NULL, "SMTP"); if (pPolicyConfig->memcap) { DynamicPreprocessorFatalMessage("%s(%d) => SMTP: memcap must be " "configured in the default config.\n", *(_dpd.config_file), *(_dpd.config_line)); } if(pPolicyConfig->log_config.log_email_hdrs && pPolicyConfig->log_config.email_hdrs_log_depth) { DynamicPreprocessorFatalMessage("%s(%d) => SMTP: email_hdrs_log_depth must be " "configured in the default config.\n", *(_dpd.config_file), *(_dpd.config_line)); } } else { pPolicyConfig->memcap = defaultConfig->memcap; pPolicyConfig->log_config.email_hdrs_log_depth = defaultConfig->log_config.email_hdrs_log_depth; if(pPolicyConfig->disabled) { pPolicyConfig->decode_conf = defaultConfig->decode_conf; return; } _dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), &(defaultConfig->decode_conf), "SMTP"); } } void SMTP_PrintConfig(SMTPConfig *config) { int i; const SMTPToken *cmd; char buf[8192]; if (config == NULL) return; memset(&buf[0], 0, sizeof(buf)); _dpd.logMsg("SMTP Config:\n"); if(config->disabled) { _dpd.logMsg(" SMTP: INACTIVE\n"); } snprintf(buf, sizeof(buf) - 1, " Ports: "); for(i = 0; i < 65536; i++) { if( isPortEnabled( config->ports, i ) ) { _dpd.printfappend(buf, sizeof(buf) - 1, "%d ", i); } } _dpd.logMsg("%s\n", buf); _dpd.logMsg(" Inspection Type: %s\n", config->inspection_type ? "Stateful" : "Stateless"); snprintf(buf, sizeof(buf) - 1, " Normalize: "); switch (config->normalize) { case NORMALIZE_ALL: _dpd.printfappend(buf, sizeof(buf) - 1, "all"); break; case NORMALIZE_NONE: _dpd.printfappend(buf, sizeof(buf) - 1, "none"); break; case NORMALIZE_CMDS: if (config->print_cmds) { for (cmd = config->cmds; cmd->name != NULL; cmd++) { if (config->cmd_config[cmd->search_id].normalize) { _dpd.printfappend(buf, sizeof(buf) - 1, "%s ", cmd->name); } } } else { _dpd.printfappend(buf, sizeof(buf) - 1, "cmds"); } break; } _dpd.logMsg("%s\n", buf); _dpd.logMsg(" Ignore Data: %s\n", config->decode_conf.ignore_data ? "Yes" : "No"); _dpd.logMsg(" Ignore TLS Data: %s\n", config->ignore_tls_data ? "Yes" : "No"); _dpd.logMsg(" Ignore SMTP Alerts: %s\n", config->no_alerts ? "Yes" : "No"); if (!config->no_alerts) { snprintf(buf, sizeof(buf) - 1, " Max Command Line Length: "); if (config->max_command_line_len == 0) _dpd.printfappend(buf, sizeof(buf) - 1, "Unlimited"); else _dpd.printfappend(buf, sizeof(buf) - 1, "%d", config->max_command_line_len); _dpd.logMsg("%s\n", buf); snprintf(buf, sizeof(buf) - 1, " Max auth Command Line Length: "); _dpd.printfappend(buf, sizeof(buf) - 1, "%d", config->max_auth_command_line_len); _dpd.logMsg("%s\n", buf); if (config->print_cmds) { int max_line_len_count = 0; int max_line_len = 0; snprintf(buf, sizeof(buf) - 1, " Max Specific Command Line Length: "); for (cmd = config->cmds; cmd->name != NULL; cmd++) { max_line_len = config->cmd_config[cmd->search_id].max_line_len; if (max_line_len != 0) { if (max_line_len_count % 5 == 0) { _dpd.logMsg("%s\n", buf); snprintf(buf, sizeof(buf) - 1, " %s:%d ", cmd->name, max_line_len); } else { _dpd.printfappend(buf, sizeof(buf) - 1, "%s:%d ", cmd->name, max_line_len); } max_line_len_count++; } } if (max_line_len_count == 0) _dpd.logMsg("%sNone\n", buf); else _dpd.logMsg("%s\n", buf); } snprintf(buf, sizeof(buf) - 1, " Max Header Line Length: "); if (config->max_header_line_len == 0) _dpd.logMsg("%sUnlimited\n", buf); else _dpd.logMsg("%s%d\n", buf, config->max_header_line_len); snprintf(buf, sizeof(buf) - 1, " Max Response Line Length: "); if (config->max_response_line_len == 0) _dpd.logMsg("%sUnlimited\n", buf); else _dpd.logMsg("%s%d\n", buf, config->max_response_line_len); } _dpd.logMsg(" X-Link2State Alert: %s\n", config->alert_xlink2state ? "Yes" : "No"); if (config->alert_xlink2state) { _dpd.logMsg(" Drop on X-Link2State Alert: %s\n", config->drop_xlink2state ? "Yes" : "No"); } if (config->print_cmds && !config->no_alerts) { int alert_count = 0; snprintf(buf, sizeof(buf) - 1, " Alert on commands: "); for (cmd = config->cmds; cmd->name != NULL; cmd++) { if (config->cmd_config[cmd->search_id].alert) { _dpd.printfappend(buf, sizeof(buf) - 1, "%s ", cmd->name); alert_count++; } } if (alert_count == 0) { _dpd.logMsg("%sNone\n", buf); } else { _dpd.logMsg("%s\n", buf); } } _dpd.logMsg(" Alert on unknown commands: %s\n", config->alert_unknown_cmds ? "Yes" : "No"); _dpd.logMsg(" SMTP Memcap: %u\n", config->memcap); _dpd.logMsg(" MIME Max Mem: %d\n", config->decode_conf.max_mime_mem); if(config->decode_conf.b64_depth > -1) { _dpd.logMsg(" Base64 Decoding: %s\n", "Enabled"); switch(config->decode_conf.b64_depth) { case 0: _dpd.logMsg(" Base64 Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Base64 Decoding Depth: %d\n", config->decode_conf.b64_depth); break; } } else _dpd.logMsg(" Base64 Decoding: %s\n", "Disabled"); if(config->decode_conf.qp_depth > -1) { _dpd.logMsg(" Quoted-Printable Decoding: %s\n","Enabled"); switch(config->decode_conf.qp_depth) { case 0: _dpd.logMsg(" Quoted-Printable Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Quoted-Printable Decoding Depth: %d\n", config->decode_conf.qp_depth); break; } } else _dpd.logMsg(" Quoted-Printable Decoding: %s\n", "Disabled"); if(config->decode_conf.uu_depth > -1) { _dpd.logMsg(" Unix-to-Unix Decoding: %s\n","Enabled"); switch(config->decode_conf.uu_depth) { case 0: _dpd.logMsg(" Unix-to-Unix Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Unix-to-Unix Decoding Depth: %d\n", config->decode_conf.uu_depth); break; } } else _dpd.logMsg(" Unix-to-Unix Decoding: %s\n", "Disabled"); if(config->decode_conf.bitenc_depth > -1) { _dpd.logMsg(" Non-Encoded MIME attachment Extraction: %s\n","Enabled"); switch(config->decode_conf.bitenc_depth) { case 0: _dpd.logMsg(" Non-Encoded MIME attachment Extraction Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Non-Encoded MIME attachment Extraction Depth: %d\n", config->decode_conf.bitenc_depth); break; } } else _dpd.logMsg(" Non-Encoded MIME attachment Extraction/text: %s\n", "Disabled"); _dpd.logMsg(" Log Attachment filename: %s\n", config->log_config.log_filename ? "Enabled" : "Not Enabled"); _dpd.logMsg(" Log MAIL FROM Address: %s\n", config->log_config.log_mailfrom ? "Enabled" : "Not Enabled"); _dpd.logMsg(" Log RCPT TO Addresses: %s\n", config->log_config.log_rcptto ? "Enabled" : "Not Enabled"); _dpd.logMsg(" Log Email Headers: %s\n", config->log_config.log_email_hdrs ? "Enabled" : "Not Enabled"); if(config->log_config.log_email_hdrs) { _dpd.logMsg(" Email Hdrs Log Depth: %u\n", config->log_config.email_hdrs_log_depth); } } /* ** NAME ** ProcessPorts:: */ /** ** Process the port list. ** ** This configuration is a list of valid ports and is ended by a ** delimiter. ** ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessPorts(SMTPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int iPort; int iEndPorts = 0; int num_ports = 0; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "SMTP config is NULL.\n"); return -1; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid port list format."); return -1; } if(strcmp(CONF_START_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a port list with the '%s' token.", CONF_START_LIST); return -1; } /* Since ports are specified, clear default ports */ disablePort( config->ports, SMTP_DEFAULT_SERVER_PORT ); disablePort( config->ports, XLINK2STATE_DEFAULT_PORT ); disablePort( config->ports, SMTP_DEFAULT_SUBMISSION_PORT ); while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(CONF_END_LIST, pcToken)) { iEndPorts = 1; break; } iPort = strtol(pcToken, &pcEnd, 10); /* ** Validity check for port */ if(*pcEnd) { snprintf(ErrorString, ErrStrLen, "Invalid port number."); return -1; } if(iPort < 0 || iPort > MAXPORTS-1) { snprintf(ErrorString, ErrStrLen, "Invalid port number. Must be between 0 and 65535."); return -1; } enablePort( config->ports, iPort ); num_ports++; } if(!iEndPorts) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", CONF_PORTS, CONF_END_LIST); return -1; } else if(!num_ports) { snprintf(ErrorString, ErrStrLen, "SMTP: Empty port list not allowed."); return -1; } return 0; } /* ** NAME ** ProcessCmds:: */ /** ** Process the command list. ** ** This configuration is a list of valid ports and is ended by a ** delimiter. ** ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error */ static int ProcessCmds(SMTPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr, int action, SMTPCmdTypeEnum type) { char *pcToken; int iEndCmds = 0; int id; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "SMTP config is NULL.\n"); return -1; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if (!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid command list format."); return -1; } if (strcmp(CONF_START_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a command list with the '%s' token.", CONF_START_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if (strcmp(CONF_END_LIST, pcToken) == 0) { iEndCmds = 1; break; } id = GetCmdId(config, pcToken, type); if (action == ACTION_ALERT) { config->cmd_config[id].alert = 1; } else if (action == ACTION_NO_ALERT) { config->cmd_config[id].alert = 0; } else if (action == ACTION_NORMALIZE) { config->cmd_config[id].normalize = 1; } } if (!iEndCmds) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", action == ACTION_ALERT ? CONF_INVALID_CMDS : (action == ACTION_NO_ALERT ? CONF_VALID_CMDS : (action == ACTION_NORMALIZE ? CONF_NORMALIZE_CMDS : "")), CONF_END_LIST); return -1; } return 0; } /* Return id associated with a given command string */ static int GetCmdId(SMTPConfig *config, char *name, SMTPCmdTypeEnum type) { SMTPToken *cmd; for (cmd = config->cmds; cmd->name != NULL; cmd++) { if (strcasecmp(cmd->name, name) == 0) { if (type && (type != cmd->type)) cmd->type = type; return cmd->search_id; } } return AddCmd(config, name, type); } static int AddCmd(SMTPConfig *config, char *name, SMTPCmdTypeEnum type) { SMTPToken *cmds, *tmp_cmds; SMTPSearch *cmd_search; SMTPCmdConfig *cmd_config; int ret; if (config == NULL) { DynamicPreprocessorFatalMessage("%s(%d) SMTP config is NULL.\n", __FILE__, __LINE__); } config->num_cmds++; /* allocate enough memory for new commmand - alloc one extra for NULL entry */ cmds = (SMTPToken *)_dpd.snortAlloc(config->num_cmds + 1, sizeof(SMTPToken), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (cmds == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory for SMTP " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } /* This gets filled in later */ cmd_search = (SMTPSearch *)_dpd.snortAlloc(config->num_cmds, sizeof(SMTPSearch), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (cmd_search == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory for SMTP " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } cmd_config = (SMTPCmdConfig *)_dpd.snortAlloc(config->num_cmds, sizeof(SMTPCmdConfig), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (cmd_config == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory for SMTP " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } /* copy existing commands into newly allocated memory * don't need to copy anything from cmd_search since this hasn't been initialized yet */ ret = SafeMemcpy(cmds, config->cmds, (config->num_cmds - 1) * sizeof(SMTPToken), cmds, cmds + (config->num_cmds - 1)); if (ret != SAFEMEM_SUCCESS) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to memory copy SMTP command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } ret = SafeMemcpy(cmd_config, config->cmd_config, (config->num_cmds - 1) * sizeof(SMTPCmdConfig), cmd_config, cmd_config + (config->num_cmds - 1)); if (ret != SAFEMEM_SUCCESS) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to memory copy SMTP command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } /* add new command to cmds * cmd_config doesn't need anything added - this will probably be done by a calling function * cmd_search will be initialized when the searches are initialized */ tmp_cmds = &cmds[config->num_cmds - 1]; tmp_cmds->name = strdup(name); tmp_cmds->name_len = strlen(name); tmp_cmds->search_id = config->num_cmds - 1; if (type) tmp_cmds->type = type; if (tmp_cmds->name == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory for SMTP " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } /* free global memory structures */ if (config->cmds != NULL) _dpd.snortFree(config->cmds, sizeof(*(config->cmds)), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (config->cmd_search != NULL) _dpd.snortFree(config->cmd_search, sizeof(*(config->cmd_search)), PP_SMTP, PP_MEM_CATEGORY_CONFIG); if (config->cmd_config != NULL) _dpd.snortFree(config->cmd_config, sizeof(*(config->cmd_config)), PP_SMTP, PP_MEM_CATEGORY_CONFIG); /* set globals to new memory */ config->cmds = cmds; config->cmd_search = cmd_search; config->cmd_config = cmd_config; return (config->num_cmds - 1); } static int ProcessMaxMimeDepth(SMTPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr) { char *endptr; char *value; int max_mime_depth = 0; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "SMTP config is NULL.\n"); return -1; } value = strtok_r(NULL, CONF_SEPARATORS, saveptr); if ( value == NULL ) { snprintf(ErrorString, ErrStrLen, "Invalid format for max_mime_depth."); return -1; } max_mime_depth = strtol(value, &endptr, 10); if(*endptr) { snprintf(ErrorString, ErrStrLen, "Invalid format for max_mime_depth."); return -1; } if (max_mime_depth < MIN_MIME_DEPTH || max_mime_depth > MAX_MIME_DEPTH) { snprintf(ErrorString, ErrStrLen, "Invalid value for max_mime_depth." "It should range between %d and %d.", MIN_MIME_DEPTH, MAX_MIME_DEPTH); return -1; } if(max_mime_depth & 3) { max_mime_depth += 4 - (max_mime_depth & 3); _dpd.logMsg("WARNING: %s(%d) => SMTP: 'max_mime_depth' is not a multiple of 4. " "Rounding up to the next multiple of 4. The new 'max_mime_depth' is %d.\n", *(_dpd.config_file), *(_dpd.config_line), max_mime_depth); } config->max_mime_depth = max_mime_depth; return 0; } static int ProcessLogDepth(SMTPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr) { char *endptr; char *value; uint32_t log_depth = 0; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "SMTP config is NULL.\n"); return -1; } value = strtok_r(NULL, CONF_SEPARATORS, saveptr); if ( value == NULL ) { snprintf(ErrorString, ErrStrLen, "Missing value for email_hdrs_log_depth."); return -1; } log_depth = strtoul(value, &endptr, 10); if((value[0] == '-') || (*endptr != '\0')) { snprintf(ErrorString, ErrStrLen, "Invalid format '%s' for email_hdrs_log_depth.", value); return -1; } if(log_depth && log_depth < MIN_LOG_DEPTH) { snprintf(ErrorString, ErrStrLen, "Invalid value for email_hdrs_log_depth." "It should range between %d and %d.", MIN_LOG_DEPTH, MAX_LOG_DEPTH); return -1; } else if (log_depth > MAX_LOG_DEPTH) { _dpd.logMsg("WARNING: %s(%d) => Invalid value for email_hdrs_log_depth. " "It should range between %d and %d. The email_hdrs_log_depth " "will be reduced to the max value.\n", *(_dpd.config_file), *(_dpd.config_line), MIN_LOG_DEPTH, MAX_LOG_DEPTH); log_depth = MAX_LOG_DEPTH; } /* Rounding the log depth to a multiple of 8 since * multiple sessions use the same mempool * * Moved from spp_smtp.c */ if (log_depth & 7) log_depth += (8 - (log_depth & 7)); config->log_config.email_hdrs_log_depth = log_depth; return 0; } /* ** NAME ** ProcessAltMaxCmdLen:: */ /** ** ** alt_max_command_line_len { [] } ** ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error */ static int ProcessAltMaxCmdLen(SMTPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcLen; char *pcLenEnd; int iEndCmds = 0; int id; int cmd_len; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "SMTP config is NULL.\n"); return -1; } /* Find number */ pcLen = strtok_r(NULL, CONF_SEPARATORS, saveptr); if (!pcLen) { snprintf(ErrorString, ErrStrLen, "Invalid format for alt_max_command_line_len."); return -1; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if (!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid format for alt_max_command_line_len."); return -1; } cmd_len = strtoul(pcLen, &pcLenEnd, 10); if (pcLenEnd == pcLen) { snprintf(ErrorString, ErrStrLen, "Invalid format for alt_max_command_line_len (non-numeric)."); return -1; } if (strcmp(CONF_START_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start alt_max_command_line_len list with the '%s' token.", CONF_START_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if (strcmp(CONF_END_LIST, pcToken) == 0) { iEndCmds = 1; break; } id = GetCmdId(config, pcToken, SMTP_CMD_TYPE_NORMAL); config->cmd_config[id].max_line_len = cmd_len; } if (!iEndCmds) { snprintf(ErrorString, ErrStrLen, "Must end alt_max_command_line_len configuration with '%s'.", CONF_END_LIST); return -1; } return 0; } /* ** NAME ** ProcessXlink2State:: */ /** ** ** xlink2state { } ** ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error */ static int ProcessXlink2State(SMTPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int iEnd = 0; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "SMTP config is NULL.\n"); return -1; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid xlink2state argument format."); return -1; } if(strcmp(CONF_START_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start xlink2state arguments with the '%s' token.", CONF_START_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(CONF_END_LIST, pcToken)) { iEnd = 1; break; } if ( !strcasecmp(CONF_DISABLE, pcToken) ) { config->alert_xlink2state = 0; disablePort( config->ports, XLINK2STATE_DEFAULT_PORT ); } else if ( !strcasecmp(CONF_ENABLE, pcToken) ) { config->alert_xlink2state = 1; disablePort( config->ports, XLINK2STATE_DEFAULT_PORT ); } else if ( !strcasecmp(CONF_INLINE_DROP, pcToken) ) { if (!config->alert_xlink2state) { snprintf(ErrorString, ErrStrLen, "Alerting on X-LINK2STATE must be enabled to drop."); return -1; } config->drop_xlink2state = 1; } } if(!iEnd) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", CONF_XLINK2STATE, CONF_END_LIST); return -1; } return 0; } snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_normalize.h0000644000175000017500000000217414241076372022424 0ustar apoapo /* * smtp_normalize.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * Author: Andy Mullican * */ #ifndef __SMTP_NORMALIZE_H__ #define __SMTP_NORMALIZE_H__ #include "sf_snort_packet.h" int SMTP_NormalizeCmd(SFSnortPacket *, const uint8_t *, const uint8_t *, const uint8_t *); #endif snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_log.h0000644000175000017500000000657514241076366021221 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * smtp_log.h * * Author: Andy Mullican * **************************************************************************/ #ifndef __SMTP_LOG_H__ #define __SMTP_LOG_H__ #define GENERATOR_SMTP 124 /* Events for SMTP */ #define SMTP_COMMAND_OVERFLOW 1 #define SMTP_DATA_HDR_OVERFLOW 2 #define SMTP_RESPONSE_OVERFLOW 3 #define SMTP_SPECIFIC_CMD_OVERFLOW 4 #define SMTP_UNKNOWN_CMD 5 #define SMTP_ILLEGAL_CMD 6 #define SMTP_HEADER_NAME_OVERFLOW 7 #define SMTP_XLINK2STATE_OVERFLOW 8 #define SMTP_DECODE_MEMCAP_EXCEEDED 9 #define SMTP_B64_DECODING_FAILED 10 #define SMTP_QP_DECODING_FAILED 11 /* Do not delete or reuse this SID. Commenting this SID as this alert is no longer valid.* * #define SMTP_BITENC_DECODING_FAILED 12 */ #define SMTP_UU_DECODING_FAILED 13 #define SMTP_AUTH_ABORT_AUTH 14 #define SMTP_AUTH_COMMAND_OVERFLOW 15 #define SMTP_EVENT_MAX 16 /* Messages for each event */ #define SMTP_COMMAND_OVERFLOW_STR "(smtp) Attempted command buffer overflow" #define SMTP_DATA_HDR_OVERFLOW_STR "(smtp) Attempted data header buffer overflow" #define SMTP_RESPONSE_OVERFLOW_STR "(smtp) Attempted response buffer overflow" #define SMTP_SPECIFIC_CMD_OVERFLOW_STR "(smtp) Attempted specific command buffer overflow" #define SMTP_UNKNOWN_CMD_STR "(smtp) Unknown command" #define SMTP_ILLEGAL_CMD_STR "(smtp) Illegal command" #define SMTP_HEADER_NAME_OVERFLOW_STR "(smtp) Attempted header name buffer overflow" #define SMTP_XLINK2STATE_OVERFLOW_STR "(smtp) Attempted X-Link2State command buffer overflow" #define SMTP_DECODE_MEMCAP_EXCEEDED_STR "(smtp) No memory available for decoding. Max Mime Mem exceeded" #define SMTP_B64_DECODING_FAILED_STR "(smtp) Base64 Decoding failed." #define SMTP_QP_DECODING_FAILED_STR "(smtp) Quoted-Printable Decoding failed." #define SMTP_UU_DECODING_FAILED_STR "(smtp) Unix-to-Unix Decoding failed." #define SMTP_AUTH_ABORT_AUTH_STR "(smtp) Cyrus SASL authentication attack." #define SMTP_AUTH_COMMAND_OVERFLOW_STR "(smtp) Attempted authentication command buffer overflow" #define EVENT_STR_LEN 256 /* Function prototypes */ void SMTP_GenerateAlert(int, char *, ...); void SMTP_Decode( void ); void SMTP_DecodeAlert(void *ds); #endif snort-2.9.20/src/dynamic-preprocessors/smtp/spp_smtp.h0000644000175000017500000000217414241076410021217 0ustar apoapo /* * spp_smtp.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * Author: Andy Mullican * * Description: * * This file defines the publicly available functions for the SMTP * functionality for Snort. * */ #ifndef __SPP_SMTP_H__ #define __SPP_SMTP_H__ void SetupSMTP(void); #endif snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_config.h0000644000175000017500000001602014241076361021662 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*************************************************************************** * * smtp_config.h * * Author: Andy Mullican * Author: Todd Wease * ***************************************************************************/ #ifndef __SMTP_CONFIG_H__ #define __SMTP_CONFIG_H__ #include "sfPolicyUserData.h" #include "file_mail_common.h" #include "sf_email_attach_decode.h" #include "file_api.h" #define CONF_SEPARATORS " \t\n\r" #define CONF_PORTS "ports" #define CONF_INSPECTION_TYPE "inspection_type" #define CONF_NORMALIZE "normalize" #define CONF_NORMALIZE_CMDS "normalize_cmds" #define CONF_IGNORE_DATA "ignore_data" #define CONF_IGNORE_TLS_DATA "ignore_tls_data" #define CONF_MAX_COMMAND_LINE_LEN "max_command_line_len" #define CONF_MAX_HEADER_LINE_LEN "max_header_line_len" #define CONF_MAX_RESPONSE_LINE_LEN "max_response_line_len" #define CONF_ALT_MAX_COMMAND_LINE_LEN "alt_max_command_line_len" #define CONF_MAX_MIME_MEM "max_mime_mem" #define CONF_MAX_MIME_DEPTH "max_mime_depth" #define CONF_ENABLE_MIME_DECODING "enable_mime_decoding" #define CONF_B64_DECODE "b64_decode_depth" #define CONF_QP_DECODE "qp_decode_depth" #define CONF_BITENC_DECODE "bitenc_decode_depth" #define CONF_UU_DECODE "uu_decode_depth" #define CONF_LOG_FILENAME "log_filename" #define CONF_LOG_MAIL_FROM "log_mailfrom" #define CONF_LOG_RCPT_TO "log_rcptto" #define CONF_LOG_EMAIL_HDRS "log_email_hdrs" #define CONF_SMTP_MEMCAP "memcap" #define CONF_EMAIL_HDRS_LOG_DEPTH "email_hdrs_log_depth" #define CONF_DISABLED "disabled" #define CONF_NO_ALERTS "no_alerts" #define CONF_VALID_CMDS "valid_cmds" #define CONF_INVALID_CMDS "invalid_cmds" #define CONF_PRINT_CMDS "print_cmds" #define CONF_ALERT_UNKNOWN_CMDS "alert_unknown_cmds" #define CONF_XLINK2STATE "xlink2state" #define CONF_ENABLE "enable" #define CONF_DISABLE "disable" #define CONF_INLINE_DROP "drop" #define CONF_STATEFUL "stateful" #define CONF_STATELESS "stateless" #define CONF_YES "yes" #define CONF_ALL "all" #define CONF_NONE "none" #define CONF_CMDS "cmds" #define CONF_AUTH_CMDS "auth_cmds" #define CONF_DATA_CMDS "data_cmds" #define CONF_BDATA_CMDS "binary_data_cmds" #define CONF_START_LIST "{" #define CONF_END_LIST "}" #define CONF_MAX_AUTH_COMMAND_LINE_LEN "max_auth_command_line_len" #define NORMALIZE_NONE 0 #define NORMALIZE_CMDS 1 #define NORMALIZE_ALL 2 #define ACTION_ALERT 0 #define ACTION_NO_ALERT 1 #define ACTION_NORMALIZE 2 #define DEFAULT_MAX_COMMAND_LINE_LEN 0 #define DEFAULT_MAX_HEADER_LINE_LEN 0 #define DEFAULT_MAX_RESPONSE_LINE_LEN 0 #define DEFAULT_AUTH_MAX_COMMAND_LINE_LEN 1000 /*These are temporary values*/ #define MAX_DEPTH 65535 #define MIN_DEPTH -1 #define DEFAULT_MAX_MIME_MEM 838860 #define DEFAULT_MAX_MIME_DEPTH 1460 #define DEFAULT_SMTP_MEMCAP 838860 #define DEFAULT_LOG_DEPTH 1464 #define MAX_MIME_MEM 104857600 #define MIN_MIME_MEM 3276 #define MAX_MIME_DEPTH 20480 #define MIN_MIME_DEPTH 4 #define MAX_SMTP_MEMCAP 104857600 #define MIN_SMTP_MEMCAP 3276 #define MAX_LOG_DEPTH 20480 #define MIN_LOG_DEPTH 1 #define SMTP_DEFAULT_SERVER_PORT 25 /* SMTP normally runs on port 25 */ #define SMTP_DEFAULT_SUBMISSION_PORT 587 /* SMTP Submission port - see RFC 2476 */ #define XLINK2STATE_DEFAULT_PORT 691 /* XLINK2STATE sometimes runs on port 691 */ #define ERRSTRLEN 512 typedef enum _SMTPCmdTypeEnum { SMTP_CMD_TYPE_NORMAL = 0, SMTP_CMD_TYPE_DATA, SMTP_CMD_TYPE_BDATA, SMTP_CMD_TYPE_AUTH, SMTP_CMD_TYPE_LAST } SMTPCmdTypeEnum; typedef struct _SMTPSearch { char *name; int name_len; } SMTPSearch; typedef struct _SMTPToken { char *name; int name_len; int search_id; SMTPCmdTypeEnum type; } SMTPToken; typedef struct _SMTPCmdConfig { char alert; /* 1 if alert when seen */ char normalize; /* 1 if we should normalize this command */ int max_line_len; /* Max length of this particular command */ } SMTPCmdConfig; typedef struct _SMTPConfig { uint8_t ports[8192]; char inspection_type; char normalize; char ignore_tls_data; int max_command_line_len; int max_header_line_len; int max_response_line_len; char no_alerts; char alert_unknown_cmds; char alert_xlink2state; char drop_xlink2state; char print_cmds; char enable_mime_decoding; MAIL_LogConfig log_config; uint32_t memcap; int max_mime_depth; DecodeConfig decode_conf; SMTPToken *cmds; SMTPCmdConfig *cmd_config; SMTPSearch *cmd_search; void *cmd_search_mpse; int num_cmds; int disabled; int ref_count; uint32_t xtra_filename_id; uint32_t xtra_mfrom_id; uint32_t xtra_rcptto_id; uint32_t xtra_ehdrs_id; int max_auth_command_line_len; } SMTPConfig; typedef struct _SMTP_Stats { uint64_t sessions; uint64_t conc_sessions; uint64_t max_conc_sessions; uint64_t log_memcap_exceeded; uint64_t cur_sessions; MimeStats mime_stats; } SMTP_Stats; extern SMTP_Stats smtp_stats; /* Function prototypes */ void SMTP_ParseArgs(SMTPConfig *, char *); void SMTP_PrintConfig(SMTPConfig *config); void SMTP_CheckConfig(SMTPConfig *, tSfPolicyUserContextId); #endif snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_util.c0000644000175000017500000002641714241076376021406 0ustar apoapo/* * smtp_util.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * Author: Andy Mullican * * Description: * * This file contains SMTP helper functions. * * Entry point functions: * * safe_strchr() * safe_strstr() * copy_to_space() * safe_sscanf() * * */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "snort_bounds.h" #include "snort_smtp.h" #include "smtp_util.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_packet.h" #include "memory_stats.h" extern SMTP *smtp_ssn; extern char smtp_normalizing; extern MemPool *smtp_mime_mempool; extern MemPool *smtp_mempool; int SMTP_Print_Mem_Stats(FILE *fd, char *buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; if (fd) { len = fprintf(fd, ",%lu,%lu,%lu" ",%lu,%u,%u" ",%lu,%u,%u,%lu" , smtp_stats.sessions , smtp_stats.max_conc_sessions , smtp_stats.cur_sessions , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); return len; } if (buffer) { /* * Old buffer output for control socket comm, * like via, "show snort preprocessor-memory-usage" * CLI preserved as is */ len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics of SMTP on: %s\n" "SMTP Session Statistics:\n" " Total Sessions seen: " STDu64 "\n" " Max concurrent sessions: " STDu64 "\n" " Current Active sessions: " STDu64 "\n" "\n Memory Pool:\n" " Free Memory:\n" " SMTP Mime Pool: %14zu bytes\n" " SMTP Pool: %14zu bytes\n" " Used Memory:\n" " SMTP Mime Pool: %14zu bytes\n" " SMTP Pool: %14zu bytes\n" " ------------------- ---------------\n" " Total Memory: %14zu bytes\n" , ctime(&curr_time) , smtp_stats.sessions , smtp_stats.max_conc_sessions , smtp_stats.cur_sessions , (smtp_mime_mempool) ? (smtp_mime_mempool->max_memory - smtp_mime_mempool->used_memory) : 0 , (smtp_mempool) ? (smtp_mempool->max_memory - smtp_mempool->used_memory) : 0 , (smtp_mime_mempool) ? smtp_mime_mempool->used_memory : 0 , (smtp_mempool) ? smtp_mempool->used_memory : 0 , ((smtp_mime_mempool) ? (smtp_mime_mempool->max_memory) : 0) + ((smtp_mempool) ? (smtp_mempool->max_memory) : 0)); len += PopulateMemStatsBuffTrailer(buffer+len, len, meminfo); } else { _dpd.logMsg("SMTP Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions : %lu \n", smtp_stats.sessions); _dpd.logMsg(" Max concurrent sessions : %lu \n", smtp_stats.max_conc_sessions); _dpd.logMsg(" Current sessions : %lu \n", smtp_stats.cur_sessions); _dpd.logMsg(" SMTP Session \n"); _dpd.logMsg(" Used Memory :%14lu\n", meminfo[PP_MEM_CATEGORY_SESSION].used_memory); _dpd.logMsg(" No of Allocs :%14u\n", meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc); _dpd.logMsg(" No of Frees :%14u\n", meminfo[PP_MEM_CATEGORY_SESSION].num_of_free); _dpd.logMsg(" SMTP Config \n"); _dpd.logMsg(" Used Memory :%14lu\n", meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); _dpd.logMsg(" No of Allocs :%14u\n", meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc); _dpd.logMsg(" No of Frees :%14u\n", meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free); _dpd.logMsg(" Total memory used :%14lu\n", meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); } return len; } void SMTP_GetEOL(const uint8_t *ptr, const uint8_t *end, const uint8_t **eol, const uint8_t **eolm) { const uint8_t *tmp_eol; const uint8_t *tmp_eolm; /* XXX maybe should fatal error here since none of these * pointers should be NULL */ if (ptr == NULL || end == NULL || eol == NULL || eolm == NULL) return; tmp_eol = (uint8_t *)memchr(ptr, '\n', end - ptr); if (tmp_eol == NULL) { tmp_eol = end; tmp_eolm = end; } else { /* end of line marker (eolm) should point to marker and * end of line (eol) should point to end of marker */ if ((tmp_eol > ptr) && (*(tmp_eol - 1) == '\r')) { tmp_eolm = tmp_eol - 1; } else { tmp_eolm = tmp_eol; } /* move past newline */ tmp_eol++; } *eol = tmp_eol; *eolm = tmp_eolm; } int SMTP_CopyToAltBuffer(SFSnortPacket *p, const uint8_t *start, int length) { uint8_t *alt_buf; int alt_size; uint16_t *alt_len; int ret; /* if we make a call to this it means we want to use the alt buffer * regardless of whether we copy any data into it or not - barring a failure */ smtp_normalizing = 1; /* if start and end the same, nothing to copy */ if (length == 0) return 0; alt_buf = _dpd.altBuffer->data; alt_size = sizeof(_dpd.altBuffer->data); alt_len = &_dpd.altBuffer->len; ret = SafeMemcpy(alt_buf + *alt_len, start, length, alt_buf, alt_buf + alt_size); if (ret != SAFEMEM_SUCCESS) { _dpd.DetectFlag_Disable(SF_FLAG_ALT_DECODE); smtp_normalizing = 0; return -1; } *alt_len += length; _dpd.SetAltDecode(*alt_len); return 0; } /* Accumulate EOL seperated headers, one or more at a time */ int SMTP_CopyEmailHdrs(const uint8_t *start, int length, MAIL_LogState *log_state) { int log_avail = 0; uint8_t *log_buf; uint32_t *hdrs_logged; int ret = 0; if ((log_state == NULL) || (length <= 0)) return -1; log_avail = (log_state->log_depth - log_state->hdrs_logged); hdrs_logged = &(log_state->hdrs_logged); log_buf = (uint8_t *)log_state->emailHdrs; if(log_avail <= 0) { return -1; } if(length > log_avail ) { length = log_avail; } /* appended by the EOL \r\n */ ret = SafeMemcpy(log_buf + *hdrs_logged, start, length, log_buf, log_buf+(log_state->log_depth)); if (ret != SAFEMEM_SUCCESS) { return -1; } *hdrs_logged += length; return 0; } /* Accumulate email addresses from RCPT TO and/or MAIL FROM commands. Email addresses are separated by comma */ int SMTP_CopyEmailID(const uint8_t *start, int length, int command_type, MAIL_LogState *log_state) { uint8_t *alt_buf; int alt_size; uint16_t *alt_len; int ret; int log_avail=0; const uint8_t *tmp_eol; if ((log_state == NULL) || (length <= 0)) return -1; tmp_eol = (uint8_t *)memchr(start, ':', length); if(tmp_eol == NULL) return -1; if((tmp_eol+1) < (start+length)) { length = length - ( (tmp_eol+1) - start ); start = tmp_eol+1; } else return -1; switch (command_type) { case CMD_MAIL: alt_buf = log_state->senders; alt_size = MAX_EMAIL; alt_len = &(log_state->snds_logged); break; case CMD_RCPT: alt_buf = log_state->recipients; alt_size = MAX_EMAIL; alt_len = &(log_state->rcpts_logged); break; default: return -1; } log_avail = alt_size - *alt_len; if(log_avail <= 0 || !alt_buf) return -1; else if(log_avail < length) length = log_avail; if ( *alt_len > 0 && ((*alt_len + 1) < alt_size)) { alt_buf[*alt_len] = ','; *alt_len = *alt_len + 1; if(log_avail == length) length--; } ret = SafeMemcpy(alt_buf + *alt_len, start, length, alt_buf, alt_buf + alt_size); if (ret != SAFEMEM_SUCCESS) { if(*alt_len != 0) *alt_len = *alt_len - 1; return -1; } *alt_len += length; return 0; } void SMTP_LogFuncs(SMTPConfig *config, SFSnortPacket *p, MimeState *mime_ssn) { if((mime_ssn->log_flags == 0) || !config) return; if(mime_ssn->log_flags & FLAG_FILENAME_PRESENT) { _dpd.streamAPI->set_extra_data(p->stream_session, p, config->xtra_filename_id); } if(mime_ssn->log_flags & FLAG_MAIL_FROM_PRESENT) { _dpd.streamAPI->set_extra_data(p->stream_session, p, config->xtra_mfrom_id); } if(mime_ssn->log_flags & FLAG_RCPT_TO_PRESENT) { _dpd.streamAPI->set_extra_data(p->stream_session, p, config->xtra_rcptto_id); } if(mime_ssn->log_flags & FLAG_EMAIL_HDRS_PRESENT) { _dpd.streamAPI->set_extra_data(p->stream_session, p, config->xtra_ehdrs_id); } } #ifdef DEBUG_MSGS char smtp_print_buffer[65537]; const char * SMTP_PrintBuffer(SFSnortPacket *p) { const uint8_t *ptr = NULL; int len = 0; int iorig, inew; if (smtp_normalizing) { ptr = _dpd.altBuffer->data; len = _dpd.altBuffer->len; } else { ptr = p->payload; len = p->payload_size; } for (iorig = 0, inew = 0; iorig < len; iorig++, inew++) { if ((isascii((int)ptr[iorig]) && isprint((int)ptr[iorig])) || (ptr[iorig] == '\n')) { smtp_print_buffer[inew] = ptr[iorig]; } else if (ptr[iorig] == '\r' && ((iorig + 1) < len) && (ptr[iorig + 1] == '\n')) { iorig++; smtp_print_buffer[inew] = '\n'; } else if (isspace((int)ptr[iorig])) { smtp_print_buffer[inew] = ' '; } else { smtp_print_buffer[inew] = '.'; } } smtp_print_buffer[inew] = '\0'; return &smtp_print_buffer[0]; } #endif snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_paf.h0000644000175000017500000000262314241076374021173 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __SMTP_PAF_H__ #define __SMTP_PAF_H__ #include "sfPolicy.h" #include "sfPolicyUserData.h" void smtp_paf_free(void); #ifdef TARGET_BASED void register_smtp_paf_service(struct _SnortConfig *sc, int16_t app, tSfPolicyId policy); #endif void register_smtp_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy); bool is_data_end (void* ssn); #endif snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_util.h0000644000175000017500000000360614241076377021407 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************* * * smtp_util.h * * Author: Andy Mullican * Author: Todd Wease * *************************************************************************/ #ifndef __SMTP_UTIL_H__ #define __SMTP_UTIL_H__ #include "sf_snort_packet.h" void SMTP_GetEOL(const uint8_t *, const uint8_t *, const uint8_t **, const uint8_t **); int SMTP_CopyToAltBuffer(SFSnortPacket *, const uint8_t *, int); int SMTP_CopyEmailHdrs(const uint8_t *, int, MAIL_LogState *log_state ); int SMTP_CopyEmailID(const uint8_t *, int , int, MAIL_LogState *log_state ); void SMTP_LogFuncs(SMTPConfig *config, SFSnortPacket *p, MimeState *mime_ssn); int SMTP_Print_Mem_Stats(FILE *fd, char *buffer, PreprocMemInfo *meminfo); #ifdef DEBUG_MSGS const char * SMTP_PrintBuffer(SFSnortPacket *); #endif #endif /* __SMTP_UTIL_H__ */ snort-2.9.20/src/dynamic-preprocessors/smtp/smtp_buffer_dump.h0000644000175000017500000000331014241076351022710 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file smtp_buffer_dump.h ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during SMTP inspection. */ #ifndef __SMTP_BUFFER_DUMP_H__ #define __SMTP_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { STATE_COMMAND_DUMP, STATE_DATA_DUMP, STATE_BDATA_DUMP, STATE_XEXCH50_DUMP, STATE_AUTH_DUMP, STATE_UNKNOWN_DUMP, STATE_RESP_DUMP } SMTP_BUFFER_DUMP; void dumpBuffer(SMTP_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getSMTPBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/dynamic_preprocessors.dsp0000444000175000017500000000365114230012554023337 0ustar apoapo# Microsoft Developer Studio Project File - Name="dynamic_preprocessors" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Generic Project" 0x010a CFG=dynamic_preprocessors - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "dynamic_preprocessors.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "dynamic_preprocessors.mak" CFG="dynamic_preprocessors - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "dynamic_preprocessors - Win32 Release" (based on "Win32 (x86) Generic Project") !MESSAGE "dynamic_preprocessors - Win32 Debug" (based on "Win32 (x86) Generic Project") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" MTL=midl.exe !IF "$(CFG)" == "dynamic_preprocessors - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" !ELSEIF "$(CFG)" == "dynamic_preprocessors - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" !ENDIF # Begin Target # Name "dynamic_preprocessors - Win32 Release" # Name "dynamic_preprocessors - Win32 Debug" # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/treenodes.sed0000444000175000017500000000051314230012554020671 0ustar apoapos/Packet /SFSnortPacket / s/rules\.h/signature.h/ /signature.h/ a\ #include "sf_snort_packet.h" \ #include "event.h" s/RspFpList/void/ s/OutputFuncNode/void/ s/TagData/void/ s/RuleType/int/ s/IpAddrSet/void/ s/PortObject/void/ s/ActivateListNode/void/ s/struct _ListHead/void/ /sfutil\/sfghash\.h/d /sf_types\.h/d s/SFGHASH/void/g snort-2.9.20/src/dynamic-preprocessors/ssl_common/0000755000175000017500000000000014242725715020375 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl.c0000644000175000017500000004040014241076460021333 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 1998-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * */ /* * Adam Keeton * ssl.c * 10/09/07 */ #ifdef HAVE_CONFIG_H #include #endif #ifndef WIN32 #include #include #include #include #endif #include "sf_snort_packet.h" #include "ssl.h" #include "sf_types.h" #define THREE_BYTE_LEN(x) (x[2] | x[1] << 8 | x[0] << 16) static uint32_t SSL_decode_version_v3(uint8_t major, uint8_t minor) { /* Should only be called internally and by functions which have previously * validated their arguments */ if(major == 3) { /* Minor version */ switch(minor) { case 0: return SSL_VER_SSLV3_FLAG; break; case 1: return SSL_VER_TLS10_FLAG; break; case 2: return SSL_VER_TLS11_FLAG; break; case 3: return SSL_VER_TLS12_FLAG; break; default: return SSL_BAD_VER_FLAG; }; } /* This is a special case. Technically, major == 0, minor == 2 is SSLv2. * But if this traffic was SSLv2, this code path would not have been * exercised. */ else if(minor == 2) { return SSL_BAD_VER_FLAG; } return SSL_BAD_VER_FLAG; } static uint32_t SSL_decode_handshake_v3(const uint8_t *pkt , int size, uint32_t cur_flags, uint32_t pkt_flags) { SSL_handshake_t *handshake; SSL_handshake_hello_t *hello; uint32_t hs_len; uint32_t retval = 0; while (size > 0) { if (size < (int)SSL_HS_PAYLOAD_OFFSET) { retval |= SSL_TRUNCATED_FLAG; break; } /* Note, handhshake version field is optional depending on type */ /* Will recast to different type as necessary. */ handshake = (SSL_handshake_t *)pkt; pkt += SSL_HS_PAYLOAD_OFFSET; size -= SSL_HS_PAYLOAD_OFFSET; /* The code below effectively implements the following: * hs_len = 0; * memcpy(&hs_len, handshake->length, 3); * hs_len = ntohl(hs_len); * It was written this way for performance */ hs_len = THREE_BYTE_LEN(handshake->length); switch(handshake->type) { case SSL_HS_CHELLO: if(pkt_flags & FLAG_FROM_SERVER) retval |= SSL_BOGUS_HS_DIR_FLAG; else retval |= SSL_CLIENT_HELLO_FLAG | SSL_CUR_CLIENT_HELLO_FLAG; /* This type of record contains a version string. */ /* Make sure there is room for a version. */ if (size < (int)sizeof(uint16_t)) { retval |= SSL_TRUNCATED_FLAG; break; } hello = (SSL_handshake_hello_t *)handshake; retval |= SSL_decode_version_v3(hello->major, hello->minor); /* Compare version of record with version of handshake */ if((cur_flags & SSL_VERFLAGS) != (retval & SSL_VERFLAGS)) retval |= SSL_BAD_VER_FLAG; break; case SSL_HS_SHELLO: if(pkt_flags & FLAG_FROM_SERVER) retval |= SSL_SERVER_HELLO_FLAG | SSL_CUR_SERVER_HELLO_FLAG; else retval |= SSL_BOGUS_HS_DIR_FLAG; /* This type of record contains a version string. */ if (size < (int)sizeof(uint16_t)) { retval |= SSL_TRUNCATED_FLAG; break; } hello = (SSL_handshake_hello_t *)handshake; retval |= SSL_decode_version_v3(hello->major, hello->minor); /* Compare version of record with version of handshake */ if((cur_flags & SSL_VERFLAGS) != (retval & SSL_VERFLAGS)) retval |= SSL_BAD_VER_FLAG; break; case SSL_HS_SHELLO_DONE: if(pkt_flags & FLAG_FROM_SERVER) retval |= SSL_HS_SDONE_FLAG; else retval |= SSL_BOGUS_HS_DIR_FLAG; break; case SSL_HS_SKEYX: if(pkt_flags & FLAG_FROM_SERVER) retval |= SSL_SERVER_KEYX_FLAG | SSL_CUR_SERVER_KEYX_FLAG; else retval |= SSL_BOGUS_HS_DIR_FLAG; break; case SSL_HS_CKEYX: if(pkt_flags & FLAG_FROM_SERVER) retval |= SSL_BOGUS_HS_DIR_FLAG; else retval |= SSL_CLIENT_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG; break; case SSL_HS_CERT: retval |= SSL_CERTIFICATE_FLAG; break; /* The following types are not presently of interest */ case SSL_HS_HELLO_REQ: case SSL_HS_CERT_VERIFY: case SSL_HS_CERT_REQ: case SSL_CERT_URL: /* RFC 3546 */ case SSL_CERT_STATUS: /* RFC 3546 */ break; /* Will never see this since it's always encrypted */ case SSL_HS_FINISHED: default: /* Could be either a bad type or an encrypted handshake record */ /* If the record is encrypted, the type will likely appear bogus. */ return SSL_POSSIBLE_HS_FLAG | SSL_POSSIBLY_ENC_FLAG; } size -= hs_len; pkt += hs_len; } if (size < 0) retval |= SSL_TRUNCATED_FLAG; return retval; } static uint32_t SSL_decode_v3(const uint8_t *pkt, int size, uint32_t pkt_flags, uint8_t *alert_flags, uint16_t *partial_rec_len, int max_hb_len) { SSL_record_t *record; uint32_t retval = 0; uint16_t reclen; uint16_t hblen; int ccs = 0; /* Set if we see a Change Cipher Spec and reset after the next record */ SSL_heartbeat *heartbeat; uint16_t psize = 0; if( size && partial_rec_len && *partial_rec_len > 0) { if(size < (int)(*partial_rec_len)) { *partial_rec_len = *partial_rec_len - size; retval |= SSL_TRUNCATED_FLAG; return retval; } else { pkt += *partial_rec_len; size -= *partial_rec_len; } *partial_rec_len = 0; } while(size > 0) { if (size < (int)SSL_REC_PAYLOAD_OFFSET) { retval |= SSL_TRUNCATED_FLAG; break; } record = (SSL_record_t*)pkt; pkt += SSL_REC_PAYLOAD_OFFSET; size -= SSL_REC_PAYLOAD_OFFSET; retval |= SSL_decode_version_v3(record->major, record->minor); reclen = ntohs(record->length); psize = (size < reclen)? (reclen - size) : 0; switch (record->type) { case SSL_CHANGE_CIPHER_REC: retval |= SSL_CHANGE_CIPHER_FLAG; /* If there is another record, mark it as possibly encrypted */ if((size - (int)reclen) > 0) retval |= SSL_POSSIBLY_ENC_FLAG; ccs = 1; break; case SSL_ALERT_REC: retval |= SSL_ALERT_FLAG; ccs = 0; break; case SSL_HEARTBEAT_REC: retval |= SSL_HEARTBEAT_SEEN; ccs = 0; if((size < 0) || ((unsigned int)size < sizeof(SSL_heartbeat)) || !max_hb_len || !alert_flags) break; heartbeat = (SSL_heartbeat*)pkt; if((heartbeat->type) == SSL_HEARTBEAT_REQUEST) { hblen = ntohs(heartbeat->length); if(hblen > max_hb_len) *alert_flags = SSL_HEARTBLEED_REQUEST; } else if((heartbeat->type) == SSL_HEARTBEAT_RESPONSE) { if(reclen > max_hb_len ) *alert_flags = SSL_HEARTBLEED_RESPONSE; } else if (!(retval & SSL_BAD_VER_FLAG)) { if(reclen > max_hb_len ) *alert_flags = SSL_HEARTBLEED_UNKNOWN; } break; case SSL_HANDSHAKE_REC: /* If the CHANGE_CIPHER_FLAG is set, the following handshake * record should be encrypted */ if(!(retval & SSL_CHANGE_CIPHER_FLAG)) { int hsize = size < (int)reclen ? size : (int)reclen; retval |= SSL_decode_handshake_v3(pkt, hsize, retval, pkt_flags); } else if (ccs) { /* If we just got a change cipher spec, the next record must * be a finished encrypted, which has no type, so it will fall * into this default case, but it's good and we still need to * see client and server app data */ retval |= SSL_HS_SDONE_FLAG; } ccs = 0; break; case SSL_APPLICATION_REC: if(pkt_flags & FLAG_FROM_SERVER) retval |= SSL_SAPP_FLAG; else retval |= SSL_CAPP_FLAG; ccs = 0; break; default: retval |= SSL_BAD_TYPE_FLAG; ccs = 0; break; }; size -= reclen; pkt += reclen; } if (size < 0) retval |= SSL_TRUNCATED_FLAG; if(!(retval & SSL_VERFLAGS) || (retval & SSL_BAD_VER_FLAG)) { psize = 0; retval = retval | SSL_UNKNOWN_FLAG; } if(partial_rec_len) *partial_rec_len = psize; return retval; } // See RFCs 6101, 2246, 4346 and 5246 for SSL 3.0, TLS 1.0, 1.1 and 1.2 respectively // Appendix E. Backward Compatibility With SSL static inline bool SSL_v3_back_compat_v2(SSLv2_chello_t *chello) { if ((chello->major == 3) && (chello->minor <= 3)) return true; return false; } static uint32_t SSL_decode_v2(const uint8_t *pkt, int size, uint32_t pkt_flags) { uint16_t reclen; SSLv2_chello_t *chello; SSLv2_shello_t *shello; uint32_t retval = 0; SSLv2_record_t *record = (SSLv2_record_t*)pkt; while (size > 0) { if(size < SSL_V2_MIN_LEN) { retval |= SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG; break; } /* Note: top bit has special meaning and is not included * with the length */ reclen = ntohs(record->length) & 0x7fff; switch(record->type) { case SSL_V2_CHELLO: if(pkt_flags & FLAG_FROM_SERVER) retval |= SSL_BOGUS_HS_DIR_FLAG; else retval |= SSL_CLIENT_HELLO_FLAG | SSL_CUR_CLIENT_HELLO_FLAG ; if (size < (int)sizeof(SSLv2_chello_t)) { retval |= SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG; break; } chello = (SSLv2_chello_t*)pkt; // Check for SSLv3/TLS backward compatibility if (SSL_v3_back_compat_v2(chello)) retval |= SSL_V3_BACK_COMPAT_V2; else if (chello->minor != 2) retval |= SSL_BAD_VER_FLAG | SSL_UNKNOWN_FLAG; break; case SSL_V2_SHELLO: if(pkt_flags & FLAG_FROM_CLIENT) retval |= SSL_BOGUS_HS_DIR_FLAG; else retval |= SSL_SERVER_HELLO_FLAG | SSL_CUR_SERVER_HELLO_FLAG; if (size < (int)sizeof(SSLv2_shello_t)) { retval |= SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG; break; } shello = (SSLv2_shello_t*)pkt; if (shello->minor != 2) { retval |= SSL_BAD_VER_FLAG | SSL_UNKNOWN_FLAG; break; } break; case SSL_V2_CKEY: retval |= SSL_CLIENT_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG; break; default: return retval | SSL_BAD_TYPE_FLAG | SSL_UNKNOWN_FLAG; } size -= (reclen + 2); pkt += (reclen + 2); } if (size < 0) retval |= SSL_TRUNCATED_FLAG; return retval | SSL_VER_SSLV2_FLAG; } uint32_t SSL_decode(const uint8_t *pkt, int size, uint32_t pkt_flags, uint32_t prev_flags, uint8_t *alert_flags, uint16_t *partial_rec_len, int max_hb_len) { SSL_record_t *record; uint16_t reclen; uint32_t datalen; if(!pkt || !size) return SSL_ARG_ERROR_FLAG; if (size < (int)SSL_REC_PAYLOAD_OFFSET) return SSL_TRUNCATED_FLAG | SSL_UNKNOWN_FLAG; if(!( prev_flags & SSL_HS_SDONE_FLAG )) { /* Determine the protocol type. */ /* Only SSL v2 will have these bits set */ if(((pkt[0] & 0x80) || (pkt[0] & 0x40)) && !(partial_rec_len && *partial_rec_len)) return SSL_decode_v2(pkt, size, pkt_flags); /* If this packet is only 5 bytes, it inconclusive whether its SSLv2 or TLS. * If it is v2, it's definitely truncated anyway. By decoding a 5 byte * SSLv2 as TLS,the decoder will either catch a bad type, bad version, or * indicate that it is truncated. */ if(size == 5) return SSL_decode_v3(pkt, size, pkt_flags, alert_flags, partial_rec_len, max_hb_len); /* At this point, 'size' has to be > 5 */ /* If the field below contains a 2, it's either an SSLv2 client hello or * it is TLS and is containing a server hello. */ if(pkt[4] == 2) { /* This could be a TLS server hello. Check for a TLS version string */ if(size >= 10) { if(pkt[9] == 3) { /* Saw a TLS version, but this could also be an SSHv2 length. * If it is, check if a hypothetical TLS record-data length agress * with its record length */ datalen = THREE_BYTE_LEN( (pkt+6) ); record = (SSL_record_t*)pkt; reclen = ntohs(record->length); /* If these lengths match, it's v3 */ /* Otherwise, it's v2 */ if(reclen - SSL_HS_PAYLOAD_OFFSET != datalen) return SSL_decode_v2(pkt, size, pkt_flags); } } } /* Check if it's possibly a SSLv2 server-hello, in which case the version * is at byte 7 */ else if(size >= 8 && pkt[7] == 2) { /* A version of '2' at byte 7 overlaps with TLS record-data length. * Check if a hypothetical TLS record-data length agress with its * record length */ datalen = THREE_BYTE_LEN( (pkt+6) ); record = (SSL_record_t*)pkt; reclen = ntohs(record->length); /* If these lengths match, it's v3 */ /* Otherwise, it's v2 */ if(reclen - SSL_HS_PAYLOAD_OFFSET != datalen) return SSL_decode_v2(pkt, size, pkt_flags); } } return SSL_decode_v3(pkt, size, pkt_flags, alert_flags, partial_rec_len, max_hb_len); } snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_config.h0000644000175000017500000000616014241076464022676 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ /* * File: ssl_config.h * Author: Bhagyashree Bantwal * Brief: Configuration header file */ #ifndef SSL_CONFIG_H #define SSL_CONFIG_H /******************INCLUDES************************/ #include "ssl_include.h" #include "ssl_session.h" /******************DEFINES************************/ /* Configuration flags */ #define SSLPP_DISABLE_FLAG 0x0001 #define SSLPP_TRUSTSERVER_FLAG 0x0002 /******************DATASTRUCTURES************************/ typedef struct _SslRuleOptData { int flags; int mask; } SslRuleOptData; #ifdef ENABLE_HA typedef struct _SSLHAConfig { struct timeval min_sync_interval; char *startup_input_file; char *runtime_output_file; char *shutdown_output_file; # ifdef SIDE_CHANNEL uint8_t use_side_channel; # endif } SSLHAConfig; #endif typedef struct _SSLPP_config { ports_tbl_t ports; uint16_t flags; char *ssl_rules_dir; char *pki_dir; int memcap; int decrypt_memcap; int max_heartbeat_len; bool enable_ssl_ha; #ifdef ENABLE_HA SSLHAConfig *ssl_ha_config; #endif void *current_handle; void *reload_handle; } SSLPP_config_t; typedef struct { /* * SSL preprocessor global configuration structure. */ SSLPP_config_t config; } tSslPolicyConfig; #ifdef TARGET_BASED extern int16_t ssl_app_id; #endif #ifdef PERF_PROFILING extern PreprocStats sslpp_perf_stats; #endif /*****************EXTERNS************************/ /* Prototypes for public interface */ extern tSslPolicyConfig sslPolicyConfig[]; extern tSfPolicyUserContextId ssl_config; extern void SSLPP_init(struct _SnortConfig *sc, char *args); #ifdef SNORT_RELOAD extern void SSLReload(struct _SnortConfig *, char *, void **); extern int SSLReloadVerify(struct _SnortConfig *, void *); extern void * SSLReloadSwap(struct _SnortConfig *, void *); extern void SSLReloadSwapFree(void *); #endif /*****************FUNCTIONS************************/ void SSL_InitGlobals(void); int SSLPP_rule_eval(void *raw_packet, const uint8_t **cursor, void *data); void SSLPP_process(void *raw_packet, void *context); void SSLPP_drop_stats(int exiting); void DisplaySSLPPStats (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); #endif /* SSL_CONFIG_H */ snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_inspect.c0000644000175000017500000007273714241076477023112 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ /* * File: ssl_inspect.c * Author: Bhagyashree Bantwal * Brief: SSL traffic inspection */ #include "ssl_inspect.h" #include "ssl_session.h" #include "sf_types.h" #ifdef ENABLE_HA #include "ssl_ha.h" #endif #include #ifdef DUMP_BUFFER #include "../ssl/ssl_buffer_dump.h" #endif /* Ultimately calls SnortEventqAdd */ /* Arguments are: gid, sid, rev, classification, priority, message, rule_info */ #define ALERT(x,y) { _dpd.alertAdd(GENERATOR_SPP_SSLPP, x, 1, 0, 3, y, 0 ); } /* Wraps disabling detect with incrementing the counter */ #define DISABLE_DETECT() { _dpd.disableDetect(packet); counts.disabled++; } #define SSL_ERROR_FLAGS (SSL_BOGUS_HS_DIR_FLAG | \ SSL_BAD_VER_FLAG | \ SSL_BAD_TYPE_FLAG | \ SSL_UNKNOWN_FLAG) #define SSL3_FIRST_BYTE 0x16 #define SSL3_SECOND_BYTE 0x03 #define SSL2_CHELLO_BYTE 0x01 #define SSL2_SHELLO_BYTE 0x04 /* very simplistic - just enough to say this is binary data - the rules will make a final * judgement. Should maybe add an option to the imap configuration to enable the * continuing of command inspection like ftptelnet. */ bool IsTlsClientHello(const uint8_t *ptr, const uint8_t *end) { /* at least 3 bytes of data - see below */ if ((end - ptr) < 3) return false; if ((ptr[0] == SSL3_FIRST_BYTE) && (ptr[1] == SSL3_SECOND_BYTE)) { /* TLS v1 or SSLv3 */ return true; } else if ((ptr[2] == SSL2_CHELLO_BYTE) || (ptr[3] == SSL2_CHELLO_BYTE)) { /* SSLv2 */ return true; } return false; } /* this may at least tell us whether the server accepted the client hello by the presence * of binary data */ bool IsTlsServerHello(const uint8_t *ptr, const uint8_t *end) { /* at least 3 bytes of data - see below */ if ((end - ptr) < 3) return false; if ((ptr[0] == SSL3_FIRST_BYTE) && (ptr[1] == SSL3_SECOND_BYTE)) { /* TLS v1 or SSLv3 */ return true; } else if (ptr[2] == SSL2_SHELLO_BYTE) { /* SSLv2 */ return true; } return false; } bool IsSSL(const uint8_t *ptr, int len, int pkt_flags) { uint32_t ssl_flags = SSL_decode(ptr, len, pkt_flags, 0, NULL, NULL, 0); if ((ssl_flags != SSL_ARG_ERROR_FLAG) && !(ssl_flags & SSL_ERROR_FLAGS)) { return true; } return false; } static void SSL_SsnFree(void *); static SSL_SsnData *SSL_NewSession(SFSnortPacket *); void SSL_Set_flow_id( void *app_data, uint32_t fid ); static SSLPP_counters_t counts; void SSL_InitGlobals(void) { memset(&counts, 0, sizeof(counts)); } #ifdef DEBUG_MSGS static void SSL_PrintFlags(uint32_t flags) { if (flags & SSL_CHANGE_CIPHER_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CHANGE_CIPHER_FLAG\n");); } if (flags & SSL_ALERT_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_ALERT_FLAG\n");); } if (flags & SSL_POSSIBLE_HS_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_POSSIBLE_HS_FLAG\n");); } if (flags & SSL_CLIENT_HELLO_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CLIENT_HELLO_FLAG\n");); } if (flags & SSL_SERVER_HELLO_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_SERVER_HELLO_FLAG\n");); } if (flags & SSL_CERTIFICATE_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CERTIFICATE_FLAG\n");); } if (flags & SSL_SERVER_KEYX_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_SERVER_KEYX_FLAG\n");); } if (flags & SSL_CLIENT_KEYX_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CLIENT_KEYX_FLAG\n");); } if (flags & SSL_CIPHER_SPEC_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CIPHER_SPEC_FLAG\n");); } if (flags & SSL_SFINISHED_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_SFINISHED_FLAG\n");); } if (flags & SSL_SAPP_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_SAPP_FLAG\n");); } if (flags & SSL_CAPP_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CAPP_FLAG\n");); } if (flags & SSL_HS_SDONE_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_HS_SDONE_FLAG\n");); } if (flags & SSL_POSSIBLY_ENC_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_POSSIBLY_ENC_FLAG\n");); } if (flags & SSL_VER_SSLV2_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_VER_SSLV2_FLAG\n");); } if (flags & SSL_VER_SSLV3_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_VER_SSLV3_FLAG\n");); } if (flags & SSL_VER_TLS10_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_VER_TLS10_FLAG\n");); } if (flags & SSL_VER_TLS11_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_VER_TLS11_FLAG\n");); } if (flags & SSL_VER_TLS12_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_VER_TLS12_FLAG\n");); } #if 0 SSL_VERFLAGS (SSL_VER_SSLV2_FLAG | SSL_VER_SSLV3_FLAG | \ SSL_VER_TLS10_FLAG | SSL_VER_TLS11_FLAG | \ SSL_VER_TLS12_FLAG) #endif if (flags & SSL_CUR_CLIENT_HELLO_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CUR_CLIENT_HELLO_FLAG\n");); } if (flags & SSL_CUR_SERVER_HELLO_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CUR_SERVER_HELLO_FLAG\n");); } if (flags & SSL_CUR_SERVER_KEYX_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CUR_SERVER_KEYX_FLAG\n");); } if (flags & SSL_CUR_CLIENT_KEYX_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_CUR_CLIENT_KEYX_FLAG\n");); } if (flags & SSL_ENCRYPTED_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_ENCRYPTED_FLAG\n");); } if (flags & SSL_UNKNOWN_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_UNKNOWN_FLAG\n");); } #if 0 SSL_STATEFLAGS (SSL_CUR_CLIENT_HELLO_FLAG | SSL_CUR_SERVER_HELLO_FLAG | \ SSL_CUR_SERVER_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG | \ SSL_UNKNOWN_FLAG) #endif if (flags & SSL_BOGUS_HS_DIR_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_BOGUS_HS_DIR_FLAG\n");); } if (flags & SSL_TRAILING_GARB_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_TRAILING_GARB_FLAG\n");); } if (flags & SSL_BAD_TYPE_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_BAD_TYPE_FLAG\n");); } if (flags & SSL_BAD_VER_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_BAD_VER_FLAG\n");); } if (flags & SSL_TRUNCATED_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_TRUNCATED_FLAG\n");); } if (flags == SSL_ARG_ERROR_FLAG) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL_ARG_ERROR_FLAG\n");); } } #endif static void SSL_UpdateCounts(const uint32_t new_flags) { if(new_flags & SSL_CHANGE_CIPHER_FLAG) counts.cipher_change++; if (new_flags & SSL_ALERT_FLAG) counts.alerts++; if (new_flags & SSL_CLIENT_HELLO_FLAG) counts.hs_chello++; if (new_flags & SSL_SERVER_HELLO_FLAG) counts.hs_shello++; if (new_flags & SSL_CERTIFICATE_FLAG) counts.hs_cert++; if (new_flags & SSL_SERVER_KEYX_FLAG) counts.hs_skey++; if (new_flags & SSL_CLIENT_KEYX_FLAG) counts.hs_ckey++; if (new_flags & SSL_SFINISHED_FLAG) counts.hs_finished++; if (new_flags & SSL_HS_SDONE_FLAG) counts.hs_sdone++; if (new_flags & SSL_SAPP_FLAG) counts.sapp++; if (new_flags & SSL_CAPP_FLAG) counts.capp++; } static inline bool SSLPP_is_encrypted(uint32_t ssl_flags, SFSnortPacket *packet) { SSLPP_config_t *config = NULL; config = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_config); if (config->flags & SSLPP_TRUSTSERVER_FLAG) { if(ssl_flags & SSL_SAPP_FLAG) return true; } if (SSL_IS_CLEAN(ssl_flags)) { if (((ssl_flags & SSLPP_ENCRYPTED_FLAGS) == SSLPP_ENCRYPTED_FLAGS) || ((ssl_flags & SSLPP_ENCRYPTED_FLAGS2) == SSLPP_ENCRYPTED_FLAGS2)) { counts.completed_hs++; return true; } /* Check if we're either midstream or if packets were missed after the * connection was established */ else if ((_dpd.sessionAPI->get_session_flags (packet->stream_session) & SSNFLAG_MIDSTREAM) || (_dpd.streamAPI->missed_packets(packet->stream_session, SSN_DIR_BOTH))) { if ((ssl_flags & (SSL_CAPP_FLAG | SSL_SAPP_FLAG)) == (SSL_CAPP_FLAG | SSL_SAPP_FLAG)) { return true; } } } return false; } static inline uint32_t SSLPP_process_alert( uint32_t ssn_flags, uint32_t new_flags, SFSnortPacket *packet) { SSLPP_config_t *config = NULL; config = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_config); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Process Alert\n");); ssn_flags |= new_flags; /* Check if we've seen a handshake, that this isn't it, * that the cipher flags is not set, and that we are disabling detection */ if(SSL_IS_HANDSHAKE(ssn_flags) && !SSL_IS_HANDSHAKE(new_flags) && !(new_flags & SSL_CHANGE_CIPHER_FLAG) && (config->flags & SSLPP_DISABLE_FLAG) && !(new_flags & SSL_HEARTBEAT_SEEN)) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Disabling detect\n");); DISABLE_DETECT(); } #ifdef DUMP_BUFFER dumpBuffer(SSL_PROCESS_ALERT_DUMP,packet->payload,packet->payload_size); #endif /* Need to negate the application flags from the opposing side. */ if(packet->flags & FLAG_FROM_CLIENT) return ssn_flags & ~SSL_SAPP_FLAG; else if(packet->flags & FLAG_FROM_SERVER) return ssn_flags & ~SSL_CAPP_FLAG; return ssn_flags; } static inline uint32_t SSLPP_process_hs(uint32_t ssl_flags, uint32_t new_flags) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Process Handshake\n");); if(!SSL_BAD_HS(new_flags)) { ssl_flags |= new_flags & (SSL_CLIENT_HELLO_FLAG | SSL_SERVER_HELLO_FLAG | SSL_CLIENT_KEYX_FLAG | SSL_SFINISHED_FLAG); } else { counts.bad_handshakes++; } return ssl_flags; } static inline uint32_t SSLPP_process_app( uint32_t ssn_flags, uint32_t new_flags, SFSnortPacket *packet) { SSLPP_config_t *config = NULL; config = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_config); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Process Application\n");); if(!(config->flags & SSLPP_DISABLE_FLAG)) return ssn_flags | new_flags; if(SSLPP_is_encrypted(ssn_flags | new_flags, packet) ) { ssn_flags |= SSL_ENCRYPTED_FLAG; // Heartbleed check is disabled. Stop inspection on this session. if(!config->max_heartbeat_len) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "STOPPING INSPECTION (process_app)\n");); _dpd.sessionAPI->stop_inspection(packet->stream_session, packet, SSN_DIR_BOTH, -1, 0); counts.stopped++; } else if(!(new_flags & SSL_HEARTBEAT_SEEN)) { DISABLE_DETECT(); } } #ifdef DUMP_BUFFER dumpBuffer(SSL_PROCESS_APP_DUMP,packet->payload,packet->payload_size); #endif return ssn_flags | new_flags; } static inline void SSLPP_process_other( SSL_SsnData *sd, uint32_t new_flags, SFSnortPacket *packet) { SSLPP_config_t *config = NULL; config = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_config); /* Encrypted SSLv2 will appear unrecognizable. Check if the handshake was * seen and stop inspecting if so. */ /* Check for an existing handshake from both sides */ if((sd->ssn_flags & SSL_VER_SSLV2_FLAG) && SSL_IS_CHELLO(sd->ssn_flags) && SSL_IS_SHELLO(sd->ssn_flags) && (config->flags & SSLPP_DISABLE_FLAG) && !(new_flags & SSL_CHANGE_CIPHER_FLAG) && !(new_flags & SSL_HEARTBEAT_SEEN)) { sd->ssn_flags |= SSL_ENCRYPTED_FLAG | new_flags; if(!config->max_heartbeat_len) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "STOPPING INSPECTION (process_other)\n");); _dpd.sessionAPI->stop_inspection(packet->stream_session, packet, SSN_DIR_BOTH, -1, 0); } else if(!(new_flags & SSL_HEARTBEAT_SEEN)) { DISABLE_DETECT(); } } else { counts.unrecognized++; /* Special handling for SSLv2 */ if(new_flags & SSL_VER_SSLV2_FLAG) sd->ssn_flags |= new_flags; if(new_flags & SSL_UNKNOWN_FLAG) sd->ssn_flags |= new_flags; /* The following block is intentionally disabled. */ /* If we were unable to decode the packet, and previous packets had been * missed, we will not assume it is encrypted SSLv2. */ #if 0 /* More special handling for SSLv2. * If both server-side and client-side data was missed, and it has not * been identified it as TLS, it is possibly encrypted SSLv2. */ if( !(ssn_flags & ( SSL_VER_SSLV3_FLAG | SSL_VER_TLS10_FLAG | SSL_VER_TLS11_FLAG | SSL_VER_TLS12_FLAG)) ) { if(packet->stream_session && _dpd.streamAPI->missed_packets( packet->stream_session, SSN_DIR_SERVER) && _dpd.streamAPI->missed_packets( packet->stream_session, SSN_DIR_CLIENT) ) ssn_flags |= SSL_VER_SSLV2_FLAG; } #endif } #ifdef DUMP_BUFFER dumpBuffer(SSL_PROCESS_OTHER_DUMP,packet->payload,packet->payload_size); #endif } /* SSL Preprocessor process callback. */ void SSLPP_process(void *raw_packet, void *context) { SFSnortPacket *packet; uint32_t new_flags; SSL_SsnData *sd = NULL; ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); #ifdef TARGET_BASED int16_t app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif uint8_t heartbleed_type = 0; SSLPP_config_t *config = NULL; PROFILE_VARS; sfPolicyUserPolicySet (ssl_config, _dpd.getNapRuntimePolicy()); config = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_config); if (config == NULL) return; DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL Start ================================\n");); packet = (SFSnortPacket*)raw_packet; assert(IsTCP(packet) && packet->payload && packet->payload_size); if(!packet->stream_session) { #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL - Not inspecting packet\n");); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL End ================================\n");); #endif return; } if ( ssl_cb && ssl_cb->is_session_ssl( packet ) ) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL - SSL Packet\n");); } else { #ifdef TARGET_BASED /* Check packet based on protocol number */ app_id = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session); if (app_id == SFTARGET_UNKNOWN_PROTOCOL) { return; } if (app_id && (app_id != ssl_app_id)) { return; } /* Fall back to port checking */ if (!app_id) { #endif /* Make sure the packet is on the right port */ if(!(config->ports[PORT_INDEX(packet->src_port)] & CONV_PORT(packet->src_port)) && !(config->ports[PORT_INDEX(packet->dst_port)] & CONV_PORT(packet->dst_port))) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL - Not configured for these ports\n");); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL End ================================\n");); return; } #ifdef TARGET_BASED } #endif } #ifdef DEBUG_MSGS if (packet->flags & FLAG_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Server packet\n");); } else { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Client packet\n");); } if (packet->flags & FLAG_REBUILT_STREAM) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Packet is rebuilt\n");); } #endif PREPROC_PROFILE_START(sslpp_perf_stats); /* Flush opposite direction to keep conversation in sync */ if (!(packet->flags & FLAG_REBUILT_STREAM)) { switch (_dpd.streamAPI->get_reassembly_direction(packet->stream_session)) { case SSN_DIR_TO_SERVER: if (packet->flags & FLAG_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Flushing server side\n");); _dpd.streamAPI->response_flush_stream(packet); } break; case SSN_DIR_TO_CLIENT: if (packet->flags & FLAG_FROM_CLIENT) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Flushing client side\n");); _dpd.streamAPI->response_flush_stream(packet); } break; case SSN_DIR_BOTH: DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Flushing opposite side\n");); _dpd.streamAPI->response_flush_stream(packet); break; case SSN_DIR_NONE: default: break; } } #if 0 /* XXX If the preprocessor should in the future need to do any data * reassembly, one or the other of raw or reassembled needs to be used */ if (packet->flags & FLAG_STREAM_INSERT) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Packet is stream inserted - not inspecting\n");); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL End ================================\n");); return; } #endif sd = (SSL_SsnData *)SSL_GetAppData(packet); if (sd == NULL) { sd = SSL_NewSession(packet); if (sd == NULL) { PREPROC_PROFILE_END(sslpp_perf_stats); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL End ================================\n");); return; } sd->is_ssl = true; } #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Ssn flags before ----------------------\n");); SSL_PrintFlags(sd->ssn_flags); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "---------------------------------------\n");); #endif SSL_CLEAR_TEMPORARY_FLAGS(sd->ssn_flags); #ifdef DEBUG_MSGS if (packet->payload_size >= 5) { const uint8_t *pkt = packet->payload; DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Five bytes of data: %02x %02x %02x %02x %02x\n", pkt[0], pkt[1], pkt[2], pkt[3], pkt[4]);); } else { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Payload size < 5 bytes");); } #endif if(ssl_cb && !(ssl_cb->get_ssl_flow_flags(packet, sd, &new_flags))) { #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Skipping SSL_decode\n");); #endif } else { // Indexing into an array of partial record lengths partial_rec_len[raw+c][raw+s][reassm+c][reassm+s] uint8_t dir = (packet->flags & FLAG_FROM_SERVER)? 1 : 0; uint8_t index = (packet->flags & FLAG_REBUILT_STREAM)? 2 : 0; new_flags = SSL_decode(packet->payload, (int)packet->payload_size, packet->flags, sd->ssn_flags, &heartbleed_type, &(sd->partial_rec_len[dir+index]), config->max_heartbeat_len); #ifdef DUMP_BUFFER dumpBuffer(SSL_DECODE_DUMP,packet->payload,packet->payload_size); #endif if(heartbleed_type & SSL_HEARTBLEED_REQUEST) { ALERT(SSL_ALERT_HB_REQUEST, SSL_HEARTBLEED_REQUEST_STR); } else if(heartbleed_type & SSL_HEARTBLEED_RESPONSE) { ALERT(SSL_ALERT_HB_RESPONSE, SSL_HEARTBLEED_RESPONSE_STR); } else if(heartbleed_type & SSL_HEARTBLEED_UNKNOWN) { if(!dir) { ALERT(SSL_ALERT_HB_REQUEST, SSL_HEARTBLEED_REQUEST_STR); } else { ALERT(SSL_ALERT_HB_RESPONSE, SSL_HEARTBLEED_RESPONSE_STR); } } } if(sd->ssn_flags & SSL_ENCRYPTED_FLAG ) { counts.decoded++; #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SSL, "New flags -----------------------------\n");); SSL_PrintFlags(new_flags); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "---------------------------------------\n");); #endif SSL_UpdateCounts(new_flags); if(!(new_flags & SSL_HEARTBEAT_SEEN)) { DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Disabling detect\n");); DISABLE_DETECT(); PREPROC_PROFILE_END(sslpp_perf_stats); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL End ================================\n");); } sd->ssn_flags |= new_flags; #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Ssn flags after -----------------------\n");); SSL_PrintFlags(sd->ssn_flags); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "---------------------------------------\n");); #endif return; } if(SSL_IS_CHELLO(new_flags)) { if(ssl_cb) ssl_cb->session_initialize(packet, sd, SSL_Set_flow_id); } // If the client used an SSLv2 ClientHello with an SSLv3/TLS version and // the server replied with an SSLv3/TLS ServerHello, remove the backward // compatibility flag and the SSLv2 flag since this session will continue // as SSLv3/TLS. if ((sd->ssn_flags & SSL_V3_BACK_COMPAT_V2) && SSL_V3_SERVER_HELLO(new_flags)) sd->ssn_flags &= ~(SSL_VER_SSLV2_FLAG|SSL_V3_BACK_COMPAT_V2); if( SSL_IS_CHELLO(new_flags) && SSL_IS_CHELLO(sd->ssn_flags) && SSL_IS_SHELLO(sd->ssn_flags) ) { ALERT(SSL_INVALID_CLIENT_HELLO, SSL_INVALID_CLIENT_HELLO_STR); } else if(!(config->flags & SSLPP_TRUSTSERVER_FLAG)) { if( (SSL_IS_SHELLO(new_flags) && !SSL_IS_CHELLO(sd->ssn_flags) )) { if(!(_dpd.streamAPI->missed_packets( packet->stream_session, SSN_DIR_FROM_CLIENT))) ALERT(SSL_INVALID_SERVER_HELLO, SSL_INVALID_SERVER_HELLO_STR); } } counts.decoded++; #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SSL, "New flags -----------------------------\n");); SSL_PrintFlags(new_flags); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "---------------------------------------\n");); #endif SSL_UpdateCounts(new_flags); /* Note, there can be multiple record types in each SSL packet. * Processing them in this order is intentional. If there is an * Alert, we don't care about the other records */ if(SSL_IS_ALERT(new_flags)) { sd->ssn_flags = SSLPP_process_alert(sd->ssn_flags, new_flags, packet); } else if(SSL_IS_HANDSHAKE(new_flags)) { sd->ssn_flags = SSLPP_process_hs(sd->ssn_flags, new_flags); } else if(SSL_IS_APP(new_flags)) { sd->ssn_flags = SSLPP_process_app(sd->ssn_flags, new_flags, packet); } else { /* Different record type that we don't care about. * Either it's a 'change cipher spec' or we failed to recognize the * record type. Do not update session data */ SSLPP_process_other(sd, new_flags, packet); /* Application data is updated inside of SSLPP_process_other */ PREPROC_PROFILE_END(sslpp_perf_stats); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL End ================================\n");); return; } sd->ssn_flags |= new_flags; #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_SSL, "Ssn flags after -----------------------\n");); SSL_PrintFlags(sd->ssn_flags); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "---------------------------------------\n");); #endif PREPROC_PROFILE_END(sslpp_perf_stats); DEBUG_WRAP(DebugMessage(DEBUG_SSL, "SSL End ================================\n");); } /* Rule option evaluation (for both rule options) */ int SSLPP_rule_eval(void *raw_packet, const uint8_t **cursor, void *data) { SSL_SsnData *sd; SFSnortPacket *p = (SFSnortPacket*)raw_packet; SslRuleOptData *sdata = (SslRuleOptData *)data; if (!p || !p->tcp_header || !p->stream_session || !data) return RULE_NOMATCH; sd = (SSL_SsnData *)SSL_GetAppData(p); if(!sd) return RULE_NOMATCH; if ((sdata->flags & sd->ssn_flags) ^ sdata->mask) return RULE_MATCH; return RULE_NOMATCH; } void DisplaySSLPPStats (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f) { char buffer[CS_STATS_BUF_SIZE + 1]; int len = 0; if(counts.decoded) { len += snprintf(buffer, CS_STATS_BUF_SIZE, "SSL Preprocessor:\n" " SSL packets decoded: " FMTu64("-10") "\n" " Client Hello: " FMTu64("-10") "\n" " Server Hello: " FMTu64("-10") "\n" " Certificate: " FMTu64("-10") "\n" " Server Done: " FMTu64("-10") "\n" " Client Key Exchange: " FMTu64("-10") "\n" " Server Key Exchange: " FMTu64("-10") "\n" " Change Cipher: " FMTu64("-10") "\n" " Finished: " FMTu64("-10") "\n" " Client Application: " FMTu64("-10") "\n" " Server Application: " FMTu64("-10") "\n" " Alert: " FMTu64("-10") "\n" " Unrecognized records: " FMTu64("-10") "\n" " Completed handshakes: " FMTu64("-10") "\n" " Bad handshakes: " FMTu64("-10") "\n" " Sessions ignored: " FMTu64("-10") "\n" " Detection disabled: " FMTu64("-10") "\n" , counts.decoded , counts.hs_chello , counts.hs_shello , counts.hs_cert , counts.hs_sdone , counts.hs_ckey , counts.hs_skey , counts.cipher_change , counts.hs_finished , counts.capp , counts.sapp , counts.alerts , counts.unrecognized , counts.completed_hs , counts.bad_handshakes , counts.stopped , counts.disabled); #ifdef ENABLE_HA len += DisplaySSLHAStats(buffer + len); #endif } else { len = snprintf(buffer, CS_STATS_BUF_SIZE, "SSL Packet Details Not available\n SSL packets decoded: " FMTu64("-10") "\n", counts.decoded); } if (-1 == f(te, (const uint8_t *)buffer, len)) { _dpd.logMsg("Unable to send data to the frontend\n"); } } void SSLPP_drop_stats(int exiting) { if(!counts.decoded) return; _dpd.logMsg("SSL Preprocessor:\n"); _dpd.logMsg(" SSL packets decoded: " FMTu64("-10") "\n", counts.decoded); _dpd.logMsg(" Client Hello: " FMTu64("-10") "\n", counts.hs_chello); _dpd.logMsg(" Server Hello: " FMTu64("-10") "\n", counts.hs_shello); _dpd.logMsg(" Certificate: " FMTu64("-10") "\n", counts.hs_cert); _dpd.logMsg(" Server Done: " FMTu64("-10") "\n", counts.hs_sdone); _dpd.logMsg(" Client Key Exchange: " FMTu64("-10") "\n", counts.hs_ckey); _dpd.logMsg(" Server Key Exchange: " FMTu64("-10") "\n", counts.hs_skey); _dpd.logMsg(" Change Cipher: " FMTu64("-10") "\n", counts.cipher_change); _dpd.logMsg(" Finished: " FMTu64("-10") "\n", counts.hs_finished); _dpd.logMsg(" Client Application: " FMTu64("-10") "\n", counts.capp); _dpd.logMsg(" Server Application: " FMTu64("-10") "\n", counts.sapp); _dpd.logMsg(" Alert: " FMTu64("-10") "\n", counts.alerts); _dpd.logMsg(" Unrecognized records: " FMTu64("-10") "\n", counts.unrecognized); _dpd.logMsg(" Completed handshakes: " FMTu64("-10") "\n", counts.completed_hs); _dpd.logMsg(" Bad handshakes: " FMTu64("-10") "\n", counts.bad_handshakes); _dpd.logMsg(" Sessions ignored: " FMTu64("-10") "\n", counts.stopped); _dpd.logMsg(" Detection disabled: " FMTu64("-10") "\n", counts.disabled); #ifdef ENABLE_HA SSLPrintHAStats(); #endif } static void SSL_SsnFree(void *data) { SSL_SsnData *sd = (SSL_SsnData *)data; ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if(sd == NULL) return; if(ssl_cb) ssl_cb->session_free(sd->flow_id); free(sd); return; } static SSL_SsnData *SSL_NewSession(SFSnortPacket *p) { SSL_SsnData *sd = NULL; if (p->stream_session == NULL) return NULL; sd = (SSL_SsnData *)calloc(1, sizeof(SSL_SsnData)); if(sd == NULL) return NULL; sd->flow_id = 0; SSL_SetAppData(p, (void *)sd, SSL_SsnFree); return sd; } void SSL_Set_flow_id( void *app_data, uint32_t fid ) { SSL_SsnData *sd = (SSL_SsnData *)app_data; if( sd ) sd->flow_id = fid; } snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_inspect.h0000644000175000017500000000507714241076505023100 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ /* * File: ssl_inspect.h * Author: Bhagyashree Bantwal * Brief: Header file for SSL inspection */ #ifndef SSL_INSPECT_H #define SSL_INSPECT_H /*********************INCLUDES***************/ #include "ssl_config.h" /*********************DEFINES***************/ #define SSLPP_ENCRYPTED_FLAGS (SSL_HS_SDONE_FLAG | SSL_CLIENT_KEYX_FLAG | \ SSL_CAPP_FLAG | SSL_SAPP_FLAG) #define SSLPP_ENCRYPTED_FLAGS2 (SSL_HS_SDONE_FLAG | SSL_CHANGE_CIPHER_FLAG | \ SSL_CAPP_FLAG | SSL_SAPP_FLAG) #define GENERATOR_SPP_SSLPP 137 #define SSL_INVALID_CLIENT_HELLO 1 #define SSL_INVALID_SERVER_HELLO 2 #define SSL_ALERT_HB_REQUEST 3 #define SSL_ALERT_HB_RESPONSE 4 #define SSL_INVALID_CLIENT_HELLO_STR "(spp_ssl) Invalid Client HELLO after Server HELLO Detected" #define SSL_INVALID_SERVER_HELLO_STR "(spp_ssl) Invalid Server HELLO without Client HELLO Detected" #define SSL_HEARTBLEED_REQUEST_STR "(spp_ssl) Heartbeat Read Overrun Attempt Detected" #define SSL_HEARTBLEED_RESPONSE_STR "(spp_ssl) Large Heartbeat Response Detected" /*********************DATASTRUCTURES***************/ typedef struct _SSLPP_counters { uint64_t stopped; uint64_t disabled; uint64_t decoded; uint64_t alerts; uint64_t cipher_change; uint64_t unrecognized; uint64_t completed_hs; uint64_t bad_handshakes; uint64_t hs_chello; uint64_t hs_shello; uint64_t hs_cert; uint64_t hs_skey; uint64_t hs_ckey; uint64_t hs_finished; uint64_t hs_sdone; uint64_t capp; uint64_t sapp; } SSLPP_counters_t; #endif /* SSL_INSPECT_H */ snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_session.h0000644000175000017500000000323514241076506023111 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ /* * File: ssl_session.h * Author: Bhagyashree Bantwal * Brief: Header file for SSL session handling */ #ifndef SSL_SESSION_H #define SSL_SESSION_H #include "ssl_include.h" typedef struct _SSL_SsnData { uint32_t ssn_flags; uint32_t flow_id; bool is_ssl; uint16_t partial_rec_len[4]; } SSL_SsnData; static inline void SSL_SetAppData(const SFSnortPacket *p, void *data, StreamAppDataFree sdfree) { if( p->stream_session != NULL ) _dpd.sessionAPI->set_application_data( p->stream_session, PP_SSL, data, sdfree ); } static inline void * SSL_GetAppData(const SFSnortPacket *p) { if( p->stream_session != NULL ) return _dpd.sessionAPI->get_application_data( p->stream_session, PP_SSL ); else return NULL; } #endif snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_config.c0000644000175000017500000010023214241076463022663 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ /* * File: ssl_config.c * Author: Bhagyashree Bantwal * Brief: Configuration for the SSL preprocessor */ #include "ssl_config.h" #ifdef ENABLE_HA #include "ssl_ha.h" #endif #include #define PATH_MAX 4096 #define MIN_HB_LEN 0 #define MAX_HB_LEN 65535 #ifdef TARGET_BASED int16_t ssl_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif #ifdef PERF_PROFILING PreprocStats sslpp_perf_stats; #endif tSfPolicyUserContextId ssl_config = NULL; static void SSLFreeConfig(tSfPolicyUserContextId config, bool full_cleanup); static void SSLCleanExit(int, void *); static void SSLResetStats(int, void *); static int SSLPP_CheckConfig(struct _SnortConfig *); /* Parsing for the ssl_state rule option */ static int SSLPP_state_init(struct _SnortConfig *sc, char *name, char *params, void **data) { int flags = 0, mask = 0; char *end = NULL; char *tok; SslRuleOptData *sdata; tok = strtok_r(params, ",", &end); if(!tok) DynamicPreprocessorFatalMessage("%s(%d) => missing argument to" "ssl_state keyword\n", *(_dpd.config_file), *(_dpd.config_line)); do { int negated = 0; if (tok[0] == '!') { negated = 1; tok++; } if(!strcasecmp("client_hello", tok)) { flags |= SSL_CUR_CLIENT_HELLO_FLAG; if (negated) mask |= SSL_CUR_CLIENT_HELLO_FLAG; } else if(!strcasecmp("server_hello", tok)) { flags |= SSL_CUR_SERVER_HELLO_FLAG; if (negated) mask |= SSL_CUR_SERVER_HELLO_FLAG; } else if(!strcasecmp("client_keyx", tok)) { flags |= SSL_CUR_CLIENT_KEYX_FLAG; if (negated) mask |= SSL_CUR_CLIENT_KEYX_FLAG; } else if(!strcasecmp("server_keyx", tok)) { flags |= SSL_CUR_SERVER_KEYX_FLAG; if (negated) mask |= SSL_CUR_SERVER_KEYX_FLAG; } else if(!strcasecmp("unknown", tok)) { flags |= SSL_UNKNOWN_FLAG; if (negated) mask |= SSL_UNKNOWN_FLAG; } else { DynamicPreprocessorFatalMessage( "%s(%d) => %s is not a recognized argument to %s.\n", *(_dpd.config_file), _dpd.config_file, tok, name); } } while( (tok = strtok_r(NULL, ",", &end)) != NULL ); sdata = (SslRuleOptData *)calloc(1, sizeof(*sdata)); if (sdata == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "ssl_state preprocessor rule option.\n"); } sdata->flags = flags; sdata->mask = mask; *data = (void *)sdata; return 1; } /* Parsing for the ssl_version rule option */ static int SSLPP_ver_init(struct _SnortConfig *sc, char *name, char *params, void **data) { int flags = 0, mask = 0; char *end = NULL; char *tok; SslRuleOptData *sdata; tok = strtok_r(params, ",", &end); if(!tok) DynamicPreprocessorFatalMessage("%s(%d) => missing argument to" "ssl_state keyword\n", *(_dpd.config_file), *(_dpd.config_line)); do { int negated = 0; if (tok[0] == '!') { negated = 1; tok++; } if(!strcasecmp("sslv2", tok)) { flags |= SSL_VER_SSLV2_FLAG; if (negated) mask |= SSL_VER_SSLV2_FLAG; } else if(!strcasecmp("sslv3", tok)) { flags |= SSL_VER_SSLV3_FLAG; if (negated) mask |= SSL_VER_SSLV3_FLAG; } else if(!strcasecmp("tls1.0", tok)) { flags |= SSL_VER_TLS10_FLAG; if (negated) mask |= SSL_VER_TLS10_FLAG; } else if(!strcasecmp("tls1.1", tok)) { flags |= SSL_VER_TLS11_FLAG; if (negated) mask |= SSL_VER_TLS11_FLAG; } else if(!strcasecmp("tls1.2", tok)) { flags |= SSL_VER_TLS12_FLAG; if (negated) mask |= SSL_VER_TLS12_FLAG; } else { DynamicPreprocessorFatalMessage( "%s(%d) => %s is not a recognized argument to %s.\n", *(_dpd.config_file), _dpd.config_file, tok, name); } } while( (tok = strtok_r(NULL, ",", &end)) != NULL ); sdata = (SslRuleOptData *)calloc(1, sizeof(*sdata)); if (sdata == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "ssl_version preprocessor rule option.\n"); } sdata->flags = flags; sdata->mask = mask; *data = (void *)sdata; return 1; } static void UpdatePathToDir(char *full_path_dirname, unsigned int max_size, char *dirname) { int iRet; char *snort_conf_dir = *(_dpd.snort_conf_dir); if (!snort_conf_dir || !(*snort_conf_dir) || !full_path_dirname || !dirname) { DynamicPreprocessorFatalMessage(" %s(%d) => can't create path.\n", *(_dpd.config_file), *(_dpd.config_line)); } /*dirname is too long*/ if ( max_size < strlen(dirname) ) { DynamicPreprocessorFatalMessage(" %s(%d) => the dir name length %u is longer than allowed %u.\n", *(_dpd.config_file), *(_dpd.config_line), strlen(dirname), max_size); } /* * If an absolute path is specified, then use that. */ #ifndef WIN32 if(dirname[0] == '/') { iRet = snprintf(full_path_dirname, max_size, "%s", dirname); } else { /* * Set up the dir name directory. */ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '/') { iRet = snprintf(full_path_dirname,max_size, "%s%s", snort_conf_dir, dirname); } else { iRet = snprintf(full_path_dirname, max_size, "%s/%s", snort_conf_dir, dirname); } } #else if(strlen(dirname)>3 && dirname[1]==':' && dirname[2]=='\\') { iRet = snprintf(full_path_dirname, max_size, "%s", dirname); } else { /* ** Set up the dir name directory */ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '\\' || snort_conf_dir[strlen(snort_conf_dir) - 1] == '/' ) { iRet = snprintf(full_path_dirname,max_size, "%s%s", snort_conf_dir, dirname); } else { iRet = snprintf(full_path_dirname, max_size, "%s\\%s", snort_conf_dir, dirname); } } #endif if(iRet < 0) { DynamicPreprocessorFatalMessage(" %s(%d) => the dir name length %u is longer than allowed %u.\n", *(_dpd.config_file), *(_dpd.config_line), strlen(dirname), max_size); } } /* SSL Preprocessor configuration parsing */ static void SSLPP_config(SSLPP_config_t *config, char *conf) { char *saveptr; char *space_tok; char *comma_tok; char *portptr; char *search; SFP_errstr_t err; if(!conf) return; if (config == NULL) return; search = conf; while( (comma_tok = strtok_r(search, ",", &saveptr)) != NULL ) { search = NULL; space_tok = strtok_r(comma_tok, " ", &portptr); if(!space_tok) return; if(!strcasecmp(space_tok, "ports")) { memset(config->ports, 0, sizeof(config->ports)); if(SFP_ports(config->ports, portptr, err) != SFP_SUCCESS) DynamicPreprocessorFatalMessage( "%s(%d) => Failed to parse: %s\n", *(_dpd.config_file), *(_dpd.config_line), SFP_GET_ERR(err)); } else if(!strcasecmp(space_tok, "noinspect_encrypted")) { char *tmpChar; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to the" " SSL preprocessor: '%s' in %s\n", *(_dpd.config_file), *(_dpd.config_line), space_tok, tmpChar); } config->flags |= SSLPP_DISABLE_FLAG; } else if(!strcasecmp(space_tok, "trustservers")) { char *tmpChar; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to the" " SSL preprocessor: '%s' in %s\n", *(_dpd.config_file), *(_dpd.config_line), space_tok, tmpChar); } config->flags |= SSLPP_TRUSTSERVER_FLAG; } else if(!strcasecmp(space_tok, "pki_dir")) { char *tmpChar; char full_path_dirname[PATH_MAX+1]; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } UpdatePathToDir(full_path_dirname, PATH_MAX, tmpChar); config->pki_dir = strdup(full_path_dirname); if (!config->pki_dir) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "option in SSL preprocessor\n", __FILE__, __LINE__); } } else if(!strcasecmp(space_tok, "ssl_rules_dir")) { char *tmpChar; char full_path_dirname[PATH_MAX+1]; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } UpdatePathToDir(full_path_dirname, PATH_MAX, tmpChar); config->ssl_rules_dir = strdup(full_path_dirname); if (!config->ssl_rules_dir) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "option in SSL preprocessor\n", __FILE__, __LINE__); } } else if(!strcasecmp(space_tok, "memcap")) { int value; char *endStr = NULL; char *tmpChar; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } value = _dpd.SnortStrtol( tmpChar, &endStr, 10); if (( *endStr) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } config->memcap = value; } else if(!strcasecmp(space_tok, "decrypt_memcap")) { int value; char *endStr = NULL; char *tmpChar; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } value = _dpd.SnortStrtol( tmpChar, &endStr, 10); if (( *endStr) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } config->decrypt_memcap = value; } #ifdef ENABLE_HA else if(!strcasecmp(space_tok, "enable_ssl_ha")) { char *tmpChar; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } if(!strcasecmp(tmpChar, "yes")) config->enable_ssl_ha = true; else if(!strcasecmp(tmpChar, "no")) config->enable_ssl_ha = false; else { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument '%s' to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), tmpChar, space_tok); } } #endif else if(!strcasecmp(space_tok, "max_heartbeat_length")) { int value; char *endStr = NULL; char *tmpChar; tmpChar = strtok_r(NULL, " \t\n", &portptr); if(tmpChar == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } value = _dpd.SnortStrtol( tmpChar, &endStr, 10); if (( *endStr) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to '%s' option in the" " SSL preprocessor\n", *(_dpd.config_file), *(_dpd.config_line), space_tok); } if (value < MIN_HB_LEN || value > MAX_HB_LEN) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), space_tok, MIN_HB_LEN, MAX_HB_LEN); } config->max_heartbeat_len = value; } else { DynamicPreprocessorFatalMessage("%s(%d) => Invalid argument to the" " SSL preprocessor: '%s' in %s\n", *(_dpd.config_file), *(_dpd.config_line), comma_tok, conf); } } /* Verify configured options make sense */ if ((config->flags & SSLPP_TRUSTSERVER_FLAG) && !(config->flags & SSLPP_DISABLE_FLAG)) { DynamicPreprocessorFatalMessage("%s(%d) => SSL preprocessor: 'trustservers' " "requires 'noinspect_encrypted' to be useful.\n", *(_dpd.config_file), *(_dpd.config_line)); } } static void SSLPP_print_config(SSLPP_config_t *config) { char buf[1024]; /* For syslog printing */ int i; int newline; if (config == NULL) return; memset(buf, 0, sizeof(buf)); _dpd.logMsg("SSLPP config:\n"); _dpd.logMsg(" Encrypted packets: %s\n", config->flags & SSLPP_DISABLE_FLAG ? "not inspected" : "inspected"); _dpd.logMsg(" Ports:\n"); for(newline = 0, i = 0; i < MAXPORTS; i++) { if( config->ports[ PORT_INDEX(i) ] & CONV_PORT(i) ) { SFP_snprintfa(buf, sizeof(buf), " %5d", i); if( !((++newline) % 5) ) { SFP_snprintfa(buf, sizeof(buf), "\n"); _dpd.logMsg(buf); memset(buf, 0, sizeof(buf)); } } } if(newline % 5) SFP_snprintfa(buf, sizeof(buf), "\n"); _dpd.logMsg(buf); if ( config->flags & SSLPP_TRUSTSERVER_FLAG ) { _dpd.logMsg(" Server side data is trusted\n"); } if ( config->pki_dir ) { _dpd.logMsg(" PKI Directory: %s\n", config->pki_dir); } if ( config->ssl_rules_dir ) { _dpd.logMsg(" SSL Rules Directory: %s\n", config->ssl_rules_dir); } #ifdef ENABLE_HA _dpd.logMsg(" SSL HA enabled : %s\n", config->enable_ssl_ha ? "YES" : "NO" ); #endif _dpd.logMsg(" Maximum SSL Heartbeat length: %d\n", config->max_heartbeat_len); } static inline void SSLSetPort(SSLPP_config_t *config, int port) { if (config == NULL) return; config->ports[ PORT_INDEX(port) ] |= CONV_PORT(port); } static void SSLPP_init_config(SSLPP_config_t *config) { if (config == NULL) return; config->ssl_rules_dir = NULL; config->pki_dir = NULL; config->memcap = 100000; config->enable_ssl_ha = false; config->decrypt_memcap = 100000; config->current_handle = NULL; config->reload_handle = NULL; config->max_heartbeat_len = 0; /* Setup default ports */ SSLSetPort(config, 443); /* HTTPS */ SSLSetPort(config, 465); /* SMTPS */ SSLSetPort(config, 563); /* NNTPS */ SSLSetPort(config, 636); /* LDAPS */ SSLSetPort(config, 989); /* FTPS */ SSLSetPort(config, 992); /* TelnetS */ SSLSetPort(config, 993); /* IMAPS */ SSLSetPort(config, 994); /* IRCS */ SSLSetPort(config, 995); /* POPS */ } static void registerPortsForDispatch( struct _SnortConfig *sc, SSLPP_config_t *policy ) { int port; for ( port = 0; port < MAXPORTS; port++ ) { if( policy->ports[ port / 8 ] & ( 1 << ( port % 8 ) ) ) { _dpd.sessionAPI->enable_preproc_for_port( sc, PP_SSL, PROTO_BIT__TCP, port ); } } } static void registerPortsForReassembly( SSLPP_config_t *policy, int direction ) { uint32_t port; for ( port = 0; port < MAXPORTS; port++ ) { if( policy->ports[ port / 8 ] & ( 1 << ( port % 8 ) ) ) { _dpd.streamAPI->register_reassembly_port( NULL, port, direction ); } } } static void _addPortsToStream5Filter(struct _SnortConfig *sc, SSLPP_config_t *config, tSfPolicyId policy_id) { unsigned int portNum; if (config == NULL) return; for (portNum = 0; portNum < MAXPORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status (sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); } } } #ifdef TARGET_BASED static void _addServicesToStream5Filter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status (sc, ssl_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif void SSLPP_init(struct _SnortConfig *sc, char *args) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); SSLPP_config_t *pPolicyConfig = NULL; /* For SFR CLI*/ _dpd.controlSocketRegisterHandler(CS_TYPE_SSL_STATS, NULL, NULL, &DisplaySSLPPStats); if (ssl_config == NULL) { //create a context ssl_config = sfPolicyConfigCreate(); if (ssl_config == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "SSL preprocessor configuration.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage( "SSLPP_init(): The Stream preprocessor must be enabled.\n"); } SSL_InitGlobals(); _dpd.registerPreprocStats("ssl", SSLPP_drop_stats); _dpd.addPreprocConfCheck(sc, SSLPP_CheckConfig); _dpd.addPreprocExit(SSLCleanExit, NULL, PRIORITY_LAST, PP_SSL); _dpd.addPreprocResetStats(SSLResetStats, NULL, PRIORITY_LAST, PP_SSL); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("ssl", (void *)&sslpp_perf_stats, 0, _dpd.totalPerfStats, NULL); #endif #ifdef ENABLE_HA _dpd.addFuncToPostConfigList(sc, SSLHAPostConfigInit, NULL); #endif #ifdef TARGET_BASED ssl_app_id = _dpd.findProtocolReference("ssl"); if (ssl_app_id == SFTARGET_UNKNOWN_PROTOCOL) { ssl_app_id = _dpd.addProtocolReference("ssl"); } _dpd.sessionAPI->register_service_handler( PP_SSL, ssl_app_id ); #endif } sfPolicyUserPolicySet (ssl_config, policy_id); pPolicyConfig = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("SSL preprocessor can only be " "configured once.\n"); } pPolicyConfig = (SSLPP_config_t *)calloc(1, sizeof(SSLPP_config_t)); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "SSL preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(ssl_config, pPolicyConfig); SSLPP_init_config(pPolicyConfig); SSLPP_config(pPolicyConfig, args); SSLPP_print_config(pPolicyConfig); _dpd.preprocOptRegister(sc, "ssl_state", SSLPP_state_init, SSLPP_rule_eval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, "ssl_version", SSLPP_ver_init, SSLPP_rule_eval, free, NULL, NULL, NULL, NULL); _dpd.addPreproc( sc, SSLPP_process, PRIORITY_APPLICATION, PP_SSL, PROTO_BIT__TCP ); registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStream5Filter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStream5Filter(sc, policy_id); #endif } static int SSLFreeConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SSLPP_config_t *pPolicyConfig = (SSLPP_config_t *)pData; //do any housekeeping before freeing SSLPP_config_t sfPolicyUserDataClear (config, policyId); if(pPolicyConfig->pki_dir) free(pPolicyConfig->pki_dir); if(pPolicyConfig->ssl_rules_dir) free(pPolicyConfig->ssl_rules_dir); free(pPolicyConfig); return 0; } static void SSLFreeConfig(tSfPolicyUserContextId config, bool full_cleanup) { SSLPP_config_t *defaultConfig; ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if (config == NULL) return; defaultConfig = (SSLPP_config_t *)sfPolicyUserDataGetDefault(config); if(defaultConfig && ssl_cb) { ssl_cb->policy_free(&(defaultConfig->current_handle), full_cleanup); #ifdef ENABLE_HA if(defaultConfig->ssl_ha_config) { SSLHAConfigFree(defaultConfig->ssl_ha_config); defaultConfig->ssl_ha_config = NULL; } #endif } sfPolicyUserDataFreeIterate (config, SSLFreeConfigPolicy); sfPolicyConfigDelete(config); } static void SSLCleanExit(int signal, void *data) { if (ssl_config != NULL) { #ifdef ENABLE_HA SSLCleanHA(); #endif SSLFreeConfig(ssl_config, true); ssl_config = NULL; } } static void SSLResetStats(int signal, void *data) { SSL_InitGlobals(); #ifdef ENABLE_HA SSLResetHAStats(); #endif } static int SSLPP_SetSSLPolicy(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { _dpd.setSSLPolicyEnabled(sc, policyId, true); return 0; } static int SSLPP_PolicyInit(struct _SnortConfig *sc, tSfPolicyUserContextId ssl_config, SSLPP_config_t *pPolicyConfig, tSfPolicyId policyId, bool reloading) { ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); // Load SSL once for default policy if (pPolicyConfig != NULL) { if(pPolicyConfig->pki_dir && pPolicyConfig->ssl_rules_dir && ssl_cb) { if ( ssl_cb->policy_initialize(pPolicyConfig, reloading) != 0 ) { _dpd.errMsg( "SSLPP_PolicyInit(): Failed to initialize ssl_rules_dir and pki_dir.\n"); return -1; } else { if((sfPolicyUserDataIterate (sc, ssl_config, SSLPP_SetSSLPolicy))!= 0) { _dpd.errMsg( "SSLPP_PolicyInit(): SetSSLpolicy failed.\n"); return -1; } } } } return 0; } static int SSLPP_CheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { _dpd.setParserPolicy(sc, policyId); if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg( "SSLPP_CheckPolicyConfig(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } // Enable SSL preproc for any policies that have inspection turned on. static int SSLPP_CheckPolicyEnabled( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { if(_dpd.isSSLPolicyEnabled(sc)) { // Send packets to SSL preproc even when only doing Network Discovery. _dpd.reenablePreprocBit(sc, PP_SSL); } return 0; } static int SSLPP_CheckConfig(struct _SnortConfig *sc) { #ifdef ENABLE_HA int haNotConfigured = 0; #endif int rval; SSLPP_config_t *defaultConfig = (SSLPP_config_t *)sfPolicyUserDataGetDefault(ssl_config); if ((rval = sfPolicyUserDataIterate (sc, ssl_config, SSLPP_CheckPolicyConfig))) return rval; // Load SSL once for default policy if (defaultConfig) { if( SSLPP_PolicyInit(sc, ssl_config, defaultConfig, _dpd.getDefaultPolicy(), false) != 0 ) return -1; #ifdef ENABLE_HA if (defaultConfig->enable_ssl_ha) { haNotConfigured = (SSLVerifyHAConfig(sc, defaultConfig->ssl_ha_config) != 0); if (haNotConfigured) { _dpd.errMsg("WARNING: SSL HA misconfigured.\n"); return -1; } } #endif } if ((rval = sfPolicyUserDataIterate (sc, ssl_config, SSLPP_CheckPolicyEnabled))) return rval; return 0; } #ifdef SNORT_RELOAD void SSLReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId ssl_swap_config = (tSfPolicyUserContextId)*new_config; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); SSLPP_config_t * pPolicyConfig = NULL; if (ssl_swap_config == NULL) { //create a context ssl_swap_config = sfPolicyConfigCreate(); if (ssl_swap_config == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "SSL preprocessor configuration.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage( "SSLPP_init(): The Stream preprocessor must be enabled.\n"); } *new_config = (void *)ssl_swap_config; } sfPolicyUserPolicySet (ssl_swap_config, policy_id); pPolicyConfig = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_swap_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("SSL preprocessor can only be " "configured once.\n"); } pPolicyConfig = (SSLPP_config_t *)calloc(1, sizeof(SSLPP_config_t)); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "SSL preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(ssl_swap_config, pPolicyConfig); SSLPP_init_config(pPolicyConfig); SSLPP_config(pPolicyConfig, args); SSLPP_print_config(pPolicyConfig); _dpd.preprocOptRegister(sc, "ssl_state", SSLPP_state_init, SSLPP_rule_eval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, "ssl_version", SSLPP_ver_init, SSLPP_rule_eval, free, NULL, NULL, NULL, NULL); _dpd.addPreproc(sc, SSLPP_process, PRIORITY_APPLICATION, PP_SSL, PROTO_BIT__TCP); registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStream5Filter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStream5Filter(sc, policy_id); #endif } int SSLReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId ssl_swap_config = (tSfPolicyUserContextId)swap_config; SSLPP_config_t *reload_config = NULL; SSLPP_config_t *start_config = NULL; tSfPolicyId policyId = _dpd.getDefaultPolicy(); int ret = 0; bool register_sfssl_preproc_reload_flag = false; ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("SSLPP_init(): The Stream preprocessor must be enabled.\n"); return -1; } if (!ssl_swap_config || !ssl_config) return 0; reload_config = (SSLPP_config_t *)sfPolicyUserDataGet(ssl_swap_config, policyId); start_config = (SSLPP_config_t *)sfPolicyUserDataGet(ssl_config, policyId); if( !reload_config || !start_config ) { _dpd.errMsg("SSL reload: Turning on or off SSL preprocessor requires a restart.\n"); return -1; } if (ssl_cb && ssl_cb->reload_mem_adjust_available()) { _dpd.logMsg("SSL reload: SFSSL reload memcap adjust is available.\n"); register_sfssl_preproc_reload_flag = true; } if (!register_sfssl_preproc_reload_flag && reload_config->memcap != start_config->memcap) { /* Reload mem adjust not available, restart snort. */ _dpd.errMsg("SSL reload: Changing the memcap requires a restart.\n"); return -1; } if (!register_sfssl_preproc_reload_flag && reload_config->decrypt_memcap != start_config->decrypt_memcap) { /* Reload mem adjust not available, restart snort. */ _dpd.errMsg("SSL reload: Changing the decrypt_memcap requires a restart.\n"); return -1; } if (reload_config->memcap != start_config->memcap) { /* reload mem adjust is available, any change in sfssl memcap, adjust the difference in sftls memcap */ reload_config->decrypt_memcap += reload_config->memcap - start_config->memcap; _dpd.logMsg("SSL reload: Change in sfssl memcap:%d, sftls memcap:%d.\n", reload_config->memcap, reload_config->decrypt_memcap); } ret = SSLPP_PolicyInit(sc, ssl_swap_config, reload_config, policyId, true); if (!ret) { start_config->reload_handle = reload_config->current_handle; } if (register_sfssl_preproc_reload_flag) { /* Always register the SFSSL preproc reload, if sftls memcap adjuster is available * even in case where decrypt memcap doesn't change, may be we will add sftls_memcap in * ssl_tuning.conf file which might be differrent. */ ssl_cb->register_reload_mem_adjust(sc, reload_config); } return ret; } void * SSLReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId ssl_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = ssl_config; if (ssl_swap_config == NULL) return NULL; ssl_config = ssl_swap_config; return (void *)old_config; } void SSLReloadSwapFree(void *data) { if (data == NULL) return; SSLFreeConfig((tSfPolicyUserContextId)data, false); } #endif snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl.h0000644000175000017500000001573114241076462021353 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 1998-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * */ /* * Adam Keeton * ssl.h * 10/09/07 */ #ifndef SSL_H #define SSL_H #include #include #define SSL_NO_FLAG 0x00000000 // IMP: Changes to these flags will require updates to NSE's snort flow flags as well. /* SSL record type flags */ #define SSL_CHANGE_CIPHER_FLAG 0x00000001 #define SSL_ALERT_FLAG 0x00000002 #define SSL_POSSIBLE_HS_FLAG 0x00000004 /* For handshakes in TLSv3 that are encrypted */ #define SSL_CLIENT_HELLO_FLAG 0x00000008 #define SSL_SERVER_HELLO_FLAG 0x00000010 #define SSL_CERTIFICATE_FLAG 0x00000020 #define SSL_SERVER_KEYX_FLAG 0x00000040 #define SSL_CLIENT_KEYX_FLAG 0x00000080 #define SSL_CIPHER_SPEC_FLAG 0x00000100 #define SSL_SFINISHED_FLAG 0x00000200 #define SSL_SAPP_FLAG 0x00000400 #define SSL_CAPP_FLAG 0x00000800 #define SSL_HS_SDONE_FLAG 0x00001000 #define SSL_HEARTBEAT_SEEN 0x00002000 #define SSL_VER_SSLV2_FLAG 0x00004000 #define SSL_VER_SSLV3_FLAG 0x00008000 #define SSL_VER_TLS10_FLAG 0x00010000 #define SSL_VER_TLS11_FLAG 0x00020000 #define SSL_VER_TLS12_FLAG 0x00040000 /* Misc state flag */ #define SSL_POSSIBLY_ENC_FLAG 0x00080000 /* Version flags */ #define SSL_VERFLAGS (SSL_VER_SSLV2_FLAG | SSL_VER_SSLV3_FLAG | \ SSL_VER_TLS10_FLAG | SSL_VER_TLS11_FLAG | \ SSL_VER_TLS12_FLAG) #define SSL_V3_SERVER_HELLO(x) (((x) & SSL_CUR_SERVER_HELLO_FLAG) \ && ((x) & SSL_VERFLAGS) && (((x) & SSL_VERFLAGS) != SSL_VER_SSLV2_FLAG)) /* For rule state matching. These are only set when presently valid, * and do not stay set across packets. */ #define SSL_CUR_CLIENT_HELLO_FLAG 0x00100000 #define SSL_CUR_SERVER_HELLO_FLAG 0x00200000 #define SSL_CUR_SERVER_KEYX_FLAG 0x00400000 #define SSL_CUR_CLIENT_KEYX_FLAG 0x00800000 #define SSL_UNKNOWN_FLAG 0x01000000 /* Set when we decoded mostly garbage */ // Flag set when a client uses SSLv3/TLS backward compatibility and sends a // SSLv2 Hello specifying an SSLv3/TLS version. #define SSL_V3_BACK_COMPAT_V2 0x02000000 #define SSL_ENCRYPTED_FLAG 0x04000000 /* Provided for external use */ #define SSL_STATEFLAGS (SSL_CUR_CLIENT_HELLO_FLAG | SSL_CUR_SERVER_HELLO_FLAG | \ SSL_CUR_SERVER_KEYX_FLAG | SSL_CUR_CLIENT_KEYX_FLAG | \ SSL_UNKNOWN_FLAG) /* Error flags */ #define SSL_BOGUS_HS_DIR_FLAG 0x08000000 /* Record type disagrees with direction */ #define SSL_TRAILING_GARB_FLAG 0x10000000 #define SSL_BAD_TYPE_FLAG 0x20000000 #define SSL_BAD_VER_FLAG 0x40000000 #define SSL_TRUNCATED_FLAG 0x80000000 #define SSL_ARG_ERROR_FLAG 0x00000000 /* Note: overloaded with SSL_NO_FLAG */ /* The following flags are not presently of interest: * #define SSL_CERT_URL_FLAG (RFC 3546) * #define SSL_CERT_STATUS_FLAG (RFC 3546) * #define SSL_CFINISHED_FLAG This is contained in encrypted data * #define SSL_HS_FINISHED_FLAG Ignored for our purposes */ /* The constants used below are from RFC 2246 */ /* SSLv3 & TLS Record types */ #define SSL_CHANGE_CIPHER_REC 20 #define SSL_ALERT_REC 21 #define SSL_HANDSHAKE_REC 22 #define SSL_APPLICATION_REC 23 #define SSL_HEARTBEAT_REC 24 /* SSLv3 heartbeat types */ #define SSL_HEARTBEAT_REQUEST 1 #define SSL_HEARTBEAT_RESPONSE 2 /* SSLv3 & TLS handshake types */ #define SSL_HS_HELLO_REQ 0 #define SSL_HS_CHELLO 1 #define SSL_HS_SHELLO 2 #define SSL_HS_CERT 11 #define SSL_HS_SKEYX 12 #define SSL_HS_CERT_REQ 13 #define SSL_HS_SHELLO_DONE 14 #define SSL_HS_CERT_VERIFY 15 #define SSL_HS_CKEYX 16 #define SSL_HS_FINISHED 20 #define SSL_CERT_URL 21 #define SSL_CERT_STATUS 22 /* SSLv2 handshake types */ #define SSL_V2_CHELLO 1 #define SSL_V2_CKEY 2 #define SSL_V2_SHELLO 4 #ifdef WIN32 #pragma pack(push,ssl_hdrs,1) #else #pragma pack(1) #endif typedef struct _SSL_record { uint8_t type; uint8_t major; uint8_t minor; uint16_t length; } SSL_record_t; #define SSL_REC_PAYLOAD_OFFSET (sizeof(uint8_t) * 5) typedef struct _SSL_heartbeat { uint8_t type; uint16_t length; } SSL_heartbeat; typedef struct _SSL_handshake { uint8_t type; uint8_t length[3]; } SSL_handshake_t; typedef struct _SSL_handshake_hello { uint8_t type; uint8_t length[3]; uint8_t major; uint8_t minor; } SSL_handshake_hello_t; // http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html typedef struct _SSLv2_record { uint16_t length; uint8_t type; } SSLv2_record_t; typedef struct _SSLv2_chello { uint16_t length; uint8_t type; uint8_t major; uint8_t minor; } SSLv2_chello_t; typedef struct _SSLv2_shello { uint16_t length; uint8_t type; uint8_t ssnid; uint8_t certtype; uint8_t major; uint8_t minor; } SSLv2_shello_t; #define SSL_V2_MIN_LEN 5 #ifdef WIN32 #pragma pack(pop,ssl_hdrs) #else #pragma pack() #endif #define SSL_HS_PAYLOAD_OFFSET (sizeof(uint8_t) * 4) /* Type and length fields */ #define SSL_BAD_HS(x) (x & SSL_BOGUS_HS_DIR_FLAG) #define SSL_IS_HANDSHAKE(x) (x & (SSL_CLIENT_HELLO_FLAG | SSL_SERVER_HELLO_FLAG | \ SSL_CERTIFICATE_FLAG | SSL_SERVER_KEYX_FLAG | \ SSL_CLIENT_KEYX_FLAG | SSL_CIPHER_SPEC_FLAG)) #define SSL_IS_CHELLO(x) (x & SSL_CLIENT_HELLO_FLAG) #define SSL_IS_SHELLO(x) (x & SSL_SERVER_HELLO_FLAG) #define SSL_IS_CKEYX(x) (x & SSL_CLIENT_KEYX_FLAG) #define SSL_IS_APP(x) ((x & SSL_SAPP_FLAG) || (x & SSL_CAPP_FLAG)) #define SSL_IS_ALERT(x) (x & SSL_ALERT_FLAG) #define SSL_CLEAR_TEMPORARY_FLAGS(x) x &= ~SSL_STATEFLAGS; /* Verifies that the error flags haven't been triggered */ #define SSL_IS_CLEAN(x) !(x & (SSL_BOGUS_HS_DIR_FLAG | SSL_TRUNCATED_FLAG | \ SSL_BAD_VER_FLAG | SSL_BAD_TYPE_FLAG | \ SSL_TRAILING_GARB_FLAG | SSL_UNKNOWN_FLAG)) #define SSL_HEARTBLEED_REQUEST 0x01 #define SSL_HEARTBLEED_RESPONSE 0x02 #define SSL_HEARTBLEED_UNKNOWN 0x03 uint32_t SSL_decode(const uint8_t *pkt, int size, uint32_t pktflags, uint32_t prevflags, uint8_t *alert_flags, uint16_t *partial_rec_len, int hblen); #endif snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_include.h0000644000175000017500000000473514241076471023060 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2013-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ /* * File: ssl_include.h * Author: Bhagyashree Bantwal * Brief: Header file with all includes required by SSL */ #ifndef SSL_INCLUDE_H #define SSL_INCLUDE_H /******************INCLUDES************************/ #include #include #include #include #ifndef WIN32 #include #include #include #else #include "sf_types.h" #endif #include #include #include "ssl.h" #include "sfcommon.h" #include "profiler.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #include "sf_preproc_info.h" #include "sf_snort_packet.h" typedef void (*PP_Set_Flow_Id_Callback_Func) (void *app_data, uint32_t flow_context); typedef struct _ssl_callback_interface { int (*policy_initialize)(void *, bool); void (*policy_free)(void **, bool); void (*session_initialize)(SFSnortPacket* p, void *app_data, PP_Set_Flow_Id_Callback_Func pp_callback); void (*session_free)(uint32_t fid); bool (*is_session_ssl)(SFSnortPacket* p); int (*get_ssl_flow_flags)(SFSnortPacket* p, void *sd, uint32_t *ssn_flags); void (*register_ha_funcs)(void); bool (*reload_mem_adjust_available)(void); void (*register_reload_mem_adjust)(struct _SnortConfig *sc, void *reload_config); } ssl_callback_interface_t; extern bool IsTlsClientHello(const uint8_t *ptr, const uint8_t *end); extern bool IsTlsServerHello(const uint8_t *ptr, const uint8_t *end); extern bool IsSSL(const uint8_t *ptr, int len, int pkt_flags); #endif snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_ha.h0000644000175000017500000000516214241076470022017 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2012-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ /************************************************************************** * * ssl_ha.h * * Authors: Michael Altizer , Russ Combs , Bhagyashree Bantwal * * Description: * * SSL high availability exported functionality. * **************************************************************************/ #ifndef __SSL_HA_H__ #define __SSL_HA_H__ #ifdef ENABLE_HA #include "ssl_config.h" typedef enum { HA_EVENT_UPDATE, HA_EVENT_DELETE, HA_EVENT_MAX } HA_Event; typedef uint32_t (*SSLHAProducerFunc)(uint8_t *buf, void *data_to_copy, uint32_t size); typedef int (*SSLHAConsumerFunc)(const uint8_t *data, uint32_t length); int RegisterSSLHAFuncs(uint32_t preproc_id, uint8_t subcode, uint32_t size, SSLHAProducerFunc produce, SSLHAConsumerFunc consume, SSLHAConsumerFunc deletion); void UnregisterStreamHAFuncs(uint32_t preproc_id, uint8_t subcode); //void SSLSetHAPendingBit(void *ssnptr, int bit); void SSLHAInit(struct _SnortConfig *sc, char *args); int SSLVerifyHAConfig(struct _SnortConfig *sc, void *config); #if defined(SNORT_RELOAD) void SSLHAReload(struct _SnortConfig *sc, char *args, void **new_config); void *SSLHASwapReload( struct _SnortConfig *sc, void *data ); #endif void SSLHAConfigFree(void *config); void SSLHAPostConfigInit(struct _SnortConfig *sc, int unused, void *arg); void SSLCleanHA(void); void SSLPrintHAStats(void); int DisplaySSLHAStats(char *buffer); void SSLResetHAStats(void); void SSLProcessHA(int ssl_index, bool update, void *data, uint32_t ssl_size); #endif /* ENABLE_HA */ #endif /* __SSL_HA_H__ */ snort-2.9.20/src/dynamic-preprocessors/ssl_common/ssl_ha.c0000644000175000017500000007267014241076466022027 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2012-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * *****************************************************************************/ /************************************************************************** * * ssl_ha.c * * Authors: Michael Altizer , Russ Combs , Bhagyashree Bantwal * * Description: * * SSL high availability support. * **************************************************************************/ #ifdef ENABLE_HA #include "ssl_include.h" #include "ssl_ha.h" #include "ssl_config.h" #include #include typedef struct _SSLHAFuncsNode { uint16_t id; uint16_t mask; uint8_t preproc_id; uint8_t subcode; uint32_t size; SSLHAProducerFunc produce; SSLHAConsumerFunc consume; SSLHAConsumerFunc deletion; uint32_t produced; uint32_t consumed; uint32_t deleted; } SSLHAFuncsNode; typedef enum { HA_TYPE_PSD, // Preprocessor-specific Data HA_TYPE_MAX } SSL_HA_Type; typedef struct _MsgHeader { uint8_t event; uint8_t version; uint16_t total_length; } MsgHeader; typedef struct _RecordHeader { uint8_t type; uint32_t length; } RecordHeader; typedef struct _PreprocDataHeader { uint8_t preproc_id; uint8_t subcode; } PreprocDataHeader; typedef struct { uint32_t update_messages_received; uint32_t delete_messages_received; uint32_t update_messages_sent; uint32_t delete_messages_sent; } SSLHAStats; typedef struct _HADebugSessionConstraints { sfcidr_t sip; sfcidr_t dip; uint16_t sport; uint16_t dport; uint8_t protocol; } HADebugSessionConstraints; #define MAX_SSL_HA_FUNCS 8 // depends on sizeof(SSLLWSession.ha_pending_mask) #define HA_SSL_MESSAGE_VERSION 0x82 static SSLHAFuncsNode *ssl_ha_funcs[MAX_SSL_HA_FUNCS]; static int n_ssl_ha_funcs = 0; static int runtime_output_fd = -1; static uint8_t file_io_buffer[UINT16_MAX]; static SSLHAStats sslha_stats; /* Runtime debugging stuff. */ //#define HA_DEBUG_SESSION_ID_SIZE (39+1+5+5+39+1+5+1+3+1) /* ": <-> : \0" */ //static HADebugSessionConstraints ssl_ha_debug_info; //static volatile int ssl_ha_debug_flag = 0; //static char ssl_ha_debug_session[HA_DEBUG_SESSION_ID_SIZE]; #ifdef PERF_PROFILING PreprocStats sslHAPerfStats; PreprocStats sslHAConsumePerfStats; PreprocStats sslHAProducePerfStats; #endif #if 0 // NEED TO ADD THIS FOR SSL //-------------------------------------------------------------------- // Runtime debugging support. //-------------------------------------------------------------------- static inline bool SSLHADebugCheck(const SessionKey *key, volatile int debug_flag, HADebugSessionConstraints *info, char *debug_session, size_t debug_session_len) { } static void SSLHADebugParse(const char *desc, const uint8_t *data, uint32_t length, volatile int *debug_flag, HADebugSessionConstraints *info) { } static int SSLDebugHA(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char *statusBuf, int statusBuf_len) { SSLHADebugParse("S5HA", data, length, &ssl_ha_debug_flag, &ssl_ha_debug_info); return 0; } #endif int RegisterSSLHAFuncs(uint32_t preproc_id, uint8_t subcode, uint32_t size, SSLHAProducerFunc produce, SSLHAConsumerFunc consume, SSLHAConsumerFunc deletion) { SSLHAFuncsNode *node; int i, idx; if (produce == NULL || consume == NULL) { DynamicPreprocessorFatalMessage("One must be both a producer and a consumer to participate in SSL HA!\n"); } if (preproc_id > UINT8_MAX) { DynamicPreprocessorFatalMessage("Preprocessor ID must be between 0 and %d to participate in SSL HA!\n", UINT8_MAX); } idx = n_ssl_ha_funcs; for (i = 0; i < n_ssl_ha_funcs; i++) { node = ssl_ha_funcs[i]; if (node) { if (preproc_id == node->preproc_id && subcode == node->subcode) { DynamicPreprocessorFatalMessage("Duplicate SSL HA registration attempt for preprocessor %hu with subcode %hu\n", node->preproc_id, node->subcode); } } else if (idx == n_ssl_ha_funcs) idx = i; } if (idx == MAX_SSL_HA_FUNCS) { DynamicPreprocessorFatalMessage("Attempted to register more than %d SSL HA types!\n", MAX_SSL_HA_FUNCS); } if (idx == n_ssl_ha_funcs) n_ssl_ha_funcs++; node = (SSLHAFuncsNode *)calloc(1, sizeof(SSLHAFuncsNode)); if (!node) { DynamicPreprocessorFatalMessage("SSLHA: Failed to allocate memory for new node\n"); } node->id = idx; node->mask = (1 << idx); node->preproc_id = (uint8_t) preproc_id; node->subcode = subcode; node->size = size; node->produce = produce; node->consume = consume; node->deletion = deletion; ssl_ha_funcs[idx] = node; _dpd.logMsg("SSLHA: Registered node %hu for preprocessor ID %hhu with subcode %hhu (size %hhu)\n", node->id, node->preproc_id, node->subcode, node->size); return idx; } void UnregisterSSLHAFuncs(uint32_t preproc_id, uint8_t subcode) { SSLHAFuncsNode *node; int i; for (i = 0; i < n_ssl_ha_funcs; i++) { node = ssl_ha_funcs[i]; if (node && preproc_id == node->preproc_id && subcode == node->subcode) { ssl_ha_funcs[i] = NULL; free(node); break; } } if ((i + 1) == n_ssl_ha_funcs) n_ssl_ha_funcs--; } static void SSLParseHAArgs(struct _SnortConfig *sc, SSLHAConfig *config, char *args) { char **toks; int num_toks; int i; char **stoks = NULL; int s_toks; char *endPtr = NULL; unsigned long int value; if (config == NULL) return; if ((args == NULL) || (strlen(args) == 0)) return; toks = _dpd.tokenSplit(args, ",", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { stoks = _dpd.tokenSplit(toks[i], " ", 2, &s_toks, 0); if (s_toks == 0) { DynamicPreprocessorFatalMessage("%s(%d) => Missing parameter in SSL HA config.\n", __FILE__, __LINE__); } if (!strcmp(stoks[0], "min_sync_interval")) { if (stoks[1]) value = strtoul(stoks[1], &endPtr, 10); else value = 0; if (!stoks[1] || (endPtr == &stoks[1][0])) { DynamicPreprocessorFatalMessage("%s(%d) => Invalid '%s' in config file. Requires integer parameter.\n", __FILE__, __LINE__, stoks[0]); } if (value > UINT16_MAX) { DynamicPreprocessorFatalMessage("%s(%d) => '%s %lu' invalid: value must be between 0 and %d milliseconds.\n", __FILE__, __LINE__, stoks[0], value, UINT16_MAX); } config->min_sync_interval.tv_sec = 0; while (value >= 1000) { config->min_sync_interval.tv_sec++; value -= 1000; } config->min_sync_interval.tv_usec = value * 1000; } else if (!strcmp(stoks[0], "startup_input_file")) { if (!stoks[1]) { DynamicPreprocessorFatalMessage("%s(%d) => '%s' missing an argument\n", __FILE__, __LINE__, stoks[0]); } if (config->startup_input_file) { DynamicPreprocessorFatalMessage("%s(%d) => '%s' specified multiple times\n", __FILE__, __LINE__, stoks[0]); } config->startup_input_file = strdup(stoks[1]); if (!config->startup_input_file) { DynamicPreprocessorFatalMessage("%s(%d) => Memory allocation failure.\n", __FILE__, __LINE__); } } else if (!strcmp(stoks[0], "runtime_output_file")) { if (!stoks[1]) { DynamicPreprocessorFatalMessage("%s(%d) => '%s' missing an argument\n", __FILE__, __LINE__, stoks[0]); } if (config->runtime_output_file) { DynamicPreprocessorFatalMessage("%s(%d) => '%s' specified multiple times\n", __FILE__, __LINE__, stoks[0]); } config->runtime_output_file = strdup(stoks[1]); if (!config->runtime_output_file) { DynamicPreprocessorFatalMessage("%s(%d) => Memory allocation failure.\n", __FILE__, __LINE__); } } else if (!strcmp(stoks[0], "shutdown_output_file")) { if (!stoks[1]) { DynamicPreprocessorFatalMessage("%s(%d) => '%s' missing an argument\n", __FILE__, __LINE__, stoks[0]); } if (config->shutdown_output_file) { DynamicPreprocessorFatalMessage("%s(%d) => '%s' specified multiple times\n", __FILE__, __LINE__, stoks[0]); } config->shutdown_output_file = strdup(stoks[1]); if (!config->shutdown_output_file) { DynamicPreprocessorFatalMessage("%s(%d) => Memory allocation failure.\n", __FILE__, __LINE__); } } else if (!strcmp(stoks[0], "use_side_channel")) { #ifdef SIDE_CHANNEL if (!_dpd.isSCEnabled()) { DynamicPreprocessorFatalMessage("%s(%d) => '%s' cannot be specified without enabling the Snort side channel.\n", __FILE__, __LINE__, stoks[0]); } config->use_side_channel = 1; #else DynamicPreprocessorFatalMessage("%s(%d) => Snort has been compiled without Side Channel support.\n", __FILE__, __LINE__); #endif } else { DynamicPreprocessorFatalMessage("%s(%d) => Invalid SSL HA config option '%s'\n", __FILE__, __LINE__, stoks[0]); } _dpd.tokenFree(&stoks, s_toks); } _dpd.tokenFree(&toks, num_toks); } static void SSLPrintHAConfig(SSLHAConfig *config) { if (config == NULL) return; _dpd.logMsg("SSL HA config:\n"); _dpd.logMsg(" Minimum Sync Interval: %lu milliseconds\n", config->min_sync_interval.tv_sec * 1000 + config->min_sync_interval.tv_usec / 1000); if (config->startup_input_file) _dpd.logMsg(" Startup Input File: %s\n", config->startup_input_file); if (config->runtime_output_file) _dpd.logMsg(" Runtime Output File: %s\n", config->runtime_output_file); if (config->shutdown_output_file) _dpd.logMsg(" Shutdown Output File: %s\n", config->shutdown_output_file); } int DisplaySSLHAStats(char *buffer) { SSLHAFuncsNode *node; int i; int len = 0; len += snprintf(buffer, CS_STATS_BUF_SIZE, " High Availability\n" " Updates Received: %u\n" " Deletions Received: %u\n" " Updates Sent: %u\n" " Deletions Sent: %u\n" , sslha_stats.update_messages_received , sslha_stats.delete_messages_received , sslha_stats.update_messages_sent , sslha_stats.delete_messages_sent); for (i = 0; i < n_ssl_ha_funcs && len < CS_STATS_BUF_SIZE; i++) { node = ssl_ha_funcs[i]; if (!node) continue; len += snprintf(buffer+len, CS_STATS_BUF_SIZE-len, " Node %hhu/%hhu: %u produced, %u consumed, %u deleted\n", node->preproc_id, node->subcode, node->produced, node->consumed, node->deleted); } return len; } void SSLPrintHAStats(void) { SSLHAFuncsNode *node; int i; _dpd.logMsg(" High Availability\n"); _dpd.logMsg(" Updates Received: %u\n", sslha_stats.update_messages_received); _dpd.logMsg(" Deletions Received: %u\n", sslha_stats.delete_messages_received); _dpd.logMsg(" Updates Sent: %u\n", sslha_stats.update_messages_sent); _dpd.logMsg(" Deletions Sent: %u\n", sslha_stats.delete_messages_sent); for (i = 0; i < n_ssl_ha_funcs; i++) { node = ssl_ha_funcs[i]; if (!node) continue; _dpd.logMsg(" Node %hhu/%hhu: %u produced, %u consumed, %u deleted\n", node->preproc_id, node->subcode, node->produced, node->consumed, node->deletion); } } void SSLResetHAStats(void) { memset(&sslha_stats, 0, sizeof(sslha_stats)); } void SSLHAInit(struct _SnortConfig *sc, char *args) { SSLPP_config_t *pDefaultPolicyConfig = NULL; if (ssl_config == NULL) DynamicPreprocessorFatalMessage("Tried to config SSL HA policy without global config!\n"); if( _dpd.getParserPolicy(sc) != _dpd.getDefaultPolicy() ) return; pDefaultPolicyConfig = (SSLPP_config_t *)sfPolicyUserDataGetDefault(ssl_config); if (pDefaultPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Tried to config SSL HA policy without SSL config!\n"); } if (pDefaultPolicyConfig->ssl_ha_config != NULL) { DynamicPreprocessorFatalMessage("%s(%d) ==> Cannot duplicate SSL HA configuration\n", __FILE__, __LINE__); } if (!pDefaultPolicyConfig->enable_ssl_ha) { return; } pDefaultPolicyConfig->ssl_ha_config = (SSLHAConfig*)calloc(1, sizeof( SSLHAConfig )); if (!pDefaultPolicyConfig->ssl_ha_config) { DynamicPreprocessorFatalMessage("Failed to allocate storage for Session HA configuration.\n"); } SSLParseHAArgs(sc, pDefaultPolicyConfig->ssl_ha_config, args); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("sslHAProduce", &sslHAProducePerfStats, 2, &sslHAPerfStats, NULL); _dpd.addPreprocProfileFunc("sslHAConsume", &sslHAConsumePerfStats, 0, _dpd.totalPerfStats, NULL); #endif SSLPrintHAConfig(pDefaultPolicyConfig->ssl_ha_config); } #if defined(SNORT_RELOAD) void SSLHAReload(struct _SnortConfig *sc, char *args, void **new_config) { SSLHAConfig *ssl_ha_config = ( SSLHAConfig *) *new_config; SSLPP_config_t *config; if( _dpd.getParserPolicy(sc) != _dpd.getDefaultPolicy( ) ) return; config = (SSLPP_config_t *)sfPolicyUserDataGetDefault(ssl_config); if( !config || !config->enable_ssl_ha ) return; if (ssl_ha_config == NULL) { ssl_ha_config = (SSLHAConfig *)calloc(1, sizeof(SSLHAConfig)); if ( ssl_ha_config == NULL ) DynamicPreprocessorFatalMessage("Failed to allocate new storage for Session HA configuration.\n"); *new_config = ssl_ha_config; } else { DynamicPreprocessorFatalMessage("%s(%d) ==> Cannot duplicate SSL HA configuration\n", __FILE__, __LINE__); } SSLParseHAArgs(sc, ssl_ha_config, args); SSLPrintHAConfig(ssl_ha_config); } #endif int SSLVerifyHAConfig(struct _SnortConfig *sc, void *config) { if (config == NULL) return -1; return 0; } void *SSLHASwapReload( struct _SnortConfig *sc, void *data ) { SSLPP_config_t *config; config = (SSLPP_config_t *)sfPolicyUserDataGet(ssl_config, _dpd.getDefaultPolicy()); if(config) config->ssl_ha_config = ( SSLHAConfig * ) data; return NULL; } void SSLHAConfigFree(void *data) { SSLHAConfig *config = (SSLHAConfig *)data; if (config == NULL) return; if (config->startup_input_file) free(config->startup_input_file); if (config->runtime_output_file) free(config->runtime_output_file); if (config->shutdown_output_file) free(config->shutdown_output_file); free(config); } static inline int DeserializeSSLPreprocData(uint8_t event, uint8_t preproc_id, uint8_t subcode, const uint8_t *data, uint32_t length) { SSLHAFuncsNode *node; int i; for (i = 0; i < n_ssl_ha_funcs; i++) { node = ssl_ha_funcs[i]; if (node && preproc_id == node->preproc_id && subcode == node->subcode) { if (length < node->size) { _dpd.errMsg("SSL HA preprocessor data record's length is less than expected size! (%u vs %u)\n", length, node->size); return -1; } if ( event == HA_EVENT_UPDATE ) { node->consumed++; sslha_stats.update_messages_received++; return node->consume(data, length); } else { node->deleted++; sslha_stats.delete_messages_received++; return node->deletion(data, length); } } } _dpd.errMsg("SSL HA preprocessor data record received with unrecognized preprocessor ID/subcode! (%hhu:%hhu)\n", preproc_id, subcode); return -1; } static int ConsumeSSLHAMessage(const uint8_t *msg, uint32_t msglen) { MsgHeader *msg_hdr; RecordHeader *rec_hdr; PreprocDataHeader *psd_hdr; uint32_t offset; int rval = 1; PROFILE_VARS; PREPROC_PROFILE_START(sslHAConsumePerfStats); /* Read the message header */ if (msglen < sizeof(*msg_hdr)) { _dpd.errMsg("SSL HA message length shorter than header length! (%u)\n", msglen); goto ssl_consume_exit; } msg_hdr = (MsgHeader *) msg; offset = sizeof(*msg_hdr); if (msg_hdr->total_length != msglen) { _dpd.errMsg("SSL HA message header's total length does not match actual length! (%u vs %u)\n", msg_hdr->total_length, msglen); goto ssl_consume_exit; } if (msg_hdr->event != HA_EVENT_UPDATE && msg_hdr->event != HA_EVENT_DELETE) { _dpd.errMsg("SSL HA message has unknown event type: %hhu!\n", msg_hdr->event); goto ssl_consume_exit; } /* Read the key */ /* Read any/all records. */ while (offset < msglen) { if (sizeof(*rec_hdr) > (msglen - offset)) { _dpd.errMsg("SSL HA message contains a truncated record header! (%zu vs %u)\n", sizeof(*rec_hdr), msglen - offset); goto ssl_consume_exit; } rec_hdr = (RecordHeader *) (msg + offset); offset += sizeof(*rec_hdr); switch (rec_hdr->type) { case HA_TYPE_PSD: if (sizeof(*psd_hdr) > (msglen - offset)) { _dpd.errMsg("SSL HA message contains a truncated preprocessor data record header! (%zu vs %u)\n", sizeof(*psd_hdr), msglen - offset); goto ssl_consume_exit; } psd_hdr = (PreprocDataHeader *) (msg + offset); offset += sizeof(*psd_hdr); if (rec_hdr->length > (msglen - offset)) { _dpd.errMsg("SSL HA message contains truncated preprocessor data record data! (%u vs %u)\n", rec_hdr->length, msglen - offset); goto ssl_consume_exit; } if (DeserializeSSLPreprocData(msg_hdr->event, psd_hdr->preproc_id, psd_hdr->subcode, msg + offset, rec_hdr->length) != 0) { _dpd.errMsg("SSL HA message contained invalid preprocessor data record!\n"); goto ssl_consume_exit; } offset += rec_hdr->length; break; default: _dpd.errMsg("SSL HA message contains unrecognized record type: %hhu!\n", rec_hdr->type); goto ssl_consume_exit; } } /* Mark the session as being in standby mode since we just received an update. */ rval = 0; ssl_consume_exit: PREPROC_PROFILE_END(sslHAConsumePerfStats); return rval; } /* * File I/O */ static inline ssize_t Read(int fd, void *buf, size_t count) { ssize_t n; errno = 0; while ((n = read(fd, buf, count)) <= (ssize_t) count) { if (n == (ssize_t) count) return 0; if (n > 0) { buf = (uint8_t *) buf + n; count -= n; } else if (n == 0) break; else if (errno != EINTR) { _dpd.errMsg("Error reading from SSL HA message file: %s (%d)\n", strerror(errno), errno); break; } } return -1; } static int ReadHAMessagesFromFile(const char *filename) { MsgHeader *msg_header; uint8_t *msg; int rval, fd; fd = open(filename, O_RDONLY, 0664); if (fd < 0) { DynamicPreprocessorFatalMessage("Could not open %s for reading HA messages from: %s (%d)\n", filename, strerror(errno), errno); } _dpd.logMsg("Reading SSL HA messages from '%s'...\n", filename); msg = file_io_buffer; while ((rval = Read(fd, msg, sizeof(*msg_header))) == 0) { msg_header = (MsgHeader *) msg; if (msg_header->total_length < sizeof(*msg_header)) { _dpd.errMsg("SSL HA Message total length (%hu) is way too short!\n", msg_header->total_length); close(fd); return -1; } else if (msg_header->total_length > (UINT16_MAX - sizeof(*msg_header))) { _dpd.errMsg("SSL HA Message total length (%hu) is too long!\n", msg_header->total_length); close(fd); return -1; } else if (msg_header->total_length > sizeof(*msg_header)) { if ((rval = Read(fd, msg + sizeof(*msg_header), msg_header->total_length - sizeof(*msg_header))) != 0) { _dpd.errMsg("Error reading the remaining %zu bytes of an HA message from file: %s (%d)\n", msg_header->total_length - sizeof(*msg_header), strerror(errno), errno); close(fd); return rval; } } if( msg_header->version == HA_SSL_MESSAGE_VERSION ) { if ((rval = ConsumeSSLHAMessage(msg, msg_header->total_length)) != 0) { close(fd); return rval; } } } close(fd); return 0; } static inline ssize_t Write(int fd, const void *buf, size_t count) { ssize_t n; errno = 0; while ((n = write(fd, buf, count)) <= (ssize_t) count) { if (n == (ssize_t) count) return 0; if (n > 0) count -= n; else if (errno != EINTR) { _dpd.errMsg("Error writing to SSL HA message file: %s (%d)\n", strerror(errno), errno); break; } } return -1; } static uint32_t WriteSSLHAMessageHeader(uint8_t event, uint16_t msglen, uint8_t *msg) { MsgHeader *msg_hdr; uint32_t offset; msg_hdr = (MsgHeader *) msg; offset = sizeof(*msg_hdr); msg_hdr->event = event; msg_hdr->version = HA_SSL_MESSAGE_VERSION; msg_hdr->total_length = msglen; return offset; } static uint32_t WriteSSLPreprocDataRecord(SSLHAFuncsNode *node, uint8_t *msg, void *data, int size) { RecordHeader *rec_hdr; PreprocDataHeader *psd_hdr; uint32_t offset; rec_hdr = (RecordHeader *) msg; offset = sizeof(*rec_hdr); rec_hdr->type = HA_TYPE_PSD; psd_hdr = (PreprocDataHeader *) (msg + offset); offset += sizeof(*psd_hdr); psd_hdr->preproc_id = node->preproc_id; psd_hdr->subcode = node->subcode; rec_hdr->length = node->produce(msg + offset, data, size); offset += rec_hdr->length; node->produced++; return offset; } static void UpdateSSLHAMessageHeaderLength(uint8_t *msg, uint16_t msglen) { MsgHeader *msg_hdr; msg_hdr = (MsgHeader *) msg; msg_hdr->total_length = msglen; } static uint32_t CalculateSSLHAMessageSize(uint8_t event, int index, uint32_t ssl_size) { SSLHAFuncsNode *node; uint32_t msg_size; msg_size = sizeof(MsgHeader); if (event == HA_EVENT_UPDATE) { /* Preprocessor data records */ if( index < n_ssl_ha_funcs ) { node = ssl_ha_funcs[index]; if(!node) return 0; msg_size += sizeof(RecordHeader) + sizeof(PreprocDataHeader) + ssl_size; } } return msg_size; } static uint32_t GenerateSSLHAUpdateMessage(uint8_t *msg, uint32_t msg_size, int index, uint8_t event, void *data, uint32_t size) { SSLHAFuncsNode *node; uint32_t offset; PROFILE_VARS; PREPROC_PROFILE_START(sslHAProducePerfStats); offset = WriteSSLHAMessageHeader(event, msg_size, msg); if( index < n_ssl_ha_funcs ) { node = ssl_ha_funcs[index]; if(!node) return 0; offset += WriteSSLPreprocDataRecord(node, msg + offset, data, size); } /* Update the message header length since it might be shorter than originally anticipated. */ UpdateSSLHAMessageHeaderLength(msg, offset); if( event == HA_EVENT_UPDATE ) { sslha_stats.update_messages_sent++; } else { sslha_stats.delete_messages_sent++; } PREPROC_PROFILE_END(sslHAProducePerfStats); return offset; } #ifdef SIDE_CHANNEL static void SendSSLSCUpdateMessage(int index, uint32_t msg_size, uint8_t event, void *data, uint32_t size) { SCMsgHdr *schdr; void *msg_handle; uint8_t *msg; int rval; /* Allocate space for the message. */ if ((rval = _dpd.scAllocMessageTX(msg_size, &schdr, &msg, &msg_handle)) != 0) { /* TODO: Error stuff goes here. */ return; } /* Gnerate the message. */ msg_size = GenerateSSLHAUpdateMessage(msg, msg_size, index, event, data, size); /* Send the message. */ schdr->type = SC_MSG_TYPE_SSL_STATE_TRACKING; schdr->timestamp = _dpd.pktTime(); _dpd.scEnqueueMessageTX(schdr, msg, msg_size, msg_handle, NULL); } #endif void SSLProcessHA(int ssl_index, bool update, void *data, uint32_t ssl_size) { uint32_t msg_size; SSLPP_config_t *config = NULL; SSLPP_config_t *defaultconfig = NULL; uint8_t event = (update ? HA_EVENT_UPDATE : HA_EVENT_DELETE); PROFILE_VARS; PREPROC_PROFILE_START(sslHAPerfStats); config = (SSLPP_config_t *)sfPolicyUserDataGetCurrent(ssl_config); defaultconfig = (SSLPP_config_t *)sfPolicyUserDataGet(ssl_config, _dpd.getDefaultPolicy()); if (!config || !defaultconfig || !defaultconfig->ssl_ha_config || !config->enable_ssl_ha) { PREPROC_PROFILE_END(sslHAPerfStats); return; } /* Calculate the size of the update message. */ msg_size = CalculateSSLHAMessageSize(event, ssl_index, ssl_size); if (runtime_output_fd >= 0) { msg_size = GenerateSSLHAUpdateMessage(file_io_buffer, msg_size, ssl_index, event, data, ssl_size); if (Write(runtime_output_fd, file_io_buffer, msg_size) == -1) { /* TODO: Error stuff here. */ } } #ifdef SIDE_CHANNEL if (defaultconfig->ssl_ha_config->use_side_channel) { SendSSLSCUpdateMessage(ssl_index, msg_size, event, data, ssl_size); } #endif } #ifdef SIDE_CHANNEL static int SSLHASCMsgHandler(SCMsgHdr *hdr, const uint8_t *msg, uint32_t msglen) { int rval; PROFILE_VARS; PREPROC_PROFILE_START(sslHAPerfStats); rval = ConsumeSSLHAMessage(msg, msglen); PREPROC_PROFILE_END(sslHAPerfStats); return rval; } #endif void SSLHAPostConfigInit(struct _SnortConfig *sc, int unused, void *arg) { SSLPP_config_t *pDefaultPolicyConfig; int rval; pDefaultPolicyConfig = (SSLPP_config_t *)sfPolicyUserDataGet(ssl_config, _dpd.getDefaultPolicy()); if (!pDefaultPolicyConfig->enable_ssl_ha || !(pDefaultPolicyConfig->ssl_ha_config)) return; if (pDefaultPolicyConfig->ssl_ha_config->startup_input_file) { if ((rval = ReadHAMessagesFromFile(pDefaultPolicyConfig->ssl_ha_config->startup_input_file)) != 0) { _dpd.errMsg("Errors were encountered while reading HA messages from file!"); } } if (pDefaultPolicyConfig->ssl_ha_config->runtime_output_file) { runtime_output_fd = open(pDefaultPolicyConfig->ssl_ha_config->runtime_output_file, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (runtime_output_fd < 0) { DynamicPreprocessorFatalMessage("Could not open %s for writing HA messages to: %s (%d)\n", pDefaultPolicyConfig->ssl_ha_config->runtime_output_file, strerror(errno), errno); } } #ifdef SIDE_CHANNEL if (pDefaultPolicyConfig->ssl_ha_config->use_side_channel) { if ((rval = _dpd.scRegisterRXHandler(SC_MSG_TYPE_SSL_STATE_TRACKING, SSLHASCMsgHandler, NULL)) != 0) { /* TODO: Fatal error here or something. */ } } #endif } void SSLCleanHA(void) { int i; for (i = 0; i < n_ssl_ha_funcs; i++) { if (ssl_ha_funcs[i]) { free(ssl_ha_funcs[i]); ssl_ha_funcs[i] = NULL; } } if (runtime_output_fd >= 0) { close(runtime_output_fd); runtime_output_fd = -1; } } #endif snort-2.9.20/src/dynamic-preprocessors/gtp/0000755000175000017500000000000014242725716017017 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/gtp/gtp_config.c0000644000175000017500000007320414241076015021276 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 7/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "sf_types.h" #include "sf_snort_packet.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "gtp_config.h" #include "spp_gtp.h" #include "gtp_debug.h" #define METHOD_NOT_FOUND (-1) /* * Default GTP port */ #define GTP_C_PORT (2123) #define GTP_C_PORT_V0 (3386) /* * Keyword strings for parsing configuration options. */ #define GTP_PORTS_KEYWORD "ports" #define GTP_CONFIG_SECTION_SEPERATORS ",;" #define GTP_CONFIG_VALUE_SEPERATORS " " /* * Message type defined */ static GTP_MsgType GTPv0_MsgTypes[] = { {1, 1, "echo_request"}, {2, 1, "echo_response"}, {3, 1, "version_not_supported"}, {4, 1, "node_alive_request"}, {5, 1, "node_alive_response"}, {6, 1, "redirection_request"}, {7, 1, "redirection_response"}, {16, 1,"create_pdp_context_request"}, {17, 1,"create_pdp_context_response"}, {18, 1,"update_pdp_context_request"}, {19, 1,"update_pdp_context_response"}, {20, 1,"delete_pdp_context_request"}, {21, 1,"delete_pdp_context_response"}, {22, 1,"create_aa_pdp_context_request"}, {23, 1,"create_aa_pdp_context_response"}, {24, 1,"delete_aa_pdp_context_request"}, {25, 1,"delete_aa_pdp_context_response"}, {26, 1,"error_indication"}, {27, 1,"pdu_notification_request"}, {28, 1,"pdu_notification_response"}, {29, 1,"pdu_notification_reject_request"}, {30, 1,"pdu_notification_reject_response"}, {32, 1,"send_routing_info_request"}, {33, 1,"send_routing_info_response"}, {34, 1,"failure_report_request"}, {35, 1,"failure_report_response"}, {36, 1,"note_ms_present_request"}, {37, 1,"note_ms_present_response"}, {48, 1,"identification_request"}, {49, 1,"identification_response"}, {50, 1,"sgsn_context_request"}, {51, 1,"sgsn_context_response"}, {52, 1,"sgsn_context_ack"}, {240, 1,"data_record_transfer_request"}, {241, 1,"data_record_transfer_response"}, {255, 1,"pdu"}, {0, 0, NULL} }; static GTP_MsgType GTPv1_MsgTypes[] = { {1, 1, "echo_request"}, {2, 1, "echo_response"}, {3, 1, "version_not_supported"}, {4, 1, "node_alive_request"}, {5, 1, "node_alive_response"}, {6, 1, "redirection_request"}, {7, 1, "redirection_response"}, {16, 1,"create_pdp_context_request"}, {17, 1,"create_pdp_context_response"}, {18, 1,"update_pdp_context_request"}, {19, 1,"update_pdp_context_response"}, {20, 1,"delete_pdp_context_request"}, {21, 1,"delete_pdp_context_response"}, {22, 1,"init_pdp_context_activation_request"}, {23, 1,"init_pdp_context_activation_response"}, {26, 1,"error_indication"}, {27, 1,"pdu_notification_request"}, {28, 1,"pdu_notification_response"}, {29, 1,"pdu_notification_reject_request"}, {30, 1,"pdu_notification_reject_response"}, {31, 1,"supported_ext_header_notification"}, {32, 1,"send_routing_info_request"}, {33, 1,"send_routing_info_response"}, {34, 1,"failure_report_request"}, {35, 1,"failure_report_response"}, {36, 1,"note_ms_present_request"}, {37, 1,"note_ms_present_response"}, {48, 1,"identification_request"}, {49, 1,"identification_response"}, {50, 1,"sgsn_context_request"}, {51, 1,"sgsn_context_response"}, {52, 1,"sgsn_context_ack"}, {53, 1,"forward_relocation_request"}, {54, 1,"forward_relocation_response"}, {55, 1,"forward_relocation_complete"}, {56, 1,"relocation_cancel_request"}, {57, 1,"relocation_cancel_response"}, {58, 1,"forward_srns_contex"}, {59, 1,"forward_relocation_complete_ack"}, {60, 1,"forward_srns_contex_ack"}, {70, 1,"ran_info_relay"}, {96, 1,"mbms_notification_request"}, {97, 1,"mbms_notification_response"}, {98, 1,"mbms_notification_reject_request"}, {99, 1,"mbms_notification_reject_response"}, {100,1,"create_mbms_context_request"}, {101,1,"create_mbms_context_response"}, {102,1,"update_mbms_context_request"}, {103,1,"update_mbms_context_response"}, {104,1,"delete_mbms_context_request"}, {105,1,"delete_mbms_context_response"}, {112,1,"mbms_register_request"}, {113,1,"mbms_register_response"}, {114,1,"mbms_deregister_request"}, {115,1,"mbms_deregister_response"}, {116,1,"mbms_session_start_request"}, {117,1,"mbms_session_start_response"}, {118,1,"mbms_session_stop_request"}, {119,1,"mbms_session_stop_response"}, {120,1,"mbms_session_update_request"}, {121,1,"mbms_session_update_response"}, {128, 1,"ms_info_change_request"}, {129, 1,"ms_info_change_response"}, {240, 1,"data_record_transfer_request"}, {241, 1,"data_record_transfer_response"}, {254, 1,"end_marker"}, {255, 1,"pdu"}, {0, 0, NULL} }; static GTP_MsgType GTPv2_MsgTypes[] = { {1, 1, "echo_request"}, {2, 1, "echo_response"}, {3, 1, "version_not_supported"}, {32, 1,"create_session_request"}, {33, 1,"create_session_response"}, {34, 1,"modify_bearer_request"}, {35, 1,"modify_bearer_response"}, {36, 1,"delete_session_request"}, {37, 1,"delete_session_response"}, {38, 1,"change_notification_request"}, {39, 1,"change_notification_response"}, {64, 1,"modify_bearer_command"}, {65, 1,"modify_bearer_failure_indication"}, {66, 1,"delete_bearer_command"}, {67, 1,"delete_bearer_failure_indication"}, {68, 1,"bearer_resource_command"}, {69, 1,"bearer_resource_failure_indication"}, {70, 1,"downlink_failure_indication"}, {71, 1,"trace_session_activation"}, {72, 1,"trace_session_deactivation"}, {73, 1,"stop_paging_indication"}, {95, 1,"create_bearer_request"}, {96, 1,"create_bearer_response"}, {97, 1,"update_bearer_request"}, {98, 1,"update_bearer_response"}, {99, 1,"delete_bearer_request"}, {100,1,"delete_bearer_response"}, {101,1,"delete_pdn_request"}, {102,1,"delete_pdn_response"}, {128, 1,"identification_request"}, {129, 1,"identification_response"}, {130, 1,"sgsn_context_request"}, {131, 1,"sgsn_context_response"}, {132, 1,"sgsn_context_ack"}, {133, 1,"forward_relocation_request"}, {134, 1,"forward_relocation_response"}, {135, 1,"forward_relocation_complete"}, {136, 1,"forward_relocation_complete_ack"}, {137, 1,"forward_access"}, {138, 1,"forward_access_ack"}, {139, 1,"relocation_cancel_request"}, {140, 1,"relocation_cancel_response"}, {141, 1,"configuration_transfer_tunnel"}, {149, 1,"detach"}, {150, 1,"detach_ack"}, {151, 1,"cs_paging"}, {152, 1,"ran_info_relay"}, {153, 1,"alert_mme"}, {154, 1,"alert_mme_ack"}, {155, 1,"ue_activity"}, {156, 1,"ue_activity_ack"}, {160,1,"create_forward_tunnel_request"}, {161,1,"create_forward_tunnel_response"}, {162, 1,"suspend"}, {163, 1,"suspend_ack"}, {164, 1,"resume"}, {165, 1,"resume_ack"}, {166,1,"create_indirect_forward_tunnel_request"}, {167,1,"create_indirect_forward_tunnel_response"}, {168,1,"delete_indirect_forward_tunnel_request"}, {169,1,"delete_indirect_forward_tunnel_response"}, {170,1,"release_access_bearer_request"}, {171,1,"release_access_bearer_response"}, {176,1,"downlink_data"}, {177,1,"downlink_data_ack"}, {179,1,"pgw_restart"}, {180,1,"pgw_restart_ack"}, {200,1,"update_pdn_request"}, {201,1,"update_pdn_response"}, {211,1,"modify_access_bearer_request"}, {212,1,"modify_access_bearer_response"}, {231,1,"mbms_session_start_request"}, {232,1,"mbms_session_start_response"}, {233,1,"mbms_session_update_request"}, {234,1,"mbms_session_update_response"}, {235,1,"mbms_session_stop_request"}, {236,1,"mbms_session_stop_response"}, {0, 0, NULL} }; /* * Information elements defined */ static GTP_InfoElement GTPv0_InfoElements[] = { {1, 1, "cause", 2}, {2, 1, "imsi", 9}, {3, 1, "rai", 7}, {4, 1, "tlli", 5}, {5, 1, "p_tmsi", 5}, {6, 1, "qos", 4}, {8, 1, "recording_required", 2}, {9, 1, "authentication", 29}, {11, 1, "map_cause", 2}, {12, 1, "p_tmsi_sig", 4}, {13, 1, "ms_validated", 2}, {14, 1, "recovery", 2}, {15, 1, "selection_mode", 2}, {16, 1, "flow_label_data_1", 3}, {17, 1, "flow_label_signalling", 3}, {18, 1, "flow_label_data_2", 4}, {19, 1, "ms_unreachable", 2}, {127, 1, "charge_id", 5}, {128, 1, "end_user_address", 0}, {129, 1, "mm_context", 0}, {130, 1, "pdp_context", 0}, {131, 1, "apn", 0}, {132, 1, "protocol_config", 0}, {133, 1, "gsn", 0}, {134, 1, "msisdn", 0}, {251, 1, "charging_gateway_addr", 0}, {255, 1, "private_extension", 0}, {0, 0, NULL, 0}, }; static GTP_InfoElement GTPv1_InfoElements[] = { {1, 1, "cause", 2}, {2, 1, "imsi", 9}, {3, 1, "rai", 7}, {4, 1, "tlli", 5}, {5, 1, "p_tmsi", 5}, {8, 1, "recording_required", 2}, {9, 1, "authentication", 29}, {11, 1, "map_cause", 2}, {12, 1, "p_tmsi_sig", 4}, {13, 1, "ms_validated", 2}, {14, 1, "recovery", 2}, {15, 1, "selection_mode", 2}, {16, 1, "teid_1", 5}, {17, 1, "teid_control", 5}, {18, 1, "teid_2", 6}, {19, 1, "teardown_ind", 2}, {20, 1, "nsapi", 2}, {21, 1, "ranap", 2}, {22, 1, "rab_context", 10}, {23, 1, "radio_priority_sms", 2}, {24, 1, "radio_priority", 2}, {25, 1, "packet_flow_id", 3}, {26, 1, "charging_char", 3}, {27, 1, "trace_ref", 3}, {28, 1, "trace_type", 3}, {29, 1, "ms_unreachable", 2}, {127, 1, "charge_id", 5}, {128, 1, "end_user_address", 0}, {129, 1, "mm_context", 0}, {130, 1, "pdp_context", 0}, {131, 1, "apn", 0}, {132, 1, "protocol_config", 0}, {133, 1, "gsn", 0}, {134, 1, "msisdn", 0}, {135, 1, "qos", 0}, {136, 1, "authentication_qu", 0}, {137, 1, "tft", 0}, {138, 1, "target_id", 0}, {139, 1, "utran_trans", 0}, {140, 1, "rab_setup", 0}, {141, 1, "ext_header", 0}, {142, 1, "trigger_id", 0}, {143, 1, "omc_id", 0}, {144, 1, "ran_trans", 0}, {145, 1, "pdp_context_pri", 0}, {146, 1, "addi_rab_setup", 0}, {147, 1, "sgsn_number", 0}, {148, 1, "common_flag", 0}, {149, 1, "apn_restriction", 0}, {150, 1, "radio_priority_lcs", 4}, {151, 1, "rat_type", 0}, {152, 1, "user_loc_info", 0}, {153, 1, "ms_time_zone", 0}, {154, 1, "imei_sv", 0}, {155, 1, "camel", 0}, {156, 1, "mbms_ue_context", 0}, {157, 1, "tmp_mobile_group_id", 0}, {158, 1, "rim_routing_addr", 0}, {159, 1, "mbms_config", 0}, {160, 1, "mbms_service_area", 0}, {161, 1, "src_rnc_pdcp", 0}, {162, 1, "addi_trace_info", 0}, {163, 1, "hop_counter", 0}, {164, 1, "plmn_id", 0}, {165, 1, "mbms_session_id", 0}, {166, 1, "mbms_2g3g_indicator", 0}, {167, 1, "enhanced_nsapi", 0}, {168, 1, "mbms_session_duration", 0}, {169, 1, "addi_mbms_trace_info", 0}, {170, 1, "mbms_session_repetition_num", 0}, {171, 1, "mbms_time_to_data", 0}, {173, 1, "bss", 0}, {174, 1, "cell_id", 0}, {175, 1, "pdu_num", 0}, {177, 1, "mbms_bearer_capab", 0}, {178, 1, "rim_routing_disc", 0}, {179, 1, "list_pfc", 0}, {180, 1, "ps_xid", 0}, {181, 1, "ms_info_change_report", 4}, {182, 1, "direct_tunnel_flags", 0}, {183, 1, "correlation_id", 0}, {184, 1, "bearer_control_mode", 0}, {185, 1, "mbms_flow_id", 0}, {186, 1, "mbms_ip_multicast", 0}, {187, 1, "mbms_distribution_ack", 4}, {188, 1, "reliable_inter_rat_handover", 0}, {189, 1, "rfsp_index", 0}, {190, 1, "fqdn", 0}, {191, 1, "evolved_allocation1", 0}, {192, 1, "evolved_allocation2", 0}, {193, 1, "extended_flags", 0}, {194, 1, "uci", 0}, {195, 1, "csg_info", 0}, {196, 1, "csg_id", 0}, {197, 1, "cmi", 4}, {198, 1, "apn_ambr", 0}, {199, 1, "ue_network", 0}, {200, 1, "ue_ambr", 0}, {201, 1, "apn_ambr_nsapi", 0}, {202, 1, "ggsn_backoff_timer", 0}, {203, 1, "signalling_priority_indication", 0}, {204, 1, "signalling_priority_indication_nsapi", 0}, {205, 1, "high_bitrate", 4}, {206, 1, "max_mbr", 0}, {251, 1, "charging_gateway_addr", 0}, {255, 1, "private_extension", 0}, {0, 0, NULL, 0}, }; static GTP_InfoElement GTPv2_InfoElements[] = { {1, 1, "imsi", 0}, {2, 1, "cause", 0}, {3, 1, "recovery", 0}, {71, 1, "apn", 0}, {72, 1, "ambr", 0}, {73, 1, "ebi", 0}, {74, 1, "ip_addr", 0}, {75, 1, "mei", 0}, {76, 1, "msisdn", 0}, {77, 1, "indication", 0}, {78, 1, "pco", 0}, {79, 1, "paa", 0}, {80, 1, "bearer_qos", 0}, {81, 1, "flow_qos", 0}, {82, 1, "rat_type", 0}, {83, 1, "serving_network", 0}, {84, 1, "bearer_tft", 0}, {85, 1, "tad", 0}, {86, 1, "uli", 0}, {87, 1, "f_teid", 0}, {88, 1, "tmsi", 0}, {89, 1, "cn_id", 0}, {90, 1, "s103pdf", 0}, {91, 1, "s1udf", 0}, {92, 1, "delay_value", 0}, {93, 1, "bearer_context", 0}, {94, 1, "charging_id", 0}, {95, 1, "charging_char", 0}, {96, 1, "trace_info", 0}, {97, 1, "bearer_flag", 0}, {99, 1, "pdn_type", 0}, {100, 1, "pti", 0}, {101, 1, "drx_parameter", 0}, {103, 1, "gsm_key_tri", 0}, {104, 1, "umts_key_cipher_quin", 0}, {105, 1, "gsm_key_cipher_quin", 0}, {106, 1, "umts_key_quin", 0}, {107, 1, "eps_quad", 0}, {108, 1, "umts_key_quad_quin", 0}, {109, 1, "pdn_connection", 0}, {110, 1, "pdn_number", 0}, {111, 1, "p_tmsi", 0}, {112, 1, "p_tmsi_sig", 0}, {113, 1, "hop_counter", 0}, {114, 1, "ue_time_zone", 0}, {115, 1, "trace_ref", 0}, {116, 1, "complete_request_msg", 0}, {117, 1, "guti", 0}, {118, 1, "f_container", 0}, {119, 1, "f_cause", 0}, {120, 1, "plmn_id", 0}, {121, 1, "target_id", 0}, {123, 1, "packet_flow_id", 0}, {124, 1, "rab_contex", 0}, {125, 1, "src_rnc_pdcp", 0}, {126, 1, "udp_src_port", 0}, {127, 1, "apn_restriction", 0}, {128, 1, "selection_mode", 0}, {129, 1, "src_id", 0}, {131, 1, "change_report_action", 0}, {132, 1, "fq_csid", 0}, {133, 1, "channel", 0}, {134, 1, "emlpp_pri", 0}, {135, 1, "node_type", 0}, {136, 1, "fqdn", 0}, {137, 1, "ti", 0}, {138, 1, "mbms_session_duration", 0}, {139, 1, "mbms_service_area", 0}, {140, 1, "mbms_session_id", 0}, {141, 1, "mbms_flow_id", 0}, {142, 1, "mbms_ip_multicast", 0}, {143, 1, "mbms_distribution_ack", 0}, {144, 1, "rfsp_index", 0}, {145, 1, "uci", 0}, {146, 1, "csg_info", 0}, {147, 1, "csg_id", 0}, {148, 1, "cmi", 0}, {149, 1, "service_indicator", 0}, {150, 1, "detach_type", 0}, {151, 1, "ldn", 0}, {152, 1, "node_feature", 0}, {153, 1, "mbms_time_to_transfer", 0}, {154, 1, "throttling", 0}, {155, 1, "arp", 0}, {156, 1, "epc_timer", 0}, {157, 1, "signalling_priority_indication", 0}, {158, 1, "tmgi", 0}, {159, 1, "mm_srvcc", 0}, {160, 1, "flags_srvcc", 0}, {161, 1, "mmbr", 0}, {255, 1, "private_extension", 0}, {0, 0, NULL, 0}, }; /* * Function prototype(s) */ static void InitGTPInfoElementTable(GTPConfig *); static void DisplayGTPConfig(GTPConfig *); static void GTP_ParsePortList(char **, uint8_t *); /* Update the information elements table for one GTP version. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * GTP_InfoElement *: Information elements * uint8_t: version number for information elements * * RETURNS: Nothing. */ static void UpdateGTPInfoElementTable(GTPConfig *config, GTP_InfoElement *InfoElements, uint8_t version) { int i = 0; while(NULL != InfoElements[i].name) { config->infoElementTable[version][InfoElements[i].type] = &InfoElements[i]; i++; } } /* Update the information elements table for the GTP preprocessor. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * * RETURNS: Nothing. */ static void InitGTPInfoElementTable(GTPConfig *config) { GTP_InfoElement *InfoElements; InfoElements = GTPv0_InfoElements; UpdateGTPInfoElementTable(config,InfoElements, 0); InfoElements = GTPv1_InfoElements; UpdateGTPInfoElementTable(config,InfoElements, 1); InfoElements = GTPv2_InfoElements; UpdateGTPInfoElementTable(config,InfoElements, 2); } /* Update the message types table for one GTP version. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * GTP_MsgType *: message types * uint8_t: version number for message types * * RETURNS: Nothing. */ static void UpdateGTPMsgTypeTable(GTPConfig *config, GTP_MsgType *MsgTypes, uint8_t version) { int i = 0; while(NULL != MsgTypes[i].name) { config->msgTypeTable[version][MsgTypes[i].type] = &MsgTypes[i]; gtp_stats.msgTypeTable[version][MsgTypes[i].type] = &MsgTypes[i]; i++; } } /* Update the message types table for the GTP preprocessor. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * * RETURNS: Nothing. */ static void InitGTPMsgTypeTable(GTPConfig *config) { GTP_MsgType *MsgTypes; MsgTypes = GTPv0_MsgTypes; UpdateGTPMsgTypeTable(config,MsgTypes, 0); MsgTypes = GTPv1_MsgTypes; UpdateGTPMsgTypeTable(config,MsgTypes, 1); MsgTypes = GTPv2_MsgTypes; UpdateGTPMsgTypeTable(config,MsgTypes, 2); } #ifdef DEBUG_MSGS /* Display the message types for the GTP preprocessor. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * * RETURNS: Nothing. */ static void DisplayMsgTypes(GTPConfig *config) { int i, j; _dpd.logMsg(" Supported message types:\n"); for(i = 0; i < MAX_GTP_TYPE_CODE + 1; i++) { _dpd.logMsg("\t%3d ", i); for (j = 0; j < MAX_GTP_VERSION_CODE + 1; j++) { if (config->msgTypeTable[j][i]) { _dpd.logMsg("%40s ", config->msgTypeTable[j][i]->name); } else _dpd.logMsg("%40s ", "N/A"); } _dpd.logMsg("\n"); } } /* Display the information element for the GTP preprocessor. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * * RETURNS: Nothing. */ static void DisplayInfoElements(GTPConfig *config) { int i, j; _dpd.logMsg(" Supported information elements:\n"); for(i = 0; i < MAX_GTP_IE_CODE + 1; i++) { _dpd.logMsg("\t%3d ", i); for (j = 0; j < MAX_GTP_VERSION_CODE + 1 ; j++) { if (config->infoElementTable[j][i]) _dpd.logMsg(" %40s ", config->infoElementTable[j][i]->name); else _dpd.logMsg(" %40s ", "N/A"); } _dpd.logMsg("\n"); } } #endif /* Display the configuration for the GTP preprocessor. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * * RETURNS: Nothing. */ static void DisplayGTPConfig(GTPConfig *config) { int index; int newline; if (config == NULL) return; _dpd.logMsg("GTP config: \n"); /* Traverse list, printing ports, 5 per line */ newline = 1; _dpd.logMsg(" Ports:\n"); for(index = 0; index < MAXPORTS; index++) { if( config->ports[ PORT_INDEX(index) ] & CONV_PORT(index) ) { _dpd.logMsg("\t%d", index); if ( !((newline++)% 5) ) _dpd.logMsg("\n"); } } _dpd.logMsg("\n"); DEBUG_WRAP(DisplayMsgTypes(config)); DEBUG_WRAP(DisplayInfoElements(config)); } /******************************************************************** * Function: GTP_ParsePortList() * * Parses a port list and adds bits associated with the ports * parsed to a bit array. * * Arguments: * char ** * Pointer to the pointer to the current position in the * configuration line. This is updated to the current position * after parsing the IP list. * uint8_t * * Pointer to the port array mask to set bits for the ports * parsed. * * Returns: * GTP_Ret * GTP_SUCCESS if we were able to successfully parse the * port list. * GTP_FAILURE if an error occured in parsing the port list. * ********************************************************************/ static void GTP_ParsePortList(char **ptr, uint8_t *port_array) { long int port = -1; char* cur_tokenp = *ptr; /* If the user specified ports, remove GTP_C_PORT for now since * it now needs to be set explicitly. */ port_array[ PORT_INDEX( GTP_C_PORT ) ] = 0; port_array[ PORT_INDEX( GTP_C_PORT_V0 ) ] = 0; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Port configurations: %s\n",*ptr );); /* Eat the open brace. */ cur_tokenp = strtok( NULL, GTP_CONFIG_VALUE_SEPERATORS); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Port token: %s\n",cur_tokenp );); /* Check the space after '{'*/ if (( !cur_tokenp ) || ( 0 != strncmp (cur_tokenp, "{", 2 ))) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s, make sure space before and after '{'.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_PORTS_KEYWORD); } cur_tokenp = strtok( NULL, GTP_CONFIG_VALUE_SEPERATORS); while (( cur_tokenp ) && ( 0 != strncmp (cur_tokenp, "}", 2 ))) { char *endStr = NULL; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Port token: %s\n",cur_tokenp );); if ( !cur_tokenp ) { DynamicPreprocessorFatalMessage(" %s(%d) => No option to '%s'.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_PORTS_KEYWORD); } port = _dpd.SnortStrtol( cur_tokenp, &endStr, 10); if (*endStr) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_PORTS_KEYWORD, 1, MAXPORTS-1); } if ((port < 0 || port > MAXPORTS-1) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_PORTS_KEYWORD, 1, MAXPORTS-1); } port_array[ PORT_INDEX( port ) ] |= CONV_PORT(port); cur_tokenp = strtok( NULL, GTP_CONFIG_VALUE_SEPERATORS); } if ( NULL == cur_tokenp ) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s, missing '}'.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_PORTS_KEYWORD); } if ( -1 == port) { DynamicPreprocessorFatalMessage(" %s(%d) => No ports specified.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_PORTS_KEYWORD); } *ptr = cur_tokenp; } /* Parses and processes the configuration arguments * supplied in the GTP preprocessor rule. * * PARAMETERS: * * GTPConfig *config: GTP preprocessor configuration. * argp: Pointer to string containing the config arguments. * * RETURNS: Nothing. */ void ParseGTPArgs(GTPConfig *config, u_char* argp) { char* cur_sectionp = NULL; char* next_sectionp = NULL; char* argcpyp = NULL; if (NULL == config) return; /* Set up default port to listen on */ config->ports[ PORT_INDEX( GTP_C_PORT ) ] |= CONV_PORT(GTP_C_PORT); config->ports[ PORT_INDEX( GTP_C_PORT_V0 ) ] |= CONV_PORT(GTP_C_PORT_V0); InitGTPInfoElementTable(config); InitGTPMsgTypeTable(config); /* Sanity check(s) */ if (NULL == argp) { DisplayGTPConfig(config); return; } argcpyp = strdup( (char*) argp ); if ( !argcpyp ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse GTP options.\n"); return; } DEBUG_WRAP(DebugMessage(DEBUG_GTP, "GTP configurations: %s\n",argcpyp );); cur_sectionp = strtok_r( argcpyp, GTP_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Arguments token: %s\n",cur_sectionp );); while ( cur_sectionp ) { char* cur_config; char* cur_tokenp = strtok( cur_sectionp, GTP_CONFIG_VALUE_SEPERATORS); if (!cur_tokenp) { cur_sectionp = strtok_r( next_sectionp, GTP_CONFIG_SECTION_SEPERATORS, &next_sectionp); continue; } cur_config = cur_tokenp; if ( !strcmp( cur_tokenp, GTP_PORTS_KEYWORD )) { GTP_ParsePortList(&cur_tokenp, config->ports); } else { DynamicPreprocessorFatalMessage(" %s(%d) => Invalid argument: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); return; } /*Check whether too many parameters*/ if (NULL != strtok( NULL, GTP_CONFIG_VALUE_SEPERATORS)) { DynamicPreprocessorFatalMessage("%s(%d) => To many arguments: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_config); } cur_sectionp = strtok_r( next_sectionp, GTP_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Arguments token: %s\n",cur_sectionp );); } DisplayGTPConfig(config); free(argcpyp); } /* Search the message type information * * PARAMETERS: * * uint8_t: version number for the message type * char* the message type name * * RETURNS: * * GTP_MsgType*: the message type, NULL if not found */ GTP_MsgType* GetMsgTypeByName(uint8_t version, char *name) { int i = 0; GTP_MsgType *MsgTypes; switch (version) { case 0: MsgTypes = GTPv0_MsgTypes; break; case 1: MsgTypes = GTPv1_MsgTypes; break; case 2: MsgTypes = GTPv2_MsgTypes; break; default: return NULL; } while(NULL != MsgTypes[i].name) { if ( MsgTypes[i].isKeyword &&(strlen(MsgTypes[i].name) == strlen(name)) && (0 == strncmp(MsgTypes[i].name, name, strlen(name)))) return (&(MsgTypes[i])); i++; } return NULL; } /* Search the information element information * * PARAMETERS: * * uint8_t: version number for information elements * char* the information element name * * RETURNS: * * GTP_InfoElement*: the information element, NULL if not found */ GTP_InfoElement* GetInfoElementByName(uint8_t version, char *name) { int i = 0; GTP_InfoElement *InfoElements; switch (version) { case 0: InfoElements = GTPv0_InfoElements; break; case 1: InfoElements = GTPv1_InfoElements; break; case 2: InfoElements = GTPv2_InfoElements; break; default: return NULL; } while(NULL != InfoElements[i].name) { if (InfoElements[i].isKeyword && (strlen(InfoElements[i].name) == strlen(name)) && (0 == strncmp(InfoElements[i].name, name, strlen(name)))) return (&InfoElements[i]); i++; } return NULL; } snort-2.9.20/src/dynamic-preprocessors/gtp/spp_gtp.c0000644000175000017500000005544014241076025020636 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * GTP preprocessor * * This is the main entry point for this preprocessor * * Author: Hui Cao * Date: 07-15-2011 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #include "spp_gtp.h" #include "gtp_config.h" #include "gtp_roptions.h" #include "gtp_parser.h" #include #include #include #include #ifndef WIN32 #include #include #endif #include #include #ifdef DUMP_BUFFER #include "gtp_buffer_dump.h" #endif #include "profiler.h" #ifdef PERF_PROFILING PreprocStats gtpPerfStats; #endif #include "sf_types.h" const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_GTP"; #define SetupGTP DYNAMIC_PREPROC_SETUP #ifdef TARGET_BASED int16_t gtp_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif /* * Session state flags for GTPData::state_flags */ #define GTP_FLG_REASSEMBLY_SET (0x20000) /* * Function prototype(s) */ GTPData * GTPGetNewSession(SFSnortPacket *, tSfPolicyId); static void GTPInit( struct _SnortConfig *, char* ); static int GTPCheckConfig(struct _SnortConfig *); static void FreeGTPData( void* ); static inline int GTP_Process(SFSnortPacket *, GTPData*); static void GTPmain( void*, void* ); static inline int CheckGTPPort( uint16_t ); static void GTPFreeConfig(tSfPolicyUserContextId); static void registerPortsForDispatch( struct _SnortConfig *sc, GTPConfig *policy ); static void registerPortsForReassembly( GTPConfig *policy, int direction ); static void _addPortsToStreamFilter(struct _SnortConfig *, GTPConfig *, tSfPolicyId); static void GTP_PrintStats(int); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static void GTPCleanExit(int, void *); /******************************************************************** * Global variables ********************************************************************/ uint32_t numSessions = 0; GTP_Stats gtp_stats; GTPConfig *gtp_eval_config; tSfPolicyUserContextId gtp_config; #ifdef SNORT_RELOAD static void GTPReload(struct _SnortConfig *, char *, void **); static int GTPReloadVerify(struct _SnortConfig *, void *); static void * GTPReloadSwap(struct _SnortConfig *, void *); static void GTPReloadSwapFree(void *); #endif /* Called at preprocessor setup time. Links preprocessor keyword * to corresponding preprocessor initialization function. * * PARAMETERS: None. * * RETURNS: Nothing. * */ void SetupGTP(void) { /* Link preprocessor keyword to initialization function * in the preprocessor list. */ #ifndef SNORT_RELOAD _dpd.registerPreproc( "gtp", GTPInit ); #else _dpd.registerPreproc("gtp", GTPInit, GTPReload, GTPReloadVerify, GTPReloadSwap, GTPReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getGTPBuffers, GTP_BUFFER_DUMP_FUNC); #endif } #ifdef REG_TEST static inline void PrintGTPSize(void) { _dpd.logMsg("\nGTP Session Size: %lu\n", (long unsigned int)sizeof(GTPData)); } #endif /* Initializes the GTP preprocessor module and registers * it in the preprocessor list. * * PARAMETERS: * * argp: Pointer to argument string to process for config data. * * RETURNS: Nothing. */ static void GTPInit(struct _SnortConfig *sc, char *argp) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); GTPConfig *pDefaultPolicyConfig = NULL; GTPConfig *pPolicyConfig = NULL; if (gtp_config == NULL) { /*create a context*/ gtp_config = sfPolicyConfigCreate(); if (gtp_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for GTP config.\n"); } _dpd.addPreprocConfCheck(sc, GTPCheckConfig); _dpd.registerPreprocStats(GTP_NAME, GTP_PrintStats); _dpd.addPreprocExit(GTPCleanExit, NULL, PRIORITY_LAST, PP_GTP); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("gtp", (void *)>pPerfStats, 0, _dpd.totalPerfStats, NULL); #endif #ifdef TARGET_BASED gtp_app_id = _dpd.findProtocolReference("gtp"); if (gtp_app_id == SFTARGET_UNKNOWN_PROTOCOL) gtp_app_id = _dpd.addProtocolReference("gtp"); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_GTP, gtp_app_id ); #endif } #ifdef REG_TEST PrintGTPSize(); #endif sfPolicyUserPolicySet (gtp_config, policy_id); pDefaultPolicyConfig = (GTPConfig *)sfPolicyUserDataGetDefault(gtp_config); pPolicyConfig = (GTPConfig *)sfPolicyUserDataGetCurrent(gtp_config); if ((pPolicyConfig != NULL) && (pDefaultPolicyConfig == NULL)) { DynamicPreprocessorFatalMessage("GTP preprocessor can only be " "configured once.\n"); } pPolicyConfig = (GTPConfig *)calloc(1, sizeof(GTPConfig)); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "GTP preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(gtp_config, pPolicyConfig); GTP_RegRuleOptions(sc); ParseGTPArgs(pPolicyConfig, (u_char *)argp); if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupGTP(): The Stream preprocessor must be enabled.\n"); } _dpd.addPreproc( sc, GTPmain, PRIORITY_APPLICATION, PP_GTP, PROTO_BIT__UDP ); // register ports with session and stream registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStreamFilter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } /********************************************************************* * Main entry point for GTP processing. * * Arguments: * SFSnortPacket * - pointer to packet structure * * Returns: * int - GTP_SUCCESS * GTP_FAILURE * *********************************************************************/ static inline int GTP_Process(SFSnortPacket *p, GTPData* sessp) { int status; const uint8_t* gtp_buff = p->payload; static uint32_t msgId = 0; GTP_Roptions *pRopts; GTPMsg gtpMsg; pRopts = &(sessp->ropts); memset(>pMsg, 0, GTPMSG_ZERO_LEN); /* msg_id is used to associate message with information elements * If msg_id matches, the information element in the info_elements * belongs to the message * Using msg_id avoids initializing info_elements for every message * Tabled based info_elements improves information element search performance */ /* To avoid id overlap, clean table when msgId resets*/ if ( msgId == 0) gtp_cleanInfoElements(); gtpMsg.msg_id = ++msgId; status = gtp_parse(>pMsg, gtp_buff, p->payload_size); /*Update the session data*/ pRopts->gtp_type = gtpMsg.msg_type; pRopts->gtp_version = gtpMsg.version; pRopts->gtp_infoElements = gtpMsg.info_elements; pRopts->gtp_header = gtpMsg.gtp_header; pRopts->msg_id = gtpMsg.msg_id; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "GTP message version: %d\n", gtpMsg.version)); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "GTP message type: %d\n", gtpMsg.msg_type)); return status; } /* Main runtime entry point for GTP preprocessor. * Analyzes GTP packets for anomalies/exploits. * * PARAMETERS: * * packetp: Pointer to current packet to process. * contextp: Pointer to context block, not used. * * RETURNS: Nothing. */ static void GTPmain( void* ipacketp, void* contextp ) { GTPData* sessp = NULL; uint8_t source = 0; uint8_t dest = 0; SFSnortPacket* packetp; #ifdef TARGET_BASED int16_t app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); PROFILE_VARS; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "%s\n", GTP_DEBUG__START_MSG)); packetp = (SFSnortPacket*) ipacketp; sfPolicyUserPolicySet (gtp_config, policy_id); #ifdef DUMP_BUFFER dumpBufferInit(); dumpBuffer(PAYLOAD_DUMP,packetp->payload,packetp->payload_size); #endif // precoditions - what we registered for assert(IsUDP(packetp) && packetp->payload && packetp->payload_size); PREPROC_PROFILE_START(gtpPerfStats); gtp_eval_config = sfPolicyUserDataGetCurrent(gtp_config); /* Attempt to get a previously allocated GTP block. */ sessp = _dpd.sessionAPI->get_application_data(packetp->stream_session, PP_GTP); if (sessp != NULL) { gtp_eval_config = sfPolicyUserDataGet(sessp->config, sessp->policy_id); } if (sessp == NULL) { /* If not doing autodetection, check the ports to make sure this is * running on an GTP port, otherwise no need to examine the traffic. */ #ifdef TARGET_BASED app_id = _dpd.sessionAPI->get_application_protocol_id(packetp->stream_session); if (app_id == SFTARGET_UNKNOWN_PROTOCOL) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Unknown protocol - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "%s\n", GTP_DEBUG__END_MSG)); PREPROC_PROFILE_END(gtpPerfStats); return; } else if (app_id && (app_id != gtp_app_id)) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Not GTP - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "%s\n", GTP_DEBUG__END_MSG)); PREPROC_PROFILE_END(gtpPerfStats); return; } else if (!app_id) { #endif source = (uint8_t)CheckGTPPort( packetp->src_port ); dest = (uint8_t)CheckGTPPort( packetp->dst_port ); if ( !source && !dest ) { /* Not one of the ports we care about. */ DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Not GTP ports - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "%s\n", GTP_DEBUG__END_MSG)); PREPROC_PROFILE_END(gtpPerfStats); return; } #ifdef TARGET_BASED } #endif /* Check the stream session. If it does not currently * have our GTP data-block attached, create one. */ sessp = GTPGetNewSession(packetp, policy_id); if ( !sessp ) { /* Could not get/create the session data for this packet. */ DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Create session error - not inspecting.\n")); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "%s\n", GTP_DEBUG__END_MSG)); PREPROC_PROFILE_END(gtpPerfStats); return; } } /* We're interested in this session. Turn on stream reassembly. */ if ( !(sessp->state_flags & GTP_FLG_REASSEMBLY_SET )) { _dpd.streamAPI->set_reassembly(packetp->stream_session, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_BOTH, STREAM_FLPOLICY_SET_ABSOLUTE); sessp->state_flags |= GTP_FLG_REASSEMBLY_SET; } /* * Start process PAYLOAD */ GTP_Process(packetp,sessp); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "%s\n", GTP_DEBUG__END_MSG)); PREPROC_PROFILE_END(gtpPerfStats); } /********************************************************************** * Retrieves the GTP data block registered with the stream * session associated w/ the current packet. If none exists, * allocates it and registers it with the stream API. * * Arguments: * * packetp: Pointer to the packet from which/in which to * retrieve/store the GTP data block. * * RETURNS: Pointer to an GTP data block, upon success. * NULL, upon failure. **********************************************************************/ GTPData * GTPGetNewSession(SFSnortPacket *packetp, tSfPolicyId policy_id) { GTPData* datap = NULL; /* Sanity check(s) */ assert( packetp ); if ( !packetp->stream_session ) { return NULL; } datap = (GTPData *)calloc(1, sizeof(GTPData)); if ( !datap ) return NULL; /*Register the new GTP data block in the stream session. */ _dpd.sessionAPI->set_application_data( packetp->stream_session, PP_GTP, datap, FreeGTPData ); datap->policy_id = policy_id; datap->config = gtp_config; ((GTPConfig *)sfPolicyUserDataGetCurrent(gtp_config))->ref_count++; gtp_stats.sessions++; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Number of sessions created: %u\n", gtp_stats.sessions)); return datap; } /*********************************************************************** * Registered as a callback with our GTP data blocks when * they are added to the underlying stream session. Called * by the stream preprocessor when a session is about to be * destroyed. * * PARAMETERS: * * idatap: Pointer to the moribund data. * * RETURNS: Nothing. ***********************************************************************/ static void FreeGTPData( void* idatap ) { GTPData *ssn = (GTPData *)idatap; GTPConfig *config = NULL; if (ssn == NULL) return; if (numSessions > 0) numSessions--; /*Clean the configuration data*/ if (ssn->config != NULL) { config = (GTPConfig *)sfPolicyUserDataGet(ssn->config, ssn->policy_id); } if (config == NULL) { free(ssn); return; } config->ref_count--; if ((config->ref_count == 0) && (ssn->config != gtp_config)) { sfPolicyUserDataClear (ssn->config, ssn->policy_id); free(config); if (sfPolicyUserPolicyGetActive(ssn->config) == 0) { /* No more outstanding configs - free the config array */ GTPFreeConfig(ssn->config); } } free(ssn); } /* ********************************************************************** * Validates given port as an GTP server port. * * PARAMETERS: * * port: Port to validate. * * RETURNS: GTP_TRUE, if the port is indeed an GTP server port. * GTP_FALSE, otherwise. ***********************************************************************/ static inline int CheckGTPPort( uint16_t port ) { if ( gtp_eval_config->ports[ PORT_INDEX(port) ] & CONV_PORT( port ) ) { return GTP_TRUE; } return GTP_FALSE; } static void registerPortsForDispatch( struct _SnortConfig *sc, GTPConfig *policy ) { int port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_GTP, PROTO_BIT__UDP, port ); } } static void registerPortsForReassembly( GTPConfig *policy, int direction ) { uint32_t port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.streamAPI->register_reassembly_port( NULL, port, direction ); } } /* ********************************************************************** * Add ports in the configuration to stream5 filter. * * PARAMETERS: * * GTPConfig: configuration to be used. * tSfPolicyId: policy ID * * RETURNS: None ***********************************************************************/ static void _addPortsToStreamFilter(struct _SnortConfig *sc, GTPConfig *config, tSfPolicyId policy_id) { int portNum; assert(config); assert(_dpd.streamAPI); for (portNum = 0; portNum < MAXPORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_UDP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, gtp_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif static int GTPCheckPolicyConfig(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData) { _dpd.setParserPolicy(sc, policyId); if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("GTPCheckPolicyConfig(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } int GTPCheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate (sc, gtp_config, GTPCheckPolicyConfig))) return rval; return 0; } static void GTPCleanExit(int signal, void *data) { if (gtp_config != NULL) { GTPFreeConfig(gtp_config); gtp_config = NULL; } } static int GTPFreeConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { GTPConfig *pPolicyConfig = (GTPConfig *)pData; //do any housekeeping before freeing GTPConfig sfPolicyUserDataClear (config, policyId); free(pPolicyConfig); return 0; } void GTPFreeConfig(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, GTPFreeConfigPolicy); sfPolicyConfigDelete(config); } /****************************************************************** * Print statistics being kept by the preprocessor. * * Arguments: * int - whether Snort is exiting or not * * Returns: None * ******************************************************************/ static void GTP_PrintStats(int exiting) { int i, j; _dpd.logMsg("GTP Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions: "STDu64"\n", gtp_stats.sessions); if (gtp_stats.sessions < 1) return; if (gtp_stats.events > 0) _dpd.logMsg(" Preprocessor events: "STDu64"\n", gtp_stats.events); _dpd.logMsg(" Total reserved messages: "STDu64"\n", gtp_stats.unknownTypes); _dpd.logMsg(" Packets with reserved information elements: "STDu64"\n", gtp_stats.unknownIEs); for (i = 0; i < MAX_GTP_VERSION_CODE + 1; i++ ) { uint64_t total_msgs = 0; DEBUG_WRAP(_dpd.logMsg(" Messages of version %d:\n", i);); for(j = 0; j < MAX_GTP_TYPE_CODE + 1; j++) { GTP_MsgType *msg = gtp_stats.msgTypeTable[i][j]; if ( msg && msg->name) { DEBUG_WRAP(_dpd.logMsg("%39s: "STDu64"\n", msg->name, gtp_stats.messages[i][j]);); } total_msgs += gtp_stats.messages[i][j]; } if (total_msgs > 0) _dpd.logMsg(" Total messages of version %d: %u\n", i, total_msgs); } } #ifdef SNORT_RELOAD static void GTPReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId gtp_swap_config = (tSfPolicyUserContextId)*new_config; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); GTPConfig * pPolicyConfig = NULL; if (gtp_swap_config == NULL) { //create a context gtp_swap_config = sfPolicyConfigCreate(); if (gtp_swap_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for GTP config.\n"); } *new_config = (void *)gtp_swap_config; } sfPolicyUserPolicySet (gtp_swap_config, policy_id); pPolicyConfig = (GTPConfig *)sfPolicyUserDataGetCurrent(gtp_swap_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("GTP preprocessor can only be " "configured once.\n"); } pPolicyConfig = (GTPConfig *)calloc(1, sizeof(GTPConfig)); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "GTP preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(gtp_swap_config, pPolicyConfig); GTP_RegRuleOptions(sc); ParseGTPArgs(pPolicyConfig, (u_char *)args); if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupGTP(): The Stream preprocessor must be enabled.\n"); } _dpd.addPreproc( sc, GTPmain, PRIORITY_APPLICATION, PP_GTP, PROTO_BIT__UDP ); // register ports with session and stream registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStreamFilter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } static int GTPReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId gtp_swap_config = (tSfPolicyUserContextId)swap_config; GTPConfig * pPolicyConfig = NULL; GTPConfig * pCurrentConfig = NULL; if (gtp_swap_config == NULL) return 0; pPolicyConfig = (GTPConfig *)sfPolicyUserDataGet(gtp_swap_config, _dpd.getDefaultPolicy()); if (!pPolicyConfig) return 0; if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("SetupGTP(): The Stream preprocessor must be enabled.\n"); return -1; } if (gtp_config != NULL) { pCurrentConfig = (GTPConfig *)sfPolicyUserDataGet(gtp_config, _dpd.getDefaultPolicy()); } if (!pCurrentConfig) return 0; return 0; } static int GTPFreeUnusedConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { GTPConfig *pPolicyConfig = (GTPConfig *)pData; //do any housekeeping before freeing GTPConfig if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); free(pPolicyConfig); } return 0; } static void * GTPReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId gtp_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = gtp_config; if (gtp_swap_config == NULL) return NULL; gtp_config = gtp_swap_config; sfPolicyUserDataFreeIterate (old_config, GTPFreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_config; } return NULL; } static void GTPReloadSwapFree(void *data) { if (data == NULL) return; GTPFreeConfig((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/gtp/sf_gtp.vcxproj0000444000175000017500000004073714230012554021711 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {70A5AB00-59EA-490C-9DBB-4FC3249F44C4} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_gtp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_gtp.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_gtp.bsc true true Console .\Release\sf_gtp.dll .\Release\sf_gtp.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_gtp.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_gtp.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_gtp.bsc true true Console .\Release\sf_gtp.dll .\Release\sf_gtp.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_gtp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_gtp.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_gtp.bsc true true true Console .\Debug\sf_gtp.dll .\Debug\sf_gtp.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_gtp.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_gtp.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_gtp.bsc true true true Console .\Debug\sf_gtp.dll .\Debug\sf_gtp.lib ws2_32.lib;%(AdditionalDependencies) snort-2.9.20/src/dynamic-preprocessors/gtp/spp_gtp.h0000644000175000017500000000704114241076026020636 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * spp_gtp.h: Definitions, structs, function prototype(s) for * the GTP preprocessor. * Author: Hui Cao */ #ifndef SPP_GTP_H #define SPP_GTP_H #include #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "gtp_roptions.h" /* Convert port value into an index for the gtp_config->ports array */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) /* * Boolean values. */ #define GTP_TRUE (1) #define GTP_FALSE (0) /* * Error codes. */ #define GTP_SUCCESS (1) #define GTP_FAILURE (0) /* * Per-session data block containing current state * of the GTP preprocessor for the session. * * state_flags: Bit vector describing the current state of the * session. */ typedef struct _gtpData { uint32_t state_flags; GTP_Roptions ropts; tSfPolicyId policy_id; tSfPolicyUserContextId config; } GTPData; typedef struct _GTPMsg { uint8_t version; uint8_t msg_type; uint16_t msg_length; uint16_t header_len; uint8_t *gtp_header; GTP_IEData *info_elements; /* nothing after this point is zeroed ...*/ uint32_t msg_id; /*internal state, new msg will have a new id*/ } GTPMsg; #define GTPMSG_ZERO_LEN offsetof(GTPMsg, msg_id) /* * Generator id. Define here the same as the official registry * in generators.h */ #define GENERATOR_SPP_GTP 143 /* Ultimately calls SnortEventqAdd */ /* Arguments are: gid, sid, rev, classification, priority, message, rule_info */ #define ALERT(x,y) { _dpd.alertAdd(GENERATOR_SPP_GTP, x, 1, 0, 3, y, 0 ); gtp_stats.events++; } /* * GTP preprocessor alert types. */ #define GTP_EVENT_BAD_MSG_LEN (1) #define GTP_EVENT_BAD_IE_LEN (2) #define GTP_EVENT_OUT_OF_ORDER_IE (3) #define GTP_TEID_MISSING (4) /* * GTP preprocessor alert strings. */ #define GTP_EVENT_BAD_MSG_LEN_STR "(spp_gtp) Message length is invalid" #define GTP_EVENT_BAD_IE_LEN_STR "(spp_gtp) Information element length is invalid" #define GTP_EVENT_OUT_OF_ORDER_IE_STR "(spp_gtp) Information elements are out of order" #define GTP_TEID_MISSING_STR "(spp_gtp) TEID is Missing" typedef struct _GTP_Stats { uint64_t sessions; uint64_t events; uint64_t unknownTypes; uint64_t unknownIEs; uint64_t messages[MAX_GTP_VERSION_CODE + 1][MAX_GTP_TYPE_CODE + 1]; GTP_MsgType *msgTypeTable[MAX_GTP_VERSION_CODE + 1][MAX_GTP_TYPE_CODE + 1]; } GTP_Stats; extern GTP_Stats gtp_stats; extern GTPConfig *gtp_eval_config; extern tSfPolicyUserContextId gtp_config; /* Prototypes for public interface */ void SetupGTP(void); #endif /* SPP_GTP_H */ snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_debug.h0000644000175000017500000000340114241076017021116 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides macros and functions for debugging the preprocessor. * If Snort is not configured to do debugging, macros are empty. * * 8/17/2008 - Initial implementation ... Todd Wease * ****************************************************************************/ #ifndef _GTP_DEBUG_H_ #define _GTP_DEBUG_H_ #include #include "snort_debug.h" /******************************************************************** * Macros ********************************************************************/ #define GTP_DEBUG__START_MSG "GTP Start ********************************************" #define GTP_DEBUG__END_MSG "GTP End **********************************************" #endif /* _GTP_DEBUG_H_ */ snort-2.9.20/src/dynamic-preprocessors/gtp/Makefile.in0000644000175000017500000005317614242725546021101 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@gtp_buffer_dump.h \ @BUILD_BUFFER_DUMP_TRUE@gtp_buffer_dump.c subdir = src/dynamic-preprocessors/gtp ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_gtp_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_gtp_preproc_la_SOURCES_DIST = spp_gtp.c spp_gtp.h \ gtp_config.c gtp_config.h gtp_parser.c gtp_parser.h \ gtp_roptions.c gtp_roptions.h gtp_debug.h gtp_buffer_dump.h \ gtp_buffer_dump.c @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = gtp_buffer_dump.lo am_libsf_gtp_preproc_la_OBJECTS = spp_gtp.lo gtp_config.lo \ gtp_parser.lo gtp_roptions.lo $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_gtp_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_gtp_preproc_la_OBJECTS = $(am_libsf_gtp_preproc_la_OBJECTS) \ $(nodist_libsf_gtp_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_gtp_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_gtp_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_gtp_preproc_la_SOURCES) \ $(nodist_libsf_gtp_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_gtp_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_gtp_preproc.la libsf_gtp_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_gtp_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_gtp_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c libsf_gtp_preproc_la_SOURCES = spp_gtp.c spp_gtp.h gtp_config.c \ gtp_config.h gtp_parser.c gtp_parser.h gtp_roptions.c \ gtp_roptions.h gtp_debug.h $(am__append_1) EXTRA_DIST = \ sf_gtp.vcxproj \ sf_gtp.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/gtp/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/gtp/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_gtp_preproc.la: $(libsf_gtp_preproc_la_OBJECTS) $(libsf_gtp_preproc_la_DEPENDENCIES) $(EXTRA_libsf_gtp_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_gtp_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_gtp_preproc_la_OBJECTS) $(libsf_gtp_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_roptions.c0000644000175000017500000003651614241076022021711 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * This processes the rule options for this preprocessor * * Author: Hui Cao * Date: 07-25-2011 ****************************************************************************/ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "gtp_roptions.h" #include "spp_gtp.h" #include "sf_types.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "sf_dynamic_engine.h" #include "sf_snort_plugin_api.h" #include "sfhashfcn.h" #include "profiler.h" #include "gtp_debug.h" #include "gtp_config.h" #include "treenodes.h" #define GTP_ROPT__TYPE "gtp_type" #define GTP_ROPT__IE "gtp_info" #define GTP_ROPT__VERSION "gtp_version" #define GTP_VERSION_0_FLAG (0x01) #define GTP_VERSION_1_FLAG (0x02) #define GTP_VERSION_2_FLAG (0x04) #define GTP_VERSION_ALL_FLAG (GTP_VERSION_0_FLAG|GTP_VERSION_1_FLAG|GTP_VERSION_2_FLAG) /******************************************************************** * Private function prototypes ********************************************************************/ static int GTP_TypeInit(struct _SnortConfig *, char *, char *, void **); static int GTP_TypeEval(void *, const uint8_t **, void *); static int GTP_IEInit(struct _SnortConfig *, char *, char *, void **); static int GTP_IEEval(void *, const uint8_t **, void *); static int GTP_VersionInit(struct _SnortConfig *, char *, char *, void **); static int GTP_VersionEval(void *, const uint8_t **, void *); static inline int GTP_RoptDoEval(SFSnortPacket *p) { if ((p->payload_size == 0) || (p->stream_session == NULL) || (!IsUDP(p))) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "No payload or no " "session pointer or not TCP or UDP - not evaluating.\n")); return 0; } return 1; } /*gtp type can be numbers*/ static bool GTP_AddTypeByNumer(GTP_TypeRuleOptData *sdata, char *tok) { char *endStr = NULL; unsigned long gtpType; gtpType = _dpd.SnortStrtoul(tok, &endStr, 10); if ( *endStr) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d, OR a correct name.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__TYPE, MIN_GTP_TYPE_CODE, MAX_GTP_TYPE_CODE); } if ((gtpType > MAX_GTP_TYPE_CODE) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %d and %d, OR a correct name.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__TYPE, MIN_GTP_TYPE_CODE, MAX_GTP_TYPE_CODE); } DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Rule GTP type: %d.\n",gtpType)); sdata->types[gtpType] = GTP_VERSION_ALL_FLAG; return true; } /*gtp type can be names*/ static bool GTP_AddTypeByKeword(GTP_TypeRuleOptData *sdata, char *name) { GTP_MsgType *msgType; int i; bool found = false; for( i = 0; i < MAX_GTP_VERSION_CODE + 1; i++) { if (NULL != (msgType = GetMsgTypeByName((uint8_t)i, name))) { sdata->types[msgType->type] |= 1 << i; found = true; } } return found; } /* Parsing for the rule option */ static int GTP_TypeInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *nextPara = NULL; char *tok; GTP_TypeRuleOptData *sdata; if (strcasecmp(name, GTP_ROPT__TYPE) != 0) return 0; /* Must have arguments */ if (_dpd.SnortIsStrEmpty(params)) { DynamicPreprocessorFatalMessage("%s(%d) => missing argument to gtp_type keyword\n", *(_dpd.config_file), *(_dpd.config_line)); } tok = strtok_r(params, ",", &nextPara); if(!tok) DynamicPreprocessorFatalMessage("%s(%d) => missing argument to gtp_type keyword\n", *(_dpd.config_file), *(_dpd.config_line)); sdata = (GTP_TypeRuleOptData *)calloc(1, sizeof(*sdata)); if (sdata == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "gtp preprocessor rule option.\n"); } while (NULL != tok) { bool found; if ( isdigit(*tok)) { found = GTP_AddTypeByNumer(sdata, tok); } else /*check keyword*/ { found = GTP_AddTypeByKeword(sdata, tok); } if (! found ) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d, OR a correct name.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__TYPE, MIN_GTP_TYPE_CODE, MAX_GTP_TYPE_CODE); } tok = strtok_r(NULL, ", ", &nextPara); } *data = (void *)sdata; return 1; } /* Rule option evaluation */ static int GTP_TypeEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; GTPData *sd; GTP_Roptions *ropts; GTP_TypeRuleOptData *sdata = (GTP_TypeRuleOptData *)data; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Evaluating \"%s\" rule option.\n", GTP_ROPT__TYPE)); if (!GTP_RoptDoEval(p)) return RULE_NOMATCH; sd = (GTPData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_GTP); if (sd == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "GTP type in packet: %d \n", ropts->gtp_type)); /*Match the GTP type*/ if ((1 << ropts->gtp_version) & sdata->types[ropts->gtp_type]) return RULE_MATCH; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Rule No Match\n")); return RULE_NOMATCH; } /*gtp information element can be number*/ static bool GTP_AddInfoElementByNumer(GTP_InfoRuleOptData *sdata, char *tok) { char *end = NULL; unsigned long gtpIE; int i; gtpIE = _dpd.SnortStrtoul(tok, &end, 10); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Rule GTP information element: %d.\n",gtpIE)); if ( *end) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d, OR a correct name.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__IE, MIN_GTP_IE_CODE, MAX_GTP_IE_CODE); } if ((gtpIE > MAX_GTP_IE_CODE) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage("%s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %d and %d," "OR a correct name.\n ", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__IE, MIN_GTP_IE_CODE, MAX_GTP_IE_CODE); } for( i = 0; i < MAX_GTP_VERSION_CODE + 1; i++) { sdata->types[i] = (uint8_t)gtpIE; } return true; } /*gtp information element can be name*/ static bool GTP_AddInfoElementByKeyword(GTP_InfoRuleOptData *sdata, char *name) { int i; bool found = false; GTP_InfoElement* infoElement; for( i = 0; i < MAX_GTP_VERSION_CODE + 1; i++) { if (NULL != (infoElement = GetInfoElementByName((uint8_t)i, name))) { sdata->types[i] = infoElement->type; found = true; } } return found; } /* Parsing for the rule option */ static int GTP_IEInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *nextPara = NULL; char *tok; GTP_InfoRuleOptData *sdata; bool found = false; if (strcasecmp(name, GTP_ROPT__IE) != 0) return 0; /* Must have arguments */ if (_dpd.SnortIsStrEmpty(params)) { DynamicPreprocessorFatalMessage("%s(%d) => missing argument to %s keyword\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__IE); } tok = strtok_r(params, ",", &nextPara); if(!tok) { DynamicPreprocessorFatalMessage("%s(%d) => missing argument to %s keyword\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__IE); } sdata = (GTP_InfoRuleOptData *)calloc(1, sizeof(*sdata)); if (sdata == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "gtp preprocessor rule option.\n"); } if ( isdigit(*tok)) { found = GTP_AddInfoElementByNumer(sdata, tok); } else { found = GTP_AddInfoElementByKeyword(sdata, tok); } if (! found ) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d, OR a correct name.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__IE, MIN_GTP_IE_CODE, MAX_GTP_IE_CODE); } if (!_dpd.SnortIsStrEmpty(nextPara)) { /* Must have only 1 argument*/ DynamicPreprocessorFatalMessage("%s, %s(%d) => rule option: This option has no arguments.\n", GTP_ROPT__IE, *(_dpd.config_file), *(_dpd.config_line)); } *data = (void *)sdata; return 1; } /* Rule option evaluation */ static int GTP_IEEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; GTPData *sd; GTP_Roptions *ropts; GTP_InfoRuleOptData *ie; uint8_t ieType; GTP_IEData *ieData; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Evaluating \"%s\" rule option.\n", GTP_ROPT__IE)); if (!GTP_RoptDoEval(p)) return RULE_NOMATCH; sd = (GTPData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_GTP); if (sd == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; if (NULL == ropts->gtp_infoElements) return RULE_NOMATCH; /*Match the status code*/ ie = (GTP_InfoRuleOptData *)data; ieType = ie->types[ropts->gtp_version]; if (!ieType) { return RULE_NOMATCH; } ieData = &ropts->gtp_infoElements[ieType]; /*if the data is up to date*/ if (ieData->msg_id == ropts->msg_id) { *cursor = ieData->shift + (uint8_t *)ropts->gtp_header; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Setting cursor to IE data: %p.\n", *cursor)); /*Limit the length*/ _dpd.SetAltDetect((uint8_t *)*cursor, ieData->length); return RULE_MATCH; } DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Rule No Match\n")); return RULE_NOMATCH; } /* Parsing for the rule option */ static int GTP_VersionInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *end = NULL; char *nextPara = NULL; char *tok; uint8_t *sdata; unsigned long gtpVersion; if (strcasecmp(name, GTP_ROPT__VERSION) != 0) return 0; /* Must have arguments */ if (_dpd.SnortIsStrEmpty(params)) { DynamicPreprocessorFatalMessage("%s(%d) => missing argument to %s keyword\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__VERSION); } tok = strtok_r(params, ",", &nextPara); if(!tok) { DynamicPreprocessorFatalMessage("%s(%d) => missing argument to %s keyword\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__VERSION); } sdata = (uint8_t *)calloc(1, sizeof(*sdata)); if (sdata == NULL) { DynamicPreprocessorFatalMessage("Could not allocate memory for the " "gtp preprocessor rule option.\n"); } gtpVersion = _dpd.SnortStrtoul(tok, &end, 10); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Rule GTP version: %d.\n",gtpVersion)); if ( *end) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__VERSION, MIN_GTP_VERSION_CODE, MAX_GTP_VERSION_CODE); } if ((gtpVersion > MAX_GTP_VERSION_CODE) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage("%s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %d and %d\n ", *(_dpd.config_file), *(_dpd.config_line), GTP_ROPT__VERSION, MIN_GTP_VERSION_CODE, MAX_GTP_VERSION_CODE); } *sdata = (uint8_t) gtpVersion; if (!_dpd.SnortIsStrEmpty(nextPara)) { /* Must have only 1 argument*/ DynamicPreprocessorFatalMessage("%s, %s(%d) => rule option: This option has only one argument.\n", GTP_ROPT__IE, *(_dpd.config_file), *(_dpd.config_line)); } *data = (void *)sdata; return 1; } /* Rule option evaluation */ static int GTP_VersionEval(void *pkt, const uint8_t **cursor, void *data) { SFSnortPacket *p = (SFSnortPacket *)pkt; GTPData *sd; GTP_Roptions *ropts; uint8_t version = *((uint8_t *)data); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Evaluating \"%s\" rule option.\n", GTP_ROPT__VERSION)); if (!GTP_RoptDoEval(p)) return RULE_NOMATCH; sd = (GTPData *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_GTP); if (sd == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "No session data - not evaluating.\n")); return RULE_NOMATCH; } ropts = &sd->ropts; /*Match the status code*/ if (version == ropts->gtp_version) { return RULE_MATCH; } DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Rule No Match\n")); return RULE_NOMATCH; } /******************************************************************** * Function: GTP_RegRuleOptions * * Purpose: Register rule options * * Arguments: void * * Returns: void * ********************************************************************/ void GTP_RegRuleOptions(struct _SnortConfig *sc) { _dpd.preprocOptRegister(sc, GTP_ROPT__TYPE, GTP_TypeInit, GTP_TypeEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, GTP_ROPT__IE, GTP_IEInit, GTP_IEEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, GTP_ROPT__VERSION, GTP_VersionInit, GTP_VersionEval, free, NULL, NULL, NULL, NULL); } snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_buffer_dump.c0000644000175000017500000000410214241076013022314 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file gtp_buffer_dump.c ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during GTP inspection. */ #include "gtp_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_GTP_BUFFER_DUMP] = {{"PAYLOAD_DUMP", "", 0}, {"GTP_v0_DUMP", "", 0}, {"GTP_v1_DUMP", "", 0}, {"GTP_v2_DUMP", "", 0}, {"GTP_HEADER_DUMP", "", 0}, {"INFO_ELEMENTS_DUMP", "", 0}}; void dumpBuffer(GTP_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *)content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_GTP_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getGTPBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_parser.h0000644000175000017500000000276714241076021021335 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 2/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef _GTP_PARSER_H_ #define _GTP_PARSER_H_ #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "gtp_debug.h" #include "spp_gtp.h" int gtp_parse(GTPMsg *, const uint8_t *, uint16_t); void gtp_cleanInfoElements(void); #endif snort-2.9.20/src/dynamic-preprocessors/gtp/Makefile.am0000555000175000017500000000161114230012554021035 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_gtp_preproc.la libsf_gtp_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_gtp_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_gtp_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c endif libsf_gtp_preproc_la_SOURCES = \ spp_gtp.c \ spp_gtp.h \ gtp_config.c \ gtp_config.h \ gtp_parser.c \ gtp_parser.h \ gtp_roptions.c \ gtp_roptions.h \ gtp_debug.h if BUILD_BUFFER_DUMP libsf_gtp_preproc_la_SOURCES += \ gtp_buffer_dump.h \ gtp_buffer_dump.c endif EXTRA_DIST = \ sf_gtp.vcxproj \ sf_gtp.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_buffer_dump.h0000644000175000017500000000322614241076014022330 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file gtp_buffer_dump.h ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during GTP inspection. */ #ifndef __GTP_BUFFER_DUMP_H__ #define __GTP_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { PAYLOAD_DUMP, GTP_v0_DUMP, GTP_v1_DUMP, GTP_v2_DUMP, GTP_HEADER_DUMP, INFO_ELEMENTS_DUMP } GTP_BUFFER_DUMP; void dumpBuffer(GTP_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getGTPBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_config.h0000644000175000017500000000610514241076016021300 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 8/1/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef _GTP_CONFIG_H_ #define _GTP_CONFIG_H_ #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "gtp_debug.h" #define GTP_NAME "gtp" #define MAX_GTP_TYPE_CODE (255) #define MIN_GTP_TYPE_CODE (0) #define MAX_GTP_IE_CODE (255) #define MIN_GTP_IE_CODE (0) #define MAX_GTP_VERSION_CODE (2) #define MIN_GTP_VERSION_CODE (0) /* * Message type */ typedef struct _GTP_MsgType { uint8_t type; /* the message type*/ uint8_t isKeyword; /*whether the name can be used as keyword*/ char *name; /*name of the type*/ }GTP_MsgType; /* * Information elements */ typedef struct _GTP_InfoElement { uint8_t type; /* the IE type*/ uint8_t isKeyword; /*whether the name can be used as keyword*/ char *name; /*name of the IE*/ uint16_t length; /* the length of IE; if 0, means variable length*/ }GTP_InfoElement; /* * One of these structures is kept for each configured * server port. */ typedef struct _gtpPortlistNode { uint16_t server_port; struct _gtpPortlistNode* nextp; } GTPPortNode; /* * GTP preprocessor configuration. * * ports: Which ports to check for GTP messages * infoElementTable: information elements table, for quick retrieve * msgTypeTable: message type table, for quick retrieve */ typedef struct _gtpConfig { uint8_t ports[MAXPORTS/8]; GTP_InfoElement* infoElementTable[MAX_GTP_VERSION_CODE + 1 ][MAX_GTP_IE_CODE + 1]; GTP_MsgType *msgTypeTable[MAX_GTP_VERSION_CODE + 1][MAX_GTP_TYPE_CODE + 1]; int ref_count; } GTPConfig; /******************************************************************** * Public function prototypes ********************************************************************/ void ParseGTPArgs(GTPConfig *, u_char*); GTP_MsgType* GetMsgTypeByName(uint8_t, char *); GTP_InfoElement* GetInfoElementByName(uint8_t, char *); #endif snort-2.9.20/src/dynamic-preprocessors/gtp/sf_gtp.dsp0000555000175000017500000001312014230012554020771 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_gtp" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_gtp - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_gtp.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_gtp.mak" CFG="sf_gtp - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_gtp - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_gtp - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_gtp - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_gtp - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_gtp - Win32 Release" # Name "sf_gtp - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\gtp_config.c # End Source File # Begin Source File SOURCE=.\gtp_parser.c # End Source File # Begin Source File SOURCE=.\gtp_roptions.c # End Source File # Begin Source File SOURCE=..\include\inet_aton.c # End Source File # Begin Source File SOURCE=..\include\inet_pton.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_ip.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\spp_gtp.c # End Source File # Begin Source File SOURCE=..\include\strtok_r.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\gtp_config.h # End Source File # Begin Source File SOURCE=.\gtp_debug.h # End Source File # Begin Source File SOURCE=.\gtp_parser.h # End Source File # Begin Source File SOURCE=.\gtp_roptions.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\spp_gtp.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_parser.c0000644000175000017500000004322414241076020021320 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 7/17/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifndef HAVE_PARSER_H #include #include "sf_types.h" #include "sf_snort_packet.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "gtp_parser.h" #include "spp_gtp.h" #include "gtp_config.h" #ifdef DUMP_BUFFER #include "gtp_buffer_dump.h" #endif #ifdef WIN32 #pragma pack(push,gtp_hdrs,1) #else #pragma pack(1) #endif /* GTP basic Header */ typedef struct _GTP_C_Hdr { uint8_t flag; /* flag: version (bit 6-8), PT (5), E (3), S (2), PN (1) */ uint8_t type; /* message type */ uint16_t length; /* length */ } GTP_C_Hdr; typedef struct _GTP_C_Hdr_v1_2 { GTP_C_Hdr hdr; uint32_t teid; } GTP_C_Hdr_v1_2; typedef struct _GTP_C_Hdr_v0 { GTP_C_Hdr hdr; uint16_t sequence_num; uint16_t flow_lable; uint64_t tid; } GTP_C_Hdr_v0; /* GTP Information element Header */ typedef struct _GTP_IE_Hdr { uint8_t type; uint16_t length; /* length */ } GTP_IE_Hdr; #ifdef WIN32 #pragma pack(pop,gtp_hdrs) #else #pragma pack() #endif /* This table stores all the information elements in a packet * To save memory, only one table for all packets, because we inspect * one packet at a time * The information in the table might from previous packet, * use msg_id to find out whether the information is current. * */ GTP_IEData gtp_ies[MAX_GTP_IE_CODE + 1]; #define GTP_HEADER_LEN_V0 (20) #define GTP_HEADER_LEN_V1 (12) #define GTP_HEADER_LEN_V2 (8) #define GTP_HEADER_LEN_EPC_V2 (12) #define GTP_LENGTH_OFFSET_V0 (GTP_HEADER_LEN_V0) #define GTP_LENGTH_OFFSET_V1 (8) #define GTP_LENGTH_OFFSET_V2 (4) #define GTP_MIN_HEADER_LEN (8) static int gtp_processInfoElements(GTPMsg *msg, const uint8_t *, uint16_t ); /*Because different GTP versions have different format, * they are processed separately*/ static int gtp_parse_v0(GTPMsg *msg, const uint8_t *,uint16_t ); static int gtp_parse_v1(GTPMsg *msg, const uint8_t *, uint16_t ); static int gtp_parse_v2(GTPMsg *msg, const uint8_t *, uint16_t ); #ifdef DEBUG_MSGS /*Display the content*/ static void convertToHex( char *output, int outputSize, const uint8_t *input, int inputSize) { int i = 0; int length; int numBytesInLine = 0; int totalBytes = outputSize; char *buf_ptr = output; while ((i < inputSize)&&(totalBytes > 0)) { length = snprintf(buf_ptr, totalBytes, "%.2x ", (uint8_t)input[i]); buf_ptr += length; totalBytes -= length; if (totalBytes < 0) break; numBytesInLine += length; if (numBytesInLine > 80) { snprintf(buf_ptr++, totalBytes, "\n"); totalBytes--; numBytesInLine = 0; } i++; } return; } /* Display the information elements*/ static void printInfoElements(GTP_IEData *info_elements, GTPMsg *msg) { int i ; for (i=0; i < MAX_GTP_IE_CODE + 1; i++) { char buf[STD_BUF]; if (info_elements[i].msg_id == msg->msg_id) { convertToHex( (char *)buf, sizeof(buf), msg->gtp_header + info_elements[i].shift, info_elements[i].length); DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Info type: %.3d, content: %s\n", i, buf);); } } } #endif /******************************************************************** * Function: gtp_processInfoElements() * * Process information elements * * Arguments: * GTPMsg *: the GTP message * * char * * Pointer to the current position in the GTP message. * * uint8_t * * Pointer to the port array mask to set bits for the ports * parsed. * * Returns: * GTP_Ret * GTP_SUCCESS if we were able to successfully parse the * port list. * GTP_FAILURE if an error occured in parsing the port list. * ********************************************************************/ static int gtp_processInfoElements(GTPMsg *msg, const uint8_t *buff, uint16_t len ) { uint8_t *start; uint8_t type; int32_t unprocessed_len; uint8_t previous_type; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Information elements: length: %d\n", len);); #ifdef DUMP_BUFFER dumpBuffer(INFO_ELEMENTS_DUMP,buff,len); #endif start = (uint8_t *)buff; previous_type = (uint8_t) *start; unprocessed_len = len; while ( unprocessed_len > 0) { GTP_InfoElement* ie; uint16_t length; type = *start; if(previous_type > type) { ALERT(GTP_EVENT_OUT_OF_ORDER_IE,GTP_EVENT_OUT_OF_ORDER_IE_STR); } ie = gtp_eval_config->infoElementTable[msg->version][type]; if ( NULL == ie ) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Unsupported Information elements!\n");); gtp_stats.unknownIEs++; return GTP_FAILURE; } /*For fixed length, use the table*/ if (ie->length) { length = ie->length; } else /*For variable length, use the length field*/ { GTP_IE_Hdr *ieHdr; /*check the length before reading*/ if (sizeof(*ieHdr) > (unsigned) unprocessed_len) { ALERT(GTP_EVENT_BAD_IE_LEN,GTP_EVENT_BAD_IE_LEN_STR); return GTP_FAILURE; } ieHdr = (GTP_IE_Hdr *)start; length = ntohs(ieHdr->length); /*Check the length */ if (length > UINT16_MAX - GTP_MIN_HEADER_LEN - sizeof(*ieHdr)) { ALERT(GTP_EVENT_BAD_IE_LEN,GTP_EVENT_BAD_IE_LEN_STR); return GTP_FAILURE; } if (msg->version == 2) length += 4; else length += 3; } if (length > unprocessed_len ) { ALERT(GTP_EVENT_BAD_IE_LEN,GTP_EVENT_BAD_IE_LEN_STR); return GTP_FAILURE; } /*Combine the same information element type into one buffer*/ if ((previous_type == type) && (msg->info_elements[type].msg_id == msg->msg_id)) { msg->info_elements[type].length += length; } else { msg->info_elements[type].length = length; msg->info_elements[type].shift = start - msg->gtp_header; msg->info_elements[type].msg_id = msg->msg_id; } DEBUG_WRAP(DebugMessage(DEBUG_GTP, "GTP information element: %s(%d), length: %d\n", ie->name, ie->type, length)); start += length; unprocessed_len -= length; previous_type = type; } DEBUG_WRAP(printInfoElements(msg->info_elements, msg);); return GTP_SUCCESS; } /******************************************************************** * Function: gtp_parse_v0() * * process the GTP v0 message. * * Arguments: * GTPMsg * - gtp message * char* buff - start of the gtp message buffer * uint16_t - length of the message * * Returns: * GTP_FAILURE * GTP_SUCCESS * Bits *Octets 8 7 6 5 4 3 2 1 *1 Version PT 1 1 1 SNN *2 Message Type *3-4 Length *5-6 Sequence Number *7-8 Flow Label *9 SNDCP N-PDULLC Number *10 Spare ‘ 1 1 1 1 1 1 1 1 ‘ *11 Spare ‘ 1 1 1 1 1 1 1 1 ‘ *12 Spare ‘ 1 1 1 1 1 1 1 1 ‘ *13-20 TID * ********************************************************************/ static int gtp_parse_v0(GTPMsg *msg, const uint8_t *buff, uint16_t gtp_len) { GTP_C_Hdr *hdr; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "This is a GTP v0 packet.\n");); #ifdef DUMP_BUFFER dumpBuffer(GTP_v0_DUMP,buff,gtp_len); #endif hdr = (GTP_C_Hdr *) buff; msg->header_len = GTP_HEADER_LEN_V0; #ifdef DUMP_BUFFER dumpBuffer(GTP_HEADER_DUMP,msg->gtp_header,msg->header_len); #endif /*Check the length field. */ if (gtp_len != ((unsigned int)ntohs(hdr->length) + GTP_LENGTH_OFFSET_V0)) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Calculated length %d != %d in header.\n", gtp_len - GTP_LENGTH_OFFSET_V0, ntohs(hdr->length));); ALERT(GTP_EVENT_BAD_MSG_LEN,GTP_EVENT_BAD_MSG_LEN_STR); return GTP_FAILURE; } return GTP_SUCCESS; } /******************************************************************** * Function: gtp_parse_v1() * * process the GTP v1 message. * * Arguments: * GTPMsg * - gtp message * char* buff - start of the gtp message buffer * uint16_t - length of the message * * Returns: * GTP_FAILURE * GTP_SUCCESS * * Octets 8 7 6 5 4 3 2 1 * 1 Version PT (*) E S PN * 2 Message Type * 3 Length (1st Octet) * 4 Length (2nd Octet) * 5 Tunnel Endpoint Identifier (1st Octet) * 6 Tunnel Endpoint Identifier (2nd Octet) * 7 Tunnel Endpoint Identifier (3rd Octet) * 8 Tunnel Endpoint Identifier (4th Octet) * 9 Sequence Number (1st Octet) * 10 Sequence Number (2nd Octet) * 11 N-PDU Number * 12 Next Extension Header Type ********************************************************************/ static int gtp_parse_v1(GTPMsg *msg, const uint8_t *buff, uint16_t gtp_len) { uint8_t next_hdr_type; GTP_C_Hdr *hdr; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "This ia a GTP v1 packet.\n");); #ifdef DUMP_BUFFER dumpBuffer(GTP_v1_DUMP,buff,gtp_len); #endif hdr = (GTP_C_Hdr *) buff; /*Check the length based on optional fields and extension header*/ if (hdr->flag & 0x07) { msg->header_len = GTP_HEADER_LEN_V1; /*Check optional fields*/ if (gtp_len < msg->header_len) { ALERT(GTP_EVENT_BAD_MSG_LEN,GTP_EVENT_BAD_MSG_LEN_STR); return GTP_FAILURE; } next_hdr_type = *(buff + msg->header_len - 1); /*Check extension headers*/ while (next_hdr_type) { uint16_t ext_header_len; /*check length before reading data, at lease 4 bytes per extension header*/ if (gtp_len < msg->header_len + 4) { ALERT(GTP_EVENT_BAD_MSG_LEN,GTP_EVENT_BAD_MSG_LEN_STR); return GTP_FAILURE; } ext_header_len = *(buff + msg->header_len); if (!ext_header_len) { ALERT(GTP_EVENT_BAD_MSG_LEN,GTP_EVENT_BAD_MSG_LEN_STR); return GTP_FAILURE; } /*Extension header length is a unit of 4 octets*/ msg->header_len += ext_header_len*4; /*check length before reading data*/ if (gtp_len < msg->header_len) { ALERT(GTP_EVENT_BAD_MSG_LEN,GTP_EVENT_BAD_MSG_LEN_STR); return GTP_FAILURE; } next_hdr_type = *(buff + msg->header_len - 1); } } else msg->header_len = GTP_HEADER_LEN_V1; #ifdef DUMP_BUFFER dumpBuffer(GTP_HEADER_DUMP,msg->gtp_header,msg->header_len); #endif /*Check the length field. */ if (gtp_len != ((unsigned int)ntohs(hdr->length) + GTP_LENGTH_OFFSET_V1)) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Calculated length %d != %d in header.\n", gtp_len - GTP_LENGTH_OFFSET_V1, ntohs(hdr->length));); ALERT(GTP_EVENT_BAD_MSG_LEN,GTP_EVENT_BAD_MSG_LEN_STR); return GTP_FAILURE; } return GTP_SUCCESS; } /******************************************************************** * Function: gtp_parse_v2() * * process the GTP v2 message. * * Arguments: * GTPMsg * - gtp message * char* buff - start of the gtp message buffer * uint16_t - length of the message * * Returns: * GTP_FAILURE * GTP_SUCCESS * *Octets 8 7 6 5 4 3 2 1 *1 Version P T Spare Spare Spare *2 Message Type *3 Message Length (1st Octet) *4 Message Length (2nd Octet) *m to k(m+3) If T flag is set to 1, then TEID shall be placed into octets 5-8. * Otherwise, TEID field is not present at all. *n to (n+2) Sequence Number *(n+3) Spare ********************************************************************/ static int gtp_parse_v2(GTPMsg *msg, const uint8_t *buff, uint16_t gtp_len) { GTP_C_Hdr *hdr; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "This ia a GTP v2 packet.\n");); #ifdef DUMP_BUFFER dumpBuffer(GTP_v2_DUMP,buff,gtp_len); #endif hdr = (GTP_C_Hdr *) buff; if (hdr->flag & 0x8) msg->header_len = GTP_HEADER_LEN_EPC_V2; else msg->header_len = GTP_HEADER_LEN_V2; #ifdef DUMP_BUFFER dumpBuffer(GTP_HEADER_DUMP,msg->gtp_header,msg->header_len); #endif /*Check the length field. */ if (gtp_len != ((unsigned int)ntohs(hdr->length) + GTP_LENGTH_OFFSET_V2)) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Calculated length %d != %d in header.\n", gtp_len - GTP_LENGTH_OFFSET_V2, ntohs(hdr->length));); ALERT(GTP_EVENT_BAD_MSG_LEN,GTP_EVENT_BAD_MSG_LEN_STR); return GTP_FAILURE; } return GTP_SUCCESS; } /******************************************************************** * Function: gtp_parse() * * The main entry for parser: process the gtp messages. * * Arguments: * GTPMsg * - gtp message * char* buff - start of the gtp message buffer * uint16_t - length of the message * * Returns: * GTP_FAILURE * GTP_SUCCESS ********************************************************************/ int gtp_parse(GTPMsg *msg, const uint8_t *buff, uint16_t gtp_len) { int status; GTP_C_Hdr *hdr; GTP_MsgType *msgType; GTP_C_Hdr_v1_2 *hdrv1_2; /*Initialize key values*/ status = GTP_SUCCESS; DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Start parsing...\n")); hdrv1_2 = (GTP_C_Hdr_v1_2 *) buff; hdr = &(hdrv1_2->hdr); /*Check the length*/ DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Basic header length: %d\n", GTP_MIN_HEADER_LEN)); if (gtp_len < GTP_MIN_HEADER_LEN) return GTP_FAILURE; /*The first 3 bits are version number*/ msg->version = (hdr->flag & 0xE0) >> 5; msg->msg_type = hdr->type; msg->gtp_header = (uint8_t *)buff; if (msg->version > MAX_GTP_VERSION_CODE) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Unsupported GTP version: %d!\n",msg->version);); return GTP_FAILURE; } /*Check whether this is GTP or GTP', Exit if GTP'*/ if (!(hdr->flag & 0x10)) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Unsupported GTP'!\n");); return GTP_FAILURE; } msgType = gtp_eval_config->msgTypeTable[msg->version][msg->msg_type]; if ( NULL == msgType ) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Unsupported GTP message type: %d!\n",msg->msg_type);); gtp_stats.unknownTypes++; return GTP_FAILURE; } else { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "GTP version: %d, message type: %s(%d)\n", msg->version, msgType->name, msg->msg_type)); } gtp_stats.messages[msg->version][msg->msg_type]++; /* We only care about control types*/ if ( hdr->type == 255) return GTP_FAILURE; switch (msg->version) { case 0: /*GTP v0*/ status = gtp_parse_v0(msg, buff, gtp_len); break; case 1: /*GTP v1*/ if ((msg->msg_type > 3) && (hdrv1_2->teid == 0)) { ALERT(GTP_TEID_MISSING, GTP_TEID_MISSING_STR); } status = gtp_parse_v1(msg, buff, gtp_len); break; case 2:/*GTP v2 */ if ((msg->msg_type > 3) && (hdr->flag & 0x08) && (hdrv1_2->teid == 0)) { ALERT(GTP_TEID_MISSING, GTP_TEID_MISSING_STR); } status = gtp_parse_v2(msg, buff, gtp_len); break; default: DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Unknown protocol version.\n");); return GTP_FAILURE; } /*Parse information elements*/ if ((msg->header_len < gtp_len)&& (GTP_SUCCESS == status)) { msg->info_elements = gtp_ies; buff += msg->header_len; status = gtp_processInfoElements(msg, buff, (uint16_t)(gtp_len - msg->header_len)); } return status; } /******************************************************************** * Function: gtp_cleanInfoElements() * * Clean up the shared information elements table * * Arguments: * None * * Returns: * None ********************************************************************/ void gtp_cleanInfoElements(void) { DEBUG_WRAP(DebugMessage(DEBUG_GTP, "Cleaned total bytes %d, length %d.\n", (MAX_GTP_IE_CODE + 1) * sizeof(GTP_IEData), sizeof(gtp_ies));); memset(gtp_ies, 0, sizeof(gtp_ies)); } #endif snort-2.9.20/src/dynamic-preprocessors/gtp/gtp_roptions.h0000644000175000017500000000520114241076023021702 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * This processes the rule options for this preprocessor * * Author: Hui Cao * Date: 07-25-2011 ****************************************************************************/ #ifndef _GTP_ROPTIONS_H_ #define _GTP_ROPTIONS_H_ #include "gtp_config.h" /******************************************************************** * Structures ********************************************************************/ typedef struct _GTP_IEData { uint16_t length; /*length of the data*/ uint16_t shift; /*shift relative to the header*/ uint32_t msg_id; /* used to associate to current msg */ }GTP_IEData; typedef struct _GTP_Roptions { /* gtp_type data*/ uint8_t gtp_type; /* gtp_version data*/ uint8_t gtp_version; uint8_t *gtp_header; uint32_t msg_id; /* used to associate to current msg */ /* gtp ie data */ GTP_IEData *gtp_infoElements; } GTP_Roptions; /*For every value types[i], the bit mask show the version to be applied * bit 1 is for version 0, * bit 2 is for version 1, * bit 3 is for version 2 * */ typedef struct _GTP_TypeRuleOptData { /*Total 256 types*/ uint8_t types[MAX_GTP_TYPE_CODE + 1]; } GTP_TypeRuleOptData; /* * byte 0 is for version 0, * byte 1 is for version 1, * byte 2 is for version 2 * */ typedef struct _GTP_InfoRuleOptData { uint8_t types[MAX_GTP_VERSION_CODE + 1]; } GTP_InfoRuleOptData; /******************************************************************** * Public function prototypes ********************************************************************/ void GTP_RegRuleOptions(struct _SnortConfig *sc); #endif /* _GTP_ROPTIONS_H_ */ snort-2.9.20/src/dynamic-preprocessors/ssl/0000755000175000017500000000000014242725716017026 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/ssl/sf_ssl.vcxproj0000444000175000017500000004156414230012554021726 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {6BA82E34-A6FB-4900-90FE-D88BF7AEB001} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\..\;..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_ssl.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_ssl.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_ssl.bsc true true Console .\Release\sf_ssl.dll .\Release\sf_ssl.lib ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\..\;..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_ssl.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_ssl.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_ssl.bsc true true Console .\Release\sf_ssl.dll .\Release\sf_ssl.lib ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\..\;..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_ssl.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_ssl.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_ssl.bsc true true true Console .\Debug\sf_ssl.dll .\Debug\sf_ssl.lib ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\..\;..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_ssl.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_ssl.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_ssl.bsc true true true Console .\Debug\sf_ssl.dll .\Debug\sf_ssl.lib ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/ssl/Makefile.in0000644000175000017500000005576714242725547021121 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@ssl_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@ssl_buffer_dump.h subdir = src/dynamic-preprocessors/ssl ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_ssl_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_ssl_preproc_la_SOURCES_DIST = ssl_setup.c ssl_setup.h \ ssl_buffer_dump.c ssl_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = ssl_buffer_dump.lo am_libsf_ssl_preproc_la_OBJECTS = ssl_setup.lo $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_ssl_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo ssl.lo \ @SO_WITH_STATIC_LIB_FALSE@ ssl_config.lo ssl_inspect.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfparser.lo libsf_ssl_preproc_la_OBJECTS = $(am_libsf_ssl_preproc_la_OBJECTS) \ $(nodist_libsf_ssl_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_ssl_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_ssl_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_ssl_preproc_la_SOURCES) \ $(nodist_libsf_ssl_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_ssl_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs -I${srcdir}/../libs/ssl_common INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_ssl_preproc.la libsf_ssl_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_ssl_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_ssl_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_config.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_inspect.c \ @SO_WITH_STATIC_LIB_FALSE@../libs/sfparser.c libsf_ssl_preproc_la_SOURCES = ssl_setup.c ssl_setup.h $(am__append_1) EXTRA_DIST = \ sf_ssl.vcxproj \ sf_ssl.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/ssl/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/ssl/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_ssl_preproc.la: $(libsf_ssl_preproc_la_OBJECTS) $(libsf_ssl_preproc_la_DEPENDENCIES) $(EXTRA_libsf_ssl_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_ssl_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_ssl_preproc_la_OBJECTS) $(libsf_ssl_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c ssl.lo: ../ssl_common/ssl.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl.lo `test -f '../ssl_common/ssl.c' || echo '$(srcdir)/'`../ssl_common/ssl.c ssl_config.lo: ../ssl_common/ssl_config.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_config.lo `test -f '../ssl_common/ssl_config.c' || echo '$(srcdir)/'`../ssl_common/ssl_config.c ssl_inspect.lo: ../ssl_common/ssl_inspect.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_inspect.lo `test -f '../ssl_common/ssl_inspect.c' || echo '$(srcdir)/'`../ssl_common/ssl_inspect.c sfparser.lo: ../libs/sfparser.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfparser.lo `test -f '../libs/sfparser.c' || echo '$(srcdir)/'`../libs/sfparser.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/ssl/ssl_setup.h0000644000175000017500000000201014241076457021211 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ #ifndef SSL_SETUP_H #define SSL_SETUP_H #include "ssl_include.h" #include "ssl_config.h" extern void SetupSSLPP(void); #endif snort-2.9.20/src/dynamic-preprocessors/ssl/Makefile.am0000444000175000017500000000165414230012554021050 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs -I${srcdir}/../libs/ssl_common dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_ssl_preproc.la libsf_ssl_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_ssl_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_ssl_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c \ ../ssl_common/ssl.c \ ../ssl_common/ssl_config.c \ ../ssl_common/ssl_inspect.c \ ../libs/sfparser.c endif libsf_ssl_preproc_la_SOURCES = \ ssl_setup.c \ ssl_setup.h if BUILD_BUFFER_DUMP libsf_ssl_preproc_la_SOURCES += \ ssl_buffer_dump.c \ ssl_buffer_dump.h endif EXTRA_DIST = \ sf_ssl.vcxproj \ sf_ssl.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/ssl/sf_ssl.dsp0000444000175000017500000001300114230012554021002 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_ssl" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_ssl - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_ssl.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_ssl.mak" CFG="sf_ssl - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_ssl - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_ssl - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_ssl - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\..\\" /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "SF_SNORT_PREPROC_DLL" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib ../libs/Release/sfdynamic_preproc_libs.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_ssl - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\..\\" /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "SF_SNORT_PREPROC_DLL" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib ../libs/Debug/sfdynamic_preproc_libs.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_ssl - Win32 Release" # Name "sf_ssl - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\libs\sfparser.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_ha.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_inspect.c # End Source File # Begin Source File SOURCE=.\ssl_setup.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\ssl_common\ssl_include.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\ssl_setup.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/ssl/ssl_buffer_dump.h0000644000175000017500000000320014241076450022342 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file ssl_buffer_dump.h ** ** @author Krishnakanth ** ** @brief This file contains structures and functions for ** dumping buffers for SSL protocol */ #ifndef __SSL_BUFFER_DUMP_H__ #define __SSL_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { SSL_DECODE_DUMP, SSL_PROCESS_OTHER_DUMP, SSL_PROCESS_ALERT_DUMP, SSL_PROCESS_APP_DUMP } SSL_BUFFER_DUMP; void dumpBuffer(SSL_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getSSLBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/ssl/ssl_buffer_dump.c0000644000175000017500000000371214241076446022352 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file ssl_buffer_dump.c ** ** @author Krishnakanth ** ** @brief This file contains structures and functions for ** SSL related dumping buffers. */ #include "ssl_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_SSL_BUFFER_DUMP] = { {"SSL_DECODE_DUMP","", 0}, {"SSL_PROCESS_OTHER_DUMP","", 0}, {"SSL_PROCESS_ALERT_DUMP","", 0}, {"SSL_PROCESS_APP_DUMP","", 0}}; void dumpBuffer(SSL_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *) content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_SSL_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getSSLBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/ssl/ssl_setup.c0000644000175000017500000000273014241076451021207 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ #include "ssl_setup.h" #ifdef DUMP_BUFFER #include "ssl_buffer_dump.h" #endif #define SetupSSLPP DYNAMIC_PREPROC_SETUP const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 4; const char *PREPROC_NAME = "SF_SSLPP"; void SetupSSLPP(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc( "ssl", SSLPP_init); #else _dpd.registerPreproc("ssl", SSLPP_init, SSLReload, SSLReloadVerify, SSLReloadSwap, SSLReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getSSLBuffers, SSL_BUFFER_DUMP_FUNC); dumpBufferInit(); #endif } snort-2.9.20/src/dynamic-preprocessors/modbus/0000755000175000017500000000000014242725716017516 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/modbus/modbus_roptions.h0000644000175000017500000000351114241076117023106 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Rule options for Modbus preprocessor. * */ #ifndef MODBUS_ROPTIONS_H #define MODBUS_ROPTIONS_H #include #define MODBUS_FUNC_NAME "modbus_func" #define MODBUS_UNIT_NAME "modbus_unit" #define MODBUS_DATA_NAME "modbus_data" /* Data types */ typedef enum _modbus_option_type_t { MODBUS_FUNC = 0, MODBUS_UNIT, MODBUS_DATA } modbus_option_type_t; typedef struct _modbus_option_data_t { modbus_option_type_t type; uint16_t arg; } modbus_option_data_t; typedef struct _modbus_func_map_t { char *name; uint8_t func; } modbus_func_map_t; int ModbusFuncInit(struct _SnortConfig *sc, char *name, char *params, void **data); int ModbusUnitInit(struct _SnortConfig *sc, char *name, char *params, void **data); int ModbusDataInit(struct _SnortConfig *sc, char *name, char *params, void **data); int ModbusRuleEval(void *raw_packet, const uint8_t **cursor, void *data); #endif /* MODBUS_ROPTIONS_H */ snort-2.9.20/src/dynamic-preprocessors/modbus/modbus_buffer_dump.c0000644000175000017500000000365314241076077023536 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file modbus_buffer_dump.c ** ** @author Krishnakanth ** ** @brief This file contains structures and functions for ** modbus related dumping buffers. */ #include "modbus_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_MODBUS_BUFFER_DUMP] = {{"MODBUS_SERVER_RESPONSE_DUMP","", 0}, {"MODBUS_CLINET_REQUEST_DUMP","", 0}, {"MODBUS_RESERVED_FUN_DUMP","", 0}}; void dumpBuffer(MODBUS_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *)content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_MODBUS_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getMODBUSBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/modbus/modbus_decode.h0000644000175000017500000000333114241076110022445 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the Modbus protocol * */ #ifndef MODBUS_DECODE_H #define MODBUS_DECODE_H #include #include "spp_modbus.h" #include "sf_snort_plugin_api.h" /* Need 8 bytes for MBAP Header + Function Code */ #define MODBUS_MIN_LEN 8 /* GIDs, SIDs, and Strings */ #define GENERATOR_SPP_MODBUS 144 #define MODBUS_BAD_LENGTH 1 #define MODBUS_BAD_PROTO_ID 2 #define MODBUS_RESERVED_FUNCTION 3 #define MODBUS_BAD_LENGTH_STR "(spp_modbus): Length in Modbus MBAP header does not match the length needed for the given Modbus function." #define MODBUS_BAD_PROTO_ID_STR "(spp_modbus): Modbus protocol ID is non-zero." #define MODBUS_RESERVED_FUNCTION_STR "(spp_modbus): Reserved Modbus function code in use." int ModbusDecode(modbus_config_t *config, SFSnortPacket *packet); #endif /* MODBUS_DECODE_H */ snort-2.9.20/src/dynamic-preprocessors/modbus/modbus_roptions.c0000644000175000017500000001711614241076115023105 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Rule options for Modbus preprocessor * */ #include #include "sf_types.h" #include "sf_snort_plugin_api.h" #include "sf_dynamic_preprocessor.h" #include "spp_modbus.h" #include "modbus_decode.h" #include "modbus_roptions.h" #include "modbus_paf.h" /* Mapping of name -> function code for 'modbus_func' option. */ static modbus_func_map_t func_map[] = { {"read_coils", 1}, {"read_discrete_inputs", 2}, {"read_holding_registers", 3}, {"read_input_registers", 4}, {"write_single_coil", 5}, {"write_single_register", 6}, {"read_exception_status", 7}, {"diagnostics", 8}, {"get_comm_event_counter", 11}, {"get_comm_event_log", 12}, {"write_multiple_coils", 15}, {"write_multiple_registers", 16}, {"report_slave_id", 17}, {"read_file_record", 20}, {"write_file_record", 21}, {"mask_write_register", 22}, {"read_write_multiple_registers", 23}, {"read_fifo_queue", 24}, {"encapsulated_interface_transport", 43} }; int ModbusFuncInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *endptr; modbus_option_data_t *modbus_data; unsigned int func_code = 0; if (name == NULL || data == NULL) return 0; if (strcmp(name, MODBUS_FUNC_NAME) != 0) return 0; if (params == NULL) { DynamicPreprocessorFatalMessage("%s(%d): No argument given for modbus_func. " "modbus_func requires a number between 0 and 255, or a valid function " "name.\n", *_dpd.config_file, *_dpd.config_line); } modbus_data = (modbus_option_data_t *)calloc(1, sizeof(modbus_option_data_t)); if (modbus_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "modbus_func data structure.\n", __FILE__, __LINE__); } /* Parsing time */ if (isdigit(params[0])) { /* Function code given as integer */ func_code = _dpd.SnortStrtoul(params, &endptr, 10); if ((func_code > 255) || (*endptr != '\0')) { DynamicPreprocessorFatalMessage("%s(%d): modbus_func requires a " "number between 0 and 255, or a valid function name.\n", *_dpd.config_file, *_dpd.config_line); } } else { /* Check the argument against the map in modbus_roptions.h */ size_t i; int parse_success = 0; for (i = 0; i < (sizeof(func_map) / sizeof(modbus_func_map_t)); i++) { if (strcmp(params, func_map[i].name) == 0) { parse_success = 1; func_code = func_map[i].func; break; } } if (!parse_success) { DynamicPreprocessorFatalMessage("%s(%d): modbus_func requires a " "number between 0 and 255, or a valid function name.\n", *_dpd.config_file, *_dpd.config_line); } } modbus_data->type = MODBUS_FUNC; modbus_data->arg = (uint8_t) func_code; *data = (void *)modbus_data; return 1; } int ModbusUnitInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *endptr; modbus_option_data_t *modbus_data; unsigned int unit_code; if (name == NULL || data == NULL) return 0; if (strcmp(name, MODBUS_UNIT_NAME) != 0) return 0; if (params == NULL) { DynamicPreprocessorFatalMessage("%s(%d): No argument given for modbus_unit. " "modbus_unit requires a number between 0 and 255.\n", *_dpd.config_file, *_dpd.config_line); } modbus_data = (modbus_option_data_t *)calloc(1, sizeof(modbus_option_data_t)); if (modbus_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "modbus_unit data structure.\n", __FILE__, __LINE__); } /* Parsing time */ unit_code = _dpd.SnortStrtoul(params, &endptr, 10); if ((unit_code > 255) || (*endptr != '\0')) { DynamicPreprocessorFatalMessage("%s(%d): modbus_unit requires a " "number between 0 and 255.\n", *_dpd.config_file, *_dpd.config_line); } modbus_data->type = MODBUS_UNIT; modbus_data->arg = (uint8_t) unit_code; *data = (void *)modbus_data; return 1; } int ModbusDataInit(struct _SnortConfig *sc, char *name, char *params, void **data) { modbus_option_data_t *modbus_data; if (strcmp(name, MODBUS_DATA_NAME) != 0) return 0; /* Nothing to parse. */ if (params) { DynamicPreprocessorFatalMessage("%s(%d): modbus_data does not take " "any arguments.\n", *_dpd.config_file, *_dpd.config_line); } modbus_data = (modbus_option_data_t *)calloc(1, sizeof(modbus_option_data_t)); if (modbus_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "modbus_data data structure.\n", __FILE__, __LINE__); } modbus_data->type = MODBUS_DATA; modbus_data->arg = 0; *data = (void *)modbus_data; return 1; } /* Modbus rule evaluation callback. */ int ModbusRuleEval(void *raw_packet, const uint8_t **cursor, void *data) { SFSnortPacket *packet = (SFSnortPacket *)raw_packet; modbus_option_data_t *rule_data = (modbus_option_data_t *)data; modbus_session_data_t *session_data; /* The preprocessor only evaluates PAF-flushed PDUs. If the rule options don't check for this, they'll fire on stale session data when the original packet goes through before flushing. */ if (!PacketHasFullPDU(packet) && ModbusIsPafActive(packet)) return RULE_NOMATCH; session_data = (modbus_session_data_t *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_MODBUS); if ((packet->payload_size == 0 ) || (session_data == NULL)) { return RULE_NOMATCH; } switch (rule_data->type) { case MODBUS_FUNC: if (session_data->func == rule_data->arg) return RULE_MATCH; break; case MODBUS_UNIT: if (session_data->unit == rule_data->arg) return RULE_MATCH; break; case MODBUS_DATA: /* XXX: If a PDU contains only the MBAP + Function, should this option fail or set the cursor to the end of the payload? */ if (packet->payload_size < MODBUS_MIN_LEN) return RULE_NOMATCH; /* Modbus data is always directly after the function code. */ *cursor = (const uint8_t *) (packet->payload + MODBUS_MIN_LEN); _dpd.SetAltDetect((uint8_t *)*cursor, (uint16_t)(packet->payload_size - MODBUS_MIN_LEN)); return RULE_MATCH; } return RULE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/modbus/modbus_paf.h0000644000175000017500000000430314241076114021774 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Protocol-Aware Flushing (PAF) code for the Modbus preprocessor. * */ #ifndef MODBUS_PAF__H #define MODBUS_PAF__H #include "spp_modbus.h" #include "stream_api.h" typedef enum _modbus_paf_state { MODBUS_PAF_STATE__TRANS_ID_1 = 0, MODBUS_PAF_STATE__TRANS_ID_2, MODBUS_PAF_STATE__PROTO_ID_1, MODBUS_PAF_STATE__PROTO_ID_2, MODBUS_PAF_STATE__LENGTH_1, MODBUS_PAF_STATE__LENGTH_2, MODBUS_PAF_STATE__SET_FLUSH } modbus_paf_state_t; typedef struct _modbus_paf_data { modbus_paf_state_t state; uint16_t modbus_length; } modbus_paf_data_t; void ModbusAddPortsToPaf(struct _SnortConfig *sc, modbus_config_t *config, tSfPolicyId policy_id); int ModbusPafRegisterPort(struct _SnortConfig *sc, uint16_t port, tSfPolicyId policy_id); int ModbusAddServiceToPaf(struct _SnortConfig *sc, uint16_t service, tSfPolicyId policy_id); PAF_Status ModbusPaf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint64_t *flags, uint32_t *fp, uint32_t *fp_eoh); static inline bool ModbusIsPafActive(const SFSnortPacket *p) { bool to_server = (p->flags & FLAG_FROM_CLIENT)? true:false; if ((p->stream_session_ptr) && _dpd.streamAPI->is_paf_active(p->stream_session_ptr, to_server)) return true; return false; } #endif /* MODBUS_PAF__H */ snort-2.9.20/src/dynamic-preprocessors/modbus/spp_modbus.h0000644000175000017500000000376414241076121022040 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the Modbus protocol * */ #ifndef SPP_MODBUS_H #define SPP_MODBUS_H #include "sf_types.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #define MAX_PORTS 65536 /* Default MODBUS port */ #define MODBUS_PORT 502 /* Convert port value into an index for the modbus_config->ports array */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) /* Session data flags */ #define MODBUS_FUNC_RULE_FIRED 0x0001 #define MODBUS_UNIT_RULE_FIRED 0x0002 #define MODBUS_DATA_RULE_FIRED 0x0004 /* Modbus preprocessor configuration */ typedef struct _modbus_config { uint8_t ports[MAX_PORTS/8]; int ref_count; } modbus_config_t; /* Modbus session data */ typedef struct _modbus_session_data { uint8_t func; uint8_t unit; uint16_t flags; tSfPolicyId policy_id; tSfPolicyUserContextId context_id; } modbus_session_data_t; #define MODBUS_PORTS_KEYWORD "ports" #define MODBUS_MEMCAP_KEYWORD "memcap" #define MODBUS_OK 1 #define MODBUS_FAIL (-1) #endif /* SPP_MODBUS_H */ snort-2.9.20/src/dynamic-preprocessors/modbus/Makefile.in0000644000175000017500000005335014242725546021572 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@modbus_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@modbus_buffer_dump.h subdir = src/dynamic-preprocessors/modbus ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_modbus_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_modbus_preproc_la_SOURCES_DIST = spp_modbus.c spp_modbus.h \ modbus_decode.c modbus_decode.h modbus_roptions.c \ modbus_roptions.h modbus_paf.c modbus_paf.h \ modbus_buffer_dump.c modbus_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = modbus_buffer_dump.lo am_libsf_modbus_preproc_la_OBJECTS = spp_modbus.lo modbus_decode.lo \ modbus_roptions.lo modbus_paf.lo $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_modbus_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_modbus_preproc_la_OBJECTS = \ $(am_libsf_modbus_preproc_la_OBJECTS) \ $(nodist_libsf_modbus_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_modbus_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_modbus_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_modbus_preproc_la_SOURCES) \ $(nodist_libsf_modbus_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_modbus_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_modbus_preproc.la libsf_modbus_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_modbus_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_modbus_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c libsf_modbus_preproc_la_SOURCES = spp_modbus.c spp_modbus.h \ modbus_decode.c modbus_decode.h modbus_roptions.c \ modbus_roptions.h modbus_paf.c modbus_paf.h $(am__append_1) EXTRA_DIST = \ sf_modbus.vcxproj \ sf_modbus.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/modbus/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/modbus/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_modbus_preproc.la: $(libsf_modbus_preproc_la_OBJECTS) $(libsf_modbus_preproc_la_DEPENDENCIES) $(EXTRA_libsf_modbus_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_modbus_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_modbus_preproc_la_OBJECTS) $(libsf_modbus_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/modbus/modbus_paf.c0000644000175000017500000001167214241076112021774 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Protocol-Aware Flushing (PAF) code for the Modbus preprocessor. * */ #include "spp_modbus.h" #include "modbus_decode.h" #include "modbus_paf.h" #include "sf_dynamic_preprocessor.h" /* Defines */ #define MODBUS_MIN_HDR_LEN 2 /* Enough for Unit ID + Function */ #define MODBUS_MAX_HDR_LEN 254 /* Max PDU size is 260, 6 bytes already seen */ int ModbusPafRegisterPort (struct _SnortConfig *sc, uint16_t port, tSfPolicyId policy_id) { if (!_dpd.isPafEnabled()) return 0; _dpd.streamAPI->register_paf_port(sc, policy_id, port, 0, ModbusPaf, true); _dpd.streamAPI->register_paf_port(sc, policy_id, port, 1, ModbusPaf, true); return 0; } #ifdef TARGET_BASED int ModbusAddServiceToPaf (struct _SnortConfig *sc, uint16_t service, tSfPolicyId policy_id) { if (!_dpd.isPafEnabled()) return 0; _dpd.streamAPI->register_paf_service(sc, policy_id, service, 0, ModbusPaf, true); _dpd.streamAPI->register_paf_service(sc, policy_id, service, 1, ModbusPaf, true); return 0; } #endif /* Function: ModbusPaf() Purpose: Modbus/TCP PAF callback. Statefully inspects Modbus traffic from the start of a session, Reads up until the length octet is found, then sets a flush point. Arguments: void * - stream5 session pointer void ** - Modbus state tracking structure const uint8_t * - payload data to inspect uint32_t - length of payload data uint32_t - flags to check whether client or server uint32_t * - pointer to set flush point uint32_t * - pointer to set header flush point Returns: PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise */ PAF_Status ModbusPaf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint64_t *flags, uint32_t *fp, uint32_t *fp_eoh) { modbus_paf_data_t *pafdata = *(modbus_paf_data_t **)user; uint32_t bytes_processed = 0; /* Allocate state object if it doesn't exist yet. */ if (pafdata == NULL) { pafdata = calloc(1, sizeof(modbus_paf_data_t)); if (pafdata == NULL) return PAF_ABORT; *user = pafdata; } /* Process this packet 1 byte at a time */ while (bytes_processed < len) { switch (pafdata->state) { /* Skip the Transaction & Protocol IDs */ case MODBUS_PAF_STATE__TRANS_ID_1: case MODBUS_PAF_STATE__TRANS_ID_2: case MODBUS_PAF_STATE__PROTO_ID_1: case MODBUS_PAF_STATE__PROTO_ID_2: pafdata->state++; break; /* Read length 1 byte at a time, in case a TCP segment is sent * with only 5 bytes from the MBAP header */ case MODBUS_PAF_STATE__LENGTH_1: pafdata->modbus_length |= ( *(data + bytes_processed) << 8 ); pafdata->state++; break; case MODBUS_PAF_STATE__LENGTH_2: pafdata->modbus_length |= *(data + bytes_processed); pafdata->state++; break; case MODBUS_PAF_STATE__SET_FLUSH: if ((pafdata->modbus_length < MODBUS_MIN_HDR_LEN) || (pafdata->modbus_length > MODBUS_MAX_HDR_LEN)) { _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_BAD_LENGTH, 1, 0, 3, MODBUS_BAD_LENGTH_STR, 0); } *fp = pafdata->modbus_length + bytes_processed; pafdata->state = MODBUS_PAF_STATE__TRANS_ID_1; pafdata->modbus_length = 0; return PAF_FLUSH; } bytes_processed++; } return PAF_SEARCH; } /* Take a Modbus config + Snort policy, iterate through ports, register PAF callback */ void ModbusAddPortsToPaf(struct _SnortConfig *sc, modbus_config_t *config, tSfPolicyId policy_id) { unsigned int i; for (i = 0; i < MAX_PORTS; i++) { if (config->ports[PORT_INDEX(i)] & CONV_PORT(i)) { ModbusPafRegisterPort(sc, (uint16_t) i, policy_id); } } } snort-2.9.20/src/dynamic-preprocessors/modbus/modbus_decode.c0000644000175000017500000004257314241076102022454 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the Modbus protocol * */ #include "modbus_decode.h" /* Modbus Function Codes */ #define MODBUS_FUNC_READ_COILS 0x01 #define MODBUS_FUNC_READ_DISCRETE_INPUTS 0x02 #define MODBUS_FUNC_READ_HOLDING_REGISTERS 0x03 #define MODBUS_FUNC_READ_INPUT_REGISTERS 0x04 #define MODBUS_FUNC_WRITE_SINGLE_COIL 0x05 #define MODBUS_FUNC_WRITE_SINGLE_REGISTER 0x06 #define MODBUS_FUNC_READ_EXCEPTION_STATUS 0x07 #define MODBUS_FUNC_DIAGNOSTICS 0x08 #define MODBUS_FUNC_GET_COMM_EVENT_COUNTER 0x0B #define MODBUS_FUNC_GET_COMM_EVENT_LOG 0x0C #define MODBUS_FUNC_WRITE_MULTIPLE_COILS 0x0F #define MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS 0x10 #define MODBUS_FUNC_REPORT_SLAVE_ID 0x11 #define MODBUS_FUNC_READ_FILE_RECORD 0x14 #define MODBUS_FUNC_WRITE_FILE_RECORD 0x15 #define MODBUS_FUNC_MASK_WRITE_REGISTER 0x16 #define MODBUS_FUNC_READ_WRITE_MULTIPLE_REGISTERS 0x17 #define MODBUS_FUNC_READ_FIFO_QUEUE 0x18 #define MODBUS_FUNC_ENCAPSULATED_INTERFACE_TRANSPORT 0x2B #define MODBUS_SUB_FUNC_CANOPEN 0x0D #define MODBUS_SUB_FUNC_READ_DEVICE_ID 0x0E /* Various Modbus lengths */ #define MODBUS_BYTE_COUNT_SIZE 1 #define MODBUS_DOUBLE_BYTE_COUNT_SIZE 2 #define MODBUS_FILE_RECORD_SUB_REQUEST_SIZE 7 #define MODBUS_FILE_RECORD_SUB_REQUEST_LEN_OFFSET 5 #define MODBUS_READ_DEVICE_ID_HEADER_LEN 6 #define MODBUS_READ_DEVICE_ID_NUM_OBJ_OFFSET 5 #define MODBUS_EMPTY_DATA_LEN 0 #define MODBUS_FOUR_DATA_BYTES 4 #define MODBUS_BYTE_COUNT_SIZE 1 #define MODBUS_WRITE_MULTIPLE_BYTE_COUNT_OFFSET 4 #define MODBUS_WRITE_MULTIPLE_MIN_SIZE 5 #define MODBUS_MASK_WRITE_REGISTER_SIZE 6 #define MODBUS_READ_WRITE_MULTIPLE_BYTE_COUNT_OFFSET 8 #define MODBUS_READ_WRITE_MULTIPLE_MIN_SIZE 9 #define MODBUS_READ_FIFO_SIZE 2 #define MODBUS_MEI_MIN_SIZE 1 #define MODBUS_FUNC_READ_EXCEPTION_RESP_SIZE 1 #define MODBUS_SUB_FUNC_READ_DEVICE_ID_SIZE 3 #define MODBUS_SUB_FUNC_READ_DEVICE_START_LEN 2 #define MODBUS_SUB_FUNC_READ_DEVICE_LENGTH_OFFSET 1 #ifdef DUMP_BUFFER #include "modbus_buffer_dump.h" #endif /* Other defines */ #define MODBUS_PROTOCOL_ID 0 /* Modbus data structures */ typedef struct _modbus_header { /* MBAP Header */ uint16_t transaction_id; uint16_t protocol_id; uint16_t length; uint8_t unit_id; /* PDU Start */ uint8_t function_code; } modbus_header_t; static void ModbusCheckRequestLengths(modbus_session_data_t *session, SFSnortPacket *packet) { uint16_t modbus_payload_len = packet->payload_size - MODBUS_MIN_LEN; uint8_t byte_count; int check_passed = 0; switch (session->func) { case MODBUS_FUNC_READ_COILS: case MODBUS_FUNC_READ_DISCRETE_INPUTS: case MODBUS_FUNC_READ_HOLDING_REGISTERS: case MODBUS_FUNC_READ_INPUT_REGISTERS: case MODBUS_FUNC_WRITE_SINGLE_COIL: case MODBUS_FUNC_WRITE_SINGLE_REGISTER: case MODBUS_FUNC_DIAGNOSTICS: if (modbus_payload_len == MODBUS_FOUR_DATA_BYTES) check_passed = 1; break; case MODBUS_FUNC_READ_EXCEPTION_STATUS: case MODBUS_FUNC_GET_COMM_EVENT_COUNTER: case MODBUS_FUNC_GET_COMM_EVENT_LOG: case MODBUS_FUNC_REPORT_SLAVE_ID: if (modbus_payload_len == MODBUS_EMPTY_DATA_LEN) check_passed = 1; break; case MODBUS_FUNC_WRITE_MULTIPLE_COILS: case MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS: if (modbus_payload_len >= MODBUS_WRITE_MULTIPLE_MIN_SIZE) { byte_count = *(packet->payload + MODBUS_MIN_LEN + MODBUS_WRITE_MULTIPLE_BYTE_COUNT_OFFSET); if (modbus_payload_len == byte_count + MODBUS_WRITE_MULTIPLE_MIN_SIZE) check_passed = 1; } break; case MODBUS_FUNC_MASK_WRITE_REGISTER: if (modbus_payload_len == MODBUS_MASK_WRITE_REGISTER_SIZE) check_passed = 1; break; case MODBUS_FUNC_READ_WRITE_MULTIPLE_REGISTERS: if (modbus_payload_len >= MODBUS_READ_WRITE_MULTIPLE_MIN_SIZE) { byte_count = *(packet->payload + MODBUS_MIN_LEN + MODBUS_READ_WRITE_MULTIPLE_BYTE_COUNT_OFFSET); if (modbus_payload_len == MODBUS_READ_WRITE_MULTIPLE_MIN_SIZE + byte_count) check_passed = 1; } break; case MODBUS_FUNC_READ_FIFO_QUEUE: if (modbus_payload_len == MODBUS_READ_FIFO_SIZE) check_passed = 1; break; case MODBUS_FUNC_ENCAPSULATED_INTERFACE_TRANSPORT: if (modbus_payload_len >= MODBUS_MEI_MIN_SIZE) { uint8_t mei_type = *(packet->payload + MODBUS_MIN_LEN); /* MEI Type 0x0E is covered under the Modbus spec as "Read Device Identification". Type 0x0D is defined in the spec as "CANopen General Reference Request and Response PDU" and falls outside the scope of the Modbus preprocessor. Other values are reserved. */ if ((mei_type == MODBUS_SUB_FUNC_READ_DEVICE_ID) && (modbus_payload_len == MODBUS_SUB_FUNC_READ_DEVICE_ID_SIZE)) check_passed = 1; } break; case MODBUS_FUNC_READ_FILE_RECORD: /* Modbus read file record request contains a byte count, followed by a set of 7-byte sub-requests. */ if (modbus_payload_len >= MODBUS_BYTE_COUNT_SIZE) { byte_count = *(packet->payload + MODBUS_MIN_LEN); if ((byte_count == modbus_payload_len - MODBUS_BYTE_COUNT_SIZE) && (byte_count % MODBUS_FILE_RECORD_SUB_REQUEST_SIZE == 0)) { check_passed = 1; } } break; case MODBUS_FUNC_WRITE_FILE_RECORD: /* Modbus write file record request contains a byte count, followed by a set of sub-requests that contain a 7-byte header and a variable amount of data. */ if (modbus_payload_len >= MODBUS_BYTE_COUNT_SIZE) { byte_count = *(packet->payload + MODBUS_MIN_LEN); if (byte_count == modbus_payload_len - MODBUS_BYTE_COUNT_SIZE) { uint16_t bytes_processed = 0; while (bytes_processed < (uint16_t)byte_count) { /* Check space for sub-request header info */ if ((modbus_payload_len - bytes_processed) < MODBUS_FILE_RECORD_SUB_REQUEST_SIZE) break; /* Extract record length. Sub-request record length is * 2 bytes, but shouldn't be bigger than byte_count * (request total data length) which is uint8_t. */ const uint8_t extra_byte = *(packet->payload + MODBUS_MIN_LEN + MODBUS_BYTE_COUNT_SIZE + bytes_processed + MODBUS_FILE_RECORD_SUB_REQUEST_LEN_OFFSET); if (extra_byte != 0) break; const uint8_t record_length = *(packet->payload + MODBUS_MIN_LEN + MODBUS_BYTE_COUNT_SIZE + bytes_processed + MODBUS_FILE_RECORD_SUB_REQUEST_LEN_OFFSET + 1); /* Jump over record data. record_length is in words, multiplied by 2.*/ bytes_processed += MODBUS_FILE_RECORD_SUB_REQUEST_SIZE + 2*(uint16_t)record_length; if (bytes_processed == (uint16_t)byte_count) check_passed = 1; } } } break; default: /* Don't alert if we couldn't check the length. */ check_passed = 1; break; } if (!check_passed) { _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_BAD_LENGTH, 1, 0, 3, MODBUS_BAD_LENGTH_STR, 0); } } static void ModbusCheckResponseLengths(modbus_session_data_t *session, SFSnortPacket *packet) { uint16_t modbus_payload_len = packet->payload_size - MODBUS_MIN_LEN; uint8_t byte_count; int check_passed = 0; switch (session->func) { case MODBUS_FUNC_READ_COILS: case MODBUS_FUNC_READ_DISCRETE_INPUTS: case MODBUS_FUNC_GET_COMM_EVENT_LOG: case MODBUS_FUNC_READ_WRITE_MULTIPLE_REGISTERS: if (modbus_payload_len >= MODBUS_BYTE_COUNT_SIZE) { byte_count = *(packet->payload + MODBUS_MIN_LEN); if (modbus_payload_len == MODBUS_BYTE_COUNT_SIZE + byte_count) check_passed = 1; } break; case MODBUS_FUNC_READ_HOLDING_REGISTERS: case MODBUS_FUNC_READ_INPUT_REGISTERS: if (modbus_payload_len >= MODBUS_BYTE_COUNT_SIZE) { byte_count = *(packet->payload + MODBUS_MIN_LEN); if (modbus_payload_len == MODBUS_BYTE_COUNT_SIZE + byte_count) check_passed = 1; } break; case MODBUS_FUNC_WRITE_SINGLE_COIL: case MODBUS_FUNC_WRITE_SINGLE_REGISTER: case MODBUS_FUNC_DIAGNOSTICS: case MODBUS_FUNC_GET_COMM_EVENT_COUNTER: case MODBUS_FUNC_WRITE_MULTIPLE_COILS: case MODBUS_FUNC_WRITE_MULTIPLE_REGISTERS: if (modbus_payload_len == MODBUS_FOUR_DATA_BYTES) check_passed = 1; break; case MODBUS_FUNC_READ_EXCEPTION_STATUS: if (modbus_payload_len == MODBUS_FUNC_READ_EXCEPTION_RESP_SIZE) check_passed = 1; break; case MODBUS_FUNC_MASK_WRITE_REGISTER: if (modbus_payload_len == MODBUS_MASK_WRITE_REGISTER_SIZE) check_passed = 1; break; case MODBUS_FUNC_READ_FIFO_QUEUE: if (modbus_payload_len >= MODBUS_DOUBLE_BYTE_COUNT_SIZE) { uint16_t byte_count_16; /* This function uses a 2-byte byte count!! */ byte_count_16 = *(uint16_t *)(packet->payload + MODBUS_MIN_LEN); byte_count_16 = ntohs(byte_count_16); if (modbus_payload_len == MODBUS_DOUBLE_BYTE_COUNT_SIZE + byte_count_16) check_passed = 1; } break; case MODBUS_FUNC_ENCAPSULATED_INTERFACE_TRANSPORT: if (modbus_payload_len >= MODBUS_READ_DEVICE_ID_HEADER_LEN) { uint8_t mei_type = *(packet->payload + MODBUS_MIN_LEN); uint8_t num_objects = *(packet->payload + MODBUS_MIN_LEN + MODBUS_READ_DEVICE_ID_NUM_OBJ_OFFSET); uint16_t offset; uint8_t i; /* MEI Type 0x0E is covered under the Modbus spec as "Read Device Identification". Type 0x0D is defined in the spec as "CANopen General Reference Request and Response PDU" and falls outside the scope of the Modbus preprocessor. Other values are reserved. */ if (mei_type == MODBUS_SUB_FUNC_CANOPEN) check_passed = 1; if (mei_type != MODBUS_SUB_FUNC_READ_DEVICE_ID) break; /* Loop through sub-requests, make sure that the lengths inside don't violate our total Modbus PDU size. */ offset = MODBUS_READ_DEVICE_ID_HEADER_LEN; for (i = 0; i < num_objects; i++) { uint8_t sub_request_data_len; /* Sub request starts with 2 bytes, type + len */ if (offset + MODBUS_SUB_FUNC_READ_DEVICE_START_LEN > modbus_payload_len) break; /* Length is second byte in sub-request */ sub_request_data_len = *(packet->payload + MODBUS_MIN_LEN + offset + MODBUS_SUB_FUNC_READ_DEVICE_LENGTH_OFFSET); /* Set offset to byte after sub-request */ offset += (MODBUS_SUB_FUNC_READ_DEVICE_START_LEN + sub_request_data_len); } if ((i == num_objects) && (offset == modbus_payload_len)) check_passed = 1; } break; /* Cannot check this response, as it is device specific. */ case MODBUS_FUNC_REPORT_SLAVE_ID: /* Cannot check these responses, as their sizes depend on the corresponding requests. Can re-visit if we bother with request/response tracking. */ case MODBUS_FUNC_READ_FILE_RECORD: case MODBUS_FUNC_WRITE_FILE_RECORD: default: /* Don't alert if we couldn't check the lengths. */ check_passed = 1; break; } if (!check_passed) { _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_BAD_LENGTH, 1, 0, 3, MODBUS_BAD_LENGTH_STR, 0); } } static void ModbusCheckReservedFuncs(modbus_header_t *header, SFSnortPacket *packet) { switch (header->function_code) { /* Reserved function codes */ case MODBUS_FUNC_DIAGNOSTICS: /* Only some sub-functions are reserved here. */ { uint16_t sub_func; if (packet->payload_size < MODBUS_MIN_LEN+2) break; sub_func = *((uint16_t *)(packet->payload + MODBUS_MIN_LEN)); sub_func = ntohs(sub_func); if ((sub_func == 19) || (sub_func >= 21)) { _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_RESERVED_FUNCTION, 1, 0, 3, MODBUS_RESERVED_FUNCTION_STR, 0); } } break; case 0x09: case 0x0A: case 0x0D: case 0x0E: case 0x29: case 0x2A: case 0x5A: case 0x5B: case 0x7D: case 0x7E: case 0x7F: _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_RESERVED_FUNCTION, 1, 0, 3, MODBUS_RESERVED_FUNCTION_STR, 0); break; } #ifdef DUMP_BUFFER dumpBuffer(MODBUS_RESERVED_FUN_DUMP,packet->payload,packet->payload_size); #endif } int ModbusDecode(modbus_config_t *config, SFSnortPacket *packet) { modbus_session_data_t *session; modbus_header_t *header; if (packet->payload_size < MODBUS_MIN_LEN) return MODBUS_FAIL; session = (modbus_session_data_t *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_MODBUS); /* Lay the header struct over the payload */ header = (modbus_header_t *) packet->payload; /* The protocol ID field should read 0x0000 for Modbus. It allows for multiplexing with some other protocols over serial line. */ if (header->protocol_id != MODBUS_PROTOCOL_ID) { _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_BAD_PROTO_ID, 1, 0, 3, MODBUS_BAD_PROTO_ID_STR, 0); return MODBUS_FAIL; } /* Set the session data. Normally we'd need to swap byte order, but these are 8-bit fields. */ session->unit = header->unit_id; session->func = header->function_code; /* Check for reserved function codes */ ModbusCheckReservedFuncs(header, packet); /* Read the Modbus payload and check lengths against the expected length for each function. */ if (packet->flags & FLAG_FROM_CLIENT) { ModbusCheckRequestLengths(session, packet); #ifdef DUMP_BUFFER dumpBuffer(MODBUS_CLINET_REQUEST_DUMP,packet->payload,packet->payload_size); #endif } else { ModbusCheckResponseLengths(session, packet); #ifdef DUMP_BUFFER dumpBuffer(MODBUS_SERVER_RESPONSE_DUMP,packet->payload,packet->payload_size); #endif } return MODBUS_OK; } snort-2.9.20/src/dynamic-preprocessors/modbus/sf_modbus.dsp0000444000175000017500000001255414230012554022176 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_modbus" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_modbus - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_modbus.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_modbus.mak" CFG="sf_modbus - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_modbus - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_modbus - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_modbus - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "SF_SNORT_PREPROC_DLL" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_modbus - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "SF_SNORT_PREPROC_DLL" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_modbus - Win32 Release" # Name "sf_modbus - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\modbus_decode.c # End Source File # Begin Source File SOURCE=.\modbus_paf.c # End Source File # Begin Source File SOURCE=.\modbus_roptions.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\spp_modbus.c # End Source File # Begin Source File SOURCE=..\include\strtok_r.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\modbus_decode.h # End Source File # Begin Source File SOURCE=.\modbus_paf.h # End Source File # Begin Source File SOURCE=.\modbus_roptions.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\spp_modbus.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/modbus/modbus_buffer_dump.h0000644000175000017500000000321414241076100023517 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file modbus_buffer_dump.h ** ** @author Krishnakanth ** ** @brief This file contains structures and functions for ** dumping buffers for mobdus protocol */ #ifndef __MODBUS_BUFFER_DUMP_H__ #define __MODBUS_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { MODBUS_SERVER_RESPONSE_DUMP, MODBUS_CLINET_REQUEST_DUMP, MODBUS_RESERVED_FUN_DUMP } MODBUS_BUFFER_DUMP; void dumpBuffer(MODBUS_BUFFER_DUMP type,const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getMODBUSBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/modbus/Makefile.am0000444000175000017500000000162614230012554021537 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_modbus_preproc.la libsf_modbus_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_modbus_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_modbus_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c endif libsf_modbus_preproc_la_SOURCES = \ spp_modbus.c \ spp_modbus.h \ modbus_decode.c \ modbus_decode.h \ modbus_roptions.c \ modbus_roptions.h \ modbus_paf.c \ modbus_paf.h if BUILD_BUFFER_DUMP libsf_modbus_preproc_la_SOURCES += \ modbus_buffer_dump.c \ modbus_buffer_dump.h endif EXTRA_DIST = \ sf_modbus.vcxproj \ sf_modbus.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/modbus/spp_modbus.c0000644000175000017500000005144514241076120022031 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Ryan Jordan * * Dynamic preprocessor for the Modbus protocol * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #include "spp_modbus.h" #include "sf_preproc_info.h" #include "profiler.h" #ifdef PERF_PROFILING PreprocStats modbusPerfStats; #endif #include "sf_types.h" #include "modbus_decode.h" #include "modbus_roptions.h" #include "modbus_paf.h" #ifdef DUMP_BUFFER #include "modbus_buffer_dump.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_MODBUS"; #define SetupModbus DYNAMIC_PREPROC_SETUP /* Preprocessor config objects */ static tSfPolicyUserContextId modbus_context_id = NULL; static modbus_config_t *modbus_eval_config = NULL; /* Target-based app ID */ #ifdef TARGET_BASED int16_t modbus_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif /* Prototypes */ static void ModbusInit(struct _SnortConfig *, char *); static inline void ModbusOneTimeInit(struct _SnortConfig *); static inline modbus_config_t * ModbusPerPolicyInit(struct _SnortConfig *, tSfPolicyUserContextId); static void ProcessModbus(void *, void *); #ifdef SNORT_RELOAD static void ModbusReload(struct _SnortConfig *, char *, void **); static int ModbusReloadVerify(struct _SnortConfig *, void *); static void * ModbusReloadSwap(struct _SnortConfig *, void *); static void ModbusReloadSwapFree(void *); #endif static void registerPortsForDispatch( struct _SnortConfig *sc, modbus_config_t *policy ); static void registerPortsForReassembly( modbus_config_t *policy, int direction ); static void _addPortsToStreamFilter(struct _SnortConfig *, modbus_config_t *, tSfPolicyId); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static void ModbusFreeConfig(tSfPolicyUserContextId context_id); static void FreeModbusData(void *); static int ModbusCheckConfig(struct _SnortConfig *); static void ModbusCleanExit(int, void *); static void ParseModbusArgs(modbus_config_t *config, char *args); static void ModbusPrintConfig(modbus_config_t *config); static int ModbusPortCheck(modbus_config_t *config, SFSnortPacket *packet); static modbus_session_data_t * ModbusCreateSessionData(SFSnortPacket *); /* Register init callback */ void SetupModbus(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc("modbus", ModbusInit); #else _dpd.registerPreproc("modbus", ModbusInit, ModbusReload, ModbusReloadVerify, ModbusReloadSwap, ModbusReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getMODBUSBuffers, MODBUS_BUFFER_DUMP_FUNC); #endif } #ifdef REG_TEST static inline void PrintMODBUSSize(void) { _dpd.logMsg("\nMODBUS Session Size: %lu\n", (long unsigned int)sizeof(modbus_session_data_t)); } #endif /* Allocate memory for preprocessor config, parse the args, set up callbacks */ static void ModbusInit(struct _SnortConfig *sc, char *argp) { modbus_config_t *modbus_policy = NULL; #ifdef REG_TEST PrintMODBUSSize(); #endif if (modbus_context_id == NULL) { ModbusOneTimeInit(sc); } modbus_policy = ModbusPerPolicyInit(sc, modbus_context_id); ParseModbusArgs(modbus_policy, argp); /* Can't add ports until they've been parsed... */ ModbusAddPortsToPaf(sc, modbus_policy, _dpd.getParserPolicy(sc)); #ifdef TARGET_BASED ModbusAddServiceToPaf(sc, modbus_app_id, _dpd.getParserPolicy(sc)); #endif // register ports with session and stream registerPortsForDispatch( sc, modbus_policy ); registerPortsForReassembly( modbus_policy, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); ModbusPrintConfig(modbus_policy); #ifdef DUMP_BUFFER dumpBufferInit(); #endif } static inline void ModbusOneTimeInit(struct _SnortConfig *sc) { /* context creation & error checking */ modbus_context_id = sfPolicyConfigCreate(); if (modbus_context_id == NULL) { _dpd.fatalMsg("%s(%d) Failed to allocate memory for " "Modbus config.\n", *_dpd.config_file, *_dpd.config_line); } if (_dpd.streamAPI == NULL) { _dpd.fatalMsg("%s(%d) SetupModbus(): The Stream preprocessor " "must be enabled.\n", *_dpd.config_file, *_dpd.config_line); } /* callback registration */ _dpd.addPreprocConfCheck(sc, ModbusCheckConfig); _dpd.addPreprocExit(ModbusCleanExit, NULL, PRIORITY_LAST, PP_MODBUS); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("modbus", (void *)&modbusPerfStats, 0, _dpd.totalPerfStats, NULL); #endif /* Set up target-based app id */ #ifdef TARGET_BASED modbus_app_id = _dpd.findProtocolReference("modbus"); if (modbus_app_id == SFTARGET_UNKNOWN_PROTOCOL) modbus_app_id = _dpd.addProtocolReference("modbus"); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_MODBUS, modbus_app_id ); #endif } /* Responsible for allocating a Modbus policy. Never returns NULL. */ static inline modbus_config_t * ModbusPerPolicyInit(struct _SnortConfig *sc, tSfPolicyUserContextId context_id) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); modbus_config_t *modbus_policy = NULL; /* Check for existing policy & bail if found */ sfPolicyUserPolicySet(context_id, policy_id); modbus_policy = (modbus_config_t *)sfPolicyUserDataGetCurrent(context_id); if (modbus_policy != NULL) { _dpd.fatalMsg("%s(%d) Modbus preprocessor can only be " "configured once.\n", *_dpd.config_file, *_dpd.config_line); } /* Allocate new policy */ modbus_policy = (modbus_config_t *)calloc(1, sizeof(modbus_config_t)); if (!modbus_policy) { _dpd.fatalMsg("%s(%d) Could not allocate memory for " "modbus preprocessor configuration.\n" , *_dpd.config_file, *_dpd.config_line); } sfPolicyUserDataSetCurrent(context_id, modbus_policy); /* Register callbacks that are done for each policy */ _dpd.addPreproc(sc, ProcessModbus, PRIORITY_APPLICATION, PP_MODBUS, PROTO_BIT__TCP); _addPortsToStreamFilter(sc, modbus_policy, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif /* Add preprocessor rule options here */ /* _dpd.preprocOptRegister("foo_bar", FOO_init, FOO_rule_eval, free, NULL, NULL, NULL, NULL); */ _dpd.preprocOptRegister(sc, "modbus_func", ModbusFuncInit, ModbusRuleEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, "modbus_unit", ModbusUnitInit, ModbusRuleEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, "modbus_data", ModbusDataInit, ModbusRuleEval, free, NULL, NULL, NULL, NULL); return modbus_policy; } static void ParseSinglePort(modbus_config_t *config, char *token) { /* single port number */ char *endptr; unsigned long portnum = _dpd.SnortStrtoul(token, &endptr, 10); if ((*endptr != '\0') || (portnum >= MAX_PORTS)) { _dpd.fatalMsg("%s(%d) Bad modbus port number: %s\n" "Port number must be an integer between 0 and 65535.\n", *_dpd.config_file, *_dpd.config_line, token); } /* Good port number! */ config->ports[PORT_INDEX(portnum)] |= CONV_PORT(portnum); } static void ParseModbusArgs(modbus_config_t *config, char *args) { char *saveptr; char *token; /* Set default port */ config->ports[PORT_INDEX(MODBUS_PORT)] |= CONV_PORT(MODBUS_PORT); /* No args? Stick to the default. */ if (args == NULL) return; token = strtok_r(args, " ", &saveptr); while (token != NULL) { if (strcmp(token, "ports") == 0) { unsigned nPorts = 0; /* Un-set the default port */ config->ports[PORT_INDEX(MODBUS_PORT)] = 0; /* Parse ports */ token = strtok_r(NULL, " ", &saveptr); if (token == NULL) { _dpd.fatalMsg("%s(%d) Missing argument for Modbus preprocessor " "'ports' option.\n", *_dpd.config_file, *_dpd.config_line); } if (isdigit(token[0])) { ParseSinglePort(config, token); nPorts++; } else if (*token == '{') { /* list of ports */ token = strtok_r(NULL, " ", &saveptr); while (token != NULL && *token != '}') { ParseSinglePort(config, token); nPorts++; token = strtok_r(NULL, " ", &saveptr); } } else { nPorts = 0; } if ( nPorts == 0 ) { _dpd.fatalMsg("%s(%d) Bad Modbus 'ports' argument: '%s'\n" "Argument to Modbus 'ports' must be an integer, or a list " "enclosed in { } braces.\n", *_dpd.config_file, *_dpd.config_line, token); } } else { _dpd.fatalMsg("%s(%d) Failed to parse modbus argument: %s\n", *_dpd.config_file, *_dpd.config_line, token); } token = strtok_r(NULL, " ", &saveptr); } } /* Print a Modbus config */ static void ModbusPrintConfig(modbus_config_t *config) { int index; int newline = 1; if (config == NULL) return; _dpd.logMsg("Modbus config: \n"); _dpd.logMsg(" Ports:\n"); /* Loop through port array & print, 5 ports per line */ for (index = 0; index < MAX_PORTS; index++) { if (config->ports[PORT_INDEX(index)] & CONV_PORT(index)) { _dpd.logMsg("\t%d", index); if ( !((newline++) % 5) ) { _dpd.logMsg("\n"); } } } _dpd.logMsg("\n"); } /* Main runtime entry point */ static void ProcessModbus(void *ipacketp, void *contextp) { SFSnortPacket *packetp = (SFSnortPacket *)ipacketp; modbus_session_data_t *sessp; PROFILE_VARS; // preconditions - what we registered for assert(IsTCP(packetp) && packetp->payload && packetp->payload_size); PREPROC_PROFILE_START(modbusPerfStats); /* Fetch me a preprocessor config to use with this VLAN/subnet/etc.! */ modbus_eval_config = sfPolicyUserDataGetCurrent(modbus_context_id); /* Look for a previously-allocated session data. */ sessp = _dpd.sessionAPI->get_application_data(packetp->stream_session, PP_MODBUS); if (sessp == NULL) { /* No existing session. Check those ports. */ if (ModbusPortCheck(modbus_eval_config, packetp) != MODBUS_OK) { PREPROC_PROFILE_END(modbusPerfStats); return; } } if ( !PacketHasFullPDU(packetp) && ModbusIsPafActive(packetp) ) { if ( sessp ) { sessp->unit = 0; sessp->func = 0; } /* If a packet is rebuilt, but not a full PDU, then it's garbage that got flushed at the end of a stream. */ if ( packetp->flags & (FLAG_REBUILT_STREAM|FLAG_PDU_HEAD) ) { _dpd.alertAdd(GENERATOR_SPP_MODBUS, MODBUS_BAD_LENGTH, 1, 0, 3, MODBUS_BAD_LENGTH_STR, 0); } PREPROC_PROFILE_END(modbusPerfStats); return; } if (sessp == NULL) { /* Create session data and attach it to the Stream session */ sessp = ModbusCreateSessionData(packetp); if ( !sessp ) { PREPROC_PROFILE_END(modbusPerfStats); return; } } /* When pipelined Modbus PDUs appear in a single TCP segment, the detection engine caches the results of the rule options after evaluating on the first PDU. Setting this flag stops the caching. */ packetp->flags |= FLAG_ALLOW_MULTIPLE_DETECT; /* Do preprocessor-specific detection stuff here */ if (ModbusDecode(modbus_eval_config, packetp) == MODBUS_FAIL) { sessp->unit = 0; sessp->func = 0; } /* That's the end! */ PREPROC_PROFILE_END(modbusPerfStats); } /* Check ports & services */ static int ModbusPortCheck(modbus_config_t *config, SFSnortPacket *packet) { #ifdef TARGET_BASED int16_t app_id = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session); /* call to get_application_protocol_id gave an error */ if (app_id == SFTARGET_UNKNOWN_PROTOCOL) return MODBUS_FAIL; /* this is positively identified as something non-modbus */ if (app_id && (app_id != modbus_app_id)) return MODBUS_FAIL; /* this is identified as modbus */ if (app_id == modbus_app_id) return MODBUS_OK; /* fall back to port check */ #endif if (config->ports[PORT_INDEX(packet->src_port)] & CONV_PORT(packet->src_port)) return MODBUS_OK; if (config->ports[PORT_INDEX(packet->dst_port)] & CONV_PORT(packet->dst_port)) return MODBUS_OK; return MODBUS_FAIL; } static modbus_session_data_t * ModbusCreateSessionData(SFSnortPacket *packet) { modbus_session_data_t *data = NULL; /* Sanity Check */ if (!packet || !packet->stream_session) return NULL; data = (modbus_session_data_t *)calloc(1, sizeof(modbus_session_data_t)); if (!data) return NULL; /* Attach to Stream session */ _dpd.sessionAPI->set_application_data(packet->stream_session, PP_MODBUS, data, FreeModbusData); /* Not sure when this reference counting stuff got added to the old preprocs */ data->policy_id = _dpd.getNapRuntimePolicy(); data->context_id = modbus_context_id; ((modbus_config_t *)sfPolicyUserDataGetCurrent(modbus_context_id))->ref_count++; return data; } /* Reload functions */ #ifdef SNORT_RELOAD /* Almost like ModbusInit, but not quite. */ static void ModbusReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId modbus_swap_context_id = (tSfPolicyUserContextId)*new_config; modbus_config_t *modbus_policy = NULL; if (modbus_swap_context_id == NULL) { modbus_swap_context_id = sfPolicyConfigCreate(); if (modbus_swap_context_id == NULL) { _dpd.fatalMsg("Failed to allocate memory " "for Modbus config.\n"); } if (_dpd.streamAPI == NULL) { _dpd.fatalMsg("SetupModbus(): The Stream preprocessor " "must be enabled.\n"); } *new_config = (void *)modbus_swap_context_id; } modbus_policy = ModbusPerPolicyInit(sc, modbus_swap_context_id); ParseModbusArgs(modbus_policy, args); /* Can't add ports until they've been parsed... */ ModbusAddPortsToPaf(sc, modbus_policy, _dpd.getParserPolicy(sc)); ModbusPrintConfig(modbus_policy); } static int ModbusReloadVerify(struct _SnortConfig *sc, void *swap_config) { if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("SetupModbus(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } static int ModbusFreeUnusedConfigPolicy( tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { modbus_config_t *modbus_config = (modbus_config_t *)data; /* do any housekeeping before freeing modbus config */ if (modbus_config->ref_count == 0) { sfPolicyUserDataClear(context_id, policy_id); free(modbus_config); } return 0; } static void * ModbusReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId modbus_swap_context_id = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_context_id = modbus_context_id; if (modbus_swap_context_id == NULL) return NULL; modbus_context_id = modbus_swap_context_id; sfPolicyUserDataFreeIterate(old_context_id, ModbusFreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_context_id) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_context_id; } return NULL; } static void ModbusReloadSwapFree(void *data) { if (data == NULL) return; ModbusFreeConfig( (tSfPolicyUserContextId)data ); } #endif static void registerPortsForDispatch( struct _SnortConfig *sc, modbus_config_t *policy ) { uint32_t port; for ( port = 0; port < MAX_PORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_MODBUS, PROTO_BIT__TCP, port ); } } static void registerPortsForReassembly( modbus_config_t *policy, int direction ) { uint32_t port; for ( port = 0; port < MAX_PORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.streamAPI->register_reassembly_port( NULL, port, direction ); } } /* Stream filter functions */ static void _addPortsToStreamFilter(struct _SnortConfig *sc, modbus_config_t *config, tSfPolicyId policy_id) { if (config == NULL) return; if (_dpd.streamAPI) { int portNum; for (portNum = 0; portNum < MAX_PORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status( sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1 ); } } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, modbus_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif static int ModbusFreeConfigPolicy( tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { modbus_config_t *modbus_config = (modbus_config_t *)data; /* do any housekeeping before freeing modbus_config */ sfPolicyUserDataClear(context_id, policy_id); free(modbus_config); return 0; } static void ModbusFreeConfig(tSfPolicyUserContextId context_id) { if (context_id == NULL) return; sfPolicyUserDataFreeIterate(context_id, ModbusFreeConfigPolicy); sfPolicyConfigDelete(context_id); } static int ModbusCheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { _dpd.setParserPolicy(sc, policy_id); if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("%s(%d) ModbusCheckPolicyConfig(): The Stream preprocessor " "must be enabled.\n", *_dpd.config_file, *_dpd.config_line); return -1; } return 0; } static int ModbusCheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate(sc, modbus_context_id, ModbusCheckPolicyConfig))) return rval; return 0; } static void ModbusCleanExit(int signal, void *data) { if (modbus_context_id != NULL) { ModbusFreeConfig(modbus_context_id); modbus_context_id = NULL; } } static void FreeModbusData(void *data) { modbus_session_data_t *session = (modbus_session_data_t *)data; modbus_config_t *config = NULL; if (session == NULL) return; if (session->context_id != NULL) { config = (modbus_config_t *)sfPolicyUserDataGet(session->context_id, session->policy_id); } if (config != NULL) { config->ref_count--; if ((config->ref_count == 0) && (session->context_id != modbus_context_id)) { sfPolicyUserDataClear(session->context_id, session->policy_id); free(config); if (sfPolicyUserPolicyGetActive(session->context_id) == 0) { /* No more outstanding configs - free the config array */ ModbusFreeConfig(session->context_id); } } } free(session); } snort-2.9.20/src/dynamic-preprocessors/modbus/sf_modbus.vcxproj0000444000175000017500000004117614230012554023105 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {442239F3-EB5E-4499-9078-CA1EE69D0055} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Debug\ .\Debug\ true true .\Release\ .\Release\ false false .\Release\ .\Release\ MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_modbus.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_modbus.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_modbus.bsc true true true Console .\Debug\sf_modbus.dll .\Debug\sf_modbus.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_modbus.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_modbus.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_modbus.bsc true true true Console .\Debug\sf_modbus.dll .\Debug\sf_modbus.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_modbus.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_modbus.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_modbus.bsc true true Console .\Release\sf_modbus.dll .\Release\sf_modbus.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_modbus.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_modbus.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_modbus.bsc true true Console .\Release\sf_modbus.dll .\Release\sf_modbus.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/Makefile.am0000444000175000017500000006602514232707762020270 0ustar apoapo## $Id$ AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I${top_builddir}/src/dynamic-preprocessors/include -I${top_builddir}/src/dynamic-preprocessors/ssl_common -I${top_srcdir}/src/dynamic-preprocessors/libs -I${top_builddir} if SO_WITH_STATIC_LIB preproclibdir=$(pkglibdir)/dynamic_preproc preproclib_LTLIBRARIES = libsf_dynamic_preproc.la libsf_dynamic_preproc_la_CFLAGS = -fPIC -DPIC -DDYNAMIC_PREPROC_CONTEXT libsf_dynamic_preproc_la_LDFLAGS = -static libsf_dynamic_preproc_la_SOURCES = \ ssl_common/ssl.c \ ssl_common/ssl_config.c \ ssl_common/ssl_inspect.c \ ssl_common/ssl_ha.c nodist_libsf_dynamic_preproc_la_SOURCES = \ include/sf_dynamic_preproc_lib.c \ include/sf_ip.c \ include/sfrt.c \ include/sfrt_dir.c \ include/sfrt_flat.c \ include/sfrt_flat_dir.c \ include/segment_mem.c \ include/mempool.c \ include/sf_sdlist.c \ include/sfPolicyUserData.c \ include/util_unfold.c \ include/sf_base64decode.c \ include/sf_email_attach_decode.c \ include/reg_test.c \ libs/sfparser.c preprocdir=$(pkgincludedir)/dynamic_preproc preproc_HEADERS = \ ssl_common/ssl.h \ ssl_common/ssl_include.h \ ssl_common/ssl_session.h \ ssl_common/ssl_config.h \ ssl_common/ssl_ha.h \ ssl_common/ssl_inspect.h nodist_preproc_HEADERS = \ libs/sfcommon.h \ libs/sf_preproc_info.h \ include/sf_snort_packet.h \ include/sf_protocols.h \ include/sf_snort_plugin_api.h \ include/sf_decompression.h \ include/sf_decompression_define.h \ include/sfPolicyUserData.h \ include/snort_debug.h \ include/snort_bounds.h \ include/cpuclock.h \ include/profiler.h \ include/bitop.h \ include/mempool.h \ include/sf_sdlist_types.h \ include/sf_ip.h \ include/sfrt_flat.h \ include/sfrt_flat_dir.h \ include/segment_mem.h \ include/sf_dynamic_common.h \ include/sf_dynamic_engine.h \ include/sf_dynamic_define.h \ include/sf_dynamic_meta.h \ include/sf_dynamic_preprocessor.h \ include/sf_dynamic_preproc_lib.h \ include/ipv6_port.h \ include/sfPolicy.h \ include/sfrt.h \ include/sfrt_dir.h \ include/sfrt_trie.h \ include/obfuscation.h \ include/packet_time.h \ include/session_api.h \ include/stream_api.h \ include/str_search.h \ include/preprocids.h \ include/sfcontrol.h \ include/sidechannel_define.h \ include/idle_processing.h \ include/sf_seqnums.h \ include/perf_indicators.h \ include/mpse_methods.h \ include/file_api.h \ include/reload_api.h \ include/smtp_api.h \ include/reg_test.h \ include/memory_stats.h preproclib_LTLIBRARIES += libsf_dynamic_utils.la libsf_dynamic_utils_la_CFLAGS = -fPIC -DPIC -DDYNAMIC_PREPROC_CONTEXT libsf_dynamic_utils_la_LDFLAGS = -static if FEAT_OPEN_APPID nodist_libsf_dynamic_utils_la_SOURCES = include/sfprimetable.c include/sfxhash.c include/sfmemcap.c include/sfmemcap.h \ include/sfghash.c include/sfhashfcn.c include/sflsq.c include/md5.c nodist_preproc_HEADERS += include/appId.h include/appIdApi.h include/thirdparty_appid_types.h \ include/thirdparty_appid_api.h include/dns_defs.h else nodist_libsf_dynamic_utils_la_SOURCES = include/sfmemcap.c include/sfmemcap.h endif if BUILD_SNORT_RELOAD #appdata_adjuster nodist_libsf_dynamic_utils_la_SOURCES += include/appdata_adjuster.c include/sfxhash.c include/sfhashfcn.c include/sfmemcap.c include/sfprimetable.c include/reg_test.h include/reg_test.c nodist_preproc_HEADERS += include/appdata_adjuster.h endif all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/build install-preproclibLTLIBRARIES endif BUILT_SOURCES = \ include/snort_bounds.h \ include/snort_debug.h \ include/preprocids.h \ include/profiler.h \ include/cpuclock.h \ include/sf_dynamic_common.h \ include/sf_dynamic_engine.h \ include/sf_dynamic_define.h \ include/sf_dynamic_meta.h \ include/sf_dynamic_preprocessor.h \ include/sf_dynamic_preproc_lib.c \ include/sf_dynamic_preproc_lib.h \ include/sfghash.h \ include/sfhashfcn.h \ include/bitop.h \ include/sf_ip.h \ include/sf_ip.c \ include/sf_ipvar.h \ include/sf_vartable.h \ include/ipv6_port.h \ include/sfsnort_dynamic_detection_lib.c \ include/sfsnort_dynamic_detection_lib.h \ include/sf_snort_packet.h \ include/sf_protocols.h \ include/sf_snort_plugin_api.h \ include/sf_decompression.h \ include/sf_decompression_define.h \ include/pcap_pkthdr32.h \ include/session_api.h \ include/stream_api.h \ include/str_search.h \ include/sf_types.h \ include/sfrt.h \ include/sfrt.c \ include/sfrt_dir.h \ include/sfrt_dir.c \ include/sfrt_flat.h \ include/sfrt_flat.c \ include/sfrt_flat_dir.h \ include/sfrt_flat_dir.c \ include/sfrt_trie.h \ include/segment_mem.h \ include/segment_mem.c \ include/mempool.h \ include/mempool.c \ include/sfmemcap.h \ include/sfmemcap.c \ include/sf_sdlist.h \ include/sf_sdlist_types.h \ include/sf_sdlist.c \ include/sfPolicyUserData.c \ include/sfPolicyUserData.h \ include/sfPolicy.h \ include/util_unfold.h \ include/util_unfold.c \ include/sf_base64decode.h \ include/sf_base64decode.c \ include/sf_email_attach_decode.h \ include/sf_email_attach_decode.c \ include/treenodes.h \ include/signature.h \ include/plugin_enum.h \ include/obfuscation.h \ include/packet_time.h \ include/rule_option_types.h \ include/event.h \ include/Unified2_common.h \ include/sfcontrol.h \ include/sidechannel_define.h \ include/idle_processing.h \ include/sf_seqnums.h \ include/perf_indicators.h \ include/file_api.h \ include/file_mail_common.h \ include/mpse_methods.h \ include/sfdebug.h \ include/sip_common.h \ include/cip_common.h \ include/reload_api.h \ include/smtp_api.h \ include/reg_test.h \ include/reg_test.c \ ssl_common/ssl.h \ ssl_common/ssl.c \ ssl_common/ssl_include.h \ ssl_common/ssl_config.h \ ssl_common/ssl_config.c \ ssl_common/ssl_session.h \ ssl_common/ssl_inspect.h \ ssl_common/ssl_inspect.c \ ssl_common/ssl_ha.h \ ssl_common/ssl_ha.c \ libs/sfparser.c \ libs/sfcommon.h \ include/memory_stats.h if FEAT_OPEN_APPID BUILT_SOURCES += include/appId.h include/appIdApi.h include/thirdparty_appid_types.h \ include/thirdparty_appid_api.h \ include/sfprimetable.h include/sfprimetable.c include/sfxhash.h \ include/sfxhash.c \ include/sfghash.c include/sfhashfcn.c include/sflsq.h include/sflsq.c \ include/md5.h include/md5.c \ include/dns_defs.h endif if BUILD_SNORT_RELOAD BUILT_SOURCES += include/sfprimetable.h \ include/sfprimetable.c \ include/sfmemcap.h \ include/sfmemcap.c \ include/sfhashfcn.h \ include/sfhashfcn.c \ include/sfxhash.h \ include/sfxhash.c \ include/appdata_adjuster.h \ include/appdata_adjuster.c endif sed_ipv6_headers = \ sed -e "s/->iph->ip_src/->ip4_header->source/" \ -e "s/->iph->ip_dst/->ip4_header->destination/" \ -e "s/->iph->/->ip4_header->/" \ -e "s/->iph$$/->ip4_header/" \ -e "s/orig_iph/orig_ipv4h/" \ -e "s/ip_verhl/version_headerlength/" \ -e "s/ip_tos/type_service/" \ -e "s/ip_len/data_length/" \ -e "s/ip_id/identifier/" \ -e "s/ip_off/offset/" \ -e "s/ip_ttl/time_to_live/" \ -e "s/ip_proto/proto/" \ -e "s/ip_csum/checksum/" \ $$dst_header.new > $$dst_header massage_ipv6_headers = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_ipv6_headers); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_ipv6_headers); \ fi sed_headers = \ sed -e "s/Packet /SFSnortPacket /" \ -e "s/SnortPktHdr /SFSnortPktHdr /" \ -e "s/decode\.h/sf_snort_packet.h/" \ -e "/sfportobject\.h/d" \ -e "s/PortObject \*/void */g" \ $$dst_header.new > $$dst_header massage_headers = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_headers); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_headers); \ fi sed_debug_header = \ sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" \ -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" \ -e "s/; DebugMessageFunc$$/; _dpd.debugMsg/" \ -e "s/; DebugWideMessageFunc$$/; _dpd.debugWideMsg/" \ $$dst_header.new > $$dst_header copy_debug_header = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_debug_header); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_debug_header); \ fi copy_error_message = \ if test -f $$dst_header; then \ sed -e "s/ErrorMessage/_dpd.errMsg/" \ -e "s/LogMessage/_dpd.logMsg/" \ -e "s/FatalError/_dpd.fatalMsg/" \ -e "/util.h/d" \ -e "/snort.h/d" \ $$dst_header > $$dst_header.new; \ mv -f $$dst_header.new $$dst_header; \ fi copy_no_static_hash = \ if test -f $$dst_header; then \ echo "Updating " $$dst_header; \ sed -e "s/\#ifndef MODULUS_HASH/\#ifdef STATIC_HASH/" \ $$dst_header > $$dst_header.new; \ mv -f $$dst_header.new $$dst_header; \ fi replace_policy_globals = \ if test -f $$dst_header; then \ sed -e "/SharedObjectAddStarts/d" \ -e "/SharedObjectAddEnds/d" \ -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" \ -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" \ -e "s/SnortStrnStr/_dpd.SnortStrnStr/" \ -e "s/SnortStrncpy/_dpd.SnortStrncpy/" \ -e "s/ReloadAdjustRegister/_dpd.reloadAdjustRegister/" \ -e "s/session_api/_dpd.sessionAPI/" \ $$dst_header > $$dst_header.new; \ mv -f $$dst_header.new $$dst_header; \ fi copy_headers = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header; \ fi sed_treenode_header = \ sed -f $(srcdir)/treenodes.sed $$dst_header.new > $$dst_header copy_treenode_header = \ mkdir -p include; \ mkdir -p build; \ if test -f $$dst_header; then \ x=`diff $$src_header $$dst_header.new.new >> /dev/null`; \ if test "$$x" != "0"; then \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_treenode_header); \ fi \ else \ echo "Updating " $$dst_header; \ cp $$src_header $$dst_header.new; \ $(sed_treenode_header); \ fi # From main src tree include/snort_debug.h: $(srcdir)/../snort_debug.h @src_header=$?; dst_header=$@; $(copy_debug_header) include/preprocids.h: $(srcdir)/../preprocids.h @src_header=$?; dst_header=$@; $(copy_headers) include/profiler.h: $(srcdir)/../profiler.h @src_header=$?; dst_header=$@; $(copy_headers) include/cpuclock.h: $(srcdir)/../cpuclock.h @src_header=$?; dst_header=$@; $(copy_headers) include/pcap_pkthdr32.h: $(srcdir)/../pcap_pkthdr32.h @src_header=$?; dst_header=$@; $(copy_headers) include/snort_bounds.h: $(srcdir)/../snort_bounds.h @src_header=$?; dst_header=$@; $(copy_headers) include/ipv6_port.h: $(srcdir)/../ipv6_port.h @src_header=$?; dst_header=$@; $(massage_ipv6_headers) include/sf_types.h: $(srcdir)/../sf_types.h @src_header=$?; dst_header=$@; $(copy_headers) include/obfuscation.h: $(srcdir)/../obfuscation.h @src_header=$?; dst_header=$@; $(massage_headers) include/packet_time.h: $(srcdir)/../packet_time.h @src_header=$?; dst_header=$@; $(massage_headers) include/rule_option_types.h: $(srcdir)/../rule_option_types.h @src_header=$?; dst_header=$@; $(copy_headers) include/event.h: $(srcdir)/../event.h @src_header=$?; dst_header=$@; $(copy_headers) include/sidechannel_define.h: $(srcdir)/../side-channel/sidechannel_define.h @src_header=$?; dst_header=$@; $(massage_headers) include/reload_api.h: $(srcdir)/../reload_api.h @src_header=$?; dst_header=$@; $(massage_headers) include/smtp_api.h: $(srcdir)/../smtp_api.h @src_header=$?; dst_header=$@; $(massage_headers) include/reg_test.h: $(srcdir)/../reg_test.h @src_header=$?; dst_header=$@; $(copy_headers) include/reg_test.c: $(srcdir)/../reg_test.c @src_header=$?; dst_header=$@; $(copy_headers) # From dynamic-plugins include/sf_dynamic_common.h: $(srcdir)/../dynamic-plugins/sf_dynamic_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_engine.h: $(srcdir)/../dynamic-plugins/sf_dynamic_engine.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_define.h: $(srcdir)/../dynamic-plugins/sf_dynamic_define.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_meta.h: $(srcdir)/../dynamic-plugins/sf_dynamic_meta.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_preprocessor.h: $(srcdir)/../dynamic-plugins/sf_dynamic_preprocessor.h @src_header=$?; dst_header=$@; $(massage_headers) # From dynamic-plugins/sf_preproc_example include/sf_dynamic_preproc_lib.c: $(srcdir)/../dynamic-plugins/sf_preproc_example/sf_dynamic_preproc_lib.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_dynamic_preproc_lib.h: $(srcdir)/../dynamic-plugins/sf_preproc_example/sf_dynamic_preproc_lib.h @src_header=$?; dst_header=$@; $(copy_headers) # From Utils include/sfghash.h: $(srcdir)/../sfutil/sfghash.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfhashfcn.h: $(srcdir)/../sfutil/sfhashfcn.h @src_header=$?; dst_header=$@; $(copy_headers) include/bitop.h: $(srcdir)/../sfutil/bitop.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_ip.h: $(srcdir)/../sfutil/sf_ip.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_ip.c: $(srcdir)/../sfutil/sf_ip.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_ipvar.h: $(srcdir)/../sfutil/sf_ipvar.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_vartable.h: $(srcdir)/../sfutil/sf_vartable.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt.h: $(srcdir)/../sfutil/sfrt.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt.c: $(srcdir)/../sfutil/sfrt.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_dir.h: $(srcdir)/../sfutil/sfrt_dir.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_dir.c: $(srcdir)/../sfutil/sfrt_dir.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat.h: $(srcdir)/../sfutil/sfrt_flat.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat.c: $(srcdir)/../sfutil/sfrt_flat.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat_dir.h: $(srcdir)/../sfutil/sfrt_flat_dir.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_flat_dir.c: $(srcdir)/../sfutil/sfrt_flat_dir.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfrt_trie.h: $(srcdir)/../sfutil/sfrt_trie.h @src_header=$?; dst_header=$@; $(copy_headers) include/segment_mem.c: $(srcdir)/../sfutil/segment_mem.c @src_header=$?; dst_header=$@; $(copy_headers) include/segment_mem.h: $(srcdir)/../sfutil/segment_mem.h @src_header=$?; dst_header=$@; $(copy_headers) include/mempool.h: $(srcdir)/../mempool.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/mempool.c: $(srcdir)/../mempool.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfmemcap.h: $(srcdir)/../sfutil/sfmemcap.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfmemcap.c: $(srcdir)/../sfutil/sfmemcap.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sf_sdlist.h: $(srcdir)/../sf_sdlist.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sf_sdlist_types.h: $(srcdir)/../sf_sdlist_types.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sf_sdlist.c: $(srcdir)/../sf_sdlist.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfPolicyUserData.c: $(srcdir)/../sfutil/sfPolicyUserData.c @src_header=$?; dst_header=$@; $(copy_headers); $(replace_policy_globals) include/sfPolicyUserData.h: $(srcdir)/../sfutil/sfPolicyUserData.h @src_header=$?; dst_header=$@; $(copy_headers); $(replace_policy_globals) include/sfPolicy.h: $(srcdir)/../sfutil/sfPolicy.h @src_header=$?; dst_header=$@; $(copy_headers); $(replace_policy_globals) include/util_unfold.h: $(srcdir)/../sfutil/util_unfold.h @src_header=$?; dst_header=$@; $(copy_headers) include/util_unfold.c: $(srcdir)/../sfutil/util_unfold.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_base64decode.h: $(srcdir)/../sfutil/sf_base64decode.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_base64decode.c: $(srcdir)/../sfutil/sf_base64decode.c @src_header=$?; dst_header=$@; $(copy_headers) include/sf_email_attach_decode.h: $(srcdir)/../sfutil/sf_email_attach_decode.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_email_attach_decode.c: $(srcdir)/../sfutil/sf_email_attach_decode.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/Unified2_common.h: $(srcdir)/../sfutil/Unified2_common.h @src_header=$?; dst_header=$@; $(copy_headers) # From dynamic-plugins/sf_engine/examples include/sfsnort_dynamic_detection_lib.c: $(srcdir)/../dynamic-plugins/sf_engine/examples/sfsnort_dynamic_detection_lib.c @src_header=$?; dst_header=$@; $(copy_headers) include/sfsnort_dynamic_detection_lib.h: $(srcdir)/../dynamic-plugins/sf_engine/examples/sfsnort_dynamic_detection_lib.h @src_header=$?; dst_header=$@; $(copy_headers) # From dynamic-plugins/sf_engine include/sf_snort_packet.h: $(srcdir)/../dynamic-plugins/sf_engine/sf_snort_packet.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_protocols.h: $(srcdir)/../sf_protocols.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_snort_plugin_api.h: $(srcdir)/../dynamic-plugins/sf_engine/sf_snort_plugin_api.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_decompression.h: $(srcdir)/../dynamic-plugins/sf_engine/sf_decompression.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_decompression_define.h: $(srcdir)/../dynamic-plugins/sf_decompression_define.h @src_header=$?; dst_header=$@; $(copy_headers) # Session API/String Searching, massage it to use SFSnortPacket include/session_api.h: $(srcdir)/../preprocessors/session_api.h @src_header=$?; dst_header=$@; $(massage_headers) # Stream API/String Searching, massage it to use SFSnortPacket include/stream_api.h: $(srcdir)/../preprocessors/stream_api.h @src_header=$?; dst_header=$@; $(massage_headers) include/str_search.h: $(srcdir)/../preprocessors/str_search.h @src_header=$?; dst_header=$@; $(massage_headers) include/treenodes.h: $(srcdir)/../treenodes.h @src_header=$?; dst_header=$@; $(copy_treenode_header) include/signature.h: $(srcdir)/../signature.h @src_header=$?; dst_header=$@; $(copy_treenode_header) include/plugin_enum.h: $(srcdir)/../plugin_enum.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfcontrol.h: $(top_srcdir)/src/control/sfcontrol.h @src_header=$?; dst_header=$@; $(copy_headers) include/idle_processing.h: $(top_srcdir)/src/idle_processing.h @src_header=$?; dst_header=$@; $(copy_headers) include/sf_seqnums.h: $(top_srcdir)/src/sfutil/sf_seqnums.h @src_header=$?; dst_header=$@; $(copy_headers) include/perf_indicators.h: $(srcdir)/../preprocessors/perf_indicators.h @src_header=$?; dst_header=$@; $(copy_headers) include/file_api.h: $(top_srcdir)/src/file-process/file_api.h @src_header=$?; dst_header=$@; $(copy_headers) include/file_mail_common.h: $(top_srcdir)/src/file-process/file_mail_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfdebug.h: $(srcdir)/../sfutil/sfdebug.h @src_header=$?; dst_header=$@; $(copy_headers) include/mpse_methods.h: $(srcdir)/../sfutil/mpse_methods.h @src_header=$?; dst_header=$@; $(copy_headers) include/sip_common.h: $(srcdir)/../preprocessors/sip_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/cip_common.h: $(srcdir)/../preprocessors/cip_common.h @src_header=$?; dst_header=$@; $(copy_headers) include/appId.h: $(srcdir)/appid/appId.h @src_header=$?; dst_header=$@; $(copy_headers) if FEAT_OPEN_APPID include/appIdApi.h: $(srcdir)/../appIdApi.h @src_header=$?; dst_header=$@; $(copy_headers) include/thirdparty_appid_types.h: $(srcdir)/appid/thirdparty_appid_types.h @src_header=$?; dst_header=$@; $(copy_headers) include/thirdparty_appid_api.h: $(srcdir)/appid/thirdparty_appid_api.h @src_header=$?; dst_header=$@; $(copy_headers) include/dns_defs.h: $(srcdir)/appid/dns_defs.h @src_header=$?; dst_header=$@; $(copy_headers) include/md5.c: $(srcdir)/../sfutil/md5.c @src_header=$?; dst_header=$@; $(copy_headers) include/md5.h: $(srcdir)/../sfutil/md5.h @src_header=$?; dst_header=$@; $(copy_headers) include/sfprimetable.h: $(srcdir)/../sfutil/sfprimetable.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfprimetable.c: $(srcdir)/../sfutil/sfprimetable.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfxhash.h: $(srcdir)/../sfutil/sfxhash.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfxhash.c: $(srcdir)/../sfutil/sfxhash.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfghash.c: $(srcdir)/../sfutil/sfghash.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfhashfcn.c: $(srcdir)/../sfutil/sfhashfcn.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals); $(copy_no_static_hash) include/sflsq.h: $(srcdir)/../sfutil/sflsq.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sflsq.c: $(srcdir)/../sfutil/sflsq.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) else if BUILD_SNORT_RELOAD include/sfxhash.h: $(srcdir)/../sfutil/sfxhash.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfxhash.c: $(srcdir)/../sfutil/sfxhash.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfprimetable.h: $(srcdir)/../sfutil/sfprimetable.h @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfprimetable.c: $(srcdir)/../sfutil/sfprimetable.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/sfhashfcn.c: $(srcdir)/../sfutil/sfhashfcn.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals); $(copy_no_static_hash) endif endif if BUILD_SNORT_RELOAD include/appdata_adjuster.c: $(srcdir)/../reload-adjust/appdata_adjuster.c @src_header=$?; dst_header=$@; $(copy_headers); $(copy_error_message); $(replace_policy_globals) include/appdata_adjuster.h: $(srcdir)/../reload-adjust/appdata_adjuster.h @src_header=$?; dst_header=$@; $(copy_headers) endif include/memory_stats.h: $(srcdir)/../memory_stats.h @src_header=$?; dst_header=$@; $(copy_headers) if FEAT_FILE_INSPECT FILE_INSPECT_DIR = file endif SUBDIRS = . libs ftptelnet pop imap smtp ssh dns ssl dcerpc2 sdf sip reputation gtp modbus dnp3 s7commplus $(FILE_INSPECT_DIR) if FEAT_OPEN_APPID SUBDIRS += appid endif clean-local: rm -rf include build EXTRA_DIST = \ dynamic_preprocessors.vcxproj \ dynamic_preprocessors.dsp \ sf_dynamic_initialize/sf_dynamic_initialize.vcxproj \ sf_dynamic_initialize/sf_dynamic_initialize.dsp \ treenodes.sed srcinstdir = $(exec_prefix)/src/snort_dynamicsrc exported_files = \ include/sf_dynamic_common.h \ include/sf_dynamic_define.h \ include/sf_dynamic_engine.h \ include/sf_dynamic_meta.h \ include/sf_dynamic_preprocessor.h \ include/sf_dynamic_preproc_lib.h \ include/sf_dynamic_preproc_lib.c \ include/sf_ip.h \ include/sf_snort_packet.h \ include/sf_protocols.h \ include/sf_snort_plugin_api.h \ include/sf_decompression.h \ include/sf_decompression_define.h \ include/sf_types.h \ include/sfsnort_dynamic_detection_lib.h \ include/sfsnort_dynamic_detection_lib.c \ include/pcap_pkthdr32.h \ include/str_search.h \ include/session_api.h \ include/stream_api.h \ include/snort_debug.h \ include/profiler.h \ include/sfghash.h \ include/sfhashfcn.h \ include/sfmemcap.h \ include/bitop.h \ include/preprocids.h \ include/sfPolicyUserData.h \ include/util_unfold.h \ include/util_unfold.c \ include/sf_base64decode.h \ include/sf_base64decode.c \ include/sf_email_attach_decode.h \ include/sf_email_attach_decode.c \ include/treenodes.h \ include/signature.h \ include/plugin_enum.h \ include/sfPolicyUserData.c \ include/obfuscation.h \ include/sidechannel_define.h \ include/rule_option_types.h \ include/event.h \ include/Unified2_common.h \ include/sfcontrol.h \ include/idle_processing.h \ include/sf_seqnums.h \ include/perf_indicators.h \ include/file_api.h \ include/file_mail_common.h \ include/mpse_methods.h \ include/sfdebug.h \ include/sip_common.h \ include/cip_common.h \ include/reload_api.h \ include/reg_test.h \ include/reg_test.c \ ssl_common/ssl.h \ ssl_common/ssl.c \ ssl_common/ssl_include.h \ ssl_common/ssl_config.h \ ssl_common/ssl_config.c \ ssl_common/ssl_session.h \ ssl_common/ssl_inspect.h \ ssl_common/ssl_inspect.c \ ssl_common/ssl_ha.h \ ssl_common/ssl_ha.c \ libs/sfparser.c \ include/memory_stats.h if FEAT_OPEN_APPID exported_files += include/appId.h include/appIdApi.h include/thirdparty_appid_types.h \ include/thirdparty_appid_api.h \ include/sfprimetable.h include/sfxhash.h include/sfhashfcn.h \ include/md5.h \ include/dns_defs.h endif if BUILD_SNORT_RELOAD exported_files += include/sfprimetable.h \ include/sfprimetable.c \ include/sfmemcap.h \ include/sfmemcap.c \ include/sfhashfcn.h \ include/sfhashfcn.c \ include/sfxhash.h \ include/sfxhash.c \ include/appdata_adjuster.h \ include/appdata_adjuster.c endif install-data-local: @for f in $(exported_files); do \ ## Compute the filename only truefile=`echo $$f | sed -e "s/.*\///"`; \ ## Make the install directory. $(mkinstalldirs) $(DESTDIR)$(srcinstdir); \ ## Find the header file -- in our case it might be in srcdir or ## it might be in the build directory. "p" is the variable that ## names the actual file we will install. if test -f $(srcdir)/$$f; then p=$(srcdir)/$$f; else p=$$f; fi; \ ## Actually install the file. $(INSTALL_DATA) $$p $(DESTDIR)$(srcinstdir)/$$truefile; \ done uninstall-local: @for f in $(exported_files); do \ ## Compute the filename only truefile=`echo $$f | sed -e "s/.*\///"`; \ ## Make the install directory. $(mkinstalldirs) $(DESTDIR)$(srcinstdir); \ ## Actually install the file. rm -f $(DESTDIR)$(srcinstdir)/$$truefile; \ done snort-2.9.20/src/dynamic-preprocessors/libs/0000755000175000017500000000000014242725715017155 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/libs/sf_preproc_info.h0000644000175000017500000000264314241076064022503 0ustar apoapo/* * sf_preproc_info.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file is part of the dynamically loadable preprocessor library. The * items must be globally defined within the source file of a given * preprocessor. * * Author: Steven A. Sturges * * NOTES: * */ #ifndef SF_PREPROC_INFO_H_ #define SF_PREPROC_INFO_H_ extern const int MAJOR_VERSION; extern const int MINOR_VERSION; extern const int BUILD_VERSION; extern const char *PREPROC_NAME; extern void DYNAMIC_PREPROC_SETUP(void); #endif /* SF_PREPROC_INFO_H_ */ snort-2.9.20/src/dynamic-preprocessors/libs/sfcommon.h0000644000175000017500000000333114241076066021144 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef DYN_PP_PARSER_H #define DYN_PP_PARSER_H #include "snort_bounds.h" #include "snort_debug.h" #define SFP_MIN_ERR_STR 128 /* Convert port value into an index */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) typedef enum _SFP_ret { SFP_SUCCESS, SFP_ERROR } SFP_ret_t; typedef uint8_t ports_tbl_t[MAXPORTS/8]; typedef char SFP_errstr_t[SFP_MIN_ERR_STR + 1]; static inline char *SFP_GET_ERR(SFP_errstr_t err) { return (char*)err; } SFP_ret_t SFP_ports(ports_tbl_t ports, char *str, SFP_errstr_t errstr); SFP_ret_t SFP_snprintfa(char *buf, size_t buf_size, const char *format, ...); #endif snort-2.9.20/src/dynamic-preprocessors/libs/sfdynamic_preproc_libs.dsp0000555000175000017500000000731514230012554024377 0ustar apoapo# Microsoft Developer Studio Project File - Name="sfdynamic_preproc_libs" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Static Library" 0x0104 CFG=sfdynamic_preproc_libs - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sfdynamic_preproc_libs.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sfdynamic_preproc_libs.mak" CFG="sfdynamic_preproc_libs - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sfdynamic_preproc_libs - Win32 Release" (based on "Win32 (x86) Static Library") !MESSAGE "sfdynamic_preproc_libs - Win32 Debug" (based on "Win32 (x86) Static Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "sfdynamic_preproc_libs - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\include" /I "..\..\win32\Win32-Includes" /I "..\..\\" /I ".\\" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "_LIB" /D "SF_SNORT_PREPROC_DLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ACTIVE_RESPONSE" /D "_AFXDLL" /FD /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LIB32=link.exe -lib # ADD BASE LIB32 /nologo # ADD LIB32 /nologo !ELSEIF "$(CFG)" == "sfdynamic_preproc_libs - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\\" /I "..\include" /I "..\..\win32\Win32-Includes" /I "..\..\\" /I ".\\" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "_LIB" /D "SF_SNORT_PREPROC_DLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ACTIVE_RESPONSE" /D "_AFXDLL" /FD /GZ /c # SUBTRACT CPP /YX # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LIB32=link.exe -lib # ADD BASE LIB32 /nologo # ADD LIB32 /nologo !ENDIF # Begin Target # Name "sfdynamic_preproc_libs - Win32 Release" # Name "sfdynamic_preproc_libs - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\sfparser.c # End Source File # Begin Source File SOURCE="..\..\win32\WIN32-Code\strtok_r.c" # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\sfcommon.h # End Source File # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/libs/sfdynamic_preproc_libs.vcxproj0000444000175000017500000003266114230012554025303 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {3B06C0CA-3CC0-4514-9737-69A5D41E610D} 10.0.17763.0 Application v141 Application v141 StaticLibrary v141 Dynamic MultiByte StaticLibrary v141 Dynamic MultiByte StaticLibrary v141 Dynamic MultiByte StaticLibrary v141 Dynamic MultiByte .\Debug\ .\Debug\ true true .\Release\ .\Release\ false false .\Release\ .\Release\ MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue ..\;..\include;..\..\win32\Win32-Includes;..\..\;.\;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;_LIB;SF_SNORT_PREPROC_DLL;WIN32;HAVE_CONFIG_H;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ACTIVE_RESPONSE;%(PreprocessorDefinitions) .\Debug\ .\Debug\sfdynamic_preproc_libs.pch .\Debug\ .\Debug\ EnableFastChecks 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sfdynamic_preproc_libs.bsc true .\Debug\sfdynamic_preproc_libs.lib MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase ..\;..\include;..\..\win32\Win32-Includes;..\..\;.\;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;_LIB;SF_SNORT_PREPROC_DLL;WIN32;HAVE_CONFIG_H;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ACTIVE_RESPONSE;%(PreprocessorDefinitions) .\Debug\ .\Debug\sfdynamic_preproc_libs.pch .\Debug\ .\Debug\ EnableFastChecks 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sfdynamic_preproc_libs.bsc true .\Debug\sfdynamic_preproc_libs.lib MultiThreadedDLL Default true true MaxSpeed true Level3 ..\include;..\..\win32\Win32-Includes;..\..\;.\;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;_LIB;SF_SNORT_PREPROC_DLL;WIN32;HAVE_CONFIG_H;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ACTIVE_RESPONSE;%(PreprocessorDefinitions) .\Release\ .\Release\sfdynamic_preproc_libs.pch .\Release\ .\Release\ 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sfdynamic_preproc_libs.bsc true .\Release\sfdynamic_preproc_libs.lib MultiThreadedDLL Default true true MaxSpeed true Level3 ..\include;..\..\win32\Win32-Includes;..\..\;.\;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;_LIB;SF_SNORT_PREPROC_DLL;WIN32;HAVE_CONFIG_H;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ACTIVE_RESPONSE;%(PreprocessorDefinitions) .\Release\ .\Release\sfdynamic_preproc_libs.pch .\Release\ .\Release\ 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sfdynamic_preproc_libs.bsc true .\Release\sfdynamic_preproc_libs.lib {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/libs/Makefile.in0000644000175000017500000003570714242725546021240 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/dynamic-preprocessors/libs ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = snort_preproc.pc CONFIG_CLEAN_VPATH_FILES = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = depcomp = am__maybe_remake_depfiles = SOURCES = DIST_SOURCES = am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(pkgconfigdir)" DATA = $(pkgconfig_DATA) am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/snort_preproc.pc.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies EXTRA_DIST = \ sfdynamic_preproc_libs.vcxproj \ sfdynamic_preproc_libs.dsp \ sfparser.c \ sfcommon.h \ sf_preproc_info.h \ snort_preproc.pc.in @SO_WITH_STATIC_LIB_TRUE@pkgconfigdir = $(libdir)/pkgconfig @SO_WITH_STATIC_LIB_TRUE@pkgconfig_DATA = snort_preproc.pc all: all-am .SUFFIXES: $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/libs/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/libs/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): snort_preproc.pc: $(top_builddir)/config.status $(srcdir)/snort_preproc.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ done | $(am__base_list) | \ while read files; do \ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ done uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) tags TAGS: ctags CTAGS: cscope cscopelist: distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(DATA) installdirs: for dir in "$(DESTDIR)$(pkgconfigdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-generic dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-pkgconfigDATA install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-generic mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-pkgconfigDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ cscopelist-am ctags-am distclean distclean-generic \ distclean-libtool distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-pkgconfigDATA install-ps \ install-ps-am install-strip installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ ps ps-am tags-am uninstall uninstall-am \ uninstall-pkgconfigDATA .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/libs/Makefile.am0000444000175000017500000000043614230012554021175 0ustar apoapoAUTOMAKE_OPTIONS=foreign no-dependencies EXTRA_DIST = \ sfdynamic_preproc_libs.vcxproj \ sfdynamic_preproc_libs.dsp \ sfparser.c \ sfcommon.h \ sf_preproc_info.h \ snort_preproc.pc.in if SO_WITH_STATIC_LIB pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = snort_preproc.pc endif snort-2.9.20/src/dynamic-preprocessors/libs/sfparser.c0000644000175000017500000001046514241076075021151 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include "sf_types.h" #include "sfcommon.h" #include "ctype.h" #define SET_ERR(x,y) \ if(errstr && \ snprintf((char*)errstr, SFP_MIN_ERR_STR, x, y) >= \ SFP_MIN_ERR_STR) \ { \ /* tok exceeded errstr. Overwrite trailing characters for \ * printability */ \ strcpy(((char*)errstr) + SFP_MIN_ERR_STR-4, "..."); \ } #define CLR_ERR() ((char*)errstr)[0] = 0; SFP_ret_t SFP_ports(ports_tbl_t port_tbl, char *str, SFP_errstr_t errstr) { char *tok; char *saveptr; char end_brace_found = 0; char port_found = 0; if(!str) { SET_ERR("%s", "Invalid pointer"); return SFP_ERROR; } if((tok = strtok_r(str, " ", &saveptr)) == NULL) { SET_ERR("%s", "No ports specified"); return SFP_ERROR; } /* This string had better start with a '{' and end with a '}', or else! */ if(strcmp(tok, "{")) { SET_ERR("Malformed port list: %s. Expecting a leading '{ '", tok); return SFP_ERROR; } while((tok = strtok_r(NULL, " ", &saveptr)) != NULL) { char *port_end; long int port; str = NULL; if(end_brace_found) { SET_ERR("Last character of a port list must be '}': %s", tok); return SFP_ERROR; } if(!strcmp(tok, "}")) { end_brace_found = 1; continue; } errno = 0; port = strtol(tok, &port_end, 10); if((port_end == tok) || (*port_end && *port_end != '}') || (errno == ERANGE)) { SET_ERR("Unable to parse: %s", tok); return SFP_ERROR; } if(port < 0 || port > MAXPORTS-1) { SET_ERR("Port out of range: %s", tok); return SFP_ERROR; } port_tbl[ PORT_INDEX(port) ] |= CONV_PORT(port); port_found = 1; } if(!end_brace_found) { SET_ERR("%s", "No end brace found"); return SFP_ERROR; } if(!port_found) { SET_ERR("%s", "No ports specified"); return SFP_ERROR; } CLR_ERR(); return SFP_SUCCESS; } SFP_ret_t SFP_snprintfa(char *buf, size_t buf_size, const char *format, ...) { size_t str_len; int ret; va_list ap; if (buf == NULL || buf_size <= 0 || format == NULL) return SFP_ERROR; /* equivalent of a strlen that stops at buf_size */ for(str_len = 0; (str_len < buf_size) && buf[str_len]; str_len++) ; /* Note: this is from the original SnortSnprintfAppend */ /* "since we've already checked buf and buf_size an error * indicates no null termination, so just start at * beginning of buffer" */ if(str_len >= buf_size) { buf[0] = '\0'; str_len = 0; } buf[buf_size - 1] = '\0'; va_start(ap, format); ret = vsnprintf(buf + str_len, buf_size - str_len, format, ap); va_end(ap); if (ret < 0) return SFP_ERROR; if (buf[buf_size - 1] != '\0' || (size_t)ret >= buf_size) { /* truncation occured */ buf[buf_size - 1] = '\0'; return SFP_ERROR; } return SFP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/libs/snort_preproc.pc.in0000444000175000017500000000070114230012554022764 0ustar apoapoprefix=@prefix@ exec_prefix=@exec_prefix@ bindir=@bindir@ libdir=@libdir@ package=@PACKAGE@ includedir=@includedir@ datarootdir=@datarootdir@ datadir=@datadir@ mandir=@infodir@ infodir=@infodir@ Name: Snort Description: Snort dynamic preprocessors URL: www.snort.org Version: @VERSION@ Libs: -L${libdir}/${package}/dynamic_preproc -lsf_dynamic_preproc Cflags: -I${includedir}/${package}/dynamic_preproc @CONFIGFLAGS@ @CCONFIGFLAGS@ @ICONFIGFLAGS@ snort-2.9.20/src/dynamic-preprocessors/s7commplus/0000755000175000017500000000000014242725717020337 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/s7commplus/s7comm_decode.c0000644000175000017500000001101614241076232023200 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Dynamic preprocessor for the S7comm protocol * */ /* * This is the encapsulation of S7comm/S7comm-plus protocol: * Ethernet | IP | TCP (server port 102) | TPKT | COTP | S7comm or S7comm-plus */ #include "s7comm_decode.h" /* TPKT header */ typedef struct _tpkt_header { uint8_t version; uint8_t reserved; uint16_t length; } tpkt_header_t; /* COTP header */ typedef struct _cotp_header { uint8_t length; uint8_t pdu_type; uint8_t tpdu_num; } cotp_header_t; /* S7commplus data structures */ typedef struct _s7commplus_header { uint8_t proto_id; uint8_t proto_version; uint16_t data_len; } s7commplus_header_t; #pragma pack(1) typedef struct _s7commplus_data_hdr { uint8_t opcode; uint16_t reserved_1; uint16_t function; uint16_t reserved_2; } s7commplus_data_hdr_t; #pragma pack() static int S7commplusProtocolDecode(s7commplus_session_data_t *session, SFSnortPacket *packet) { const s7commplus_header_t* s7commplus_header; const s7commplus_data_hdr_t* s7commplus_data_header; int offset; offset = sizeof(tpkt_header_t) + sizeof(cotp_header_t); s7commplus_header = (const s7commplus_header_t*)(packet->payload + offset); /* Set the session data. Swap byte order for 16-bit fields. */ session->s7commplus_proto_id = s7commplus_header->proto_id; session->s7commplus_proto_version= s7commplus_header->proto_version; session->s7commplus_data_len = ntohs(s7commplus_header->data_len); /* V1 or V2 header packets */ if (s7commplus_header->proto_version <= 0x02) offset += sizeof(s7commplus_header_t); else { /* 33 byte Integrity part for V3 header packets */ offset += sizeof(s7commplus_header_t) + INTEGRITY_PART_LEN ; } s7commplus_data_header = (const s7commplus_data_hdr_t *)(packet->payload + offset); /* Set the session data. Swap byte order for 16-bit fields. */ session->s7commplus_opcode = s7commplus_data_header->opcode; session->s7commplus_reserved_1 = ntohs(s7commplus_data_header->reserved_1); session->s7commplus_function = ntohs(s7commplus_data_header->function); session->s7commplus_reserved_2 = ntohs(s7commplus_data_header->reserved_2); return true; } int S7commplusDecode(s7commplus_config_t *config, SFSnortPacket *packet) { s7commplus_session_data_t *session; const tpkt_header_t *tpkt_header; const cotp_header_t *cotp_header; const s7commplus_header_t *s7commplus_header; uint16_t tpkt_length; if (packet->payload_size < TPKT_MIN_HDR_LEN) { memset(&session, 0, sizeof(session)); return false; } session = (s7commplus_session_data_t *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_S7COMMPLUS); session->s7commplus_proto_id = 0; /* Lay the header struct over the payload */ tpkt_header = (const tpkt_header_t *) packet->payload; cotp_header = (const cotp_header_t *) (packet->payload + sizeof(tpkt_header_t)); tpkt_length = ntohs(tpkt_header->length); /* It might be COTP fragment data */ if ((tpkt_length == TPKT_MIN_HDR_LEN) || (tpkt_length == TPKT_MIN_DATA_HDR_LEN)) { memset(&session, 0, sizeof(session)); return true; } /* It might be a TPKT/COTP packet for other purpose, e.g. connect */ if (cotp_header->length != COTP_HDR_LEN_FOR_S7COMMPLUS || cotp_header->pdu_type != COTP_HDR_PDU_TYPE_DATA) { memset(&session, 0, sizeof(session)); return true; } s7commplus_header = (const s7commplus_header_t *)(packet->payload + sizeof(tpkt_header_t) + sizeof(cotp_header_t)); if (s7commplus_header->proto_id == S7COMMPLUS_PROTOCOL_ID) { return (S7commplusProtocolDecode(session, packet)); } else { _dpd.alertAdd(GENERATOR_SPP_S7COMMPLUS, S7COMMPLUS_BAD_PROTO_ID, 1, 0, 3, S7COMMPLUS_BAD_PROTO_ID_STR, 0); return false; } } snort-2.9.20/src/dynamic-preprocessors/s7commplus/s7comm_decode.h0000644000175000017500000000420514241076233023210 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Dynamic preprocessor for the S7commplus protocol * */ #ifndef S7COMM_DECODE_H #define S7COMM_DECODE_H #include #include /* For memset */ #include "spp_s7comm.h" #include "sf_snort_plugin_api.h" /* GIDs, SIDs, and Strings */ #define GENERATOR_SPP_S7COMMPLUS 149 /* matches generators.h */ /* S7Commplus defines */ #define S7COMMPLUS_PROTOCOL_ID 0x72 #define S7COMMPLUS_PDUTYPE_CONNECT 0x01 #define S7COMMPLUS_PDUTYPE_DATA 0x02 #define S7COMMPLUS_PDUTYPE_DATAFW1_5 0x03 #define S7COMMPLUS_PDUTYPE_KEEPALIVE 0xFF #define COTP_HDR_LEN_FOR_S7COMMPLUS 2 #define COTP_HDR_PDU_TYPE_DATA 0xF0 #define S7COMMPLUS_BAD_LENGTH 1 #define S7COMMPLUS_BAD_PROTO_ID 2 #define S7COMMPLUS_RESERVED_FUNCTION 3 #define S7COMMPLUS_BAD_LENGTH_STR "(spp_s7commplus): Length in S7commplus header does not match the length needed for the given S7comm function." #define S7COMMPLUS_BAD_PROTO_ID_STR "(spp_s7commplus): S7commplus protocol ID is non-zero." #define S7COMMPLUS_RESERVED_FUNCTION_STR "(spp_s7commplus): Reserved S7commplus function code in use." int S7commplusDecode(s7commplus_config_t *config, SFSnortPacket *packet); #endif /* S7COMM_DECODE_H */ snort-2.9.20/src/dynamic-preprocessors/s7commplus/Makefile.in0000644000175000017500000005260514242725546022414 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/dynamic-preprocessors/s7commplus ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_s7commplus_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am_libsf_s7commplus_preproc_la_OBJECTS = spp_s7comm.lo \ s7comm_decode.lo s7comm_roptions.lo s7comm_paf.lo @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_s7commplus_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_s7commplus_preproc_la_OBJECTS = \ $(am_libsf_s7commplus_preproc_la_OBJECTS) \ $(nodist_libsf_s7commplus_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_s7commplus_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_s7commplus_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_s7commplus_preproc_la_SOURCES) \ $(nodist_libsf_s7commplus_preproc_la_SOURCES) DIST_SOURCES = $(libsf_s7commplus_preproc_la_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_s7commplus_preproc.la libsf_s7commplus_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_s7commplus_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_s7commplus_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c libsf_s7commplus_preproc_la_SOURCES = \ spp_s7comm.c \ spp_s7comm.h \ s7comm_decode.c \ s7comm_decode.h \ s7comm_roptions.c \ s7comm_roptions.h \ s7comm_paf.c \ s7comm_paf.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/s7commplus/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/s7commplus/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_s7commplus_preproc.la: $(libsf_s7commplus_preproc_la_OBJECTS) $(libsf_s7commplus_preproc_la_DEPENDENCIES) $(EXTRA_libsf_s7commplus_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_s7commplus_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_s7commplus_preproc_la_OBJECTS) $(libsf_s7commplus_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile #EXTRA_DIST = \ #sf_s7comm.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/s7commplus/s7comm_roptions.h0000644000175000017500000000402214241076246023643 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Rule options for S7commplus preprocessor. * */ #ifndef S7COMM_ROPTIONS_H #define S7COMM_ROPTIONS_H #include #define S7COMMPLUS_OPCODE_NAME "s7commplus_opcode" #define S7COMMPLUS_FUNC_NAME "s7commplus_func" #define S7COMMPLUS_CONTENT_NAME "s7commplus_content" /* Data types */ typedef enum _s7commplus_option_type_t { S7COMMPLUS_OPCODE = 0, S7COMMPLUS_FUNC, S7COMMPLUS_CONTENT } s7commplus_option_type_t; typedef struct _s7commplus_option_data_t { s7commplus_option_type_t type; uint16_t arg; } s7commplus_option_data_t; typedef struct _s7commplus_opcode_map_t { char *name; uint8_t opcode; } s7commplus_opcode_map_t; typedef struct _s7commplus_func_map_t { char *name; uint16_t func; } s7commplus_func_map_t; int S7commplusOpcodeInit(struct _SnortConfig *sc, char *name, char *params, void **data); int S7commplusFuncInit(struct _SnortConfig *sc, char *name, char *params, void **data); int S7commplusContentInit(struct _SnortConfig *sc, char *name, char *params, void **data); int S7commplusRuleEval(void *raw_packet, const uint8_t **cursor, void *data); #endif /* S7COMM_ROPTIONS_H */ snort-2.9.20/src/dynamic-preprocessors/s7commplus/s7comm_paf.c0000644000175000017500000001030414241076235022525 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Protocol-Aware Flushing (PAF) code for the S7commplus preprocessor. * */ #include "spp_s7comm.h" #include "s7comm_decode.h" #include "s7comm_paf.h" #include "sf_dynamic_preprocessor.h" int S7commplusPafRegisterPort (struct _SnortConfig *sc, uint16_t port, tSfPolicyId policy_id) { if (!_dpd.isPafEnabled()) return 0; _dpd.streamAPI->register_paf_port(sc, policy_id, port, 0, (PAF_Callback)S7commplusPaf, true); _dpd.streamAPI->register_paf_port(sc, policy_id, port, 1, (PAF_Callback)S7commplusPaf, true); return 0; } #ifdef TARGET_BASED int S7commplusAddServiceToPaf (struct _SnortConfig *sc, uint16_t service, tSfPolicyId policy_id) { if (!_dpd.isPafEnabled()) return 0; _dpd.streamAPI->register_paf_service(sc, policy_id, service, 0, (PAF_Callback)S7commplusPaf, true); _dpd.streamAPI->register_paf_service(sc, policy_id, service, 1, (PAF_Callback)S7commplusPaf, true); return 0; } #endif /* Function: S7commplusPaf() Purpose: S7commplus/TCP PAF callback. Statefully inspects S7commplus traffic from the start of a session, Reads up until the length octet is found, then sets a flush point. Arguments: void * - stream5 session pointer void ** - S7commplus state tracking structure const uint8_t * - payload data to inspect uint32_t - length of payload data uint32_t - flags to check whether client or server uint32_t * - pointer to set flush point Returns: PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise */ PAF_Status S7commplusPaf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint32_t flags, uint32_t *fp, uint32_t *fp_eoh) { s7commplus_paf_data_t *pafdata = *(s7commplus_paf_data_t **)user; uint32_t bytes_processed = 0; /* Allocate state object if it doesn't exist yet. */ if (pafdata == NULL) { pafdata = calloc(1, sizeof(s7commplus_paf_data_t)); if (pafdata == NULL) return PAF_ABORT; *user = pafdata; } /* Process this packet 1 byte at a time */ while (bytes_processed < len) { switch (pafdata->state) { /* Skip the Transaction & Protocol IDs */ case S7COMMPLUS_PAF_STATE__TPKT_VER: case S7COMMPLUS_PAF_STATE__TPKT_RESERVED: case S7COMMPLUS_PAF_STATE__COPT_LEN: case S7COMMPLUS_PAF_STATE__COPT_PDU_TYPE: pafdata->state++; break; case S7COMMPLUS_PAF_STATE__TPKT_LEN_1: pafdata->tpkt_length |= ( *(data + bytes_processed) << 8 ); pafdata->state++; break; case S7COMMPLUS_PAF_STATE__TPKT_LEN_2: pafdata->tpkt_length |= *(data + bytes_processed); pafdata->state++; break; case S7COMMPLUS_PAF_STATE__SET_FLUSH: if ((pafdata->tpkt_length < TPKT_MIN_HDR_LEN)) { _dpd.alertAdd(GENERATOR_SPP_S7COMMPLUS, S7COMMPLUS_BAD_LENGTH, 1, 0, 3, S7COMMPLUS_BAD_LENGTH_STR, 0); } /* flush point at the end of payload */ *fp = pafdata->tpkt_length; pafdata->state = S7COMMPLUS_PAF_STATE__TPKT_VER; pafdata->tpkt_length = 0; return PAF_FLUSH; } bytes_processed++; } return PAF_SEARCH; } /* Take a S7commplus config + Snort policy, iterate through ports, register PAF callback */ void S7commplusAddPortsToPaf(struct _SnortConfig *sc, s7commplus_config_t *config, tSfPolicyId policy_id) { unsigned int i; for (i = 0; i < MAX_PORTS; i++) { if (config->ports[PORT_INDEX(i)] & CONV_PORT(i)) { S7commplusPafRegisterPort(sc, (uint16_t) i, policy_id); } } } snort-2.9.20/src/dynamic-preprocessors/s7commplus/spp_s7comm.c0000644000175000017500000005170714241076247022600 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Dynamic preprocessor for the S7commplus protocol * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include #include #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #include "spp_s7comm.h" #include "sf_preproc_info.h" #include "profiler.h" #ifdef PERF_PROFILING PreprocStats s7commplusPerfStats; #endif #include "sf_types.h" #include "s7comm_decode.h" #include "s7comm_roptions.h" #include "s7comm_paf.h" const int MAJOR_VERSION = 1; const int MINOR_VERSION = 0; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_S7COMMPLUS"; #define SetupS7commplus DYNAMIC_PREPROC_SETUP /* Preprocessor config objects */ static tSfPolicyUserContextId s7commplus_context_id = NULL; static s7commplus_config_t *s7commplus_eval_config = NULL; /* Target-based app ID */ #ifdef TARGET_BASED int16_t s7commplus_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif /* Prototypes */ static void S7commplusInit(struct _SnortConfig *, char *); static inline void S7commplusOneTimeInit(struct _SnortConfig *); static inline s7commplus_config_t * S7commplusPerPolicyInit(struct _SnortConfig *, tSfPolicyUserContextId); static void ProcessS7commplus(void *, void *); #ifdef SNORT_RELOAD static void S7commplusReload(struct _SnortConfig *, char *, void **); static int S7commplusReloadVerify(struct _SnortConfig *, void *); static void * S7commplusReloadSwap(struct _SnortConfig *, void *); static void S7commplusReloadSwapFree(void *); #endif static void registerPortsForDispatch( struct _SnortConfig *sc, s7commplus_config_t *policy ); static void registerPortsForReassembly( s7commplus_config_t *policy, int direction ); static void _addPortsToStreamFilter(struct _SnortConfig *, s7commplus_config_t *, tSfPolicyId); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static void S7commplusFreeConfig(tSfPolicyUserContextId context_id); static void FreeS7commplusData(void *); static int S7commplusCheckConfig(struct _SnortConfig *); static void S7commplusCleanExit(int, void *); static void ParseS7commplusArgs(s7commplus_config_t *config, char *args); static void S7commplusPrintConfig(s7commplus_config_t *config); static int S7commplusPortCheck(s7commplus_config_t *config, SFSnortPacket *packet); static s7commplus_session_data_t * S7commplusCreateSessionData(SFSnortPacket *); /* Register init callback */ void SetupS7commplus(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc("s7commplus", S7commplusInit); #else _dpd.registerPreproc("s7commplus", S7commplusInit, S7commplusReload, S7commplusReloadVerify, S7commplusReloadSwap, S7commplusReloadSwapFree); #endif } /* Allocate memory for preprocessor config, parse the args, set up callbacks */ static void S7commplusInit(struct _SnortConfig *sc, char *argp) { s7commplus_config_t *s7commplus_policy = NULL; if (s7commplus_context_id == NULL) { S7commplusOneTimeInit(sc); } s7commplus_policy = S7commplusPerPolicyInit(sc, s7commplus_context_id); ParseS7commplusArgs(s7commplus_policy, argp); /* Can't add ports until they've been parsed... */ S7commplusAddPortsToPaf(sc, s7commplus_policy, _dpd.getParserPolicy(sc)); #ifdef TARGET_BASED S7commplusAddServiceToPaf(sc, s7commplus_app_id, _dpd.getParserPolicy(sc)); #endif // register ports with session and stream registerPortsForDispatch(sc, s7commplus_policy ); registerPortsForReassembly( s7commplus_policy, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); S7commplusPrintConfig(s7commplus_policy); } static inline void S7commplusOneTimeInit(struct _SnortConfig *sc) { /* context creation & error checking */ s7commplus_context_id = sfPolicyConfigCreate(); if (s7commplus_context_id == NULL) { _dpd.fatalMsg("%s(%d) Failed to allocate memory for " "S7commplus config.\n", *_dpd.config_file, *_dpd.config_line); } if (_dpd.streamAPI == NULL) { _dpd.fatalMsg("%s(%d) SetupS7commplus(): The Stream preprocessor " "must be enabled.\n", *_dpd.config_file, *_dpd.config_line); } /* callback registration */ _dpd.addPreprocConfCheck(sc, S7commplusCheckConfig); _dpd.addPreprocExit(S7commplusCleanExit, NULL, PRIORITY_LAST, PP_S7COMMPLUS); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("s7commplus", (void *)&s7commplusPerfStats, 0, _dpd.totalPerfStats, NULL); #endif /* Set up target-based app id */ #ifdef TARGET_BASED s7commplus_app_id = _dpd.findProtocolReference("cotp"); if (s7commplus_app_id == SFTARGET_UNKNOWN_PROTOCOL) s7commplus_app_id = _dpd.addProtocolReference("s7commplus"); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_S7COMMPLUS, s7commplus_app_id ); #endif } /* Responsible for allocating a S7commplus policy. Never returns NULL. */ static inline s7commplus_config_t * S7commplusPerPolicyInit(struct _SnortConfig *sc, tSfPolicyUserContextId context_id) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); s7commplus_config_t *s7commplus_policy = NULL; /* Check for existing policy & bail if found */ sfPolicyUserPolicySet(context_id, policy_id); s7commplus_policy = (s7commplus_config_t *)sfPolicyUserDataGetCurrent(context_id); if (s7commplus_policy != NULL) { _dpd.fatalMsg("%s(%d) S7commplus preprocessor can only be " "configured once.\n", *_dpd.config_file, *_dpd.config_line); } /* Allocate new policy */ s7commplus_policy = (s7commplus_config_t *)calloc(1, sizeof(s7commplus_config_t)); if (!s7commplus_policy) { _dpd.fatalMsg("%s(%d) Could not allocate memory for " "s7commplus preprocessor configuration.\n" , *_dpd.config_file, *_dpd.config_line); } sfPolicyUserDataSetCurrent(context_id, s7commplus_policy); /* Register callbacks that are done for each policy */ _dpd.addPreproc(sc, ProcessS7commplus, PRIORITY_APPLICATION, PP_S7COMMPLUS, PROTO_BIT__TCP); _addPortsToStreamFilter(sc, s7commplus_policy, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif /* Add preprocessor rule options here */ _dpd.preprocOptRegister(sc, S7COMMPLUS_OPCODE_NAME, S7commplusOpcodeInit, S7commplusRuleEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, S7COMMPLUS_FUNC_NAME, S7commplusFuncInit, S7commplusRuleEval, free, NULL, NULL, NULL, NULL); _dpd.preprocOptRegister(sc, S7COMMPLUS_CONTENT_NAME, S7commplusContentInit, S7commplusRuleEval, free, NULL, NULL, NULL, NULL); return s7commplus_policy; } static void ParseSinglePort(s7commplus_config_t *config, char *token) { /* single port number */ char *endptr; unsigned long portnum = _dpd.SnortStrtoul(token, &endptr, 10); if ((*endptr != '\0') || (portnum >= MAX_PORTS)) { _dpd.fatalMsg("%s(%d) Bad s7commplus port number: %s\n" "Port number must be an integer between 0 and 65535.\n", *_dpd.config_file, *_dpd.config_line, token); } /* Good port number! */ config->ports[PORT_INDEX(portnum)] |= CONV_PORT(portnum); } static void ParseS7commplusArgs(s7commplus_config_t *config, char *args) { char *saveptr; char *token; /* Set default port */ config->ports[PORT_INDEX(S7COMMPLUS_PORT)] |= CONV_PORT(S7COMMPLUS_PORT); /* No args? Stick to the default. */ if (args == NULL) return; token = strtok_r(args, " ", &saveptr); while (token != NULL) { if (strcmp(token, "ports") == 0) { unsigned nPorts = 0; /* Un-set the default port */ config->ports[PORT_INDEX(S7COMMPLUS_PORT)] = 0; /* Parse ports */ token = strtok_r(NULL, " ", &saveptr); if (token == NULL) { _dpd.fatalMsg("%s(%d) Missing argument for S7commplus preprocessor " "'ports' option.\n", *_dpd.config_file, *_dpd.config_line); } if (isdigit(token[0])) { ParseSinglePort(config, token); nPorts++; } else if (*token == '{') { /* list of ports */ token = strtok_r(NULL, " ", &saveptr); while (token != NULL && *token != '}') { ParseSinglePort(config, token); nPorts++; token = strtok_r(NULL, " ", &saveptr); } } else { nPorts = 0; } if ( nPorts == 0 ) { _dpd.fatalMsg("%s(%d) Bad S7commplus 'ports' argument: '%s'\n" "Argument to S7commplus 'ports' must be an integer, or a list " "enclosed in { } braces.\n", *_dpd.config_file, *_dpd.config_line, token); } } else { _dpd.fatalMsg("%s(%d) Failed to parse s7commplus argument: %s\n", *_dpd.config_file, *_dpd.config_line, token); } token = strtok_r(NULL, " ", &saveptr); } } /* Print a S7commplus config */ static void S7commplusPrintConfig(s7commplus_config_t *config) { int index; int newline = 1; if (config == NULL) return; _dpd.logMsg("S7commplus config: \n"); _dpd.logMsg(" Ports:\n"); /* Loop through port array & print, 5 ports per line */ for (index = 0; index < MAX_PORTS; index++) { if (config->ports[PORT_INDEX(index)] & CONV_PORT(index)) { _dpd.logMsg("\t%d", index); if ( !((newline++) % 5) ) { _dpd.logMsg("\n"); } } } _dpd.logMsg("\n"); } /* Main runtime entry point */ static void ProcessS7commplus(void *ipacketp, void *contextp) { SFSnortPacket *packetp = (SFSnortPacket *)ipacketp; s7commplus_session_data_t *sessp; PROFILE_VARS; // preconditions - what we registered for assert(IsTCP(packetp) && packetp->payload && packetp->payload_size); PREPROC_PROFILE_START(s7commplusPerfStats); /* Fetch me a preprocessor config to use with this VLAN/subnet/etc.! */ s7commplus_eval_config = sfPolicyUserDataGetCurrent(s7commplus_context_id); /* Look for a previously-allocated session data. */ sessp = _dpd.sessionAPI->get_application_data(packetp->stream_session, PP_S7COMMPLUS); if (sessp == NULL) { /* No existing session. Check those ports. */ if (S7commplusPortCheck(s7commplus_eval_config, packetp) != true) { PREPROC_PROFILE_END(s7commplusPerfStats); return; } } if ( !PacketHasFullPDU(packetp) && S7commplusIsPafActive(packetp) ) { /* If a packet is rebuilt, but not a full PDU, then it's garbage that got flushed at the end of a stream. */ if ( packetp->flags & (FLAG_REBUILT_STREAM|FLAG_PDU_HEAD) ) { _dpd.alertAdd(GENERATOR_SPP_S7COMMPLUS, S7COMMPLUS_BAD_LENGTH, 1, 0, 3, S7COMMPLUS_BAD_LENGTH_STR, 0); } PREPROC_PROFILE_END(s7commplusPerfStats); return; } if (sessp == NULL) { /* Create session data and attach it to the Stream session */ sessp = S7commplusCreateSessionData(packetp); if ( !sessp ) { PREPROC_PROFILE_END(s7commplusPerfStats); return; } } /* When pipelined S7commplus PDUs appear in a single TCP segment, the detection engine caches the results of the rule options after evaluating on the first PDU. Setting this flag stops the caching. */ packetp->flags |= FLAG_ALLOW_MULTIPLE_DETECT; /* Do preprocessor-specific detection stuff here */ S7commplusDecode(s7commplus_eval_config, packetp); /* That's the end! */ PREPROC_PROFILE_END(s7commplusPerfStats); } /* Check ports & services */ static int S7commplusPortCheck(s7commplus_config_t *config, SFSnortPacket *packet) { #ifdef TARGET_BASED int16_t app_id = _dpd.sessionAPI->get_application_protocol_id(packet->stream_session); /* call to get_application_protocol_id gave an error */ if (app_id == SFTARGET_UNKNOWN_PROTOCOL) return false; /* this is identified as non-s7commplus */ if (app_id && (app_id != s7commplus_app_id)) return false; /* this is identified as s7commplus */ if (app_id == s7commplus_app_id) return true; /* fall back to port check */ #endif if (config->ports[PORT_INDEX(packet->src_port)] & CONV_PORT(packet->src_port)) return true; if (config->ports[PORT_INDEX(packet->dst_port)] & CONV_PORT(packet->dst_port)) return true; return false; } static s7commplus_session_data_t* S7commplusCreateSessionData(SFSnortPacket *packet) { s7commplus_session_data_t *data = NULL; /* Sanity Check */ if (!packet || !packet->stream_session) return NULL; data = (s7commplus_session_data_t *)calloc(1, sizeof(s7commplus_session_data_t)); if (!data) return NULL; /* Attach to Stream session */ _dpd.sessionAPI->set_application_data(packet->stream_session, PP_S7COMMPLUS, data, FreeS7commplusData); /* This reference counting stuff got from old preprocs */ data->policy_id = _dpd.getNapRuntimePolicy(); data->context_id = s7commplus_context_id; ((s7commplus_config_t *)sfPolicyUserDataGetCurrent(s7commplus_context_id))->ref_count++; return data; } /* Reload functions */ #ifdef SNORT_RELOAD /* Almost like S7commplusInit, but not quite. */ static void S7commplusReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId s7commplus_swap_context_id = (tSfPolicyUserContextId)*new_config; s7commplus_config_t *s7commplus_policy = NULL; if (s7commplus_swap_context_id == NULL) { s7commplus_swap_context_id = sfPolicyConfigCreate(); if (s7commplus_swap_context_id == NULL) { _dpd.fatalMsg("Failed to allocate memory " "for S7commplus config.\n"); } if (_dpd.streamAPI == NULL) { _dpd.fatalMsg("SetupS7commplus(): The Stream preprocessor " "must be enabled.\n"); } *new_config = (void *)s7commplus_swap_context_id; } s7commplus_policy = S7commplusPerPolicyInit(sc, s7commplus_swap_context_id); ParseS7commplusArgs(s7commplus_policy, args); /* Can't add ports until they've been parsed... */ S7commplusAddPortsToPaf(sc, s7commplus_policy, _dpd.getParserPolicy(sc)); S7commplusPrintConfig(s7commplus_policy); } static int S7commplusReloadVerify(struct _SnortConfig *sc, void *swap_config) { if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("SetupS7commplus(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } static int S7commplusFreeUnusedConfigPolicy( tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { s7commplus_config_t *s7commplus_config = (s7commplus_config_t *)data; /* do any housekeeping before freeing s7commplus config */ if (s7commplus_config->ref_count == 0) { sfPolicyUserDataClear(context_id, policy_id); free(s7commplus_config); } return 0; } static void * S7commplusReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId s7commplus_swap_context_id = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_context_id = s7commplus_context_id; if (s7commplus_swap_context_id == NULL) return NULL; s7commplus_context_id = s7commplus_swap_context_id; sfPolicyUserDataFreeIterate(old_context_id, S7commplusFreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_context_id) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_context_id; } return NULL; } static void S7commplusReloadSwapFree(void *data) { if (data == NULL) return; S7commplusFreeConfig( (tSfPolicyUserContextId)data ); } #endif //Reload functions ends here static void registerPortsForDispatch( struct _SnortConfig *sc, s7commplus_config_t *policy ) { uint32_t port; for ( port = 0; port < MAX_PORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_S7COMMPLUS, PROTO_BIT__TCP, port ); } } static void registerPortsForReassembly( s7commplus_config_t *policy, int direction ) { uint32_t port; for ( port = 0; port < MAX_PORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.streamAPI->register_reassembly_port( NULL, port, direction ); } } /* Stream filter functions */ static void _addPortsToStreamFilter(struct _SnortConfig *sc, s7commplus_config_t *config, tSfPolicyId policy_id) { if (config == NULL) return; if (_dpd.streamAPI) { int portNum; for (portNum = 0; portNum < MAX_PORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status( sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1 ); } } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, s7commplus_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif static int S7commplusFreeConfigPolicy( tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { s7commplus_config_t *s7commplus_config = (s7commplus_config_t *)data; /* do any housekeeping before freeing s7commplus_config */ sfPolicyUserDataClear(context_id, policy_id); free(s7commplus_config); return 0; } static void S7commplusFreeConfig(tSfPolicyUserContextId context_id) { if (context_id == NULL) return; sfPolicyUserDataFreeIterate(context_id, S7commplusFreeConfigPolicy); sfPolicyConfigDelete(context_id); } static int S7commplusCheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId context_id, tSfPolicyId policy_id, void *data ) { _dpd.setParserPolicy(sc, policy_id); if (!_dpd.isPreprocEnabled(sc, PP_STREAM)) { _dpd.errMsg("%s(%d) S7commplusCheckPolicyConfig(): The Stream preprocessor " "must be enabled.\n", *_dpd.config_file, *_dpd.config_line); return -1; } return 0; } static int S7commplusCheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate(sc, s7commplus_context_id, S7commplusCheckPolicyConfig))) return rval; return 0; } static void S7commplusCleanExit(int signal, void *data) { if (s7commplus_context_id != NULL) { S7commplusFreeConfig(s7commplus_context_id); s7commplus_context_id = NULL; } } static void FreeS7commplusData(void *data) { s7commplus_session_data_t *session = (s7commplus_session_data_t *)data; s7commplus_config_t *config = NULL; if (session == NULL) return; if (session->context_id != NULL) { config = (s7commplus_config_t *)sfPolicyUserDataGet(session->context_id, session->policy_id); } if (config != NULL) { config->ref_count--; if ((config->ref_count == 0) && (session->context_id != s7commplus_context_id)) { sfPolicyUserDataClear(session->context_id, session->policy_id); free(config); if (sfPolicyUserPolicyGetActive(session->context_id) == 0) { /* No more outstanding configs - free the config array */ S7commplusFreeConfig(session->context_id); } } } free(session); } snort-2.9.20/src/dynamic-preprocessors/s7commplus/Makefile.am0000444000175000017500000000145314230012554022355 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_s7commplus_preproc.la libsf_s7commplus_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_s7commplus_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_s7commplus_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c endif libsf_s7commplus_preproc_la_SOURCES = \ spp_s7comm.c \ spp_s7comm.h \ s7comm_decode.c \ s7comm_decode.h \ s7comm_roptions.c \ s7comm_roptions.h \ s7comm_paf.c \ s7comm_paf.h #EXTRA_DIST = \ #sf_s7comm.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/s7commplus/s7comm_paf.h0000644000175000017500000000433514241076236022542 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Protocol-Aware Flushing (PAF) code for the S7commplus preprocessor. * */ #ifndef S7COMM_PAF__H #define S7COMM_PAF__H #include "spp_s7comm.h" #include "stream_api.h" typedef enum _s7commplus_paf_state { S7COMMPLUS_PAF_STATE__TPKT_VER = 0, S7COMMPLUS_PAF_STATE__TPKT_RESERVED, S7COMMPLUS_PAF_STATE__TPKT_LEN_1, S7COMMPLUS_PAF_STATE__TPKT_LEN_2, S7COMMPLUS_PAF_STATE__COPT_LEN, S7COMMPLUS_PAF_STATE__COPT_PDU_TYPE, S7COMMPLUS_PAF_STATE__SET_FLUSH } s7commplus_paf_state_t; typedef struct _s7commplus_paf_data { s7commplus_paf_state_t state; uint16_t tpkt_length; } s7commplus_paf_data_t; void S7commplusAddPortsToPaf(struct _SnortConfig *sc, s7commplus_config_t *config, tSfPolicyId policy_id); int S7commplusPafRegisterPort(struct _SnortConfig *sc, uint16_t port, tSfPolicyId policy_id); int S7commplusAddServiceToPaf(struct _SnortConfig *sc, uint16_t service, tSfPolicyId policy_id); PAF_Status S7commplusPaf(void *ssn, void **user, const uint8_t *data, uint32_t len, uint32_t flags, uint32_t *fp, uint32_t *fp_eoh); static inline bool S7commplusIsPafActive(const SFSnortPacket *p) { bool to_server = (p->flags & FLAG_FROM_CLIENT)? true:false; if ((p->stream_session_ptr) && _dpd.streamAPI->is_paf_active(p->stream_session_ptr, to_server)) return true; return false; } #endif /* S7COMMPLUS_PAF__H */ snort-2.9.20/src/dynamic-preprocessors/s7commplus/s7comm_roptions.c0000644000175000017500000001752614241076244023651 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Rule options for S7commplus preprocessor * */ #include #include "sf_types.h" #include "sf_snort_plugin_api.h" #include "sf_dynamic_preprocessor.h" #include "spp_s7comm.h" #include "s7comm_decode.h" #include "s7comm_roptions.h" #include "s7comm_paf.h" static s7commplus_opcode_map_t s7commplus_opcode_map[] = { {"request", 0x31}, {"response", 0x32}, {"notification", 0x33}, {"response2", 0x02} }; /* Mapping of name -> function code for 's7commplus_function' option. */ static s7commplus_func_map_t s7commplus_func_map[] = { {"explore", 0x04BB}, {"createobject", 0x04CA}, {"deleteobject", 0x04D4}, {"setvariable", 0x04F2}, {"getlink", 0x0524}, {"setmultivar", 0x0542}, {"getmultivar", 0x054C}, {"beginsequence", 0x0556}, {"endsequence", 0x0560}, {"invoke", 0x056B}, {"getvarsubstr", 0x0586} }; int S7commplusOpcodeInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *endptr; s7commplus_option_data_t *s7commplus_data; unsigned int opcode = 0; if (name == NULL || data == NULL) return 0; if (strcmp(name, S7COMMPLUS_OPCODE_NAME) != 0) return 0; if (params == NULL) { DynamicPreprocessorFatalMessage("%s(%d): No argument given for s7commplus_opcode. " "s7commplus_opcode requires a number between 0 and 0xFF.\n", *_dpd.config_file, *_dpd.config_line); } s7commplus_data = (s7commplus_option_data_t *)calloc(1, sizeof(s7commplus_option_data_t)); if (s7commplus_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "s7commplus_option_data_t data structure.\n", __FILE__, __LINE__); } /* Parsing time */ if (isdigit(params[0])) { /* argument given as integer (hexidecimal) */ opcode = _dpd.SnortStrtoul(params, &endptr, 16); if ((opcode > 255) || (*endptr != '\0')) { DynamicPreprocessorFatalMessage("%s(%d): s7commplus_opcode requires a " "number between 0 and 0xFF.\n", *_dpd.config_file, *_dpd.config_line); } } else { /* Check the argument against the map */ size_t i; int parse_success = 0; for (i = 0; i < (sizeof(s7commplus_opcode_map) / sizeof(s7commplus_opcode_map_t)); i++) { if (strcmp(params, s7commplus_opcode_map[i].name) == 0) { parse_success = 1; opcode = s7commplus_opcode_map[i].opcode; break; } } if (!parse_success) { DynamicPreprocessorFatalMessage("%s(%d): s7commplus_opcode requires a " "number between 0 and 0xFF, or a valid opcode name.\n", *_dpd.config_file, *_dpd.config_line); } } s7commplus_data->type = S7COMMPLUS_OPCODE; s7commplus_data->arg = (uint8_t) opcode; *data = (void *)s7commplus_data; return 1; } int S7commplusFuncInit(struct _SnortConfig *sc, char *name, char *params, void **data) { char *endptr; s7commplus_option_data_t *s7commplus_data; unsigned int func = 0; if (name == NULL || data == NULL) return 0; if (strcmp(name, S7COMMPLUS_FUNC_NAME) != 0) return 0; if (params == NULL) { DynamicPreprocessorFatalMessage("%s(%d): No argument given for s7commplus_function. " "s7commplus_function requires a number between 0 and 0xFFFF.\n", *_dpd.config_file, *_dpd.config_line); } s7commplus_data = (s7commplus_option_data_t *)calloc(1, sizeof(s7commplus_option_data_t)); if (s7commplus_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "s7commplus_option_data_t data structure.\n", __FILE__, __LINE__); } /* Parsing time */ if (isdigit(params[0])) { /* argument given as integer (hexidecimal) */ func = _dpd.SnortStrtoul(params, &endptr, 16); if ((func > 0xFFFF) || (*endptr != '\0')) { DynamicPreprocessorFatalMessage("%s(%d): s7commplus_function requires a " "number between 0 and 0xFFFF.\n", *_dpd.config_file, *_dpd.config_line); } } else { /* Check the argument against the map */ size_t i; int parse_success = 0; for (i = 0; i < (sizeof(s7commplus_func_map) / sizeof(s7commplus_func_map_t)); i++) { if (strcmp(params, s7commplus_func_map[i].name) == 0) { parse_success = 1; func = s7commplus_func_map[i].func; break; } } if (!parse_success) { DynamicPreprocessorFatalMessage("%s(%d): s7commplus_function requires a " "number between 0 and 0xFFFF, or a valid function name.\n", *_dpd.config_file, *_dpd.config_line); } } s7commplus_data->type = S7COMMPLUS_FUNC; s7commplus_data->arg = (uint16_t) func; *data = (void *)s7commplus_data; return 1; } int S7commplusContentInit(struct _SnortConfig *sc, char *name, char *params, void **data) { s7commplus_option_data_t *s7commplus_data; if (strcmp(name, S7COMMPLUS_CONTENT_NAME) != 0) return 0; /* Nothing to parse. */ if (params) { DynamicPreprocessorFatalMessage("%s(%d): s7commplus_content does not take " "any arguments.\n", *_dpd.config_file, *_dpd.config_line); } s7commplus_data = (s7commplus_option_data_t *)calloc(1, sizeof(s7commplus_option_data_t)); if (s7commplus_data == NULL) { DynamicPreprocessorFatalMessage("%s(%d) Failed to allocate memory for " "s7commplus_option_data_t data structure.\n", __FILE__, __LINE__); } s7commplus_data->type = S7COMMPLUS_CONTENT; s7commplus_data->arg = 0; *data = (void *)s7commplus_data; return 1; } /* S7commplus rule evaluation callback. */ int S7commplusRuleEval(void *raw_packet, const uint8_t **cursor, void *data) { SFSnortPacket *packet = (SFSnortPacket *)raw_packet; s7commplus_option_data_t *rule_data = (s7commplus_option_data_t *)data; s7commplus_session_data_t *session_data; /* * The preprocessor only evaluates PAF-flushed PDUs. If the rule options * don't check for this, they'll fire on stale session data when the * original packet goes through before flushing. */ if (!PacketHasFullPDU(packet) && S7commplusIsPafActive(packet)) return RULE_NOMATCH; session_data = (s7commplus_session_data_t *) _dpd.sessionAPI->get_application_data(packet->stream_session, PP_S7COMMPLUS); if ((packet->payload_size == 0 ) || (session_data == NULL)) { return RULE_NOMATCH; } switch (rule_data->type) { case S7COMMPLUS_OPCODE: if (session_data->s7commplus_proto_id != S7COMMPLUS_PROTOCOL_ID) return RULE_NOMATCH; if (session_data->s7commplus_opcode == rule_data->arg) return RULE_MATCH; break; case S7COMMPLUS_FUNC: if (session_data->s7commplus_proto_id != S7COMMPLUS_PROTOCOL_ID) return RULE_NOMATCH; if (session_data->s7commplus_function == rule_data->arg) return RULE_MATCH; break; case S7COMMPLUS_CONTENT: if (session_data->s7commplus_proto_id != S7COMMPLUS_PROTOCOL_ID) return RULE_NOMATCH; if (packet->payload_size < TPKT_MIN_HDR_LEN + S7COMMPLUS_MIN_HDR_LEN) return RULE_NOMATCH; /* S7commplus data */ *cursor = (const uint8_t *) (packet->payload + TPKT_MIN_HDR_LEN + S7COMMPLUS_MIN_HDR_LEN); _dpd.SetAltDetect((uint8_t *)*cursor, (uint16_t)(packet->payload_size - TPKT_MIN_HDR_LEN - S7COMMPLUS_MIN_HDR_LEN)); return RULE_MATCH; } return RULE_NOMATCH; } snort-2.9.20/src/dynamic-preprocessors/s7commplus/spp_s7comm.h0000644000175000017500000000450514241076251022572 0ustar apoapo/* * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2020-2022 Cisco and/or its affiliates. All rights reserved. * * Authors: Jeffrey Gu , Pradeep Damodharan * * Dynamic preprocessor for the S7commplus protocol * */ #ifndef SPP_S7COMM_H #define SPP_S7COMM_H #include "sf_types.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #define MAX_PORTS 65536 /* Default S7commplus port */ #define S7COMMPLUS_PORT 102 /* Convert port value into an index for the s7comm_config->ports array */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) /* S7commplus preprocessor configuration */ typedef struct _s7commplus_config { uint8_t ports[MAX_PORTS/8]; int ref_count; } s7commplus_config_t; /* S7commplus session data */ typedef struct _s7commplus_session_data { uint8_t s7commplus_proto_id; uint8_t s7commplus_proto_version; uint16_t s7commplus_data_len; uint8_t s7commplus_opcode; uint16_t s7commplus_function, s7commplus_reserved_1, s7commplus_reserved_2; tSfPolicyId policy_id; tSfPolicyUserContextId context_id; } s7commplus_session_data_t; #define S7COMMPLUS_PORTS_KEYWORD "ports" #define S7COMMPLUS_MEMCAP_KEYWORD "memcap" #define TPKT_MIN_HDR_LEN 7 /* length field in TPKT header for S7commplus */ #define TPKT_MIN_DATA_HDR_LEN 11 /* length field in TPKT header for S7commplus */ #define INTEGRITY_PART_LEN 33 /* length of Integrity part in V3 Header packets */ #define S7COMMPLUS_MIN_HDR_LEN 4 #define S7COMMPLUS_PROTOCOL_ID 0x72 #endif /* SPP_S7COMM_H */ snort-2.9.20/src/dynamic-preprocessors/reputation/0000755000175000017500000000000014242725716020417 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/reputation/reputation_debug.h0000644000175000017500000000355514241076165024134 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides macros and functions for debugging the preprocessor. * If Snort is not configured to do debugging, macros are empty. * * 6/11/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef _REPUTATION_DEBUG_H_ #define _REPUTATION_DEBUG_H_ #include #include "sfPolicyUserData.h" /******************************************************************** * Macros ********************************************************************/ #define DEBUG_REPUTATION 0x00000020 /* 16 */ #define REPUTATION_DEBUG__START_MSG "REPUTATION Start ********************************************" #define REPUTATION_DEBUG__END_MSG "REPUTATION End **********************************************" #endif /* _REPUTATION_DEBUG_H_ */ snort-2.9.20/src/dynamic-preprocessors/reputation/sf_reputation.vcxproj0000444000175000017500000004231614230012554024704 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {27AC2703-100A-4534-9EC4-9DBA758152FC} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Debug\ .\Debug\ true true .\Release\ .\Release\ false false .\Release\ .\Release\ MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_reputation.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_reputation.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_reputation.bsc true true true Console .\Debug\sf_reputation.dll .\Debug\sf_reputation.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_reputation.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_reputation.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_reputation.bsc true true true Console .\Debug\sf_reputation.dll .\Debug\sf_reputation.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_reputation.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_reputation.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_reputation.bsc true true Console .\Release\sf_reputation.dll .\Release\sf_reputation.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_reputation.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_reputation.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_reputation.bsc true true Console .\Release\sf_reputation.dll .\Release\sf_reputation.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/reputation/reputation_config.h0000644000175000017500000000720514241076164024306 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 6/11/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef _REPUTATION_CONFIG_H_ #define _REPUTATION_CONFIG_H_ #include "sf_types.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "reputation_debug.h" #include "sf_ip.h" #include "sfrt_flat.h" #ifdef SHARED_REP #include "./shmem/shmem_mgmt.h" #endif #define REPUTATION_NAME "reputation" typedef enum _NestedIP { INNER, OUTER, BOTH }NestedIP; typedef enum _WhiteAction { UNBLACK, TRUST }WhiteAction; typedef struct _SharedMem { char *path; uint32_t updateInterval; uint16_t maxInstances; }SharedMem; typedef enum _IPdecision { DECISION_NULL , MONITORED, BLACKLISTED , WHITELISTED_UNBLACK, WHITELISTED_TRUST, DECISION_MAX }IPdecision; typedef struct _ListInfo{ uint8_t listIndex; uint8_t listType; uint32_t listId; #ifdef SHARED_REP bool zones[MAX_NUM_ZONES]; char padding[2 + MAX_NUM_ZONES - MAX_NUM_ZONES/4*4]; #endif } ListInfo; /* * Reputation preprocessor configuration. * * memcap: the memcap for IP table. * numEntries: number of entries in the table * scanlocal: to scan local network * prioirity: the priority of whitelist, blacklist * nestedIP: which IP address to use when IP encapsulation * iplist: the IP table * ref_count: reference account */ typedef struct _reputationConfig { uint32_t memcap; int numEntries; uint8_t scanlocal; IPdecision priority; NestedIP nestedIP; WhiteAction whiteAction; MEM_OFFSET local_black_ptr; MEM_OFFSET local_white_ptr; void *emptySegment; void *localSegment; SharedMem sharedMem; int segment_version; uint32_t memsize; bool memCapReached; table_flat_t *iplist; ListInfo *listInfo; int ref_count; char *statusBuf; int statusBuf_len; } ReputationConfig; #define NUM_INDEX_PER_ENTRY 4 typedef struct _IPrepInfo{ char listIndexes[NUM_INDEX_PER_ENTRY]; MEM_OFFSET next; } IPrepInfo; /******************************************************************** * Public function prototypes ********************************************************************/ void Reputation_FreeConfig(ReputationConfig *); void ParseReputationArgs(ReputationConfig *, u_char*); void initShareMemory(struct _SnortConfig *sc, void *config); void ReputationRepInfo(IPrepInfo *, uint8_t *, char *, int); DEBUG_WRAP(void ReputationPrintRepInfo(IPrepInfo * repInfo, uint8_t *base);) #endif snort-2.9.20/src/dynamic-preprocessors/reputation/Makefile.in0000644000175000017500000006630114242725546022473 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/dynamic-preprocessors/reputation ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_reputation_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_reputation_preproc_la_SOURCES_DIST = spp_reputation.c \ spp_reputation.h reputation_config.c reputation_config.h \ reputation_utils.c reputation_utils.h reputation_debug.h \ ./shmem/sflinux_helpers.c ./shmem/sflinux_helpers.h \ ./shmem/shmem_common.h ./shmem/shmem_config.h \ ./shmem/shmem_config.c ./shmem/shmem_datamgmt.h \ ./shmem/shmem_datamgmt.c ./shmem/shmem_lib.h \ ./shmem/shmem_lib.c ./shmem/shmem_mgmt.h ./shmem/shmem_mgmt.c @HAVE_SHARED_REP_FALSE@am_libsf_reputation_preproc_la_OBJECTS = \ @HAVE_SHARED_REP_FALSE@ spp_reputation.lo reputation_config.lo \ @HAVE_SHARED_REP_FALSE@ reputation_utils.lo @HAVE_SHARED_REP_TRUE@am_libsf_reputation_preproc_la_OBJECTS = \ @HAVE_SHARED_REP_TRUE@ spp_reputation.lo reputation_config.lo \ @HAVE_SHARED_REP_TRUE@ reputation_utils.lo sflinux_helpers.lo \ @HAVE_SHARED_REP_TRUE@ shmem_config.lo shmem_datamgmt.lo \ @HAVE_SHARED_REP_TRUE@ shmem_lib.lo shmem_mgmt.lo @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_reputation_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo sf_ip.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfrt.lo sfrt_dir.lo sfrt_flat.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfrt_flat_dir.lo segment_mem.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_reputation_preproc_la_OBJECTS = \ $(am_libsf_reputation_preproc_la_OBJECTS) \ $(nodist_libsf_reputation_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_reputation_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_reputation_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_reputation_preproc_la_SOURCES) \ $(nodist_libsf_reputation_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_reputation_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_reputation_preproc.la libsf_reputation_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_reputation_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_reputation_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_ip.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfrt.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfrt_dir.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfrt_flat.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfrt_flat_dir.c \ @SO_WITH_STATIC_LIB_FALSE@../include/segment_mem.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c @HAVE_SHARED_REP_FALSE@libsf_reputation_preproc_la_SOURCES = \ @HAVE_SHARED_REP_FALSE@spp_reputation.c \ @HAVE_SHARED_REP_FALSE@spp_reputation.h \ @HAVE_SHARED_REP_FALSE@reputation_config.c \ @HAVE_SHARED_REP_FALSE@reputation_config.h \ @HAVE_SHARED_REP_FALSE@reputation_utils.c \ @HAVE_SHARED_REP_FALSE@reputation_utils.h \ @HAVE_SHARED_REP_FALSE@reputation_debug.h @HAVE_SHARED_REP_TRUE@libsf_reputation_preproc_la_SOURCES = \ @HAVE_SHARED_REP_TRUE@spp_reputation.c \ @HAVE_SHARED_REP_TRUE@spp_reputation.h \ @HAVE_SHARED_REP_TRUE@reputation_config.c \ @HAVE_SHARED_REP_TRUE@reputation_config.h \ @HAVE_SHARED_REP_TRUE@reputation_utils.c \ @HAVE_SHARED_REP_TRUE@reputation_utils.h \ @HAVE_SHARED_REP_TRUE@reputation_debug.h \ @HAVE_SHARED_REP_TRUE@./shmem/sflinux_helpers.c \ @HAVE_SHARED_REP_TRUE@./shmem/sflinux_helpers.h \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_common.h \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_config.h \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_config.c \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_datamgmt.h \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_datamgmt.c \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_lib.h \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_lib.c \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_mgmt.h \ @HAVE_SHARED_REP_TRUE@./shmem/shmem_mgmt.c EXTRA_DIST = \ sf_reputation.vcxproj \ sf_reputation.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/reputation/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/reputation/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_reputation_preproc.la: $(libsf_reputation_preproc_la_OBJECTS) $(libsf_reputation_preproc_la_DEPENDENCIES) $(EXTRA_libsf_reputation_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_reputation_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_reputation_preproc_la_OBJECTS) $(libsf_reputation_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sflinux_helpers.lo: ./shmem/sflinux_helpers.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sflinux_helpers.lo `test -f './shmem/sflinux_helpers.c' || echo '$(srcdir)/'`./shmem/sflinux_helpers.c shmem_config.lo: ./shmem/shmem_config.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o shmem_config.lo `test -f './shmem/shmem_config.c' || echo '$(srcdir)/'`./shmem/shmem_config.c shmem_datamgmt.lo: ./shmem/shmem_datamgmt.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o shmem_datamgmt.lo `test -f './shmem/shmem_datamgmt.c' || echo '$(srcdir)/'`./shmem/shmem_datamgmt.c shmem_lib.lo: ./shmem/shmem_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o shmem_lib.lo `test -f './shmem/shmem_lib.c' || echo '$(srcdir)/'`./shmem/shmem_lib.c shmem_mgmt.lo: ./shmem/shmem_mgmt.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o shmem_mgmt.lo `test -f './shmem/shmem_mgmt.c' || echo '$(srcdir)/'`./shmem/shmem_mgmt.c sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sf_ip.lo: ../include/sf_ip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_ip.lo `test -f '../include/sf_ip.c' || echo '$(srcdir)/'`../include/sf_ip.c sfrt.lo: ../include/sfrt.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt.lo `test -f '../include/sfrt.c' || echo '$(srcdir)/'`../include/sfrt.c sfrt_dir.lo: ../include/sfrt_dir.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt_dir.lo `test -f '../include/sfrt_dir.c' || echo '$(srcdir)/'`../include/sfrt_dir.c sfrt_flat.lo: ../include/sfrt_flat.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt_flat.lo `test -f '../include/sfrt_flat.c' || echo '$(srcdir)/'`../include/sfrt_flat.c sfrt_flat_dir.lo: ../include/sfrt_flat_dir.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt_flat_dir.lo `test -f '../include/sfrt_flat_dir.c' || echo '$(srcdir)/'`../include/sfrt_flat_dir.c segment_mem.lo: ../include/segment_mem.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o segment_mem.lo `test -f '../include/segment_mem.c' || echo '$(srcdir)/'`../include/segment_mem.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/reputation/spp_reputation.c0000644000175000017500000011053114241076227023633 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Reputation preprocessor * * This is the main entry point for this preprocessor * * Author: Hui Cao * Date: 06-01-2011 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #include "spp_reputation.h" #include "reputation_config.h" #include "reputation_utils.h" #include #include #include #include #ifndef WIN32 #include #include #endif #include #include #ifdef SHARED_REP #include "./shmem/shmem_mgmt.h" #endif #include "profiler.h" #ifdef PERF_PROFILING PreprocStats reputationPerfStats; #endif #ifdef REG_TEST #include "reg_test.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_REPUTATION"; #define PP_IPREP_PRIORITY PRIORITY_CORE + PP_CORE_ORDER_IPREP #define SetupReputation DYNAMIC_PREPROC_SETUP /* * Function prototype(s) */ static void ReputationInit( struct _SnortConfig *, char* ); static int ReputationCheckConfig(struct _SnortConfig *); static inline void ReputationProcess(SFSnortPacket *); static void ReputationMain( void*, void* ); static void ReputationFreeConfig(tSfPolicyUserContextId); static void ReputationPrintStats(int); static void ReputationCleanExit(int, void *); static inline IPrepInfo* ReputationLookup(sfaddr_t* ip); static inline IPdecision GetReputation(IPrepInfo *, SFSnortPacket *, uint32_t *); int reputation_get_entry_count(void); bool reputation_process_external_ip(void *p, sfaddr_t* ip); #ifdef SHARED_REP static void ReputationMaintenanceCheck(int, void *); #endif /******************************************************************** * Global variables ********************************************************************/ int totalNumEntries = 0; Reputation_Stats reputation_stats; ReputationConfig *reputation_eval_config; tSfPolicyUserContextId reputation_config; ReputationConfig *pDefaultPolicyConfig = NULL; #ifdef SHARED_REP Swith_State switch_state = NO_SWITCH; int available_segment = NO_DATASEG; #endif static uint8_t reputation_update_count; #ifdef SNORT_RELOAD static void ReputationReload(struct _SnortConfig *, char *, void **); static void *ReputationReloadSwap(struct _SnortConfig *, void *); static void ReputationReloadSwapFree(void *); static int ReputationReloadVerify(struct _SnortConfig *, void *); #endif /* Called at preprocessor setup time. Links preprocessor keyword * to corresponding preprocessor initialization function. * * PARAMETERS: None. * * RETURNS: Nothing. * */ void SetupReputation(void) { /* Link preprocessor keyword to initialization function * in the preprocessor list. */ #ifndef SNORT_RELOAD _dpd.registerPreproc( "reputation", ReputationInit ); #else _dpd.registerPreproc("reputation", ReputationInit, ReputationReload, ReputationReloadVerify, ReputationReloadSwap, ReputationReloadSwapFree); #endif _dpd.registerReputationGetEntryCount(reputation_get_entry_count); _dpd.registerReputationProcessExternal(reputation_process_external_ip); } #ifdef SHARED_REP static int Reputation_MgmtInfo(uint16_t type, const uint8_t *data, uint32_t length, void **new_config, char *statusBuf, int statusBufLen) { ShmemMgmtInfo(statusBuf, statusBufLen); return 0; } static int Reputation_Lookup(uint16_t type, const uint8_t *data, uint32_t length, void **new_config, char *statusBuf, int statusBufLen) { sfaddr_t addr; IPrepInfo *repInfo = NULL; char *tokstr, *save, *data_copy; CSMessageDataHeader *msg_hdr = (CSMessageDataHeader *)data; statusBuf[0] = 0; if (length <= sizeof(*msg_hdr)) { return -1; } length -= sizeof(*msg_hdr); if (length != (uint32_t)ntohs(msg_hdr->length)) { return -1; } data += sizeof(*msg_hdr); data_copy = malloc(length + 1); if (data_copy == NULL) { return -1; } memcpy(data_copy, data, length); data_copy[length] = 0; tokstr = strtok_r(data_copy, " \t\n", &save); if (tokstr == NULL) { free(data_copy); return -1; } /* Convert tokstr to sfip type */ if (sfaddr_pton(tokstr, &addr) != SFIP_SUCCESS) { free(data_copy); return -1; } /* Get the reputation info */ repInfo = ReputationLookup(&addr); if (!repInfo) { snprintf(statusBuf, statusBufLen, "Reputation Info: Error doing lookup"); free(data_copy); return -1; } /* Are we looking to obtain the decision? */ tokstr = strtok_r(NULL, " \t\n", &save); if (tokstr) { uint32_t listid; char *decision; #ifdef DAQ_PKTHDR_UNKNOWN int zone = atoi(tokstr); #endif SFSnortPacket p; #ifdef DAQ_PKTHDR_UNKNOWN DAQ_PktHdr_t hdr; p.pkt_header = &hdr; hdr.ingress_group = zone; #else p.pkt_header = NULL; #endif switch (GetReputation(repInfo, &p, &listid)) { case DECISION_NULL: decision = "DECISION_NULL"; break; case BLACKLISTED: decision = "BLOCK-LIST"; break; case WHITELISTED_UNBLACK: decision = "DO-NOT-BLOCK-LIST UNBLOCK"; break; case MONITORED: decision = "MONITORED"; break; case WHITELISTED_TRUST: decision = "DO-NOT-BLOCK-LIST TRUST"; break; default: decision = "UNKNOWN"; break; } snprintf(statusBuf, statusBufLen, "Reputation Info: verdict %s in list %d" #ifdef DAQ_PKTHDR_UNKNOWN " from zone %d" #endif ,decision, listid #ifdef DAQ_PKTHDR_UNKNOWN ,zone #endif ); } else { ReputationRepInfo(repInfo, (uint8_t *)reputation_eval_config->iplist, statusBuf, statusBufLen); } free(data_copy); return 0; } static int Reputation_PreControl(uint16_t type, const uint8_t *data, uint32_t length, void **new_config, char *statusBuf, int statusBufLen) { ReputationConfig *pDefaultPolicyConfig = NULL; ReputationConfig *nextConfig = NULL; statusBuf[0] = 0; if (SWITCHING == switch_state ) return -1; pDefaultPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetDefault(reputation_config); if (!pDefaultPolicyConfig) { *new_config = NULL; return -1; } nextConfig = (ReputationConfig *)calloc(1, sizeof(ReputationConfig)); if (!nextConfig) { *new_config = NULL; return -1; } switch_state = SWITCHING; nextConfig->segment_version = NO_DATASEG; nextConfig->memcap = pDefaultPolicyConfig->memcap; nextConfig->statusBuf = statusBuf; nextConfig->statusBuf_len = statusBufLen; reputation_shmem_config = nextConfig; if ((available_segment = LoadSharedMemDataSegmentForWriter(RELOAD)) >= 0) { *new_config = nextConfig; nextConfig->segment_version = available_segment; _dpd.logMsg(" Reputation Preprocessor: Received segment %d\n", available_segment); if (!statusBuf[0]) snprintf(statusBuf,statusBufLen, "Reputation Preprocessor: Received segment %d successful", available_segment); return 0; } else if (available_segment != SHMEM_ERR) { *new_config = NULL; free(nextConfig); switch_state = NO_SWITCH; if (!statusBuf[0]) snprintf(statusBuf,statusBufLen, "Reputation Preprocessor: No segments received"); return 0; } //There was an error *new_config = NULL; free(nextConfig); switch_state = NO_SWITCH; return -1; } static int Reputation_Control(uint16_t type, void *new_config, void **old_config) { ReputationConfig *config = (ReputationConfig *) new_config; if (NULL != config) { SwitchToActiveSegment(config->segment_version, &IPtables); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION,"***Switched to segment %d\n", config->segment_version)); *old_config = config; return 0; } if (switch_state == NO_SWITCH) return 0; switch_state = NO_SWITCH; return -1; } static void Reputation_PostControl(uint16_t type, void *old_config, struct _THREAD_ELEMENT *te, ControlDataSendFunc f) { ReputationConfig *config = (ReputationConfig *) old_config; ReputationConfig *pDefaultPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetDefault(reputation_config); if (!config || !pDefaultPolicyConfig) switch_state = NO_SWITCH; if (switch_state == NO_SWITCH) return; UnmapInactiveSegments(); pDefaultPolicyConfig->memCapReached = config->memCapReached; pDefaultPolicyConfig->segment_version = config->segment_version; pDefaultPolicyConfig->memsize = config->memsize; pDefaultPolicyConfig->numEntries = config->numEntries; pDefaultPolicyConfig->iplist = config->iplist; pDefaultPolicyConfig->statusBuf = NULL; reputation_shmem_config = pDefaultPolicyConfig; switch_state = SWITCHED; free(config); } static void ReputationMaintenanceCheck(int signal, void *data) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Reputation Preprocessor Maintenance!\n");); PrintShmemMgmtInfo(); reputation_eval_config = sfPolicyUserDataGetDefault(reputation_config); if (SHMEM_SERVER_ID == _dpd.getSnortInstance()) { ManageUnusedSegments(); /*check whether new shared memory has been applied. If yes, release the old one*/ if ((SWITCHED == switch_state) && reputation_eval_config && (reputation_eval_config->iplist == (table_flat_t *)*IPtables)) { _dpd.logMsg(" Reputation Preprocessor: Instance %d switched to segment_version %d\n", _dpd.getSnortInstance(), available_segment); // Increment iprep update counter reputation_update_count++; // set snort's iprep counter _dpd.setIPRepUpdateCount(reputation_update_count); UnmapInactiveSegments(); switch_state = NO_SWITCH; } } else { if ((NO_SWITCH == switch_state)&&((available_segment = CheckForSharedMemSegment(reputation_eval_config->sharedMem.maxInstances)) >= 0)) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION,"***Switched to segment_version %d ",available_segment);); SwitchToActiveSegment(available_segment, &IPtables); switch_state = SWITCHED; } /*check whether new shared memory has been applied. If yes, release the old one*/ else if ((SWITCHED == switch_state) && reputation_eval_config && (reputation_eval_config->iplist == (table_flat_t *)*IPtables)) { _dpd.logMsg(" Reputation Preprocessor: Instance %d switched to segment_version %d\n", _dpd.getSnortInstance(), available_segment); // Increment iprep counter reputation_update_count++; _dpd.setIPRepUpdateCount (reputation_update_count); UnmapInactiveSegments(); switch_state = NO_SWITCH; } } } /*Switch for idle*/ static void ReputationShmemSwitch(void) { if (switch_state == NO_SWITCH) return; reputation_eval_config = sfPolicyUserDataGetDefault(reputation_config); if (reputation_eval_config) reputation_eval_config->iplist = (table_flat_t *)*IPtables; } void SetupReputationUpdate(uint32_t updateInterval) { _dpd.addPeriodicCheck(ReputationMaintenanceCheck,NULL, PP_IPREP_PRIORITY, PP_REPUTATION, updateInterval); _dpd.registerIdleHandler(ReputationShmemSwitch); /*Only writer or server has control channel*/ if (SHMEM_SERVER_ID == _dpd.getSnortInstance()) { _dpd.controlSocketRegisterHandler(CS_TYPE_REPUTATION_SHAREMEM, &Reputation_PreControl, &Reputation_Control, &Reputation_PostControl); _dpd.controlSocketRegisterHandler(CS_TYPE_REPUTATION_SHAREMEM_LOOKUP, &Reputation_Lookup, NULL, NULL); _dpd.controlSocketRegisterHandler(CS_TYPE_REPUTATION_SHAREMEM_MGMT_INFO, &Reputation_MgmtInfo, NULL, NULL); } } #endif /* Initializes the Reputation preprocessor module and registers * it in the preprocessor list. * * PARAMETERS: * * argp: Pointer to argument string to process for configuration data. * * RETURNS: Nothing. */ static void ReputationInit(struct _SnortConfig *sc, char *argp) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); ReputationConfig *pDefaultPolicyConfig = NULL; ReputationConfig *pPolicyConfig = NULL; if (reputation_config == NULL) { /*create a context*/ reputation_config = sfPolicyConfigCreate(); if (reputation_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for Reputation config.\n"); } _dpd.addPreprocConfCheck(sc, ReputationCheckConfig); _dpd.registerPreprocStats(REPUTATION_NAME, ReputationPrintStats); _dpd.addPreprocExit(ReputationCleanExit, NULL, PRIORITY_LAST, PP_REPUTATION); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("reputation", (void *)&reputationPerfStats, 0, _dpd.totalPerfStats, NULL); #endif } sfPolicyUserPolicySet (reputation_config, policy_id); pDefaultPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetDefault(reputation_config); pPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetCurrent(reputation_config); if ((policy_id != 0) && (pDefaultPolicyConfig == NULL)) { DynamicPreprocessorFatalMessage("%s(%d) => Reputation configuration may only" " be enabled in default configuration\n", *_dpd.config_file, *_dpd.config_line); } if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Reputation preprocessor can only be " "configured once.\n", *_dpd.config_file, *_dpd.config_line); } pPolicyConfig = (ReputationConfig *)calloc(1, sizeof(ReputationConfig)); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "Reputation preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(reputation_config, pPolicyConfig); ParseReputationArgs(pPolicyConfig, (u_char *)argp); if ((0 == pPolicyConfig->numEntries)&&(!pPolicyConfig->sharedMem.path)) { return; } if (policy_id != 0) pPolicyConfig->memcap = pDefaultPolicyConfig->memcap; if (!pPolicyConfig->sharedMem.path && pPolicyConfig->localSegment) IPtables = &pPolicyConfig->localSegment; #ifdef SHARED_REP if (pPolicyConfig->sharedMem.path && (!_dpd.isTestMode())) _dpd.addPostConfigFunc(sc, initShareMemory, pPolicyConfig); #endif } #ifdef REG_TEST /* Generate zones from ports for regression tests*/ static inline void createZones(uint32_t *ingressZone, uint32_t *egressZone, SFSnortPacket *p) { const uint32_t zone_base = 0xF700; *ingressZone = p->src_port - zone_base; *egressZone = p->dst_port - zone_base; } #endif /********************************************************************* * Lookup the IP information stored in the data entry. * * Returns: * IPdecision - * DECISION_NULL * BLACKLISTED * WHITELISTED_UNBLACK * MONITORED * WHITELISTED_TRUST * *********************************************************************/ static inline IPdecision GetReputation( IPrepInfo * repInfo, SFSnortPacket *p, uint32_t *listid) { IPdecision decision = DECISION_NULL; uint8_t *base ; ListInfo *listInfo; #ifdef SHARED_REP uint32_t ingressZone = 0; uint32_t egressZone = 0; #ifdef DAQ_PKTHDR_UNKNOWN if (p->pkt_header) { ingressZone = p->pkt_header->ingress_group; if (p->pkt_header->egress_index < 0) egressZone = ingressZone; else egressZone = p->pkt_header->egress_group; #ifdef REG_TEST createZones(&ingressZone,&egressZone,p); #endif /*Make sure zone ids are in the support range*/ if (ingressZone >= MAX_NUM_ZONES) ingressZone = 0; if (egressZone >= MAX_NUM_ZONES) egressZone = 0; } #endif #endif /*Walk through the IPrepInfo lists*/ base = (uint8_t *) reputation_eval_config->iplist; listInfo = (ListInfo *)(&base[reputation_eval_config->iplist->list_info]); while(repInfo) { int i; for(i = 0; i < NUM_INDEX_PER_ENTRY; i++) { int list_index = repInfo->listIndexes[i]; if (!list_index) break; list_index--; #ifdef SHARED_REP DEBUG_WRAP(PrintListInfo (listInfo[list_index].zones, listInfo[list_index].listId);); /*Check both ingress zone and egress zone*/ if (listInfo[list_index].zones[ingressZone] || listInfo[list_index].zones[egressZone]) #endif { if (WHITELISTED_UNBLACK == (IPdecision)listInfo[list_index].listType) return DECISION_NULL; if (reputation_eval_config->priority == (IPdecision)listInfo[list_index].listType ) { *listid = listInfo[list_index].listId; return ((IPdecision)listInfo[list_index].listType); } else if ( decision < listInfo[list_index].listType) { decision = (IPdecision)listInfo[list_index].listType; *listid = listInfo[list_index].listId; } } } if (!repInfo->next) break; repInfo = (IPrepInfo *)(&base[repInfo->next]); } return decision; } /********************************************************************* * Lookup the iplist table. * * Arguments: * sfaddr_t* - ip to be searched * * Returns: * * IPrepInfo * - The reputation information in the table * *********************************************************************/ static inline IPrepInfo* ReputationLookup(sfaddr_t* ip) { IPrepInfo * result; DEBUG_WRAP( DebugMessage(DEBUG_REPUTATION, "Lookup address: %s \n",sfip_to_str(ip) );); if (!reputation_eval_config->scanlocal) { if (sfip_is_private(ip) ) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Private address\n");); return NULL; } } result = (IPrepInfo *) sfrt_flat_dir8x_lookup(ip, reputation_eval_config->iplist ); return (result); } /********************************************************************* * Make decision based on ip addresses * * Arguments: * SFSnortPacket * - pointer to packet structure * * Returns: * IPdecision - * DECISION_NULL * BLACKLISTED * WHITELISTED_UNBLACK * MONITORED * WHITELISTED_TRUST * *********************************************************************/ static inline IPdecision ReputationDecision(SFSnortPacket *p) { sfaddr_t* ip; IPdecision decision; IPdecision decision_final = DECISION_NULL; IPrepInfo *result; /*Check INNER IP, when configured or only one layer*/ if (( ! p->outer_family ) ||(INNER == reputation_eval_config->nestedIP) ||(BOTH == reputation_eval_config->nestedIP)) { ip = GET_INNER_SRC_IP(((SFSnortPacket *)p)); result = ReputationLookup(ip); if(result) { DEBUG_WRAP(ReputationPrintRepInfo(result,(uint8_t *) reputation_eval_config->iplist);); decision = GetReputation(result,p, &p->iplist_id); p->iprep_layer = IP_INNER_LAYER; p->flags |= FLAG_IPREP_SOURCE_TRIGGERED; if ( reputation_eval_config->priority == decision) return decision; decision_final = decision; } ip = GET_INNER_DST_IP(((SFSnortPacket *)p)); result = ReputationLookup(ip); if(result) { DEBUG_WRAP(ReputationPrintRepInfo(result,(uint8_t *) reputation_eval_config->iplist);); decision = GetReputation(result,p, &p->iplist_id); p->iprep_layer = IP_INNER_LAYER; p->flags &=~FLAG_IPREP_SOURCE_TRIGGERED; if ( reputation_eval_config->priority == decision) return decision; decision_final = decision; } } /*Check OUTER IP*/ if (( p->outer_family) && ((OUTER == reputation_eval_config->nestedIP) ||(BOTH == reputation_eval_config->nestedIP))) { ip = GET_OUTER_SRC_IP(((SFSnortPacket *)p)); result = ReputationLookup(ip); if(result) { decision = GetReputation(result,p, &p->iplist_id); p->iprep_layer = IP_OUTTER_LAYER; p->flags |= FLAG_IPREP_SOURCE_TRIGGERED; if ( reputation_eval_config->priority == decision) return decision; decision_final = decision; } ip = GET_OUTER_DST_IP(((SFSnortPacket *)p)); result = ReputationLookup(ip); if(result) { decision = GetReputation(result,p, &p->iplist_id); p->iprep_layer = IP_OUTTER_LAYER; p->flags &=~FLAG_IPREP_SOURCE_TRIGGERED; if ( reputation_eval_config->priority == decision) return decision; decision_final = decision; } } return (decision_final); } /********************************************************************* * Main entry point for Reputation processing. * * Arguments: * SFSnortPacket * - pointer to packet structure * * Returns: * None * *********************************************************************/ static inline void ReputationProcess(SFSnortPacket *p) { IPdecision decision; if (!IPtables) return; reputation_eval_config->iplist = (table_flat_t *)*IPtables; decision = ReputationDecision(p); if (DECISION_NULL == decision) { return; } else if (BLACKLISTED == decision) { uint32_t flags = SSNFLAG_DETECTION_DISABLED; ALERT(REPUTATION_EVENT_BLACKLIST,REPUTATION_EVENT_BLACKLIST_STR); #ifdef POLICY_BY_ID_ONLY _dpd.inlineForceDropSession(p); flags |= SSNFLAG_FORCE_BLOCK; if (*_dpd.pkt_tracer_enabled) _dpd.addPktTrace(VERDICT_REASON_REPUTATION, snprintf(_dpd.trace, _dpd.traceMax, "Reputation: packet verdict block-list, drop\n")); else _dpd.addPktTrace(VERDICT_REASON_REPUTATION, 0); #endif // disable all preproc analysis and detection for this packet _dpd.disablePacketAnalysis(p); _dpd.sessionAPI->set_session_flags( p->stream_session, flags ); reputation_stats.blacklisted++; } else if (MONITORED == decision) { ALERT(REPUTATION_EVENT_MONITOR,REPUTATION_EVENT_MONITOR_STR); p->flags |= FLAG_IPREP_DATA_SET; reputation_stats.monitored++; } else if (WHITELISTED_TRUST == decision) { ALERT(REPUTATION_EVENT_WHITELIST,REPUTATION_EVENT_WHITELIST_STR); p->flags |= FLAG_IGNORE_PORT; _dpd.disablePacketAnalysis(p); _dpd.sessionAPI->set_session_flags( p->stream_session, SSNFLAG_DETECTION_DISABLED ); reputation_stats.whitelisted++; } } int reputation_get_entry_count(void) { return (IPtables? sfrt_flat_num_entries((table_flat_t *)*IPtables) : 0); } bool reputation_process_external_ip(void *pkt, sfaddr_t *ip) { IPdecision decision = DECISION_NULL; IPrepInfo *result; bool retval = false; uint32_t flags = 0; SFSnortPacket *p = (SFSnortPacket*)pkt; if(!IPtables || !ip || !p) { return false; } reputation_eval_config = sfPolicyUserDataGetDefault(reputation_config); reputation_eval_config->iplist = (table_flat_t *)*IPtables; result = ReputationLookup(ip); if(result) { DEBUG_WRAP(ReputationPrintRepInfo(result,(uint8_t *) reputation_eval_config->iplist);); decision = GetReputation(result,p, &p->iplist_id); switch (decision) { case BLACKLISTED: flags = SSNFLAG_DETECTION_DISABLED; ALERT(REPUTATION_EVENT_BLACKLIST,REPUTATION_EVENT_BLACKLIST_STR); #ifdef POLICY_BY_ID_ONLY _dpd.inlineForceDropSession(p); flags |= SSNFLAG_FORCE_BLOCK; if (*_dpd.pkt_tracer_enabled) _dpd.addPktTrace(VERDICT_REASON_REPUTATION, snprintf(_dpd.trace, _dpd.traceMax, "Reputation: packet verdict block-list based on IP fetched from URL, drop\n")); else _dpd.addPktTrace(VERDICT_REASON_REPUTATION, 0); #endif // disable all preproc analysis and detection for this packet _dpd.disablePacketAnalysis(p); _dpd.sessionAPI->set_session_flags( p->stream_session, flags ); reputation_stats.blacklisted++; retval = true; break; case WHITELISTED_TRUST: ALERT(REPUTATION_EVENT_WHITELIST,REPUTATION_EVENT_WHITELIST_STR); p->flags |= FLAG_IGNORE_PORT; _dpd.disablePacketAnalysis(p); _dpd.sessionAPI->set_session_flags( p->stream_session, SSNFLAG_DETECTION_DISABLED ); reputation_stats.whitelisted++; retval = true; break; case MONITORED: if(!(p->flags & FLAG_IPREP_DATA_SET)) // Monitor the flow if it is not already monitored { ALERT(REPUTATION_EVENT_MONITOR,REPUTATION_EVENT_MONITOR_STR); p->flags |= FLAG_IPREP_DATA_SET; reputation_stats.monitored++; } break; case DECISION_NULL: default: break; } } return retval; } /* Main runtime entry point for Reputation preprocessor. * Analyzes Reputation packets for anomalies/exploits. * * PARAMETERS: * * packetp: Pointer to current packet to process. * contextp: Pointer to context block, not used. * * RETURNS: Nothing. */ static void ReputationMain( void* ipacketp, void* contextp ) { PROFILE_VARS; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "%s\n", REPUTATION_DEBUG__START_MSG)); // preconditions - what we registered for assert(IsIP((SFSnortPacket*)ipacketp)); if (((SFSnortPacket*)ipacketp)->flags & FLAG_REBUILT_FRAG || ((SFSnortPacket*)ipacketp)->flags & FLAG_REBUILT_STREAM ) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION," -> spp_reputation: Not IP or Is a rebuilt packet\n");); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "%s\n", REPUTATION_DEBUG__END_MSG)); return; } reputation_eval_config = sfPolicyUserDataGetDefault(reputation_config); PREPROC_PROFILE_START(reputationPerfStats); ReputationProcess((SFSnortPacket*) ipacketp); // Update the reputation update counter in scb of the packet with the current iprep update counter _dpd.sessionAPI->set_reputation_update_counter ( ((( SFSnortPacket * ) ipacketp)->stream_session ), reputation_update_count); // Reputation has processed a packet for this session, no need to process // subsequent packets so we turn ourselves off for remainder of session if // all detection not already disabled if( ( _dpd.sessionAPI->get_session_flags( ( ( SFSnortPacket * ) ipacketp)->stream_session ) & SSNFLAG_DETECTION_DISABLED ) != SSNFLAG_DETECTION_DISABLED ) { _dpd.sessionAPI->disable_preproc_for_session( ( ( SFSnortPacket * ) ipacketp)->stream_session, PP_REPUTATION ); } DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "%s\n", REPUTATION_DEBUG__END_MSG)); PREPROC_PROFILE_END(reputationPerfStats); } static void initializeReputationForDispatch( struct _SnortConfig *sc ) { _dpd.sessionAPI->enable_preproc_all_ports_all_policies( sc, PP_REPUTATION, PROTO_BIT__IP ); _dpd.addPreprocAllPolicies( sc, ReputationMain, PP_IPREP_PRIORITY, PP_REPUTATION, PROTO_BIT__IP ); } int ReputationCheckConfig(struct _SnortConfig *sc) { ReputationConfig *pDefaultPolicyConfig; if( reputation_config == NULL ) return 0; pDefaultPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetDefault(reputation_config); if ((!IPtables || (pDefaultPolicyConfig->numEntries <= 0)) && (!pDefaultPolicyConfig->sharedMem.path)) return 0; // register reputation preprocessor to run in all policies initializeReputationForDispatch( sc ); return 0; } static void ReputationCleanExit(int signal, void *data) { if (reputation_config != NULL) { ReputationFreeConfig(reputation_config); reputation_config = NULL; #ifdef SHARED_REP ShutdownSharedMemory(); if (emptyIPtables != NULL) { free(emptyIPtables); emptyIPtables = NULL; } #endif } } static int ReputationFreeConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { ReputationConfig *pPolicyConfig = (ReputationConfig *)pData; //do any housekeeping before freeing ReputationConfig sfPolicyUserDataClear (config, policyId); Reputation_FreeConfig(pPolicyConfig); return 0; } void ReputationFreeConfig(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, ReputationFreeConfigPolicy); sfPolicyConfigDelete(config); } /****************************************************************** * Print statistics being kept by the preprocessor. * * Arguments: * int - whether Snort is exiting or not * * Returns: None * ******************************************************************/ static void ReputationPrintStats(int exiting) { _dpd.logMsg("Reputation Preprocessor Statistics\n"); _dpd.logMsg(" Total Memory Allocated: "STDu64"\n", reputation_stats.memoryAllocated); if (reputation_stats.blacklisted > 0) _dpd.logMsg(" Number of block-list packets: "STDu64"\n", reputation_stats.blacklisted); if (reputation_stats.whitelisted > 0) _dpd.logMsg(" Number of do-not-block-list packets: "STDu64"\n", reputation_stats.whitelisted); if (reputation_stats.monitored > 0) _dpd.logMsg(" Number of packets monitored: "STDu64"\n", reputation_stats.monitored); } #ifdef SNORT_RELOAD static void ReputationReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId reputation_swap_config = (tSfPolicyUserContextId)*new_config; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); ReputationConfig * pPolicyConfig = NULL; ReputationConfig *pDefaultPolicyConfig = NULL; if (reputation_swap_config == NULL) { //create a context reputation_swap_config = sfPolicyConfigCreate(); if (reputation_swap_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for Reputation config.\n"); } *new_config = (void *)reputation_swap_config; } sfPolicyUserPolicySet (reputation_swap_config, policy_id); pPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetCurrent(reputation_swap_config); pDefaultPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetDefault(reputation_config); if ((policy_id != 0) && (pDefaultPolicyConfig == NULL)) { DynamicPreprocessorFatalMessage("%s(%d) => Reputation configuration may only" " be enabled in default configuration\n", *_dpd.config_file, *_dpd.config_line); } if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Reputation preprocessor can only be " "configured once.\n", *_dpd.config_file, *_dpd.config_line); } pPolicyConfig = (ReputationConfig *)calloc(1, sizeof(ReputationConfig)); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "Reputation preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(reputation_swap_config, pPolicyConfig); ParseReputationArgs(pPolicyConfig, (u_char *)args); if ((0 == pPolicyConfig->numEntries) &&(!pPolicyConfig->sharedMem.path)) { return; } if ((policy_id != 0) &&(pDefaultPolicyConfig)) pPolicyConfig->memcap = pDefaultPolicyConfig->memcap; } static int ReputationReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId reputation_swap_config = (tSfPolicyUserContextId)swap_config; ReputationConfig * pPolicyConfig = NULL; ReputationConfig * pCurrentConfig = NULL; if (reputation_swap_config == NULL) return 0; pPolicyConfig = (ReputationConfig *)sfPolicyUserDataGet(reputation_swap_config, _dpd.getDefaultPolicy()); if (!pPolicyConfig) return 0; if (reputation_config != NULL) pCurrentConfig = (ReputationConfig *)sfPolicyUserDataGet(reputation_config, _dpd.getDefaultPolicy()); if (!pCurrentConfig) return 0; if (pPolicyConfig->memcap != pCurrentConfig->memcap) { _dpd.logMsg( "Reputation reload: Memcap changed, current memcap = %u , new memcap = %u \n", pCurrentConfig->memcap, pPolicyConfig->memcap); #ifdef REG_TEST if ( REG_TEST_FLAG_REPUTATION & getRegTestFlags() ) printf("[REPUTATION]: Memcap changed, current memcap = %u, new memcap = %u \n", pCurrentConfig->memcap, pPolicyConfig->memcap); #endif } #ifdef SHARED_REP /* Shared memory is used*/ if (pPolicyConfig->sharedMem.path || pCurrentConfig->sharedMem.path) { /*Shared memory setting is changed*/ if ( (!pCurrentConfig->sharedMem.path)||(!pPolicyConfig->sharedMem.path) || strcmp(pPolicyConfig->sharedMem.path, pCurrentConfig->sharedMem.path) ||(pPolicyConfig->sharedMem.updateInterval != pCurrentConfig->sharedMem.updateInterval)) { _dpd.errMsg("Reputation reload: Changing memory settings requires a restart.\n"); return -1; } } #endif // register reputation preprocessor to run in all policies initializeReputationForDispatch( sc ); return 0; } static int ReputationFreeUnusedConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { ReputationConfig *pPolicyConfig = (ReputationConfig *)pData; //do any housekeeping before freeing ReputationConfig if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); Reputation_FreeConfig(pPolicyConfig); } return 0; } static void * ReputationReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId reputation_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = reputation_config; ReputationConfig *pDefaultPolicyConfig = NULL; if (reputation_swap_config == NULL) return NULL; reputation_config = reputation_swap_config; pDefaultPolicyConfig = (ReputationConfig *)sfPolicyUserDataGetDefault(reputation_config); if (pDefaultPolicyConfig->localSegment) IPtables = &pDefaultPolicyConfig->localSegment; sfPolicyUserDataFreeIterate (old_config, ReputationFreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_config; } return NULL; } static void ReputationReloadSwapFree(void *data) { if (data == NULL) return; ReputationFreeConfig((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/reputation/reputation_config.c0000644000175000017500000017070114241076162024301 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions for parsing and querying configuration. * * 6/7/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sf_snort_packet.h" #include "sf_types.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "reputation_config.h" #include "spp_reputation.h" #include "reputation_debug.h" #include "reputation_utils.h" #ifdef SHARED_REP #include "./shmem/shmem_mgmt.h" #include #endif enum { IP_INSERT_SUCCESS = 0, IP_INVALID, IP_INSERT_FAILURE, IP_INSERT_DUPLICATE, IP_MEM_ALLOC_FAILURE }; /* * Default values for configurable parameters. */ #define REPUTATION_DEFAULT_MEMCAP 500 /*Mega bytes*/ #define REPUTATION_DEFAULT_REFRESH_PERIOD 60 /*60 seconds*/ #define REPUTATION_DEFAULT_SHARED_MEM_INSTANCES 50 /* * Min/Max values for each configurable parameter. */ #define MIN_MEMCAP 1 #define MAX_MEMCAP 4095 #define MIN_SHARED_MEM_REFRESH_PERIOD 1 #define MAX_SHARED_MEM_REFRESH_PERIOD UINT32_MAX #define MIN_SHARED_MEM_INSTANCES 2 #define MAX_SHARED_MEM_INSTANCES 256 #define MAX_ADDR_LINE_LENGTH 8192 /* * Keyword strings for parsing configuration options. */ #define REPUTATION_MEMCAP_KEYWORD "memcap" #define REPUTATION_SCANLOCAL_KEYWORD "scan_local" #define REPUTATION_BLACKLIST_KEYWORD "blacklist" #define REPUTATION_WHITELIST_KEYWORD "whitelist" #define REPUTATION_MONITORLIST_KEYWORD "monitorlist" #define REPUTATION_PRIORITY_KEYWORD "priority" #define REPUTATION_NESTEDIP_KEYWORD "nested_ip" #define REPUTATION_SHARED_MEM_KEYWORD "shared_mem" #define REPUTATION_SHARED_REFRESH_KEYWORD "shared_refresh" #define REPUTATION_SHARED_MAX_INSTANCES_KEYWORD "shared_max_instances" #define REPUTATION_WHITEACTION_KEYWORD "white" #define REPUTATION_CONFIG_SECTION_SEPERATORS ",;" #define REPUTATION_CONFIG_VALUE_SEPERATORS " " #define REPUTATION_SEPARATORS " \t\r\n" static char *black_info = REPUTATION_BLACKLIST_KEYWORD; static char *white_info = REPUTATION_WHITELIST_KEYWORD; static char *monitor_info = REPUTATION_MONITORLIST_KEYWORD; char* NestedIPKeyword[] = { "inner", "outer", "both", NULL }; char* WhiteActionOption[] = { "unblack", "trust", NULL }; #define MAX_MSGS_TO_PRINT 20 static unsigned long total_duplicates; static unsigned long total_invalids; void **IPtables; #ifdef SHARED_REP ReputationConfig *reputation_shmem_config; table_flat_t *emptyIPtables; #endif /* * Function prototype(s) */ static void IpListInit(uint32_t,ReputationConfig *config); static void LoadListFile(char *filename, INFO info, ReputationConfig *config); static void DisplayIPlistStats(ReputationConfig *); static void DisplayReputationConfig(ReputationConfig *); /* ******************************************************************** * Function: estimateSizeFromEntries * * Estimate the memory segment size based on number of entries and memcap. * * Arguments: * * uint32_t num_entries: number of entries. * uint32_t the memcap value set in configuration * * RETURNS: estimated memory size. *********************************************************************/ uint32_t estimateSizeFromEntries(uint32_t num_entries, uint32_t memcap) { uint64_t size; uint64_t sizeFromEntries; /*memcap value is in Megabytes*/ size = (uint64_t)memcap << 20; if (size > UINT32_MAX) size = UINT32_MAX; /*Worst case, 15k ~ 2^14 per entry, plus one Megabytes for empty table*/ if (num_entries > ((UINT32_MAX - (1 << 20))>> 15)) sizeFromEntries = UINT32_MAX; else sizeFromEntries = (num_entries << 15) + (1 << 20); if (size > sizeFromEntries) { size = sizeFromEntries; } return (uint32_t) size; } #ifdef SHARED_REP /**************************************************************************** * * Function: CheckIPlistDir() * * Purpose: We only check if IP list directory exist and * readable * Arguments: None. * * Returns: * 0 : fail * 1 : success * ****************************************************************************/ static int CheckIPlistDir(char *path) { struct stat st; if (path == NULL) return 0; if (stat(path, &st) == -1) return 0; if (!S_ISDIR(st.st_mode) || (access(path, R_OK) == -1)) { return 0; } return 1; } /* ******************************************************************** * Function: LoadFileIntoShmem * * Call back function for shared memory * This is called when new files in the list * Arguments: * * void* ptrSegment: start of shared memory segment. * ShmemDataFileList** file_list: the list of whitelist/blacklist files * int num_files: number of files * * RETURNS: * 0: success * other value fails *********************************************************************/ int LoadFileIntoShmem(void* ptrSegment, ShmemDataFileList** file_list, int num_files) { table_flat_t *table; int i; MEM_OFFSET list_ptr; ListInfo *listInfo; uint8_t *base; if (num_files > MAX_IPLIST_FILES) { _dpd.logMsg("Reputation preprocessor: Too many IP list files. " "The maximum is: %d, current is: %d.\n", MAX_IPLIST_FILES, num_files); num_files = MAX_IPLIST_FILES; } segment_meminit((uint8_t*)ptrSegment, reputation_shmem_config->memsize); /*DIR_16x7_4x4 for performance, but memory usage is high *Use DIR_8x16 worst case IPV4 5K, IPV6 15K (bytes) *Use DIR_16x7_4x4 worst case IPV4 500, IPV6 2.5M */ table = sfrt_flat_new(DIR_8x16, IPv6, reputation_shmem_config->numEntries, reputation_shmem_config->memcap); if (table == NULL) { _dpd.errMsg("Reputation preprocessor: Failed to create IP list.\n"); return -1; } reputation_shmem_config->iplist = table; base = (uint8_t *)ptrSegment; /*Copy the list information table to shared memory block*/ list_ptr = segment_calloc(num_files, sizeof(ListInfo)); if (list_ptr == 0) { _dpd.errMsg("Reputation preprocessor:: Failed to create IP list table.\n"); return -1; } listInfo = (ListInfo *)&base[list_ptr]; table->list_info = list_ptr; reputation_shmem_config->listInfo = listInfo; reputation_shmem_config->memCapReached = false; /*Reset the log message count*/ total_duplicates = 0; for (i = 0; i < num_files; i++) { listInfo[i].listIndex = (uint8_t)i + 1; listInfo[i].listType = (uint8_t)file_list[i]->filetype; listInfo[i].listId = file_list[i]->listid; memcpy(listInfo[i].zones, file_list[i]->zones, MAX_NUM_ZONES); LoadListFile(file_list[i]->filename, list_ptr, reputation_shmem_config); list_ptr += sizeof(ListInfo); } _dpd.logMsg("Reputation Preprocessor shared memory summary:\n"); DisplayIPlistStats(reputation_shmem_config); return 0; } /* ******************************************************************** * Function: GetSegmentSizeFromFileList * * Call back function for shared memory * This is called when new files in the list * * Arguments: * * ShmemDataFileList** file_list: the list of whitelist/blacklist files * int num_files: number of files * * RETURNS: * uint32_t: segment size *********************************************************************/ uint32_t GetSegmentSizeFromFileList(ShmemDataFileList** file_list, int file_count) { int numlines; int totalLines = 0; int i; if (file_count == 0) { return ZEROSEG; } for (i = 0; i < file_count; i++) { errno = 0; numlines = numLinesInFile(file_list[i]->filename); if ((0 == numlines) && (0 != errno)) { char errBuf[STD_BUF]; #ifdef WIN32 snprintf(errBuf, STD_BUF, "%s", strerror(errno)); #else strerror_r(errno, errBuf, STD_BUF); #endif DynamicPreprocessorFatalMessage( "Unable to open address file %s, Error: %s\n", file_list[i]->filename, errBuf); } if (totalLines + numlines < totalLines) { DynamicPreprocessorFatalMessage("Too many entries.\n"); } totalLines += numlines; } if (totalLines == 0) { return ZEROSEG; } reputation_shmem_config->numEntries = totalLines + 1; reputation_shmem_config->memsize = estimateSizeFromEntries(reputation_shmem_config->numEntries, reputation_shmem_config->memcap); return reputation_shmem_config->memsize; } /* ******************************************************************** * Function: InitPerProcessZeroSegment * * Call back function for shared memory * This is called during initialization * * Arguments: * * void*** data_ptr: (output) the address of shared memory address * * RETURNS: * uint32_t: segment size *********************************************************************/ int InitPerProcessZeroSegment(void*** data_ptr) { /*The size of empty segment is 1 Megabytes*/ size_t size = 1; long maxEntries = 1; static bool initiated = false; if (true == initiated) { *data_ptr = (void **)&emptyIPtables; return 0; } reputation_shmem_config->emptySegment = malloc(size*1024*1024); if (reputation_shmem_config->emptySegment == NULL) { DynamicPreprocessorFatalMessage( "Failed to allocate memory for empty segment.\n"); } segment_meminit((uint8_t*) reputation_shmem_config->emptySegment, size*1024*1024); initiated = true; /*DIR_16x7_4x4 for performance, but memory usage is high *Use DIR_8x16 worst case IPV4 5K, IPV6 15K (bytes) *Use DIR_16x7_4x4 worst case IPV4 500, IPV6 2.5M */ emptyIPtables = sfrt_flat_new(DIR_8x16, IPv6, maxEntries, size); if (emptyIPtables == NULL) { DynamicPreprocessorFatalMessage("Reputation preprocessor: Failed to create IP list.\n"); } *data_ptr = (void **)&emptyIPtables; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, " Total memory " "allocated for empty table: %d bytes\n", sfrt_flat_usage(emptyIPtables));); return 0; } /* ******************************************************************** * Function: initShareMemory * * Initialize for shared memory * This is called during initialization * * Arguments: * * ReputationConfig *config: the configure file * * RETURNS: * 1: success *********************************************************************/ void initShareMemory(struct _SnortConfig *sc, void *conf) { uint32_t snortID; ReputationConfig *config = (ReputationConfig *)conf; switch_state = SWITCHING; reputation_shmem_config = config; if (InitShmemDataMgmtFunctions(InitPerProcessZeroSegment, GetSegmentSizeFromFileList,LoadFileIntoShmem)) { DynamicPreprocessorFatalMessage("Unable to initialize DataManagement functions\n"); } /*use snort instance ID to designate server (writer)*/ snortID = _dpd.getSnortInstance(); if (SHMEM_SERVER_ID == snortID) { if ((available_segment = InitShmemWriter(snortID, IPREP, GROUP_0, NUMA_0, config->sharedMem.path, &IPtables, config->sharedMem.updateInterval, config->sharedMem.maxInstances)) == NO_ZEROSEG) { DynamicPreprocessorFatalMessage("Unable to init share memory writer\n"); } switch_state = SWITCHED; } else { if ((available_segment = InitShmemReader(snortID, IPREP,GROUP_0, NUMA_0, config->sharedMem.path, &IPtables, config->sharedMem.updateInterval, config->sharedMem.maxInstances)) == NO_ZEROSEG) { DynamicPreprocessorFatalMessage("Unable to init share memory reader\n"); } switch_state = SWITCHED; } SetupReputationUpdate(config->sharedMem.updateInterval); } #endif /* ******************************************************************** * Function: DisplayIPlistStats * * Display the statistics for the Reputation iplist table. * * Arguments: * * ReputationConfig *config: Reputation preprocessor configuration. * * RETURNS: Nothing. *********************************************************************/ static void DisplayIPlistStats(ReputationConfig *config) { /*Print out the summary*/ reputation_stats.memoryAllocated = sfrt_flat_usage(config->iplist); _dpd.logMsg(" Reputation total memory usage: %u bytes\n", reputation_stats.memoryAllocated); config->numEntries = sfrt_flat_num_entries(config->iplist); _dpd.logMsg(" Reputation total entries loaded: %u, invalid: %u, re-defined: %u\n", config->numEntries,total_invalids,total_duplicates); } /* ******************************************************************** * Function: DisplayReputationConfig * * Display the configuration for the Reputation preprocessor. * * Arguments: * * ReputationConfig *config: Reputation preprocessor configuration. * * RETURNS: Nothing. *********************************************************************/ static void DisplayReputationConfig(ReputationConfig *config) { if (config == NULL) return; _dpd.logMsg(" Memcap: %d %s \n", config->memcap, config->memcap == REPUTATION_DEFAULT_MEMCAP ? "(Default) M bytes" : "M bytes" ); _dpd.logMsg(" Scan local network: %s\n", config->scanlocal ? "ENABLED":"DISABLED (Default)"); _dpd.logMsg(" Reputation priority: %s \n", ((config->priority == WHITELISTED_TRUST) || (config->priority == WHITELISTED_UNBLACK))? REPUTATION_WHITELIST_KEYWORD "(Default)" : REPUTATION_BLACKLIST_KEYWORD ); _dpd.logMsg(" Nested IP: %s %s \n", NestedIPKeyword[config->nestedIP], config->nestedIP == INNER? "(Default)" : "" ); _dpd.logMsg(" White action: %s %s \n", WhiteActionOption[config->whiteAction], config->whiteAction == UNBLACK? "(Default)" : "" ); if (config->sharedMem.path) { _dpd.logMsg(" Shared memory supported, Update directory: %s\n", config->sharedMem.path ); _dpd.logMsg(" Shared memory refresh period: %d %s \n", config->sharedMem.updateInterval, config->sharedMem.updateInterval == REPUTATION_DEFAULT_REFRESH_PERIOD ? "(Default) seconds" : "seconds" ); _dpd.logMsg(" Shared memory max instances: %u\n", config->sharedMem.maxInstances); } else { _dpd.logMsg(" Shared memory is Not supported.\n"); } _dpd.logMsg("\n"); } /******************************************************************** * Function: IpListInit * * Initiate an iplist table * * Arguments: * Reputation_Config * * The configuration to use. * * Returns: None * ********************************************************************/ static void IpListInit(uint32_t maxEntries, ReputationConfig *config) { uint8_t *base; ListInfo *whiteInfo; ListInfo *blackInfo; MEM_OFFSET list_ptr; if (config->iplist == NULL) { uint32_t mem_size; mem_size = estimateSizeFromEntries(maxEntries, config->memcap); config->localSegment = malloc(mem_size); if (config->localSegment == NULL) { DynamicPreprocessorFatalMessage( "Failed to allocate memory for local segment\n"); } segment_meminit((uint8_t*)config->localSegment,mem_size); base = (uint8_t *)config->localSegment; /*DIR_16x7_4x4 for performance, but memory usage is high *Use DIR_8x16 worst case IPV4 5K, IPV6 15K (bytes) *Use DIR_16x7_4x4 worst case IPV4 500, IPV6 2.5M */ config->iplist = sfrt_flat_new(DIR_8x16, IPv6, maxEntries, config->memcap); if (config->iplist == NULL || !(list_ptr = segment_calloc((size_t)DECISION_MAX, sizeof(ListInfo)))) { DynamicPreprocessorFatalMessage("%s(%d): Failed to create IP list.\n", *(_dpd.config_file), *(_dpd.config_line)); } config->iplist->list_info = list_ptr; config->local_black_ptr = list_ptr + BLACKLISTED * sizeof(ListInfo); blackInfo = (ListInfo *)&base[config->local_black_ptr]; blackInfo->listType = BLACKLISTED; blackInfo->listIndex = BLACKLISTED + 1; #ifdef SHARED_REP memset(blackInfo->zones, true, MAX_NUM_ZONES); #endif if (UNBLACK == config->whiteAction) { config->local_white_ptr = list_ptr + WHITELISTED_UNBLACK * sizeof(ListInfo); whiteInfo = (ListInfo *)&base[config->local_white_ptr]; whiteInfo->listType = WHITELISTED_UNBLACK; whiteInfo->listIndex = WHITELISTED_UNBLACK + 1; #ifdef SHARED_REP memset(whiteInfo->zones, true, MAX_NUM_ZONES); #endif } else { config->local_white_ptr = list_ptr + WHITELISTED_TRUST * sizeof(ListInfo); whiteInfo = (ListInfo *)&base[config->local_white_ptr]; whiteInfo->listType = WHITELISTED_TRUST; whiteInfo->listIndex = WHITELISTED_TRUST + 1; #ifdef SHARED_REP memset(whiteInfo->zones, true, MAX_NUM_ZONES); #endif } } } /********************************************************************* * * Get the last index in the IP repuation information * * Arguments: * * IPrepInfo *repInfo: IP reputation information * uint8_t *base: the base pointer in shared memory * * RETURNS: * int *lastIndex: the last index * IPrepInfo *: the last IP info that stores the last index * NULL if not found *********************************************************************/ static inline IPrepInfo * getLastIndex(IPrepInfo *repInfo, uint8_t *base, int *lastIndex) { int i; assert(repInfo); /* Move to the end of current info*/ while (repInfo->next) { repInfo = (IPrepInfo *)&base[repInfo->next]; } for (i = 0; i < NUM_INDEX_PER_ENTRY; i++) { if (!repInfo->listIndexes[i]) break; } if (i > 0) { *lastIndex = i-1; return repInfo; } else { return NULL; } } /********************************************************************* * * Duplicate IP reputation information * * Arguments: * * IPrepInfo *currentInfo: IP reputation information copy from * IPrepInfo *destInfo: IP reputation information copy to * uint8_t *base: the base pointer in shared memory * * RETURNS: * number of bytes duplicated * -1 if out of memory * *********************************************************************/ static inline int duplicateInfo(IPrepInfo *destInfo,IPrepInfo *currentInfo, uint8_t *base) { int bytesAllocated = 0; while (currentInfo) { INFO nextInfo; *destInfo = *currentInfo; if (!currentInfo->next) break; nextInfo = segment_calloc(1,sizeof(IPrepInfo)); if (!nextInfo) { destInfo->next = 0; return -1; } else { destInfo->next = nextInfo; } bytesAllocated += sizeof(IPrepInfo); currentInfo = (IPrepInfo *)&base[currentInfo->next]; destInfo = (IPrepInfo *)&base[nextInfo]; } return bytesAllocated; } /********************************************************************* * New information for the same IP will be appended to the current * * If current information is empty (0), new information will be created. * * Arguments: * * INFO *current: (address to the location of) current IP reputation information * INFO new: (location of) new IP reputation information * uint8_t *base: the base pointer in shared memory * SaveDest saveDest: whether to update reputation at current location * or update at new location * * Returns: number of bytes allocated, -1 if out of memory * *********************************************************************/ static int64_t updateEntryInfo (INFO *current, INFO new, SaveDest saveDest, uint8_t *base) { IPrepInfo *currentInfo; IPrepInfo *newInfo; IPrepInfo *destInfo; IPrepInfo *lastInfo; int64_t bytesAllocated = 0; int i; char newIndex; if(!(*current)) { /* Copy the data to segment memory*/ *current = segment_calloc(1,sizeof(IPrepInfo)); if (!(*current)) { return -1; } bytesAllocated = sizeof(IPrepInfo); } if (*current == new) return bytesAllocated; currentInfo = (IPrepInfo *)&base[*current]; newInfo = (IPrepInfo *)&base[new]; /*The latest information is always the last entry */ lastInfo = getLastIndex(newInfo, base, &i); if (!lastInfo) { return bytesAllocated; } newIndex = lastInfo->listIndexes[i++]; DEBUG_WRAP( DebugMessage(DEBUG_REPUTATION, "Current IP reputation information: \n");); DEBUG_WRAP(ReputationPrintRepInfo(currentInfo, base);); DEBUG_WRAP( DebugMessage(DEBUG_REPUTATION, "New IP reputation information: \n");); DEBUG_WRAP(ReputationPrintRepInfo(newInfo, base);); if (SAVE_TO_NEW == saveDest) { int bytesDuplicated; /* When updating new entry, current information should be reserved * because current information is inherited from parent */ if ((bytesDuplicated = duplicateInfo(newInfo, currentInfo, base)) < 0) return -1; else bytesAllocated += bytesDuplicated; destInfo = newInfo; } else { destInfo = currentInfo; } /* Add the new list information to the end * This way, the order of list information is preserved. * The first one always has the highest priority, * because it is checked first during lookup. */ while (destInfo->next) { destInfo = (IPrepInfo *)&base[destInfo->next]; } for (i = 0; i < NUM_INDEX_PER_ENTRY; i++) { if (!destInfo->listIndexes[i]) break; else if (destInfo->listIndexes[i] == newIndex) { DEBUG_WRAP( DebugMessage(DEBUG_REPUTATION, "Final IP reputation information: \n");); DEBUG_WRAP(ReputationPrintRepInfo(destInfo, base);); return bytesAllocated; } } if (i < NUM_INDEX_PER_ENTRY) { destInfo->listIndexes[i] = newIndex; } else { IPrepInfo *nextInfo; MEM_OFFSET ipInfo_ptr = segment_calloc(1,sizeof(IPrepInfo)); if (!ipInfo_ptr) return -1; destInfo->next = ipInfo_ptr; nextInfo = (IPrepInfo *)&base[destInfo->next]; nextInfo->listIndexes[0] = newIndex; bytesAllocated += sizeof(IPrepInfo); } DEBUG_WRAP( DebugMessage(DEBUG_REPUTATION, "Final IP reputation information: \n");); DEBUG_WRAP(ReputationPrintRepInfo(destInfo, base);); return bytesAllocated; } /******************************************************************** * Function: AddIPtoList * * Add ip address to config file * * Arguments: * sfcidr_t *: ip address * void *: information about the file. * ReputationConfig *: The configuration to be update. * * Returns: * IP_INSERT_SUCCESS=0, * IP_INSERT_FAILURE, * IP_INSERT_DUPLICATE * ********************************************************************/ static int AddIPtoList(sfcidr_t *ipAddr,INFO ipInfo_ptr, ReputationConfig *config) { int iRet; int iFinalRet = IP_INSERT_SUCCESS; /*This variable is used to check whether a more generic address * overrides specific address */ uint32_t usageBeforeAdd; uint32_t usageAfterAdd; #ifdef DEBUG_MSGS if (NULL != sfrt_flat_lookup(&ipAddr->addr, config->iplist)) { DebugMessage(DEBUG_REPUTATION, "Find address before insert: %s\n", sfip_to_str(&ipAddr->addr) ); } else { DebugMessage(DEBUG_REPUTATION, "Can't find address before insert: %s\n", sfip_to_str(&ipAddr->addr) ); } #endif usageBeforeAdd = sfrt_flat_usage(config->iplist); /*Check whether the same or more generic address is already in the table*/ if (NULL != sfrt_flat_lookup(&ipAddr->addr, config->iplist)) { iFinalRet = IP_INSERT_DUPLICATE; } iRet = sfrt_flat_insert(ipAddr, (unsigned char)ipAddr->bits, ipInfo_ptr, RT_FAVOR_ALL, config->iplist, &updateEntryInfo); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Unused memory: %d \n",segment_unusedmem());); if (RT_SUCCESS == iRet) { #ifdef DEBUG_MSGS IPrepInfo * result; DebugMessage(DEBUG_REPUTATION, "Number of entries input: %d, in table: %d \n", totalNumEntries,sfrt_flat_num_entries(config->iplist) ); DebugMessage(DEBUG_REPUTATION, "Memory allocated: %d \n",sfrt_flat_usage(config->iplist) ); result = sfrt_flat_lookup(&ipAddr->addr, config->iplist); if (NULL != result) { DebugMessage(DEBUG_REPUTATION, "Find address after insert: %s \n",sfip_to_str(&ipAddr->addr) ); DEBUG_WRAP(ReputationPrintRepInfo(result, (uint8_t *)config->iplist);); } #endif totalNumEntries++; } else if (MEM_ALLOC_FAILURE == iRet) { iFinalRet = IP_MEM_ALLOC_FAILURE; DEBUG_WRAP( DebugMessage(DEBUG_REPUTATION, "Insert error: %d for address: %s \n",iRet, sfip_to_str(&ipAddr->addr) );); } else { iFinalRet = IP_INSERT_FAILURE; DEBUG_WRAP( DebugMessage(DEBUG_REPUTATION, "Insert error: %d for address: %s \n",iRet, sfip_to_str(&ipAddr->addr) );); } usageAfterAdd = sfrt_flat_usage(config->iplist); /*Compare in the same scale*/ if (usageAfterAdd > (config->memcap << 20)) { iFinalRet = IP_MEM_ALLOC_FAILURE; } /*Check whether there a more specific address will be overridden*/ if (usageBeforeAdd > usageAfterAdd ) { iFinalRet = IP_INSERT_DUPLICATE; } return iFinalRet; } static int snort_pton__address( char const *src, sfcidr_t *dest ) { unsigned char _temp[sizeof(struct in6_addr)]; if ( inet_pton(AF_INET, src, _temp) == 1 ) { sfip_set_raw(&dest->addr, _temp, AF_INET); } else if ( inet_pton(AF_INET6, src, _temp) == 1 ) { sfip_set_raw(&dest->addr, _temp, AF_INET6); } else { return 0; } dest->bits = 128; return 1; } #define isident(x) (isxdigit((x)) || (x) == ':' || (x) == '.') static int snort_pton( char const * src, sfcidr_t * dest ) { char ipbuf[INET6_ADDRSTRLEN]; char cidrbuf[sizeof("128")]; char *out = ipbuf; enum { BEGIN, IP, CIDR1, CIDR2, END, INVALID } state; memset(ipbuf, '\0', sizeof(ipbuf)); memset(cidrbuf, '\0', sizeof(cidrbuf)); state = BEGIN; while ( *src ) { char ch = *src; //printf("State:%d; C:%x; P:%p\n", state, ch, src ); src += 1; switch ( state ) { // Scan for beginning of IP address case BEGIN: if ( isident((int)ch) ) { // Set the first ipbuff byte and change state *out++ = ch; state = IP; } else if ( !isspace((int)ch) ) { state = INVALID; } break; // Fill in ipbuf with ip identifier characters // Move to CIDR1 if a cidr divider (i.e., '/') is found. case IP: if ( isident((int)ch) && (out - ipbuf + 1) < (int)sizeof(ipbuf) ) { *out++ = ch; } else if ( ch == '/' ) { state = CIDR1; } else if ( isspace((int)ch) ) { state = END; } else { state = INVALID; } break; // First cidr digit case CIDR1: if ( !isdigit((int)ch) ) { state = INVALID; } else { // Set output to the cidrbuf buffer out = cidrbuf; *out++ = ch; state = CIDR2; } break; // Consume any addition digits for cidrbuf case CIDR2: if ( isdigit((int)ch) && (out - cidrbuf + 1) < (int)sizeof(cidrbuf) ) { *out++ = ch; } else if ( isspace((int)ch) ) { state = END; } else { state = INVALID; } break; // Scan for junk at the EOL case END: if ( !isspace((int)ch) ) { state = INVALID; } break; // Can't get here default: break; } if ( state == INVALID ) return -1; } if ( snort_pton__address(ipbuf, dest) < 1 ) return 0; if ( *cidrbuf ) { char *end; int value = strtol(cidrbuf, &end, 10); if ( value > dest->bits || value <= 0 || errno == ERANGE ) return 0; if (sfaddr_family(&dest->addr) == AF_INET && value <= 32) dest->bits = value + 96; else dest->bits = value; } return 1; } /******************************************************************** * Function: * * Load one IP list file * * Arguments: * char *: the line to be processed * void *: information about the file. * ReputationConfig *: The configuration to be update. * * Returns: * IP_INSERT_SUCCESS, * IP_INSERT_FAILURE, * IP_INSERT_DUPLICATE * ********************************************************************/ static int ProcessLine(char *line, INFO info, ReputationConfig *config) { sfcidr_t address; if ( !line || *line == '\0' ) return IP_INSERT_SUCCESS; if ( snort_pton(line, &address) < 1 ) return IP_INVALID; return AddIPtoList(&address, info, config); } /******************************************************************** * Function: UpdatePathToFile * * Update the path to a file, if using relative path. * The relative path is based on config file directory. * * Arguments: * full_path_filename: file name string * max_size: ? * char *filename: ? * * Returns: * 1 successful * 0 fail * ********************************************************************/ static int UpdatePathToFile(char *full_path_filename, unsigned int max_size, char *filename) { char *snort_conf_dir = *(_dpd.snort_conf_dir); if (!snort_conf_dir || !(*snort_conf_dir) || !full_path_filename || !filename) { DynamicPreprocessorFatalMessage(" %s(%d) => can't create path.\n", *(_dpd.config_file), *(_dpd.config_line)); return 0; } /*filename is too long*/ if ( max_size < strlen(filename) ) { DynamicPreprocessorFatalMessage(" %s(%d) => the file name length %u is longer than allowed %u.\n", *(_dpd.config_file), *(_dpd.config_line), strlen(filename), max_size); return 0; } /* * If an absolute path is specified, then use that. */ #ifndef WIN32 if(filename[0] == '/') { snprintf(full_path_filename, max_size, "%s", filename); } else { /* * Set up the file name directory. */ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '/') { snprintf(full_path_filename,max_size, "%s%s", snort_conf_dir, filename); } else { snprintf(full_path_filename, max_size, "%s/%s", snort_conf_dir, filename); } } #else if(strlen(filename)>3 && filename[1]==':' && filename[2]=='\\') { snprintf(full_path_filename, max_size, "%s", filename); } else { /* ** Set up the file name directory */ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '\\' || snort_conf_dir[strlen(snort_conf_dir) - 1] == '/' ) { snprintf(full_path_filename,max_size, "%s%s", snort_conf_dir, filename); } else { snprintf(full_path_filename, max_size, "%s\\%s", snort_conf_dir, filename); } } #endif return 1; } /******************************************************************** * Function: GetListInfo * * Get information about the file * * Arguments: * * info: information about the file. * * Returns: * None * ********************************************************************/ static char* GetListInfo(INFO info) { uint8_t *base; ListInfo *info_value; base = (uint8_t *)segment_basePtr(); info_value = (ListInfo *)(&base[info]); switch(info_value->listType) { case DECISION_NULL: return NULL; break; case BLACKLISTED: return black_info; break; case WHITELISTED_UNBLACK: return white_info; break; case MONITORED: return monitor_info; break; case WHITELISTED_TRUST: return white_info; break; default: return NULL; } return NULL; } /******************************************************************** * Function: LoadListFile * * Load one IP list file * * Arguments: * filename: file name string * info: information about the file. * ReputationConfig *: The configuration to be update. * * Returns: * None * ********************************************************************/ static void LoadListFile(char *filename, INFO info, ReputationConfig *config) { char linebuf[MAX_ADDR_LINE_LENGTH]; char full_path_filename[PATH_MAX+1]; int addrline = 0; FILE *fp = NULL; char *cmt = NULL; char *list_info; ListInfo *listInfo; IPrepInfo *ipInfo; MEM_OFFSET ipInfo_ptr; uint8_t *base; /*entries processing statistics*/ unsigned int duplicate_count = 0; /*number of duplicates in this file*/ unsigned int invalid_count = 0; /*number of invalid entries in this file*/ unsigned int fail_count = 0; /*number of invalid entries in this file*/ unsigned int num_loaded_before = 0; /*number of valid entries loaded */ if ((NULL == filename)||(0 == info)|| (NULL == config)||config->memCapReached) return; UpdatePathToFile(full_path_filename, PATH_MAX, filename); list_info = GetListInfo(info); if (!list_info) return; /*convert list info to ip entry info*/ ipInfo_ptr = segment_calloc(1,sizeof(IPrepInfo)); if (!(ipInfo_ptr)) { return; } base = (uint8_t*)config->iplist; ipInfo = ((IPrepInfo *)&base[ipInfo_ptr]); listInfo = ((ListInfo *)&base[info]); ipInfo->listIndexes[0] = listInfo->listIndex; _dpd.logMsg(" Processing %s file %s\n", list_info, full_path_filename); if((fp = fopen(full_path_filename, "r")) == NULL) { char errBuf[STD_BUF]; #ifdef WIN32 snprintf(errBuf, STD_BUF, "%s", strerror(errno)); #else strerror_r(errno, errBuf, STD_BUF); #endif errBuf[STD_BUF-1] = '\0'; _dpd.errMsg("%s(%d) => Unable to open address file %s, Error: %s\n", *(_dpd.config_file), *(_dpd.config_line), full_path_filename, errBuf); return; } num_loaded_before = sfrt_flat_num_entries(config->iplist); while( fgets(linebuf, MAX_ADDR_LINE_LENGTH, fp) ) { int iRet; addrline++; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Reputation configurations: %s\n",linebuf );); // Remove comments if( (cmt = strchr(linebuf, '#')) ) *cmt = '\0'; // Remove newline as well, prevent double newline in logging. if( (cmt = strchr(linebuf, '\n')) ) *cmt = '\0'; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Reputation configurations: %s\n",linebuf );); /* process the line */ iRet = ProcessLine(linebuf, ipInfo_ptr, config); if (IP_INSERT_SUCCESS == iRet) { continue; } else if (IP_INSERT_FAILURE == iRet && fail_count++ < MAX_MSGS_TO_PRINT) { _dpd.errMsg(" (%d) => Failed to insert address: \'%s\'\n", addrline, linebuf); } else if (IP_INVALID == iRet && invalid_count++ < MAX_MSGS_TO_PRINT) { _dpd.errMsg(" (%d) => Invalid address: \'%s\'\n", addrline, linebuf); } else if (IP_INSERT_DUPLICATE == iRet && duplicate_count++ < MAX_MSGS_TO_PRINT) { _dpd.errMsg(" (%d) => Re-defined address: '%s'\n", addrline, linebuf ); } else if (IP_MEM_ALLOC_FAILURE == iRet) { _dpd.errMsg("WARNING: %s(%d) => Memcap %u Mbytes reached when inserting IP Address: %s\n", full_path_filename, addrline, config->memcap,linebuf); if (config->statusBuf) { snprintf(config->statusBuf, config->statusBuf_len, "WARNING: %s(%d) => Memcap %u Mbytes reached when inserting IP Address: %s\n", full_path_filename, addrline, config->memcap,linebuf); config->statusBuf[config->statusBuf_len] = '\0'; } config->memCapReached = true; break; } } total_duplicates += duplicate_count; total_invalids += invalid_count; /*Print out the summary*/ if (fail_count > MAX_MSGS_TO_PRINT) _dpd.errMsg(" Additional addresses failed insertion but were not listed.\n"); if (invalid_count > MAX_MSGS_TO_PRINT) _dpd.errMsg(" Additional invalid addresses were not listed.\n"); if (duplicate_count > MAX_MSGS_TO_PRINT) _dpd.errMsg(" Additional duplicate addresses were not listed.\n"); _dpd.logMsg(" Reputation entries loaded: %u, invalid: %u, re-defined: %u (from file %s)\n", sfrt_flat_num_entries(config->iplist) - num_loaded_before, invalid_count, duplicate_count, full_path_filename); fclose(fp); } /******************************************************************** * Function: Reputation_FreeConfig * * Frees a reputation configuration * * Arguments: * Reputation_Config * * The configuration to free. * * Returns: None * ********************************************************************/ void Reputation_FreeConfig (ReputationConfig *config) { if (config == NULL) return; if (config->localSegment != NULL) { free(config->localSegment); } if(config->sharedMem.path) free(config->sharedMem.path); free(config); } /********************************************************************* * Function: EstimateNumEntries * * First pass to decide iplist table size. * * Arguments: * * ReputationConfig *config: Reputation preprocessor configuration. * argp: Pointer to string containing the config arguments. * * RETURNS: int. estimated number of Entries based on number of lines *********************************************************************/ static int EstimateNumEntries(ReputationConfig *config, u_char* argp) { char* cur_sectionp = NULL; char* next_sectionp = NULL; char* argcpyp = NULL; int totalLines = 0; /*Default values*/ argcpyp = strdup( (char*) argp ); if ( !argcpyp ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse Reputation options.\n"); return 0; } cur_sectionp = strtok_r( argcpyp, REPUTATION_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Arguments token: %s\n",cur_sectionp );); while ( cur_sectionp ) { char* next_tokenp = NULL; char* cur_tokenp = strtok_r( cur_sectionp, REPUTATION_CONFIG_VALUE_SEPERATORS, &next_tokenp); if (!cur_tokenp) { cur_sectionp = strtok_r( next_sectionp, REPUTATION_CONFIG_SECTION_SEPERATORS, &next_sectionp); continue; } if ( !strcasecmp( cur_tokenp, REPUTATION_MEMCAP_KEYWORD )) { int value; char *endStr = NULL; cur_tokenp = strtok_r(next_tokenp, REPUTATION_CONFIG_VALUE_SEPERATORS, &next_tokenp); if ( !cur_tokenp ) { DynamicPreprocessorFatalMessage(" %s(%d) => No option to '%s'.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_MEMCAP_KEYWORD); } value = _dpd.SnortStrtol( cur_tokenp, &endStr, 10); if (( *endStr) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_MEMCAP_KEYWORD, MIN_MEMCAP, MAX_MEMCAP); } if (value < MIN_MEMCAP || value > MAX_MEMCAP) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %d and %d.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_MEMCAP_KEYWORD, MIN_MEMCAP, MAX_MEMCAP); } config->memcap = (uint32_t) value; } else if ( !strcasecmp( cur_tokenp, REPUTATION_BLACKLIST_KEYWORD ) ||!strcasecmp( cur_tokenp, REPUTATION_WHITELIST_KEYWORD )) { int numlines; char full_path_filename[PATH_MAX+1]; cur_tokenp = strtok_r( next_tokenp, REPUTATION_CONFIG_VALUE_SEPERATORS, &next_tokenp); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Check list size %s\n",cur_tokenp );); if(cur_tokenp == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Bad list filename in IP List.\n", *(_dpd.config_file), *(_dpd.config_line)); } errno = 0; UpdatePathToFile(full_path_filename,PATH_MAX, cur_tokenp); numlines = numLinesInFile(full_path_filename); if ((0 == numlines) && (0 != errno)) { char errBuf[STD_BUF]; #ifdef WIN32 snprintf(errBuf, STD_BUF, "%s", strerror(errno)); #else strerror_r(errno, errBuf, STD_BUF); #endif DynamicPreprocessorFatalMessage("%s(%d) => Unable to open address file %s, Error: %s\n", *(_dpd.config_file), *(_dpd.config_line), full_path_filename, errBuf); } if (totalLines + numlines < totalLines) { DynamicPreprocessorFatalMessage("%s(%d) => Too many entries in one file.\n", *(_dpd.config_file), *(_dpd.config_line)); } totalLines += numlines; } else if ( !strcasecmp( cur_tokenp, REPUTATION_WHITEACTION_KEYWORD )) { int i = 0; char WhiteActionKeyworBuff[STD_BUF]; WhiteActionKeyworBuff[0] = '\0'; cur_tokenp = strtok_r( next_tokenp, REPUTATION_CONFIG_VALUE_SEPERATORS, &next_tokenp); if (!cur_tokenp) { DynamicPreprocessorFatalMessage(" %s(%d) => Missing argument for %s\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_WHITEACTION_KEYWORD); } while(NULL != WhiteActionOption[i]) { if( !strcasecmp(WhiteActionOption[i],cur_tokenp)) { config->whiteAction = (WhiteAction) i; break; } _dpd.printfappend(WhiteActionKeyworBuff, STD_BUF, "[%s] ", WhiteActionOption[i] ); i++; } if (NULL == WhiteActionOption[i]) { DynamicPreprocessorFatalMessage(" %s(%d) => Invalid argument: %s for %s, use %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp, REPUTATION_WHITEACTION_KEYWORD, WhiteActionKeyworBuff); } } #ifdef SHARED_REP else if ( !strcasecmp( cur_tokenp, REPUTATION_SHARED_MEM_KEYWORD )) { if (Reputation_IsEmptyStr(next_tokenp)) { DynamicPreprocessorFatalMessage(" %s(%d) => Missing argument for %s," " please specify a path\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_SHARED_MEM_KEYWORD); } if (!CheckIPlistDir(next_tokenp)) { DynamicPreprocessorFatalMessage(" %s(%d) => Can't find or access the path: %s\n", *(_dpd.config_file), *(_dpd.config_line), next_tokenp); } config->sharedMem.path = strdup( (char*) next_tokenp ); if ( !config->sharedMem.path ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse Reputation options.\n"); } config->sharedMem.updateInterval = REPUTATION_DEFAULT_REFRESH_PERIOD; } #endif cur_sectionp = strtok_r( next_sectionp, REPUTATION_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Arguments token: %s\n",cur_sectionp );); } free(argcpyp); return totalLines; } /********************************************************************* * Function: ParseReputationArgs * * Parses and processes the configuration arguments * supplied in the Reputation preprocessor rule. * * Arguments: * * ReputationConfig *config: Reputation preprocessor configuration. * argp: Pointer to string containing the config arguments. * * RETURNS: Nothing. *********************************************************************/ void ParseReputationArgs(ReputationConfig *config, u_char* argp) { char* cur_sectionp = NULL; char* next_sectionp = NULL; char* argcpyp = NULL; #ifdef SHARED_REP long nprocs; #endif if (config == NULL) return; _dpd.logMsg("Reputation config: \n"); /*Default values*/ config->memcap = REPUTATION_DEFAULT_MEMCAP; config->priority = WHITELISTED_TRUST; config->nestedIP = INNER; config->whiteAction = UNBLACK; config->emptySegment = NULL; config->localSegment = NULL; config->memsize = 0; config->memCapReached = false; #ifdef SHARED_REP nprocs = sysconf(_SC_NPROCESSORS_CONF); if (nprocs < 1) { _dpd.logMsg("WARNING: Could not determine the number of CPUs on the system, " "defaulting to a max of %d concurrent shared memory users.\n", REPUTATION_DEFAULT_SHARED_MEM_INSTANCES); config->sharedMem.maxInstances = REPUTATION_DEFAULT_SHARED_MEM_INSTANCES; } else if (nprocs < MIN_SHARED_MEM_INSTANCES) { _dpd.logMsg("WARNING: Fewer CPUs found than the minimum number of " "concurrent shared memory users, defaulting to a max of %d.\n", MIN_SHARED_MEM_INSTANCES); config->sharedMem.maxInstances = MIN_SHARED_MEM_INSTANCES; } else config->sharedMem.maxInstances = nprocs; #endif /* Sanity check(s) */ if ( !argp ) { _dpd.logMsg("WARNING: Can't find any %s/%s entries. " "Reputation Preprocessor disabled.\n", REPUTATION_WHITELIST_KEYWORD, REPUTATION_BLACKLIST_KEYWORD); return; } argcpyp = strdup( (char*) argp ); if ( !argcpyp ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse Reputation options.\n"); return; } DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Reputation configurations: %s\n",argcpyp );); /*We need to parse the memcap, numEntries earlier, then create iplist table*/ config->numEntries = EstimateNumEntries(config, argp ); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Estimated number of entries: %d\n",config->numEntries );); if ((config->numEntries <= 0) && (!config->sharedMem.path)) { _dpd.logMsg("WARNING: Can't find any %s/%s entries. " "Reputation Preprocessor disabled.\n", REPUTATION_WHITELIST_KEYWORD, REPUTATION_BLACKLIST_KEYWORD); free(argcpyp); return; } if (!config->sharedMem.path) IpListInit(config->numEntries + 1,config); cur_sectionp = strtok_r( argcpyp, REPUTATION_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Arguments token: %s\n",cur_sectionp );); /*Reset the log message count*/ total_duplicates = 0; while ( cur_sectionp ) { char* cur_config; char* cur_tokenp = strtok( cur_sectionp, REPUTATION_CONFIG_VALUE_SEPERATORS); if (!cur_tokenp) { cur_sectionp = strtok_r( next_sectionp, REPUTATION_CONFIG_SECTION_SEPERATORS, &next_sectionp); continue; } cur_config = cur_tokenp; if ( !strcasecmp( cur_tokenp, REPUTATION_SCANLOCAL_KEYWORD )) { config->scanlocal = 1; } else if ( !strcasecmp( cur_tokenp, REPUTATION_MEMCAP_KEYWORD )) { cur_tokenp = strtok( NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); /* processed before */ } else if ( !strcasecmp( cur_tokenp, REPUTATION_BLACKLIST_KEYWORD )) { cur_tokenp = strtok( NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Loading %s from %s\n", REPUTATION_BLACKLIST_KEYWORD, cur_tokenp );); if(cur_tokenp == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Bad list filename in IP List.\n", *(_dpd.config_file), *(_dpd.config_line)); } if (!config->sharedMem.path) LoadListFile(cur_tokenp, config->local_black_ptr, config); else { _dpd.logMsg("WARNING: %s(%d) => List file %s is not loaded " "when using shared memory.\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); } } else if ( !strcasecmp( cur_tokenp, REPUTATION_WHITELIST_KEYWORD )) { cur_tokenp = strtok( NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Loading %s from %s\n", REPUTATION_WHITELIST_KEYWORD, cur_tokenp );); if(cur_tokenp == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Bad list filename in IP List.\n", *(_dpd.config_file), *(_dpd.config_line)); } if (!config->sharedMem.path) LoadListFile(cur_tokenp, config->local_white_ptr, config); else { _dpd.logMsg("WARNING: %s(%d) => List file %s is not loaded " "when using shared memory.\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); } } else if ( !strcasecmp( cur_tokenp, REPUTATION_PRIORITY_KEYWORD )) { cur_tokenp = strtok( NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); if (!cur_tokenp) { DynamicPreprocessorFatalMessage(" %s(%d) => Missing argument for %s\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_PRIORITY_KEYWORD); return; } if((strlen(REPUTATION_BLACKLIST_KEYWORD) == strlen (cur_tokenp)) && !strcasecmp(REPUTATION_BLACKLIST_KEYWORD,cur_tokenp)) { config->priority = BLACKLISTED; } else if((strlen(REPUTATION_WHITELIST_KEYWORD) == strlen (cur_tokenp)) && !strcasecmp(REPUTATION_WHITELIST_KEYWORD,cur_tokenp)) { config->priority = WHITELISTED_TRUST; if (UNBLACK == config->whiteAction) { _dpd.logMsg("WARNING: %s(%d) => Keyword %s for %s is not applied " "when white action is unblack.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_PRIORITY_KEYWORD, REPUTATION_WHITELIST_KEYWORD); config->priority = WHITELISTED_UNBLACK; } } else { DynamicPreprocessorFatalMessage(" %s(%d) => Invalid argument: %s for %s," " Use [%s] or [%s]\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp, REPUTATION_PRIORITY_KEYWORD, REPUTATION_BLACKLIST_KEYWORD, REPUTATION_WHITELIST_KEYWORD); return; } } else if ( !strcasecmp( cur_tokenp, REPUTATION_NESTEDIP_KEYWORD )) { int i = 0; char NestIPKeyworBuff[STD_BUF]; NestIPKeyworBuff[0] = '\0'; cur_tokenp = strtok( NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); if (!cur_tokenp) { DynamicPreprocessorFatalMessage(" %s(%d) => Missing argument for %s\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_NESTEDIP_KEYWORD); return; } while(NULL != NestedIPKeyword[i]) { if((strlen(NestedIPKeyword[i]) == strlen (cur_tokenp)) && !strcasecmp(NestedIPKeyword[i],cur_tokenp)) { config->nestedIP = (NestedIP) i; break; } _dpd.printfappend(NestIPKeyworBuff, STD_BUF, "[%s] ", NestedIPKeyword[i] ); i++; } if (NULL == NestedIPKeyword[i]) { DynamicPreprocessorFatalMessage(" %s(%d) => Invalid argument: %s for %s, use %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp, REPUTATION_NESTEDIP_KEYWORD, NestIPKeyworBuff); return; } } else if ( !strcasecmp( cur_tokenp, REPUTATION_WHITEACTION_KEYWORD )) { cur_tokenp = strtok( NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); /* processed before */ } #ifdef SHARED_REP else if ( !strcasecmp( cur_tokenp, REPUTATION_SHARED_MEM_KEYWORD )) { cur_sectionp = strtok_r( next_sectionp, REPUTATION_CONFIG_SECTION_SEPERATORS, &next_sectionp); continue; /* processed before */ } else if ( !strcasecmp( cur_tokenp, REPUTATION_SHARED_REFRESH_KEYWORD )) { unsigned long value; char *endStr = NULL; if (!config->sharedMem.path) { DynamicPreprocessorFatalMessage(" %s(%d) => Specify option '%s' when using option '%s'.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_SHARED_MEM_KEYWORD, REPUTATION_SHARED_REFRESH_KEYWORD); } cur_tokenp = strtok(NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); if ( !cur_tokenp ) { DynamicPreprocessorFatalMessage(" %s(%d) => No option to '%s'.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_SHARED_REFRESH_KEYWORD); } value = _dpd.SnortStrtoul( cur_tokenp, &endStr, 10); if ( *endStr) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %u and %u.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_SHARED_REFRESH_KEYWORD, MIN_SHARED_MEM_REFRESH_PERIOD, MAX_SHARED_MEM_REFRESH_PERIOD); } if (value < MIN_SHARED_MEM_REFRESH_PERIOD || value > MAX_SHARED_MEM_REFRESH_PERIOD || (errno == ERANGE)) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified for %s is out of " "bounds. Please specify an integer between %u and %u.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_SHARED_REFRESH_KEYWORD, MIN_SHARED_MEM_REFRESH_PERIOD, MAX_SHARED_MEM_REFRESH_PERIOD); } config->sharedMem.updateInterval = (uint32_t) value; } else if (!strcasecmp(cur_tokenp, REPUTATION_SHARED_MAX_INSTANCES_KEYWORD)) { unsigned long value; char *endStr = NULL; cur_tokenp = strtok(NULL, REPUTATION_CONFIG_VALUE_SEPERATORS); if (!cur_tokenp) { DynamicPreprocessorFatalMessage(" %s(%d) => No option to '%s'.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_SHARED_MAX_INSTANCES_KEYWORD); } value = _dpd.SnortStrtoul( cur_tokenp, &endStr, 10); if (*endStr || (errno == ERANGE) || value < MIN_SHARED_MEM_INSTANCES || value > MAX_SHARED_MEM_INSTANCES) { DynamicPreprocessorFatalMessage(" %s(%d) => Bad value specified for %s. " "Please specify an integer between %u and %u.\n", *(_dpd.config_file), *(_dpd.config_line), REPUTATION_SHARED_MAX_INSTANCES_KEYWORD, MIN_SHARED_MEM_INSTANCES, MAX_SHARED_MEM_INSTANCES); } config->sharedMem.maxInstances = value; } #endif else { DynamicPreprocessorFatalMessage(" %s(%d) => Invalid argument: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_tokenp); return; } /*Check whether too many parameters*/ if (NULL != strtok( NULL, REPUTATION_CONFIG_VALUE_SEPERATORS)) { DynamicPreprocessorFatalMessage("%s(%d) => Too many arguments: %s\n", *(_dpd.config_file), *(_dpd.config_line), cur_config); } cur_sectionp = strtok_r( next_sectionp, REPUTATION_CONFIG_SECTION_SEPERATORS, &next_sectionp); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Arguments token: %s\n",cur_sectionp );); } DisplayIPlistStats(config); DisplayReputationConfig(config); free(argcpyp); } void ReputationRepInfo(IPrepInfo * repInfo, uint8_t *base, char *repInfoBuff, int bufLen) { char *index = repInfoBuff; int len = bufLen -1 ; int writed; writed = snprintf(index, len, "Reputation Info: "); if (writed >= len || writed < 0) return; index += writed; len -= writed; while(repInfo) { int i; for(i = 0; i < NUM_INDEX_PER_ENTRY; i++) { writed = snprintf(index, len, "%d,",repInfo->listIndexes[i]); if (writed >= len || writed < 0) return; else { index += writed; len -=writed; } } writed = snprintf(index, len, "->"); if (writed >= len || writed < 0) return; else { index += writed; len -=writed; } if (!repInfo->next) break; repInfo = (IPrepInfo *)(&base[repInfo->next]); } } #ifdef DEBUG_MSGS void ReputationPrintRepInfo(IPrepInfo * repInfo, uint8_t *base) { char repInfoBuff[STD_BUF]; int len = STD_BUF -1 ; repInfoBuff[STD_BUF -1] = '\0'; ReputationRepInfo(repInfo, base, repInfoBuff, len); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Reputation Info: %s \n", repInfoBuff);); } #endif snort-2.9.20/src/dynamic-preprocessors/reputation/Makefile.am0000555000175000017500000000274014230012554022441 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs -I$(srcdir)/includes dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_reputation_preproc.la libsf_reputation_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_reputation_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_reputation_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sf_ip.c \ ../include/sfrt.c \ ../include/sfrt_dir.c \ ../include/sfrt_flat.c \ ../include/sfrt_flat_dir.c \ ../include/segment_mem.c \ ../include/sfPolicyUserData.c endif if HAVE_SHARED_REP libsf_reputation_preproc_la_SOURCES = \ spp_reputation.c \ spp_reputation.h \ reputation_config.c \ reputation_config.h \ reputation_utils.c \ reputation_utils.h \ reputation_debug.h \ ./shmem/sflinux_helpers.c \ ./shmem/sflinux_helpers.h \ ./shmem/shmem_common.h \ ./shmem/shmem_config.h \ ./shmem/shmem_config.c \ ./shmem/shmem_datamgmt.h \ ./shmem/shmem_datamgmt.c \ ./shmem/shmem_lib.h \ ./shmem/shmem_lib.c \ ./shmem/shmem_mgmt.h \ ./shmem/shmem_mgmt.c else libsf_reputation_preproc_la_SOURCES = \ spp_reputation.c \ spp_reputation.h \ reputation_config.c \ reputation_config.h \ reputation_utils.c \ reputation_utils.h \ reputation_debug.h endif EXTRA_DIST = \ sf_reputation.vcxproj \ sf_reputation.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/0000755000175000017500000000000014242725716021530 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_config.h0000644000175000017500000000676314241076177024352 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_config.h // @author Pramod Chandrashekar #ifndef _SHMEMCFG_H_ #define _SHMEMCFG_H_ #include #include "shmem_datamgmt.h" //defines shmemdata filelist #include "shmem_common.h" #define SHMEM_MGMT "SFShmemMgmt" #define MAX_SEGMENTS 2 #define WRITE 0 #define READ 1 #define SERVER 0 #define CLIENT1 1 #define CLIENT2 2 #define STARTUP 1 #define RELOAD 0 #define ACTIVE 1 #define INACTIVE 0 #define NO_DATASEG -1 #define NO_ZEROSEG -2 #define UNMAP_OLDSEG -3 #define SHMEM_ERR -4 #define ZEROSEG 100 #define GO_INACTIVE 10 #define NUMA_0 0 #define NUMA_1 1 #define GROUP_0 0 #define SLEEP_TIME 2 // in micro seconds #define TBMAP 99 #define UNUSED_TIMEOUT -1 //this number is multiplied with outofband check time to determine timeout.If set to -1 it disables expiring timed out instances. #define OUT_OF_BAND_CHEK_TIME 10 typedef struct shmemUserInfo { uint32_t instance_num; //unique ID for each snort instance int instance_type; // READ or WRITE int dataset; // IPRep int group_id; // 0,1... int numa_node; char mgmtSeg[MAX_NAME]; char dataSeg[MAX_SEGMENTS][MAX_NAME]; char path[MAX_NAME]; uint32_t instance_polltime; }ShmemUserInfo; typedef struct { const char *const name; const uint32_t type; } DatasetInfo; typedef struct shmemDataManagmentFunctions { int (*CreatePerProcessZeroSegment)(void*** data_ptr); uint32_t (*GetSegmentSize)(ShmemDataFileList** file_list, int file_count); int (*LoadShmemData)(void* data_ptr, ShmemDataFileList** file_list, int file_count); } ShmemDataMgmtFunctions; typedef int (*CreateMallocZero)(void***); typedef uint32_t (*GetDataSize)(ShmemDataFileList**, int); typedef int (*LoadData)(void*,ShmemDataFileList**,int); extern ShmemDataMgmtFunctions *dmfunc_ptr; extern ShmemUserInfo *shmusr_ptr; void PrintConfig(void); int InitShmemUser(uint32_t instance_num, int instance_type, int dataset, int group_id, int numa_node, const char* path, uint32_t instance_polltime, uint16_t max_instances); int InitShmemDataMgmtFunctions( CreateMallocZero create_malloc_zero, GetDataSize get_data_size, LoadData load_data); void FreeShmemUser(void); void FreeShmemDataMgmtFunctions(void); #endif snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/sflinux_helpers.h0000644000175000017500000000234114241076171025104 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file sflinux_helpers.h // @author Pramod Chandrashekar #ifndef _SFLINUX_HELPERS_H_ #define _SFLINUX_HELPERS_H_ int CheckNumaNodes(void); #endif snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_lib.h0000644000175000017500000000265614241076216023642 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_lib.h // @author Pramod Chandrashekar #ifndef _SHMEMLIB_H_ #define _SHMEMLIB_H_ #include #include int ShmemExists(const char *shmemName, off_t *size); void* ShmemMap(const char* segment_name, uint32_t size, int mode); void ShmemUnlink(const char *shmemName); void ShmemDestroy(const char *shmemName); #endif snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_mgmt.c0000644000175000017500000006034714241076223024032 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_mgmt.c // @author Pramod Chandrashekar #include "shmem_lib.h" #include "shmem_mgmt.h" #include ShmemMgmtData* mgmt_ptr = NULL; void* zeroseg_ptr = NULL; unsigned int usec = SLEEP_TIME; static const char* const MODULE_NAME = "SharedMemMgmt"; static void SetShmemMgmtVariables(int value, uint32_t instance_num) { int i; void* temp_zerosegptr; if (shmusr_ptr->instance_num == instance_num) temp_zerosegptr = zeroseg_ptr; else temp_zerosegptr = mgmt_ptr->instance[instance_num].shmemZeroPtr; if (value == GO_INACTIVE) { mgmt_ptr->instance[instance_num].goInactive = 1; value = mgmt_ptr->instance[instance_num].active; } else { mgmt_ptr->instance[instance_num].goInactive = 0; } mgmt_ptr->instance[instance_num].active = value; mgmt_ptr->instance[instance_num].version = 0; mgmt_ptr->instance[instance_num].activeSegment = NO_DATASEG; mgmt_ptr->instance[instance_num].prevSegment = NO_DATASEG; mgmt_ptr->instance[instance_num].updateTime = time(NULL); mgmt_ptr->instance[instance_num].shmemCurrPtr = temp_zerosegptr; mgmt_ptr->instance[instance_num].shmemZeroPtr = temp_zerosegptr; for (i=0; iinstance[instance_num].shmemSegActiveFlag[i] = 0; for (i=0; iinstance[instance_num].shmemSegmentPtr[i] = temp_zerosegptr; } static void UnsetGoInactive() { int i; for (i = 0; i < mgmt_ptr->maxInstances; i++) mgmt_ptr->instance[i].goInactive = 0; } static int MapShmemMgmt(unsigned int max_instances) { size_t nBytes = sizeof(ShmemMgmtData) + sizeof(shmemInstance) * max_instances; off_t shmSize; int mgmtExists, i; mgmtExists = ShmemExists(shmusr_ptr->mgmtSeg, &shmSize); if (mgmtExists) { if (shmSize == nBytes) { mgmt_ptr = (ShmemMgmtData *) ShmemMap(shmusr_ptr->mgmtSeg, nBytes, shmusr_ptr->instance_type); if (!mgmt_ptr) return SF_EINVAL; /* Simple sanity check: If the segment is the expected size, it *should* have the same instance count. */ if (mgmt_ptr->maxInstances == max_instances) { _dpd.logMsg("Mapped shared management region of size %zu as a %s.\n", nBytes, (shmusr_ptr->instance_type == READ) ? "reader" : "writer"); return SF_SUCCESS; } else if (shmusr_ptr->instance_type == READ) { _dpd.errMsg("%s: Expected instance count does not match that of the management segment (%u vs %u). " "Please remove the segment and try again.\n", MODULE_NAME, max_instances, mgmt_ptr->maxInstances); munmap(mgmt_ptr, nBytes); mgmt_ptr = NULL; return SF_EINVAL; } else /* Writer */ { _dpd.errMsg("%s: Expected instance count does not match that of the management segment (%u vs %u). " "Unlinking and recreating it.\n", MODULE_NAME, max_instances, mgmt_ptr->maxInstances); munmap(mgmt_ptr, nBytes); mgmt_ptr = NULL; ShmemUnlink(shmusr_ptr->mgmtSeg); mgmtExists = 0; } } else if (shmusr_ptr->instance_type == READ) { _dpd.errMsg("%s: Management segment shared memory size does not match the expected size (%u vs %u). " "Refusing to use it.\n", MODULE_NAME, shmSize, nBytes); return SF_EINVAL; } else /* Writer */ { _dpd.logMsg("%s: Management segment shared memory size does not match the expected size (%u vs %u). " "Unlinking and recreating it.\n", MODULE_NAME, shmSize, nBytes); ShmemUnlink(shmusr_ptr->mgmtSeg); mgmtExists = 0; } } /* This can become false in the wrong size as a writer scenario. */ if (!mgmtExists) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "No Shmem mgmt segment present\n");); /* Readers must give up if there is no existing management segment. */ if (shmusr_ptr->instance_type == READ) return SF_EINVAL; /* On the other hand, writers create a new management segment and initialize its values. */ mgmt_ptr = (ShmemMgmtData *) ShmemMap(shmusr_ptr->mgmtSeg, nBytes, shmusr_ptr->instance_type); if (!mgmt_ptr) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Failed to create shmem mgmt segment\n");); return SF_EINVAL; } mgmt_ptr->activeSegment = NO_DATASEG; mgmt_ptr->maxInstances = max_instances; for (i = 0; i < MAX_SEGMENTS; i++) { mgmt_ptr->segment[i].version = 0; mgmt_ptr->segment[i].active = 0; mgmt_ptr->segment[i].size = 0; } UnsetGoInactive(); } return SF_SUCCESS; } static void DoHeartbeat() { uint32_t instance_num = shmusr_ptr->instance_num; if (mgmt_ptr) { mgmt_ptr->instance[instance_num].updateTime = time(NULL); } return; } static void ForceShutdown() { int currActiveSegment; _dpd.logMsg(" Reputation Preprocessor: Shared memory is disabled. \n"); if (!mgmt_ptr) return; mgmt_ptr->instance[shmusr_ptr->instance_num].shmemCurrPtr = mgmt_ptr->instance[shmusr_ptr->instance_num].shmemZeroPtr; if ((currActiveSegment = mgmt_ptr->instance[shmusr_ptr->instance_num].activeSegment) >= 0) { mgmt_ptr->instance[shmusr_ptr->instance_num].activeSegment = NO_DATASEG; mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[currActiveSegment] = 0; } return; } //client side calls for shared memory int CheckForSharedMemSegment(unsigned int max_instances) { void *shmem_ptr = NULL; int currActive = NO_DATASEG, newSegment = NO_DATASEG; uint32_t size = 0; if (!mgmt_ptr) { if (MapShmemMgmt(max_instances)) return newSegment; SetShmemMgmtVariables(ACTIVE,shmusr_ptr->instance_num); } if (mgmt_ptr->instance[shmusr_ptr->instance_num].goInactive) goto exit; if ((currActive = mgmt_ptr->activeSegment) >= 0) { if ( mgmt_ptr->instance[shmusr_ptr->instance_num].activeSegment != currActive && mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[currActive] != TBMAP ) { //new segment available and not mapped already mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[currActive] = TBMAP; if ((size = mgmt_ptr->segment[currActive].size) != 0) { if ((shmem_ptr = ShmemMap(shmusr_ptr->dataSeg[currActive],size,READ)) != NULL) { //Store Data segment pointer for instance mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[currActive] = shmem_ptr; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Shmem ptr for segment %d is %p\n",currActive,shmem_ptr);); newSegment = currActive; } else { currActive = NO_DATASEG; } } else { mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[currActive] = 0; } } } else if (mgmt_ptr->instance[shmusr_ptr->instance_num].activeSegment >= 0) { ForceShutdown(); goto exit; } DoHeartbeat(); exit: DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "new segment being returned is %d\n", newSegment);); return newSegment; } int InitShmemReader ( uint32_t instance_num, int dataset, int group_id, int numa_node, const char* path, void*** data_ptr, uint32_t instance_polltime, unsigned int max_instances) { int segment_number = NO_ZEROSEG; if (InitShmemUser(instance_num,READ,dataset,group_id,numa_node,path,instance_polltime,max_instances)) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Could not initialize config data \n");); return segment_number; } if (dmfunc_ptr->CreatePerProcessZeroSegment(data_ptr)) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Could not initialize zero segment\n");); return segment_number; } zeroseg_ptr = **data_ptr; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Address of zero segment is %p\n",zeroseg_ptr);); if ((segment_number = CheckForSharedMemSegment(max_instances)) >=0) { SwitchToActiveSegment(segment_number,data_ptr); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Switched to segment %d\n",segment_number);); } return segment_number; } static int FindFirstUnusedShmemSegment() { int i; for (i=0; isegment[i].active != 1) return i; } return NO_DATASEG; } static int FindActiveSharedMemDataSegmentVersion() { if (mgmt_ptr->activeSegment < 0) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Active segment does not exist\n");); return 0; } DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Active segment is %d and current version is %u\n", mgmt_ptr->activeSegment,mgmt_ptr->segment[mgmt_ptr->activeSegment].version);); return mgmt_ptr->segment[mgmt_ptr->activeSegment].version; } static int MapShmemDataSegmentForWriter(uint32_t size, uint32_t disk_version, int *mode) { int available_segment = NO_DATASEG; uint32_t active_version = 0; void* shmem_ptr = NULL; *mode = WRITE; if ((active_version = FindActiveSharedMemDataSegmentVersion()) == disk_version ) { if ((available_segment = mgmt_ptr->activeSegment) >= 0) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Attaching to segment %d\n", available_segment);); *mode = READ; size = mgmt_ptr->segment[available_segment].size; } else { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "No active segment to attach to\n");); goto exit; } } if (*mode == WRITE) { if ((available_segment = FindFirstUnusedShmemSegment()) < 0) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "No more segments available, all are in use\n");); goto exit; } DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Shared memory segment %d will be initialized\n",available_segment);); } mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[available_segment] = TBMAP; if ((shmem_ptr = ShmemMap(shmusr_ptr->dataSeg[available_segment],size,*mode)) != NULL) { //store data segment pointer for instance mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[available_segment] = shmem_ptr; } else { mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[available_segment] = 0; available_segment = SHMEM_ERR; } exit: return available_segment; } static void ShutdownSegment(int32_t segment_num) { mgmt_ptr->segment[segment_num].active = 0; mgmt_ptr->segment[segment_num].version = 0; if (mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[segment_num] != mgmt_ptr->instance[shmusr_ptr->instance_num].shmemZeroPtr) { munmap(mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[segment_num], mgmt_ptr->segment[segment_num].size); } ShmemDestroy(shmusr_ptr->dataSeg[segment_num]); mgmt_ptr->segment[segment_num].size = 0; mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[segment_num] = mgmt_ptr->instance[shmusr_ptr->instance_num].shmemZeroPtr; } // writer side static int InitSharedMemDataSegmentForWriter(uint32_t size, uint32_t disk_version) { int segment_num = NO_DATASEG, mode = -1; int rval; if ((segment_num = MapShmemDataSegmentForWriter(size,disk_version,&mode)) < 0) goto exit; if (mode == WRITE) { if ((rval = dmfunc_ptr->LoadShmemData((void *)( mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[segment_num]), filelist_ptr, filelist_count)) != SF_SUCCESS) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Loading file into shared memory failed\n");); ShutdownSegment(segment_num); segment_num = SHMEM_ERR; goto exit; } mgmt_ptr->segment[segment_num].size = size; if (mgmt_ptr->activeSegment != segment_num) mgmt_ptr->activeSegment = segment_num; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Active segment is %d\n",mgmt_ptr->activeSegment);); mgmt_ptr->segment[segment_num].active = 1; mgmt_ptr->segment[segment_num].version = disk_version; ManageUnusedSegments(); } exit: return segment_num; } int LoadSharedMemDataSegmentForWriter(int startup) { int segment_num = NO_DATASEG; uint32_t size; uint32_t disk_version, shmem_version; if ( !mgmt_ptr ) return NO_DATASEG; shmem_version = FindActiveSharedMemDataSegmentVersion(); //if version file is not present(open source user), increment version and reload. if (GetLatestShmemDataSetVersionOnDisk(&disk_version) == SF_SUCCESS) { if (disk_version > 0) { if ((shmem_version == disk_version) && !startup) goto exit; } else { goto force_shutdown; } } else { disk_version = shmem_version + 1; if (disk_version == 0) disk_version++; } if ( GetSortedListOfShmemDataFiles( ) != SF_SUCCESS ) return SHMEM_ERR; #ifdef DEBUG_MSGS PrintDataFiles(); #endif if ((size = dmfunc_ptr->GetSegmentSize(filelist_ptr, filelist_count)) != ZEROSEG) { segment_num = InitSharedMemDataSegmentForWriter(size,disk_version); goto exit; } force_shutdown: //got back zero which means its time to shutdown shared memory mgmt_ptr->activeSegment = NO_DATASEG; ForceShutdown(); exit: return segment_num; } int InitShmemWriter( uint32_t instance_num, int dataset, int group_id, int numa_node, const char* path, void*** data_ptr, uint32_t instance_polltime, unsigned int max_instances) { int segment_number = NO_ZEROSEG; if (InitShmemUser(instance_num,WRITE,dataset,group_id,numa_node,path,instance_polltime,max_instances)) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Could not initialize shmem writer config\n");); goto exit; } if (dmfunc_ptr->CreatePerProcessZeroSegment(data_ptr)) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Could not initialize zero segment\n");); goto cleanup_exit; } zeroseg_ptr = **data_ptr; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Address of zero segment is %p\n",zeroseg_ptr);); if (MapShmemMgmt(max_instances)) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Could not initialize shared memory management segment\n");); FreeShmemDataFileList(); goto cleanup_exit; } ManageUnusedSegments(); SetShmemMgmtVariables(ACTIVE,shmusr_ptr->instance_num); //valid segments are 0 through N if ((segment_number = LoadSharedMemDataSegmentForWriter(STARTUP)) >= 0) SwitchToActiveSegment(segment_number,data_ptr); //pointer switch goto exit; cleanup_exit: FreeShmemUser(); exit: return segment_number; } //switch to active DB void SwitchToActiveSegment(int segment_num, void*** data_ptr) { if ((segment_num < 0)|| (!mgmt_ptr)) return; mgmt_ptr->instance[shmusr_ptr->instance_num].shmemCurrPtr = mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[segment_num]; *data_ptr = (void *)(&mgmt_ptr->instance[shmusr_ptr->instance_num].shmemCurrPtr); mgmt_ptr->instance[shmusr_ptr->instance_num].prevSegment = mgmt_ptr->instance[shmusr_ptr->instance_num].activeSegment; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Prev segment has been set to %d\n", mgmt_ptr->instance[shmusr_ptr->instance_num].prevSegment);); mgmt_ptr->instance[shmusr_ptr->instance_num].activeSegment = segment_num; mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[segment_num] = 1; } void UnmapInactiveSegments() { int i, segment_num; if (!mgmt_ptr) return; for (i=0; iinstance[shmusr_ptr->instance_num].activeSegment) { if (shmusr_ptr->instance_type != WRITE) { if ((segment_num = mgmt_ptr->instance[shmusr_ptr->instance_num].prevSegment) != NO_DATASEG) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Unmapping segment %d which has address %p and size %u\n", segment_num,mgmt_ptr->instance[shmusr_ptr->instance_num]. shmemSegmentPtr[segment_num],mgmt_ptr->segment[segment_num].size);); munmap(mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[segment_num], mgmt_ptr->segment[segment_num].size); ShmemUnlink(shmusr_ptr->dataSeg[segment_num]); mgmt_ptr->instance[shmusr_ptr->instance_num].prevSegment = NO_DATASEG; mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegmentPtr[i] = mgmt_ptr->instance[shmusr_ptr->instance_num].shmemZeroPtr; } } mgmt_ptr->instance[shmusr_ptr->instance_num].shmemSegActiveFlag[i] = 0; } } DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Active segment for instance %u is %d\n", shmusr_ptr->instance_num,mgmt_ptr->instance[shmusr_ptr->instance_num].activeSegment);); return; } static void ExpireTimedoutInstances() { int i; int64_t max_timeout; time_t current_time = time(NULL); /*timeout will be at least 60 seconds*/ max_timeout = UNUSED_TIMEOUT * (int64_t) shmusr_ptr->instance_polltime + 60; if (max_timeout > UINT32_MAX) max_timeout = UINT32_MAX; for(i = 0; i < mgmt_ptr->maxInstances; i++) { if (!mgmt_ptr->instance[i].active) continue; if ((int64_t)current_time > mgmt_ptr->instance[i].updateTime + max_timeout) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Instance %d has expired, last update %jd and current time is %jd\n", i,(intmax_t)mgmt_ptr->instance[i].updateTime,(intmax_t)current_time);); SetShmemMgmtVariables(GO_INACTIVE, i); } } return; } //WRITER only void ManageUnusedSegments() { int s, i, in_use; if (!mgmt_ptr) return; DoHeartbeat(); //writer heartbeat if (UNUSED_TIMEOUT != -1) ExpireTimedoutInstances(); for (s = 0; s < MAX_SEGMENTS; s++) { if (!mgmt_ptr->segment[s].active || (mgmt_ptr->activeSegment == s)) continue; in_use = 0; for (i = 0; i < mgmt_ptr->maxInstances; i++) { if (mgmt_ptr->instance[i].active && !mgmt_ptr->instance[i].goInactive && mgmt_ptr->instance[i].shmemSegActiveFlag[s]) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Instance %u is still using segment %d\n", i, s);); in_use++; } } if (!in_use) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Shutting down segment %d\n", s);); ShutdownSegment(s); } } if (UNUSED_TIMEOUT != -1) UnsetGoInactive(); } int ShutdownSharedMemory() { if (mgmt_ptr) SetShmemMgmtVariables(INACTIVE,shmusr_ptr->instance_num); FreeShmemUser(); FreeShmemDataMgmtFunctions(); FreeShmemDataFileList(); return SF_SUCCESS; } void ShmemMgmtInfo(char *buf, int bufLen) { uint32_t i; int writed; int len = bufLen -1; char *index = buf; if (!mgmt_ptr) return; for (i = 0; i < mgmt_ptr->maxInstances; i++) { shmemInstance *shmem_info = (shmemInstance *) &(mgmt_ptr->instance[i]); if (shmem_info->shmemCurrPtr) { writed = snprintf(index, len, "instance:%u active:%d goInactive:%d updateTime:%jd\n", i,(int)shmem_info->active, (int)shmem_info->goInactive, (intmax_t)shmem_info->updateTime); if (writed >= len || writed < 0) return; index += writed; len -= writed; writed = snprintf(index, len, "instance:%u activeSegment:%d prevSegment:%d currentPtr:%p zeroPtr:%p\n", i,(int)shmem_info->activeSegment, (int)shmem_info->prevSegment, (void *)shmem_info->shmemCurrPtr, (void *)shmem_info->shmemZeroPtr); if (writed >= len || writed < 0) return; index += writed; len -= writed; } } for (i=0; isegment[i]); writed = snprintf(index, len, "segment:%u active:%d version:%u\n", i,(int)shmem_seg->active,(uint32_t)shmem_seg->version); if (writed >= len || writed < 0) return; index += writed; len -= writed; } writed = snprintf(index, len, "active segment:%d\n\n",mgmt_ptr->activeSegment); /* returning either way */ } void PrintShmemMgmtInfo() { uint32_t i; if (!mgmt_ptr) return; for (i = 0; i < mgmt_ptr->maxInstances; i++) { shmemInstance *shmem_info = (shmemInstance *) &(mgmt_ptr->instance[i]); if (shmem_info->shmemCurrPtr) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "instance:%u active:%d goInactive:%d updateTime:%jd\n", i,(int)shmem_info->active, (int)shmem_info->goInactive, (intmax_t)shmem_info->updateTime);); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "instance:%u activeSegment:%d prevSegment:%d currentPtr:%p zeroPtr:%p\n", i,(int)shmem_info->activeSegment, (int)shmem_info->prevSegment, (void *)shmem_info->shmemCurrPtr, (void *)shmem_info->shmemZeroPtr);); } } for (i=0; isegment[i]);); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "segment:%u active:%d version:%u\n", i,(int)shmem_seg->active,(uint32_t)shmem_seg->version);); } DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "active segment:%d\n\n",mgmt_ptr->activeSegment);); } snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_lib.c0000644000175000017500000000763514241076210023631 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_lib.c // @author Pramod Chandrashekar #include #include #include #include #include "shmem_mgmt.h" #include "shmem_lib.h" static const char* const MODULE_NAME = "ShmemLib"; static int ShmemOpen(const char *shmemName, uint32_t size, int mode) { int fd, flags; mode_t prev_mask; if (mode == WRITE) flags = (O_CREAT | O_RDWR); else if (mode == READ) flags = O_RDWR; else { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Invalid mode specified\n");); return -1; } prev_mask = umask(0); if ( (fd = shm_open(shmemName, flags, (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) )) == -1 ) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Unable to open shared memory\n");); umask(prev_mask); return -1; } umask(prev_mask); if (ftruncate(fd, size) == -1) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Unable to open shared memory\n");); return -1; } _dpd.logMsg(" Reputation Preprocessor: Size of shared memory segment %s is %u\n", shmemName, size); return fd; } static void *ShmemMMap (int fd, uint32_t size) { void *shmem_ptr; if ((shmem_ptr = mmap(0, size,(PROT_READ | PROT_WRITE),MAP_SHARED,fd,0)) == MAP_FAILED ) return NULL; return shmem_ptr; } int ShmemExists(const char *shmemName, off_t *size) { struct stat sb; int fd; if ((fd = shm_open(shmemName,(O_RDWR),(S_IRUSR))) < 0 ) return 0; if (size) { if (fstat(fd, &sb)) return 0; *size = sb.st_size; } close(fd); return SF_EEXIST; } void ShmemUnlink(const char *shmemName) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Unlinking segment %\n",shmemName);); shm_unlink(shmemName); } void ShmemDestroy(const char *shmemName) { if (!ShmemExists(shmemName, NULL)) return; ShmemUnlink(shmemName); unlink(shmemName); _dpd.logMsg(" Reputation Preprocessor: %s is freed\n", shmemName); } void* ShmemMap(const char* segment_name, uint32_t size, int mode) { int fd = 0; void *shmem_ptr = NULL; if ((mode == WRITE) && ShmemExists(segment_name, NULL)) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Cannot create shared memory segment %s, already exists\n", segment_name);); mode = READ; } if ((fd = ShmemOpen(segment_name,size,mode)) == -1) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Failed to open shm %s\n",segment_name);); return NULL; } if ((shmem_ptr = ShmemMMap(fd,size)) == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Failed to mmmap %s\n",segment_name);); } close(fd); return shmem_ptr; } snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_mgmt.h0000644000175000017500000000545314241076225024036 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_mgmt.h // @author Pramod Chandrashekar #ifndef _SHMEMMGMT_H_ #define _SHMEMMGMT_H_ #include #include #include "shmem_config.h" typedef struct _shmemInstance { int active; int goInactive; uint32_t version; time_t updateTime; int activeSegment; int prevSegment; int shmemSegActiveFlag[MAX_SEGMENTS]; void* shmemSegmentPtr[MAX_SEGMENTS]; void* shmemCurrPtr; void* shmemZeroPtr; } shmemInstance; typedef struct _shmemSegment { int active; uint32_t version; uint32_t size; } shmemSegment; typedef struct _shmemMgmtData { shmemSegment segment[MAX_SEGMENTS]; int activeSegment; unsigned int maxInstances; shmemInstance instance[]; } ShmemMgmtData; extern void *zeroseg_ptr; //reader int InitShmemReader(uint32_t instance_num, int dataset, int group_id, int numa_node, const char* path, void*** data_ptr, uint32_t instance_polltime, unsigned int max_instances); int CheckForSharedMemSegment(unsigned int max_instances); //writer int InitShmemWriter(uint32_t instance_num, int dataset, int group_id, int numa_node, const char* path, void*** data_ptr, uint32_t instance_polltime, unsigned int max_instances); int LoadSharedMemDataSegmentForWriter(int startup); void SwitchToActiveSegment(int segment_num,void*** data_ptr); void UnmapInactiveSegments(void); void ManageUnusedSegments(void); int ShutdownSharedMemory(void); void ShmemMgmtInfo(char *buf, int bufLen); void PrintShmemMgmtInfo(void); #endif snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_datamgmt.h0000644000175000017500000000437314241076207024670 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_datamgmt.h // @author Pramod Chandrashekar #ifndef _SHMEM_DMGMT_H_ #define _SHMEM_DMGMT_H_ #include #include #define SF_SUCCESS 0 #define SF_EINVAL 1 // Invalid argument #define SF_ENOMEM 2 // Not enough space #define SF_EEXIST 3 // File exists #define SF_ENOSPC 4 // No space #define SF_ENOENT 5 // No such file or directory #define MAX_NAME 1024 #define FILE_LIST_BUCKET_SIZE 64 #define MAX_NUM_ZONES 1052 #define MAX_MANIFEST_LINE_LENGTH (8*MAX_NUM_ZONES) #define MAX_LIST_ID UINT32_MAX #define MAX_IPLIST_FILES 256 struct _ShmemDataFile { char* filename; int filetype; uint32_t listid; bool zones[MAX_NUM_ZONES]; }; typedef struct _ShmemDataFile ShmemDataFileList; extern ShmemDataFileList** filelist_ptr; extern int filelist_count; /* Functions ****************************************************************/ int GetSortedListOfShmemDataFiles(void); int GetLatestShmemDataSetVersionOnDisk(uint32_t*); void FreeShmemDataFileList(void); #ifdef DEBUG_MSGS void PrintDataFiles(void); void PrintListInfo(bool*, uint32_t); #endif /* DEBUG_MSGS */ #endif /* _SHMEM_DMGMT_H_ */ snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_datamgmt.c0000644000175000017500000004015214241076201024650 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_datamgmt.c // @author Pramod Chandrashekar #include #include #include #include #include #include #include "shmem_config.h" #include "shmem_common.h" #define MANIFEST_SEPARATORS ",\r\n" #define MIN_MANIFEST_COLUMNS 3 #define WHITE_TYPE_KEYWORD "white" #define BLACK_TYPE_KEYWORD "block" #define MONITOR_TYPE_KEYWORD "monitor" static const char* const MODULE_NAME = "ShmemFileMgmt"; // FIXME eliminate these globals ShmemDataFileList **filelist_ptr = NULL; int filelist_size = 0; // Number of slots in the filelist int filelist_count = 0; // Number of 'used' slots in the file list static inline bool FileListFull( ) { return (filelist_count == filelist_size); } static int StringCompare(const void *elem1, const void *elem2) { ShmemDataFileList * const *a = elem1; ShmemDataFileList * const *b = elem2; return strcmp((*a)->filename,(*b)->filename); } /* (Re)allocate *filelist to the next bucket size. * See `man 3 realloc' for deeper insight. */ static ShmemDataFileList** ShmemDataFileList_Realloc ( ShmemDataFileList *filelist[] ) { ShmemDataFileList **temp; int _size = filelist_size + FILE_LIST_BUCKET_SIZE; // Don't call this function unnecessarily. assert( filelist_size == 0 || filelist_size > filelist_count ); // Use errno to communicate any problems errno = 0; if ( filelist_size >= MAX_IPLIST_FILES ) { errno = ENOSPC; return NULL; } if ( _size > MAX_IPLIST_FILES ) _size = MAX_IPLIST_FILES; // Rely on errno being set by realloc. temp = realloc(filelist, _size * sizeof(*filelist)); if ( temp == NULL ) return NULL; filelist_size = _size; return temp; } static void FreeShmemDataFileListFiles() { int i; if ( !filelist_count || !filelist_ptr ) return; for(i = 0; i < filelist_count; i++) { free(filelist_ptr[i]->filename); free(filelist_ptr[i]); filelist_ptr[i] = NULL; } filelist_count = 0; } void FreeShmemDataFileList() { if ( !filelist_ptr ) return; FreeShmemDataFileListFiles( ); free(filelist_ptr); filelist_ptr = NULL; filelist_size = 0; } static int ReadShmemDataFilesWithoutManifest() { struct dirent *de; DIR *dd; FreeShmemDataFileListFiles(); if ((dd = opendir(shmusr_ptr->path)) == NULL) { _dpd.errMsg("%s: Could not access %s: %s\n", MODULE_NAME, shmusr_ptr->path, strerror(errno)); return SF_EINVAL; } while (( de = readdir(dd) )) { char filename[PATH_MAX]; ShmemDataFileList *listinfo; const char *ext; int type; ext = strrchr(de->d_name , '.'); if ( ext == NULL || *(ext + 1) == '\0' ) continue; if ( strcasecmp(ext, ".blf") == 0 ) type = BLACK_LIST; else if ( strcasecmp(ext, ".wlf") == 0 ) type = WHITE_LIST; else continue; if ( FileListFull() ) { ShmemDataFileList **_temp = ShmemDataFileList_Realloc( filelist_ptr ); if ( !_temp ) { closedir(dd); if ( errno == ENOSPC ) { _dpd.errMsg("%s: Cannot load more than %u ip lists.", MODULE_NAME, MAX_IPLIST_FILES); return SF_ENOSPC; } DynamicPreprocessorFatalMessage("%s: Failed to allocate filelist_ptr: %s\n", MODULE_NAME, strerror(errno)); return SF_ENOMEM; } filelist_ptr = _temp; } listinfo = (ShmemDataFileList *)calloc(1, sizeof(*listinfo)); if ( listinfo == NULL ) { DynamicPreprocessorFatalMessage( "%s: Cannot allocate memory to store file information.\n", MODULE_NAME); } snprintf(filename, sizeof(filename), "%s/%s", shmusr_ptr->path, de->d_name); listinfo->filename = strdup(filename); if ( listinfo->filename == NULL ) { free(listinfo); closedir(dd); DynamicPreprocessorFatalMessage("%s: Error resolving filename: %s\n", MODULE_NAME, strerror(errno)); } listinfo->filetype = type; listinfo->listid = 0; memset(listinfo->zones, true, MAX_NUM_ZONES); filelist_ptr[filelist_count] = listinfo; filelist_count++; } closedir(dd); return SF_SUCCESS; } /*Ignore the space characters from string*/ static char *ignoreStartSpace(char *str) { while((*str) && (isspace((int)*str))) { str++; } return str; } /*Get file type */ static int getFileTypeFromName (char *typeName) { int type = UNKNOWN_LIST; /* Trim the starting spaces */ if (!typeName) return type; typeName = ignoreStartSpace(typeName); if (strncasecmp(typeName, WHITE_TYPE_KEYWORD, strlen(WHITE_TYPE_KEYWORD)) == 0) { type = WHITE_LIST; typeName += strlen(WHITE_TYPE_KEYWORD); } else if (strncasecmp(typeName, BLACK_TYPE_KEYWORD, strlen(BLACK_TYPE_KEYWORD)) == 0) { type = BLACK_LIST; typeName += strlen(BLACK_TYPE_KEYWORD); } else if (strncasecmp(typeName, MONITOR_TYPE_KEYWORD, strlen(MONITOR_TYPE_KEYWORD)) == 0) { type = MONITOR_LIST; typeName += strlen(MONITOR_TYPE_KEYWORD); } if (UNKNOWN_LIST != type ) { /*Ignore spaces in the end*/ typeName = ignoreStartSpace(typeName); if ( *typeName ) { type = UNKNOWN_LIST; } } return type; } /* Parse the line item in manifest file * * The format of manifest is: * file_name, list_id, action (block, white, monitor), zone information * * If no zone information provided, this means all zones are applied. * * */ static ShmemDataFileList* processLineInManifest(char *manifest, char *line, int linenumber) { char* token; int tokenIndex = 0; ShmemDataFileList* listItem = NULL; char* nextPtr = line; char filename[PATH_MAX]; bool hasZone = false; if ((listItem = (ShmemDataFileList*)calloc(1,sizeof(ShmemDataFileList))) == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Cannot allocate memory to " "store reputation manifest file information\n", manifest, linenumber); return NULL; } while((token = strtok_r(nextPtr, MANIFEST_SEPARATORS, &nextPtr)) != NULL) { char *endStr; long zone_id; long list_id; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Process reputation list token: %s\n",token );); switch (tokenIndex) { case 0: /* File name */ DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Reputation list filename: %s\n",token );); snprintf(filename, sizeof(filename), "%s/%s", shmusr_ptr->path,token); listItem->filename = strdup(filename); if (listItem->filename == NULL) { free(listItem); listItem = NULL; DynamicPreprocessorFatalMessage( "%s(%d) => Error resolving filename: %s\n", manifest, linenumber, strerror(errno)); } break; case 1: /* List ID */ list_id = _dpd.SnortStrtol( token, &endStr, 10); /*Ignore spaces in the end*/ endStr = ignoreStartSpace(endStr); if ( *endStr ) { DynamicPreprocessorFatalMessage("%s(%d) => Bad value (%s) specified for listID. " "Please specify an integer between %d and %li.\n", manifest, linenumber, token, 0, MAX_LIST_ID); } if ((list_id < 0) || (list_id > MAX_LIST_ID) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified (%s) is out of " "bounds. Please specify an integer between %d and %li.\n", manifest, linenumber, token, 0, MAX_LIST_ID); } listItem->listid = (uint32_t) list_id; break; case 2: /* Action */ token = ignoreStartSpace(token); listItem->filetype = getFileTypeFromName(token); if (UNKNOWN_LIST == listItem->filetype) { DynamicPreprocessorFatalMessage(" %s(%d) => Unknown action specified (%s)." " Please specify a value: %s | %s | %s.\n", manifest, linenumber, token, WHITE_TYPE_KEYWORD, BLACK_TYPE_KEYWORD, MONITOR_TYPE_KEYWORD); } break; default: /*Ignore spaces in the beginning*/ token= ignoreStartSpace(token); if (!(*token)) break; zone_id = _dpd.SnortStrtol( token, &endStr, 10); /*Ignore spaces in the end*/ endStr = ignoreStartSpace(endStr); if ( *endStr ) { DynamicPreprocessorFatalMessage("%s(%d) => Bad value (%s) specified for zone. " "Please specify an integer between %d and %li.\n", manifest, linenumber, token, 0, MAX_NUM_ZONES - 1); } if ((zone_id < 0) || (zone_id >= MAX_NUM_ZONES ) || (errno == ERANGE)) { DynamicPreprocessorFatalMessage(" %s(%d) => Value specified (%s) for zone is " "out of bounds. Please specify an integer between %d and %li.\n", manifest, linenumber, token, 0, MAX_NUM_ZONES - 1); } listItem->zones[zone_id] = true; hasZone = true; } tokenIndex++; } if ( tokenIndex < MIN_MANIFEST_COLUMNS ) { free(listItem); if ( tokenIndex > 0 ) { DynamicPreprocessorFatalMessage("%s(%d) => Too few columns in line: %s.\n", manifest, linenumber, line); } return NULL; } if (false == hasZone) { memset(listItem->zones, true, MAX_NUM_ZONES); } return listItem; } /*Parse the manifest file*/ static int ReadShmemDataFilesWithManifest() { FILE *fp; char line[MAX_MANIFEST_LINE_LENGTH]; char manifest_file[PATH_MAX]; int line_number = 0; snprintf(manifest_file, sizeof(manifest_file), "%s/%s",shmusr_ptr->path, MANIFEST_FILENAME); if ((fp = fopen(manifest_file, "r")) == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Error opening file at: %s\n", manifest_file);); return SF_ENOENT; } FreeShmemDataFileListFiles(); while (fgets(line, sizeof(line),fp)) { char* nextPtr = NULL; ShmemDataFileList* listItem; line_number++; DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Reputation manifest: %s\n",line );); /* remove comments */ if( (nextPtr = strchr(line, '#')) ) *nextPtr = '\0'; if ( FileListFull() ) { ShmemDataFileList **_temp = ShmemDataFileList_Realloc( filelist_ptr ); if ( !_temp ) { fclose(fp); if ( errno == ENOSPC ) { _dpd.errMsg("%s: Cannot load more than %u ip lists.", MODULE_NAME, MAX_IPLIST_FILES); return SF_ENOSPC; } DynamicPreprocessorFatalMessage("%s: Failed to allocate filelist_ptr: %s\n", MODULE_NAME, strerror(errno)); return SF_ENOMEM; } filelist_ptr = _temp; } /*Processing the line*/ listItem = processLineInManifest(manifest_file, line, line_number); if ( listItem ) { filelist_ptr[filelist_count] = listItem; filelist_count++; } } fclose(fp); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Successfully processed manifest file: %s\n", MANIFEST_FILENAME);); return SF_SUCCESS; } int GetSortedListOfShmemDataFiles() { int rval; if ((rval = ReadShmemDataFilesWithManifest()) == SF_ENOENT) { if ((rval = ReadShmemDataFilesWithoutManifest()) != SF_SUCCESS) { return rval; } // Only sort when not using a manifest file; manifest ip-lists // need to be used in the order specified used as-is. qsort(filelist_ptr, filelist_count, sizeof(*filelist_ptr), StringCompare); } return SF_SUCCESS; } //valid version values are 1 through UINT_MAX int GetLatestShmemDataSetVersionOnDisk(uint32_t* shmemVersion) { unsigned long tmpVersion; FILE *fp; char line[PATH_MAX]; char version_file[PATH_MAX]; const char *const key = "VERSION"; char* keyend_ptr = NULL; snprintf(version_file, sizeof(version_file), "%s/%s",shmusr_ptr->path,VERSION_FILENAME); if ((fp = fopen(version_file, "r")) == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Error opening file at: %s\n", version_file);); return SF_ENOENT; } while (fgets(line,sizeof(line),fp)) { char *strptr; if ( *line == '#' ) continue; if ( (strptr = strstr(line, key )) && (strptr == line) ) { if ( strlen(line) <= (strlen(key) + 1) ) break; keyend_ptr = line; keyend_ptr += strlen(key) + 1; tmpVersion = strtoul(keyend_ptr,NULL,0); break; } } if (!keyend_ptr) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Invalid file format %s\n", version_file);); fclose(fp); return SF_ENOENT; } if (tmpVersion > UINT_MAX) //someone tampers with the file *shmemVersion = 1; else *shmemVersion = (uint32_t)tmpVersion; fclose(fp); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "version information being returned is %u\n", *shmemVersion);); return SF_SUCCESS; } #ifdef DEBUG_MSGS void PrintListInfo (bool *zones, uint32_t listid) { char zonesInfo[MAX_MANIFEST_LINE_LENGTH]; int zone_id; int buf_len = sizeof(zonesInfo); char *out_buf = zonesInfo; for (zone_id = 0; zone_id < MAX_NUM_ZONES; zone_id++) { int bytesOutput; if (!zones[zone_id]) continue; bytesOutput = snprintf(out_buf, buf_len, "%d,",zone_id); out_buf += bytesOutput; buf_len -= bytesOutput; } DebugMessage(DEBUG_REPUTATION, "List %li has zones defined: %s \n", listid, zonesInfo); } void PrintDataFiles() { int i; for (i=0;i< filelist_count;i++) { DebugMessage(DEBUG_REPUTATION, "File %s of type %d found \n", filelist_ptr[i]->filename, filelist_ptr[i]->filetype); if (filelist_ptr[i]->listid) { PrintListInfo(filelist_ptr[i]->zones, filelist_ptr[i]->listid); } } } #endif /* DEBUG_MSGS */ snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_common.h0000644000175000017500000000274014241076174024361 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_common.h // @author Pramod Chandrashekar #ifndef _SHMEMCOMMON_H_ #define _SHMEMCOMMON_H_ #include "sf_types.h" #include "snort_debug.h" #include "../reputation_debug.h" #define IPREP 0 #define UNKNOWN_LIST 0 #define MONITOR_LIST 1 #define BLACK_LIST 2 #define WHITE_LIST 3 #define VERSION_FILENAME "IPRVersion.dat" #define MANIFEST_FILENAME "zone.info" #endif snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/sflinux_helpers.c0000644000175000017500000000350114241076170025075 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file sflinux_helpers.c // @author Pramod Chandrashekar #include #include #include #include #include #include #include #include #include "shmem_common.h" int CheckNumaNodes() { char filename[1024]; int num_nodes = 0; struct dirent *de; DIR *dir; snprintf(filename, sizeof(filename), "/sys/devices/system/node"); if ((dir = opendir(filename))) { while ((de = readdir(dir))) { if (strncmp(de->d_name, "node", 4) != 0) continue; num_nodes++; } } closedir(dir); DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Number of numa nodes is %d\n",num_nodes);); return num_nodes; } snort-2.9.20/src/dynamic-preprocessors/reputation/shmem/shmem_config.c0000644000175000017500000001104714241076175024332 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ // @file shmem_config.c // @author Pramod Chandrashekar #include #include "sf_types.h" #include "sf_dynamic_preprocessor.h" #include "snort_debug.h" #include "sflinux_helpers.h" #include "shmem_config.h" static const char* const MODULE_NAME ="SharedMemConfig"; ShmemUserInfo *shmusr_ptr = NULL; ShmemDataMgmtFunctions *dmfunc_ptr = NULL; static DatasetInfo dataset_names[] = { { "SFIPReputation.rt", IPREP } }; static void ConstructSegmentNames (int dataset, int group_id, int numa_node) { int i; snprintf(shmusr_ptr->mgmtSeg, sizeof(shmusr_ptr->mgmtSeg), "%s.%d.%d",SHMEM_MGMT,group_id,numa_node); for (i=0; idataSeg[i], sizeof(shmusr_ptr->dataSeg[0]), "%s.%d.%d.%d",dataset_names[dataset].name,group_id,numa_node,i); } int InitShmemUser (uint32_t instance_num, int instance_type, int dataset, int group_id, int numa_node, const char* path, uint32_t instance_polltime, uint16_t max_instances) { int rval = SF_EINVAL, num_nodes; if ((instance_num >= max_instances) || (instance_type != READ && instance_type != WRITE) || (dataset != IPREP) || !path || !instance_polltime ) goto exit; if ((shmusr_ptr = calloc(1, sizeof(*shmusr_ptr))) == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Unable to allocate memory for configuration data");); goto exit; } shmusr_ptr->instance_num = instance_num; shmusr_ptr->instance_type = instance_type; shmusr_ptr->dataset = dataset; shmusr_ptr->group_id = group_id; shmusr_ptr->instance_polltime = instance_polltime; num_nodes = CheckNumaNodes(); if (numa_node > num_nodes) numa_node = NUMA_0; shmusr_ptr->numa_node = numa_node; strncpy(shmusr_ptr->path,path,sizeof(shmusr_ptr->path)); shmusr_ptr->path[sizeof(shmusr_ptr->path)-1] = '\0'; ConstructSegmentNames(dataset,group_id,numa_node); return SF_SUCCESS; exit: DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Error in setting config");); return rval; } int InitShmemDataMgmtFunctions ( CreateMallocZero create_malloc_zero, GetDataSize get_data_size, LoadData load_data) { if ((dmfunc_ptr = (ShmemDataMgmtFunctions*) malloc(sizeof(ShmemDataMgmtFunctions))) == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_REPUTATION, "Could not allocate memory for Shmem Datamanagement function list");); return SF_EINVAL; } dmfunc_ptr->CreatePerProcessZeroSegment = create_malloc_zero; dmfunc_ptr->GetSegmentSize = get_data_size; dmfunc_ptr->LoadShmemData = load_data; return SF_SUCCESS; } void FreeShmemUser() { if (shmusr_ptr) free(shmusr_ptr); shmusr_ptr = NULL; } void FreeShmemDataMgmtFunctions() { if (dmfunc_ptr) free(dmfunc_ptr); dmfunc_ptr = NULL; } void PrintConfig() { int i; _dpd.logMsg("Instance number %u:",shmusr_ptr->instance_num); _dpd.logMsg("Instance type %d:",shmusr_ptr->instance_type); _dpd.logMsg("Instance datatype %d:",shmusr_ptr->dataset); _dpd.logMsg("Instance Group ID %d:",shmusr_ptr->group_id); _dpd.logMsg("Instance Numa node %d:",shmusr_ptr->numa_node); _dpd.logMsg("Instance Poll time %d:",shmusr_ptr->instance_polltime); _dpd.logMsg("Data Path is %s:",shmusr_ptr->path); for (i=0; idataSeg[i]); } snort-2.9.20/src/dynamic-preprocessors/reputation/sf_reputation.dsp0000555000175000017500000001416414230012554024002 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_reputation" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_reputation - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_reputation.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_reputation.mak" CFG="sf_reputation - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_reputation - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_reputation - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_reputation - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /YX /FD /c # SUBTRACT CPP /X # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_reputation - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "SF_SNORT_PREPROC_DLL" /D "_DEBUG" /D "DEBUG" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /YX /FD /GZ /c # SUBTRACT CPP /X # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_reputation - Win32 Release" # Name "sf_reputation - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\inet_aton.c # End Source File # Begin Source File SOURCE=..\include\inet_pton.c # End Source File # Begin Source File SOURCE=.\reputation_config.c # End Source File # Begin Source File SOURCE=.\reputation_utils.c # End Source File # Begin Source File SOURCE=..\include\segment_mem.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_ip.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=..\include\sfrt.c # End Source File # Begin Source File SOURCE=..\include\sfrt_dir.c # End Source File # Begin Source File SOURCE=..\include\sfrt_flat.c # End Source File # Begin Source File SOURCE=..\include\sfrt_flat_dir.c # End Source File # Begin Source File SOURCE=.\spp_reputation.c # End Source File # Begin Source File SOURCE=..\include\strtok_r.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\reputation_config.h # End Source File # Begin Source File SOURCE=.\reputation_debug.h # End Source File # Begin Source File SOURCE=.\reputation_utils.h # End Source File # Begin Source File SOURCE=..\include\segment_mem.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=..\include\sfrt_flat.h # End Source File # Begin Source File SOURCE=..\include\sfrt_flat_dir.h # End Source File # Begin Source File SOURCE=.\spp_reputation.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/reputation/reputation_utils.c0000644000175000017500000000540114241076166024172 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions. * * 6/11/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "reputation_utils.h" #include #include #define MAX_ADDR_LINE_LENGTH 8192 /******************************************************************** * Function: Reputation_IsEmptyStr() * * Checks if string is NULL, empty or just spaces. * String must be 0 terminated. * * Arguments: * char * - string to check * * Returns: * 1 if string is NULL, empty or just spaces * 0 otherwise * ********************************************************************/ int Reputation_IsEmptyStr(char *str) { char *end; if (str == NULL) return 1; end = str + strlen(str); while ((str < end) && isspace((int)*str)) str++; if (str == end) return 1; return 0; } /******************************************************************** * Function: numLinesInFile() * * Number of lines in the file * * Arguments: * fname: file name * * Returns: * uint32_t number of lines * ********************************************************************/ int numLinesInFile(char *fname) { FILE *fp; uint32_t numlines = 0; char buf[MAX_ADDR_LINE_LENGTH]; fp = fopen(fname, "rb"); if (NULL == fp) return 0; while((fgets(buf, MAX_ADDR_LINE_LENGTH, fp)) != NULL) { if (buf[0] != '#') { numlines++; if (numlines == INT_MAX) { fclose(fp); return INT_MAX; } } } fclose(fp); return numlines; } snort-2.9.20/src/dynamic-preprocessors/reputation/spp_reputation.h0000644000175000017500000000604114241076231023633 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * spp_reputation.h: Definitions, structs, function prototype(s) for * the Reputation preprocessor. * Author: Hui Cao */ #ifndef SPP_REPUTATION_H #define SPP_REPUTATION_H #include "sf_types.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" #include "sf_ip.h" #include "sfrt_flat.h" #include "reputation_config.h" /* * Generator id. Define here the same as the official registry * in generators.h */ #define GENERATOR_SPP_REPUTATION 136 #define CS_TYPE_REPUTATION_SHAREMEM ((GENERATOR_SPP_REPUTATION *10) + 1) #define CS_TYPE_REPUTATION_SHAREMEM_LOOKUP ((GENERATOR_SPP_REPUTATION *10) + 2) #define CS_TYPE_REPUTATION_SHAREMEM_MGMT_INFO ((GENERATOR_SPP_REPUTATION *10) + 3) /*These IDs are reserved for snort shared memory server (writer)*/ #define SHMEM_SERVER_ID 0 /* Ultimately calls SnortEventqAdd */ /* Arguments are: gid, sid, rev, classification, priority, message, rule_info */ #define ALERT(x,y) { _dpd.alertAdd(GENERATOR_SPP_REPUTATION, x, 1, 0, 3, y, 0 ); } #define REPUTATION_EVENT_BLACKLIST 1 #define REPUTATION_EVENT_BLACKLIST_STR "(spp_reputation) packets block-list" #define REPUTATION_EVENT_WHITELIST 2 #define REPUTATION_EVENT_WHITELIST_STR "(spp_reputation) packets do-not-block-list" #define REPUTATION_EVENT_MONITOR 3 #define REPUTATION_EVENT_MONITOR_STR "(spp_reputation) packets monitored" typedef struct _Reputation_Stats { uint64_t blacklisted; uint64_t whitelisted; uint64_t monitored; uint64_t memoryAllocated; } Reputation_Stats; extern Reputation_Stats reputation_stats; extern int totalNumEntries; extern ReputationConfig *reputation_eval_config; extern tSfPolicyUserContextId reputation_config; extern void **IPtables; #ifdef SHARED_REP typedef enum { NO_SWITCH, SWITCHING, SWITCHED }Swith_State; extern Swith_State switch_state; extern int available_segment; extern table_flat_t *emptyIPtables; extern ReputationConfig *reputation_shmem_config; #endif /* Prototypes for public interface */ void SetupReputation(void); void SetupReputationUpdate(uint32_t interval); #endif /* SPP_REPUTATION_H */ snort-2.9.20/src/dynamic-preprocessors/reputation/reputation_utils.h0000644000175000017500000000270014241076167024177 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Provides convenience functions. * * 6/11/2011 - Initial implementation ... Hui Cao * ****************************************************************************/ #ifndef REPUTATION_UTILS_H_ #define REPUTATION_UTILS_H_ #include "sf_ip.h" #include "sf_snort_packet.h" #include int Reputation_IsEmptyStr(char *); int numLinesInFile(char *fname); #endif /* REPUTATION_UTILS_H_ */ snort-2.9.20/src/dynamic-preprocessors/ssh/0000755000175000017500000000000014242725715017021 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/ssh/spp_ssh.h0000644000175000017500000002016614241076436020654 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * spp_ssh.h: Definitions, structs, function prototype(s) for * the SSH preprocessor. * Author: Chris Sherwin */ #ifndef SPP_SSH_H #define SPP_SSH_H #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "snort_bounds.h" #define MAX_PORTS 65536 /* * Default SSH port */ #define SSH_PORT 22 /* * Boolean values. */ #define SSH_TRUE (1) #define SSH_FALSE (0) /* * Error codes. */ #define SSH_SUCCESS (1) #define SSH_FAILURE (0) /* * Default values for configurable parameters. */ #define SSH_DEFAULT_MAX_ENC_PKTS 25 #define SSH_DEFAULT_MAX_CLIENT_BYTES 19600 #define SSH_DEFAULT_MAX_SERVER_VERSION_LEN 80 /* * Min/Max values for each configurable parameter. */ #define MIN_MAX_ENC_PKTS 0 #define MAX_MAX_ENC_PKTS 65535 #define MIN_MAX_CLIENT_BYTES 0 #define MAX_MAX_CLIENT_BYTES 65535 #define MIN_MAX_SERVER_VERSION_LEN 0 #define MAX_MAX_SERVER_VERSION_LEN 255 /* * One of these structures is kept for each configured * server port. */ typedef struct _sshPortlistNode { uint16_t server_port; struct _sshPortlistNode* nextp; } SSHPortNode; /* * Global SSH preprocessor configuration. * * AutodetectEnabled: Whether or not to apply auto-detection of SSH * to ports other than those configured. * MaxEncryptedPackets: Maximum number of encrypted packets examined per * session. * MaxClientBytes: Maximum bytes of encrypted data that can be * sent by client without a server response. * MaxServerVersionLen: Maximum length of a server's version string. * Configurable threshold for Secure CRT-style overflow. * DisableRules: Disable rule processing for SSH traffic. * EnabledAlerts: Bit vector describing which alerts are enabled. */ typedef struct _sshConfig { uint8_t AutodetectEnabled; uint16_t MaxEncryptedPackets; uint16_t MaxClientBytes; uint16_t MaxServerVersionLen; // uint16_t DisableRules; uint16_t EnabledAlerts; // SSHPortNode* PortList; char ports[MAX_PORTS/8]; int ref_count; } SSHConfig; /* * Per-session data block containing current state * of the SSH preprocessor for the session. * * version: Version of SSH detected for this session. * num_enc_pkts: Number of encrypted packets seen on this session. * num_client_bytes: Number of bytes of encrypted data sent by client, * without a server response. * state_flags: Bit vector describing the current state of the * session. */ typedef struct _sshData { uint8_t version; uint16_t num_enc_pkts; uint16_t num_client_bytes; uint32_t state_flags; tSfPolicyId policy_id; tSfPolicyUserContextId config; } SSHData; /* * Session state flags for SSHData::state_flags */ #define SSH_FLG_CLEAR (0x0) #define SSH_FLG_CLIENT_IDSTRING_SEEN (0x1) #define SSH_FLG_SERV_IDSTRING_SEEN (0x2) #define SSH_FLG_SERV_PKEY_SEEN (0x4) #define SSH_FLG_CLIENT_SKEY_SEEN (0x8) #define SSH_FLG_CLIENT_KEXINIT_SEEN (0x10) #define SSH_FLG_SERV_KEXINIT_SEEN (0x20) #define SSH_FLG_KEXDH_INIT_SEEN (0x40) #define SSH_FLG_KEXDH_REPLY_SEEN (0x80) #define SSH_FLG_GEX_REQ_SEEN (0x100) #define SSH_FLG_GEX_GRP_SEEN (0x200) #define SSH_FLG_GEX_INIT_SEEN (0x400) #define SSH_FLG_GEX_REPLY_SEEN (0x800) #define SSH_FLG_NEWKEYS_SEEN (0x1000) #define SSH_FLG_SESS_ENCRYPTED (0x2000) #define SSH_FLG_RESPOVERFLOW_ALERTED (0x4000) #define SSH_FLG_CRC32_ALERTED (0x8000) #define SSH_FLG_MISSED_PACKETS (0x10000) #define SSH_FLG_REASSEMBLY_SET (0x20000) #define SSH_FLG_AUTODETECTED (0x40000) /* * Some convenient combinations of state flags. */ #define SSH_FLG_BOTH_IDSTRING_SEEN (SSH_FLG_CLIENT_IDSTRING_SEEN | \ SSH_FLG_SERV_IDSTRING_SEEN ) #define SSH_FLG_V1_KEYEXCH_DONE (SSH_FLG_SERV_PKEY_SEEN | \ SSH_FLG_CLIENT_SKEY_SEEN ) #define SSH_FLG_V2_KEXINIT_DONE (SSH_FLG_CLIENT_KEXINIT_SEEN | \ SSH_FLG_SERV_KEXINIT_SEEN ) #define SSH_FLG_V2_DHOLD_DONE (SSH_FLG_KEXDH_INIT_SEEN | \ SSH_FLG_KEXDH_REPLY_SEEN | \ SSH_FLG_NEWKEYS_SEEN ) #define SSH_FLG_V2_DHNEW_DONE (SSH_FLG_GEX_REQ_SEEN | \ SSH_FLG_GEX_GRP_SEEN | \ SSH_FLG_GEX_INIT_SEEN | \ SSH_FLG_GEX_REPLY_SEEN | \ SSH_FLG_NEWKEYS_SEEN ) /* * SSH version values for SSHData::version */ #define SSH_VERSION_UNKNOWN (0x0) #define SSH_VERSION_1 (0x1) #define SSH_VERSION_2 (0x2) /* * Length of SSH2 header, in bytes. */ #define SSH2_HEADERLEN (5) #define SSH2_PACKET_MAX_SIZE (256 * 1024) /* * SSH2 binary packet struct. * * packet_length: Length of packet in bytes not including * this field or the mesg auth code (mac) * padding_length: Length of padding section. * packet_data: Variable length packet payload + padding + MAC. */ typedef struct _ssh2Packet { uint32_t packet_length; uint8_t padding_length; char packet_data[1]; } SSH2Packet; /* * SSH v1 message types (of interest) */ #define SSH_MSG_V1_SMSG_PUBLIC_KEY 2 #define SSH_MSG_V1_CMSG_SESSION_KEY 3 /* * SSH v2 message types (of interest) */ #define SSH_MSG_KEXINIT 20 #define SSH_MSG_NEWKEYS 21 #define SSH_MSG_KEXDH_INIT 30 #define SSH_MSG_KEXDH_REPLY 31 #define SSH_MSG_KEXDH_GEX_REQ 34 #define SSH_MSG_KEXDH_GEX_GRP 33 #define SSH_MSG_KEXDH_GEX_INIT 32 #define SSH_MSG_KEXDH_GEX_REPLY 31 /* Direction of sent message. */ #define SSH_DIR_FROM_SERVER (0x1) #define SSH_DIR_FROM_CLIENT (0x2) /* * Keyword strings for parsing configuration options. */ #define SSH_SERVERPORTS_KEYWORD "server_ports" #define SSH_MAX_ENC_PKTS_KEYWORD "max_encrypted_packets" #define SSH_MAX_CLIENT_BYTES_KEYWORD "max_client_bytes" #define SSH_MAX_SERVER_VERSION_KEYWORD "max_server_version_len" #define SSH_AUTODETECT_KEYWORD "autodetect" #define SSH_ENABLE_RESPOVERFLOW_KEYWORD "enable_respoverflow" #define SSH_ENABLE_CRC32_KEYWORD "enable_ssh1crc32" #define SSH_ENABLE_SECURECRT_KEYWORD "enable_srvoverflow" #define SSH_ENABLE_PROTOMISMATCH_KEYWORD "enable_protomismatch" #define SSH_ENABLE_WRONGDIR_KEYWORD "enable_badmsgdir" #define SSH_DISABLE_RULES_KEYWORD "disable_rules" #define SSH_ENABLE_PAYLOAD_SIZE "enable_paysize" #define SSH_ENABLE_UNRECOGNIZED_VER "enable_recognition" /* * SSH preprocessor alert types. */ #define SSH_EVENT_RESPOVERFLOW 1 #define SSH_EVENT_CRC32 2 #define SSH_EVENT_SECURECRT 3 #define SSH_EVENT_PROTOMISMATCH 4 #define SSH_EVENT_WRONGDIR 5 #define SSH_EVENT_PAYLOAD_SIZE 6 #define SSH_EVENT_VERSION 7 /* * SSH alert flags */ #define SSH_ALERT_NONE (0x0) #define SSH_ALERT_RESPOVERFLOW (0x1) #define SSH_ALERT_CRC32 (0x2) #define SSH_ALERT_SECURECRT (0x4) #define SSH_ALERT_PROTOMISMATCH (0x8) #define SSH_ALERT_WRONGDIR (0x10) #define SSH_ALERT_PAYSIZE (0x20) #define SSH_ALERT_UNRECOGNIZED (0x40) #define SSH_ALERT_ALL (0xFFFF) /* * SSH preprocessor alert strings. */ #define SSH_EVENT_RESPOVERFLOW_STR "(spp_ssh) Challenge-Response Overflow exploit" #define SSH_EVENT_CRC32_STR "(spp_ssh) SSH1 CRC32 exploit" #define SSH_EVENT_SECURECRT_STR "(spp_ssh) Server version string overflow" #define SSH_EVENT_PROTOMISMATCH_STR "(spp_ssh) Protocol mismatch" #define SSH_EVENT_WRONGDIR_STR "(spp_ssh) Bad message direction" #define SSH_PAYLOAD_SIZE_STR "(spp_ssh) Payload size incorrect for the given payload" #define SSH_VERSION_STR "(spp_ssh) Failed to detect SSH version string" /* Prototypes for public interface */ extern void SetupSSH(void); #endif /* SPP_SSH_H */ snort-2.9.20/src/dynamic-preprocessors/ssh/Makefile.in0000644000175000017500000005256214242725547021103 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@ssh_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@ssh_buffer_dump.h subdir = src/dynamic-preprocessors/ssh ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_ssh_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_ssh_preproc_la_SOURCES_DIST = spp_ssh.c spp_ssh.h \ ssh_buffer_dump.c ssh_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = ssh_buffer_dump.lo am_libsf_ssh_preproc_la_OBJECTS = spp_ssh.lo $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_ssh_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo libsf_ssh_preproc_la_OBJECTS = $(am_libsf_ssh_preproc_la_OBJECTS) \ $(nodist_libsf_ssh_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_ssh_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_ssh_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_ssh_preproc_la_SOURCES) \ $(nodist_libsf_ssh_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_ssh_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_ssh_preproc.la libsf_ssh_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_ssh_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_ssh_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c libsf_ssh_preproc_la_SOURCES = spp_ssh.c spp_ssh.h $(am__append_1) EXTRA_DIST = \ sf_ssh.vcxproj \ sf_ssh.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/ssh/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/ssh/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_ssh_preproc.la: $(libsf_ssh_preproc_la_OBJECTS) $(libsf_ssh_preproc_la_DEPENDENCIES) $(EXTRA_libsf_ssh_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_ssh_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_ssh_preproc_la_OBJECTS) $(libsf_ssh_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/ssh/sf_ssh.dsp0000444000175000017500000001154714230012554021007 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_ssh" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_ssh - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_ssh.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_ssh.mak" CFG="sf_ssh - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_ssh - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_ssh - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_ssh - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "SF_SNORT_PREPROC_DLL" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_ssh - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "SF_SNORT_PREPROC_DLL" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_ssh - Win32 Release" # Name "sf_ssh - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=.\spp_ssh.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\spp_ssh.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/ssh/ssh_buffer_dump.c0000644000175000017500000000500514241076444022335 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file ssh_buffer_dump.c ** ** @author Seshaiah Erugu ** ** @brief This file contains structures and functions for ** dumping buffers used during SSH inspection. */ #include "ssh_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_SSH_BUFFER_DUMP] = {{"SSH_PAYLOAD_DUMP", "", 0}, {"SSH_VERSION_DUMP", "", 0}, {"SSH_MSG_KEXDH_INIT_DUMP", "", 0}, {"SSH_MSG_KEXDH_REPLY_DUMP", "", 0}, {"SSH_MSG_KEXDH_GEX_REQ_DUMP", "", 0}, {"SSH_MSG_KEXDH_GEX_GRP_DUMP", "", 0}, {"SSH_MSG_KEXDH_GEX_INIT_DUMP", "", 0}, {"SSH_MSG_NEWKEYS_DUMP", "", 0}, {"SSH_MSG_V1_SMSG_PUBLIC_KEY_DUMP", "", 0}, {"SSH_MSG_V1_CMSG_SESSION_KEY_DUMP", "", 0}, {"SSH_MSG_TYPE_INVALID_DUMP", "", 0} }; void dumpBuffer(SSH_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content =(char*) content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_SSH_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getSSHBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/ssh/Makefile.am0000444000175000017500000000141014230012554021032 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_ssh_preproc.la libsf_ssh_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_ssh_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_ssh_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sfPolicyUserData.c endif libsf_ssh_preproc_la_SOURCES = \ spp_ssh.c \ spp_ssh.h if BUILD_BUFFER_DUMP libsf_ssh_preproc_la_SOURCES += \ ssh_buffer_dump.c \ ssh_buffer_dump.h endif EXTRA_DIST = \ sf_ssh.vcxproj \ sf_ssh.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/ssh/spp_ssh.c0000644000175000017500000014730414241076427020653 0ustar apoapo/* $Id */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * SSH preprocessor * Author: Chris Sherwin * Contributors: Adam Keeton, Ryan Jordan * * * Alert for Gobbles, CRC32, protocol mismatch (Cisco catalyst vulnerability), * and a SecureCRT vulnerability. Will also alert if the client or server * traffic appears to flow the wrong direction, or if packets appear * malformed/spoofed. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif /* HAVE_CONFIG_H */ #include "sf_types.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_plugin_api.h" #include "snort_debug.h" #include "preprocids.h" #if defined(FEAT_OPEN_APPID) #include "appId.h" #endif /* defined(FEAT_OPEN_APPID) */ #include "spp_ssh.h" #include "sf_preproc_info.h" #include #include #include #include #ifndef WIN32 #include #include #endif #include #include #include "profiler.h" #ifdef PERF_PROFILING PreprocStats sshPerfStats; #endif #include "sf_types.h" #ifdef DUMP_BUFFER #include "ssh_buffer_dump.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 1; const int BUILD_VERSION = 3; const char *PREPROC_NAME = "SF_SSH"; #define SetupSSH DYNAMIC_PREPROC_SETUP #ifdef TARGET_BASED int16_t ssh_app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif /* * Generator id. Define here the same as the official registry * in generators.h */ #define GENERATOR_SPP_SSH 128 /* * Function prototype(s) */ SSHData * SSHGetNewSession(SFSnortPacket *, tSfPolicyId); static void SSHInit( struct _SnortConfig *, char* ); static void DisplaySSHConfig(SSHConfig *); static void FreeSSHData( void* ); static void ParseSSHArgs(SSHConfig *, u_char*); static void ProcessSSH( void*, void* ); static inline int CheckSSHPort( uint16_t ); static unsigned int ProcessSSHProtocolVersionExchange( SSHData*, SFSnortPacket*, uint8_t, uint8_t ); static unsigned int ProcessSSHKeyExchange( SSHData*, SFSnortPacket*, uint8_t, unsigned int ); static unsigned int ProcessSSHKeyInitExchange( SSHData*, SFSnortPacket*, uint8_t, unsigned int); static void enablePortStreamServices(struct _SnortConfig *, SSHConfig *, tSfPolicyId); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static void SSHFreeConfig(tSfPolicyUserContextId config); static int SSHCheckConfig(struct _SnortConfig *); static void SSHCleanExit(int, void *); /* Ultimately calls SnortEventqAdd */ /* Arguments are: gid, sid, rev, classification, priority, message, rule_info */ #define ALERT(x,y) { _dpd.alertAdd(GENERATOR_SPP_SSH, x, 1, 0, 3, y, 0 ); } /* Convert port value into an index for the ssh_config->ports array */ #define PORT_INDEX(port) port/8 /* Convert port value into a value for bitwise operations */ #define CONV_PORT(port) 1<<(port%8) /** SSH configuration per Policy */ static tSfPolicyUserContextId ssh_config = NULL; static SSHConfig *ssh_eval_config = NULL; #ifdef SNORT_RELOAD static void SSHReload(struct _SnortConfig *, char *, void **); static int SSHReloadVerify(struct _SnortConfig *, void *); static void * SSHReloadSwap(struct _SnortConfig *, void *); static void SSHReloadSwapFree(void *); #endif /* Called at preprocessor setup time. Links preprocessor keyword * to corresponding preprocessor initialization function. * * PARAMETERS: None. * * RETURNS: Nothing. * */ void SetupSSH(void) { /* Link preprocessor keyword to initialization function * in the preprocessor list. */ #ifndef SNORT_RELOAD _dpd.registerPreproc( "ssh", SSHInit ); #else _dpd.registerPreproc("ssh", SSHInit, SSHReload, SSHReloadVerify, SSHReloadSwap, SSHReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getSSHBuffers, SSH_BUFFER_DUMP_FUNC); #endif } #ifdef REG_TEST static inline void PrintSSHSize(void) { _dpd.logMsg("\nSSH Session Size: %lu\n", (long unsigned int)sizeof(SSHData)); } #endif /* Initializes the SSH preprocessor module and registers * it in the preprocessor list. * * PARAMETERS: * * argp: Pointer to argument string to process for config * data. * * RETURNS: Nothing. */ static void SSHInit(struct _SnortConfig *sc, char *argp) { tSfPolicyId policy_id = _dpd.getParserPolicy(sc); SSHConfig *pPolicyConfig = NULL; #ifdef REG_TEST PrintSSHSize(); #endif if (ssh_config == NULL) { //create a context ssh_config = sfPolicyConfigCreate(); if (ssh_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for SSH config.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupSSH(): The Stream preprocessor must be enabled.\n"); } _dpd.addPreprocConfCheck(sc, SSHCheckConfig); _dpd.addPreprocExit(SSHCleanExit, NULL, PRIORITY_LAST, PP_SSH); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("ssh", (void *)&sshPerfStats, 0, _dpd.totalPerfStats, NULL); #endif #ifdef TARGET_BASED ssh_app_id = _dpd.findProtocolReference("ssh"); if (ssh_app_id == SFTARGET_UNKNOWN_PROTOCOL) ssh_app_id = _dpd.addProtocolReference("ssh"); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_SSH, ssh_app_id ); #endif } sfPolicyUserPolicySet (ssh_config, policy_id); pPolicyConfig = (SSHConfig *)sfPolicyUserDataGetCurrent(ssh_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("SSH preprocessor can only be " "configured once.\n"); } pPolicyConfig = (SSHConfig *)calloc(1, sizeof(SSHConfig)); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "SSH preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(ssh_config, pPolicyConfig); ParseSSHArgs(pPolicyConfig, (u_char *)argp); _dpd.addPreproc( sc, ProcessSSH, PRIORITY_APPLICATION, PP_SSH, PROTO_BIT__TCP ); enablePortStreamServices(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } /* Parses a single numerical value. * A fatal error is made if the parsed value is out of bounds. * * PARAMETERS: * * token: String containing argument * keyword: String containing option's name. Used when printing an error. * min: Minimum value of argument * max: Maximum value of argument * * RETURNS: bounds-checked integer value of argument. */ static int ParseNumInRange(char *token, char *keyword, int min, int max) { int value; if (( !token ) || !isdigit((int)token[0]) ) { DynamicPreprocessorFatalMessage("Bad value specified for %s. " "Please specify a number between %d and %d.\n", keyword, min, max); } value = atoi( token ); if (value < min || value > max) { DynamicPreprocessorFatalMessage("Value specified for %s is out of " "bounds. Please specify a number between %d and %d.\n", keyword, min, max); } return value; } /* Parses and processes the configuration arguments * supplied in the SSH preprocessor rule. * * PARAMETERS: * * argp: Pointer to string containing the config arguments. * * RETURNS: Nothing. */ static void ParseSSHArgs(SSHConfig *config, u_char* argp) { char* cur_tokenp = NULL; char* argcpyp = NULL; int port; if (config == NULL) return; config->MaxEncryptedPackets = SSH_DEFAULT_MAX_ENC_PKTS; config->MaxClientBytes = SSH_DEFAULT_MAX_CLIENT_BYTES; config->MaxServerVersionLen = SSH_DEFAULT_MAX_SERVER_VERSION_LEN; /* Set up default port to listen on */ config->ports[ PORT_INDEX( 22 ) ] |= CONV_PORT(22); /* Sanity check(s) */ if ( !argp ) { DisplaySSHConfig(config); return; } argcpyp = strdup( (char*) argp ); if ( !argcpyp ) { DynamicPreprocessorFatalMessage("Could not allocate memory to parse SSH options.\n"); return; } cur_tokenp = strtok( argcpyp, " "); while ( cur_tokenp ) { if ( !strcmp( cur_tokenp, SSH_SERVERPORTS_KEYWORD )) { /* If the user specified ports, remove '22' for now since * it now needs to be set explicitely. */ config->ports[ PORT_INDEX( 22 ) ] = 0; /* Eat the open brace. */ cur_tokenp = strtok( NULL, " "); if (( !cur_tokenp ) || ( cur_tokenp[0] != '{' )) { DynamicPreprocessorFatalMessage("Bad value specified for %s.\n", SSH_SERVERPORTS_KEYWORD); //free(argcpyp); //return; } cur_tokenp = strtok( NULL, " "); while (( cur_tokenp ) && ( cur_tokenp[0] != '}' )) { if ( !isdigit( (int)cur_tokenp[0] )) { DynamicPreprocessorFatalMessage("Bad port %s.\n", cur_tokenp ); //free(argcpyp); //return; } else { port = atoi( cur_tokenp ); if( port < 0 || port > MAX_PORTS ) { DynamicPreprocessorFatalMessage("Port value illegitimate: %s\n", cur_tokenp); //free(argcpyp); //return; } config->ports[ PORT_INDEX( port ) ] |= CONV_PORT(port); } cur_tokenp = strtok( NULL, " "); } } else if ( !strcmp( cur_tokenp, SSH_AUTODETECT_KEYWORD )) { config->AutodetectEnabled = 1; } else if ( !strcmp( cur_tokenp, SSH_MAX_ENC_PKTS_KEYWORD )) { cur_tokenp = strtok( NULL, " "); config->MaxEncryptedPackets = (uint16_t)ParseNumInRange(cur_tokenp, SSH_MAX_ENC_PKTS_KEYWORD, MIN_MAX_ENC_PKTS, MAX_MAX_ENC_PKTS); } else if (!strcmp( cur_tokenp, SSH_MAX_CLIENT_BYTES_KEYWORD )) { cur_tokenp = strtok( NULL, " "); config->MaxClientBytes = (uint16_t)ParseNumInRange(cur_tokenp, SSH_MAX_CLIENT_BYTES_KEYWORD, MIN_MAX_CLIENT_BYTES, MAX_MAX_CLIENT_BYTES); } else if ( !strcmp( cur_tokenp, SSH_MAX_SERVER_VERSION_KEYWORD )) { cur_tokenp = strtok( NULL, " "); config->MaxServerVersionLen = (uint16_t)ParseNumInRange(cur_tokenp, SSH_MAX_SERVER_VERSION_KEYWORD, MIN_MAX_SERVER_VERSION_LEN, MAX_MAX_SERVER_VERSION_LEN); } else if ( !strcmp( cur_tokenp, SSH_ENABLE_RESPOVERFLOW_KEYWORD )) { config->EnabledAlerts |= SSH_ALERT_RESPOVERFLOW; } else if ( !strcmp( cur_tokenp, SSH_ENABLE_CRC32_KEYWORD )) { config->EnabledAlerts |= SSH_ALERT_CRC32; } else if ( !strcmp( cur_tokenp, SSH_ENABLE_SECURECRT_KEYWORD )) { config->EnabledAlerts |= SSH_ALERT_SECURECRT; } else if ( !strcmp( cur_tokenp, SSH_ENABLE_PROTOMISMATCH_KEYWORD )) { config->EnabledAlerts |= SSH_ALERT_PROTOMISMATCH; } else if ( !strcmp( cur_tokenp, SSH_ENABLE_WRONGDIR_KEYWORD )) { config->EnabledAlerts |= SSH_ALERT_WRONGDIR; } #if 0 else if ( !strcmp( cur_tokenp, SSH_DISABLE_RULES_KEYWORD )) { config->DisableRules++; } #endif else if( !strcmp( cur_tokenp, SSH_ENABLE_PAYLOAD_SIZE )) { config->EnabledAlerts |= SSH_ALERT_PAYSIZE; } else if( !strcmp( cur_tokenp, SSH_ENABLE_UNRECOGNIZED_VER )) { config->EnabledAlerts |= SSH_ALERT_UNRECOGNIZED; } else { DynamicPreprocessorFatalMessage("Invalid argument: %s\n", cur_tokenp); return; } cur_tokenp = strtok( NULL, " " ); } DisplaySSHConfig(config); free(argcpyp); } /* Display the configuration for the SSH preprocessor. * * PARAMETERS: None. * * RETURNS: Nothing. */ static void DisplaySSHConfig(SSHConfig *config) { int index; int newline; if (config == NULL) return; _dpd.logMsg("SSH config: \n"); _dpd.logMsg(" Autodetection: %s\n", config->AutodetectEnabled ? "ENABLED":"DISABLED"); _dpd.logMsg(" Challenge-Response Overflow Alert: %s\n", config->EnabledAlerts & SSH_ALERT_RESPOVERFLOW ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" SSH1 CRC32 Alert: %s\n", config->EnabledAlerts & SSH_ALERT_CRC32 ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Server Version String Overflow Alert: %s\n", config->EnabledAlerts & SSH_ALERT_SECURECRT ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Protocol Mismatch Alert: %s\n", config->EnabledAlerts & SSH_ALERT_PROTOMISMATCH? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Bad Message Direction Alert: %s\n", config->EnabledAlerts & SSH_ALERT_WRONGDIR ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Bad Payload Size Alert: %s\n", config->EnabledAlerts & SSH_ALERT_PAYSIZE ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Unrecognized Version Alert: %s\n", config->EnabledAlerts & SSH_ALERT_UNRECOGNIZED ? "ENABLED" : "DISABLED" ); _dpd.logMsg(" Max Encrypted Packets: %d %s \n", config->MaxEncryptedPackets, config->MaxEncryptedPackets == SSH_DEFAULT_MAX_ENC_PKTS ? "(Default)" : "" ); _dpd.logMsg(" Max Server Version String Length: %d %s \n", config->MaxServerVersionLen, config->MaxServerVersionLen == SSH_DEFAULT_MAX_SERVER_VERSION_LEN ? "(Default)" : "" ); if ( config->EnabledAlerts & (SSH_ALERT_RESPOVERFLOW | SSH_ALERT_CRC32)) { _dpd.logMsg(" MaxClientBytes: %d %s \n", config->MaxClientBytes, config->MaxClientBytes == SSH_DEFAULT_MAX_CLIENT_BYTES ? "(Default)" : "" ); } /* Traverse list, printing ports, 5 per line */ newline = 1; _dpd.logMsg(" Ports:\n"); for(index = 0; index < MAX_PORTS; index++) { if( config->ports[ PORT_INDEX(index) ] & CONV_PORT(index) ) { _dpd.logMsg("\t%d", index); if ( !((newline++)% 5) ) { _dpd.logMsg("\n"); } } } _dpd.logMsg("\n"); } /* Returns the true length of the ssh packet, including * the ssh packet header and all padding. * * If the packet length is invalid, 0 is returned. * The return value is never larger than buflen. * * PARAMETERS: * p: Pointer to the SSH packet. * buflen: the size of packet buffer. */ static unsigned int SSHPacket_GetLength(SSH2Packet *p, size_t buflen) { unsigned int ssh_length; if (buflen < sizeof(SSH2Packet)) return 0; ssh_length = ntohl(p->packet_length); if ((ssh_length < sizeof(SSH2Packet) + 1) || ssh_length > SSH2_PACKET_MAX_SIZE) return 0; /* Everything after packet length field (including padding) is included in the packet_length */ ssh_length += sizeof(p->packet_length); if (buflen < ssh_length) return buflen; /* truncated */ return ssh_length; } /* Main runtime entry point for SSH preprocessor. * Analyzes SSH packets for anomalies/exploits. * * PARAMETERS: * * packetp: Pointer to current packet to process. * contextp: Pointer to context block, not used. * * RETURNS: Nothing. */ static void ProcessSSH( void* ipacketp, void* contextp ) { SSHData* sessp = NULL; uint8_t source = 0; uint8_t dest = 0; uint8_t known_port = 0; uint8_t direction; unsigned int offset = 0; SFSnortPacket* packetp; #ifdef TARGET_BASED int16_t app_id = SFTARGET_UNKNOWN_PROTOCOL; #endif #ifdef DUMP_BUFFER dumpBufferInit(); #endif uint32_t search_dir_ver, search_dir_keyinit; char flags = STREAM_FLPOLICY_SET_ABSOLUTE; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); PROFILE_VARS; packetp = (SFSnortPacket*) ipacketp; sfPolicyUserPolicySet (ssh_config, policy_id); // preconditions - what we registered for assert(packetp->payload && packetp->payload_size && IPH_IS_VALID(packetp) && packetp->tcp_header); PREPROC_PROFILE_START(sshPerfStats); ssh_eval_config = sfPolicyUserDataGetCurrent(ssh_config); #ifdef DUMP_BUFFER dumpBuffer(SSH_PAYLOAD_DUMP,packetp->payload,packetp->payload_size); #endif /* Attempt to get a previously allocated SSH block. */ sessp = _dpd.sessionAPI->get_application_data(packetp->stream_session, PP_SSH); if (sessp != NULL) { ssh_eval_config = sfPolicyUserDataGet(sessp->config, sessp->policy_id); known_port = 1; } else { /* If not doing autodetection, check the ports to make sure this is * running on an SSH port, otherwise no need to examine the traffic. */ #ifdef TARGET_BASED app_id = _dpd.sessionAPI->get_application_protocol_id(packetp->stream_session); if (app_id == SFTARGET_UNKNOWN_PROTOCOL) { PREPROC_PROFILE_END(sshPerfStats); return; } if (app_id && (app_id != ssh_app_id)) { PREPROC_PROFILE_END(sshPerfStats); return; } if (app_id == ssh_app_id) { known_port = 1; } if (!app_id) { #endif source = (uint8_t)CheckSSHPort( packetp->src_port ); dest = (uint8_t)CheckSSHPort( packetp->dst_port ); if ( !ssh_eval_config->AutodetectEnabled && !source && !dest ) { /* Not one of the ports we care about. */ PREPROC_PROFILE_END(sshPerfStats); return; } #ifdef TARGET_BASED } #endif /* Check the stream session. If it does not currently * have our SSH data-block attached, create one. */ sessp = SSHGetNewSession(packetp, policy_id); if ( !sessp ) { /* Could not get/create the session data for this packet. */ PREPROC_PROFILE_END(sshPerfStats); return; } /* See if a known server port is involved. */ if (!known_port) { known_port = ( source || dest ? 1 : 0 ); /* If this is a non-SSH port, but autodetect is on, we flag this session to reduce false positives later on. */ if (!known_port && ssh_eval_config->AutodetectEnabled) { sessp->state_flags |= SSH_FLG_AUTODETECTED; flags = STREAM_FLPOLICY_SET_APPEND; } } } /* Don't process if we've missed packets */ if (sessp->state_flags & SSH_FLG_MISSED_PACKETS) { PREPROC_PROFILE_END(sshPerfStats); return; } /* We're interested in this session. Turn on stream reassembly. */ if ( !(sessp->state_flags & SSH_FLG_REASSEMBLY_SET )) { _dpd.streamAPI->set_reassembly(packetp->stream_session, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_BOTH, flags); sessp->state_flags |= SSH_FLG_REASSEMBLY_SET; } /* Make sure this preprocessor should run. */ /* check if we're waiting on stream reassembly */ if ( packetp->flags & FLAG_STREAM_INSERT ) { PREPROC_PROFILE_END(sshPerfStats); return; } /* If we picked up mid-stream or missed any packets (midstream pick up * means we've already missed packets) set missed packets flag and make * sure we don't do any more reassembly on this session */ if ((_dpd.sessionAPI->get_session_flags(packetp->stream_session) & SSNFLAG_MIDSTREAM) || _dpd.streamAPI->missed_packets(packetp->stream_session, SSN_DIR_BOTH)) { /* Order only matters if the packets are not encrypted */ if ( !(sessp->state_flags & SSH_FLG_SESS_ENCRYPTED )) { /* Don't turn off reassembly if autodetected since another preprocessor * may actually be looking at this session as well and the SSH * autodetect of this session may be wrong. */ if (!(sessp->state_flags & SSH_FLG_AUTODETECTED)) { _dpd.streamAPI->set_reassembly(packetp->stream_session, STREAM_FLPOLICY_IGNORE, SSN_DIR_BOTH, STREAM_FLPOLICY_SET_APPEND); } sessp->state_flags |= SSH_FLG_MISSED_PACKETS; PREPROC_PROFILE_END(sshPerfStats); return; } } /* Get the direction of the packet. */ if( packetp->flags & FLAG_FROM_SERVER ) { direction = SSH_DIR_FROM_SERVER; search_dir_ver = SSH_FLG_SERV_IDSTRING_SEEN; search_dir_keyinit = SSH_FLG_SERV_PKEY_SEEN | SSH_FLG_SERV_KEXINIT_SEEN; } else { direction = SSH_DIR_FROM_CLIENT; search_dir_ver = SSH_FLG_CLIENT_IDSTRING_SEEN; search_dir_keyinit = SSH_FLG_CLIENT_SKEY_SEEN | SSH_FLG_CLIENT_KEXINIT_SEEN; } if ( !(sessp->state_flags & SSH_FLG_SESS_ENCRYPTED )) { /* If server and client have not performed the protocol * version exchange yet, must look for version strings. */ if ( !(sessp->state_flags & search_dir_ver) ) { offset = ProcessSSHProtocolVersionExchange( sessp, packetp, direction, known_port); if (!offset) { /*Error processing protovers exchange msg */ PREPROC_PROFILE_END(sshPerfStats); return; } /* found protocol version. Stream reassembly might have appended an ssh packet, * such as the key exchange init. Thus call ProcessSSHKeyInitExchange() too. */ } /* Expecting to see the key init exchange at this point * (in SSH2) or the actual key exchange if SSH1 */ if ( !(sessp->state_flags & search_dir_keyinit) ) { offset = ProcessSSHKeyInitExchange( sessp, packetp, direction, offset); if (!offset) { if ( !(sessp->state_flags & SSH_FLG_SESS_ENCRYPTED )) { PREPROC_PROFILE_END(sshPerfStats); return; } } } /* If SSH2, need to process the actual key exchange msgs. * The actual key exchange type was negotiated in the * key exchange init msgs. SSH1 won't arrive here. */ offset = ProcessSSHKeyExchange( sessp, packetp, direction, offset); if (!offset) { PREPROC_PROFILE_END(sshPerfStats); return; } } if ( (sessp->state_flags & SSH_FLG_SESS_ENCRYPTED )) { /* Traffic on this session is currently encrypted. * Two of the major SSH exploits, SSH1 CRC-32 and * the Challenge-Response Overflow attack occur within * the encrypted portion of the SSH session. Therefore, * the only way to detect these attacks is by examining * amounts of data exchanged for anomalies. */ sessp->num_enc_pkts++; if ( sessp->num_enc_pkts <= ssh_eval_config->MaxEncryptedPackets ) { if ( direction == SSH_DIR_FROM_CLIENT ) { if(!offset) { sessp->num_client_bytes += packetp->payload_size; } else { sessp->num_client_bytes += (packetp->payload_size - offset); } if ( (sessp->num_client_bytes >= ssh_eval_config->MaxClientBytes) #if defined(FEAT_OPEN_APPID) && (_dpd.getAppId(packetp->stream_session) != APP_ID_SFTP) #endif /* defined(FEAT_OPEN_APPID) */ ) { /* Probable exploit in progress.*/ if (sessp->version == SSH_VERSION_1) { if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_CRC32 ) { ALERT(SSH_EVENT_CRC32, SSH_EVENT_CRC32_STR); _dpd.sessionAPI->stop_inspection( packetp->stream_session, packetp, SSN_DIR_BOTH, -1, 0 ); } } else { if (ssh_eval_config->EnabledAlerts & SSH_ALERT_RESPOVERFLOW ) { ALERT(SSH_EVENT_RESPOVERFLOW, SSH_EVENT_RESPOVERFLOW_STR); _dpd.sessionAPI->stop_inspection( packetp->stream_session, packetp, SSN_DIR_BOTH, -1, 0 ); } } } } else { /* * Have seen a server response, so * this appears to be a valid exchange. * Reset suspicious byte count to zero. */ sessp->num_client_bytes = 0; } } else { /* Have already examined more than the limit * of encrypted packets. Both the Gobbles and * the CRC32 attacks occur during authentication * and therefore cannot be used late in an * encrypted session. For performance purposes, * stop examining this session. */ _dpd.sessionAPI->stop_inspection( packetp->stream_session, packetp, SSN_DIR_BOTH, -1, 0 ); } } PREPROC_PROFILE_END(sshPerfStats); } /* Retrieves the SSH data block registered with the stream * session associated w/ the current packet. If none exists, * allocates it and registers it with the stream API. * * PARAMETERS: * * packetp: Pointer to the packet from which/in which to * retrieve/store the SSH data block. * * RETURNS: Pointer to an SSH data block, upon success. * NULL, upon failure. */ SSHData * SSHGetNewSession(SFSnortPacket *packetp, tSfPolicyId policy_id) { SSHData* datap = NULL; /* Sanity check(s) */ if (( !packetp ) || ( !packetp->stream_session )) { return NULL; } datap = (SSHData *)calloc(1, sizeof(SSHData)); if ( !datap ) return NULL; /*Register the new SSH data block in the stream session. */ _dpd.sessionAPI->set_application_data( packetp->stream_session, PP_SSH, datap, FreeSSHData ); datap->policy_id = policy_id; datap->config = ssh_config; ((SSHConfig *)sfPolicyUserDataGetCurrent(ssh_config))->ref_count++; return datap; } static int SshFreeConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SSHConfig *pPolicyConfig = (SSHConfig *)pData; //do any housekeeping before freeing SSHConfig sfPolicyUserDataClear (config, policyId); free(pPolicyConfig); return 0; } static void SSHFreeConfig(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, SshFreeConfigPolicy); sfPolicyConfigDelete(config); } /* Registered as a callback with our SSH data blocks when * they are added to the underlying stream session. Called * by the stream preprocessor when a session is about to be * destroyed. * * PARAMETERS: * * idatap: Pointer to the moribund data. * * RETURNS: Nothing. */ static void FreeSSHData( void* idatap ) { SSHData *ssn = (SSHData *)idatap; SSHConfig *config = NULL; if (ssn == NULL) return; if (ssn->config != NULL) { config = (SSHConfig *)sfPolicyUserDataGet(ssn->config, ssn->policy_id); } if (config != NULL) { config->ref_count--; if ((config->ref_count == 0) && (ssn->config != ssh_config)) { sfPolicyUserDataClear (ssn->config, ssn->policy_id); free(config); if (sfPolicyUserPolicyGetActive(ssn->config) == 0) { /* No more outstanding configs - free the config array */ SSHFreeConfig(ssn->config); } } } free(ssn); } /* Validates given port as an SSH server port. * * PARAMETERS: * * port: Port to validate. * * RETURNS: SSH_TRUE, if the port is indeed an SSH server port. * SSH_FALSE, otherwise. */ static inline int CheckSSHPort( uint16_t port ) { if ( ssh_eval_config->ports[ PORT_INDEX(port) ] & CONV_PORT( port ) ) { return SSH_TRUE; } return SSH_FALSE; } /* Checks if the string 'str' is 'max' bytes long or longer. * Returns 0 if 'str' is less than or equal to 'max' bytes; * returns 1 otherwise. */ static inline int SSHCheckStrlen(char *str, int max) { while(*(str++) && max--) ; if(max > 0) return 0; /* str size is <= max bytes */ return 1; } /* Attempts to process current packet as a protocol version exchange * packet. This function will be called if either the client or server * protocol version message (or both) has not been sent. * * PARAMETERS: * * sessionp: Pointer to SSH data for packet's session. * packetp: Pointer to the packet to inspect. * direction: Which direction the packet is going. * known_port: A pre-configured or default server port is involved. * * RETURNS: SSH_SUCCESS, if successfully processed a proto exch msg * SSH_FAILURE, otherwise. */ static unsigned int ProcessSSHProtocolVersionExchange( SSHData* sessionp, SFSnortPacket* packetp, uint8_t direction, uint8_t known_port ) { char* version_stringp = (char*) packetp->payload; uint8_t version; char *version_end; #ifdef DUMP_BUFFER dumpBuffer(SSH_PAYLOAD_DUMP,packetp->payload,packetp->payload_size); #endif /* Get the version. */ if ( packetp->payload_size >= 6 && !strncasecmp( version_stringp, "SSH-1.", 6)) { if (( packetp->payload_size > 7 ) && ( version_stringp[6] == '9') && (version_stringp[7] == '9')) { /* SSH 1.99 which is the same as SSH2.0 */ version = SSH_VERSION_2; } else { version = SSH_VERSION_1; } /* CAN-2002-0159 */ /* Verify the version string is not greater than * the configured maximum. * We've already verified the first 6 bytes, so we'll start * check from &version_string[6] */ if( (ssh_eval_config->EnabledAlerts & SSH_ALERT_SECURECRT ) && /* First make sure the payload itself is sufficiently large */ (packetp->payload_size > ssh_eval_config->MaxServerVersionLen) && /* CheckStrlen will check if the version string up to * MaxServerVersionLen+1 since there's no reason to * continue checking after that point*/ (SSHCheckStrlen(&version_stringp[6], ssh_eval_config->MaxServerVersionLen-6))) { ALERT(SSH_EVENT_SECURECRT, SSH_EVENT_SECURECRT_STR); } } else if ( packetp->payload_size >= 6 && !strncasecmp( version_stringp, "SSH-2.", 6)) { version = SSH_VERSION_2; } else { /* Not SSH on SSH port, CISCO vulnerability */ if ((direction == SSH_DIR_FROM_CLIENT) && ( known_port != 0 ) && ( !(sessionp->state_flags & SSH_FLG_AUTODETECTED) ) && ( ssh_eval_config->EnabledAlerts & SSH_ALERT_PROTOMISMATCH )) { ALERT(SSH_EVENT_PROTOMISMATCH, SSH_EVENT_PROTOMISMATCH_STR); } return 0; } /* Saw a valid protocol exchange message. Mark the session * according to the direction. */ switch( direction ) { case SSH_DIR_FROM_SERVER: sessionp->state_flags |= SSH_FLG_SERV_IDSTRING_SEEN; break; case SSH_DIR_FROM_CLIENT: sessionp->state_flags |= SSH_FLG_CLIENT_IDSTRING_SEEN; break; } sessionp->version = version; version_end = memchr(version_stringp, '\n', packetp->payload_size); if (version_end) return ((version_end - version_stringp) + 1); /* incomplete version string, should end with \n or \r\n for sshv2 */ return packetp->payload_size; } /* Called to process SSH1 key exchange or SSH2 key exchange init * messages. On failure, inspection will be continued, but the packet * will be alerted on, and ignored. * * PARAMETERS: * * sessionp: Pointer to SSH data for packet's session. * packetp: Pointer to the packet to inspect. * direction: Which direction the packet is going. * * RETURN: SSH_SUCCESS, if a valid key exchange message is processed * SSH_FAILURE, otherwise. */ static unsigned int ProcessSSHKeyInitExchange( SSHData* sessionp, SFSnortPacket* packetp, uint8_t direction, unsigned int offset ) { SSH2Packet* ssh2packetp = NULL; uint16_t payload_size = packetp->payload_size; const unsigned char *payload = packetp->payload; unsigned int ssh_length = 0; if (payload_size < sizeof(SSH2Packet) || (payload_size < (offset + sizeof(SSH2Packet))) || (payload_size <= offset)) return 0; payload_size -= offset; payload += offset; if ( sessionp->version == SSH_VERSION_1 ) { uint32_t length; uint8_t padding_length; uint8_t message_type; /* * Validate packet payload. * First 4 bytes should have the SSH packet length, * minus any padding. */ if ( payload_size < 4 ) { if(ssh_eval_config->EnabledAlerts & SSH_ALERT_PAYSIZE) { ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return 0; } /* * SSH1 key exchange is very simple and * consists of only two messages, a server * key and a client key message.` */ memcpy(&length, payload, sizeof(length)); length = ntohl(length); /* Packet payload should be larger than length, due to padding. */ if ( payload_size < length ) { if(ssh_eval_config->EnabledAlerts & SSH_ALERT_PAYSIZE) { ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return 0; } padding_length = (uint8_t)(8 - (length % 8)); /* * With the padding calculated, verify payload is sufficiently large * to include the message type. */ if ( payload_size < (padding_length + 4 + 1 + offset)) { if((offset == 0) && (ssh_eval_config->EnabledAlerts & SSH_ALERT_PAYSIZE)) { ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return 0; } message_type = *( (uint8_t*) (payload + padding_length + 4)); switch( message_type ) { case SSH_MSG_V1_SMSG_PUBLIC_KEY: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_V1_SMSG_PUBLIC_KEY_DUMP,payload,payload_size); #endif if ( direction == SSH_DIR_FROM_SERVER ) { sessionp->state_flags |= SSH_FLG_SERV_PKEY_SEEN; } else if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg not from server. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_V1_CMSG_SESSION_KEY: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_V1_CMSG_SESSION_KEY_DUMP,payload,payload_size); #endif if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_CLIENT_SKEY_SEEN; } else if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Client msg not from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; default: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_TYPE_INVALID_DUMP,payload,payload_size); #endif /* Invalid msg type*/ break; } /* Once the V1 key exchange is done, remainder of * communications are encrypted. */ ssh_length = length + padding_length + sizeof(length) + offset; if ( (sessionp->state_flags & SSH_FLG_V1_KEYEXCH_DONE) == SSH_FLG_V1_KEYEXCH_DONE ) { sessionp->state_flags |= SSH_FLG_SESS_ENCRYPTED; } } else if ( sessionp->version == SSH_VERSION_2 ) { /* We want to overlay the payload on our data packet struct, * so first verify that the payload size is big enough. * This may legitimately occur such as in the case of a * retransmission. */ if ( payload_size < sizeof(SSH2Packet) ) { return 0; } /* Overlay the SSH2 binary data packet struct on the packet */ ssh2packetp = (SSH2Packet*) payload; if ( payload_size < SSH2_HEADERLEN + 1) { /* Invalid packet length. */ return 0; } ssh_length = offset + ntohl(ssh2packetp->packet_length) + sizeof(ssh2packetp->packet_length); switch ( payload[SSH2_HEADERLEN] ) { case SSH_MSG_KEXINIT: sessionp->state_flags |= (direction == SSH_DIR_FROM_SERVER ? SSH_FLG_SERV_KEXINIT_SEEN : SSH_FLG_CLIENT_KEXINIT_SEEN ); break; default: /* Unrecognized message type. */ break; } } else { if(ssh_eval_config->EnabledAlerts & SSH_ALERT_UNRECOGNIZED) { /* Unrecognized version. */ ALERT(SSH_EVENT_VERSION, SSH_VERSION_STR); } return 0; } if(ssh_length < packetp->payload_size) return ssh_length; else return 0; } /* Called to process SSH2 key exchange msgs (key exch init msgs already * processed earlier). On failure, inspection will be continued, but the * packet will be alerted on, and ignored. * * PARAMETERS: * * sessionp: Pointer to SSH data for packet's session. * packetp: Pointer to the packet to inspect. * direction: Which direction the packet is going. * * RETURN: SSH_SUCCESS, if a valid key exchange message is processed * SSH_FAILURE, otherwise. */ static unsigned int ProcessSSHKeyExchange( SSHData* sessionp, SFSnortPacket* packetp, uint8_t direction, unsigned int offset) { SSH2Packet* ssh2packetp = NULL; uint16_t payload_size = packetp->payload_size; const unsigned char *payload = packetp->payload; unsigned int ssh_length; bool next_packet = true; unsigned int npacket_offset = 0; if (payload_size < sizeof(SSH2Packet) || (payload_size < (offset + sizeof(SSH2Packet))) || (payload_size <= offset)) { return 0; } payload_size -= offset; payload += offset; #ifdef DUMP_DUFFER dumpBuffer(SSH_KEY_DUMP,payload,payload_size); #endif while(next_packet) { ssh2packetp = (SSH2Packet*) (payload + npacket_offset); ssh_length = SSHPacket_GetLength(ssh2packetp, payload_size); if (ssh_length == 0) { if( sessionp->state_flags & SSH_FLG_SESS_ENCRYPTED ) { return ( npacket_offset + offset ); } if(ssh_eval_config->EnabledAlerts & SSH_ALERT_PAYSIZE) { /* Invalid packet length. */ ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } return 0; } switch(payload[npacket_offset + SSH2_HEADERLEN] ) { case SSH_MSG_KEXDH_INIT: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_KEXDH_INIT_DUMP,(const uint8_t *)ssh2packetp,ssh_length); #endif if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_KEXDH_INIT_SEEN; } else if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Client msg from server. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_REPLY: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_KEXDH_REPLY_DUMP,(const uint8_t *)ssh2packetp,ssh_length); #endif if ( direction == SSH_DIR_FROM_SERVER ) { /* KEXDH_REPLY has the same msg * type as the new style GEX_REPLY */ sessionp->state_flags |= SSH_FLG_KEXDH_REPLY_SEEN | SSH_FLG_GEX_REPLY_SEEN; } else if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_GEX_REQ: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_KEXDH_GEX_REQ_DUMP,(const uint8_t *)ssh2packetp,ssh_length); #endif if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_GEX_REQ_SEEN; } else if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_GEX_GRP: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_KEXDH_GEX_GRP_DUMP,(const uint8_t *)ssh2packetp,ssh_length); #endif if ( direction == SSH_DIR_FROM_SERVER ) { sessionp->state_flags |= SSH_FLG_GEX_GRP_SEEN; } else if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Client msg from server. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_KEXDH_GEX_INIT: #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_KEXDH_GEX_INIT_DUMP,(const uint8_t *)ssh2packetp,ssh_length); #endif if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_GEX_INIT_SEEN; } else if ( ssh_eval_config->EnabledAlerts & SSH_ALERT_WRONGDIR ) { /* Server msg from client. */ ALERT(SSH_EVENT_WRONGDIR, SSH_EVENT_WRONGDIR_STR); } break; case SSH_MSG_NEWKEYS: /* This message is required to complete the * key exchange. Both server and client should * send one, but as per Alex Kirk's note on this, * in some implementations the server does not * actually send this message. So receving a new * keys msg from the client is sufficient. */ #ifdef DUMP_BUFFER dumpBuffer(SSH_MSG_NEWKEYS_DUMP,(const uint8_t *)ssh2packetp,ssh_length); #endif if ( direction == SSH_DIR_FROM_CLIENT ) { sessionp->state_flags |= SSH_FLG_NEWKEYS_SEEN; } break; default: /* Unrecognized message type. Possibly encrypted */ sessionp->state_flags |= SSH_FLG_SESS_ENCRYPTED; return ( npacket_offset + offset); } /* If either an old-style or new-style Diffie Helman exchange * has completed, the session will enter encrypted mode. */ if (( (sessionp->state_flags & SSH_FLG_V2_DHOLD_DONE) == SSH_FLG_V2_DHOLD_DONE ) || ( (sessionp->state_flags & SSH_FLG_V2_DHNEW_DONE) == SSH_FLG_V2_DHNEW_DONE )) { sessionp->state_flags |= SSH_FLG_SESS_ENCRYPTED; if(ssh_length < payload_size) { if( ssh_length >= 4 ) { npacket_offset += ssh_length; payload_size -= ssh_length; continue; } return ( npacket_offset + offset ); } else return 0; } if ((ssh_length < payload_size) && (ssh_length >= 4)) { npacket_offset += ssh_length; payload_size -= ssh_length; } else { next_packet = false; npacket_offset = 0; if(ssh_eval_config->EnabledAlerts & SSH_ALERT_PAYSIZE) { /* Invalid packet length. */ ALERT(SSH_EVENT_PAYLOAD_SIZE, SSH_PAYLOAD_SIZE_STR); } } } return (npacket_offset + offset); } static void enablePortStreamServices(struct _SnortConfig *sc, SSHConfig *config, tSfPolicyId policy_id) { if (config == NULL) return; if (_dpd.streamAPI) { uint32_t portNum; for (portNum = 0; portNum < MAXPORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status( sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1 ); _dpd.streamAPI->register_reassembly_port( NULL, (uint16_t) portNum, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _dpd.sessionAPI->enable_preproc_for_port( sc, PP_SSH, PROTO_BIT__TCP, (uint16_t) portNum ); } } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, ssh_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif static int SSHCheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { _dpd.setParserPolicy(sc, policyId); if (_dpd.streamAPI == NULL) { _dpd.errMsg("SSHCheckPolicyConfig(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } static int SSHCheckConfig(struct _SnortConfig *sc) { int rval; if ((rval = sfPolicyUserDataIterate (sc, ssh_config, SSHCheckPolicyConfig))) return rval; return 0; } static void SSHCleanExit(int signal, void *data) { if (ssh_config != NULL) { SSHFreeConfig(ssh_config); ssh_config = NULL; } } #ifdef SNORT_RELOAD static void SSHReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId ssh_swap_config = (tSfPolicyUserContextId)*new_config; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); SSHConfig * pPolicyConfig = NULL; if (ssh_swap_config == NULL) { //create a context ssh_swap_config = sfPolicyConfigCreate(); if (ssh_swap_config == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory " "for SSH config.\n"); } if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("SetupSSH(): The Stream preprocessor must be enabled.\n"); } *new_config = (void *)ssh_swap_config; } sfPolicyUserPolicySet (ssh_swap_config, policy_id); pPolicyConfig = (SSHConfig *)sfPolicyUserDataGetCurrent(ssh_swap_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("SSH preprocessor can only be " "configured once.\n"); } pPolicyConfig = (SSHConfig *)calloc(1, sizeof(SSHConfig)); if (!pPolicyConfig) { DynamicPreprocessorFatalMessage("Could not allocate memory for " "SSH preprocessor configuration.\n"); } sfPolicyUserDataSetCurrent(ssh_swap_config, pPolicyConfig); ParseSSHArgs(pPolicyConfig, (u_char *)args); _dpd.addPreproc( sc, ProcessSSH, PRIORITY_APPLICATION, PP_SSH, PROTO_BIT__TCP ); enablePortStreamServices(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } static int SSHReloadVerify(struct _SnortConfig *sc, void *swap_config) { if (_dpd.streamAPI == NULL) { _dpd.errMsg("SetupSSH(): The Stream preprocessor must be enabled.\n"); return -1; } return 0; } static int SshFreeUnusedConfigPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { SSHConfig *pPolicyConfig = (SSHConfig *)pData; //do any housekeeping before freeing SSHConfig if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); free(pPolicyConfig); } return 0; } static void * SSHReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId ssh_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = ssh_config; if (ssh_swap_config == NULL) return NULL; ssh_config = ssh_swap_config; sfPolicyUserDataFreeIterate (old_config, SshFreeUnusedConfigPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) { /* No more outstanding configs - free the config array */ return (void *)old_config; } return NULL; } static void SSHReloadSwapFree(void *data) { if (data == NULL) return; SSHFreeConfig((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/ssh/sf_ssh.vcxproj0000444000175000017500000004037514230012554021715 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {967A437F-57A3-4C85-A273-C70C75DF16CC} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_ssh.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_ssh.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_ssh.bsc true true Console .\Release\sf_ssh.dll .\Release\sf_ssh.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;SF_SNORT_PREPROC_DLL;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_ssh.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_ssh.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_ssh.bsc true true Console .\Release\sf_ssh.dll .\Release\sf_ssh.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_ssh.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_ssh.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_ssh.bsc true true true Console .\Debug\sf_ssh.dll .\Debug\sf_ssh.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) SF_SNORT_PREPROC_DLL;_DEBUG;DEBUG;ENABLE_PAF;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_ssh.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_ssh.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_ssh.bsc true true true Console .\Debug\sf_ssh.dll .\Debug\sf_ssh.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/ssh/ssh_buffer_dump.h0000644000175000017500000000356014241076445022347 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file ssh_buffer_dump.h ** ** @author Seshaiah Erugu ** ** @brief This file contains structures and functions for ** dumping buffers used during SSH inspection. */ #ifndef __SSH_BUFFER_DUMP_H__ #define __SSH_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { SSH_PAYLOAD_DUMP, SSH_VERSION_DUMP, SSH_MSG_KEXDH_INIT_DUMP, SSH_MSG_KEXDH_REPLY_DUMP, SSH_MSG_KEXDH_GEX_REQ_DUMP, SSH_MSG_KEXDH_GEX_GRP_DUMP, SSH_MSG_KEXDH_GEX_INIT_DUMP, SSH_MSG_NEWKEYS_DUMP, SSH_MSG_V1_SMSG_PUBLIC_KEY_DUMP, SSH_MSG_V1_CMSG_SESSION_KEY_DUMP, SSH_MSG_TYPE_INVALID_DUMP } SSH_BUFFER_DUMP; void dumpBuffer(SSH_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getSSHBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/0000755000175000017500000000000014242725715020231 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_eo_events.h0000644000175000017500000001040414241075753023420 0ustar apoapo/* * ftpp_eo_events.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Defines the events for the FTP Telnet Preprocessor. * * NOTES: * - 20.09.04: Initial Development. SAS * */ #ifndef __FTP_EO_EVENTS_H__ #define __FTP_EO_EVENTS_H__ #include "ftpp_include.h" /* * FTP Events */ #define FTP_EO_TELNET_CMD 0 #define FTP_EO_INVALID_CMD 1 #define FTP_EO_PARAMETER_LENGTH_OVERFLOW 2 #define FTP_EO_MALFORMED_PARAMETER 3 #define FTP_EO_PARAMETER_STR_FORMAT 4 #define FTP_EO_RESPONSE_LENGTH_OVERFLOW 5 #define FTP_EO_ENCRYPTED 6 #define FTP_EO_BOUNCE 7 #define FTP_EO_EVASIVE_TELNET_CMD 8 #define FTP_EO_TELNET_CMD_SID 1 #define FTP_EO_INVALID_CMD_SID 2 #define FTP_EO_PARAMETER_LENGTH_OVERFLOW_SID 3 #define FTP_EO_MALFORMED_PARAMETER_SID 4 #define FTP_EO_PARAMETER_STR_FORMAT_SID 5 #define FTP_EO_RESPONSE_LENGTH_OVERFLOW_SID 6 #define FTP_EO_ENCRYPTED_SID 7 #define FTP_EO_BOUNCE_SID 8 #define FTP_EO_EVASIVE_TELNET_CMD_SID 9 /* * IMPORTANT: * Every time you add an FTP event, this number must be * incremented. */ #define FTP_EO_EVENT_NUM 9 /* * These defines are the alert names for each event */ #define FTP_EO_TELNET_CMD_STR \ "(ftp_telnet) TELNET CMD on FTP Command Channel" #define FTP_EO_INVALID_CMD_STR \ "(ftp_telnet) Invalid FTP Command" #define FTP_EO_PARAMETER_LENGTH_OVERFLOW_STR \ "(ftp_telnet) FTP command parameters were too long" #define FTP_EO_MALFORMED_PARAMETER_STR \ "(ftp_telnet) FTP command parameters were malformed" #define FTP_EO_PARAMETER_STR_FORMAT_STR \ "(ftp_telnet) FTP command parameters contained potential string format" #define FTP_EO_RESPONSE_LENGTH_OVERFLOW_STR \ "(ftp_telnet) FTP response message was too long" #define FTP_EO_ENCRYPTED_STR \ "(ftp_telnet) FTP traffic encrypted" #define FTP_EO_BOUNCE_STR \ "(ftp_telnet) FTP bounce attempt" #define FTP_EO_EVASIVE_TELNET_CMD_STR \ "(ftp_telnet) Evasive (incomplete) TELNET CMD on FTP Command Channel" /* * TELNET Events */ #define TELNET_EO_AYT_OVERFLOW 0 #define TELNET_EO_ENCRYPTED 1 #define TELNET_EO_SB_NO_SE 2 #define TELNET_EO_AYT_OVERFLOW_SID 1 #define TELNET_EO_ENCRYPTED_SID 2 #define TELNET_EO_SB_NO_SE_SID 3 /* * IMPORTANT: * Every time you add a telnet event, this number must be * incremented. */ #define TELNET_EO_EVENT_NUM 3 /* * These defines are the alert names for each event */ #define TELNET_EO_AYT_OVERFLOW_STR \ "(ftp_telnet) Consecutive Telnet AYT commands beyond threshold" #define TELNET_EO_ENCRYPTED_STR \ "(ftp_telnet) Telnet traffic encrypted" #define TELNET_EO_SB_NO_SE_STR \ "(ftp_telnet) Telnet Subnegotiation Begin Command without Subnegotiation End" /* * Event Priorities */ #define FTPP_EO_HIGH_PRIORITY 0 #define FTPP_EO_MED_PRIORITY 1 #define FTPP_EO_LOW_PRIORITY 2 #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_eo.h0000644000175000017500000000673614241075751022047 0ustar apoapo/* * ftpp_eo.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Contains the data structures, event types, specific events, * and function prototypes for the Event Output Module. * * This file is key to alerting with FTPTelnet. It contains the header * file with all the individual alerts. * * The Event Output Module provides a mechanism to queue HttpInspect events * and prioritize them. The Event Output Module does not actually log the * events, but tracks them per session/packet. The user program needs to * do the actual logging of events. * * Each event contains the type of event, the priority of the event, and * any data that is associated with the event. * * NOTES: * - 20.09.04: Initial Development. SAS * */ #ifndef __FTPP_EO_H__ #define __FTPP_EO_H__ #include "ftpp_include.h" #include "ftpp_eo_events.h" /* * We hold the type of alert, the priority of the alert * and any data associated with this alert. */ typedef struct s_FTPP_EVENT_INFO { int alert_id; /* the alert id */ int alert_sid; /* the unique sid */ int classification; /* classification */ int priority; /* the alert priority, 0 = highest */ char *alert_str; /* the alert string */ } FTPP_EVENT_INFO; typedef struct s_FTPP_EVENT { FTPP_EVENT_INFO *event_info; int count; /* number of times event occurred in session */ void *data; /* generic ptr to data */ void (*free_data)(void *); /* function to free data */ } FTPP_EVENT; /* * This is a generic structure to translate different event types to * the same structure. This helps when logging the different types * of events. */ typedef struct s_FTPP_GEN_EVENTS { int *stack; int stack_count; FTPP_EVENT *events; } FTPP_GEN_EVENTS; /* * The idea behind this event storage structure is that we use a * simple stack to tell us which events we have set, so we don't * set an event twice and can access the events very easily. */ typedef struct s_FTP_EVENTS { int stack[FTP_EO_EVENT_NUM]; int stack_count; FTPP_EVENT events[FTP_EO_EVENT_NUM]; } FTP_EVENTS; /* * The idea behind this event storage structure is that we use a * simple stack to tell us which events we have set, so we don't * set an event twice and can access the events very easily. */ typedef struct s_TELNET_EVENTS { int stack[TELNET_EO_EVENT_NUM]; int stack_count; FTPP_EVENT events[TELNET_EO_EVENT_NUM]; } TELNET_EVENTS; #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftp_server.h0000644000175000017500000000337614241075750022566 0ustar apoapo/* * ftp_server.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Header file for FTPTelnet FTP Server Module * * This file defines the server structure and functions to access server * inspection. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTP_SERVER_H__ #define __FTP_SERVER_H__ #include "ftpp_include.h" typedef struct s_FTP_SERVER_RSP { char *rsp_line; unsigned int rsp_line_size; char *rsp_begin; char *rsp_end; unsigned int rsp_size; char *msg_begin; char *msg_end; unsigned int msg_size; char *pipeline_req; int state; } FTP_SERVER_RSP; typedef struct s_FTP_SERVER { FTP_SERVER_RSP response; } FTP_SERVER; int ftp_server_inspection(void *S, unsigned char *data, int dsize); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftp_cmd_lookup.c0000644000175000017500000001670514241075745023413 0ustar apoapo/* * ftp_cmd_lookup.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains functions to access the CMD_LOOKUP structure. * * We wrap the access to CMD_LOOKUP so changing the lookup algorithms * are more modular and independent. This is the only file that would need * to be changed to change the algorithmic lookup. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "hi_util_kmap.h" #include "ftpp_ui_config.h" #include "ftpp_return_codes.h" #include "snort_ftptelnet.h" /* * Function: ftp_cmd_lookup_init(CMD_LOOKUP **CmdLookup) * * Purpose: Initialize the cmd_lookup structure. * * We need to initialize the cmd_lookup structure for * the FTP command configuration. Don't want a NULL pointer * flying around, when we have to look for FTP commands. * * Arguments: CmdLookup => pointer to the pointer of the cmd * lookup structure. * * Returns: int => return code indicating error or success * */ int ftp_cmd_lookup_init(CMD_LOOKUP **CmdLookup) { KMAP *km = KMapNew((KMapUserFreeFunc)FTPTelnetCleanupFTPCMDConf); *CmdLookup = km; if(*CmdLookup == NULL) { return FTPP_MEM_ALLOC_FAIL; } km->nocase = 1; return FTPP_SUCCESS; } /* * Function: ftp_cmd_lookup_cleanup(CMD_LOOKUP **CmdLookup) * * Purpose: Free the cmd_lookup structure. * We need to free the cmd_lookup structure. * * Arguments: CmdLookup => pointer to the pointer of the cmd * lookup structure. * * Returns: int => return code indicating error or success * */ int ftp_cmd_lookup_cleanup(CMD_LOOKUP **CmdLookup) { KMAP *km; if (CmdLookup == NULL) return FTPP_INVALID_ARG; km = *CmdLookup; if (km) { KMapDelete(km); *CmdLookup = NULL; } return FTPP_SUCCESS; } /* * Function: ftp_cmd_lookup_add(CMD_LOOKUP *CmdLookup, * char *ip, int len, * FTP_CMD_CONF *FTPCmd) * * Purpose: Add a cmd configuration to the list. * We add these keys like you would normally think to add * them, because on low endian machines the least significant * byte is compared first. This is what we want to compare * IPs backward, doesn't work on high endian machines, but oh * well. Our platform is Intel. * * Arguments: CmdLookup => a pointer to the lookup structure * cmd => the ftp cmd * len => Length of the cmd * FTPCmd => a pointer to the cmd configuration structure * * Returns: int => return code indicating error or success * */ int ftp_cmd_lookup_add(CMD_LOOKUP *CmdLookup, char *cmd, int len, FTP_CMD_CONF *FTPCmd) { int iRet; if(!CmdLookup || !FTPCmd) { return FTPP_INVALID_ARG; } iRet = KMapAdd(CmdLookup, (void *)cmd, len, (void *)FTPCmd); if (iRet) { /* * This means the key has already been added. */ if(iRet == 1) { return FTPP_NONFATAL_ERR; } else { return FTPP_MEM_ALLOC_FAIL; } } return FTPP_SUCCESS; } /* * Function: ftp_cmd_lookup_find(CMD_LOOKUP *CmdLookup, * char *ip, int len, * int *iError) * * Purpose: Find a cmd configuration given a IP. * We look up a cmd configuration given an FTP cmd and * return a pointer to that cmd configuration if found. * * Arguments: CmdLookup => a pointer to the lookup structure * cmd => the ftp cmd * len => Length of the cmd * iError => a pointer to an error code * * Returns: int => return code indicating error or success * * Returns: FTP_CMD_CONF* => Pointer to cmd configuration structure * matching IP if found, NULL otherwise. * */ FTP_CMD_CONF *ftp_cmd_lookup_find(CMD_LOOKUP *CmdLookup, const char *cmd, int len, int *iError) { FTP_CMD_CONF *FTPCmd = NULL; if(!iError) { return NULL; } if(!CmdLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; FTPCmd = (FTP_CMD_CONF *)KMapFind(CmdLookup,(void *)cmd,len); if (!FTPCmd) { *iError = FTPP_NOT_FOUND; } return FTPCmd; } /* * Function: ftp_cmd_lookup_first(CMD_LOOKUP *CmdLookup, * int *iError) * * Purpose: This lookups the first cmd configuration, so we can * iterate through the configurations. * * Arguments: CmdLookup => pointer to the cmd lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_CMD_CONF* => Pointer to first cmd configuration structure * */ FTP_CMD_CONF *ftp_cmd_lookup_first(CMD_LOOKUP *CmdLookup, int *iError) { FTP_CMD_CONF *FTPCmd; if(!iError) { return NULL; } if(!CmdLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; FTPCmd = (FTP_CMD_CONF *)KMapFindFirst(CmdLookup); if (!FTPCmd) { *iError = FTPP_NOT_FOUND; } return FTPCmd; } /* * Function: ftp_cmd_lookup_next(CMD_LOOKUP *CmdLookup, * int *iError) * * Iterates to the next configuration, like a list it just returns * the next config in the config list. * * Purpose: This lookups the next cmd configuration, so we can * iterate through the configurations. * * Arguments: CmdLookup => pointer to the cmd lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_CMD_CONF* => Pointer to next cmd configuration structure * */ FTP_CMD_CONF *ftp_cmd_lookup_next(CMD_LOOKUP *CmdLookup, int *iError) { FTP_CMD_CONF *FTPCmd; if(!iError) { return NULL; } if(!CmdLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; FTPCmd = (FTP_CMD_CONF *)KMapFindNext(CmdLookup); if (!FTPCmd) { *iError = FTPP_NOT_FOUND; } return FTPCmd; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_ui_server_lookup.h0000644000175000017500000000350514241075770025030 0ustar apoapo/* * ftpp_ui_server_lookup.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains function definitions for server lookups. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTPP_UI_SERVER_LOOKUP_H__ #define __FTPP_UI_SERVER_LOOKUP_H__ #include "ftpp_include.h" #include "ftpp_ui_config.h" int ftpp_ui_server_lookup_init(SERVER_LOOKUP **ServerLookup); int ftpp_ui_server_lookup_cleanup(SERVER_LOOKUP **ServerLookup); int ftpp_ui_server_lookup_add(SERVER_LOOKUP *ServerLookup, sfcidr_t *IP, FTP_SERVER_PROTO_CONF *ServerConf); FTP_SERVER_PROTO_CONF *ftpp_ui_server_lookup_find(SERVER_LOOKUP *ServerLookup, sfaddr_t* Ip, int *iError); int ftpp_ui_server_iterate( struct _SnortConfig *, SERVER_LOOKUP *ServerLookup, sfrt_sc_iterator_callback3 userfunc, int *iError ); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_eo_log.h0000644000175000017500000000310114241075755022673 0ustar apoapo/* * ftpp_eo_log.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Defines the functions for logging within the FTP Telnet preprocessor. * * NOTES: * - 20.09.04: Initial Development. SAS * */ #ifndef __FTPP_EO_LOG_H__ #define __FTPP_EO_LOG_H__ #include "ftpp_include.h" #include "ftpp_si.h" #include "ftpp_return_codes.h" void ftpp_eo_event_log_init(void); int telnet_eo_event_log(TELNET_SESSION *Session, int iEvent, void *data, void (*free_data)(void *)); int ftp_eo_event_log(FTP_SESSION *Session, int iEvent, void *data, void (*free_data)(void *)); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_return_codes.h0000644000175000017500000000445414241075757024141 0ustar apoapo/* * ftpp_return_codes.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file defines the return codes for the FTPTelnet functions. * * Common return codes are defined here for all functions and libraries to * use. This should make function error checking easier. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTPP_RETURN_CODES_H__ #define __FTPP_RETURN_CODES_H__ #include "ftpp_include.h" #define FTPP_BOOL_FALSE 0 #define FTPP_SUCCESS 0 /* * Non-fatal errors are positive */ #define FTPP_BOOL_TRUE 1 #define FTPP_NONFATAL_ERR 1 #define FTPP_OUT_OF_BOUNDS 2 #define FTPP_INVALID_PROTO 3 #define FTPP_NORMALIZED 4 #define FTPP_MALFORMED_FTP_RESPONSE 5 #define FTPP_ALERTED 6 #define FTPP_NON_DIGIT 7 #define FTPP_MALFORMED_IP_PORT 8 #define FTPP_PORT_ATTACK 9 #define FTPP_INVALID_SESSION 10 #define FTPP_OR_FOUND 100 #define FTPP_OPT_END_FOUND 101 #define FTPP_CHOICE_END_FOUND 102 /* * Fatal errors are negative */ #define FTPP_FATAL_ERR -1 #define FTPP_INVALID_ARG -2 #define FTPP_MEM_ALLOC_FAIL -3 #define FTPP_NOT_FOUND -4 #define FTPP_INVALID_FILE -5 #define FTPP_ALERT -6 #define FTPP_INVALID_DATE -100 #define FTPP_INVALID_PARAM -101 #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_include.h0000644000175000017500000000273214241075756023064 0ustar apoapo/* * ftpp_include.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Global definitions for the FTPTelnet preprocessor. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTP_INCLUDE_H__ #define __FTP_INCLUDE_H__ #include "sf_types.h" #include "sf_ip.h" #include "snort_debug.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #define GENERATOR_SPP_FTPP_FTP 125 #define GENERATOR_SPP_FTPP_TELNET 126 #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_ui_config.h0000644000175000017500000002326714241075766023412 0ustar apoapo/* * ftpp_ui_config.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains the internal configuration structures * for FTPTelnet. * * This file holds the configuration constructs for the FTPTelnet global * configuration and the FTP client configurations. It also contains the * function prototypes for accessing client configurations. * * NOTES: * - 20.09.04: Initial Development. SAS */ #ifndef __FTPP_UI_CONFIG_H__ #define __FTPP_UI_CONFIG_H__ //#include "decode.h" #include "ftpp_include.h" #include "hi_util_kmap.h" #include "ipv6_port.h" #include "sfrt.h" #include "snort_bounds.h" /* * Defines */ #define FTPP_UI_CONFIG_STATELESS 0 #define FTPP_UI_CONFIG_STATEFUL 1 #define FTPP_UI_CONFIG_TELNET_DEF_AYT_THRESHOLD -1 #define FTPP_UI_CONFIG_FTP_DEF_RESP_MSG_MAX -1 #define FTPP_UI_CONFIG_FTP_DEF_CMD_PARAM_MAX 100 /**Maximum number of entries in server_lookup table. */ #define FTPP_UI_CONFIG_MAX_SERVERS 20 #define FTPP_UI_CONFIG_MAX_CLIENTS 20 #define MIN_CMD 3 #define MAX_CMD 4 /* * Defines a search type for the client configurations in the * global configuration. We want this generic so we can change * it easily if we change the search type. */ typedef table_t CLIENT_LOOKUP; typedef table_t SERVER_LOOKUP; typedef KMAP BOUNCE_LOOKUP; /* * Defines a search type for the FTP commands in the client * global configuration. We want this generic so we can change * it easily if we change the search type. */ typedef KMAP CMD_LOOKUP; /* * This structure simply holds a value for on/off and whether * alert is on/off. Should be used for many configure options. */ typedef struct s_FTPTELNET_CONF_OPT { int on; /*< if true, configuration option is on */ int alert; /*< if true, alert if option is found */ } FTPTELNET_CONF_OPT; typedef enum s_FTP_PARAM_TYPE { e_head = 0, e_unrestricted, /* The default */ e_strformat, e_int, e_number, e_char, e_date, e_literal, e_host_port, e_long_host_port, e_extd_host_port } FTP_PARAM_TYPE; /* * Some FTP servers accept MDTM commands to set the modification time * on a file. The most common are servers accept a format using * YYYYMMDDHHmmss[.uuu], while others accept a format using * YYYYMMDDHHmmss[+|-]TZ format. Because of this, the default syntax * below is for the first case (time format as specified in * http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt) * * If you need to check validity for a server that uses the TZ format, * use the following: * * cmd_validity MDTM < [ date nnnnnnnnnnnnnn[{+|-}n[n]] ] string > * * Format uses the following: * n = digit * C = character * . = period (literal) * + = plus (literal) * - = minus (literal) * [ = optional begin * ] = optional end * { = OR begin * } = OR end * | = OR separator * * ie, nnnnnnnnnnnnnn[.n[n[n]]] --> * force conformance to YYYYMMDDHHmmss.uuu, * where 1,2, or 3 microsec digits are optional. * * ie, nnnnnnnnnnnnnn[{+|-}n[n]] --> * force conformance to YYYYMMDDHHmmss+TZ, * where optional +TZ is + or - one or two digit number */ typedef struct s_FTP_DATE_FMT { char *format_string; int empty; struct s_FTP_DATE_FMT *next; struct s_FTP_DATE_FMT *prev; struct s_FTP_DATE_FMT *optional; struct s_FTP_DATE_FMT *next_a; struct s_FTP_DATE_FMT *next_b; } FTP_DATE_FMT; typedef struct s_FTP_PARAM_FMT { FTP_PARAM_TYPE type; int optional; /* Format is only used for types listed below to specify * allowable values. Other types provide no variances * for the format. */ union u_FORMAT { uint32_t chars_allowed; /* For type == e_char */ FTP_DATE_FMT *date_fmt; /* For type == e_date */ char* literal; /* For type == e_literal */ } format; struct s_FTP_PARAM_FMT *prev_param_fmt; struct s_FTP_PARAM_FMT *next_param_fmt; struct s_FTP_PARAM_FMT *optional_fmt; struct s_FTP_PARAM_FMT **choices; int numChoices; int prev_optional; /* Only set if optional is set */ const char *next_param; /* Pointer to buffer for the next parameter. To be used to backtrack for optional parameters that don't match. */ } FTP_PARAM_FMT; typedef struct s_FTP_CMD_CONF { /* Maximum length for parameters for this cmd. * Default -1 is unlimited */ unsigned int max_param_len; int max_param_len_overridden; int check_validity; int data_chan_cmd; int data_xfer_cmd; int data_rest_cmd; int file_put_cmd; int file_get_cmd; int encr_cmd; int login_cmd; int dir_response; FTP_PARAM_FMT *param_format; char cmd_name[1]; // variable length array } FTP_CMD_CONF; typedef struct s_PROTO_CONF { unsigned int port_count; char ports[MAXPORTS]; } PROTO_CONF; /* * This is the configuration construct that holds the specific * options for a FTP server. Each unique server has it's own * structure and there is a global structure for servers that * don't have a unique configuration. */ typedef struct s_FTP_SERVER_PROTO_CONF { /* Ports must be first */ PROTO_CONF proto_ports; char *serverAddr; unsigned int def_max_param_len; unsigned int max_cmd_len; int print_commands; CMD_LOOKUP *cmd_lookup; FTPTELNET_CONF_OPT telnet_cmds; FTPTELNET_CONF_OPT ignore_telnet_erase_cmds; int data_chan; /**Counts references to this allocated data structure. Each additional * reference should increment referenceCount. Each attempted free should * decrement it. When reference count reaches 0, then this * data structure should be freed. */ int referenceCount; } FTP_SERVER_PROTO_CONF; typedef struct s_FTP_BOUNCE_TO { sfcidr_t ip; unsigned short portlo; unsigned short porthi; } FTP_BOUNCE_TO; /* * This is the configuration construct that holds the specific * options for a FTP client. Each unique client has it's own * structure and there is a global structure for clients that * don't have a unique configuration. */ typedef struct s_FTP_CLIENT_PROTO_CONF { char *clientAddr; unsigned int max_resp_len; int data_chan; FTPTELNET_CONF_OPT bounce; FTPTELNET_CONF_OPT telnet_cmds; FTPTELNET_CONF_OPT ignore_telnet_erase_cmds; /* allow_bounce to IP/mask port|port-range */ /* TODO: change this to use a quick find of IP/mask */ BOUNCE_LOOKUP *bounce_lookup; /**Counts references to this allocated data structure. Each additional * reference should increment referenceCount. Each attempted free should * decrement it. When reference count reaches 0, then this * data structure should be freed. */ int referenceCount; } FTP_CLIENT_PROTO_CONF; /* * This is the configuration construct that holds the specific * options for telnet. There is a global structure for all telnet * connections. */ typedef struct s_TELNET_PROTO_CONF { /* Ports must be first */ PROTO_CONF proto_ports; int normalize; int ayt_threshold; char detect_anomalies; } TELNET_PROTO_CONF; /* * This is the configuration for the global FTPTelnet * configuration. It contains the global aspects of the * configuration, a standard global default configuration, * and client configurations. */ typedef struct s_FTPTELNET_GLOBAL_CONF { int inspection_type; int check_encrypted_data; FTPTELNET_CONF_OPT encrypted; FTP_CLIENT_PROTO_CONF *default_ftp_client; FTP_SERVER_PROTO_CONF *default_ftp_server; TELNET_PROTO_CONF *telnet_config; SERVER_LOOKUP *server_lookup; CLIENT_LOOKUP *client_lookup; uint32_t ref_count; uint32_t xtra_filename_id; } FTPTELNET_GLOBAL_CONF; /* * Functions */ int ftpp_ui_config_init_global_conf(FTPTELNET_GLOBAL_CONF *GlobalConf); int ftpp_ui_config_default(FTPTELNET_GLOBAL_CONF *GlobalConf); int ftpp_ui_config_reset_global(FTPTELNET_GLOBAL_CONF *GlobalConf); int ftpp_ui_config_reset_ftp_client(FTP_CLIENT_PROTO_CONF *ClientConf, char first); int ftpp_ui_config_reset_ftp_server(FTP_SERVER_PROTO_CONF *ServerConf, char first); void ftpp_ui_config_reset_ftp_cmd_format(FTP_PARAM_FMT *ThisFmt); void ftpp_ui_config_reset_ftp_cmd_date_format(FTP_DATE_FMT *DateFmt); int ftpp_ui_config_reset_ftp_cmd(FTP_CMD_CONF *FTPCmd); int ftpp_ui_config_reset_telnet_proto(TELNET_PROTO_CONF *ClientConf); int ftpp_ui_config_add_ftp_client(FTPTELNET_GLOBAL_CONF *GlobalConf, sfcidr_t* ClientIP, FTP_CLIENT_PROTO_CONF *ClientConf); int ftpp_ui_config_add_ftp_server(FTPTELNET_GLOBAL_CONF *GlobalConf, sfcidr_t *ClientIP, FTP_SERVER_PROTO_CONF *ClientConf); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/pp_telnet.c0000644000175000017500000003617714241076004022372 0ustar apoapo/* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2002-2013 Sourcefire, Inc. * Copyright (C) 1998-2002 Martin Roesch * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. */ /* Snort Preprocessor for Telnet Negotiation Normalization*/ /* $Id$ */ /* pp_telnet.c * * Purpose: Telnet sessions can contain telnet negotiation strings * that can disrupt pattern matching. This plugin detects * negotiation strings in stream and "normalizes" them much like * the http_decode preprocessor normalizes encoded URLs * * * official registry of options * http://www.iana.org/assignments/telnet-options * * Arguments: None * * Effect: The telnet nogiation data is removed from the payload * * Comments: * */ /* your preprocessor header file goes here */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_STRINGS_H #include #endif #include #include "ftpp_eo_log.h" #include "pp_telnet.h" #include "ftpp_return_codes.h" #include "snort_debug.h" #include "stream_api.h" #define NUL 0x00 #define CR 0x0d #define LF 0x0a /* This is the allowable number of 8 bit characters, * ie, non-ASCII, before we declare this packet/stream * as encrypted. */ #define CONSECUTIVE_8BIT_THRESHOLD 3 /* * Function: normalize_telnet(Packet *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ int normalize_telnet(FTPTELNET_GLOBAL_CONF *GlobalConf, TELNET_SESSION *tnssn, SFSnortPacket *p, int iMode, char ignoreEraseCmds) { int ret = FTPP_NORMALIZED; const unsigned char *read_ptr, *sb_start = NULL; int saw_ayt = 0; const unsigned char *start = _dpd.altBuffer->data; unsigned char *write_ptr; const unsigned char *end; int normalization_required = 0; int consec_8bit_chars = 0; /* Telnet commands are handled in here. * They can be 2 bytes long -- ie, IAC NOP, IAC AYT, etc. * Sub-negotiation strings are at least 4 bytes, IAC SB x IAC SE */ if(p->payload_size < 2) { if (tnssn && iMode == FTPP_SI_CLIENT_MODE) tnssn->consec_ayt = 0; return FTPP_SUCCESS; } /* setup the pointers */ read_ptr = p->payload; end = p->payload + p->payload_size; /* look to see if we have any telnet negotiaion codes in the payload */ while(!normalization_required && (read_ptr < end)) { /* look for the start of a negotiation string */ if(*read_ptr == (unsigned char) TNC_IAC) { /* set a flag for stage 2 normalization */ normalization_required = 1; } else { /* Okay, it wasn't an IAC also its a midstream pickup */ if (*read_ptr > 0x7F && _dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM) { consec_8bit_chars++; if (consec_8bit_chars > CONSECUTIVE_8BIT_THRESHOLD) { /* This data stream had a series of 8 bit characters. * It is very likely encrypted. This handles the case * where we either missed the option negotiation, or * lost state of an already encrypted telnet session. */ if (tnssn) { tnssn->encr_state = 1; if (GlobalConf->encrypted.alert) { /* Alert on encrypted channel */ telnet_eo_event_log(tnssn, TELNET_EO_ENCRYPTED, NULL, NULL); } if (!GlobalConf->check_encrypted_data) { /* Mark this session & packet as one to ignore */ _dpd.sessionAPI->stop_inspection(p->stream_session, p, SSN_DIR_BOTH, -1, 0); /* No point to do further normalization */ return FTPP_ALERT; } } break; } } else { consec_8bit_chars = 0; } } read_ptr++; } if(!normalization_required) { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Nothing to process!\n");); if (tnssn && iMode == FTPP_SI_CLIENT_MODE) tnssn->consec_ayt = 0; return FTPP_SUCCESS; } /* * if we found telnet negotiation strings OR backspace characters, * we're going to have to normalize the data * * Note that this is always ( now: 2002-08-12 ) done to a * alternative data buffer. */ /* rewind the data stream to p->data */ read_ptr = p->payload; /* setup for overwriting the negotaiation strings with * the follow-on data */ write_ptr = (unsigned char *) _dpd.altBuffer; /* walk thru the remainder of the packet */ while((read_ptr < end) && (write_ptr < ((unsigned char *) _dpd.altBuffer->data) + sizeof(_dpd.altBuffer->data))) { saw_ayt = 0; /* if the following byte isn't a subnegotiation initialization */ if(((read_ptr + 1) < end) && (*read_ptr == (unsigned char) TNC_IAC) && (*(read_ptr + 1) != (unsigned char) TNC_SB)) { /* NOPs are two bytes long */ switch(* ((unsigned char *)(read_ptr + 1))) { case TNC_NOP: read_ptr += 2; break; case TNC_EAC: read_ptr += 2; /* wind it back a character? */ if (ignoreEraseCmds == FTPP_APPLY_TNC_ERASE_CMDS) { if(write_ptr > start) { write_ptr--; } } break; case TNC_EAL: read_ptr += 2; /* wind it back a line? */ if (ignoreEraseCmds == FTPP_APPLY_TNC_ERASE_CMDS) { /* Go back to previous CR NULL or CR LF? */ while (write_ptr > start) { /* Go to previous char */ write_ptr--; if ((*write_ptr == CR) && ((*(write_ptr+1) == NUL) || (*(write_ptr+1) == LF)) ) { /* Okay, found the CR NUL or CR LF, move it * forward past those two -- that is the * beginning of this line */ write_ptr+=2; break; } } } break; /* These are two bytes long */ case TNC_AYT: saw_ayt = 1; if (tnssn) { tnssn->consec_ayt++; if ((tnssn->telnet_conf->ayt_threshold > 0) && (tnssn->consec_ayt > tnssn->telnet_conf->ayt_threshold)) { /* Alert on consecutive AYT commands */ telnet_eo_event_log(tnssn, TELNET_EO_AYT_OVERFLOW, NULL, NULL); tnssn->consec_ayt = 0; return FTPP_ALERT; } } /* Fall through */ case TNC_BRK: case TNC_DM: case TNC_IP: case TNC_AO: case TNC_GA: #ifdef RFC1184 case TNC_EOF: case TNC_SUSP: case TNC_ABOR: #endif #ifdef RFC885 case TNC_EOR: #endif read_ptr += 2; break; case TNC_SE: /* Uh, what the heck is a Subnegotiation-end * doing here without SB?. could generate an alert. * Will just normalize it out since we may have * processed the SB in a previous packet. */ read_ptr += 2; break; case TNC_IAC: /* IAC IAC -- means the IAC character (0xff) should be * in the data stream since it was escaped */ read_ptr++; /* skip past the first IAC */ *write_ptr++ = *read_ptr++; break; case TNC_WILL: case TNC_WONT: case TNC_DO: case TNC_DONT: read_ptr += 3; break; default: /* move the read ptr up 2 bytes */ read_ptr += 2; } /* If not an AYT, reset it */ if (!saw_ayt) { if (tnssn && iMode == FTPP_SI_CLIENT_MODE) tnssn->consec_ayt = 0; } } /* check for subnegotiation */ else if(((read_ptr + 1) < end) && (*read_ptr == (unsigned char) TNC_IAC) && (*(read_ptr+1) == (unsigned char) TNC_SB)) { sb_start = read_ptr; switch (*(read_ptr+2)) { case 0x26: /* Encryption -- RFC 2946 */ /* printf("Telnet: Saw SB for Encryption\n"); */ read_ptr += 3; switch (*read_ptr) { #ifdef TRACK_ENCRYPTION_NEGOTIATION case 0x00: /* Client sending the Encryption IS marker * followed by address. */ { read_ptr++; if (*read_ptr != 0x00) /* Encryption type is not NULL */ { /* printf("Encryption being negotiated by * telnet client\n"); */ } } break; #endif case 0x03: /* Client sending the Encryption START marker * followed by address. */ { read_ptr++; /* printf("Encryption started by telnet client\n"); */ if (tnssn) { tnssn->encr_state = 1; if (GlobalConf->encrypted.alert) { /* Alert on encrypted channel */ telnet_eo_event_log(tnssn, TELNET_EO_ENCRYPTED, NULL, NULL); } if (!GlobalConf->check_encrypted_data) { /* Mark this session & packet as one to ignore */ _dpd.sessionAPI->stop_inspection(p->stream_session, p, SSN_DIR_BOTH, -1, 0); /* No point to do further normalization */ return FTPP_ALERT; } } } break; } break; } /* find the end of the subneg -- this handles when there are * embedded IAC IACs within a sub negotiation. Just looking * for the TNC_SE could cause problems. Similarly, just looking * for the TNC_IAC could end it too early. */ while(read_ptr < end) { if ((*read_ptr == (unsigned char) TNC_IAC) && (*(read_ptr+1) == (unsigned char) TNC_SE)) { sb_start = NULL; break; } read_ptr++; } if (sb_start) { /* Didn't find the IAC SE. Normalize out the IAC SB * and restart from there. Presumption is this is * just someone trying to fool us, since we usually * see the entire IAC SB ... IAC SE in one packet. */ read_ptr = sb_start+2; if (!tnssn) { /* Its an FTP session */ ret = FTPP_ALERT; } else if (GlobalConf->telnet_config->detect_anomalies) { /* Alert on SB without SE */ telnet_eo_event_log(tnssn, TELNET_EO_SB_NO_SE, NULL, NULL); ret = FTPP_ALERT; } continue; } /* Okay, found the IAC SE -- move past it */ if (read_ptr < end) { read_ptr += 2; } if (tnssn && iMode == FTPP_SI_CLIENT_MODE) tnssn->consec_ayt = 0; } else { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "overwriting %2X(%c) with %2X(%c)\n", (unsigned char)(*write_ptr&0xFF), *write_ptr, (unsigned char)(*read_ptr & 0xFF), *read_ptr);); /* overwrite the negotiation bytes with the follow-on bytes */ switch(* ((unsigned char *)(read_ptr))) { case 0x7F: /* Delete */ case 0x08: /* Backspace/Ctrl-H */ /* wind it back a character */ if (write_ptr > start) { write_ptr--; } read_ptr++; break; default: *write_ptr++ = *read_ptr++; break; } if (tnssn && iMode == FTPP_SI_CLIENT_MODE) tnssn->consec_ayt = 0; } } _dpd.SetAltDecode((uint16_t)(write_ptr - start)); /* DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Converted buffer after telnet normalization:\n"); PrintNetData(stdout, (char *) _dpd.altBuffer->data, _dpd.altBuffer->len);); */ return ret; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/spp_ftptelnet.h0000644000175000017500000000327714241076011023265 0ustar apoapo/* * spp_ftptelnet.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file defines the publicly available functions for the FTPTelnet * functionality for Snort. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __SPP_FTPTELNET_H__ #define __SPP_FTPTELNET_H__ typedef struct _FTPTelnet_Stats { uint64_t ftp_sessions; //Current sessions uint64_t max_ftp_sessions; //Max cuncurrent sessions uint64_t telnet_sessions;//Current sessions uint64_t max_telnet_sessions; //Max cuncurrent sessions uint64_t ftp_data_sessions; uint64_t max_ftp_data_sessions; uint64_t heap_memory; } FTPTelnet_Stats; extern FTPTelnet_Stats ftp_telnet_stats; void SetupFTPTelnet(void); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_si.c0000644000175000017500000011113714241075760022042 0ustar apoapo/* * ftpp_si.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains functions to select server configurations * and begin the FTPTelnet process. * * The Session Inspection Module interfaces with the Stream Inspection * Module and the User Interface Module to select the appropriate * FTPTelnet configuration and in the case of stateful inspection the * Session Inspection Module retrieves the user-data from the Stream * Module. For stateless inspection, the Session Inspection Module uses * the same structure for use by each packet. * * The main responsibility of this module is to supply the appropriate * data structures and configurations for the rest of the FTPTelnet * process. The module also determines what type of data is being * inspected, whether it is client, server, or neither. * * NOTES: * - 20.09.04: Initial Development. SAS * */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "ftpp_return_codes.h" #include "ftpp_ui_config.h" #include "ftpp_ui_client_lookup.h" #include "ftpp_ui_server_lookup.h" #include "ftpp_si.h" #include "spp_ftptelnet.h" #include "stream_api.h" #include "snort_ftptelnet.h" #include "sfPolicyUserData.h" #include "ssl_include.h" #include "memory_stats.h" #ifndef WIN32 # include #endif extern tSfPolicyUserContextId ftp_telnet_config; static FTP_SESSION StaticSession; /* * Function: PortMatch(PROTO_CONF *Conf, unsigned short port) * * Purpose: Given a configuration and a port number, we decide if * the port is in the port list. * * Arguments: PROTO_CONF => pointer to the client or server configuration * port => the port number to check for * * Returns: int => 0 indicates the port is not a client/server port. * 1 indicates the port is one of the client/server ports. * */ static int PortMatch(PROTO_CONF *Conf, unsigned short port) { if(Conf->ports[port]) { return 1; } return 0; } /* * Function: TelnetFreeSession(void *preproc_session) * * Purpose: This function frees the data that is associated with a session. * * Arguments: preproc_session => pointer to the session to free * * Returns: None */ static void TelnetFreeSession(void *preproc_session) { TELNET_SESSION *ssn = (TELNET_SESSION *)preproc_session; FTPTELNET_GLOBAL_CONF *pPolicyConfig = NULL; if (ssn == NULL) return; pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(ssn->global_conf, ssn->policy_id); if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (ssn->global_conf != ftp_telnet_config)) { sfPolicyUserDataClear (ssn->global_conf, ssn->policy_id); FTPTelnetFreeConfig(pPolicyConfig); if (sfPolicyUserPolicyGetActive(ssn->global_conf) == 0) FTPTelnetFreeConfigs(ssn->global_conf); } } ftp_telnet_stats.telnet_sessions--; ftp_telnet_stats.heap_memory -= sizeof(TELNET_SESSION); _dpd.snortFree(ssn, sizeof(TELNET_SESSION), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); } /* * Function: TelnetResetSession(TELNET_SESSION *Session) * * Purpose: This function resets all the variables that need to be * initialized for a new Session. I've tried to keep this to * a minimum, so we don't have to worry about initializing big * structures. * * Arguments: Session => pointer to the session to reset * * Returns: int => return code indicating error or success * */ static inline int TelnetResetSession(TELNET_SESSION *Session) { Session->ft_ssn.proto = FTPP_SI_PROTO_TELNET; Session->telnet_conf = NULL; Session->global_conf = NULL; Session->consec_ayt = 0; Session->encr_state = NO_STATE; Session->event_list.stack_count = 0; return FTPP_SUCCESS; } /* * Function: TelnetStatefulSessionInspection(Packet *p, * FTPTELNET_GLOBAL_CONF *GlobalConf, * TELNET_SESSION **TelnetSession, * FTPP_SI_INPUT *SiInput) * * Purpose: Initialize the session and server configurations for * this packet/stream. In this function, we set the Session * pointer (which includes the correct server configuration). * The actual processing to find which IP is the server and * which is the client, is done in the InitServerConf() function. * * Arguments: p => pointer to the packet/stream * GlobalConf => pointer to the global configuration * Session => double pointer to the Session structure * SiInput => pointer to the session information * * Returns: int => return code indicating error or success * */ static int TelnetStatefulSessionInspection(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, TELNET_SESSION **TelnetSession, FTPP_SI_INPUT *SiInput) { if (p->stream_session) { TELNET_SESSION *NewSession = (TELNET_SESSION *)_dpd.snortAlloc(1, sizeof(TELNET_SESSION), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); ftp_telnet_stats.telnet_sessions++; if (ftp_telnet_stats.telnet_sessions > ftp_telnet_stats.max_telnet_sessions) ftp_telnet_stats.max_telnet_sessions = ftp_telnet_stats.telnet_sessions; ftp_telnet_stats.heap_memory += sizeof(TELNET_SESSION); tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); if (NewSession == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory for " "new Telnet session.\n"); } TelnetResetSession(NewSession); NewSession->ft_ssn.proto = FTPP_SI_PROTO_TELNET; NewSession->telnet_conf = GlobalConf->telnet_config; NewSession->global_conf = ftp_telnet_config; NewSession->policy_id = policy_id; GlobalConf->ref_count++; SiInput->pproto = FTPP_SI_PROTO_TELNET; _dpd.sessionAPI->set_application_data(p->stream_session, PP_FTPTELNET, NewSession, &TelnetFreeSession); *TelnetSession = NewSession; return FTPP_SUCCESS; } return FTPP_NONFATAL_ERR; } /* * Function: TelnetStatelessSessionInspection(Packet *p, * FTPTELNET_GLOBAL_CONF *GlobalConf, * TELNET_SESSION **TelnetSession, * FTPP_SI_INPUT *SiInput) * * Purpose: Initialize the session and server configurations for this * packet/stream. It is important to note in stateless mode that * we assume no knowledge of the state of a connection, other * than the knowledge that we can glean from an individual packet. * So in essence, each packet is it's own session and there * is no knowledge retained from one packet to another. If you * want to track a telnet session for real, use stateful mode. * * In this function, we set the Session pointer (which includes * the correct server configuration). The actual processing to * find which IP is the server and which is the client, is done in * the InitServerConf() function. * * Arguments: p => pointer to the packet/stream * GlobalConf => pointer to the global configuration * Session => double pointer to the Session structure * SiInput => pointer to the session information * * Returns: int => return code indicating error or success * */ static int TelnetStatelessSessionInspection(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, TELNET_SESSION **Session, FTPP_SI_INPUT *SiInput) { static TELNET_SESSION TelnetStaticSession; TelnetResetSession(&TelnetStaticSession); SiInput->pproto = FTPP_SI_PROTO_TELNET; TelnetStaticSession.telnet_conf = GlobalConf->telnet_config; TelnetStaticSession.global_conf = ftp_telnet_config; *Session = &TelnetStaticSession; return FTPP_SUCCESS; } /* * Function: TelnetSessionInspection(Packet *p, * FTPTELNET_GLOBAL_CONF *GlobalConf, * FTPP_SI_INPUT *SiInput, * int *piInspectMode) * * Purpose: The Session Inspection module selects the appropriate * configuration for the session, and the type of inspection * to be performed (client or server.) * * When the Session Inspection module is in stateful mode, it * checks to see if there is a TELNET_SESSION pointer already * associated with the stream. If there is, then it uses that * session pointer, otherwise it calculates the server configuration * using the FTP_SI_INPUT and returns a TELNET_SESSION pointer. In * stateful mode, this means that memory is allocated, but in * stateless mode, the same session pointer is used for all packets * to reduce the allocation overhead. * * The inspection mode can be either client or server. * * Arguments: p => pointer to the packet/stream * GlobalConf => pointer to the global configuration * Session => double pointer to the Session structure * SiInput => pointer to the session information * piInspectMode => pointer for setting inspection mode * * Returns: int => return code indicating error or success * */ int TelnetSessionInspection(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, TELNET_SESSION **TelnetSession, FTPP_SI_INPUT *SiInput, int *piInspectMode) { int iRet; int iTelnetSip; int iTelnetDip; #ifdef TARGET_BASED int16_t app_id = SFTARGET_UNKNOWN_PROTOCOL; /* If possible, use Stream API to determine protocol. */ if (_dpd.streamAPI) { app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); } if (app_id == SFTARGET_UNKNOWN_PROTOCOL) { return FTPP_INVALID_PROTO; } if (app_id == telnet_app_id) { if (SiInput->pdir == FTPP_SI_CLIENT_MODE || SiInput->pdir == FTPP_SI_SERVER_MODE) { *piInspectMode = (int)SiInput->pdir; } } else if (app_id && app_id != telnet_app_id) { return FTPP_INVALID_PROTO; } else { #endif iTelnetSip = PortMatch((PROTO_CONF*)GlobalConf->telnet_config, SiInput->sport); iTelnetDip = PortMatch((PROTO_CONF*)GlobalConf->telnet_config, SiInput->dport); if (iTelnetSip) { *piInspectMode = FTPP_SI_SERVER_MODE; } else if (iTelnetDip) { *piInspectMode = FTPP_SI_CLIENT_MODE; } else { return FTPP_INVALID_PROTO; } #ifdef TARGET_BASED } #endif /* * We get the server configuration and the session structure differently * depending on what type of inspection we are doing. In the case of * stateful processing, we may get the session structure from the Stream * Reassembly module (which includes the server configuration) or the * structure will be allocated and added to the stream pointer for the * rest of the session. * * In stateless mode, we just use a static variable that is contained in * the function here. */ if(GlobalConf->inspection_type == FTPP_UI_CONFIG_STATEFUL) { iRet = TelnetStatefulSessionInspection(p, GlobalConf, TelnetSession, SiInput); if (iRet) return iRet; } else { /* Assume stateless processing otherwise */ iRet = TelnetStatelessSessionInspection(p, GlobalConf, TelnetSession, SiInput); if (iRet) return iRet; } return FTPP_SUCCESS; } /* * Function: FTPGetPacketDir(Packet *p) * * Purpose: Attempts to determine the direction of an FTP packet by * examining the first 3 bytes. If all three are numeric, * the packet is a server response packet. * * Arguments: p => pointer to the Packet * * Returns: int => return code indicating the mode * */ int FTPGetPacketDir(SFSnortPacket *p) { if (p->payload_size >= 3) { if (isdigit(p->payload[0]) && isdigit(p->payload[1]) && isdigit(p->payload[2]) ) { return FTPP_SI_SERVER_MODE; } else { return FTPP_SI_CLIENT_MODE; } } return FTPP_SI_NO_MODE; } /* * Function: FTPInitConf(Packet *p, FTPTELNET_GLOBAL_CONF *GlobalConf, * FTP_CLIENT_PROTO_CONF **ClientConf, * FTP_SERVER_PROTO_CONF **ServerConf, * FTPP_SI_INPUT *SiInput, int *piInspectMode) * * Purpose: When a session is initialized, we must select the appropriate * server configuration and select the type of inspection based * on the source and destination ports. * * IMPORTANT NOTE: * We should check to make sure that there are some unique configurations, * otherwise we can just default to the global default and work some magic * that way. * * Arguments: p => pointer to the Packet/Session * GlobalConf => pointer to the global configuration * ClientConf => pointer to the address of the client * config so we can set it. * ServerConf => pointer to the address of the server * config so we can set it. * SiInput => pointer to the packet info * piInspectMode => pointer so we can set the inspection mode * * Returns: int => return code indicating error or success * */ static int FTPInitConf(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_CLIENT_PROTO_CONF **ClientConf, FTP_SERVER_PROTO_CONF **ServerConf, FTPP_SI_INPUT *SiInput, int *piInspectMode) { FTP_CLIENT_PROTO_CONF *ClientConfSip; FTP_CLIENT_PROTO_CONF *ClientConfDip; FTP_SERVER_PROTO_CONF *ServerConfSip; FTP_SERVER_PROTO_CONF *ServerConfDip; int iServerSip; int iServerDip; int iErr = 0; int iRet = FTPP_SUCCESS; #ifdef TARGET_BASED int16_t app_id = 0; #endif sfaddr_t sip; sfaddr_t dip; //structure copy sip = SiInput->sip; dip = SiInput->dip; /* * We find the client configurations for both the source and dest IPs. * There should be a check on the global configuration to see if there * is at least one unique client configuration. If there isn't then we * assume the global client configuration. */ ClientConfDip = ftpp_ui_client_lookup_find(GlobalConf->client_lookup, &dip, &iErr); if(!ClientConfDip) { ClientConfDip = GlobalConf->default_ftp_client; } ClientConfSip = ftpp_ui_client_lookup_find(GlobalConf->client_lookup, &sip, &iErr); if(!ClientConfSip) { ClientConfSip = GlobalConf->default_ftp_client; } /* * Now, we find the server configurations for both the source and dest IPs. * There should be a check on the global configuration to see if there * is at least one unique client configuration. If there isn't then we * assume the global client configuration. */ ServerConfDip = ftpp_ui_server_lookup_find(GlobalConf->server_lookup, &dip, &iErr); if(!ServerConfDip) { ServerConfDip = GlobalConf->default_ftp_server; } ServerConfSip = ftpp_ui_server_lookup_find(GlobalConf->server_lookup, &sip, &iErr); if(!ServerConfSip) { ServerConfSip = GlobalConf->default_ftp_server; } /* * We check the IP and the port to see if the FTP client is talking in * the session. This should tell us whether it is client communication * or server configuration. If both IPs and ports are servers, then there * is a sort of problem. We don't know which side is the client and which * side is the server so we have to assume one. * * In stateful processing, we only do this stage on the startup of a * session, so we can still assume that the initial packet is the client * talking. */ iServerDip = PortMatch((PROTO_CONF*)ServerConfDip, SiInput->dport); iServerSip = PortMatch((PROTO_CONF*)ServerConfSip, SiInput->sport); /* * We default to the no FTP traffic case */ *piInspectMode = FTPP_SI_NO_MODE; *ClientConf = NULL; *ServerConf = NULL; /* * Depending on the type of packet direction we get from the * state machine, we evaluate client/server differently. */ switch(SiInput->pdir) { case FTPP_SI_NO_MODE: #ifdef TARGET_BASED app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if (app_id == ftp_app_id || app_id == 0) { #endif /* * We check for the case where both SIP and DIP * appear to be servers. In this case, we assume server * and process that way. */ if(iServerSip && iServerDip) { /* * We check for the case where both SIP and DIP * appear to be servers. In this case, we look at * the first few bytes of the packet to try to * determine direction -- 3 digits indicate server * response. */ /* look at the first few bytes of the packet. We might * be wrong if this is a reassembled packet and we catch * a server response mid-stream. */ *piInspectMode = FTPGetPacketDir(p); if (*piInspectMode == FTPP_SI_SERVER_MODE) { /* Packet is from server --> src is Server */ *ClientConf = ClientConfDip; *ServerConf = ServerConfSip; } else /* Assume client */ { /* Packet is from client --> dest is Server */ *piInspectMode = FTPP_SI_CLIENT_MODE; *ClientConf = ClientConfSip; *ServerConf = ServerConfDip; } SiInput->pproto = FTPP_SI_PROTO_FTP; } else if(iServerDip) { /* Packet is from client --> dest is Server */ *piInspectMode = FTPP_SI_CLIENT_MODE; *ClientConf = ClientConfSip; *ServerConf = ServerConfDip; SiInput->pproto = FTPP_SI_PROTO_FTP; } else if(iServerSip) { /* Packet is from server --> src is Server */ *piInspectMode = FTPP_SI_SERVER_MODE; *ClientConf = ClientConfDip; *ServerConf = ServerConfSip; SiInput->pproto = FTPP_SI_PROTO_FTP; } break; #ifdef TARGET_BASED } #endif case FTPP_SI_CLIENT_MODE: /* Packet is from client --> dest is Server */ #ifdef TARGET_BASED app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if ((app_id == ftp_app_id) || (!app_id && iServerDip)) #else if(iServerDip) #endif { *piInspectMode = FTPP_SI_CLIENT_MODE; *ClientConf = ClientConfSip; *ServerConf = ServerConfDip; SiInput->pproto = FTPP_SI_PROTO_FTP; } else { *piInspectMode = FTPP_SI_NO_MODE; iRet = FTPP_NONFATAL_ERR; } break; case FTPP_SI_SERVER_MODE: /* Packet is from server --> src is Server */ #ifdef TARGET_BASED app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if ((app_id == ftp_app_id) || (!app_id && iServerSip)) #else if(iServerSip) #endif { *piInspectMode = FTPP_SI_SERVER_MODE; *ClientConf = ClientConfDip; *ServerConf = ServerConfSip; SiInput->pproto = FTPP_SI_PROTO_FTP; } else { *piInspectMode = FTPP_SI_NO_MODE; iRet = FTPP_NONFATAL_ERR; } break; default: *piInspectMode = FTPP_SI_NO_MODE; *ClientConf = NULL; *ServerConf = NULL; break; } return iRet; } /* * Function: FTPFreeSession(void *preproc_session) * * Purpose: This function frees the data that is associated with a session. * * Arguments: preproc_session => pointer to the session to free * * Returns: None */ static void FTPFreeSession(void *preproc_session) { FTP_SESSION *ssn = (FTP_SESSION *)preproc_session; FTPTELNET_GLOBAL_CONF *pPolicyConfig = NULL; ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if (ssn == NULL) return; pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(ssn->global_conf, ssn->policy_id); if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (ssn->global_conf != ftp_telnet_config)) { sfPolicyUserDataClear (ssn->global_conf, ssn->policy_id); FTPTelnetFreeConfig(pPolicyConfig); if (sfPolicyUserPolicyGetActive(ssn->global_conf) == 0) FTPTelnetFreeConfigs(ssn->global_conf); } } if (ssn->filename) { ftp_telnet_stats.heap_memory -= (strlen(ssn->filename) + 1); _dpd.snortFree(ssn->filename, (strlen(ssn->filename) + 1), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); } if ( ssl_cb ) ssl_cb->session_free(ssn->flow_id); ftp_telnet_stats.ftp_sessions--; ftp_telnet_stats.heap_memory -= sizeof(FTP_SESSION); #ifdef TARGET_BASED FTP_DATA_SESSION *datassn = ssn->datassn; if(datassn && (ssn == datassn->ftpssn)) datassn->ftpssn = NULL; _dpd.snortFree(ssn, sizeof(FTP_SESSION), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); #endif } #ifdef TARGET_BASED /* Function: FTPDataSessionNew * * Create an ftp-data session from a packet */ FTP_DATA_SESSION * FTPDataSessionNew(SFSnortPacket *p) { FTP_DATA_SESSION *ftpdata = _dpd.snortAlloc(1, sizeof *ftpdata, PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); if (!ftpdata) return NULL; ftpdata->ft_ssn.proto = FTPP_SI_PROTO_FTP_DATA; ftpdata->flow_id = 0; /* Get the ftp-ctrl session key */ ftpdata->ftp_key = _dpd.sessionAPI->get_session_key(p); if (!ftpdata->ftp_key) { _dpd.snortFree(ftpdata, sizeof *ftpdata, PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); ftpdata = NULL; return ftpdata; } ftp_telnet_stats.ftp_data_sessions++; if (ftp_telnet_stats.ftp_data_sessions > ftp_telnet_stats.max_ftp_data_sessions) ftp_telnet_stats.max_ftp_data_sessions = ftp_telnet_stats.ftp_data_sessions; ftp_telnet_stats.heap_memory += (sizeof (*ftpdata) + sizeof(StreamSessionKey)); return ftpdata; } /* * Function: FTPDataSessionFree * * Free an ftp-data session */ void FTPDataSessionFree(void *p_ssn) { FTP_DATA_SESSION *ssn = (FTP_DATA_SESSION *)p_ssn; ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if (!ssn) return; FTP_SESSION * ftpssn = ssn->ftpssn; if(ftpssn && (ssn == ftpssn->datassn)) ftpssn->datassn = NULL; /* ftp-data key shouldn't exist without this but */ if (ssn->ftp_key) { // This Key is accounted in Stream during alloc _dpd.snortFree(ssn->ftp_key, sizeof(StreamSessionKey), PP_STREAM, PP_MEM_CATEGORY_SESSION); } if (ssn->filename) { ftp_telnet_stats.heap_memory -= (strlen(ssn->filename) + 1); _dpd.snortFree(ssn->filename, (strlen(ssn->filename) + 1), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); } if ( ssl_cb ) ssl_cb->session_free(ssn->flow_id); ftp_telnet_stats.ftp_data_sessions--; ftp_telnet_stats.heap_memory -= sizeof(FTP_DATA_SESSION); _dpd.snortFree(ssn, sizeof(FTP_DATA_SESSION), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); } /* Function: FTPDataDirection * * Return true if packet is from the "sending" host * Return false if packet is from the "receiving" host */ bool FTPDataDirection(SFSnortPacket *p, FTP_DATA_SESSION *ftpdata) { uint32_t direction; uint32_t pktdir = _dpd.sessionAPI->get_packet_direction(p); if (ftpdata->mode == FTPP_XFER_ACTIVE) direction = ftpdata->direction ? FLAG_FROM_SERVER : FLAG_FROM_CLIENT; else direction = ftpdata->direction ? FLAG_FROM_CLIENT : FLAG_FROM_SERVER; return (pktdir == direction); } #endif /* TARGET_BASED */ /* * Function: FTPResetSession(FTP_SESSION *FtpSession, int first) * * Purpose: This function resets all the variables that need to be * initialized for a new Session. I've tried to keep this to * a minimum, so we don't have to worry about initializing big * structures. * * Arguments: FtpSession => pointer to the session to reset * first => indicator whether this is a new conf * * Returns: int => return code indicating error or success * */ static inline int FTPResetSession(FTP_SESSION *FtpSession) { FtpSession->ft_ssn.proto = FTPP_SI_PROTO_FTP; FtpSession->server.response.pipeline_req = 0; FtpSession->server.response.state = 0; FtpSession->client.request.pipeline_req = 0; FtpSession->client.state = 0; FtpSession->client_conf = NULL; FtpSession->server_conf = NULL; FtpSession->global_conf = NULL; FtpSession->encr_state = NO_STATE; FtpSession->encr_state_chello = false; FtpSession->flow_id = 0; IP_CLEAR(FtpSession->clientIP); FtpSession->clientPort = 0; IP_CLEAR(FtpSession->serverIP); FtpSession->serverPort = 0; FtpSession->data_chan_state = NO_STATE; FtpSession->data_chan_index = 0; FtpSession->data_xfer_index = 0; FtpSession->ftp_cmd_pipe_index = 1; FtpSession->rest_cmd_offset = 0; FtpSession->event_list.stack_count = 0; return FTPP_SUCCESS; } /* * Function: FTPStatefulSessionInspection(Packet *p, * FTPTELNET_GLOBAL_CONF *GlobalConf, * FTP_SESSION **FtpSession, * FTPP_SI_INPUT *SiInput, int *piInspectMode) * * Purpose: Initialize the session and server configurations for this * packet/stream. In this function, we set the Session pointer * (which includes the correct server configuration). The actual * processing to find which IP is the server and which is the * client, is done in the InitServerConf() function. * * Arguments: p => pointer to the Packet/Session * GlobalConf => pointer to the global configuration * Session => double pointer to the Session structure * SiInput => pointer to the session information * piInspectMode => pointer so the inspection mode can be set * * Returns: int => return code indicating error or success * */ static int FTPStatefulSessionInspection(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_SESSION **FtpSession, FTPP_SI_INPUT *SiInput, int *piInspectMode) { if (p->stream_session) { FTP_CLIENT_PROTO_CONF *ClientConf; FTP_SERVER_PROTO_CONF *ServerConf; int iRet; iRet = FTPInitConf(p, GlobalConf, &ClientConf, &ServerConf, SiInput, piInspectMode); if (iRet) return iRet; if (*piInspectMode) { FTP_SESSION *NewSession = (FTP_SESSION *)_dpd.snortAlloc(1, sizeof(FTP_SESSION), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); ftp_telnet_stats.ftp_sessions++; if (ftp_telnet_stats.ftp_sessions > ftp_telnet_stats.max_ftp_sessions) ftp_telnet_stats.max_ftp_sessions = ftp_telnet_stats.ftp_sessions; ftp_telnet_stats.heap_memory += sizeof(FTP_SESSION); tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); if (NewSession == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate memory for " "new FTP session.\n"); } FTPResetSession(NewSession); NewSession->ft_ssn.proto = FTPP_SI_PROTO_FTP; NewSession->client_conf = ClientConf; NewSession->server_conf = ServerConf; NewSession->global_conf = ftp_telnet_config; NewSession->policy_id = policy_id; GlobalConf->ref_count++; _dpd.sessionAPI->set_application_data (p->stream_session, PP_FTPTELNET, NewSession, &FTPFreeSession); *FtpSession = NewSession; SiInput->pproto = FTPP_SI_PROTO_FTP; return FTPP_SUCCESS; } } return FTPP_INVALID_PROTO; } /* * Function: FTPStatelessSessionInspection(Packet *p, * FTPTELNET_GLOBAL_CONF *GlobalConf, * FTP_SESSION **FtpSession, * FTPP_SI_INPUT *SiInput, int *piInspectMode) * * Purpose: Initialize the session and server configurations for this * packet/stream. It is important to note in stateless mode that * we assume no knowledge of the state of a connection, other than * the knowledge that we can glean from an individual packet. So * in essence, each packet is it's own session and there is no * knowledge retained from one packet to another. If you want to * track an FTP session for real, use stateful mode. * * In this function, we set the Session pointer (which includes * the correct server configuration). The actual processing to find * which IP is the server and which is the client, is done in the * InitServerConf() function. * * Arguments: p => pointer to the Packet/Session * GlobalConf => pointer to the global configuration * Session => double pointer to the Session structure * SiInput => pointer to the session information * piInspectMode => pointer so the inspection mode can be set * * Returns: int => return code indicating error or success * */ static int FTPStatelessSessionInspection(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_SESSION **FtpSession, FTPP_SI_INPUT *SiInput, int *piInspectMode) { FTP_CLIENT_PROTO_CONF *ClientConf; FTP_SERVER_PROTO_CONF *ServerConf; int iRet; FTPResetSession(&StaticSession); iRet = FTPInitConf(p, GlobalConf, &ClientConf, &ServerConf, SiInput, piInspectMode); if (iRet) return iRet; StaticSession.ft_ssn.proto = FTPP_SI_PROTO_FTP; StaticSession.global_conf = ftp_telnet_config; StaticSession.client_conf = ClientConf; StaticSession.server_conf = ServerConf; SiInput->pproto = FTPP_SI_PROTO_FTP; *FtpSession = &StaticSession; return FTPP_SUCCESS; } /* * Function: FTPSessionInspection(Packet *p, * FTPTELNET_GLOBAL_CONF *GlobalConf, * FTPP_SI_INPUT *SiInput, int *piInspectMode) * * Purpose: The Session Inspection module selects the appropriate client * configuration for the session, and the type of inspection to * be performed (client or server.) * * When the Session Inspection module is in stateful mode, it * checks to see if there is a FTP_SESSION pointer already * associated with the stream. If there is, then it uses that * session pointer, otherwise it calculates the server * configuration using the FTP_SI_INPUT and returns a FTP_SESSION * pointer. In stateful mode, this means that memory is allocated, * but in stateless mode, the same session pointer is used for all * packets to reduce the allocation overhead. * * The inspection mode can be either client or server. * * Arguments: p => pointer to the Packet/Session * GlobalConf => pointer to the global configuration * SiInput => pointer to the session information * piInspectMode => pointer so the inspection mode can be set * * Returns: int => return code indicating error or success * */ int FTPSessionInspection(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_SESSION **FtpSession, FTPP_SI_INPUT *SiInput, int *piInspectMode) { int iRet; /* * We get the server configuration and the session structure differently * depending on what type of inspection we are doing. In the case of * stateful processing, we may get the session structure from the Stream * Reassembly module (which includes the server configuration) or the * structure will be allocated and added to the stream pointer for the * rest of the session. * * In stateless mode, we just use a static variable that is contained in * the function here. */ if(GlobalConf->inspection_type == FTPP_UI_CONFIG_STATEFUL) { iRet = FTPStatefulSessionInspection(p, GlobalConf, FtpSession, SiInput, piInspectMode); if (iRet) return iRet; } else { /* Assume stateless processing otherwise */ iRet = FTPStatelessSessionInspection(p, GlobalConf, FtpSession, SiInput, piInspectMode); if (iRet) return iRet; } return FTPP_SUCCESS; } /* * Function: ftpp_si_determine_proto(Packet *p, * FTPTELNET_GLOBAL_CONF *GlobalConf, * FTPP_SI_INPUT *SiInput, int *piInspectMode) * * Purpose: The Protocol Determination module determines whether this is * an FTP or telnet request. If this is an FTP request, it sets * the FTP Session data and inspection mode. * * The inspection mode can be either client or server. * * Arguments: p => pointer to the Packet/Session * GlobalConf => pointer to the global configuration * SiInput => pointer to the session information * piInspectMode => pointer so the inspection mode can be set * * Returns: int => return code indicating error or success * */ int ftpp_si_determine_proto(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_TELNET_SESSION **ft_ssn, FTPP_SI_INPUT *SiInput, int *piInspectMode) { /* Default to no FTP or Telnet case */ SiInput->pproto = FTPP_SI_PROTO_UNKNOWN; *piInspectMode = FTPP_SI_NO_MODE; TelnetSessionInspection(p, GlobalConf, (TELNET_SESSION **)ft_ssn, SiInput, piInspectMode); if (SiInput->pproto == FTPP_SI_PROTO_TELNET) return FTPP_SUCCESS; FTPSessionInspection(p, GlobalConf, (FTP_SESSION **)ft_ssn, SiInput, piInspectMode); if (SiInput->pproto == FTPP_SI_PROTO_FTP) return FTPP_SUCCESS; return FTPP_INVALID_PROTO; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/sf_ftptelnet.dsp0000444000175000017500000001770214230012554023426 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_ftptelnet" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_ftptelnet - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_ftptelnet.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_ftptelnet.mak" CFG="sf_ftptelnet - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_ftptelnet - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_ftptelnet - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_ftptelnet - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 ws2_32.lib /nologo /dll /machine:I386 !ELSEIF "$(CFG)" == "sf_ftptelnet - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_SMTP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 ws2_32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept !ENDIF # Begin Target # Name "sf_ftptelnet - Win32 Release" # Name "sf_ftptelnet - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\ssl_common\ssl.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_ha.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_inspect.c # End Source File # Begin Source File SOURCE=.\ftp_bounce_lookup.c # End Source File # Begin Source File SOURCE=.\ftp_cmd_lookup.c # End Source File # Begin Source File SOURCE=.\ftpp_eo_log.c # End Source File # Begin Source File SOURCE=.\ftpp_si.c # End Source File # Begin Source File SOURCE=.\ftpp_ui_client_lookup.c # End Source File # Begin Source File SOURCE=.\ftpp_ui_config.c # End Source File # Begin Source File SOURCE=.\ftpp_ui_server_lookup.c # End Source File # Begin Source File SOURCE=.\hi_util_kmap.c # End Source File # Begin Source File SOURCE=.\hi_util_xmalloc.c # End Source File # Begin Source File SOURCE=..\include\inet_aton.c # End Source File # Begin Source File SOURCE=..\include\inet_pton.c # End Source File # Begin Source File SOURCE=.\pp_ftp.c # End Source File # Begin Source File SOURCE=.\pp_telnet.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_ip.c # End Source File # Begin Source File SOURCE=..\libs\sfparser.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=..\include\sfrt.c # End Source File # Begin Source File SOURCE=..\include\sfrt_dir.c # End Source File # Begin Source File SOURCE=.\snort_ftptelnet.c # End Source File # Begin Source File SOURCE=.\spp_ftptelnet.c # End Source File # Begin Source File SOURCE=..\include\strtok_r.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\ssl_common\ssl.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_include.h # End Source File # Begin Source File SOURCE=.\ftp_bounce_lookup.h # End Source File # Begin Source File SOURCE=.\ftp_client.h # End Source File # Begin Source File SOURCE=.\ftp_cmd_lookup.h # End Source File # Begin Source File SOURCE=.\ftp_server.h # End Source File # Begin Source File SOURCE=.\ftpp_eo.h # End Source File # Begin Source File SOURCE=.\ftpp_eo_events.h # End Source File # Begin Source File SOURCE=.\ftpp_eo_log.h # End Source File # Begin Source File SOURCE=.\ftpp_include.h # End Source File # Begin Source File SOURCE=.\ftpp_return_codes.h # End Source File # Begin Source File SOURCE=.\ftpp_si.h # End Source File # Begin Source File SOURCE=.\ftpp_ui_client_lookup.h # End Source File # Begin Source File SOURCE=.\ftpp_ui_config.h # End Source File # Begin Source File SOURCE=.\ftpp_ui_server_lookup.h # End Source File # Begin Source File SOURCE=.\ftpp_util_kmap.h # End Source File # Begin Source File SOURCE=.\hi_util_kmap.h # End Source File # Begin Source File SOURCE=.\hi_util_xmalloc.h # End Source File # Begin Source File SOURCE=.\pp_ftp.h # End Source File # Begin Source File SOURCE=.\pp_telnet.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=.\snort_ftptelnet.h # End Source File # Begin Source File SOURCE=.\spp_ftptelnet.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/ftptelnet/Makefile.in0000644000175000017500000006373714242725546022320 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@ftptelnet_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@ftptelnet_buffer_dump.h subdir = src/dynamic-preprocessors/ftptelnet ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_ftptelnet_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_utils.la am__libsf_ftptelnet_preproc_la_SOURCES_DIST = ftp_bounce_lookup.c \ ftp_bounce_lookup.h ftp_client.h ftp_cmd_lookup.c \ ftp_cmd_lookup.h ftpp_eo_events.h ftpp_eo.h ftpp_eo_log.c \ ftpp_eo_log.h ftpp_include.h ftpp_return_codes.h ftpp_si.c \ ftpp_si.h ftpp_ui_client_lookup.c ftpp_ui_client_lookup.h \ ftpp_ui_config.c ftpp_ui_config.h ftpp_ui_server_lookup.c \ ftpp_ui_server_lookup.h ftp_server.h hi_util_kmap.c \ hi_util_kmap.h hi_util_xmalloc.c hi_util_xmalloc.h pp_ftp.c \ pp_ftp.h pp_telnet.c pp_telnet.h snort_ftptelnet.c \ snort_ftptelnet.h spp_ftptelnet.c spp_ftptelnet.h \ ftptelnet_buffer_dump.c ftptelnet_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = ftptelnet_buffer_dump.lo am_libsf_ftptelnet_preproc_la_OBJECTS = ftp_bounce_lookup.lo \ ftp_cmd_lookup.lo ftpp_eo_log.lo ftpp_si.lo \ ftpp_ui_client_lookup.lo ftpp_ui_config.lo \ ftpp_ui_server_lookup.lo hi_util_kmap.lo hi_util_xmalloc.lo \ pp_ftp.lo pp_telnet.lo snort_ftptelnet.lo spp_ftptelnet.lo \ $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_ftptelnet_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo sf_ip.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfrt.lo sfrt_dir.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo sfmemcap.lo \ @SO_WITH_STATIC_LIB_FALSE@ ssl.lo ssl_config.lo ssl_inspect.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfparser.lo libsf_ftptelnet_preproc_la_OBJECTS = \ $(am_libsf_ftptelnet_preproc_la_OBJECTS) \ $(nodist_libsf_ftptelnet_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_ftptelnet_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_ftptelnet_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_ftptelnet_preproc_la_SOURCES) \ $(nodist_libsf_ftptelnet_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_ftptelnet_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs -I${srcdir}/includes INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_ftptelnet_preproc.la libsf_ftptelnet_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_ftptelnet_preproc_la_LIBADD = ../libsf_dynamic_preproc.la ../libsf_dynamic_utils.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_ftptelnet_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_ip.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfrt.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfrt_dir.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfmemcap.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_config.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_inspect.c \ @SO_WITH_STATIC_LIB_FALSE@../libs/sfparser.c libsf_ftptelnet_preproc_la_SOURCES = ftp_bounce_lookup.c \ ftp_bounce_lookup.h ftp_client.h ftp_cmd_lookup.c \ ftp_cmd_lookup.h ftpp_eo_events.h ftpp_eo.h ftpp_eo_log.c \ ftpp_eo_log.h ftpp_include.h ftpp_return_codes.h ftpp_si.c \ ftpp_si.h ftpp_ui_client_lookup.c ftpp_ui_client_lookup.h \ ftpp_ui_config.c ftpp_ui_config.h ftpp_ui_server_lookup.c \ ftpp_ui_server_lookup.h ftp_server.h hi_util_kmap.c \ hi_util_kmap.h hi_util_xmalloc.c hi_util_xmalloc.h pp_ftp.c \ pp_ftp.h pp_telnet.c pp_telnet.h snort_ftptelnet.c \ snort_ftptelnet.h spp_ftptelnet.c spp_ftptelnet.h \ $(am__append_1) EXTRA_DIST = \ sf_ftptelnet.vcxproj \ sf_ftptelnet.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/ftptelnet/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/ftptelnet/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_ftptelnet_preproc.la: $(libsf_ftptelnet_preproc_la_OBJECTS) $(libsf_ftptelnet_preproc_la_DEPENDENCIES) $(EXTRA_libsf_ftptelnet_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_ftptelnet_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_ftptelnet_preproc_la_OBJECTS) $(libsf_ftptelnet_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c sf_ip.lo: ../include/sf_ip.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_ip.lo `test -f '../include/sf_ip.c' || echo '$(srcdir)/'`../include/sf_ip.c sfrt.lo: ../include/sfrt.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt.lo `test -f '../include/sfrt.c' || echo '$(srcdir)/'`../include/sfrt.c sfrt_dir.lo: ../include/sfrt_dir.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfrt_dir.lo `test -f '../include/sfrt_dir.c' || echo '$(srcdir)/'`../include/sfrt_dir.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c sfmemcap.lo: ../include/sfmemcap.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfmemcap.lo `test -f '../include/sfmemcap.c' || echo '$(srcdir)/'`../include/sfmemcap.c ssl.lo: ../ssl_common/ssl.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl.lo `test -f '../ssl_common/ssl.c' || echo '$(srcdir)/'`../ssl_common/ssl.c ssl_config.lo: ../ssl_common/ssl_config.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_config.lo `test -f '../ssl_common/ssl_config.c' || echo '$(srcdir)/'`../ssl_common/ssl_config.c ssl_inspect.lo: ../ssl_common/ssl_inspect.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_inspect.lo `test -f '../ssl_common/ssl_inspect.c' || echo '$(srcdir)/'`../ssl_common/ssl_inspect.c sfparser.lo: ../libs/sfparser.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfparser.lo `test -f '../libs/sfparser.c' || echo '$(srcdir)/'`../libs/sfparser.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftp_cmd_lookup.h0000644000175000017500000000341114241075746023407 0ustar apoapo/* * ftp_cmd_lookup.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains function definitions for FTP command lookups. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTP_CMD_LOOKUP_H__ #define __FTP_CMD_LOOKUP_H__ #include "ftpp_include.h" #include "ftpp_ui_config.h" int ftp_cmd_lookup_init(CMD_LOOKUP **CmdLookup); int ftp_cmd_lookup_cleanup(CMD_LOOKUP **CmdLookup); int ftp_cmd_lookup_add(CMD_LOOKUP *CmdLookup, char cmd[], int len, FTP_CMD_CONF *FTPCmd); FTP_CMD_CONF *ftp_cmd_lookup_find(CMD_LOOKUP *CmdLookup, const char cmd[], int len, int *iError); FTP_CMD_CONF *ftp_cmd_lookup_first(CMD_LOOKUP *CmdLookup, int *iError); FTP_CMD_CONF *ftp_cmd_lookup_next(CMD_LOOKUP *CmdLookup, int *iError); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftp_client.h0000644000175000017500000000364214241075744022535 0ustar apoapo/* * ftp_client.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Header file for FTPTelnet FTP Client Module * * This file defines the client reqest structure and functions * to access client inspection. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTP_CLIENT_H__ #define __FTP_CLIENT_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "ftpp_include.h" typedef struct s_FTP_CLIENT_REQ { const char *cmd_line; unsigned int cmd_line_size; const char *cmd_begin; const char *cmd_end; unsigned int cmd_size; const char *param_begin; const char *param_end; unsigned int param_size; const char *pipeline_req; } FTP_CLIENT_REQ; typedef struct s_FTP_CLIENT { FTP_CLIENT_REQ request; int (*state)(void *, unsigned char, int); } FTP_CLIENT; int ftp_client_inspection(void *Session, unsigned char *data, int dsize); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/hi_util_kmap.h0000644000175000017500000000444614241075776023063 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * kmap.h * * Keyword Trie based Map Table * * Author: Marc Norton * */ #ifndef KTRIE_H #define KTRIE_H #define ALPHABET_SIZE 256 /* * */ typedef struct _keynode { struct _keynode * next; unsigned char * key; int nkey; void * userdata; /* data associated with this pattern */ } KEYNODE; /* * */ typedef struct _kmapnode { int nodechar; /* node character */ struct _kmapnode * sibling; struct _kmapnode * child; KEYNODE * knode; } KMAPNODE; /* * */ typedef void (*KMapUserFreeFunc)(void *p); typedef struct _kmap { KMAPNODE * root[256]; /* KTrie nodes */ KEYNODE * keylist; // list of key+data pairs KEYNODE * keynext; // findfirst/findnext node KMapUserFreeFunc userfree; // fcn to free user data int nchars; // # character nodes int nocase; } KMAP; /* * PROTOTYPES */ KMAP * KMapNew ( KMapUserFreeFunc userfree ); void KMapSetNoCase( KMAP * km, int flag ); int KMapAdd ( KMAP * km, void * key, int ksize, void * userdata ); void * KMapFind( KMAP * km, void * key, int ksize ); void * KMapFindFirst( KMAP * km ); void * KMapFindNext ( KMAP * km ); KEYNODE * KMapFindFirstKey( KMAP * km ); KEYNODE * KMapFindNextKey ( KMAP * km ); void KMapDelete(KMAP *km); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/spp_ftptelnet.c0000644000175000017500000006711714241076010023262 0ustar apoapo/* * spp_ftptelnet.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file initializes FTPTelnet as a Snort preprocessor. * * This file registers the FTPTelnet initialization function, * adds the FTPTelnet function into the preprocessor list, reads * the user configuration in the snort.conf file, and prints out * the configuration that is read. * * In general, this file is a wrapper to FTPTelnet functionality, * by interfacing with the Snort preprocessor functions. The rest * of FTPTelnet should be separate from the preprocessor hooks. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "ftpp_ui_config.h" #include "snort_ftptelnet.h" #include "spp_ftptelnet.h" #include "sf_preproc_info.h" #include "profiler.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #ifdef REG_TEST #include "ftpp_si.h" #endif #ifdef DUMP_BUFFER #include "ftptelnet_buffer_dump.h" #endif #include "reg_test.h" #include "memory_stats.h" const int MAJOR_VERSION = 1; const int MINOR_VERSION = 2; const int BUILD_VERSION = 13; const char *PREPROC_NAME = "SF_FTPTELNET"; #define SetupFTPTelnet DYNAMIC_PREPROC_SETUP /* * Defines for preprocessor initialization */ /* * snort.conf preprocessor keyword */ #define GLOBAL_KEYWORD "ftp_telnet" #define PROTOCOL_KEYWORD "ftp_telnet_protocol" /* * The length of the error string buffer. */ #define ERRSTRLEN 1000 /* * External Global Variables * Variables that we need from Snort to log errors correctly and such. */ #ifdef PERF_PROFILING PreprocStats ftpPerfStats; PreprocStats telnetPerfStats; #ifdef TARGET_BASED PreprocStats ftpdataPerfStats; #endif #endif FTPTelnet_Stats ftp_telnet_stats; /* * Global Variables * This is the only way to work with Snort preprocessors because * the user configuration must be kept between the Init function * the actual preprocessor. There is no interaction between the * two except through global variable usage. */ tSfPolicyUserContextId ftp_telnet_config = NULL; FTPTELNET_GLOBAL_CONF *ftp_telnet_eval_config = NULL; #ifdef TARGET_BASED int16_t ftp_app_id = 0; int16_t ftp_data_app_id = 0; int16_t telnet_app_id = 0; #endif /* static function prototypes */ static void FTPTelnetReset(int, void *); static void FTPTelnetResetStats(int, void *); static void FTPTelnetStats(int); #ifdef SNORT_RELOAD static void FtpTelnetReloadGlobal(struct _SnortConfig *, char *, void **); static void FtpTelnetReload(struct _SnortConfig *, char *, void **); static int FtpTelnetReloadVerify(struct _SnortConfig *, void *); static void * FtpTelnetReloadSwap(struct _SnortConfig *, void *); static void FtpTelnetReloadSwapFree(void *); #endif int ftptelnet_print_mem_stats(FILE *, char*, PreprocMemInfo *); extern char *maxToken; /* * Function: FTPTelnetChecks(Packet *p) * * Purpose: This function wraps the functionality in the generic FTPTelnet * processing. We get a Packet structure and pass this into the * FTPTelnet module where the first stage in FTPTelnet is the * Normalization stage where most of the other Snortisms are * taken care of. After that, the modules are generic. * * Arguments: p => pointer to a Packet structure that contains * Snort info about the packet. * * Returns: None * */ void FTPTelnetChecks(void *pkt, void *context) { SFSnortPacket *p = (SFSnortPacket*)pkt; // precondition - what we registered for assert(IsTCP(p) && p->payload && p->payload_size); SnortFTPTelnet(p); } #ifdef TARGET_BASED void FTPDataTelnetChecks(void *pkt, void *context) { SFSnortPacket *p = (SFSnortPacket*)pkt; // precondition - what we registered for assert(IsTCP(p)); if ( _dpd.fileAPI->get_max_file_depth(NULL, false) >= 0 ) { if ( _dpd.sessionAPI->get_application_protocol_id(p->stream_session) == ftp_data_app_id ) { PROFILE_VARS; PREPROC_PROFILE_START(ftpdataPerfStats); SnortFTPData(p); PREPROC_PROFILE_END(ftpdataPerfStats); return; } } if ( !p->payload_size || (p->payload == NULL) ) return; SnortFTPTelnet(p); } #endif /* * Function: FTPTelnetInit(char *args) * * Purpose: This function cleans up FTPTelnet memory from the configuration * data. * * Arguments: sig => signal causing this to be called * args => pointer to a context strucutre * * Returns: None * */ void FTPTelnetCleanExit(int sig, void *args) { FTPTelnetFreeConfigs(ftp_telnet_config); ftp_telnet_config = NULL; } /* * Function: FTPTelnetInit(char *args) * * Purpose: This function initializes FTPTelnetInit with a user configuration. * The function is called when FTPTelnet is configured in snort.conf. * It gets passed a string of arguments, which gets parsed into * configuration constructs that FTPTelnet understands. * * This function gets called for every FTPTelnet configure line. We * use this characteristic to split up the configuration, so each * line is a configuration construct. We need to keep track of what * part of the configuration has been configured, so we don't * configure one part, then configure it again. * * Any upfront memory is allocated here (if necessary). * * Arguments: args => pointer to a string to the preprocessor arguments. * * Returns: None * */ extern char* mystrtok (char* s, const char* delim); #ifdef REG_TEST static inline void PrintFTPSize(void) { _dpd.logMsg("\nFTP Session Size: %lu\n", (long unsigned int)sizeof(FTP_SESSION)); } #endif static void FTPTelnetInit(struct _SnortConfig *sc, char *args) { char *pcToken; char ErrorString[ERRSTRLEN]; int iErrStrLen = ERRSTRLEN; int iRet = 0; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); FTPTELNET_GLOBAL_CONF *pPolicyConfig = NULL; ErrorString[0] = '\0'; #ifdef REG_TEST PrintFTPSize(); #endif if ((args == NULL) || (strlen(args) == 0)) { DynamicPreprocessorFatalMessage("%s(%d) No arguments to FtpTelnet " "configuration.\n", *_dpd.config_file, *_dpd.config_line); } /* Find out what is getting configured */ maxToken = args + strlen(args); pcToken = mystrtok(args, CONF_SEPARATORS); if (pcToken == NULL) { DynamicPreprocessorFatalMessage("%s(%d)mystrtok returned NULL when it " "should not.", __FILE__, __LINE__); } if (ftp_telnet_config == NULL) { //create a context ftp_telnet_config = sfPolicyConfigCreate(); if (ftp_telnet_config == NULL) { DynamicPreprocessorFatalMessage("No memory to allocate " "FTP/Telnet configuration.\n"); } _dpd.addPreprocExit(FTPTelnetCleanExit, NULL, PRIORITY_APPLICATION, PP_FTPTELNET); _dpd.addPreprocReset(FTPTelnetReset, NULL, PRIORITY_APPLICATION, PP_FTPTELNET); _dpd.addPreprocResetStats(FTPTelnetResetStats, NULL, PRIORITY_APPLICATION, PP_FTPTELNET); _dpd.addPreprocConfCheck(sc, FTPConfigCheck); _dpd.registerPreprocStats("ftp_telnet", FTPTelnetStats); #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("ftptelnet_ftp", (void*)&ftpPerfStats, 0, _dpd.totalPerfStats, NULL); _dpd.addPreprocProfileFunc("ftptelnet_telnet", (void*)&telnetPerfStats, 0, _dpd.totalPerfStats, NULL); #ifdef TARGET_BASED _dpd.addPreprocProfileFunc("ftptelnet_ftpdata", (void*)&ftpdataPerfStats, 0, _dpd.totalPerfStats, NULL); #endif #endif #ifdef TARGET_BASED if (_dpd.streamAPI != NULL) { /* Find and store the application ID for FTP & Telnet */ ftp_app_id = _dpd.addProtocolReference("ftp"); ftp_data_app_id = _dpd.addProtocolReference("ftp-data"); telnet_app_id = _dpd.addProtocolReference("telnet"); } // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_FTPTELNET, ftp_app_id ); _dpd.sessionAPI->register_service_handler( PP_FTPTELNET, ftp_data_app_id ); _dpd.sessionAPI->register_service_handler( PP_FTPTELNET, telnet_app_id ); #endif } /* * Global Configuration Processing * We only process the global configuration once, but always check for * user mistakes, like configuring more than once. That's why we * still check for the global token even if it's been checked. */ sfPolicyUserPolicySet (ftp_telnet_config, policy_id); pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGetCurrent(ftp_telnet_config); if (pPolicyConfig == NULL) { if (strcasecmp(pcToken, GLOBAL) != 0) { DynamicPreprocessorFatalMessage("%s(%d) Must configure the " "ftptelnet global configuration first.\n", *_dpd.config_file, *_dpd.config_line); } pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)_dpd.snortAlloc(1, sizeof(FTPTELNET_GLOBAL_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("No memory to allocate " "FTP/Telnet configuration.\n"); } sfPolicyUserDataSetCurrent(ftp_telnet_config, pPolicyConfig); iRet = FtpTelnetInitGlobalConfig(pPolicyConfig, ErrorString, iErrStrLen); if (iRet == 0) { iRet = ProcessFTPGlobalConf(pPolicyConfig, ErrorString, iErrStrLen); if (iRet == 0) { PrintFTPGlobalConf(pPolicyConfig); _dpd.preprocOptRegister(sc, "ftp.bounce", &FTPPBounceInit, &FTPPBounceEval, NULL, NULL, NULL, NULL, NULL); #ifdef TARGET_BASED if (_dpd.streamAPI != NULL) { _dpd.streamAPI->set_service_filter_status (sc, ftp_app_id, PORT_MONITOR_SESSION, policy_id, 1); _dpd.streamAPI->set_service_filter_status (sc, telnet_app_id, PORT_MONITOR_SESSION, policy_id, 1); } #endif } } } else if (strcasecmp(pcToken, TELNET) == 0) { iRet = ProcessTelnetConf(pPolicyConfig, ErrorString, iErrStrLen); enableFtpTelnetPortStreamServices( sc, &pPolicyConfig->telnet_config->proto_ports, NULL, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); } else if (strcasecmp(pcToken, FTP) == 0) { pcToken = NextToken(CONF_SEPARATORS); if ( !pcToken ) { DynamicPreprocessorFatalMessage( "%s(%d) Missing ftp_telnet ftp keyword.\n", *(_dpd.config_file), *(_dpd.config_line)); } else if (strcasecmp(pcToken, SERVER) == 0) { iRet = ProcessFTPServerConf(sc, pPolicyConfig, ErrorString, iErrStrLen); } else if (strcasecmp(pcToken, CLIENT) == 0) { iRet = ProcessFTPClientConf(sc, pPolicyConfig, ErrorString, iErrStrLen); } else { DynamicPreprocessorFatalMessage("%s(%d) Invalid ftp_telnet ftp keyword.\n", *(_dpd.config_file), *(_dpd.config_line)); } } else { DynamicPreprocessorFatalMessage("%s(%d) Invalid ftp_telnet keyword.\n", *(_dpd.config_file), *(_dpd.config_line)); } if (iRet) { if(iRet > 0) { /* * Non-fatal Error */ if(*ErrorString) { _dpd.errMsg("WARNING: %s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), ErrorString); } } else { /* * Fatal Error, log error and exit. */ if(*ErrorString) { DynamicPreprocessorFatalMessage("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), ErrorString); } else { /* * Check if ErrorString is undefined. */ if(iRet == -2) { DynamicPreprocessorFatalMessage("%s(%d) => ErrorString is undefined.\n", *(_dpd.config_file), *(_dpd.config_line)); } else { DynamicPreprocessorFatalMessage("%s(%d) => Undefined Error.\n", *(_dpd.config_file), *(_dpd.config_line)); } } } } } /* * Function: SetupFTPTelnet() * * Purpose: This function initializes FTPTelnet as a Snort preprocessor. * * It registers the preprocessor keyword for use in the snort.conf * and sets up the initialization module for the preprocessor, in * case it is configured. * * This function must be called in InitPreprocessors() in plugbase.c * in order to be recognized by Snort. * * Arguments: None * * Returns: None * */ void SetupFTPTelnet(void) { #ifndef SNORT_RELOAD _dpd.registerPreproc(GLOBAL_KEYWORD, FTPTelnetInit); _dpd.registerPreproc(PROTOCOL_KEYWORD, FTPTelnetInit); #else _dpd.registerPreproc(GLOBAL_KEYWORD, FTPTelnetInit, FtpTelnetReloadGlobal, FtpTelnetReloadVerify, FtpTelnetReloadSwap, FtpTelnetReloadSwapFree); _dpd.registerPreproc(PROTOCOL_KEYWORD, FTPTelnetInit, FtpTelnetReload, NULL, NULL, NULL); #endif DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Preprocessor: FTPTelnet is " "setup . . .\n");); #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getFTPTelnetBuffers, FTPTELNET_BUFFER_DUMP_FUNC); #endif _dpd.registerMemoryStatsFunc(PP_FTPTELNET, ftptelnet_print_mem_stats); } static void FTPTelnetReset(int signal, void *data) { return; } static void FTPTelnetResetStats(int signal, void *data) { return; } static void FTPTelnetStats(int exiting) { _dpd.logMsg("FTPTelnet Preprocessor Statistics\n"); _dpd.logMsg(" Current active FTP sessions : " STDu64 "\n", ftp_telnet_stats.ftp_sessions); _dpd.logMsg(" Max concurrent FTP sessions : " STDu64 "\n", ftp_telnet_stats.max_ftp_sessions); _dpd.logMsg(" Total FTP Data sessions : " STDu64 "\n", ftp_telnet_stats.ftp_data_sessions); _dpd.logMsg(" Max concurrent FTP Data sessions : " STDu64 "\n", ftp_telnet_stats.max_ftp_data_sessions); _dpd.logMsg(" Current active Telnet sessions : " STDu64 "\n", ftp_telnet_stats.telnet_sessions); _dpd.logMsg(" Max concurrent Telnet sessions : " STDu64 "\n", ftp_telnet_stats.max_telnet_sessions); _dpd.logMsg(" Current ftp_telnet session non-mempool memory : " STDu64 "\n", ftp_telnet_stats.heap_memory); } int ftptelnet_print_mem_stats(FILE *fd, char* buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; size_t total_heap_memory = meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory; if (fd) { len = fprintf(fd, ","STDu64","STDu64","STDu64"" ","STDu64","STDu64","STDu64"" ",%lu,%u,%u,%lu,%u,%u,%lu" , ftp_telnet_stats.ftp_sessions , ftp_telnet_stats.max_ftp_sessions , ftp_telnet_stats.ftp_data_sessions , ftp_telnet_stats.max_ftp_data_sessions , ftp_telnet_stats.telnet_sessions , ftp_telnet_stats.max_telnet_sessions , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , total_heap_memory); return len; } if (buffer) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics for FTPTelnet at: %s\n" "FTPTelnet Preprocessor Statistics:\n" " Current active FTP sessions : "STDu64"\n" " Max concurrent FTP sessions : "STDu64"\n" " Total FTP Data sessions : "STDu64"\n" " Max concurrent FTP Data sessions : "STDu64"\n" " Current active Telnet sessions : "STDu64"\n" " Max concurrent Telnet sessions : "STDu64"\n" , ctime(&curr_time) , ftp_telnet_stats.ftp_sessions , ftp_telnet_stats.max_ftp_sessions , ftp_telnet_stats.ftp_data_sessions , ftp_telnet_stats.max_ftp_data_sessions , ftp_telnet_stats.telnet_sessions , ftp_telnet_stats.max_telnet_sessions); } else { _dpd.logMsg("\n"); _dpd.logMsg("Memory Statistics of FTPTelnet at: %s\n", ctime(&curr_time)); _dpd.logMsg(" Current active FTP sessions : "STDu64"\n", ftp_telnet_stats.ftp_sessions); _dpd.logMsg(" Max concurrent FTP sessions : "STDu64"\n", ftp_telnet_stats.max_ftp_sessions); _dpd.logMsg(" Total FTP Data sessions : "STDu64"\n", ftp_telnet_stats.ftp_data_sessions); _dpd.logMsg(" Max concurrent FTP Data sessions : "STDu64"\n", ftp_telnet_stats.max_ftp_data_sessions); _dpd.logMsg(" Current active Telnet sessions : "STDu64"\n", ftp_telnet_stats.telnet_sessions); _dpd.logMsg(" Max concurrent Telnet sessions : "STDu64"\n", ftp_telnet_stats.max_telnet_sessions); } return len; } #ifdef SNORT_RELOAD static void _FtpTelnetReload(struct _SnortConfig *sc, tSfPolicyUserContextId ftp_telnet_swap_config, char *args) { char *pcToken; char ErrorString[ERRSTRLEN]; int iErrStrLen = ERRSTRLEN; int iRet = 0; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); FTPTELNET_GLOBAL_CONF *pPolicyConfig = NULL; ErrorString[0] = '\0'; if ((args == NULL) || (strlen(args) == 0)) { DynamicPreprocessorFatalMessage("%s(%d) No arguments to FtpTelnet " "configuration.\n", *_dpd.config_file, *_dpd.config_line); } /* Find out what is getting configured */ maxToken = args + strlen(args); pcToken = mystrtok(args, CONF_SEPARATORS); if (pcToken == NULL) { DynamicPreprocessorFatalMessage("%s(%d)mystrtok returned NULL when it " "should not.", __FILE__, __LINE__); } /* * Global Configuration Processing * We only process the global configuration once, but always check for * user mistakes, like configuring more than once. That's why we * still check for the global token even if it's been checked. */ sfPolicyUserPolicySet (ftp_telnet_swap_config, policy_id); pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGetCurrent(ftp_telnet_swap_config); if (pPolicyConfig == NULL) { if (strcasecmp(pcToken, GLOBAL) != 0) { DynamicPreprocessorFatalMessage("%s(%d) Must configure the " "ftptelnet global configuration first.\n", *_dpd.config_file, *_dpd.config_line); } pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)_dpd.snortAlloc(1, sizeof(FTPTELNET_GLOBAL_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("No memory to allocate " "FTP/Telnet configuration.\n"); } sfPolicyUserDataSetCurrent(ftp_telnet_swap_config, pPolicyConfig); iRet = FtpTelnetInitGlobalConfig(pPolicyConfig, ErrorString, iErrStrLen); if (iRet == 0) { iRet = ProcessFTPGlobalConf(pPolicyConfig, ErrorString, iErrStrLen); if (iRet == 0) { PrintFTPGlobalConf(pPolicyConfig); _dpd.preprocOptRegister(sc, "ftp.bounce", &FTPPBounceInit, &FTPPBounceEval, NULL, NULL, NULL, NULL, NULL); } } } else if (strcasecmp(pcToken, TELNET) == 0) { iRet = ProcessTelnetConf(pPolicyConfig, ErrorString, iErrStrLen); enableFtpTelnetPortStreamServices( sc, &pPolicyConfig->telnet_config->proto_ports, NULL, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); } else if (strcasecmp(pcToken, FTP) == 0) { pcToken = NextToken(CONF_SEPARATORS); if ( !pcToken ) { DynamicPreprocessorFatalMessage( "%s(%d) Missing ftp_telnet ftp keyword.\n", *(_dpd.config_file), *(_dpd.config_line)); } else if (strcasecmp(pcToken, SERVER) == 0) { iRet = ProcessFTPServerConf(sc, pPolicyConfig, ErrorString, iErrStrLen); } else if (strcasecmp(pcToken, CLIENT) == 0) { iRet = ProcessFTPClientConf(sc, pPolicyConfig, ErrorString, iErrStrLen); } else { DynamicPreprocessorFatalMessage("%s(%d) Invalid ftp_telnet ftp keyword.\n", *(_dpd.config_file), *(_dpd.config_line)); } } else { DynamicPreprocessorFatalMessage("%s(%d) Invalid ftp_telnet keyword.\n", *(_dpd.config_file), *(_dpd.config_line)); } if (iRet) { if(iRet > 0) { /* * Non-fatal Error */ if(*ErrorString) { _dpd.errMsg("WARNING: %s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), ErrorString); } } else { /* * Fatal Error, log error and exit. */ if(*ErrorString) { DynamicPreprocessorFatalMessage("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), ErrorString); } else { /* * Check if ErrorString is undefined. */ if(iRet == -2) { DynamicPreprocessorFatalMessage("%s(%d) => ErrorString is undefined.\n", *(_dpd.config_file), *(_dpd.config_line)); } else { DynamicPreprocessorFatalMessage("%s(%d) => Undefined Error.\n", *(_dpd.config_file), *(_dpd.config_line)); } } } } } static void FtpTelnetReloadGlobal(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId ftp_telnet_swap_config = (tSfPolicyUserContextId)*new_config; if (ftp_telnet_swap_config == NULL) { //create a context ftp_telnet_swap_config = sfPolicyConfigCreate(); if (ftp_telnet_swap_config == NULL) { DynamicPreprocessorFatalMessage("No memory to allocate " "FTP/Telnet swap_configuration.\n"); } *new_config = (void *)ftp_telnet_swap_config; } _FtpTelnetReload(sc, ftp_telnet_swap_config, args); } static void FtpTelnetReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId ftp_telnet_swap_config; ftp_telnet_swap_config = (tSfPolicyUserContextId)_dpd.getRelatedReloadData(sc, GLOBAL_KEYWORD); _FtpTelnetReload(sc, ftp_telnet_swap_config, args); } static int FtpTelnetReloadVerifyPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { return FTPTelnetCheckConfigs( sc, pData, policyId ); } static int FtpTelnetReloadVerify(struct _SnortConfig *sc, void *new_config) { tSfPolicyUserContextId ftp_telnet_swap_config = (tSfPolicyUserContextId)new_config; if (ftp_telnet_swap_config == NULL) return 0; if (sfPolicyUserDataIterate (sc, ftp_telnet_swap_config, FtpTelnetReloadVerifyPolicy)) return -1; return 0; } static int FtpTelnetReloadSwapPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { FTPTELNET_GLOBAL_CONF *pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)pData; //do any housekeeping before freeing FTPTELNET_GLOBAL_CONF if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); FTPTelnetFreeConfig(pPolicyConfig); } return 0; } static void * FtpTelnetReloadSwap(struct _SnortConfig *sc, void *new_config) { tSfPolicyUserContextId ftp_telnet_swap_config = (tSfPolicyUserContextId)new_config; tSfPolicyUserContextId old_config = ftp_telnet_config; if (ftp_telnet_swap_config == NULL) return NULL; ftp_telnet_config = ftp_telnet_swap_config; sfPolicyUserDataIterate (sc, old_config, FtpTelnetReloadSwapPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) return (void *)old_config; return NULL; } static void FtpTelnetReloadSwapFree(void *data) { if (data == NULL) return; FTPTelnetFreeConfigs((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/hi_util_kmap.c0000644000175000017500000002716714241075775023062 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * * kmap.c - a generic map library - maps key + data pairs * * Uses Lexical Keyword Trie * The tree uses linked lists to build the finite automata * * MapKeyFind(): Performs a setwise strcmp() equivalant. * * Notes: * * Keys may be ascii or binary, both may be of random sizes. * Each key may be a different size, or all one size. * Fast dictionary lookup, proportional to the length of the key, * and independent of the number of keys in the table. * May use more memory than a hash table, depends. * Memory is allocated as needed, so none is wasted. * * Author: Marc Norton * */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "hi_util_kmap.h" #include "hi_util_xmalloc.h" //#define MEMASSERT(p) if(!p){printf("KMAP-No Memory: File: %s Line:%d!\n",__FILE__,__LINE__);exit(0);} #define MEMASSERT(p) #define LOWERCASE tolower /* * */ static void * s_malloc( int n ) { void * p; p = xmalloc( n ); MEMASSERT(p); return p; } /* * */ static void s_free( void * p , int n) { if( p ) xfree( p , n ); } /* * */ KMAP * KMapNew( KMapUserFreeFunc userfree ) { KMAP * km = (KMAP*) s_malloc( sizeof(KMAP) ); if( !km ) return 0; memset(km, 0, sizeof(KMAP)); km->userfree = userfree; return km; } /* * */ void KMapSetNoCase( KMAP * km, int flag ) { km->nocase = flag; } /* * Free the list of key+data pair nodes - used by findfirst/next */ static int KMapFreeNodeList(KMAP * km ) { KEYNODE * k, *kold; for( k=km->keylist; k; ) { if( k->key ) { s_free( k->key , k->nkey ); } if( km->userfree && k->userdata ) { km->userfree( k->userdata ); } kold = k; k = k->next; s_free(kold, sizeof(KEYNODE)); } return 0; } /* * Recursively walk and free nodes */ static void KMapFreeNode( KMAP * km, KMAPNODE * r) { if( r->sibling ) { KMapFreeNode( km, r->sibling ); } if( r->child ) { KMapFreeNode( km, r->child ); } s_free( r , sizeof(KMAPNODE) ); } /* * Free the KMAP and all of it's memory and nodes */ void KMapDelete( KMAP * km ) { KMAPNODE * r; int i; /* Free the tree - on root node at a time */ for(i=0;i<256;i++) { r = km->root[i]; if( r ) { KMapFreeNode(km,r); } } /* Free the node list */ KMapFreeNodeList( km ); s_free(km, sizeof(KMAP)); } /* * Add key + data pair to the linked list of nodes */ static KEYNODE * KMapAddKeyNode(KMAP * km,void * key, int n, void * userdata ) { KEYNODE * knode; if (n <= 0) return 0; knode = (KEYNODE*) s_malloc( sizeof(KEYNODE) ); if (!knode) return 0; memset(knode, 0, sizeof(KEYNODE) ); knode->key = (unsigned char*)s_malloc(n); // Alloc the key space if( !knode->key ) { free(knode); return 0; } memcpy(knode->key,key,n); // Copy the key knode->nkey = n; knode->userdata = userdata; if( km->keylist ) // Insert at front of list { knode->next = km->keylist; km->keylist = knode; } else { km->keylist = knode; } return knode; } /* * Create a character node */ static KMAPNODE * KMapCreateNode(KMAP * km) { KMAPNODE * mn=(KMAPNODE*)s_malloc( sizeof(KMAPNODE) ); if(!mn) return NULL; memset(mn,0,sizeof(KMAPNODE)); km->nchars++; return mn; } /* * key : ptr to bytes of data used to identify this item * may be text string, or binary character sequence. * n : > 0 number of bytes in the key * <=0 key is a null terminated ascii string * userdata - ptr to data to associate with this key * * returns: * -1 - no memory * 1 - key already in table * 0 - key added successfully */ int KMapAdd( KMAP *km, void * key, int n, void * userdata ) { int i,ksize; int type = 0; unsigned char *P = (unsigned char *)key; KMAPNODE *root; unsigned char xkey[256]; if( n <= 0 ) { n = strlen( (char*) key ); if( n > (int)sizeof(xkey) ) return -99; } if( km->nocase ) { for(i=0;iroot[ *P ] ) { root = KMapCreateNode(km); if( !root ) return -1; km->root[ *P ] = root; root->nodechar = *P; }else{ root = km->root[ *P ]; } /* Walk exisitng Patterns */ while( n ) { if( root->nodechar == *P ) { //printf("matched char = %c, nleft = %d\n",*P,n-1); P++; n--; if( n && root->child ) { root=root->child; } else /* cannot continue */ { type = 0; /* Expand the tree via the child */ break; } } else { if( root->sibling ) { root=root->sibling; } else /* cannot continue */ { type = 1; /* Expand the tree via the sibling */ break; } } } /* * Add the next char of the Keyword, if any */ if( n ) { if( type == 0 ) { /* * Start with a new child to finish this Keyword */ //printf("added child branch nodechar = %c \n",*P); root->child= KMapCreateNode( km ); if( !root->child ) return -1; root=root->child; root->nodechar = *P; P++; n--; } else { /* * Start a new sibling bracnch to finish this Keyword */ //printf("added sibling branch nodechar = %c \n",*P); root->sibling= KMapCreateNode( km ); if( !root->sibling ) return -1; root=root->sibling; root->nodechar = *P; P++; n--; } } /* * Finish the keyword as child nodes */ while( n ) { //printf("added child nodechar = %c \n",*P); root->child = KMapCreateNode(km); if( !root->child ) return -1; root=root->child; root->nodechar = *P; P++; n--; } /* * Iteration support - Add this key/data to the linked list * This allows us to do a findfirst/findnext search of * all map nodes. */ if( root->knode ) /* Already present */ return 1; root->knode = KMapAddKeyNode( km, key, ksize, userdata ); if( !root->knode ) return -1; return 0; } /* * Exact Keyword Match - unique keys, with just one piece of * 'userdata' , for multiple entries, we could use a list * of 'userdata' nodes. */ void * KMapFind( KMAP * ks, void * key, int n ) { unsigned char * T = (unsigned char *)key; KMAPNODE * root; unsigned char xkey[256]; int i; if( n <= 0 ) { n = strlen( (char*)key ); if( n > (int)sizeof(xkey) ) return 0; } if( ks->nocase ) { for(i=0;iroot[ *T ]; if( !root ) return NULL; while( n ) { if( root->nodechar == *T ) { T++; n--; if( n && root->child ) { root = root->child; } else /* cannot continue -- match is over */ { break; } } else { if( root->sibling ) { root = root->sibling; } else /* cannot continue */ { break; } } } if( !n ) { if (root && root->knode) return root->knode->userdata; /* success */ } return NULL; } /* * */ KEYNODE * KMapFindFirstKey( KMAP * km ) { km->keynext = km->keylist; if(!km->keynext) { return NULL; } return km->keynext; } /* * */ void * KMapFindFirst( KMAP * km ) { km->keynext = km->keylist; if(!km->keynext) { return NULL; } return km->keynext->userdata; } /* * */ KEYNODE * KMapFindNextKey( KMAP * km ) { if( !km->keynext ) return 0; km->keynext = km->keynext->next; if( !km->keynext ) return 0; return km->keynext; } /* * */ void * KMapFindNext( KMAP * km ) { if( !km->keynext ) return 0; km->keynext = km->keynext->next; if( !km->keynext ) return 0; return km->keynext->userdata; } #ifdef KMAP_MAIN /* * */ int main( int argc, char ** argv ) { int i,n=10; KMAP * km; char * p; char str[80]; str[79] = '\0'; printf("usage: kmap nkeys (default=10)\n\n"); km = KMapNew( free ); /* use 'free' to free 'userdata' */ KMapSetNoCase(km,1); //need to add xlat.... if( argc > 1 ) { n = atoi(argv[1]); } for(i=1;i<=n;i++) { snprintf(str, sizeof(str) - 1, "KeyWord%d",i); KMapAdd( km, str, 0 /* strlen(str) */, strupr(strdup(str)) ); printf("Adding Key=%s\n",str); } printf("xmem: %u bytes, %d chars\n",xmalloc_bytes(),km->nchars); printf("\nKey Find test...\n"); for(i=1;i<=n;i++) { snprintf(str, sizeof(str) - 1, "KeyWord%d",i); p = (char*) KMapFind( km, str, 0 /*strlen(str) */ ); if(p)printf("key=%s, data=%*s\n",str,strlen(str),p); else printf("'%s' NOT found.\n",str); } KMapSetNoCase(km,0); // this should fail all key searches printf("\nKey Find test2...\n"); for(i=1;i<=n;i++) { snprintf(str, sizeof(str) - 1, "KeyWord%d",i); p = (char*) KMapFind( km, str, 0 /*strlen(str) */ ); if(p)printf("key=%s, data=%*s\n",str,strlen(str),p); else printf("'%s' NOT found.\n",str); } printf("\nKey FindFirst/Next test...\n"); for(p = (char*) KMapFindFirst(km); p; p=(char*)KMapFindNext(km) ) printf("data=%s\n",p); printf("\nKey FindFirst/Next test done.\n"); KMapDelete( km ); printf("xmem: %u bytes\n",xmalloc_bytes()); printf("normal pgm finish.\n"); return 0; } #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/snort_ftptelnet.h0000644000175000017500000000702214241076007023625 0ustar apoapo/* * snort_ftptelnet.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file defines the publicly available functions for the FTPTelnet * functionality for Snort. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __SNORT_FTPTELNET_H__ #define __SNORT_FTPTELNET_H__ #include "ftpp_ui_config.h" #include "sf_snort_packet.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" /* * The definition of the configuration separators in the snort.conf * configure line. */ #define CONF_SEPARATORS " \t\n\r" /* * These are the definitions of the parser section delimiting * keywords to configure FtpTelnet. When one of these keywords * are seen, we begin a new section. */ #define GLOBAL "global" #define TELNET "telnet" #define FTP "ftp" //#define GLOBAL_CLIENT "global_client" #define CLIENT "client" #define SERVER "server" #ifdef TARGET_BASED extern int16_t ftp_app_id; extern int16_t ftp_data_app_id; extern int16_t telnet_app_id; #endif void FTPTelnetFreeConfigs(tSfPolicyUserContextId GlobalConf); void FTPTelnetFreeConfig(FTPTELNET_GLOBAL_CONF *GlobalConf); int SnortFTPTelnet(SFSnortPacket *p); #ifdef TARGET_BASED void SnortFTPData_EOF(SFSnortPacket *p); void SnortFTPData_Flush(SFSnortPacket *p); int SnortFTPData(SFSnortPacket *p); #endif int FTPConfigCheck(struct _SnortConfig *); int FtpTelnetInitGlobalConfig(FTPTELNET_GLOBAL_CONF *, char *, int); char *NextToken(char *delimiters); int FTPPBounceInit(struct _SnortConfig *sc, char *name, char *parameters, void **dataPtr); int FTPPBounceEval(void *p, const uint8_t **cursor, void *dataPtr); void FTPTelnetChecks(void *pkt, void *context); #ifdef TARGET_BASED void FTPDataTelnetChecks(void *pkt, void *context); #endif void FTPTelnetCleanupFTPServerConf(void *serverConf); void FTPTelnetCleanupFTPCMDConf(void *ftpCmd); void FTPTelnetCleanupFTPClientConf(void *clientConf); void FTPTelnetCleanupFTPBounceTo(void *ftpBounce); int FTPTelnetCheckFTPServerConfigs(struct _SnortConfig *, FTPTELNET_GLOBAL_CONF *); int ProcessFTPGlobalConf(FTPTELNET_GLOBAL_CONF *, char *, int); int ProcessTelnetConf(FTPTELNET_GLOBAL_CONF *, char *, int); int ProcessFTPClientConf(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *, char *, int); int ProcessFTPServerConf(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *, char *, int); int PrintFTPGlobalConf(FTPTELNET_GLOBAL_CONF *); int FTPTelnetCheckConfigs(struct _SnortConfig *, void* , tSfPolicyId ); void enableFtpTelnetPortStreamServices( struct _SnortConfig *sc, PROTO_CONF *pc, char *network, int direction ); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_ui_client_lookup.h0000644000175000017500000000403214241075763024776 0ustar apoapo/* * ftpp_ui_client_lookup.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains function definitions for client lookups. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTPP_UI_CLIENT_LOOKUP_H__ #define __FTPP_UI_CLIENT_LOOKUP_H__ #include "ftpp_include.h" #include "ftpp_ui_config.h" int ftpp_ui_client_lookup_init(CLIENT_LOOKUP **ClientLookup); int ftpp_ui_client_lookup_cleanup(CLIENT_LOOKUP **ClientLookup); int ftpp_ui_client_lookup_add(CLIENT_LOOKUP *ClientLookup, sfcidr_t * IP, FTP_CLIENT_PROTO_CONF *ClientConf); FTP_CLIENT_PROTO_CONF *ftpp_ui_client_lookup_find(CLIENT_LOOKUP *ClientLookup, sfaddr_t* Ip, int *iError); FTP_CLIENT_PROTO_CONF *ftpp_ui_client_lookup_first(CLIENT_LOOKUP *ClientLookup, int *iError); FTP_CLIENT_PROTO_CONF *ftpp_ui_client_lookup_next(CLIENT_LOOKUP *ClientLookup, int *iError); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_ui_client_lookup.c0000644000175000017500000002145614241075762025001 0ustar apoapo/* * ftpp_ui_client_lookup.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains functions to access the CLIENT_LOOKUP structure. * * We wrap the access to CLIENT_LOOKUP so changing the lookup algorithms * are more modular and independent. This is the only file that would need * to be changed to change the algorithmic lookup. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "hi_util_kmap.h" #include "ftpp_ui_config.h" #include "ftpp_return_codes.h" #include "snort_ftptelnet.h" #include "sfrt.h" #include "memory_stats.h" static void clientConfFree(void *pData); /* * Function: ftpp_ui_client_lookup_init(CLIENT_LOOKUP **ClientLookup) * * Purpose: Initialize the client_lookup structure. * * We need to initialize the client_lookup structure for * the FTP client configuration. Don't want a NULL pointer * flying around, when we have to look for FTP clients. * * Arguments: ClientLookup => pointer to the pointer of the client * lookup structure. * * Returns: int => return code indicating error or success * */ #define FTPP_UI_CONFIG_MAX_CLIENTS 20 int ftpp_ui_client_lookup_init(CLIENT_LOOKUP **ClientLookup) { *ClientLookup = sfrt_new(DIR_16_4x4_16x5_4x4, IPv6, FTPP_UI_CONFIG_MAX_CLIENTS, 20); if(*ClientLookup == NULL) { return FTPP_MEM_ALLOC_FAIL; } return FTPP_SUCCESS; } /* * Function: ftpp_ui_client_lookup_cleanup(CLIENT_LOOKUP **ClientLookup) * * Purpose: Free the client_lookup structure. * We need to free the client_lookup structure. * * Arguments: ClientLookup => pointer to the pointer of the client * lookup structure. * * Returns: int => return code indicating error or success * */ int ftpp_ui_client_lookup_cleanup(CLIENT_LOOKUP **ClientLookup) { if ((ClientLookup == NULL) || (*ClientLookup == NULL)) return FTPP_INVALID_ARG; sfrt_cleanup(*ClientLookup, clientConfFree); sfrt_free(*ClientLookup); *ClientLookup = NULL; return FTPP_SUCCESS; } /* * Function: ftpp_ui_client_lookup_add(CLIENT_LOOKUP *ClientLookup, * sfcidr_t* Ip, * FTP_CLIENT_PROTO_CONF *ClientConf) * * Purpose: Add a client configuration to the list. * We add these keys like you would normally think to add * them, because on low endian machines the least significant * byte is compared first. This is what we want to compare * IPs backward, doesn't work on high endian machines, but oh * well. Our platform is Intel. * * Arguments: ClientLookup => a pointer to the lookup structure * IP => the ftp client address * ClientConf => a pointer to the client configuration structure * * Returns: int => return code indicating error or success * */ int ftpp_ui_client_lookup_add( CLIENT_LOOKUP *ClientLookup, sfcidr_t* Ip, FTP_CLIENT_PROTO_CONF *ClientConf) { int iRet; if(!ClientLookup || !ClientConf) { return FTPP_INVALID_ARG; } iRet = sfrt_insert(Ip, (unsigned char)Ip->bits, (void *)ClientConf, RT_FAVOR_SPECIFIC, ClientLookup); if (iRet) { /* * This means the key has already been added. */ if(iRet == 1) { return FTPP_NONFATAL_ERR; } else { return FTPP_MEM_ALLOC_FAIL; } } return FTPP_SUCCESS; } /* * Function: ftpp_ui_client_lookup_find(CLIENT_LOOKUP *ClientLookup, * sfaddr_t* ip, int *iError) * * Purpose: Find a client configuration given a IP. * We look up a client configuration given an IP and * return a pointer to that client configuration if found. * * Arguments: ClientLookup => a pointer to the lookup structure * IP => the ftp client address * iError => a pointer to an error code * * Returns: int => return code indicating error or success * * Returns: FTP_CLIENT_PROTO_CONF* => Pointer to client configuration * structure matching IP if found, NULL otherwise. * */ FTP_CLIENT_PROTO_CONF *ftpp_ui_client_lookup_find(CLIENT_LOOKUP *ClientLookup, sfaddr_t* Ip, int *iError) { FTP_CLIENT_PROTO_CONF *ClientConf = NULL; if(!iError) { return NULL; } if(!ClientLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; ClientConf = (FTP_CLIENT_PROTO_CONF *)sfrt_lookup(Ip, ClientLookup); if (!ClientConf) { *iError = FTPP_NOT_FOUND; } return ClientConf; } #if 0 /** Obsoleted. After changing underlying KMAP to SFRT. SFRT provides an iterator with * a callback function but does not support getFirst, getNext operations. */ /* * Function: ftpp_ui_client_lookup_first(CLIENT_LOOKUP *ClientLookup, * int *iError) * * Purpose: This lookups the first client configuration, so we can * iterate through the configurations. * * Arguments: ClientLookup => pointer to the client lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_CLIENT_PROTO_CONF* => Pointer to first client configuration * structure * */ FTP_CLIENT_PROTO_CONF *ftpp_ui_client_lookup_first(CLIENT_LOOKUP *ClientLookup, int *iError) { FTP_CLIENT_PROTO_CONF *ClientConf; if(!iError) { return NULL; } if(!ClientLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; ClientConf = (FTP_CLIENT_PROTO_CONF *)KMapFindFirst(ClientLookup); if (!ClientConf) { *iError = FTPP_NOT_FOUND; } return ClientConf; } /* * Function: ftpp_ui_client_lookup_next(CLIENT_LOOKUP *ClientLookup, * int *iError) * * Iterates to the next configuration, like a list it just returns * the next config in the config list. * * Purpose: This lookups the next client configuration, so we can * iterate through the configurations. * * Arguments: ClientLookup => pointer to the client lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_CLIENT_PROTO_CONF* => Pointer to next client configuration * structure * */ FTP_CLIENT_PROTO_CONF *ftpp_ui_client_lookup_next(CLIENT_LOOKUP *ClientLookup, int *iError) { FTP_CLIENT_PROTO_CONF *ClientConf; if(!iError) { return NULL; } if(!ClientLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; ClientConf = (FTP_CLIENT_PROTO_CONF *)KMapFindNext(ClientLookup); if (!ClientConf) { *iError = FTPP_NOT_FOUND; } return ClientConf; } #endif /**Free pData buffer, which may be referenced multiple times. ReferenceCount * is the number of times the buffer is referenced. For freeing the buffer, * we just decrement referenceCount till it reaches 0, at which time the * buffer is also freed. */ static void clientConfFree(void *pData) { FTP_CLIENT_PROTO_CONF *clientConf = (FTP_CLIENT_PROTO_CONF *)pData; if (clientConf) { clientConf->referenceCount--; if (clientConf->referenceCount == 0) { FTPTelnetCleanupFTPClientConf((void *)clientConf); _dpd.snortFree(clientConf, sizeof(FTP_CLIENT_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } } } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/sf_ftptelnet.vcxproj0000444000175000017500000004476714230012554024346 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {2304868A-4DC4-46BF-98E8-00C8A41A7511} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Debug\ .\Debug\ true true .\Release\ .\Release\ false false .\Release\ .\Release\ MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_ftptelnet.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_ftptelnet.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_ftptelnet.bsc true true true Console .\Debug\sf_ftptelnet.dll .\Debug\sf_ftptelnet.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_ftptelnet.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_ftptelnet.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_ftptelnet.bsc true true true Console .\Debug\sf_ftptelnet.dll .\Debug\sf_ftptelnet.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_ftptelnet.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_ftptelnet.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_ftptelnet.bsc true true Console .\Release\sf_ftptelnet.dll .\Release\sf_ftptelnet.lib ws2_32.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_ftptelnet.pch .\Release\ .\Release\ CompileAsC true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_ftptelnet.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_ftptelnet.bsc true true Console .\Release\sf_ftptelnet.dll .\Release\sf_ftptelnet.lib ws2_32.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftp_bounce_lookup.h0000644000175000017500000000346714241075743024127 0ustar apoapo/* * ftp_bounce_lookup.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains function definitions for bounce IP lookups. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #ifndef __FTP_BOUNCE_LOOKUP_H__ #define __FTP_BOUNCE_LOOKUP_H__ #include "ftpp_include.h" #include "ftpp_ui_config.h" int ftp_bounce_lookup_init(BOUNCE_LOOKUP **BounceLookup); int ftp_bounce_lookup_cleanup(BOUNCE_LOOKUP **BounceLookup); int ftp_bounce_lookup_add(BOUNCE_LOOKUP *BounceLookup, sfcidr_t* ip, FTP_BOUNCE_TO *BounceTo); FTP_BOUNCE_TO *ftp_bounce_lookup_find(BOUNCE_LOOKUP *BounceLookup, sfaddr_t* ip, int *iError); FTP_BOUNCE_TO *ftp_bounce_lookup_first(BOUNCE_LOOKUP *BounceLookup, int *iError); FTP_BOUNCE_TO *ftp_bounce_lookup_next(BOUNCE_LOOKUP *BounceLookup, int *iError); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/pp_ftp.h0000644000175000017500000000301214241076002021651 0ustar apoapo/* * pp_ftp.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Header file for FTPTelnet FTP Module * * This file defines the ftp checking functions * * NOTES: * - 20.09.04: Initial Development. SAS * */ #ifndef __PP_FTP_H__ #define __PP_FTP_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include //#include "decode.h" #include "ftpp_ui_config.h" #include "ftpp_si.h" /* list of function prototypes for this preprocessor */ extern int check_ftp(FTP_SESSION *Session, SFSnortPacket *p, int iMode); extern int initialize_ftp(FTP_SESSION *Session, SFSnortPacket *p, int iMode); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/Makefile.am0000444000175000017500000000317114230012554022250 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs -I${srcdir}/includes dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_ftptelnet_preproc.la libsf_ftptelnet_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_ftptelnet_preproc_la_LIBADD = ../libsf_dynamic_preproc.la ../libsf_dynamic_utils.la else nodist_libsf_ftptelnet_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/sf_ip.c \ ../include/sfrt.c \ ../include/sfrt_dir.c \ ../include/sfPolicyUserData.c \ ../include/sfmemcap.c \ ../ssl_common/ssl.c \ ../ssl_common/ssl_config.c \ ../ssl_common/ssl_inspect.c \ ../libs/sfparser.c endif libsf_ftptelnet_preproc_la_SOURCES = \ ftp_bounce_lookup.c \ ftp_bounce_lookup.h \ ftp_client.h \ ftp_cmd_lookup.c \ ftp_cmd_lookup.h \ ftpp_eo_events.h \ ftpp_eo.h \ ftpp_eo_log.c \ ftpp_eo_log.h \ ftpp_include.h \ ftpp_return_codes.h \ ftpp_si.c \ ftpp_si.h \ ftpp_ui_client_lookup.c \ ftpp_ui_client_lookup.h \ ftpp_ui_config.c \ ftpp_ui_config.h \ ftpp_ui_server_lookup.c \ ftpp_ui_server_lookup.h \ ftp_server.h \ hi_util_kmap.c \ hi_util_kmap.h \ hi_util_xmalloc.c \ hi_util_xmalloc.h \ pp_ftp.c \ pp_ftp.h \ pp_telnet.c \ pp_telnet.h \ snort_ftptelnet.c \ snort_ftptelnet.h \ spp_ftptelnet.c \ spp_ftptelnet.h if BUILD_BUFFER_DUMP libsf_ftptelnet_preproc_la_SOURCES += \ ftptelnet_buffer_dump.c \ ftptelnet_buffer_dump.h endif EXTRA_DIST = \ sf_ftptelnet.vcxproj \ sf_ftptelnet.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/ftptelnet/pp_telnet.h0000644000175000017500000000456114241076005022370 0ustar apoapo/* * pp_telnet.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * Header file for FTPTelnet telnet Module * * This file defines the telnet checking functions * * NOTES: * - 20.09.04: Initial Development. SAS * */ #ifndef __PP_TELNET_H__ #define __PP_TELNET_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* RFC 885 defines an End of Record telnet option */ #define RFC885 /* RFC 1184 defines Abort, Suspend, and End of File telnet optinos */ #define RFC1184 //#include "decode.h" #include "ftpp_ui_config.h" #include "ftpp_si.h" /* define the telnet negotiation codes (TNC) that we're interested in */ #define TNC_IAC 0xFF #define TNC_DONT 0xFE #define TNC_DO 0xFD #define TNC_WONT 0xFC #define TNC_WILL 0xFB #define TNC_SB 0xFA #define TNC_GA 0xF9 #define TNC_EAL 0xF8 #define TNC_EAC 0xF7 #define TNC_AYT 0xF6 #define TNC_AO 0xF5 #define TNC_IP 0xF4 #define TNC_BRK 0xF3 #define TNC_DM 0xF2 #define TNC_NOP 0xF1 #define TNC_SE 0xF0 #ifdef RFC885 #define TNC_EOR 0xEF #endif #ifdef RFC1184 #define TNC_ABOR 0xEE #define TNC_SUSP 0xED #define TNC_EOF 0xEC #endif #define FTPP_APPLY_TNC_ERASE_CMDS 0 #define FTPP_IGNORE_TNC_ERASE_CMDS 1 /* list of function prototypes for this preprocessor */ extern int normalize_telnet(FTPTELNET_GLOBAL_CONF *GlobalConf, TELNET_SESSION *Session, SFSnortPacket *p, int iMode, char ignoreEraseCmd); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftptelnet_buffer_dump.c0000644000175000017500000000434614241075772024767 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file ftptelnet_buffer_dump.c ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during FTPTELNET inspection. */ #include "ftptelnet_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_FTPTELNET_BUFFER_DUMP] = {{"FTP_REQUEST_CMD_LINE", "", 0}, {"FTP_REQUEST_CMD", "", 0}, {"FTP_REQUEST_PARAM", "", 0}, {"FTP_RESPONSE_LINE", "", 0}, {"FTP_RESPONSE_DUMP", "", 0}, {"FTP_RESPONSE_MSG", "", 0}, {"TELNET_DUMP", "", 0}}; void dumpBuffer(FTPTELNET_BUFFER_DUMP type, const char *content, uint16_t len){ buf[type].buf_content = (char *)content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_FTPTELNET_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getFTPTelnetBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/pp_ftp.c0000644000175000017500000021613314241076001021655 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2004-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* pp_ftp.c * * Purpose: FTP sessions contain commands and responses. Certain * commands are vectors of attack. This module checks * those FTP client commands and their parameter values, as * well as the server responses per the configuration. * * Arguments: None * * Effect: Alerts may be raised * * Comments: * */ /* your preprocessor header file goes here */ #define _GNU_SOURCE #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include #ifndef WIN32 #include #include #include #include #else #include #endif #include #include "ftpp_eo_log.h" #include "pp_ftp.h" #include "pp_telnet.h" #include "ftpp_return_codes.h" #include "ftp_cmd_lookup.h" #include "ftp_bounce_lookup.h" #include "spp_ftptelnet.h" //#include "decode.h" #include "snort_debug.h" #include "stream_api.h" //#include "plugbase.h" #ifndef MAXHOSTNAMELEN /* Why doesn't Windows define this? */ #define MAXHOSTNAMELEN 256 #endif #include "ipv6_port.h" #ifdef TARGET_BASED extern int16_t ftp_data_app_id; extern unsigned s_ftpdata_eof_cb_id; #endif #ifdef DUMP_BUFFER #include "ftptelnet_buffer_dump.h" #endif #include "memory_stats.h" #define DEFAULT_MEM_ALLOC 512 /* * Function: getIP959(char **ip_start, * char *last_char, * char term_char, * uint32_t *ipRet, * uint16_t *portRet) * * Purpose: Returns a 32bit IP address and port from an RFC 959 FTP-style * string -- ie, a,b,c,d,p1,p2. Stops checking when term_char * is seen. Used to get address and port information from FTP * PORT command and server response to PASV command. * * Arguments ip_start => Pointer to pointer to the start of string. * Updated to end of IP address if successful. * last_char => End of string * term_char => Character delimiting the end of the address. * ipRet => Return pointer to 32bit address on success * portRet => Return pointer to 16bit port on success * * Returns: int => return code indicating error or success */ static int getIP959( const char **ip_start, const char *last_char, char *term_char, sfaddr_t *ipRet, uint16_t *portRet ) { uint32_t ip=0; uint16_t port=0; int octet=0; const char *this_param = *ip_start; do { int value = 0; do { if (!isdigit((int)(*this_param))) { return FTPP_NON_DIGIT; } value = value * 10 + (*this_param - '0'); this_param++; } while ((this_param < last_char) && (*this_param != ',') && (strchr(term_char, *this_param) == NULL)); if (value > 0xFF) { return FTPP_INVALID_ARG; } if (octet < 4) { ip = (ip << 8) + value; } else { port = (port << 8) + value; } if (strchr(term_char, *this_param) == NULL) this_param++; octet++; } while ((this_param < last_char) && (strchr(term_char, *this_param) == NULL)); if (octet != 6) { return FTPP_MALFORMED_IP_PORT; } ip = htonl(ip); sfip_set_raw(ipRet, &ip, AF_INET); *portRet = port; *ip_start = this_param; return FTPP_SUCCESS; } /* * getIP1639() parses the LPRT command parameters which have this * format (ftyp == e_long_host_port): * * LPRT af,hal,h1,h2,h3,h4...,pal,p1,p2... * LPRT 4,4,132,235,1,2,2,24,131 * LPRT 6,16,16,128,0,...,0,8,128,0,32,12,65,123,2,20,162 * * (The above examples correspond to the EPRT examples below.) * * af (address family) is the IP version. h# and p# are in network * byte order (high byte first). * * This function is called for the LPSV response as well, which * has this format: * * 228 (af,hal,h1,h2,h3,h4...,pal,p1,p2...) */ static int getIP1639 ( const char **ip_start, const char *last_char, char *term_char, sfaddr_t* ipRet, uint16_t *portRet ) { char bytes[21]; /* max of 1+5+3 and 1+17+3 */ const char* tok = *ip_start; unsigned nBytes = 0; bytes[0] = 0; /* first we just try to get a sequence of csv bytes */ while ( nBytes < sizeof(bytes) && tok < last_char ) { char* endPtr = (char*)tok; unsigned long val = strtoul(tok, &endPtr, 10); if ( val > 255 || endPtr == tok || ( *endPtr && *endPtr != ',' && endPtr != last_char ) ) { return FTPP_INVALID_ARG; } bytes[nBytes++] = (uint8_t)val; tok = (endPtr < last_char) ? endPtr + 1 : endPtr; } *ip_start = tok; /* now we check that the we have a valid sequence of */ /* bytes and convert the address and port accordingly */ switch ( bytes[0] ) { case 4: if ( nBytes != 9 || bytes[1] != 4 || bytes[6] != 2 ) return FTPP_INVALID_ARG; { uint32_t ip4_addr = 0; int n; for ( n = 0; n < 4; n++ ) ip4_addr = (ip4_addr << 8) | bytes[n+2]; /* don't call sfip_set_raw() on raw bytes to avoid possible word alignment issues */ ip4_addr = htonl(ip4_addr); sfip_set_raw(ipRet, (void*)&ip4_addr, AF_INET); } *portRet = (bytes[7] << 8) | bytes[8]; break; case 6: if ( nBytes != 21 || bytes[1] != 16 || bytes[18] != 2 ) return FTPP_INVALID_ARG; sfip_set_raw(ipRet, bytes+2, AF_INET6); *portRet = (bytes[19] << 8) | bytes[20]; break; default: return FTPP_INVALID_ARG; } return FTPP_SUCCESS; } /* * getIP2428() parses the EPRT command parameters which have this * format (ftyp == e_extd_host_port): * * EPRT ||address|| * EPRT |1|132.235.1.2|6275| * EPRT |2|1080::8:800:200C:417A|5282| * * Note that the address family is 1|2 (as in RFC 2428), not 4|6 * (as in IP version), nor 2|10 (as in AF_INET[6]). * * This function is called for the EPSV response as well, which * has this format (ftyp == e_int): * * 229 (||||) * * The delimiter may be other than '|' if required to represent * the protocol address, but must be between 33-126 inclusive. * Other delimiters aren't required for IPv{4,6} but we allow * them for flexibility. * * It is assumed that *ip_start points to the first delimiter in * both cases. */ /* * this copy is unfortunate but inet_pton() doesn't * like the delim and the src buf is const so ... */ void CopyField ( char* buf, const char* tok, int max, const char* end, char delim ) { int len = end - tok + 1; char* s; if ( len >= max ) { strncpy(buf, tok, max); buf[max-1] = '\0'; } else { strncpy(buf, tok, len); buf[len] = '\0'; } s = strchr(buf, delim); if ( s ) *s = '\0'; else *buf = '\0'; } static int getIP2428 ( const char **ip_start, const char *last_char, char *term_char, sfaddr_t* ipRet, uint16_t *portRet, FTP_PARAM_TYPE ftyp ) { const char* tok = *ip_start; char delim = *tok; int field = 1, fieldMask = 0; int family = AF_UNSPEC, port = 0; char buf[64]; IP_CLEAR((*ipRet)); *portRet = 0; /* check first delimiter */ if ( delim < 33 || delim > 126 ) return FTPP_INVALID_ARG; while ( tok && tok < last_char && field < 4 ) { int check = (*++tok != delim) ? field : 0; switch ( check ) { case 0: /* empty */ break; case 1: /* check family */ family = atoi(tok); if ( family == 1 ) family = AF_INET; else if ( family == 2 ) family = AF_INET6; else return FTPP_INVALID_ARG; fieldMask |= 1; break; case 2: /* check address */ CopyField(buf, tok, sizeof(buf), last_char, delim); if ( sfaddr_pton(buf, ipRet) != SFIP_SUCCESS || ipRet->family != family ) return FTPP_INVALID_ARG; fieldMask |= 2; break; case 3: /* check port */ port = atoi(tok); if ( port < 0 || port > MAXPORTS-1 ) return FTPP_MALFORMED_IP_PORT; *portRet = port; fieldMask |= 4; break; } /* advance to next field */ tok = strchr(tok, delim); field++; } if (tok) { if ( *tok == delim ) tok++; *ip_start = tok; } else { *ip_start = last_char; } if ( ftyp == e_int && fieldMask == 4 ) /* TBD: do we need to check for bounce if addr present? */ return FTPP_SUCCESS; if ( ftyp == e_extd_host_port && fieldMask == 7 ) return FTPP_SUCCESS; return FTPP_INVALID_ARG; } static int getFTPip( FTP_PARAM_TYPE ftyp, const char **ip_start, const char *last_char, char *term_char, sfaddr_t *ipRet, uint16_t *portRet ) { if ( ftyp == e_host_port ) { return getIP959(ip_start, last_char, term_char, ipRet, portRet); } if ( ftyp == e_long_host_port ) { return getIP1639(ip_start, last_char, term_char, ipRet, portRet); } return getIP2428(ip_start, last_char, term_char, ipRet, portRet, ftyp); } /* * Function: validate_date_format( * FTP_DATE_FMT *ThisFmt, * char **this_param) * * Purpose: Recursively determines whether a date matches the * a valid format. * * Arguments: ThisFmt => Pointer to the current format * this_param => Pointer to start of the portion to validate. * Updated to end of valid section if valid. * * Returns: int => return code indicating error or success * */ static int validate_date_format(FTP_DATE_FMT *ThisFmt, const char **this_param) { int valid_string = 0; int checked_something_else = 0; int checked_next = 0; int iRet = FTPP_ALERT; const char *curr_ch; if (!ThisFmt) return FTPP_INVALID_ARG; if (!this_param || !(*this_param)) return FTPP_INVALID_ARG; curr_ch = *this_param; if (!ThisFmt->empty) { char *format_char = ThisFmt->format_string; do { switch (*format_char) { case 'n': if (!isdigit((int)(*curr_ch))) { /* Return for non-digit */ return FTPP_INVALID_DATE; } curr_ch++; format_char++; break; case 'C': if (!isalpha((int)(*curr_ch))) { /* Return for non-char */ return FTPP_INVALID_DATE; } curr_ch++; format_char++; break; default: if (*curr_ch != *format_char) { /* Return for non-matching char */ return FTPP_INVALID_DATE; } curr_ch++; format_char++; break; } valid_string = 1; } while ((*format_char != '\0') && !isspace((int)(*curr_ch))); if ((*format_char != '\0') && isspace((int)(*curr_ch))) { /* Didn't have enough chars to complete this format */ return FTPP_INVALID_DATE; } } if ((ThisFmt->optional) && !isspace((int)(*curr_ch))) { const char *tmp_ch = curr_ch; iRet = validate_date_format(ThisFmt->optional, &tmp_ch); if (iRet == FTPP_SUCCESS) curr_ch = tmp_ch; } if ((ThisFmt->next_a) && !isspace((int)(*curr_ch))) { const char *tmp_ch = curr_ch; checked_something_else = 1; iRet = validate_date_format(ThisFmt->next_a, &tmp_ch); if (iRet == FTPP_SUCCESS) { curr_ch = tmp_ch; } else if (ThisFmt->next_b) { iRet = validate_date_format(ThisFmt->next_b, &tmp_ch); if (iRet == FTPP_SUCCESS) curr_ch = tmp_ch; } if (ThisFmt->next) { iRet = validate_date_format(ThisFmt->next, &tmp_ch); if (iRet == FTPP_SUCCESS) { curr_ch = tmp_ch; checked_next = 1; } } if (iRet == FTPP_SUCCESS) { *this_param = curr_ch; return iRet; } } if ((!checked_next) && (ThisFmt->next)) { const char *tmp_ch = curr_ch; checked_something_else = 1; iRet = validate_date_format(ThisFmt->next, &tmp_ch); if (iRet == FTPP_SUCCESS) { curr_ch = tmp_ch; checked_next = 1; } } if ((isspace((int)(*curr_ch))) && ((!ThisFmt->next) || checked_next)) { *this_param = curr_ch; return FTPP_SUCCESS; } if (valid_string) { int all_okay = 0; if (checked_something_else) { if (iRet == FTPP_SUCCESS) all_okay = 1; } else { all_okay = 1; } if (all_okay) { *this_param = curr_ch; return FTPP_SUCCESS; } } return FTPP_INVALID_DATE; } /* * Function: validate_param( * Packet *p * char *param * char *end * FTP_PARAM_FMT *param_format, * FTP_SESSION *Session) * * Purpose: Validates the current parameter against the format * specified. * * Arguments: p => Pointer to the current packet * params_begin => Pointer to beginning of parameters * params_end => End of params buffer * param_format => Parameter format specifier for this command * Session => Pointer to the session info * * Returns: int => return code indicating error or success * */ static int validate_param(SFSnortPacket *p, const char *param, const char *end, FTP_PARAM_FMT *ThisFmt, FTP_SESSION *Session) { int iRet; const char *this_param = param; if (param > end) return FTPP_ALERT; switch (ThisFmt->type) { case e_head: /* shouldn't get here, but just in case */ /* this hack is because we do get here! */ this_param--; break; case e_unrestricted: /* strings/filenames only occur as the last param, * so move to the end of the param buffer. */ this_param = end; break; case e_strformat: /* Check for 2 % signs within the parameter for an FTP command * 2 % signs is the magic number per existing rules (24 Sep 2004) */ #define MAX_PERCENT_SIGNS 2 { int numPercents = 0; do { if (*this_param == '%') { numPercents++; if (numPercents >= MAX_PERCENT_SIGNS) { break; } } this_param++; } while ((this_param < end) && (*this_param != '\n')); if (numPercents >= MAX_PERCENT_SIGNS) { /* Alert on string format attack in parameter */ ftp_eo_event_log(Session, FTP_EO_PARAMETER_STR_FORMAT, NULL, NULL); return FTPP_ALERTED; } } break; case e_int: /* check that this_param is all digits up to next space */ { do { if (!isdigit((int)(*this_param))) { /* Alert on non-digit */ return FTPP_INVALID_PARAM; } this_param++; } while ((this_param < end) && (*this_param != ' ') ); } break; case e_number: /* check that this_param is all digits up to next space * and value is between 1 & 255 */ { int iValue = 0; do { if (!isdigit((int)(*this_param))) { /* Alert on non-digit */ return FTPP_INVALID_PARAM; } iValue = iValue * 10 + (*this_param - '0'); this_param++; } while ((this_param < end) && (*this_param != ' ') ); if ((iValue > 255) || (iValue == 0)) return FTPP_INVALID_PARAM; } break; case e_char: /* check that this_param is one of chars specified */ { int bitNum = (*this_param & 0x1f); if (!isalpha((int)(*this_param))) { /* Alert on non-char */ return FTPP_INVALID_PARAM; } else { if (!(ThisFmt->format.chars_allowed & (1 << (bitNum-1))) ) { /* Alert on unexpected char */ return FTPP_INVALID_PARAM; } } this_param++; /* should be a space */ } break; case e_date: /* check that this_param conforms to date specified */ { const char *tmp_ch = this_param; iRet = validate_date_format(ThisFmt->format.date_fmt, &tmp_ch); if (iRet != FTPP_SUCCESS) { /* Alert invalid date */ return FTPP_INVALID_PARAM; } if (!isspace((int)(*tmp_ch))) { /* Alert invalid date -- didn't make it to end of parameter. Overflow attempt? */ return FTPP_INVALID_PARAM; } this_param = tmp_ch; } break; case e_literal: /* check that this_param matches the literal specified */ { const char* s = ThisFmt->format.literal; size_t n = strlen(s); if ( strncmp(this_param, s, n) ) { /* Alert on non-char */ return FTPP_INVALID_PARAM; } this_param += n; } break; /* check that this_param is: */ case e_host_port: /* PORT: h1,h2,h3,h4,p1,p2 */ case e_long_host_port: /* LPRT: af,hal,h1,h2,h3,h4...,pal,p1,p2... */ case e_extd_host_port: /* EPRT: |||| */ { sfaddr_t ipAddr; uint16_t port=0; int ret = getFTPip( ThisFmt->type, &this_param, end, " \n", &ipAddr, &port ); switch (ret) { case FTPP_NON_DIGIT: /* Alert on non-digit */ return FTPP_INVALID_PARAM; break; case FTPP_INVALID_ARG: /* Alert on number > 255 */ return FTPP_INVALID_PARAM; break; case FTPP_MALFORMED_IP_PORT: /* Alert on malformed host-port */ return FTPP_INVALID_PARAM; break; } if ( ThisFmt->type == e_extd_host_port && !sfaddr_is_set(&ipAddr) ) { // actually, we expect no addr in 229 responses, which is // understood to be server address, so we set that here ipAddr = *GET_SRC_IP(p); } if ((Session->client_conf->bounce.on) && (Session->client_conf->bounce.alert)) { if (!IP_EQUALITY(&ipAddr, GET_SRC_IP(p))) { int alert = 1; FTP_BOUNCE_TO *BounceTo = ftp_bounce_lookup_find( Session->client_conf->bounce_lookup, &ipAddr, &iRet); if (BounceTo) { if (BounceTo->portlo) { if (BounceTo->porthi) { if ((port >= BounceTo->portlo) && (port <= BounceTo->porthi)) alert = 0; } else { if (port == BounceTo->portlo) alert = 0; } } } /* Alert on invalid IP address for PORT */ if (alert) { ftp_eo_event_log(Session, FTP_EO_BOUNCE, NULL, NULL); /* Return here -- because we will likely want to * inspect the data traffic over a bounced data * connection */ return FTPP_PORT_ATTACK; } } } Session->clientIP = ipAddr; Session->clientPort = port; Session->data_chan_state |= DATA_CHAN_PORT_CMD_ISSUED; if (Session->data_chan_state & DATA_CHAN_PASV_CMD_ISSUED) { /* * If there was a PORT command previously in * a series of pipelined requests, this * cancels it. */ Session->data_chan_state &= ~DATA_CHAN_PASV_CMD_ISSUED; } IP_CLEAR(Session->serverIP); Session->serverPort = 0; } break; } ThisFmt->next_param = this_param; return FTPP_SUCCESS; } /* * Function: check_ftp_param_validity( * Packet *p, * char *params_begin, * char *params_end, * FTP_PARAM_FMT *param_format, * FTP_SESSION *Session) * * Purpose: Recursively determines whether each of the parameters for * an FTP command are valid. * * Arguments: p => Pointer to the current packet * params_begin => Pointer to beginning of parameters * params_end => End of params buffer * param_format => Parameter format specifier for this command * Session => Pointer to the session info * * Returns: int => return code indicating error or success * */ static int check_ftp_param_validity(SFSnortPacket *p, const char *params_begin, const char *params_end, FTP_PARAM_FMT *param_format, FTP_SESSION *Session) { int iRet = FTPP_ALERT; FTP_PARAM_FMT *ThisFmt = param_format; FTP_PARAM_FMT *NextFmt; const char *this_param = params_begin; if (!param_format) return FTPP_INVALID_ARG; if (!params_begin && !ThisFmt->next_param_fmt && ThisFmt->optional_fmt) return FTPP_SUCCESS; /* no param is allowed in this case */ if (!params_begin && (ThisFmt->next_param_fmt && ThisFmt->next_param_fmt->type == e_strformat)) return FTPP_SUCCESS; /* string format check of non existent param */ if (!params_begin) return FTPP_INVALID_ARG; if ((!ThisFmt->next_param_fmt) && (params_begin >= params_end)) return FTPP_SUCCESS; ThisFmt->next_param = params_begin; if (ThisFmt->optional_fmt) { /* Check against optional */ iRet = validate_param(p, this_param, params_end, ThisFmt->optional_fmt, Session); if (iRet == FTPP_SUCCESS) { const char *next_param; NextFmt = ThisFmt->optional_fmt; next_param = NextFmt->next_param+1; iRet = check_ftp_param_validity(p, next_param, params_end, NextFmt, Session); if (iRet == FTPP_SUCCESS) { this_param = NextFmt->next_param+1; } } } if ((iRet != FTPP_SUCCESS) && (ThisFmt->choices)) { /* Check against choices -- one of many */ int i; int valid = 0; for (i=0;inumChoices && !valid;i++) { /* Try choice [i] */ iRet = validate_param(p, this_param, params_end, ThisFmt->choices[i], Session); if (iRet == FTPP_SUCCESS) { const char *next_param; NextFmt = ThisFmt->choices[i]; next_param = NextFmt->next_param+1; iRet = check_ftp_param_validity(p, next_param, params_end, NextFmt, Session); if (iRet == FTPP_SUCCESS) { this_param = NextFmt->next_param+1; valid = 1; break; } } } } else if ((iRet != FTPP_SUCCESS) && (ThisFmt->next_param_fmt)) { /* Check against next param */ iRet = validate_param(p, this_param, params_end, ThisFmt->next_param_fmt, Session); if (iRet == FTPP_SUCCESS) { const char *next_param; NextFmt = ThisFmt->next_param_fmt; next_param = NextFmt->next_param+1; iRet = check_ftp_param_validity(p, next_param, params_end, NextFmt, Session); if (iRet == FTPP_SUCCESS) { this_param = NextFmt->next_param+1; } } } else if ((iRet != FTPP_SUCCESS) && (!ThisFmt->next_param_fmt) && this_param) { iRet = FTPP_SUCCESS; } if (iRet == FTPP_SUCCESS) { ThisFmt->next_param = this_param; } return iRet; } /* * Function: initialize_ftp(FTP_SESSION *Session, Packet *p, int iMode) * * Purpose: Initializes the state machine for checking an FTP packet. * Does normalization checks. * * Arguments: Session => Pointer to session info * p => pointer to the current packet struct * iMode => Mode indicating server or client checks * * Returns: int => return code indicating error or success * */ int initialize_ftp(FTP_SESSION *Session, SFSnortPacket *p, int iMode) { int iRet; const unsigned char *read_ptr = p->payload; FTP_CLIENT_REQ *req; char ignoreTelnetErase = FTPP_APPLY_TNC_ERASE_CMDS; FTPTELNET_GLOBAL_CONF *global_conf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(Session->global_conf, Session->policy_id); /* Normalize this packet ala telnet */ if (((iMode == FTPP_SI_CLIENT_MODE) && (Session->client_conf->ignore_telnet_erase_cmds.on == 1)) || ((iMode == FTPP_SI_SERVER_MODE) && (Session->server_conf->ignore_telnet_erase_cmds.on == 1)) ) ignoreTelnetErase = FTPP_IGNORE_TNC_ERASE_CMDS; iRet = normalize_telnet(global_conf, NULL, p, iMode, ignoreTelnetErase); if (iRet != FTPP_SUCCESS && iRet != FTPP_NORMALIZED) { if (iRet == FTPP_ALERT) { if (global_conf->telnet_config->detect_anomalies) { ftp_eo_event_log(Session, FTP_EO_EVASIVE_TELNET_CMD, NULL, NULL); } } return iRet; } if (_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE)) { /* Normalized data will always be in decode buffer */ if ( ((Session->client_conf->telnet_cmds.alert) && (iMode == FTPP_SI_CLIENT_MODE)) || ((Session->server_conf->telnet_cmds.alert) && (iMode == FTPP_SI_SERVER_MODE)) ) { /* alert -- FTP channel with telnet commands */ ftp_eo_event_log(Session, FTP_EO_TELNET_CMD, NULL, NULL); return FTPP_ALERT; /* Nothing else to do since we alerted */ } read_ptr = _dpd.altBuffer->data; } if (iMode == FTPP_SI_CLIENT_MODE) { req = &Session->client.request; #ifdef DUMP_BUFFER dumpBuffer(FTP_REQUEST_CMD_LINE,req->cmd_line,req->cmd_line_size); dumpBuffer(FTP_REQUEST_CMD,req->cmd_begin,req->cmd_size); dumpBuffer(FTP_REQUEST_PARAM,req->param_begin,req->param_size); #endif } else if (iMode == FTPP_SI_SERVER_MODE) { FTP_SERVER_RSP *rsp = &Session->server.response; req = (FTP_CLIENT_REQ *)rsp; #ifdef DUMP_BUFFER dumpBuffer(FTP_RESPONSE_LINE,rsp->rsp_line,rsp->rsp_line_size); dumpBuffer(FTP_RESPONSE_DUMP,rsp->rsp_begin,rsp->rsp_size); dumpBuffer(FTP_RESPONSE_MSG,rsp->msg_begin,rsp->msg_size); #endif } else return FTPP_INVALID_ARG; /* Set the beginning of the pipeline to the start of the * (normalized) buffer */ req->pipeline_req = (const char *)read_ptr; return FTPP_SUCCESS; } static inline void prepareForEncryption(FTP_SESSION *Session, FTPTELNET_GLOBAL_CONF *global_conf, int new_encr_state) { Session->encr_state = new_encr_state; Session->encr_state_chello = true; if (global_conf->encrypted.alert) { /* Alert on encrypted channel */ ftp_eo_event_log(Session, FTP_EO_ENCRYPTED, NULL, NULL); } } /* * Function: do_stateful_checks(FTP_SESSION *Session, Packet *p, * FTP_CLIENT_REQ *req, int rsp_code) * * Purpose: Handle stateful checks and state updates for FTP response * packets. * * Arguments: Session => Pointer to session info * p => Pointer to the current packet struct * req => Pointer to current response from packet * (this function may be called multiple * times for pipelined requests). * rsp_code => Integer response value for server response * * Returns: int => return code indicating error or success * */ static int do_stateful_checks(FTP_SESSION *Session, SFSnortPacket *p, FTP_CLIENT_REQ *req, int rsp_code) { int iRet = FTPP_SUCCESS; FTPTELNET_GLOBAL_CONF *global_conf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(Session->global_conf, Session->policy_id); //if (Session->server_conf->data_chan) { if (rsp_code == 226) { /* Just ignore this code -- end of transfer... * If we saw all the other dat for this channel * Session->data_chan_state should be NO_STATE. */ } else if (Session->data_chan_state & DATA_CHAN_PASV_CMD_ISSUED) { if (Session->ftp_cmd_pipe_index == Session->data_chan_index) { if (Session->data_xfer_index == 0) Session->ftp_cmd_pipe_index = 1; Session->data_chan_index = 0; if ( rsp_code >= 227 && rsp_code <= 229 ) { sfaddr_t ipAddr; uint16_t port=0; const char *ip_begin = req->param_begin; IP_CLEAR(ipAddr); Session->data_chan_state &= ~DATA_CHAN_PASV_CMD_ISSUED; Session->data_chan_state |= DATA_CHAN_PASV_CMD_ACCEPT; Session->data_chan_index = 0; /* Interpret response message to identify the * Server IP/Port. Server response is inside * a pair of ()s. Find the left (, and use same * means to find IP/Port as is done for the PORT * command. */ if (req->param_size != 0) { while ((ip_begin < req->param_end) && (*ip_begin != '(')) { ip_begin++; } } if (ip_begin < req->param_end) { FTP_PARAM_TYPE ftyp = /* e_int is used in lieu of adding a new value to the * enum because this case doesn't correspond to a * validation config option; it could effectively be * replaced with an additional bool arg to getFTPip() that * differentiated between commands and responses, but * this distinction is only required for EPSV rsps. */ (rsp_code == 229) ? e_int : (rsp_code == 228 ? e_long_host_port : e_host_port); ip_begin++; iRet = getFTPip( ftyp, &ip_begin, req->param_end, ")", &ipAddr, &port ); if (iRet == FTPP_SUCCESS) { if (!sfaddr_is_set(&ipAddr)) IP_COPY_VALUE(Session->serverIP, GET_SRC_IP(p)); else { Session->serverIP = ipAddr; } Session->serverPort = port; IP_COPY_VALUE(Session->clientIP, GET_DST_IP(p)); Session->clientPort = 0; #ifdef TARGET_BASED if ((_dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false) > 0) || !(Session->server_conf->data_chan)) { FTP_DATA_SESSION *ftpdata = FTPDataSessionNew(p); if (ftpdata) { int result; /* This is a passive data transfer */ ftpdata->mode = FTPP_XFER_PASSIVE; ftpdata->data_chan = Session->server_conf->data_chan; /* Store the FTP_SESSION in FTP_DATA_SESSION, needed when FTP_DATA_SESSION is freed. * This is needed to handle pruning of FTP_DATA_SESSION */ ftpdata->ftpssn = Session; /*If a FTP_DATA_SESSION already exists, the FTP_SESSION reference in that should be removed*/ if(Session->datassn) { FTP_DATA_SESSION * ssn = Session->datassn; ssn->ftpssn = NULL; } Session->datassn = ftpdata; /* Call into Streams to mark data channel as ftp-data */ result = _dpd.streamAPI->set_application_protocol_id_expected_preassign_callback( p, IP_ARG(Session->clientIP), Session->clientPort, IP_ARG(Session->serverIP), Session->serverPort, GET_IPH_PROTO(p), ftp_data_app_id, PP_FTPTELNET, (void *)ftpdata, &FTPDataSessionFree, s_ftpdata_eof_cb_id, SE_EOF, &p->expectedSession); if (result < 0) FTPDataSessionFree(ftpdata); } } else if (Session->server_conf->data_chan) #else if (Session->server_conf->data_chan) #endif { /* Call into Streams to mark data channel as something * to ignore. */ _dpd.sessionAPI->ignore_session(p, IP_ARG(Session->clientIP), Session->clientPort, IP_ARG(Session->serverIP), Session->serverPort, GET_IPH_PROTO(p), PP_FTPTELNET, SSN_DIR_BOTH, 0 /* Not permanent */, &p->expectedSession ); } } } else { iRet = FTPP_MALFORMED_FTP_RESPONSE; } } else { Session->data_chan_index = 0; Session->data_chan_state &= ~DATA_CHAN_PASV_CMD_ISSUED; } } } else if (Session->data_chan_state & DATA_CHAN_PORT_CMD_ISSUED) { if (Session->ftp_cmd_pipe_index == Session->data_chan_index) { if (Session->data_xfer_index == 0) Session->ftp_cmd_pipe_index = 1; Session->data_chan_index = 0; if (rsp_code == 200) { Session->data_chan_state &= ~DATA_CHAN_PORT_CMD_ISSUED; Session->data_chan_state |= DATA_CHAN_PORT_CMD_ACCEPT; Session->data_chan_index = 0; if (sfaddr_is_set(&Session->clientIP)) { /* This means we're not in passive mode. */ /* Server is listening/sending from its own IP, * FTP Port -1 */ /* Client IP, Port specified via PORT command */ IP_COPY_VALUE(Session->serverIP, GET_SRC_IP(p)); /* Can't necessarily guarantee this, especially * in the case of a proxy'd connection where the * data channel might not be on port 20 (or server * port-1). Comment it out for now. */ /* Session->serverPort = ntohs(p->tcph->th_sport) -1; */ #ifdef TARGET_BASED if ((_dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false) > 0) || !(Session->server_conf->data_chan)) { FTP_DATA_SESSION *ftpdata = FTPDataSessionNew(p); if (ftpdata) { int result; /* This is a active data transfer */ ftpdata->mode = FTPP_XFER_ACTIVE; ftpdata->data_chan = Session->server_conf->data_chan; /* Store the FTP_SESSION in FTP_DATA_SESSION, needed when FTP_DATA_SESSION is freed. * This is needed to handle pruning of FTP_DATA_SESSION */ ftpdata->ftpssn = Session; /*If a FTP_DATA_SESSION already exists, the FTP_SESSION reference in that should be removed*/ if(Session->datassn) { FTP_DATA_SESSION * ssn = Session->datassn; ssn->ftpssn = NULL; } Session->datassn = ftpdata; /* Call into Streams to mark data channel as ftp-data */ result = _dpd.streamAPI->set_application_protocol_id_expected_preassign_callback( p, IP_ARG(Session->serverIP), Session->serverPort, IP_ARG(Session->clientIP), Session->clientPort, GET_IPH_PROTO(p), ftp_data_app_id, PP_FTPTELNET, (void *)ftpdata, &FTPDataSessionFree, s_ftpdata_eof_cb_id, SE_EOF, &p->expectedSession); if (result < 0) FTPDataSessionFree(ftpdata); } } else if (Session->server_conf->data_chan) #else if (Session->server_conf->data_chan) #endif { /* Call into Streams to mark data channel as something * to ignore. */ _dpd.sessionAPI->ignore_session(p, IP_ARG(Session->serverIP), Session->serverPort, IP_ARG(Session->clientIP), Session->clientPort, GET_IPH_PROTO(p), PP_FTPTELNET, SSN_DIR_BOTH, 0 /* Not permanent */, &p->expectedSession ); } } } else if (Session->ftp_cmd_pipe_index == Session->data_chan_index) { Session->data_chan_index = 0; Session->data_chan_state &= ~DATA_CHAN_PORT_CMD_ISSUED; } } } else if (Session->data_chan_state & DATA_CHAN_REST_CMD_ISSUED) { if (Session->ftp_cmd_pipe_index == Session->data_xfer_index) { if (Session->data_chan_index == 0) Session->ftp_cmd_pipe_index = 1; Session->data_xfer_index = 0; if (rsp_code == 350) { #ifdef TARGET_BASED FTP_DATA_SESSION *ftpdata = Session->datassn; if(ftpdata) ftpdata->flags |= FTPDATA_FLG_REST; #endif } else Session->rest_cmd_offset= 0; Session->data_chan_index = 0; Session->data_chan_state &= ~DATA_CHAN_REST_CMD_ISSUED; } } else if (Session->data_chan_state & DATA_CHAN_XFER_CMD_ISSUED) { if (Session->ftp_cmd_pipe_index == Session->data_xfer_index) { if (Session->data_chan_index == 0) Session->ftp_cmd_pipe_index = 1; Session->data_xfer_index = 0; if ((rsp_code == 150) || (rsp_code == 125)) { Session->data_chan_state = DATA_CHAN_XFER_STARTED; } /* Clear the session info for next transfer --> * reset host/port */ IP_CLEAR(Session->serverIP); IP_CLEAR(Session->clientIP); Session->serverPort = Session->clientPort = 0; Session->data_chan_state = NO_STATE; } } } /* if (Session->server_conf->data_chan) */ if (global_conf->encrypted.on && rsp_code == 234) { /* any of these states is now anticipatory of the client hello */ switch(Session->encr_state) { case AUTH_TLS_CMD_ISSUED: prepareForEncryption(Session, global_conf, AUTH_TLS_ENCRYPTED); DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP stream is now TLS encrypted\n");); break; case AUTH_SSL_CMD_ISSUED: prepareForEncryption(Session, global_conf, AUTH_SSL_ENCRYPTED); DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP stream is now SSL encrypted\n");); break; case AUTH_UNKNOWN_CMD_ISSUED: default: // encr_state got confused, but we do have a 234 prepareForEncryption(Session, global_conf, AUTH_UNKNOWN_ENCRYPTED); DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP stream is now encrypted\n");); break; case AUTH_TLS_ENCRYPTED: case AUTH_SSL_ENCRYPTED: case AUTH_UNKNOWN_ENCRYPTED: // already been through here break; } } /* if (global_conf->encrypted.on && rsp_code == 234) */ return iRet; } /* * Function: check_ftp(FTP_SESSION *Session, Packet *p, int iMode) * * Purpose: Handle some trivial validation checks of an FTP packet. Namely, * check argument length and some protocol enforcement. * * Wishful: This results in exposing the FTP command (and looking * at the results) to the rules layer. * * Arguments: Session => Pointer to session info * p => pointer to the current packet struct * iMode => Mode indicating server or client checks * * Returns: int => return code indicating error or success * */ #define NUL 0x00 #define CR 0x0d #define LF 0x0a #define SP 0x20 #define DASH 0x2D #define FTP_CMD_OK 0 #define FTP_CMD_INV 1 #define FTP_RESPONSE_INV 1 #define FTP_RESPONSE 2 #define FTP_RESPONSE_2BCONT 2 #define FTP_RESPONSE_CONT 3 #define FTP_RESPONSE_ENDCONT 4 int check_ftp(FTP_SESSION *ftpssn, SFSnortPacket *p, int iMode) { int iRet = FTPP_SUCCESS; int encrypted = 0; int space = 0; long state = FTP_CMD_OK; int rsp_code = 0; FTPTELNET_GLOBAL_CONF *global_conf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(ftpssn->global_conf, ftpssn->policy_id); FTP_CLIENT_REQ *req; FTP_CMD_CONF *CmdConf = NULL; #ifdef TARGET_BASED FTP_DATA_SESSION *datassn; #endif const unsigned char *read_ptr; const unsigned char *end = p->payload + p->payload_size; if (_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE)) end = _dpd.altBuffer->data + _dpd.altBuffer->len; if (iMode == FTPP_SI_CLIENT_MODE) { req = &ftpssn->client.request; ftpssn->ftp_cmd_pipe_index = 1; } else if (iMode == FTPP_SI_SERVER_MODE) { FTP_SERVER_RSP *rsp = &ftpssn->server.response; req = (FTP_CLIENT_REQ *)rsp; } else return FTPP_INVALID_ARG; while (req->pipeline_req) { state = FTP_CMD_OK; /* Starts at the beginning of the buffer/line, * so next up is a command */ read_ptr = (const unsigned char *)req->pipeline_req; /* but first we ignore leading white space */ while ( (read_ptr < end) && (iMode == FTPP_SI_CLIENT_MODE) && isspace(*read_ptr) ) read_ptr++; // ignore extra \r\n emitted by some clients if ( read_ptr == end ) break; req->cmd_begin = (const char *)read_ptr; while ((read_ptr < end) && (*read_ptr != SP) && (*read_ptr != CR) && (*read_ptr != LF) && /* Check for LF when there wasn't a CR, * protocol violation, but accepted by * some servers. */ (*read_ptr != DASH)) { /* If the first char is a digit this is a response * in server mode. */ if (iMode == FTPP_SI_SERVER_MODE) { if (isdigit(*read_ptr)) { if (state != FTP_RESPONSE_INV) { state = FTP_RESPONSE; } } else if (!isascii(*read_ptr)) { /* Non-ascii char here? Bad response */ state = FTP_RESPONSE_INV; } } /* Or, if this is not a char, this is garbage in client mode */ else if (!isalpha(*read_ptr) && (iMode == FTPP_SI_CLIENT_MODE)) { state = FTP_CMD_INV; } read_ptr++; } req->cmd_end = (const char *)read_ptr; req->cmd_size = req->cmd_end - req->cmd_begin; if (iMode == FTPP_SI_CLIENT_MODE) { if ( (req->cmd_size > ftpssn->server_conf->max_cmd_len) || (req->cmd_size < MIN_CMD) || (state == FTP_CMD_INV) ) { /* Uh, something is very wrong... * nonalpha char seen or cmd is bad length. * See if this might be encrypted, ie, non-alpha bytes. */ const unsigned char *ptr = (const unsigned char *)req->cmd_begin; while (ptr < (const unsigned char *)req->cmd_end) { if (!isalpha((int)(*ptr))) { if (!isascii((int)(*ptr)) || !isprint((int)(*ptr))) { encrypted = 1; } break; } ptr++; } } if (encrypted) { /* If the session wasn't already marked as encrypted... * Don't want to double-alert if we've already * determined the session is encrypted and we're * checking encrypted sessions. */ if (ftpssn->encr_state == 0) { ftpssn->encr_state = AUTH_UNKNOWN_ENCRYPTED; if (global_conf->encrypted.alert) { /* Alert on encrypted channel */ ftp_eo_event_log(ftpssn, FTP_EO_ENCRYPTED, NULL, NULL); } if (!global_conf->check_encrypted_data) { /* Mark this session & packet as one to ignore */ _dpd.sessionAPI->stop_inspection(p->stream_session, p, SSN_DIR_BOTH, -1, 0); } DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP client stream is now encrypted\n");); } break; } else { /* * Check the list of valid FTP commands as * supplied in ftpssn. */ if ( req->cmd_size > ftpssn->server_conf->max_cmd_len ) { /* Alert, cmd not found */ ftp_eo_event_log(ftpssn, FTP_EO_INVALID_CMD, NULL, NULL); state = FTP_CMD_INV; } else { CmdConf = ftp_cmd_lookup_find(ftpssn->server_conf->cmd_lookup, req->cmd_begin, req->cmd_size, &iRet); if ((iRet == FTPP_NOT_FOUND) || (CmdConf == NULL)) { /* Alert, cmd not found */ ftp_eo_event_log(ftpssn, FTP_EO_INVALID_CMD, NULL, NULL); state = FTP_CMD_INV; } else { /* In case we were encrypted, but aren't now */ ftpssn->encr_state = 0; } } } } else if (iMode == FTPP_SI_SERVER_MODE) { if (state == FTP_CMD_INV) state = FTP_RESPONSE_INV; if ( (req->cmd_size != 3) || (state == FTP_RESPONSE_INV) ) { /* Uh, something is very wrong... * nondigit char seen or resp code is not 3 chars. * See if this might be encrypted, ie, non-alpha bytes. */ const char *ptr = req->cmd_begin; while (ptr < req->cmd_end) { if (!isdigit((int)(*ptr))) { if (!isascii((int)(*ptr)) || !isprint((int)(*ptr))) { encrypted = 1; } break; } ptr++; } } if (encrypted) { /* If the session wasn't already marked as encrypted... * Don't want to double-alert if we've already * determined the session is encrypted and we're * checking encrypted sessions. */ if (ftpssn->encr_state == 0) { ftpssn->encr_state = AUTH_UNKNOWN_ENCRYPTED; if (global_conf->encrypted.alert) { /* Alert on encrypted channel */ ftp_eo_event_log(ftpssn, FTP_EO_ENCRYPTED, NULL, NULL); } if (!global_conf->check_encrypted_data) { /* Mark this session & packet as one to ignore */ _dpd.sessionAPI->stop_inspection(p->stream_session, p, SSN_DIR_BOTH, -1, 0); } DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP server stream is now encrypted\n");); } break; } else { /* In case we were encrypted, but aren't now */ if ((ftpssn->encr_state == AUTH_TLS_ENCRYPTED) || (ftpssn->encr_state == AUTH_SSL_ENCRYPTED) || (ftpssn->encr_state == AUTH_UNKNOWN_ENCRYPTED)) { ftpssn->encr_state = 0; } /* Otherwise, might have an encryption command pending */ } if (read_ptr < end) { if (*read_ptr != DASH) { const unsigned char *resp_begin = (const unsigned char *)req->cmd_begin; const unsigned char *resp_end = (const unsigned char *)req->cmd_end; if (resp_end - resp_begin >= 3) { if (isdigit(*(resp_begin)) && isdigit(*(resp_begin+1)) && isdigit(*(resp_begin+2)) ) { rsp_code = ( (*(resp_begin) - '0') * 100 + (*(resp_begin+1) - '0') * 10 + (*(resp_begin+2) - '0') ); if (rsp_code == ftpssn->server.response.state) { /* End of continued response */ state = FTP_RESPONSE_ENDCONT; ftpssn->server.response.state = 0; } else { /* Single line response */ state = FTP_RESPONSE; } } } if (ftpssn->server.response.state != 0) { req->cmd_begin = NULL; req->cmd_end = NULL; if (*read_ptr != SP) read_ptr--; state = FTP_RESPONSE_CONT; } } else if ((state == FTP_RESPONSE) && (*read_ptr == DASH)) { const unsigned char *resp_begin = (const unsigned char *)req->cmd_begin; if (isdigit(*(resp_begin)) && isdigit(*(resp_begin+1)) && isdigit(*(resp_begin+2)) ) { int resp_code = ( (*(resp_begin) - '0') * 100 + (*(resp_begin+1) - '0') * 10 + (*(resp_begin+2) - '0') ); if (resp_code == ftpssn->server.response.state) { /* Continuation of previous response */ state = FTP_RESPONSE_CONT; } else { /* Start of response, state stays as -2 */ state = FTP_RESPONSE_2BCONT; ftpssn->server.response.state = resp_code; rsp_code = resp_code; } } else { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "invalid FTP response code.");); ftpssn->server.response.state = FTP_RESPONSE_INV; } } } } if (read_ptr < end) { if (*read_ptr == SP) { space = 1; } read_ptr++; /* Move past the space, dash, or CR */ } /* If there is anything left... */ if (read_ptr < end) { /* Look for an LF --> implies no parameters/message */ if (*read_ptr == LF) { read_ptr++; req->param_begin = NULL; req->param_end = NULL; } else if (!space && ftpssn->server.response.state == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Missing LF from end of FTP command\n");); } else { /* Now grab the command parameters/response message * read_ptr < end already checked */ req->param_begin = (const char *)read_ptr; if ((read_ptr = memchr(read_ptr, CR, end - read_ptr)) == NULL) read_ptr = end; req->param_end = (const char *)read_ptr; read_ptr++; if (read_ptr < end) { /* Cool, got the end of the parameters, move past * the LF, so we can process the next one in * the pipeline. */ if (*read_ptr == LF) { read_ptr++; } else { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Missing LF from end of FTP command with params\n");); } } } } else { /* Nothing left --> no parameters/message. Not even an LF */ req->param_begin = NULL; req->param_end = NULL; DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Missing LF from end of FTP command sans params\n");); } /* Set the pointer for the next request/response * in the pipeline. */ if (read_ptr < end) req->pipeline_req = (const char *)read_ptr; else req->pipeline_req = NULL; req->param_size = req->param_end - req->param_begin; switch (state) { case FTP_CMD_INV: DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Illegal FTP command found: %.*s\n", req->cmd_size, req->cmd_begin)); iRet = FTPP_ALERT; break; case FTP_RESPONSE: /* Response */ DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP response: code: %.*s : M len %d : M %.*s\n", req->cmd_size, req->cmd_begin, req->param_size, req->param_size, req->param_begin)); if ((ftpssn->client_conf->max_resp_len > 0) && (req->param_size > ftpssn->client_conf->max_resp_len)) { /* Alert on response message overflow */ ftp_eo_event_log(ftpssn, FTP_EO_RESPONSE_LENGTH_OVERFLOW, NULL, NULL); iRet = FTPP_ALERT; } #ifdef TARGET_BASED if (!(ftpssn->flags & FTP_FLG_MALWARE_ENABLED) && _dpd.fileAPI->file_config_malware_check(p->stream_session, ftp_data_app_id)) { ftpssn->flags |= FTP_FLG_MALWARE_ENABLED; } #endif if (global_conf->inspection_type == FTPP_UI_CONFIG_STATEFUL) { int newRet = do_stateful_checks(ftpssn, p, req, rsp_code); if (newRet != FTPP_SUCCESS) iRet = newRet; } break; case FTP_RESPONSE_CONT: /* Response continued */ DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP response: continuation of code: %d : M len %d : M %.*s\n", ftpssn->server.response.state, req->param_size, req->param_size, req->param_begin)); if ((ftpssn->client_conf->max_resp_len > 0) && (req->param_size > ftpssn->client_conf->max_resp_len)) { /* Alert on response message overflow */ ftp_eo_event_log(ftpssn, FTP_EO_RESPONSE_LENGTH_OVERFLOW, NULL, NULL); iRet = FTPP_ALERT; } break; case FTP_RESPONSE_ENDCONT: /* Continued response end */ DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP response: final continue of code: %.*s : M len %d : " "M %.*s\n", req->cmd_size, req->cmd_begin, req->param_size, req->param_size, req->param_begin)); if ((ftpssn->client_conf->max_resp_len > 0) && (req->param_size > ftpssn->client_conf->max_resp_len)) { /* Alert on response message overflow */ ftp_eo_event_log(ftpssn, FTP_EO_RESPONSE_LENGTH_OVERFLOW, NULL, NULL); iRet = FTPP_ALERT; } break; default: DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP command: CMD: %.*s : " "P len %d : P %.*s\n", req->cmd_size, req->cmd_begin, req->param_size, req->param_size, req->param_begin)); if (CmdConf) { if ((req->param_size > CmdConf->max_param_len)) { /* Alert on param length overrun */ ftp_eo_event_log(ftpssn, FTP_EO_PARAMETER_LENGTH_OVERFLOW, NULL, NULL); DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP command: %.*s" "parameter length overrun %d > %d \n", req->cmd_size, req->cmd_begin, req->param_size, CmdConf->max_param_len)); iRet = FTPP_ALERT; } if (CmdConf->data_chan_cmd) { ftpssn->data_chan_state |= DATA_CHAN_PASV_CMD_ISSUED; ftpssn->data_chan_index = ftpssn->ftp_cmd_pipe_index; if (ftpssn->data_chan_state & DATA_CHAN_PORT_CMD_ISSUED) { /* * If there was a PORT command previously in * a series of pipelined requests, this * cancels it. */ ftpssn->data_chan_state &= ~DATA_CHAN_PORT_CMD_ISSUED; } } else if (CmdConf->data_rest_cmd) { if ((req->param_begin != NULL) && (req->param_size > 0)) { char *return_ptr = 0; unsigned long offset = 0; errno = 0; offset = strtoul(req->param_begin, &return_ptr, 10); if ((errno == ERANGE || errno == EINVAL) || (offset > 0)) { ftpssn->data_chan_state |= DATA_CHAN_REST_CMD_ISSUED; ftpssn->data_xfer_index = ftpssn->ftp_cmd_pipe_index; ftpssn->rest_cmd_offset = offset; } } } else if (CmdConf->data_xfer_cmd) { #ifdef TARGET_BASED /* If we are not ignoring the data channel OR file processing is enabled */ if (!ftpssn->server_conf->data_chan || (_dpd.fileAPI->get_max_file_depth(_dpd.getCurrentSnortConfig(), false) > -1)) { /* The following check cleans up filename for failed data * transfers. If the transfer had been successful the * filename pointer would have been handed off to the * FTP_DATA_SESSION for tracking. */ if (ftpssn->filename) { _dpd.snortFree(ftpssn->filename, (strlen(ftpssn->filename) + 1), PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); ftpssn->filename = NULL; ftpssn->file_xfer_info = FTPP_FILE_IGNORE; } // Get the file name and set direction of the get/put request. // Request could have been sent without parameters, i.e. filename, // so make sure something is there. if (((req->param_begin != NULL) && (req->param_size > 0)) && (CmdConf->file_get_cmd || CmdConf->file_put_cmd)) { ftpssn->filename = (char *)_dpd.snortAlloc(1, req->param_size + 1, PP_FTPTELNET, PP_MEM_CATEGORY_SESSION); ftp_telnet_stats.heap_memory += req->param_size+1; if (ftpssn->filename) { memcpy(ftpssn->filename, req->param_begin, req->param_size); ftpssn->filename[req->param_size] = '\0'; ftpssn->file_xfer_info = req->param_size; if (ftpssn->flags & FTP_FLG_MALWARE_ENABLED) { IP_COPY_VALUE(ftpssn->control_clientIP, GET_SRC_IP(p)); IP_COPY_VALUE(ftpssn->control_serverIP, GET_DST_IP(p)); ftpssn->control_serverPort = ntohs(p->tcp_header->destination_port); ftpssn->control_clientPort = ntohs(p->tcp_header->source_port); #ifdef TARGET_BASED datassn = (FTP_DATA_SESSION *)ftpssn->datassn; if(datassn) { char *file_name = strrchr(ftpssn->filename, '/'); if(!file_name) file_name = ftpssn->filename; datassn->path_hash = _dpd.fileAPI->str_to_hash((uint8_t *)file_name, strlen(file_name)); } #endif } } else { _dpd.errMsg("check_ftp: " "Memory allocation failed for filename in ftpssn\n"); } // 0 for Download, 1 for Upload ftpssn->data_xfer_dir = CmdConf->file_get_cmd ? false : true; FTP_DATA_SESSION *ftpdata = ftpssn->datassn; if(ftpdata) ftpdata->direction = ftpssn->data_xfer_dir; } else { ftpssn->file_xfer_info = FTPP_FILE_IGNORE; } } #endif ftpssn->data_chan_state |= DATA_CHAN_XFER_CMD_ISSUED; ftpssn->data_xfer_index = ftpssn->ftp_cmd_pipe_index; } else if (CmdConf->encr_cmd) { if (req->param_begin && (req->param_size > 0) && ((req->param_begin[0] == 'T') || (req->param_begin[0] == 't'))) { ftpssn->encr_state = AUTH_TLS_CMD_ISSUED; } else if (req->param_begin && (req->param_size > 0) && ((req->param_begin[0] == 'S') || (req->param_begin[0] == 's'))) { ftpssn->encr_state = AUTH_SSL_CMD_ISSUED; } else { ftpssn->encr_state = AUTH_UNKNOWN_CMD_ISSUED; } } if (CmdConf->check_validity) { iRet = check_ftp_param_validity(p, req->param_begin, req->param_end, CmdConf->param_format, ftpssn); /* If negative, haven't already alerted on violation */ if (iRet < 0) { /* Set Alert on malformatted parameter */ ftp_eo_event_log(ftpssn, FTP_EO_MALFORMED_PARAMETER, NULL, NULL); iRet = FTPP_ALERT; break; } else if (iRet > 0) { /* Already alerted -- ie, string format attack. */ break; } } } break; } if (iMode == FTPP_SI_CLIENT_MODE) ftpssn->ftp_cmd_pipe_index++; else if ((rsp_code != 226) && (rsp_code != 426)) { /* * In terms of counting responses, ignore * 226 response saying transfer complete * 426 response saying transfer aborted * The 226 may or may not be sent by the server. * Both are 2nd response to a transfer command. */ ftpssn->ftp_cmd_pipe_index++; } } if (iMode == FTPP_SI_CLIENT_MODE) { ftpssn->ftp_cmd_pipe_index = 1; } if (encrypted) return FTPP_ALERT; return iRet; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftptelnet_buffer_dump.h0000644000175000017500000000334614241075773024774 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file ftptelnet_buffer_dump.h ** ** @author Vishnu Sriram ** ** @brief This file contains structures and functions for ** dumping buffers used during FTPTELNET inspection. */ #ifndef __FTPTELNET_BUFFER_DUMP_H__ #define __FTPTELNET_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { FTP_REQUEST_CMD_LINE, FTP_REQUEST_CMD, FTP_REQUEST_PARAM, FTP_RESPONSE_LINE, FTP_RESPONSE_DUMP, FTP_RESPONSE_MSG, TELNET_DUMP } FTPTELNET_BUFFER_DUMP; void dumpBuffer(FTPTELNET_BUFFER_DUMP type, const char *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getFTPTelnetBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/snort_ftptelnet.c0000644000175000017500000045544714241076006023641 0ustar apoapo/* * snort_ftptelnet.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file wraps the FTPTelnet functionality for Snort * and starts the Normalization & Protocol checks. * * The file takes a Packet structure from the Snort IDS to start the * FTP/Telnet Normalization & Protocol checks. It also uses the Stream * Interface Module which is also Snort-centric. Mainly, just a wrapper * to FTP/Telnet functionality, but a key part to starting the basic flow. * * The main bulk of this file is taken up with user configuration and * parsing. The reason this is so large is because FTPTelnet takes * very detailed configuration parameters for each specified FTP client, * to provide detailed control over an internal network and robust control * of the external network. * * The main functions of note are: * - FTPTelnetSnortConf() the configuration portion * - SnortFTPTelnet() the actual normalization & inspection * - LogEvents() where we log the FTPTelnet events * * NOTES: * - 16.09.04: Initial Development. SAS * */ #define _GNU_SOURCE #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_STRINGS_H #include #endif #include #include #include #include #include #include "sf_ip.h" #ifndef WIN32 #include #include #include #include #endif #define BUF_SIZE 1024 #include "sf_types.h" #include "snort_debug.h" #include "ftpp_return_codes.h" #include "ftpp_ui_config.h" #include "ftpp_ui_client_lookup.h" #include "ftpp_ui_server_lookup.h" #include "ftp_cmd_lookup.h" #include "ftp_bounce_lookup.h" #include "ftpp_si.h" #include "ftpp_eo_log.h" #include "pp_telnet.h" #include "pp_ftp.h" #include "snort_ftptelnet.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "stream_api.h" #include "profiler.h" #include "sf_snort_plugin_api.h" #include "Unified2_common.h" #include "ssl_include.h" #include #include "memory_stats.h" #ifdef DUMP_BUFFER #include "ftptelnet_buffer_dump.h" #endif #ifdef PERF_PROFILING extern PreprocStats ftpPerfStats; extern PreprocStats telnetPerfStats; PreprocStats ftppDetectPerfStats; int ftppDetectCalled = 0; #endif #ifdef TARGET_BASED unsigned s_ftpdata_eof_cb_id = 0; unsigned s_ftpdata_flush_cb_id = 0; #endif extern tSfPolicyUserContextId ftp_telnet_config; /* * GLOBAL subkeyword values */ #define ENCRYPTED_TRAFFIC "encrypted_traffic" #define CHECK_ENCRYPTED "check_encrypted" #define INSPECT_TYPE "inspection_type" #define INSPECT_TYPE_STATELESS "stateless" #define INSPECT_TYPE_STATEFUL "stateful" /* * Protocol subkeywords. */ #define PORTS "ports" /* * Telnet subkeywords. */ #define AYT_THRESHOLD "ayt_attack_thresh" #define NORMALIZE "normalize" #define DETECT_ANOMALIES "detect_anomalies" /* * FTP SERVER subkeywords. */ #define FTP_CMDS "ftp_cmds" #define PRINT_CMDS "print_cmds" #define MAX_PARAM_LEN "def_max_param_len" #define ALT_PARAM_LEN "alt_max_param_len" #define CMD_VALIDITY "cmd_validity" #define STRING_FORMAT "chk_str_fmt" #define TELNET_CMDS "telnet_cmds" #define IGNORE_TELNET_CMDS "ignore_telnet_erase_cmds" #define DATA_CHAN_CMD "data_chan_cmds" #define DATA_XFER_CMD "data_xfer_cmds" #define DATA_REST_CMD "data_rest_cmds" #define FILE_PUT_CMD "file_put_cmds" #define FILE_GET_CMD "file_get_cmds" #define DATA_CHAN "data_chan" #define LOGIN_CMD "login_cmds" #define ENCR_CMD "encr_cmds" #define DIR_CMD "dir_cmds" #define IGNORE_DATA_CHAN "ignore_data_chan" /* * FTP CLIENT subkeywords */ #define BOUNCE "bounce" #define ALLOW_BOUNCE "bounce_to" #define MAX_RESP_LEN "max_resp_len" /* * Data type keywords */ #define START_CMD_FORMAT "<" #define END_CMD_FORMAT ">" #define F_INT "int" #define F_NUMBER "number" #define F_CHAR "char" #define F_DATE "date" #define F_LITERAL "'" #define F_STRING "string" #define F_STRING_FMT "formated_string" #define F_HOST_PORT "host_port" #define F_LONG_HOST_PORT "long_host_port" #define F_EXTD_HOST_PORT "extd_host_port" /* * Optional parameter delimiters */ #define START_OPT_FMT "[" #define END_OPT_FMT "]" #define START_CHOICE_FMT "{" #define END_CHOICE_FMT "}" #define OR_FMT "|" /* * The cmd_validity keyword can be used with the format keyword to * restrict data types. The interpretation is specific to the data * type. 'format' is only supported with date & char data types. * * A few examples: * * 1. Will perform validity checking of an FTP Mode command to * check for one of the characters A, S, B, or C. * * cmd_validity MODE char ASBC * * * 2. Will perform validity checking of an FTP MDTM command to * check for an optional date argument following the format * specified. The date would uses the YYYYMMDDHHmmss+TZ format. * * cmd_validity MDTM [ date nnnnnnnnnnnnnn[.n[n[n]]] ] string * * * 3. Will perform validity checking of an FTP ALLO command to * check for an integer, then optionally, the letter R and another * integer. * * cmd_validity ALLO int [ char R int ] */ /* * The def_max_param_len & alt_max_param_len keywords can be used to * restrict parameter length for one or more commands. The space * separated list of commands is enclosed in {}s. * * A few examples: * * 1. Restricts all command parameters to 100 characters * * def_max_param_len 100 * * 2. Overrides CWD pathname to 256 characters * * alt_max_param_len 256 { CWD } * * 3. Overrides PWD & SYST to no parameters * * alt_max_param_len 0 { PWD SYST } * */ /* * Alert subkeywords */ #define BOOL_YES "yes" #define BOOL_NO "no" /* ** IP Address list delimiters */ #define START_IPADDR_LIST "{" #define END_IPADDR_LIST "}" /* * Port list delimiters */ #define START_PORT_LIST "{" #define END_PORT_LIST "}" /* * Keyword for the default client/server configuration */ #define DEFAULT "default" /* * The default FTP server configuration for FTP command validation. * Most of this comes from RFC 959, with additional commands being * drawn from other RFCs/Internet Drafts that are in use. * * Any of the below can be overridden in snort.conf. * * This is here to eliminate most of it from snort.conf to * avoid an ugly configuration file. The default_max_param_len * is somewhat arbitrary, but is taken from the majority of * the snort FTP rules that limit parameter size to 100 * characters, as of 18 Sep 2004. * * The data_chan_cmds, data_xfer_cmds are used to track open * data channel connections. * * The login_cmds and dir_cmds are used to track state of username * and current directory. * * The file_put_cmds and file_get_cmds are used to track file transfers * over open data channel connections. */ /* DEFAULT_FTP_CONF_* deliberately break the default conf into * chunks with lengths < 509 to keep ISO C89 compilers happy */ static const char* DEFAULT_FTP_CONF[] = { "hardcoded_config " "def_max_param_len 100 " "ftp_cmds { USER PASS ACCT CWD CDUP SMNT QUIT REIN TYPE STRU" " MODE RETR STOR STOU APPE ALLO REST RNFR RNTO ABOR" " DELE RMD MKD PWD LIST NLST SITE SYST STAT HELP NOOP } " "ftp_cmds { AUTH ADAT PROT PBSZ CONF ENC } " "ftp_cmds { PORT PASV LPRT LPSV EPRT EPSV } " "ftp_cmds { FEAT OPTS } " "ftp_cmds { MDTM REST SIZE MLST MLSD } " "alt_max_param_len 0 { CDUP QUIT REIN PASV STOU ABOR PWD SYST NOOP } ", "cmd_validity MODE < char SBC > " "cmd_validity STRU < char FRPO [ string ] > " "cmd_validity ALLO < int [ char R int ] > " "cmd_validity TYPE < { char AE [ char NTC ] | char I | char L [ number ] } > " "cmd_validity PORT < host_port > " "cmd_validity LPRT < long_host_port > " "cmd_validity EPRT < extd_host_port > " "cmd_validity EPSV < [ { '1' | '2' | 'ALL' } ] > ", "data_chan_cmds { PORT PASV LPRT LPSV EPRT EPSV } " "data_xfer_cmds { RETR STOR STOU APPE LIST NLST } " "data_rest_cmds { REST } " "file_put_cmds { STOR STOU } " "file_get_cmds { RETR } " "login_cmds { USER PASS } " "dir_cmds { CWD 250 CDUP 250 PWD 257 } " "encr_cmds { AUTH } " }; #define CONF_CHUNKS (sizeof(DEFAULT_FTP_CONF)/sizeof(DEFAULT_FTP_CONF[0])) static uint8_t ftp_paf_id = 0; static char* DefaultConf (size_t* pn) { unsigned i; size_t sz = 1, ns = 0; char* str = NULL; for ( i = 0; i < CONF_CHUNKS; i++ ) sz += strlen(DEFAULT_FTP_CONF[i]); str = _dpd.snortAlloc(1, sz, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if ( !str ) DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); for ( i = 0; i < CONF_CHUNKS; i++ ) ns += snprintf(str+ns, sz-ns, "%s", DEFAULT_FTP_CONF[i]); *pn = sz; return str; } static int printedFTPHeader = 0; static int _checkServerConfig(struct _SnortConfig *, void *pData); char *maxToken = NULL; static tSfPolicyId ftp_current_policy = 0; static void _addPortsToStream(struct _SnortConfig *, char *, tSfPolicyId, int); static int _addFtpServerConfPortsToStream(struct _SnortConfig *, void *); static void _FTPTelnetAddPortsOfInterest(struct _SnortConfig *, FTPTELNET_GLOBAL_CONF *, tSfPolicyId); #ifdef TARGET_BASED static void _FTPTelnetAddService(struct _SnortConfig *, int16_t, tSfPolicyId); #endif void FTP_Set_flow_id( void *app_data, uint32_t fid ); void FTPData_Set_flow_id( void *app_data, uint32_t fid ); char* mystrtok (char* s, const char* delim) { static char* last = NULL; if ( s || last ) last = strtok(s, delim); return last; } char *NextToken(char *delimiters) { char *retTok = mystrtok(NULL, delimiters); if (retTok > maxToken) return NULL; return retTok; } /* * Function: ProcessConfOpt(FTPTELNET_CONF_OPT *ConfOpt, * char *Option, * char *ErrorString, int ErrStrLen) * * Purpose: Set the CONF_OPT on and alert fields. * * We check to make sure of valid parameters and then set * the appropriate fields. * * Arguments: ConfOpt => pointer to the configuration option * Option => character pointer to the option being configured * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessConfOpt(FTPTELNET_CONF_OPT *ConfOpt, char *Option, char *ErrorString, int ErrStrLen) { char *pcToken; pcToken = NextToken(CONF_SEPARATORS); if(pcToken == NULL) { snprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", Option); return FTPP_FATAL_ERR; } /* * Check for the alert value */ if(!strcmp(BOOL_YES, pcToken)) { ConfOpt->alert = 1; } else if(!strcmp(BOOL_NO, pcToken)) { ConfOpt->alert = 0; } else { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'.", Option); return FTPP_FATAL_ERR; } ConfOpt->on = 1; return FTPP_SUCCESS; } /* * Function: PrintConfOpt(FTPTELNET_CONF_OPT *ConfOpt, * char *Option) * * Purpose: Prints the CONF_OPT and alert fields. * * Arguments: ConfOpt => pointer to the configuration option * Option => character pointer to the option being configured * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int PrintConfOpt(FTPTELNET_CONF_OPT *ConfOpt, char *Option) { if(!ConfOpt || !Option) { return FTPP_INVALID_ARG; } if(ConfOpt->on) { _dpd.logMsg(" %s: YES alert: %s\n", Option, ConfOpt->alert ? "YES" : "NO"); } else { _dpd.logMsg(" %s: OFF\n", Option); } return FTPP_SUCCESS; } /* * Function: ProcessInspectType(FTPTELNET_CONF_OPT *ConfOpt, * char *ErrorString, int ErrStrLen) * * Purpose: Process the type of inspection. * This sets the type of inspection for FTPTelnet to do. * * Arguments: GlobalConf => pointer to the global configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessInspectType(FTPTELNET_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen) { char *pcToken; pcToken = NextToken(CONF_SEPARATORS); if(pcToken == NULL) { snprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", INSPECT_TYPE); return FTPP_FATAL_ERR; } if(!strcmp(INSPECT_TYPE_STATEFUL, pcToken)) { GlobalConf->inspection_type = FTPP_UI_CONFIG_STATEFUL; } else if(!strcmp(INSPECT_TYPE_STATELESS, pcToken)) { GlobalConf->inspection_type = FTPP_UI_CONFIG_STATELESS; } else { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'. Must be either " "'%s' or '%s'.", INSPECT_TYPE, INSPECT_TYPE_STATEFUL, INSPECT_TYPE_STATELESS); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } /* * Function: ProcessFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf, * char *ErrorString, int ErrStrLen) * * Purpose: This is where we process the global configuration for FTPTelnet. * * We set the values of the global configuraiton here. Any errors * that are encountered are specified in the error string and the * type of error is returned through the return code, i.e. fatal, * non-fatal. * * The configuration options that are dealt with here are: * - inspection_type * Indicate whether to operate in stateful stateless mode * - encrypted_traffic * Detect and alert on encrypted sessions * - check_after_encrypted * Instructs the preprocessor to continue checking a data stream * after it is encrypted, looking for an eventual * non-ecrypted data. * * Arguments: GlobalConf => pointer to the global configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int ProcessFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen) { FTPTELNET_CONF_OPT *ConfOpt; int iRet = 0; char *pcToken; int iTokens = 0; while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { /* * Show that we at least got one token */ iTokens = 1; /* * Search for configuration keywords */ if (!strcmp(pcToken, CHECK_ENCRYPTED)) { GlobalConf->check_encrypted_data = 1; } else if (!strcmp(pcToken, ENCRYPTED_TRAFFIC)) { ConfOpt = &GlobalConf->encrypted; iRet = ProcessConfOpt(ConfOpt, ENCRYPTED_TRAFFIC, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(INSPECT_TYPE, pcToken)) { iRet = ProcessInspectType(GlobalConf, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else { snprintf(ErrorString, ErrStrLen, "Invalid keyword '%s' for '%s' configuration.", pcToken, GLOBAL); return FTPP_FATAL_ERR; } } /* * If there are not any tokens to the configuration, then * we let the user know and log the error. return non-fatal * error. */ if(!iTokens) { snprintf(ErrorString, ErrStrLen, "No tokens to '%s' configuration.", GLOBAL); return FTPP_NONFATAL_ERR; } return FTPP_SUCCESS; } /* * Function: ProcessPorts(PROTO_CONF *protocol, * char *ErrorString, int ErrStrLen) * * Purpose: Process the port list for the server configuration. * This configuration is a list of valid ports and is ended * by a delimiter. * * Arguments: protocol => pointer to the ports configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessPorts(PROTO_CONF *protocol, char *ErrorString, int ErrStrLen) { char *pcToken; char *pcEnd; int iPort; int iEndPorts = 0; pcToken = NextToken(CONF_SEPARATORS); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid port list format."); return FTPP_FATAL_ERR; } if(strcmp(START_PORT_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a port list with the '%s' token.", START_PORT_LIST); return FTPP_FATAL_ERR; } /* Unset the defaults */ for (iPort = 0;iPortports[iPort] = 0; while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndPorts = 1; break; } iPort = strtol(pcToken, &pcEnd, 10); /* * Validity check for port */ if(*pcEnd) { snprintf(ErrorString, ErrStrLen, "Invalid port number."); return FTPP_FATAL_ERR; } if(iPort < 0 || iPort > MAXPORTS-1) { snprintf(ErrorString, ErrStrLen, "Invalid port number. Must be between 0 and " "65535."); return FTPP_FATAL_ERR; } protocol->ports[iPort] = 1; if(protocol->port_count < MAXPORTS) protocol->port_count++; } if(!iEndPorts) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", PORTS, END_PORT_LIST); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } /* * Function: ProcessTelnetAYTThreshold(TELNET_PROTO_CONF *TelnetConf, * char *ErrorString, int ErrStrLen) * * Purpose: Process the 'are you there' threshold configuration * This sets the maximum number of telnet ayt commands that * we will tolerate, before alerting. * * Arguments: TelnetConf => pointer to the telnet configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessTelnetAYTThreshold(TELNET_PROTO_CONF *TelnetConf, char *ErrorString, int ErrStrLen) { char *pcToken; char *pcEnd = NULL; pcToken = NextToken(CONF_SEPARATORS); if(pcToken == NULL) { snprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", AYT_THRESHOLD); return FTPP_FATAL_ERR; } TelnetConf->ayt_threshold = strtol(pcToken, &pcEnd, 10); /* * Let's check to see if the entire string was valid. * If there is an address here, then there was an * invalid character in the string. */ if(*pcEnd) { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'. Must be a positive " "number.", AYT_THRESHOLD); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } /* * Function: PrintTelnetConf(TELNET_PROTO_CONF *TelnetConf, * char *Option) * * Purpose: Prints the telnet configuration * * Arguments: TelnetConf => pointer to the telnet configuration * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int PrintTelnetConf(TELNET_PROTO_CONF *TelnetConf) { char buf[BUF_SIZE+1]; int iCtr; if(!TelnetConf) { return FTPP_INVALID_ARG; } _dpd.logMsg(" TELNET CONFIG:\n"); memset(buf, 0, BUF_SIZE+1); snprintf(buf, BUF_SIZE, " Ports: "); /* * Print out all the applicable ports. */ for(iCtr = 0; iCtr < MAXPORTS; iCtr++) { if(TelnetConf->proto_ports.ports[iCtr]) { _dpd.printfappend(buf, BUF_SIZE, "%d ", iCtr); } } _dpd.logMsg("%s\n", buf); _dpd.logMsg(" Are You There Threshold: %d\n", TelnetConf->ayt_threshold); _dpd.logMsg(" Normalize: %s\n", TelnetConf->normalize ? "YES" : "NO"); _dpd.logMsg(" Detect Anomalies: %s\n", TelnetConf->detect_anomalies ? "YES" : "NO"); return FTPP_SUCCESS; } /* * Function: ProcessTelnetConf(FTPTELNET_GLOBAL_CONF *GlobalConf, * char *ErrorString, int ErrStrLen) * * Purpose: This is where we process the telnet configuration for FTPTelnet. * * We set the values of the telnet configuraiton here. Any errors * that are encountered are specified in the error string and the * type of error is returned through the return code, i.e. fatal, * non-fatal. * * The configuration options that are dealt with here are: * - ports { x } Ports on which to do telnet checks * - normalize Turns on normalization * - ayt_attack_thresh x Detect consecutive are you there commands * * Arguments: GlobalConf => pointer to the global configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int ProcessTelnetConf(FTPTELNET_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen) { int iRet; char *pcToken; int iTokens = 0; if (GlobalConf->telnet_config != NULL) { snprintf(ErrorString, ErrStrLen, "Telnet can only be configured once.\n"); return FTPP_FATAL_ERR; } GlobalConf->telnet_config = (TELNET_PROTO_CONF *)_dpd.snortAlloc(1, sizeof(TELNET_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (GlobalConf->telnet_config == NULL) { DynamicPreprocessorFatalMessage("Out of memory trying to create " "telnet configuration.\n"); } /* * Reset the global telnet configuration */ if(ftpp_ui_config_reset_telnet_proto(GlobalConf->telnet_config)) { snprintf(ErrorString, ErrStrLen, "Cannot reset the FTPTelnet global telnet configuration."); return FTPP_FATAL_ERR; } while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { /* * Show that we at least got one token */ iTokens = 1; /* * Search for configuration keywords */ if(!strcmp(PORTS, pcToken)) { PROTO_CONF *ports = (PROTO_CONF*)GlobalConf->telnet_config; iRet = ProcessPorts(ports, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(AYT_THRESHOLD, pcToken)) { iRet = ProcessTelnetAYTThreshold(GlobalConf->telnet_config, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(NORMALIZE, pcToken)) { GlobalConf->telnet_config->normalize = 1; } else if(!strcmp(DETECT_ANOMALIES, pcToken)) { GlobalConf->telnet_config->detect_anomalies = 1; } /* * Start the CONF_OPT configurations. */ else { snprintf(ErrorString, ErrStrLen, "Invalid keyword '%s' for '%s' configuration.", pcToken, GLOBAL); return FTPP_FATAL_ERR; } } /* * If there are not any tokens to the configuration, then * we let the user know and log the error. return non-fatal * error. */ if(!iTokens) { snprintf(ErrorString, ErrStrLen, "No tokens to '%s' configuration.", TELNET); return FTPP_NONFATAL_ERR; } /* Let's print out the telnet config */ PrintTelnetConf(GlobalConf->telnet_config); return FTPP_SUCCESS; } #if 0 /**obsoleted during changes for bug_31418 */ /* * Function: GetIPAddr(char *addrString, unsigned uint32_t *ipAddr, * char *ErrorString, int ErrStrLen) * * Purpose: This is where we convert an IP address to a numeric * * Any errors that are encountered are specified in the error * string and the type of error is returned through the return * code, i.e. fatal, non-fatal. * * Arguments: addrString => pointer to the address string * ipAddr => pointer to converted address * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int GetIPAddr(char *addrString, sfaddr_t *ipAddr, char *ErrorString, int ErrStrLen) { if(sfip_pton(addrString, ipAddr) != SFIP_SUCCESS) { snprintf(ErrorString, ErrStrLen, "Invalid FTP client IP address '%s'.", addrString); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } #endif /* * Function: ProcessFTPCmdList(FTP_SERVER_PROTO_CONF *ServerConf, * char *confOption, * char *ErrorString, int ErrStrLen, * int require_cmds, int require_length) * * Purpose: Process the FTP cmd lists for the client configuration. * This configuration is a parameter length for the list of * FTP commands and is ended by a delimiter. * * Arguments: ServerConf => pointer to the FTP server configuration * confOption => pointer to the name of the option * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * require_cmds => flag to require a command list * require_length => flag to require a length specifier * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessFTPCmdList(FTP_SERVER_PROTO_CONF *ServerConf, char *confOption, char *ErrorString, int ErrStrLen, int require_cmds, int require_length) { FTP_CMD_CONF *FTPCmd = NULL; char *pcToken; char *pcEnd = NULL; char *cmd; int iLength = 0; int iEndCmds = 0; int iRet; if (require_length) { pcToken = NextToken(CONF_SEPARATORS); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid cmd list format."); return FTPP_FATAL_ERR; } iLength = strtol(pcToken, &pcEnd, 10); /* * Let's check to see if the entire string was valid. * If there is an address here, then there was an * invalid character in the string. */ if((*pcEnd) || (iLength < 0)) { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'. " "Length must be a positive number", confOption); return FTPP_FATAL_ERR; } } if (require_cmds) { pcToken = NextToken(CONF_SEPARATORS); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid cmd list format."); return FTPP_FATAL_ERR; } if(strcmp(START_PORT_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a cmd list with the '%s' token.", START_PORT_LIST); return FTPP_FATAL_ERR; } while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndCmds = 1; break; } cmd = pcToken; FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd, strlen(cmd), &iRet); if (FTPCmd == NULL) { /* Add it to the list */ // note that struct includes 1 byte for null, so just add len FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1, sizeof(FTP_CMD_CONF)+strlen(cmd), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (FTPCmd == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strcpy(FTPCmd->cmd_name, cmd); ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd, strlen(cmd), FTPCmd); FTPCmd->max_param_len = ServerConf->def_max_param_len; } if (require_length) { FTPCmd->max_param_len = iLength; FTPCmd->max_param_len_overridden = 1; } } if(!iEndCmds) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", FTP_CMDS, END_PORT_LIST); return FTPP_FATAL_ERR; } } if (!strcmp(confOption, MAX_PARAM_LEN)) { ServerConf->def_max_param_len = iLength; /* Reset the max length to the default for all existing commands */ FTPCmd = ftp_cmd_lookup_first(ServerConf->cmd_lookup, &iRet); while (FTPCmd) { if (!FTPCmd->max_param_len_overridden) { FTPCmd->max_param_len = ServerConf->def_max_param_len; } FTPCmd = ftp_cmd_lookup_next(ServerConf->cmd_lookup, &iRet); } } return FTPP_SUCCESS; } /* * Function: ResetStringFormat (FTP_PARAM_FMT *Fmt) * * Purpose: Recursively sets nodes that allow strings to nodes that check * for a string format attack within the FTP parameter validation tree * * Arguments: Fmt => pointer to the FTP Parameter configuration * * Returns: None * */ void ResetStringFormat (FTP_PARAM_FMT *Fmt) { int i; if (!Fmt) return; if (Fmt->type == e_unrestricted) Fmt->type = e_strformat; ResetStringFormat(Fmt->optional_fmt); for (i=0;inumChoices;i++) { ResetStringFormat(Fmt->choices[i]); } ResetStringFormat(Fmt->next_param_fmt); } /* * Function: ProcessFTPDataChanCmdsList(FTP_SERVER_PROTO_CONF *ServerConf, * char *confOption, * char *ErrorString, int ErrStrLen) * * Purpose: Process the FTP cmd lists for the client configuration. * This configuration is an indicator of data channels, data transfer, * string format, encryption, or login commands. * * Arguments: ServerConf => pointer to the FTP server configuration * confOption => pointer to the name of the option * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessFTPDataChanCmdsList(FTP_SERVER_PROTO_CONF *ServerConf, char *confOption, char *ErrorString, int ErrStrLen) { FTP_CMD_CONF *FTPCmd = NULL; char *pcToken; char *cmd; int iEndCmds = 0; int iRet; pcToken = NextToken(CONF_SEPARATORS); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid %s list format.", confOption); return FTPP_FATAL_ERR; } if(strcmp(START_PORT_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a %s list with the '%s' token.", confOption, START_PORT_LIST); return FTPP_FATAL_ERR; } while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndCmds = 1; break; } cmd = pcToken; FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd, strlen(cmd), &iRet); if (FTPCmd == NULL) { /* Add it to the list */ // note that struct includes 1 byte for null, so just add len FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1, sizeof(FTP_CMD_CONF) + strlen(cmd), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (FTPCmd == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strcpy(FTPCmd->cmd_name, cmd); FTPCmd->max_param_len = ServerConf->def_max_param_len; ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd, strlen(cmd), FTPCmd); } if (!strcmp(confOption, DATA_CHAN_CMD)) FTPCmd->data_chan_cmd = 1; else if (!strcmp(confOption, DATA_XFER_CMD)) FTPCmd->data_xfer_cmd = 1; else if (!strcmp(confOption, DATA_REST_CMD)) FTPCmd->data_rest_cmd = 1; else if (!strcmp(confOption, FILE_PUT_CMD)) { FTPCmd->data_xfer_cmd = 1; FTPCmd->file_put_cmd = 1; } else if (!strcmp(confOption, FILE_GET_CMD)) { FTPCmd->data_xfer_cmd = 1; FTPCmd->file_get_cmd = 1; } else if (!strcmp(confOption, STRING_FORMAT)) { FTP_PARAM_FMT *Fmt = FTPCmd->param_format; if (Fmt) { ResetStringFormat(Fmt); } else { Fmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (Fmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } Fmt->type = e_head; FTPCmd->param_format = Fmt; Fmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (Fmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } Fmt->type = e_strformat; FTPCmd->param_format->next_param_fmt = Fmt; Fmt->prev_param_fmt = FTPCmd->param_format; } FTPCmd->check_validity = 1; } else if (!strcmp(confOption, ENCR_CMD)) FTPCmd->encr_cmd = 1; else if (!strcmp(confOption, LOGIN_CMD)) FTPCmd->login_cmd = 1; } if(!iEndCmds) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", confOption, END_PORT_LIST); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } /* * Function: ProcessFTPDirCmdsList(FTP_SERVER_PROTO_CONF *ServerConf, * char *confOption, * char *ErrorString, int ErrStrLen) * * Purpose: Process the FTP cmd lists for the client configuration. * This configuration is an indicator of commands used to * retrieve or update the current directory. * * Arguments: ServerConf => pointer to the FTP server configuration * confOption => pointer to the name of the option * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessFTPDirCmdsList(FTP_SERVER_PROTO_CONF *ServerConf, char *confOption, char *ErrorString, int ErrStrLen) { FTP_CMD_CONF *FTPCmd = NULL; char *pcToken; char *pcEnd = NULL; char *cmd; int iCode; int iEndCmds = 0; int iRet; pcToken = NextToken(CONF_SEPARATORS); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid %s list format.", confOption); return FTPP_FATAL_ERR; } if(strcmp(START_PORT_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a %s list with the '%s' token.", confOption, START_PORT_LIST); return FTPP_FATAL_ERR; } while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndCmds = 1; break; } cmd = pcToken; FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd, strlen(cmd), &iRet); if (FTPCmd == NULL) { /* Add it to the list */ // note that struct includes 1 byte for null, so just add len FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1, sizeof(FTP_CMD_CONF) + strlen(cmd), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (FTPCmd == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strcpy(FTPCmd->cmd_name, cmd); FTPCmd->max_param_len = ServerConf->def_max_param_len; ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd, strlen(cmd), FTPCmd); } pcToken = NextToken(CONF_SEPARATORS); if (!pcToken) { snprintf(ErrorString, ErrStrLen, "FTP Dir Cmds must have associated response code: '%s'.", cmd); return FTPP_FATAL_ERR; } iCode = strtol(pcToken, &pcEnd, 10); /* * Let's check to see if the entire string was valid. * If there is an address here, then there was an * invalid character in the string. */ if((*pcEnd) || (iCode < 0)) { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'. " "Code must be a positive number", confOption); return FTPP_FATAL_ERR; } FTPCmd->dir_response = iCode; } if(!iEndCmds) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", confOption, END_PORT_LIST); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } static int ProcessFTPIgnoreDataChan(FTP_SERVER_PROTO_CONF *ServerConf, char *confOption, char *ErrorString, int ErrStrLen) { char *pcToken; pcToken = NextToken(CONF_SEPARATORS); if (pcToken == NULL) { snprintf(ErrorString, ErrStrLen, "No argument provided to option '%s'. " "Argument must be 'yes' or 'no'.", confOption); return FTPP_FATAL_ERR; } if (!strcasecmp("yes", pcToken)) { ServerConf->data_chan = 1; } else if (!strcasecmp("no", pcToken)) { if (ServerConf->data_chan == 1) { snprintf(ErrorString, ErrStrLen, "Both 'data_chan' and " "'ignore_data_chan' configured with conflicting options."); return FTPP_FATAL_ERR; } ServerConf->data_chan = 0; } else { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'. " "Argument must be 'yes' or 'no'.", confOption); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } /* * Function: SetOptionalsNext(FTP_PARAM_FMT *ThisFmt, * FTP_PARAM_FMT *NextFmt, * FTP_PARAM_FMT **choices, * int numChoices) * * Purpose: Recursively updates the next value for nodes in the FTP * Parameter validation tree. * * Arguments: ThisFmt => pointer to an FTP parameter validation node * NextFmt => pointer to an FTP parameter validation node * choices => pointer to a list of FTP parameter * validation nodes * numChoices => the number of nodes in the list * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static void SetOptionalsNext(FTP_PARAM_FMT *ThisFmt, FTP_PARAM_FMT *NextFmt, FTP_PARAM_FMT **choices, int numChoices) { if (!ThisFmt) return; if (ThisFmt->optional) { if (ThisFmt->next_param_fmt == NULL) { ThisFmt->next_param_fmt = NextFmt; if (numChoices) { ThisFmt->numChoices = numChoices; ThisFmt->choices = (FTP_PARAM_FMT **)_dpd.snortAlloc(numChoices, sizeof(FTP_PARAM_FMT *), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (ThisFmt->choices == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } memcpy(ThisFmt->choices, choices, sizeof(FTP_PARAM_FMT *) * numChoices); } } else { SetOptionalsNext(ThisFmt->next_param_fmt, NextFmt, choices, numChoices); } } else { int i; SetOptionalsNext(ThisFmt->optional_fmt, ThisFmt->next_param_fmt, ThisFmt->choices, ThisFmt->numChoices); for (i=0;inumChoices;i++) { SetOptionalsNext(ThisFmt->choices[i], ThisFmt, choices, numChoices); } SetOptionalsNext(ThisFmt->next_param_fmt, ThisFmt, choices, numChoices); } } /* * Function: ProcessDateFormat(FTP_DATE_FMT *dateFmt, * FTP_DATE_FMT *LastNonOptFmt, * char **format) * * Purpose: Sets the value for nodes in the FTP Date validation tree. * * Arguments: dateFmt => pointer to an FTP date validation node * LastNonOptFmt => pointer to previous FTP date validation node * format => pointer to next part of date validation string * Updated on function exit. * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessDateFormat(FTP_DATE_FMT *dateFmt, FTP_DATE_FMT *LastNonOptFmt, char **format) { char *curr_format; int iRet = FTPP_SUCCESS; int curr_len = 0; char *curr_ch; char *start_ch; FTP_DATE_FMT *CurrFmt = dateFmt; if (!dateFmt) return FTPP_INVALID_ARG; if (!format || !*format) return FTPP_INVALID_ARG; start_ch = curr_ch = *format; while (*curr_ch != '\0') { switch (*curr_ch) { case 'n': case 'C': case '+': case '-': case '.': curr_len++; curr_ch++; break; case '[': curr_ch++; if (curr_len > 0) { FTP_DATE_FMT *OptFmt; OptFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1, sizeof(FTP_DATE_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (OptFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } curr_format = (char *)_dpd.snortAlloc(curr_len + 1, sizeof(char), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (curr_format == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strncpy(curr_format, start_ch, curr_len); CurrFmt->format_string = curr_format; CurrFmt->optional = OptFmt; OptFmt->prev = CurrFmt; iRet = ProcessDateFormat(OptFmt, CurrFmt, &curr_ch); if (iRet != FTPP_SUCCESS) { _dpd.snortFree(OptFmt, sizeof(FTP_DATE_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); _dpd.snortFree(curr_format, (curr_len + 1) * sizeof(char), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); return iRet; } curr_len = 0; } start_ch = curr_ch; break; case ']': curr_ch++; if (curr_len > 0) { curr_format = (char *)_dpd.snortAlloc(curr_len + 1, sizeof(char), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (curr_format == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strncpy(curr_format, start_ch, curr_len); CurrFmt->format_string = curr_format; curr_len = 0; } *format = curr_ch; return FTPP_SUCCESS; break; case '{': curr_ch++; { FTP_DATE_FMT *NewFmt; NewFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1, sizeof(FTP_DATE_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (NewFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } if (curr_len > 0) { curr_format = (char *)_dpd.snortAlloc(curr_len + 1, sizeof(char), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (curr_format == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strncpy(curr_format, start_ch, curr_len); CurrFmt->format_string = curr_format; curr_len = 0; } else { CurrFmt->empty = 1; } NewFmt->prev = LastNonOptFmt; CurrFmt->next_a = NewFmt; iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch); if (iRet != FTPP_SUCCESS) { return iRet; } NewFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1, sizeof(FTP_DATE_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (NewFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } NewFmt->prev = LastNonOptFmt; CurrFmt->next_b = NewFmt; iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch); if (iRet != FTPP_SUCCESS) { return iRet; } NewFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1, sizeof(FTP_DATE_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (NewFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } NewFmt->prev = CurrFmt; CurrFmt->next = NewFmt; iRet = ProcessDateFormat(NewFmt, CurrFmt, &curr_ch); if (iRet != FTPP_SUCCESS) { return iRet; } } break; case '}': curr_ch++; if (curr_len > 0) { curr_format = (char *)_dpd.snortAlloc(curr_len + 1, sizeof(char), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (curr_format == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strncpy(curr_format, start_ch, curr_len); CurrFmt->format_string = curr_format; curr_len = 0; *format = curr_ch; return FTPP_SUCCESS; } else { CurrFmt->empty = 1; *format = curr_ch; return FTPP_SUCCESS; } break; case '|': curr_ch++; if (curr_len > 0) { curr_format = (char *)_dpd.snortAlloc(curr_len + 1, sizeof(char), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (curr_format == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strncpy(curr_format, start_ch, curr_len); CurrFmt->format_string = curr_format; curr_len = 0; *format = curr_ch; return FTPP_SUCCESS; } else { CurrFmt->empty = 1; *format = curr_ch; return FTPP_SUCCESS; } break; default: /* Uh, shouldn't get this. */ return FTPP_INVALID_ARG; break; } } if (curr_len > 0) { curr_format = (char *)_dpd.snortAlloc(curr_len + 1, sizeof(char), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (curr_format == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strncpy(curr_format, start_ch, curr_len); CurrFmt->format_string = curr_format; start_ch = curr_ch; curr_len = 0; } /* Should've closed all options & ORs */ *format = curr_ch; return FTPP_SUCCESS; } /* * Function: DoNextFormat(FTP_PARAM_FMT *ThisFmt, int allocated, * char *ErrorString, int ErrStrLen) * * Purpose: Processes the next FTP parameter validation node. * * Arguments: ThisFmt => pointer to an FTP parameter validation node * allocated => indicator whether the next node is allocated * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int DoNextFormat(FTP_PARAM_FMT *ThisFmt, int allocated, char *ErrorString, int ErrStrLen) { FTP_PARAM_FMT *NextFmt; int iRet = FTPP_SUCCESS; char *fmt = NextToken(CONF_SEPARATORS); if (!fmt) return FTPP_INVALID_ARG; if(!strcmp(END_CMD_FORMAT, fmt)) { return FTPP_SUCCESS; } if (!strcmp(fmt, OR_FMT)) { return FTPP_OR_FOUND; } if (!strcmp(fmt, END_OPT_FMT)) { return FTPP_OPT_END_FOUND; } if (!strcmp(fmt, END_CHOICE_FMT)) { return FTPP_CHOICE_END_FOUND; } if (!strcmp(fmt, START_OPT_FMT)) { NextFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (NextFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } ThisFmt->optional_fmt = NextFmt; NextFmt->optional = 1; NextFmt->prev_param_fmt = ThisFmt; if (ThisFmt->optional) NextFmt->prev_optional = 1; iRet = DoNextFormat(NextFmt, 1, ErrorString, ErrStrLen); if (iRet != FTPP_OPT_END_FOUND) { return FTPP_INVALID_ARG; } return DoNextFormat(ThisFmt, 0, ErrorString, ErrStrLen); } if (!strcmp(fmt, START_CHOICE_FMT)) { int numChoices = 1; do { FTP_PARAM_FMT **tmpChoices = (FTP_PARAM_FMT **)_dpd.snortAlloc(numChoices, sizeof(FTP_PARAM_FMT *), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (tmpChoices == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } if (ThisFmt->numChoices) { /* explicit check that we have enough room for copy */ if (numChoices <= ThisFmt->numChoices) DynamicPreprocessorFatalMessage("%s(%d) => Can't do memcpy - index out of range \n", *(_dpd.config_file), *(_dpd.config_line)); memcpy(tmpChoices, ThisFmt->choices, sizeof(FTP_PARAM_FMT*) * ThisFmt->numChoices); } NextFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (NextFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } tmpChoices[numChoices-1] = NextFmt; if (ThisFmt->choices) _dpd.snortFree(ThisFmt->choices, (ThisFmt->numChoices * sizeof(FTP_PARAM_FMT *)), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); ThisFmt->numChoices = numChoices; ThisFmt->choices = tmpChoices; NextFmt->prev_param_fmt = ThisFmt; iRet = DoNextFormat(NextFmt, 1, ErrorString, ErrStrLen); numChoices++; } while (iRet == FTPP_OR_FOUND); if (iRet != FTPP_CHOICE_END_FOUND) { return FTPP_INVALID_ARG; } return DoNextFormat(ThisFmt, 0, ErrorString, ErrStrLen); } if (!allocated) { NextFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (NextFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } NextFmt->prev_param_fmt = ThisFmt; ThisFmt->next_param_fmt = NextFmt; if (ThisFmt->optional) NextFmt->prev_optional = 1; } else { NextFmt = ThisFmt; } /* If its not an end cmd, OR, START/END Opt... * it must be a parameter specification. */ /* Setup the type & format specs */ if (!strcmp(fmt, F_INT)) { NextFmt->type = e_int; } else if (!strcmp(fmt, F_NUMBER)) { NextFmt->type = e_number; } else if (!strcmp(fmt, F_CHAR)) { char *chars_allowed = NextToken(CONF_SEPARATORS); if(!chars_allowed) { snprintf(ErrorString, ErrStrLen, "Illegal format '' for token '%s'.", CMD_VALIDITY); return FTPP_INVALID_ARG; } NextFmt->type = e_char; NextFmt->format.chars_allowed = 0; while (*chars_allowed != 0) { int bitNum = (*chars_allowed & 0x1f); NextFmt->format.chars_allowed |= (1 << (bitNum-1)); chars_allowed++; } } else if (!strcmp(fmt, F_DATE)) { FTP_DATE_FMT *DateFmt; char *format = NextToken(CONF_SEPARATORS); NextFmt->type = e_date; DateFmt = (FTP_DATE_FMT *)_dpd.snortAlloc(1, sizeof(FTP_DATE_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (DateFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } NextFmt->format.date_fmt = DateFmt; iRet = ProcessDateFormat(DateFmt, NULL, &format); if (iRet) { snprintf(ErrorString, ErrStrLen, "Illegal format %s for token '%s'.", format, CMD_VALIDITY); return FTPP_INVALID_ARG; } } else if ( *fmt == *F_LITERAL ) { char* end = strchr(++fmt, *F_LITERAL); int len = end ? end - fmt : 0; if ( len < 1 ) { snprintf( ErrorString, ErrStrLen, "Illegal format '' for token '%s'.", CMD_VALIDITY ); return FTPP_INVALID_ARG; } NextFmt->type = e_literal; NextFmt->format.literal = (char *)_dpd.snortAlloc(1, len + 1, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if ( !NextFmt->format.literal ) { DynamicPreprocessorFatalMessage( "%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line) ); } strncpy(NextFmt->format.literal, fmt, len); NextFmt->format.literal[len] = '\0'; } else if (!strcmp(fmt, F_STRING)) { NextFmt->type = e_unrestricted; } else if (!strcmp(fmt, F_HOST_PORT)) { NextFmt->type = e_host_port; } else if (!strcmp(fmt, F_LONG_HOST_PORT)) { NextFmt->type = e_long_host_port; } else if (!strcmp(fmt, F_EXTD_HOST_PORT)) { NextFmt->type = e_extd_host_port; } else { snprintf(ErrorString, ErrStrLen, "Illegal format type %s for token '%s'.", fmt, CMD_VALIDITY); return FTPP_INVALID_ARG; } return DoNextFormat(NextFmt, 0, ErrorString, ErrStrLen); } /* * Function: ProcessFTPCmdValidity(FTP_SERVER_PROTO_CONF *ServerConf, * char *ErrorString, int ErrStrLen) * * Purpose: Process the ftp cmd validity configuration. * This sets the FTP command parameter validation tree. * * Arguments: ServerConf => pointer to the FTP server configuration * confOption => pointer to the name of the option * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessFTPCmdValidity(FTP_SERVER_PROTO_CONF *ServerConf, char *ErrorString, int ErrStrLen) { FTP_CMD_CONF *FTPCmd = NULL; FTP_PARAM_FMT *HeadFmt = NULL; char *cmd; char *fmt; int iRet; fmt = NextToken(CONF_SEPARATORS); if(fmt == NULL) { snprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", CMD_VALIDITY); return FTPP_FATAL_ERR; } cmd = fmt; fmt = NextToken(CONF_SEPARATORS); if(!fmt) { snprintf(ErrorString, ErrStrLen, "Invalid cmd validity format."); return FTPP_FATAL_ERR; } if(strcmp(START_CMD_FORMAT, fmt)) { snprintf(ErrorString, ErrStrLen, "Must start a cmd validity with the '%s' token.", START_CMD_FORMAT); return FTPP_FATAL_ERR; } HeadFmt = (FTP_PARAM_FMT *)_dpd.snortAlloc(1, sizeof(FTP_PARAM_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (HeadFmt == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } HeadFmt->type = e_head; iRet = DoNextFormat(HeadFmt, 0, ErrorString, ErrStrLen); /* Need to check to be sure we got a complete command */ if (iRet) { return FTPP_FATAL_ERR; } SetOptionalsNext(HeadFmt, NULL, NULL, 0); FTPCmd = ftp_cmd_lookup_find(ServerConf->cmd_lookup, cmd, strlen(cmd), &iRet); if (FTPCmd == NULL) { /* Add it to the list */ // note that struct includes 1 byte for null, so just add len FTPCmd = (FTP_CMD_CONF *)_dpd.snortAlloc(1, sizeof(FTP_CMD_CONF)+strlen(cmd), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (FTPCmd == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } strcpy(FTPCmd->cmd_name, cmd); FTPCmd->max_param_len = ServerConf->def_max_param_len; ftp_cmd_lookup_add(ServerConf->cmd_lookup, cmd, strlen(cmd), FTPCmd); } FTPCmd->check_validity = 1; if (FTPCmd->param_format) { ftpp_ui_config_reset_ftp_cmd_format(FTPCmd->param_format); FTPCmd->param_format = NULL; } FTPCmd->param_format = HeadFmt; return FTPP_SUCCESS; } /* * Function: PrintFormatDate(FTP_DATE_FMT *DateFmt) * * Purpose: Recursively prints the FTP date validation tree * * Arguments: DateFmt => pointer to the date format node * * Returns: None * */ static void PrintFormatDate(char *buf, FTP_DATE_FMT *DateFmt) { FTP_DATE_FMT *OptChild; if (!DateFmt->empty) _dpd.printfappend(buf, BUF_SIZE, "%s", DateFmt->format_string); if (DateFmt->optional) { OptChild = DateFmt->optional; _dpd.printfappend(buf, BUF_SIZE, "["); PrintFormatDate(buf, OptChild); _dpd.printfappend(buf, BUF_SIZE, "]"); } if (DateFmt->next_a) { if (DateFmt->next_b) _dpd.printfappend(buf, BUF_SIZE, "{"); OptChild = DateFmt->next_a; PrintFormatDate(buf, OptChild); if (DateFmt->next_b) { _dpd.printfappend(buf, BUF_SIZE, "|"); OptChild = DateFmt->next_b; PrintFormatDate(buf, OptChild); _dpd.printfappend(buf, BUF_SIZE, "}"); } } if (DateFmt->next) PrintFormatDate(buf, DateFmt->next); } /* * Function: PrintCmdFmt(FTP_PARAM_FMT *CmdFmt) * * Purpose: Recursively prints the FTP command parameter validation tree * * Arguments: CmdFmt => pointer to the parameter validation node * * Returns: None * */ static void PrintCmdFmt(char *buf, FTP_PARAM_FMT *CmdFmt) { FTP_PARAM_FMT *OptChild; switch(CmdFmt->type) { case e_int: _dpd.printfappend(buf, BUF_SIZE, " %s", F_INT); break; case e_number: _dpd.printfappend(buf, BUF_SIZE, " %s", F_NUMBER); break; case e_char: _dpd.printfappend(buf, BUF_SIZE, " %s 0x%x", F_CHAR, CmdFmt->format.chars_allowed); break; case e_date: _dpd.printfappend(buf, BUF_SIZE, " %s", F_DATE); PrintFormatDate(buf, CmdFmt->format.date_fmt); break; case e_literal: _dpd.printfappend(buf, BUF_SIZE, " %s 0x%x", F_LITERAL, CmdFmt->format.literal); break; case e_unrestricted: _dpd.printfappend(buf, BUF_SIZE, " %s", F_STRING); break; case e_strformat: _dpd.printfappend(buf, BUF_SIZE, " %s", F_STRING_FMT); break; case e_host_port: _dpd.printfappend(buf, BUF_SIZE, " %s", F_HOST_PORT); break; case e_long_host_port: _dpd.printfappend(buf, BUF_SIZE, " %s", F_LONG_HOST_PORT); break; case e_extd_host_port: _dpd.printfappend(buf, BUF_SIZE, " %s", F_EXTD_HOST_PORT); break; case e_head: break; default: break; } if (CmdFmt->optional_fmt) { OptChild = CmdFmt->optional_fmt; _dpd.printfappend(buf, BUF_SIZE, "["); PrintCmdFmt(buf, OptChild); _dpd.printfappend(buf, BUF_SIZE, "]"); } if (CmdFmt->numChoices) { int i; _dpd.printfappend(buf, BUF_SIZE, "{"); for (i=0;inumChoices;i++) { if (i) _dpd.printfappend(buf, BUF_SIZE, "|"); OptChild = CmdFmt->choices[i]; PrintCmdFmt(buf, OptChild); } _dpd.printfappend(buf, BUF_SIZE, "}"); } if (CmdFmt->next_param_fmt && CmdFmt->next_param_fmt->prev_optional) PrintCmdFmt(buf, CmdFmt->next_param_fmt); } /* * Function: ProcessFTPMaxRespLen(FTP_CLIENT_PROTO_CONF *ClientConf, * char *ErrorString, int ErrStrLen) * * Purpose: Process the max response length configuration * This sets the max length of an FTP response that we * will tolerate, before alerting. * * Arguments: ClientConf => pointer to the FTP client configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessFTPMaxRespLen(FTP_CLIENT_PROTO_CONF *ClientConf, char *ErrorString, int ErrStrLen) { char *pcToken; char *pcEnd = NULL; long int max_resp_len; pcToken = NextToken(CONF_SEPARATORS); if(pcToken == NULL) { snprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", MAX_RESP_LEN); return FTPP_FATAL_ERR; } max_resp_len = _dpd.SnortStrtol(pcToken, &pcEnd, 10); /* * Let's check to see if the entire string was valid. * If there is an address here, then there was an * invalid character in the string. */ if ((*pcEnd) || (max_resp_len < 0) || (errno == ERANGE)) { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'. Must be a positive " "number.", MAX_RESP_LEN); return FTPP_FATAL_ERR; } ClientConf->max_resp_len = (unsigned int)max_resp_len; return FTPP_SUCCESS; } /* * Function: ParseBounceTo(char *token, FTP_BOUNCE_TO*) * * Purpose: Extract the IP address, masking bits (CIDR format), and * port information from an FTP Bounce To configuration. * * Arguments: token => string pointer to the FTP bounce configuration * required format: IP/CIDR,port[,portHi]\0 * FTP_BOUNCE_TO => populated with parsed data * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int ParseBounceTo(char* token, FTP_BOUNCE_TO* bounce) { char **toks; int num_toks; long int port_lo; char *endptr = NULL; sfcidr_t tmp_ip; toks = _dpd.tokenSplit(token, ",", 3, &num_toks, 0); if (num_toks < 2) return FTPP_INVALID_ARG; if (sfip_pton(toks[0], &tmp_ip) != SFIP_SUCCESS) { _dpd.tokenFree(&toks, num_toks); return FTPP_INVALID_ARG; } memcpy(&bounce->ip, &tmp_ip, sizeof(sfcidr_t)); port_lo = _dpd.SnortStrtol(toks[1], &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (port_lo < 0) || (port_lo >= MAXPORTS)) { _dpd.tokenFree(&toks, num_toks); return FTPP_INVALID_ARG; } bounce->portlo = (unsigned short)port_lo; if (num_toks == 3) { long int port_hi = _dpd.SnortStrtol(toks[2], &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (port_hi < 0) || (port_hi >= MAXPORTS)) { _dpd.tokenFree(&toks, num_toks); return FTPP_INVALID_ARG; } if (bounce->portlo != (unsigned short)port_hi) { bounce->porthi = (unsigned short)port_hi; if (bounce->porthi < bounce->portlo) { unsigned short tmp = bounce->porthi; bounce->porthi = bounce->portlo; bounce->portlo = tmp; } } } _dpd.tokenFree(&toks, num_toks); return FTPP_SUCCESS; } /* * Function: ProcessFTPAlowBounce(FTP_CLIENT_PROTO_CONF *ClientConf, * char *ErrorString, int ErrStrLen) * * Purpose: Process the FTP allow bounce configuration. * This creates an allow bounce node and adds it to the list for the * client configuration. * * Arguments: ClientConf => pointer to the FTP client configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int ProcessFTPAllowBounce(FTP_CLIENT_PROTO_CONF *ClientConf, char *ErrorString, int ErrStrLen) { char *pcToken; int iOneAddr = 0; int iEndList = 0; int iRet; pcToken = NextToken(CONF_SEPARATORS); if(pcToken == NULL) { snprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", ALLOW_BOUNCE); return FTPP_FATAL_ERR; } if(strcmp(START_PORT_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a %s list with the '%s' token.", ALLOW_BOUNCE, START_PORT_LIST); return FTPP_FATAL_ERR; } while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { FTP_BOUNCE_TO *newBounce; if(!strcmp(END_PORT_LIST, pcToken)) { iEndList = 1; break; } /* TODO: Maybe want to redo this with high-speed searcher for ip/port. * Would be great if we could handle both full addresses and * subnets quickly -- using CIDR format. Need something that would * return most specific match -- ie a specific host is more specific * than subnet. */ newBounce = (FTP_BOUNCE_TO *)_dpd.snortAlloc(1, sizeof(FTP_BOUNCE_TO), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (newBounce == NULL) { snprintf(ErrorString, ErrStrLen, "Failed to allocate memory for Bounce"); return FTPP_FATAL_ERR; } iRet = ParseBounceTo(pcToken, newBounce); if (iRet) { snprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s': %s", ALLOW_BOUNCE, pcToken); _dpd.snortFree(newBounce, sizeof(FTP_BOUNCE_TO), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); return FTPP_FATAL_ERR; } iRet = ftp_bounce_lookup_add( ClientConf->bounce_lookup, &newBounce->ip, newBounce ); if (iRet) { snprintf(ErrorString, ErrStrLen, "Failed to add configuration for Bounce object '%s'.", ALLOW_BOUNCE); _dpd.snortFree(newBounce, sizeof(FTP_BOUNCE_TO), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); return FTPP_FATAL_ERR; } iOneAddr = 1; } if(!iEndList) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", ALLOW_BOUNCE, END_PORT_LIST); return FTPP_FATAL_ERR; } if(!iOneAddr) { snprintf(ErrorString, ErrStrLen, "Must include at least one address in '%s' configuration.", ALLOW_BOUNCE); return FTPP_FATAL_ERR; } return FTPP_SUCCESS; } /* * Function: PrintFTPClientConf(char * client, * FTP_CLIENT_PROTO_CONF *ClientConf) * * Purpose: Prints the FTP client configuration * * Arguments: client => string pointer to the client IP * ClientConf => pointer to the client configuration * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int PrintFTPClientConf(char * client, FTP_CLIENT_PROTO_CONF *ClientConf) { FTP_BOUNCE_TO *FTPBounce; int iErr; if(!ClientConf) { return FTPP_INVALID_ARG; } if (!printedFTPHeader) { _dpd.logMsg(" FTP CONFIG:\n"); printedFTPHeader = 1; } _dpd.logMsg(" FTP Client: %s\n", client); PrintConfOpt(&ClientConf->bounce, " Check for Bounce Attacks"); PrintConfOpt(&ClientConf->telnet_cmds, " Check for Telnet Cmds"); PrintConfOpt(&ClientConf->ignore_telnet_erase_cmds, " Ignore Telnet Cmd Operations"); _dpd.logMsg(" Max Response Length: %d\n", ClientConf->max_resp_len); FTPBounce = ftp_bounce_lookup_first(ClientConf->bounce_lookup, &iErr); if (FTPBounce) { _dpd.logMsg(" Allow FTP bounces to:\n"); while (FTPBounce) { char *addr_str; char bits_str[5]; int bits; bits_str[0] = '\0'; addr_str = sfip_to_str(&FTPBounce->ip.addr); bits = (int)FTPBounce->ip.bits; if (bits != 128) { snprintf(bits_str, sizeof(bits_str), "/%d", (sfaddr_family(&FTPBounce->ip.addr) == AF_INET) ? ((bits >= 96) ? (bits - 96) : -1) : bits); } if (FTPBounce->porthi) { _dpd.logMsg(" Address: %s%s, Ports: %d-%d\n", addr_str, bits_str[0] ? bits_str : "", FTPBounce->portlo, FTPBounce->porthi); } else { _dpd.logMsg(" Address: %s%s, Port: %d\n", addr_str, bits_str[0] ? bits_str : "", FTPBounce->portlo); } FTPBounce = ftp_bounce_lookup_next(ClientConf->bounce_lookup, &iErr); } } return FTPP_SUCCESS; } /* * Function: ProcessFTPClientOptions(FTP_CLIENT_PROTO_CONF *ClientConf, * char *ErrorString, int ErrStrLen) * * Purpose: This is where we process the specific ftp client configuration * for FTPTelnet. * * We set the values of the ftp client configuraiton here. Any errors * that are encountered are specified in the error string and the type * of error is returned through the return code, i.e. fatal, non-fatal. * * Arguments: ClientConf => pointer to the client configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int ProcessFTPClientOptions(FTP_CLIENT_PROTO_CONF *ClientConf, char *ErrorString, int ErrStrLen) { FTPTELNET_CONF_OPT *ConfOpt; int iRet; char *pcToken; int iTokens = 0; while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { /* * Show that we at least got one token */ iTokens = 1; /* * Search for configuration keywords */ if(!strcmp(MAX_RESP_LEN, pcToken)) { iRet = ProcessFTPMaxRespLen(ClientConf, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(ALLOW_BOUNCE, pcToken)) { iRet = ProcessFTPAllowBounce(ClientConf, ErrorString, ErrStrLen); if (iRet) { return iRet; } } /* * Start the CONF_OPT configurations. */ else if(!strcmp(BOUNCE, pcToken)) { ConfOpt = &ClientConf->bounce; iRet = ProcessConfOpt(ConfOpt, BOUNCE, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(TELNET_CMDS, pcToken)) { ConfOpt = &ClientConf->telnet_cmds; iRet = ProcessConfOpt(ConfOpt, TELNET_CMDS, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(IGNORE_TELNET_CMDS, pcToken)) { ConfOpt = &ClientConf->ignore_telnet_erase_cmds; iRet = ProcessConfOpt(ConfOpt, IGNORE_TELNET_CMDS, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else { snprintf(ErrorString, ErrStrLen, "Invalid keyword '%s' for '%s' configuration.", pcToken, GLOBAL); return FTPP_FATAL_ERR; } } /* * If there are not any tokens to the configuration, then * we let the user know and log the error. return non-fatal * error. */ if(!iTokens) { snprintf(ErrorString, ErrStrLen, "No tokens to '%s %s' configuration.", FTP, CLIENT); return FTPP_NONFATAL_ERR; } return FTPP_SUCCESS; } /* * Function: ProcessFTPClientConf(FTPTELNET_GLOBAL_CONF *GlobalConf, * char *ErrorString, int ErrStrLen) * * Purpose: This is where we process the ftp client configuration for FTPTelnet. * * We set the values of the ftp client configuraiton here. Any errors * that are encountered are specified in the error string and the type * of error is returned through the return code, i.e. fatal, non-fatal. * * The configuration options that are dealt with here are: * ports { x } Ports on which to do FTP checks * telnet_cmds yes|no Detect telnet cmds on FTP command channel * ignore_telnet_erase_cmds yes|no Do not process telnet EAC and EAL * commands during normalization of FTP command * channel. * max_resp_len x Max response length * bounce yes|no Detect FTP bounce attacks * bounce_to IP port|port-range Allow FTP bounces to specified IP/ports * data_chan Ignore data channel OR coordinate with cmd chan * * Arguments: GlobalConf => pointer to the global configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int ProcessFTPClientConf(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen) { int iRet; int retVal = 0; char *client; char client_list[STD_BUF]; sfcidr_t ipAddr; char *pIpAddressList = NULL; char *pIpAddressList2 = NULL; char *brkt = NULL; char firstIpAddress = 1; FTP_CLIENT_PROTO_CONF *new_client_conf = NULL; //char *ConfigParseResumePtr = NULL; // Use this if a default client conf is added char ip_list = 0; FTP_CLIENT_PROTO_CONF *ftp_conf = NULL; /* * If not default, create one for this IP */ client = NextToken(CONF_SEPARATORS); if ( !client ) { DynamicPreprocessorFatalMessage( "%s(%d) Missing ftp_telnet ftp client address.\n", *(_dpd.config_file), *(_dpd.config_line)); } else if(strcmp(DEFAULT, client)) { /* ** Convert string to IP address */ /// get the first delimiter if(strcmp(START_IPADDR_LIST, client) == 0) { //list begin token matched ip_list = 1; if ((pIpAddressList = mystrtok(NULL, END_IPADDR_LIST)) == NULL) { snprintf(ErrorString, ErrStrLen, "Invalid IP Address list in '%s' token.", CLIENT); retVal = FTPP_INVALID_ARG; goto _return; } } else { //list begin didn't match so this must be an IP address pIpAddressList = client; } //ConfigParseResumePtr = pIpAddressList+strlen(pIpAddressList); pIpAddressList2 = strdup(pIpAddressList); if (!pIpAddressList2) { snprintf(ErrorString, ErrStrLen, "Could not allocate memory for server configuration."); retVal = FTPP_INVALID_ARG; goto _return; } for (client = strtok_r(pIpAddressList2, CONF_SEPARATORS, &brkt); client; client = strtok_r(NULL, CONF_SEPARATORS, &brkt)) { if (sfip_pton(client, &ipAddr) != SFIP_SUCCESS) { snprintf(ErrorString, ErrStrLen, "Invalid IP to '%s' token.", CLIENT); retVal = FTPP_INVALID_ARG; goto _return; } /* ** allocate the memory for the client configuration */ if (firstIpAddress) { // Write this IP into the buffer for printing snprintf(client_list, STD_BUF, "%s", client); new_client_conf = (FTP_CLIENT_PROTO_CONF *)_dpd.snortAlloc(1, sizeof(FTP_CLIENT_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (new_client_conf == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } ftpp_ui_config_reset_ftp_client(new_client_conf, 1); //process the first IP address as usual firstIpAddress = 0; ftp_conf = new_client_conf; } else { // Write this IP into the buffer for printing snprintf(client_list + strlen(client_list), STD_BUF - strlen(client_list) , ", %s", client); new_client_conf = ftp_conf; } ftpp_ui_config_add_ftp_client(GlobalConf, &ipAddr, new_client_conf); //create a reference new_client_conf->referenceCount++; } if (firstIpAddress) { //no IP address was found snprintf(ErrorString, ErrStrLen, "Invalid IP Address list in '%s' token.", CLIENT); retVal = FTPP_INVALID_ARG; goto _return; } } else { /**default configuration */ if (GlobalConf->default_ftp_client != NULL) { snprintf(ErrorString, ErrStrLen, "Cannot configure '%s' settings more than once.", CLIENT); retVal = FTPP_INVALID_ARG; goto _return; } GlobalConf->default_ftp_client = (FTP_CLIENT_PROTO_CONF *)_dpd.snortAlloc(1, sizeof(FTP_CLIENT_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (GlobalConf->default_ftp_client == NULL) { DynamicPreprocessorFatalMessage("Out of memory trying to create " "default ftp client configuration.\n"); } ftpp_ui_config_reset_ftp_client(GlobalConf->default_ftp_client, 0); ftp_conf = GlobalConf->default_ftp_client; //ConfigParseResumePtr = client+strlen(client); } iRet = ProcessFTPClientOptions(ftp_conf, ErrorString, ErrStrLen); if (iRet < 0) { retVal = FTPP_INVALID_ARG; goto _return; } /* * Let's print out the FTP config */ if (ip_list) { client = &client_list[0]; } else if (pIpAddressList2) { client = pIpAddressList2; } PrintFTPClientConf(client, ftp_conf); _return: if (pIpAddressList2) { free(pIpAddressList2); } return retVal; } /* * Function: PrintFTPServerConf(char * server, * FTP_SERVER_PROTO_CONF *ServerConf) * * Purpose: Prints the FTP server configuration * * Arguments: server => string pointer to the server IP * ServerConf => pointer to the server configuration * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static int PrintFTPServerConf(char * server, FTP_SERVER_PROTO_CONF *ServerConf) { const char* spaf = ""; char buf[BUF_SIZE+1]; int iCtr; int iRet; FTP_CMD_CONF *FTPCmd; if(!ServerConf) { return FTPP_INVALID_ARG; } if (!printedFTPHeader) { _dpd.logMsg(" FTP CONFIG:\n"); printedFTPHeader = 1; } if ( _dpd.isPafEnabled() ) spaf = " (PAF)"; _dpd.logMsg(" FTP Server: %s\n", server); memset(buf, 0, BUF_SIZE+1); snprintf(buf, BUF_SIZE, " Ports%s: ", spaf); /* * Print out all the applicable ports. */ for(iCtr = 0; iCtr < MAXPORTS; iCtr++) { if(ServerConf->proto_ports.ports[iCtr]) { _dpd.printfappend(buf, BUF_SIZE, "%d ", iCtr); } } _dpd.logMsg("%s\n", buf); PrintConfOpt(&ServerConf->telnet_cmds, " Check for Telnet Cmds"); PrintConfOpt(&ServerConf->ignore_telnet_erase_cmds, " Ignore Telnet Cmd Operations"); _dpd.logMsg(" Ignore open data channels: %s\n", ServerConf->data_chan ? "YES" : "NO"); if (ServerConf->print_commands) { _dpd.logMsg(" FTP Commands:\n"); FTPCmd = ftp_cmd_lookup_first(ServerConf->cmd_lookup, &iRet); while (FTPCmd != NULL) { memset(buf, 0, BUF_SIZE+1); snprintf(buf, BUF_SIZE, " %s { %d ", FTPCmd->cmd_name, FTPCmd->max_param_len); #ifdef PRINT_DEFAULT_CONFIGS if (FTPCmd->data_chan_cmd) snprintf(buf, BUF_SIZE, "%s data_chan "); if (FTPCmd->data_xfer_cmd) snprintf(buf, BUF_SIZE, "%s data_xfer "); if (FTPCmd->encr_cmd) snprintf(buf, BUF_SIZE, "%s encr "); #endif if (FTPCmd->check_validity) { FTP_PARAM_FMT *CmdFmt = FTPCmd->param_format; while (CmdFmt != NULL) { PrintCmdFmt(buf, CmdFmt); CmdFmt = CmdFmt->next_param_fmt; } } _dpd.logMsg("%s}\n", buf); FTPCmd = ftp_cmd_lookup_next(ServerConf->cmd_lookup, &iRet); } } return FTPP_SUCCESS; } /* * Function: ProcessFTPServerOptions(FTP_SERVER_PROTO_CONF *ServerConf, * char *ErrorString, int ErrStrLen) * * Purpose: This is where we process the specific ftp server configuration * for FTPTelnet. * * We set the values of the ftp server configuraiton here. Any errors * that are encountered are specified in the error string and the type * of error is returned through the return code, i.e. fatal, non-fatal. * * Arguments: ServerConf => pointer to the server configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int ProcessFTPServerOptions(FTP_SERVER_PROTO_CONF *ServerConf, char *ErrorString, int ErrStrLen) { FTPTELNET_CONF_OPT *ConfOpt; int iRet = 0; char *pcToken; int iTokens = 0; int data_chan_configured = 0; while ((pcToken = NextToken(CONF_SEPARATORS)) != NULL) { /* * Show that we at least got one token */ iTokens = 1; /* * Search for configuration keywords */ if(!strcmp(PORTS, pcToken)) { PROTO_CONF *ports = (PROTO_CONF*)&ServerConf->proto_ports; iRet = ProcessPorts(ports, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(FTP_CMDS, pcToken)) { iRet = ProcessFTPCmdList(ServerConf, FTP_CMDS, ErrorString, ErrStrLen, 1, 0); if (iRet) { return iRet; } } else if(!strcmp(MAX_PARAM_LEN, pcToken)) { iRet = ProcessFTPCmdList(ServerConf, MAX_PARAM_LEN, ErrorString, ErrStrLen, 0, 1); if (iRet) { return iRet; } } else if(!strcmp(ALT_PARAM_LEN, pcToken)) { iRet = ProcessFTPCmdList(ServerConf, ALT_PARAM_LEN, ErrorString, ErrStrLen, 1, 1); if (iRet) { return iRet; } } else if(!strcmp(CMD_VALIDITY, pcToken)) { iRet = ProcessFTPCmdValidity(ServerConf, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(STRING_FORMAT, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(DATA_CHAN_CMD, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(DATA_XFER_CMD, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(DATA_REST_CMD, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(FILE_PUT_CMD, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(FILE_GET_CMD, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(ENCR_CMD, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(LOGIN_CMD, pcToken)) { iRet = ProcessFTPDataChanCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(DIR_CMD, pcToken)) { iRet = ProcessFTPDirCmdsList(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if (!strcmp(DATA_CHAN, pcToken)) { if (data_chan_configured && ServerConf->data_chan == 0) { snprintf(ErrorString, ErrStrLen, "Both 'data_chan' and " "'ignore_data_chan' configured with conflicting options."); return FTPP_FATAL_ERR; } else { ServerConf->data_chan = 1; data_chan_configured = 1; } } else if (!strcmp(PRINT_CMDS, pcToken)) { ServerConf->print_commands = 1; } else if (!strcmp(IGNORE_DATA_CHAN, pcToken)) { iRet = ProcessFTPIgnoreDataChan(ServerConf, pcToken, ErrorString, ErrStrLen); if (iRet) { return iRet; } data_chan_configured = 1; } /* * Start the CONF_OPT configurations. */ else if(!strcmp(TELNET_CMDS, pcToken)) { ConfOpt = &ServerConf->telnet_cmds; iRet = ProcessConfOpt(ConfOpt, TELNET_CMDS, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else if(!strcmp(IGNORE_TELNET_CMDS, pcToken)) { ConfOpt = &ServerConf->ignore_telnet_erase_cmds; iRet = ProcessConfOpt(ConfOpt, IGNORE_TELNET_CMDS, ErrorString, ErrStrLen); if (iRet) { return iRet; } } else { snprintf(ErrorString, ErrStrLen, "Invalid keyword '%s' for '%s' configuration.", pcToken, GLOBAL); return FTPP_FATAL_ERR; } } /* * If there are not any tokens to the configuration, then * we let the user know and log the error. return non-fatal * error. */ if(!iTokens) { snprintf(ErrorString, ErrStrLen, "No tokens to '%s %s' configuration.", FTP, SERVER); return FTPP_NONFATAL_ERR; } return FTPP_SUCCESS; } int parseFtpServerConfigStr( FTP_SERVER_PROTO_CONF *ftp_conf, char *ConfigParseResumePtr, char ip_list, char *ErrorString, int ErrStrLen ) { int iRet = 0; char *saveMaxToken = maxToken; size_t default_conf_len; char *default_conf_str = DefaultConf(&default_conf_len); /* First, process the default configuration -- namely, the * list of FTP commands, and the parameter validation checks */ maxToken = default_conf_str + default_conf_len; mystrtok(default_conf_str, CONF_SEPARATORS); iRet = ProcessFTPServerOptions(ftp_conf, ErrorString, ErrStrLen); _dpd.snortFree(default_conf_str, default_conf_len, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); maxToken = saveMaxToken; if (iRet < 0) return iRet; /* Okay, now we need to reset the mystrtok pointers so we can process * the specific server configuration. Quick hack/trick here: reset * the end of the client string to a conf separator, then call mystrtok. * That will reset mystrtok's internal pointer to the next token after * the client name, which is what we're expecting it to be. */ if (ConfigParseResumePtr < maxToken) { /* only if there is data after the server/client name */ if (ip_list) *ConfigParseResumePtr-- = END_IPADDR_LIST[0]; else *ConfigParseResumePtr-- = CONF_SEPARATORS[0]; mystrtok(ConfigParseResumePtr, CONF_SEPARATORS); iRet = ProcessFTPServerOptions(ftp_conf, ErrorString, ErrStrLen); if (iRet < 0) return iRet; } return iRet; } void enableFtpTelnetPortStreamServices( struct _SnortConfig *sc, PROTO_CONF *pc, char *network, int direction ) { uint32_t port; for ( port = 0; port < MAXPORTS; port++ ) { if( pc->ports[ port ] ) { _dpd.streamAPI->register_reassembly_port( network, port, direction ); _dpd.sessionAPI->enable_preproc_for_port( sc, PP_FTPTELNET, PROTO_BIT__TCP, port ); } } } /* * Function: ProcessFTPServerConf:: * * Purpose: This is where we process the ftp server configuration for FTPTelnet. * * We set the values of the ftp server configuraiton here. Any * errors that are encountered are specified in the error string and * the type of error is returned through the return code, i.e. fatal, * non-fatal. * * The configuration options that are dealt with here are: * ports { x } Ports on which to do FTP checks * ftp_cmds { CMD1 CMD2 ... } Valid FTP commands * def_max_param_len x Default max param length * alt_max_param_len x { CMD1 ... } Override default max param len * for CMD * chk_str_fmt { CMD1 ...} Detect string format attacks for CMD * cmd_validity CMD < fmt > Check the parameter validity for CMD * fmt is as follows: * int Param is an int * char _chars Param is one of _chars * date _datefmt Param follows format specified where * # = Number, C=Char, []=optional, |=OR, * +-.=literal * [] Optional parameters * string Param is string (unrestricted) * data_chan Ignore data channel * * Arguments: GlobalConf => pointer to the global configuration * ErrorString => error string buffer * ErrStrLen => the length of the error string buffer * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int ProcessFTPServerConf( struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen ) { int iRet = 0; int retVal = 0; char *server; char server_list[STD_BUF]; sfcidr_t ipAddr; char *pIpAddressList = NULL; char *pIpAddressList2 = NULL; char *brkt = NULL; char firstIpAddress = 1; FTP_SERVER_PROTO_CONF *new_server_conf = NULL; char *ConfigParseResumePtr = NULL; char ip_list = 0; FTP_SERVER_PROTO_CONF *ftp_conf = NULL; /* * If not default, create one for this IP */ server = NextToken(CONF_SEPARATORS); if ( !server ) { DynamicPreprocessorFatalMessage( "%s(%d) Missing ftp_telnet ftp server address.\n", *(_dpd.config_file), *(_dpd.config_line)); } else if(strcmp(DEFAULT, server)) { if(strcmp(START_IPADDR_LIST, server) == 0) { //list begin token matched ip_list = 1; if ((pIpAddressList = mystrtok(NULL, END_IPADDR_LIST)) == NULL) { snprintf(ErrorString, ErrStrLen, "Invalid IP Address list in '%s' token.", SERVER); retVal = FTPP_INVALID_ARG; goto _return; } } else { //list begin didn't match so this must be an IP address pIpAddressList = server; } ConfigParseResumePtr = pIpAddressList+strlen(pIpAddressList); pIpAddressList2 = strdup(pIpAddressList); if (!pIpAddressList2) { snprintf(ErrorString, ErrStrLen, "Could not allocate memory for server configuration."); retVal = FTPP_INVALID_ARG; goto _return; } for (server = strtok_r(pIpAddressList2, CONF_SEPARATORS, &brkt); server; server = strtok_r(NULL, CONF_SEPARATORS, &brkt)) { if (sfip_pton(server, &ipAddr) != SFIP_SUCCESS) { snprintf(ErrorString, ErrStrLen, "Invalid IP to '%s' token.", SERVER); retVal = FTPP_INVALID_ARG; goto _return; } if (firstIpAddress) { /* Write this IP into the buffer for printing */ snprintf(server_list, STD_BUF, "%s", server); new_server_conf = (FTP_SERVER_PROTO_CONF *)_dpd.snortAlloc(1, sizeof(FTP_SERVER_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (new_server_conf == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => Failed to allocate memory\n", *(_dpd.config_file), *(_dpd.config_line)); } ftpp_ui_config_reset_ftp_server(new_server_conf, 1); new_server_conf->serverAddr = strdup(server); if (new_server_conf->serverAddr == NULL) { DynamicPreprocessorFatalMessage("ProcessFTPServerConf(): Out of memory allocing serverAddr.\n"); } ftp_conf = new_server_conf; iRet = parseFtpServerConfigStr( ftp_conf, ConfigParseResumePtr, ip_list, ErrorString, ErrStrLen ); if (iRet) { retVal = iRet; goto _return; } //process the first IP address as usual firstIpAddress = 0; } else { /* Write this IP into the buffer for printing */ snprintf(server_list + strlen(server_list), STD_BUF - strlen(server_list) , ", %s", server); new_server_conf = ftp_conf; } ftpp_ui_config_add_ftp_server(GlobalConf, &ipAddr, new_server_conf); enableFtpTelnetPortStreamServices( sc, &ftp_conf->proto_ports, server, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); //create a reference new_server_conf->referenceCount++; } if (firstIpAddress) { //no IP address was found snprintf(ErrorString, ErrStrLen, "Invalid IP Address list in '%s' token.", CLIENT); retVal = FTPP_INVALID_ARG; goto _return; } } else { if (GlobalConf->default_ftp_server != NULL) { snprintf(ErrorString, ErrStrLen, "Cannot configure '%s' settings more than once.", SERVER); retVal = FTPP_INVALID_ARG; goto _return; } GlobalConf->default_ftp_server = (FTP_SERVER_PROTO_CONF *)_dpd.snortAlloc(1, sizeof(FTP_SERVER_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); if (GlobalConf->default_ftp_server == NULL) { DynamicPreprocessorFatalMessage("Out of memory trying to create " "default ftp server configuration.\n"); } ftpp_ui_config_reset_ftp_server(GlobalConf->default_ftp_server, 0); ftp_conf = GlobalConf->default_ftp_server; ConfigParseResumePtr = server+strlen(server); GlobalConf->default_ftp_server->serverAddr = strdup("default"); if (GlobalConf->default_ftp_server->serverAddr == NULL) { _dpd.snortFree(GlobalConf->default_ftp_server, sizeof(FTP_SERVER_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); DynamicPreprocessorFatalMessage("Out of memory trying to create " "default ftp server configuration.\n"); } iRet = parseFtpServerConfigStr( ftp_conf, ConfigParseResumePtr, ip_list, ErrorString, ErrStrLen ); if (iRet) { retVal = iRet; goto _return; } enableFtpTelnetPortStreamServices( sc, &ftp_conf->proto_ports, NULL, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); } /* * Let's print out the FTP config */ if (ip_list) { server = &server_list[0]; } else if (pIpAddressList2) { server = pIpAddressList2; } PrintFTPServerConf(server, ftp_conf); _return: if (pIpAddressList2) { free(pIpAddressList2); } return retVal; } /* * Function: PrintFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf) * * Purpose: Prints the FTPTelnet preprocessor global configuration * * Arguments: GlobalConf => pointer to the global configuration * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int PrintFTPGlobalConf(FTPTELNET_GLOBAL_CONF *GlobalConf) { _dpd.logMsg("FTPTelnet Config:\n"); _dpd.logMsg(" GLOBAL CONFIG\n"); _dpd.logMsg(" Inspection Type: %s\n", GlobalConf->inspection_type == FTPP_UI_CONFIG_STATELESS ? "stateless" : "stateful"); PrintConfOpt(&GlobalConf->encrypted, "Check for Encrypted Traffic"); _dpd.logMsg(" Continue to check encrypted data: %s\n", GlobalConf->check_encrypted_data ? "YES" : "NO"); return FTPP_SUCCESS; } void FTPTelnetCleanupFTPCMDConf(void *ftpCmd) { FTP_CMD_CONF *FTPCmd = (FTP_CMD_CONF *)ftpCmd; /* Free the FTP_PARAM_FMT stuff... */ ftpp_ui_config_reset_ftp_cmd(FTPCmd); _dpd.snortFree(FTPCmd, sizeof(FTP_CMD_CONF)+strlen(FTPCmd->cmd_name), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } void FTPTelnetCleanupFTPServerConf(void *serverConf) { FTP_SERVER_PROTO_CONF *ServerConf = (FTP_SERVER_PROTO_CONF*)serverConf; if (ServerConf == NULL) return; free(ServerConf->serverAddr); ServerConf->serverAddr = NULL; /* Iterate through each cmd_lookup for this server */ ftp_cmd_lookup_cleanup(&ServerConf->cmd_lookup); } void FTPTelnetCleanupFTPBounceTo(void *ftpBounce) { FTP_BOUNCE_TO *FTPBounce = (FTP_BOUNCE_TO *)ftpBounce; _dpd.snortFree(FTPBounce, sizeof(FTP_BOUNCE_TO), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } void FTPTelnetCleanupFTPClientConf(void *clientConf) { FTP_CLIENT_PROTO_CONF *ClientConf = (FTP_CLIENT_PROTO_CONF*)clientConf; if (ClientConf == NULL) return; /* Iterate through each bounce_lookup for this client */ ftp_bounce_lookup_cleanup(&ClientConf->bounce_lookup); } static int FTPTelnetFreeConfigsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { FTPTELNET_GLOBAL_CONF *pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)pData; //do any housekeeping before freeing FTPTELNET_GLOBAL_CONF sfPolicyUserDataClear (config, policyId); FTPTelnetFreeConfig(pPolicyConfig); return 0; } void FTPTelnetFreeConfigs(tSfPolicyUserContextId GlobalConf) { if (GlobalConf == NULL) return; sfPolicyUserDataFreeIterate(GlobalConf, FTPTelnetFreeConfigsPolicy); sfPolicyConfigDelete(GlobalConf); } void FTPTelnetFreeConfig(FTPTELNET_GLOBAL_CONF *GlobalConf) { if (GlobalConf == NULL) return; if (GlobalConf->default_ftp_client != NULL) { FTPTelnetCleanupFTPClientConf((void *)GlobalConf->default_ftp_client); _dpd.snortFree(GlobalConf->default_ftp_client, sizeof(FTP_CLIENT_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } if (GlobalConf->default_ftp_server != NULL) { FTPTelnetCleanupFTPServerConf((void *)GlobalConf->default_ftp_server); _dpd.snortFree(GlobalConf->default_ftp_server, sizeof(FTP_SERVER_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } if (GlobalConf->telnet_config != NULL) _dpd.snortFree(GlobalConf->telnet_config, sizeof(TELNET_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); ftpp_ui_client_lookup_cleanup(&GlobalConf->client_lookup); ftpp_ui_server_lookup_cleanup(&GlobalConf->server_lookup); _dpd.snortFree(GlobalConf, sizeof(FTPTELNET_GLOBAL_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } /* * Function: FTPTelnetCheckFTPCmdOptions(FTP_SERVER_PROTO_CONF *serverConf) * * Purpose: This checks that the FTP configuration provided has * options for CMDs that make sense: * -- check if max_len == 0 & there is a cmd_validity * * Arguments: serverConf => pointer to Server Configuration * * Returns: 0 => no errors * 1 => errors * */ int FTPTelnetCheckFTPCmdOptions(FTP_SERVER_PROTO_CONF *serverConf) { FTP_CMD_CONF *cmdConf; int iRet =0; int config_error = 0; cmdConf = ftp_cmd_lookup_first(serverConf->cmd_lookup, &iRet); while (cmdConf && (iRet == FTPP_SUCCESS)) { size_t len = strlen(cmdConf->cmd_name); if ( len > serverConf->max_cmd_len ) serverConf->max_cmd_len = len; if (cmdConf->check_validity && (cmdConf->max_param_len == 0)) { _dpd.errMsg("FTPConfigCheck() configuration for server '%s', " "command '%s' has max length of 0 and parameters to validate\n", serverConf->serverAddr, cmdConf->cmd_name); config_error = 1; } cmdConf = ftp_cmd_lookup_next(serverConf->cmd_lookup, &iRet); } return config_error; } /* * Function: FTPTelnetCheckFTPServerConfigs(void) * * Purpose: This checks that the FTP server configurations are reasonable * * Arguments: None * * Returns: -1 on error * */ int FTPTelnetCheckFTPServerConfigs(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *config) { FTP_SERVER_PROTO_CONF *serverConf; int iRet = 0; int rval; if (config == NULL) return 0; if ((rval = ftpp_ui_server_iterate(sc, config->server_lookup, _checkServerConfig, &iRet))) return rval; serverConf = config->default_ftp_server; if (FTPTelnetCheckFTPCmdOptions(serverConf)) { _dpd.errMsg("FTPConfigCheck(): invalid configuration for FTP commands\n"); return -1; } return 0; } static int _checkServerConfig(struct _SnortConfig *sc, void *pData) { FTP_SERVER_PROTO_CONF *serverConf = (FTP_SERVER_PROTO_CONF *)pData; if (FTPTelnetCheckFTPCmdOptions(serverConf)) { _dpd.errMsg("FTPConfigCheck(): invalid configuration for FTP commands\n"); return -1; } return 0; } /* * Function: FTPConfigCheck(void) * * Purpose: This checks that the FTP configuration provided includes * the default configurations for Server & Client. * * Arguments: None * * Returns: None * */ int FTPTelnetCheckConfigs(struct _SnortConfig *sc, void* pData, tSfPolicyId policyId) { int rval; FTPTELNET_GLOBAL_CONF *pPolicyConfig = (FTPTELNET_GLOBAL_CONF *)pData; if ( pPolicyConfig == NULL ) return 0; if ((pPolicyConfig->default_ftp_server == NULL) || (pPolicyConfig->default_ftp_client == NULL)) { _dpd.errMsg("FTP/Telnet configuration requires " "default client and default server configurations.\n"); return -1; } if ( pPolicyConfig->telnet_config == NULL ) { ProcessTelnetConf(pPolicyConfig,"",0); } if ((pPolicyConfig->telnet_config->ayt_threshold > 0) && !pPolicyConfig->telnet_config->normalize) { _dpd.errMsg("WARNING: Telnet Configuration Check: using an " "AreYouThere threshold requires telnet normalization to be " "turned on.\n"); } if ((pPolicyConfig->encrypted.alert != 0) && !pPolicyConfig->telnet_config->normalize) { _dpd.errMsg("WARNING: Telnet Configuration Check: checking for " "encrypted traffic requires telnet normalization to be turned " "on.\n"); } /* So we don't have to check it every time we use it */ if ((!_dpd.streamAPI) || (_dpd.streamAPI->version < STREAM_API_VERSION5)) { _dpd.errMsg("FTPConfigCheck() Streaming & reassembly must be " "enabled\n"); return -1; } _dpd.setParserPolicy(sc, policyId); /* Add FTPTelnet into the preprocessor list */ #ifdef TARGET_BASED if ( _dpd.fileAPI->get_max_file_depth(sc, true) >= 0 ) { _dpd.addPreproc(sc, FTPDataTelnetChecks, PRIORITY_APPLICATION, PP_FTPTELNET, PROTO_BIT__TCP); s_ftpdata_eof_cb_id = _dpd.streamAPI->register_event_handler(SnortFTPData_EOF); s_ftpdata_flush_cb_id = _dpd.streamAPI->register_ftp_flush_cb(SnortFTPData_Flush); } else #endif { _dpd.addPreproc(sc, FTPTelnetChecks, PRIORITY_APPLICATION, PP_FTPTELNET, PROTO_BIT__TCP); } if ((rval = FTPTelnetCheckFTPServerConfigs(sc, pPolicyConfig))) return rval; _FTPTelnetAddPortsOfInterest(sc, pPolicyConfig, policyId); #ifdef TARGET_BASED _FTPTelnetAddService(sc, ftp_app_id, policyId); #endif return 0; } static int FTPConfigCheckPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { return FTPTelnetCheckConfigs(sc, pData, policyId); } int FTPConfigCheck(struct _SnortConfig *sc) { int rval; if (ftp_telnet_config == NULL) return 0; if ((rval = sfPolicyUserDataIterate (sc, ftp_telnet_config, FTPConfigCheckPolicy))) return rval; return 0; } /* * Function: LogFTPPEvents(FTPP_GEN_EVENTS *GenEvents, * int iGenerator) * * Purpose: This is the routine that logs FTP/Telnet Preprocessor (FTPP) * alerts through Snort. * * Every Session gets looked at for any logged events, and if * there are events to be logged then we select the one with the * highest priority. * * We use a generic event structure that we set for each different * event structure. This way we can use the same code for event * logging regardless of what type of event strucure we are dealing * with. * * The important things to know about this function is how to work * with the event queue. The number of unique events is contained * in the stack_count variable. So we loop through all the unique * events and find which one has the highest priority. During this * loop, we also re-initialize the individual event counts for the * next iteration, saving us time in a separate initialization phase. * * After we've iterated through all the events and found the one * with the highest priority, we then log that event through snort. * * We've mapped the FTPTelnet and the Snort alert IDs together, so * we can access them directly instead of having a more complex * mapping function. * * Arguments: GenEvents => pointer a list of events * iGenerator => Generator ID (Telnet or FTP) * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static inline int LogFTPPEvents(FTPP_GEN_EVENTS *GenEvents, int iGenerator) { FTPP_EVENT *OrigEvent; FTPP_EVENT *HiEvent = NULL; int iStackCnt; int iEvent; int iCtr; /* * Now starts the generic event processing */ iStackCnt = GenEvents->stack_count; /* * IMPORTANT:: * We have to check the stack count of the event queue before we process * an log. */ if(iStackCnt == 0) { return FTPP_SUCCESS; } /* * Cycle through the events and select the event with the highest * priority. */ for(iCtr = 0; iCtr < iStackCnt; iCtr++) { iEvent = GenEvents->stack[iCtr]; OrigEvent = &(GenEvents->events[iEvent]); /* * Set the event to start off the comparison */ if(!HiEvent) { HiEvent = OrigEvent; } /* * This is our "comparison function". Log the event with the highest * priority. */ if(OrigEvent->event_info->priority < HiEvent->event_info->priority) { HiEvent = OrigEvent; } /* * IMPORTANT: * This is how we reset the events in the event queue. * If you miss this step, you can be really screwed. */ OrigEvent->count = 0; } if (!HiEvent) return FTPP_SUCCESS; /* * We use the iEvent+1 because the event IDs between snort and * FTPTelnet are mapped off-by-one. They're mapped off-by one * because in the internal FTPTelnet queue, events are mapped * starting at 0. For some reason, it appears that the first * event can't be zero, so we use the internal value and add * one for snort. */ iEvent = HiEvent->event_info->alert_id + 1; /* GenID, SID, Rev, Classification, Pri, Msg, RuleInfo */ _dpd.alertAdd(iGenerator, HiEvent->event_info->alert_sid, 1, /* Revision 1 */ HiEvent->event_info->classification, HiEvent->event_info->priority, HiEvent->event_info->alert_str, NULL); /* No Rule info */ /* * Reset the event queue stack counter, in the case of pipelined * requests. */ GenEvents->stack_count = 0; return FTPP_SUCCESS; } /* * Function: LogFTPEvents(FTP_SESSION *FtpSession) * * Purpose: This is the routine that logs FTP alerts through Snort. * It maps the event into a generic event and calls * LOGFTPPEvents(). * * Arguments: FtpSession => pointer the session structure * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static inline int LogFTPEvents(FTP_SESSION *FtpSession) { FTPP_GEN_EVENTS GenEvents; int iGenerator; int iRet; GenEvents.stack = FtpSession->event_list.stack; GenEvents.stack_count = FtpSession->event_list.stack_count; GenEvents.events = FtpSession->event_list.events; iGenerator = GENERATOR_SPP_FTPP_FTP; iRet = LogFTPPEvents(&GenEvents, iGenerator); /* Reset the count... */ FtpSession->event_list.stack_count = 0; return iRet; } /* * Function: LogTelnetEvents(TELNET_SESSION *TelnetSession) * * Purpose: This is the routine that logs Telnet alerts through Snort. * It maps the event into a generic event and calls * LOGFTPPEvents(). * * Arguments: TelnetSession => pointer the session structure * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static inline int LogTelnetEvents(TELNET_SESSION *TelnetSession) { FTPP_GEN_EVENTS GenEvents; int iGenerator; int iRet; GenEvents.stack = TelnetSession->event_list.stack; GenEvents.stack_count = TelnetSession->event_list.stack_count; GenEvents.events = TelnetSession->event_list.events; iGenerator = GENERATOR_SPP_FTPP_TELNET; iRet = LogFTPPEvents(&GenEvents, iGenerator); /* Reset the count... */ TelnetSession->event_list.stack_count = 0; return iRet; } /* * Function: SetSiInput(FTPP_SI_INPUT *SiInput, Packet *p) * * Purpose: This is the routine sets the source and destination IP * address and port pairs so as to determine the direction * of the FTP or telnet connection. * * Arguments: SiInput => pointer the session input structure * p => pointer to the packet structure * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ static inline int SetSiInput(FTPP_SI_INPUT *SiInput, SFSnortPacket *p) { IP_COPY_VALUE(SiInput->sip, GET_SRC_IP(p)); IP_COPY_VALUE(SiInput->dip, GET_DST_IP(p)); SiInput->sport = p->src_port; SiInput->dport = p->dst_port; /* * We now set the packet direction */ if(p->stream_session && _dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM) { SiInput->pdir = FTPP_SI_NO_MODE; } else if(p->flags & FLAG_FROM_SERVER) { SiInput->pdir = FTPP_SI_SERVER_MODE; } else if(p->flags & FLAG_FROM_CLIENT) { SiInput->pdir = FTPP_SI_CLIENT_MODE; } else { SiInput->pdir = FTPP_SI_NO_MODE; } return FTPP_SUCCESS; } /* * Function: do_detection(Packet *p) * * Purpose: This is the routine that directly performs the rules checking * for each of the FTP & telnet preprocessing modules. * * Arguments: p => pointer to the packet structure * * Returns: None * */ void do_detection(SFSnortPacket *p) { //extern int do_detect; //extern OptTreeNode *otn_tmp; PROFILE_VARS; /* * If we get here we either had a client or server request/response. * We do the detection here, because we're starting a new paradigm * about protocol decoders. * * Protocol decoders are now their own detection engine, since we are * going to be moving protocol field detection from the generic * detection engine into the protocol module. This idea scales much * better than having all these Packet struct field checks in the * main detection engine for each protocol field. */ PREPROC_PROFILE_START(ftppDetectPerfStats); _dpd.detect(p); _dpd.disableAllDetect(p); PREPROC_PROFILE_END(ftppDetectPerfStats); #ifdef PERF_PROFILING ftppDetectCalled = 1; #endif //otn_tmp = NULL; /* * We set the global detection flag here so that if request pipelines * fail, we don't do any detection. */ //do_detect = 0; } /* * Function: SnortTelnet(FTPTELNET_GLOBAL_CONF *GlobalConf, * Packet *p, * int iInspectMode) * * Purpose: This is the routine that handles the protocol layer checks * for telnet. * * Arguments: GlobalConf => pointer the global configuration * p => pointer to the packet structure * iInspectMode => indicator whether this is a client or server * packet. * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int SnortTelnet(FTPTELNET_GLOBAL_CONF *GlobalConf, TELNET_SESSION *TelnetSession, SFSnortPacket *p, int iInspectMode) { int iRet; PROFILE_VARS; #ifdef DUMP_BUFFER dumpBuffer(TELNET_DUMP,(const char *)p->payload,p->payload_size); #endif if (!TelnetSession) { if (GlobalConf->inspection_type == FTPP_UI_CONFIG_STATEFUL) { return FTPP_NONFATAL_ERR; } else { return FTPP_INVALID_SESSION; } } if (TelnetSession->encr_state && !GlobalConf->check_encrypted_data) { return FTPP_SUCCESS; } PREPROC_PROFILE_START(telnetPerfStats); if (!GlobalConf->telnet_config->normalize) { do_detection(p); } else { iRet = normalize_telnet(GlobalConf, TelnetSession, p, iInspectMode, FTPP_APPLY_TNC_ERASE_CMDS); if ((iRet == FTPP_SUCCESS) || (iRet == FTPP_NORMALIZED)) { do_detection(p); } LogTelnetEvents(TelnetSession); } PREPROC_PROFILE_END(telnetPerfStats); #ifdef PERF_PROFILING if (ftppDetectCalled) { telnetPerfStats.ticks -= ftppDetectPerfStats.ticks; /* And Reset ticks to 0 */ ftppDetectPerfStats.ticks = 0; ftppDetectCalled = 0; } #endif return FTPP_SUCCESS; } /* * Function: SnortFTP(FTPTELNET_GLOBAL_CONF *GlobalConf, * Packet *p, * int iInspectMode) * * Purpose: This is the routine that handles the protocol layer checks * for FTP. * * Arguments: GlobalConf => pointer the global configuration * p => pointer to the packet structure * iInspectMode => indicator whether this is a client or server * packet. * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int SnortFTP(FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_SESSION *FTPSession, SFSnortPacket *p, int iInspectMode) { int iRet; ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); PROFILE_VARS; if (!FTPSession || FTPSession->server_conf == NULL || FTPSession->client_conf == NULL) { return FTPP_INVALID_SESSION; } if(((FTPSession->encr_state == AUTH_TLS_ENCRYPTED) || (FTPSession->encr_state == AUTH_SSL_ENCRYPTED) || (FTPSession->encr_state == AUTH_UNKNOWN_ENCRYPTED)) ) { if ((iInspectMode == FTPP_SI_CLIENT_MODE) && FTPSession->encr_state_chello ) { if (IsTlsClientHello(p->payload, p->payload + p->payload_size)) { FTPSession->encr_state_chello = false; if(ssl_cb) ssl_cb->session_initialize(p, FTPSession, FTP_Set_flow_id); } } if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) FTPSession->encr_state = 0; else if (!GlobalConf->check_encrypted_data) return FTPP_SUCCESS; } PREPROC_PROFILE_START(ftpPerfStats); if (iInspectMode == FTPP_SI_SERVER_MODE) { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Server packet: %.*s\n", p->payload_size, p->payload)); // FIXTHIS breaks target-based non-standard ports //if ( !_dpd.isPafEnabled() ) /* Force flush of client side of stream */ _dpd.streamAPI->response_flush_stream(p); } else { if ( !_dpd.readyForProcess(p) ) { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Client packet will be reassembled\n")); PREPROC_PROFILE_END(ftpPerfStats); return FTPP_SUCCESS; } else { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "Client packet: rebuilt %s: %.*s\n", (p->flags & FLAG_REBUILT_STREAM) ? "yes" : "no", p->payload_size, p->payload)); } } iRet = initialize_ftp(FTPSession, p, iInspectMode); if (iRet) { LogFTPEvents(FTPSession); PREPROC_PROFILE_END(ftpPerfStats); return iRet; } iRet = check_ftp(FTPSession, p, iInspectMode); if (iRet == FTPP_SUCCESS) { /* Ideally, Detect(), called from do_detection, will look at * the cmd & param buffers, or the rsp & msg buffers. Current * architecture does not support this... * So, we call do_detection() here. Otherwise, we'd call it * from inside check_ftp -- each time we process a pipelined * FTP command. */ do_detection(p); } LogFTPEvents(FTPSession); PREPROC_PROFILE_END(ftpPerfStats); #ifdef PERF_PROFILING if (ftppDetectCalled) { ftpPerfStats.ticks -= ftppDetectPerfStats.ticks; /* And Reset ticks to 0 */ ftppDetectPerfStats.ticks = 0; ftppDetectCalled = 0; } #endif return iRet; } /* * Funtcion: SnortFTPTelnet * * Purpose: This function calls the FTPTelnet function that handles * the protocol layer checks for an FTP or Telnet session, * after determining which, if either, protocol applies. * * Arguments: GlobalConf => pointer the global configuration * p => pointer to the packet structure * * Returns: int => an error code integer (0 = success, * >0 = non-fatal error, <0 = fatal error) * */ int SnortFTPTelnet(SFSnortPacket *p) { FTPP_SI_INPUT SiInput; int iInspectMode = FTPP_SI_NO_MODE; FTP_TELNET_SESSION *ft_ssn = NULL; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); FTPTELNET_GLOBAL_CONF *GlobalConf = NULL; #ifdef DUMP_BUFFER dumpBufferInit(); #endif sfPolicyUserPolicySet (ftp_telnet_config, policy_id); GlobalConf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGetCurrent(ftp_telnet_config); /* * Set up the FTPP_SI_INPUT pointer. This is what the session_inspection() * routines use to determine client and server traffic. Plus, this makes * the FTPTelnet library very independent from snort. */ SetSiInput(&SiInput, p); if (p->stream_session) { ft_ssn = (FTP_TELNET_SESSION *) _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET); if (ft_ssn != NULL) { SiInput.pproto = ft_ssn->proto; if (ft_ssn->proto == FTPP_SI_PROTO_TELNET) { TELNET_SESSION *telnet_ssn = (TELNET_SESSION *)ft_ssn; GlobalConf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(telnet_ssn->global_conf, telnet_ssn->policy_id); if (SiInput.pdir != FTPP_SI_NO_MODE) { iInspectMode = SiInput.pdir; } else { if ((telnet_ssn->telnet_conf != NULL) && (telnet_ssn->telnet_conf->proto_ports.ports[SiInput.sport])) { iInspectMode = FTPP_SI_SERVER_MODE; } else if ((telnet_ssn->telnet_conf != NULL) && (telnet_ssn->telnet_conf->proto_ports.ports[SiInput.dport])) { iInspectMode = FTPP_SI_CLIENT_MODE; } } } else if (ft_ssn->proto == FTPP_SI_PROTO_FTP) { FTP_SESSION *ftp_ssn = (FTP_SESSION *)ft_ssn; GlobalConf = (FTPTELNET_GLOBAL_CONF *)sfPolicyUserDataGet(ftp_ssn->global_conf, ftp_ssn->policy_id); if (SiInput.pdir != FTPP_SI_NO_MODE) { iInspectMode = SiInput.pdir; } else { if ((ftp_ssn->server_conf != NULL) && ftp_ssn->server_conf->proto_ports.ports[SiInput.sport]) { iInspectMode = FTPP_SI_SERVER_MODE; } else if ((ftp_ssn->server_conf != NULL) && ftp_ssn->server_conf->proto_ports.ports[SiInput.dport]) { iInspectMode = FTPP_SI_CLIENT_MODE; } else { iInspectMode = FTPGetPacketDir(p); } } } else { /* XXX - Not FTP or Telnet */ _dpd.sessionAPI->set_application_data(p->stream_session, PP_FTPTELNET, NULL, NULL); return 0; } } } if (GlobalConf == NULL) return 0; /* * FTPTelnet PACKET FLOW:: * * Determine Proto Module:: * The Session Inspection Module retrieves the appropriate * configuration for sessions, and takes care of the stateless * vs. stateful processing in order to do this. Once this module * does it's magic, we're ready for the primetime. This means * determining whether this is an FTP or a Telnet session. * * Proto Specific Module:: * This is where we normalize the data. The Protocol specific module * handles what type of normalization to do (telnet, ftp) and does * protocol related checks. * */ if (ft_ssn == NULL) { int iRet = ftpp_si_determine_proto(p, GlobalConf, &ft_ssn, &SiInput, &iInspectMode); if (iRet) return iRet; } if (ft_ssn != NULL) { switch (SiInput.pproto) { case FTPP_SI_PROTO_TELNET: return SnortTelnet(GlobalConf, (TELNET_SESSION *)ft_ssn, p, iInspectMode); break; case FTPP_SI_PROTO_FTP: return SnortFTP(GlobalConf, (FTP_SESSION *)ft_ssn, p, iInspectMode); break; } } /* Uh, shouldn't get here */ return FTPP_INVALID_PROTO; } #ifdef TARGET_BASED static void FTPDataProcess(SFSnortPacket *p, FTP_DATA_SESSION *data_ssn, uint8_t *file_data, uint16_t data_length) { int status; _dpd.setFileDataPtr((uint8_t *)p->payload, (uint16_t)p->payload_size); if(data_ssn->flags & FTPDATA_FLG_FLUSH) { _dpd.fileAPI->set_file_partial(p,data_ssn->position,data_ssn->direction,true); } if(data_ssn->flags & FTPDATA_FLG_STOP) { _dpd.fileAPI->set_file_partial(p,data_ssn->position,data_ssn->direction,false); } status = _dpd.fileAPI->file_process(p, (uint8_t *)file_data, data_length, data_ssn->position, data_ssn->direction, false, (data_ssn->flags & FTPDATA_FLG_FLUSH) ? true : false); FTP_SESSION *ft_ssn = (FTP_SESSION *) _dpd.sessionAPI->get_application_data_from_key(data_ssn->ftp_key, PP_FTPTELNET); if(ft_ssn && (ft_ssn->flags & FTP_FLG_MALWARE_ENABLED) && _dpd.active_PacketWasDropped()) { _dpd.fileAPI->file_resume_block_add_file(p, data_ssn->path_hash, 0, 0, 0, NULL, ft_ssn->control_clientPort, ft_ssn->control_serverPort, true , data_ssn->direction); } /* Filename needs to be set AFTER the first call to file_process( ) */ if (data_ssn->filename && !(data_ssn->flags & FTPDATA_FLG_FILENAME_SET)) { _dpd.fileAPI->set_file_name(p->stream_session, (uint8_t *)data_ssn->filename, data_ssn->file_xfer_info, false); data_ssn->flags |= FTPDATA_FLG_FILENAME_SET; } /* Ignore the rest of this transfer if file processing is complete * and preprocessor was configured to ignore ftp-data sessions. */ if (!status && data_ssn->data_chan) { _dpd.sessionAPI->set_ignore_direction( p->stream_session, SSN_DIR_BOTH); } } void SnortFTPData_EOF(SFSnortPacket *p) { FTP_SESSION *ftp_ssn; FTP_DATA_SESSION *data_ssn = (FTP_DATA_SESSION *) _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET); if (!PROTO_IS_FTP_DATA(data_ssn) || !FTPDataDirection(p, data_ssn)) return; ftp_ssn = (FTP_SESSION *) _dpd.sessionAPI->get_application_data_from_key(data_ssn->ftp_key, PP_FTPTELNET); initFilePosition(&data_ssn->position, _dpd.fileAPI->get_file_processed_size(p->stream_session)); finalFilePosition(&data_ssn->position); _dpd.streamAPI->request_flush_stream(p); if (ftp_ssn && (data_ssn->flags & FTPDATA_FLG_REST) && (ftp_ssn->rest_cmd_offset > 0)) { File_Verdict verdict; verdict = _dpd.fileAPI->file_resume_block_check(p, data_ssn->path_hash); data_ssn->flags &= ~FTPDATA_FLG_REST; ftp_ssn->rest_cmd_offset = 0; if((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)) { _dpd.fileAPI->file_resume_block_add_file(p, data_ssn->path_hash, 0, 0, 0, NULL, ftp_ssn->control_clientPort, ftp_ssn->control_serverPort, true, data_ssn->direction); } return; } if (!(data_ssn->flags & FTPDATA_FLG_STOP)) { data_ssn->flags |= FTPDATA_FLG_STOP; FTPDataProcess(p, (FTP_DATA_SESSION *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET), (uint8_t *)p->payload, (uint16_t)p->payload_size); } } void SnortFTPData_Flush(SFSnortPacket *p) { FTP_DATA_SESSION *data_ssn = (FTP_DATA_SESSION *) _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET); if (!PROTO_IS_FTP_DATA(data_ssn) || !FTPDataDirection(p, data_ssn)) return; initFilePosition(&data_ssn->position, _dpd.fileAPI->get_file_processed_size(p->stream_session)); data_ssn->flags |= FTPDATA_FLG_FLUSH; _dpd.streamAPI->request_flush_stream(p); data_ssn->flags &= ~FTPDATA_FLG_FLUSH; return; } int SnortFTPData(SFSnortPacket *p) { FTP_SESSION *ftp_ssn; FTP_DATA_SESSION *data_ssn; if (!p->stream_session) return -1; data_ssn = (FTP_DATA_SESSION *) _dpd.sessionAPI->get_application_data(p->stream_session, PP_FTPTELNET); if (!PROTO_IS_FTP_DATA(data_ssn)) return -2; if (data_ssn->flags & FTPDATA_FLG_STOP) return 0; /* FTP-Data Session is in limbo, we need to lookup the control session * to figure out what to do. */ ftp_ssn = (FTP_SESSION *) _dpd.sessionAPI->get_application_data_from_key(data_ssn->ftp_key, PP_FTPTELNET); if(!ftp_ssn) return -3; #ifdef DAQ_PKT_FLAG_SSL_DETECTED // Set up the flow context when an SSL client hello is detected and // ignore packets until the flow is decrypted. if(p->pkt_header->flags & DAQ_PKT_FLAG_SSL_DETECTED) { ssl_callback_interface_t *ssl_cb; ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if(ssl_cb) { ftp_ssn->data_chan_state |= DATA_CHAN_CLIENT_HELLO_SEEN; ssl_cb->session_initialize(p, data_ssn, FTPData_Set_flow_id); } return 0; // Ignore SSL client hello. } else if(ftp_ssn->data_chan_state & DATA_CHAN_CLIENT_HELLO_SEEN) { if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) { // Done handling SSL handshake. ftp_ssn->data_chan_state &= ~DATA_CHAN_CLIENT_HELLO_SEEN; } else { return 0; // Ignore packet. } } #endif if ((data_ssn->flags & FTPDATA_FLG_REST) && (ftp_ssn->rest_cmd_offset > 0)) { File_Verdict verdict; verdict = _dpd.fileAPI->file_resume_block_check(p, data_ssn->path_hash); data_ssn->flags &= ~FTPDATA_FLG_REST; ftp_ssn->rest_cmd_offset = 0; if((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)) { data_ssn->flags |= FTPDATA_FLG_STOP; _dpd.fileAPI->file_resume_block_add_file(p, data_ssn->path_hash, 0, 0, 0, NULL, ftp_ssn->control_clientPort, ftp_ssn->control_serverPort, true, data_ssn->direction); } return 0 ; } // bail if we have not rebuilt the stream yet. if (!_dpd.readyForProcess(p)) return 0; if (data_ssn->file_xfer_info == FTPP_FILE_UNKNOWN) { if (!PROTO_IS_FTP(ftp_ssn)) { DEBUG_WRAP(DebugMessage(DEBUG_FTPTELNET, "FTP-DATA Invalid FTP_SESSION retrieved durring lookup\n");); if (data_ssn->data_chan) _dpd.sessionAPI->set_ignore_direction(p->stream_session, SSN_DIR_BOTH); return -2; } switch (ftp_ssn->file_xfer_info) { case FTPP_FILE_UNKNOWN: /* Keep waiting */ break; case FTPP_FILE_IGNORE: /* This wasn't a file transfer; ignore it */ if (data_ssn->data_chan) _dpd.sessionAPI->set_ignore_direction(p->stream_session, SSN_DIR_BOTH); return 0; default: /* A file transfer was detected. */ data_ssn->direction = ftp_ssn->data_xfer_dir; data_ssn->file_xfer_info = ftp_ssn->file_xfer_info; ftp_ssn->file_xfer_info = 0; data_ssn->filename = ftp_ssn->filename; ftp_ssn->filename = NULL; break; } } if (!FTPDataDirection(p, data_ssn)) return 0; if (isFileEnd(data_ssn->position)) { data_ssn->flags |= FTPDATA_FLG_STOP; } else { initFilePosition(&data_ssn->position, _dpd.fileAPI->get_file_processed_size(p->stream_session)); if (p->tcp_header && (p->tcp_header->flags & TCPHEADER_FIN)) finalFilePosition(&data_ssn->position); } FTPDataProcess(p, data_ssn, (uint8_t *)p->payload, (uint16_t)p->payload_size); return 0; } #endif /* TARGET_BASED */ int FTPPBounceInit(struct _SnortConfig *sc, char *name, char *parameters, void **dataPtr) { char **toks; int num_toks; toks = _dpd.tokenSplit(parameters, ",", 12, &num_toks, 0); if(num_toks > 0) { DynamicPreprocessorFatalMessage("ERROR: Bad arguments to '%s' option: '%s'\n", name, parameters); } _dpd.tokenFree(&toks, num_toks); *dataPtr = NULL; return 1; } /**************************************************************************** * * Function: FTPPBounce(void *pkt, uint8_t **cursor, void **dataPtr) * * Purpose: Use this function to perform the particular detection routine * that this rule keyword is supposed to encompass. * * Arguments: p => pointer to the decoded packet * cursor => pointer to the current location in the buffer * dataPtr => pointer to rule specific data (not used for this option) * * Returns: If the detection test fails, this function *must* return a zero! * On success, it returns 1; * ****************************************************************************/ int FTPPBounceEval(void *pkt, const uint8_t **cursor, void *dataPtr) { uint32_t ip = 0; SFSnortPacket *p = (SFSnortPacket *)pkt; int octet=0; const char *start_ptr, *end_ptr; const char *this_param = *(const char **)cursor; int dsize; if ( !p->ip4_header ) return 0; if(_dpd.Is_DetectFlag(SF_FLAG_ALT_DETECT)) { dsize = _dpd.altDetect->len; start_ptr = (const char*) _dpd.altDetect->data; DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Using Alternative Detect buffer!\n");); } else if(_dpd.Is_DetectFlag(SF_FLAG_ALT_DECODE)) { dsize = _dpd.altBuffer->len; start_ptr = (const char *) _dpd.altBuffer->data; DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "Using Alternative Decode buffer!\n");); } else { start_ptr = (const char *)p->payload; dsize = p->payload_size; } DEBUG_WRAP( DebugMessage(DEBUG_PATTERN_MATCH,"[*] ftpbounce firing...\n"); DebugMessage(DEBUG_PATTERN_MATCH,"payload starts at %p\n", start_ptr); ); /* END DEBUG_WRAP */ /* save off whatever our ending pointer is */ end_ptr = start_ptr + dsize; while ((this_param < end_ptr) && isspace((int)*this_param)) this_param++; do { int value = 0; do { if (!isdigit((int)*this_param)) { DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "[*] ftpbounce non digit char failed..\n");); return RULE_NOMATCH; } value = value * 10 + (*this_param - '0'); this_param++; } while ((this_param < end_ptr) && (*this_param != ',') && (!(isspace((int)*this_param)))); if (value > 0xFF) { DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "[*] ftpbounce value > 256 ..\n");); return RULE_NOMATCH; } if (octet < 4) { ip = (ip << 8) + value; } if ((this_param < end_ptr) && !isspace((int)*this_param)) this_param++; octet++; } while ((this_param < end_ptr) && !isspace((int)*this_param) && (octet < 4)); if (octet < 4) { DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "[*] ftpbounce insufficient data ..\n");); return RULE_NOMATCH; } if (ip != ntohl(p->ip4_header->source.s_addr)) { /* Bounce attempt -- IPs not equal */ return RULE_MATCH; } else { DEBUG_WRAP(DebugMessage(DEBUG_PATTERN_MATCH, "PORT command not being used in bounce\n");); return RULE_NOMATCH; } /* Never reached */ return RULE_NOMATCH; } /* Add ports configured for ftptelnet preprocessor to stream5 port filtering so that * if any_any rules are being ignored then the packet still reaches ftptelnet. * * For ports in global_server configuration, server_lookup and server_lookupIpv6, * add the port to stream5 port filter list. */ static void _FTPTelnetAddPortsOfInterest(struct _SnortConfig *sc, FTPTELNET_GLOBAL_CONF *config, tSfPolicyId policy_id) { int i; if (config == NULL) return; /* For the server callback */ ftp_current_policy = policy_id; _addPortsToStream(sc, config->telnet_config->proto_ports.ports, policy_id, 0); _addPortsToStream(sc, config->default_ftp_server->proto_ports.ports, policy_id, 1); ftpp_ui_server_iterate(sc, config->server_lookup, _addFtpServerConfPortsToStream, &i); } static int _addFtpServerConfPortsToStream(struct _SnortConfig *sc, void *pData) { FTP_SERVER_PROTO_CONF *pConf = (FTP_SERVER_PROTO_CONF *)pData; _addPortsToStream(sc, pConf->proto_ports.ports, ftp_current_policy, 1); return 0; } // flush at last line feed in payload // preproc will deal with any pipelined commands static PAF_Status ftp_paf ( void* ssn, void** pv, const uint8_t* data, uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh) { #ifdef HAVE_MEMRCHR uint8_t* lf = memrchr(data, '\n', len); #else uint32_t n = len; uint8_t* lf = NULL, * tmp = (uint8_t*) data; while ( (tmp = memchr(tmp, '\n', n)) ) { lf = tmp++; n = len - (tmp - data); } #endif DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "%s[%d] '%*.*s'\n", __FUNCTION__, len, len, len, data)); if ( !lf ) return PAF_SEARCH; *fp = lf - data + 1; return PAF_FLUSH; } #ifdef TARGET_BASED static void _FTPTelnetAddService (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy) { if ( _dpd.isPafEnabled() ) { ftp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, ftp_paf, true); ftp_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false, ftp_paf, true); } } #endif static void _addPortsToStream(struct _SnortConfig *sc, char *ports, tSfPolicyId policy_id, int ftp) { unsigned int i; for (i = 0; i < MAXPORTS; i++) { if (ports[i]) { //Add port the port _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_TCP, (uint16_t)i, PORT_MONITOR_SESSION, policy_id, 1); if ( ftp && _dpd.isPafEnabled() ) { ftp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy_id, (uint16_t)i, true, ftp_paf, false); ftp_paf_id = _dpd.streamAPI->register_paf_port(sc, policy_id, (uint16_t)i, false, ftp_paf, false); } } } } int FtpTelnetInitGlobalConfig(FTPTELNET_GLOBAL_CONF *config, char *ErrorString, int iErrStrLen) { int iRet; if (config == NULL) { snprintf(ErrorString, iErrStrLen, "Global configuration is NULL."); return FTPP_FATAL_ERR; } iRet = ftpp_ui_config_init_global_conf(config); if (iRet) { snprintf(ErrorString, iErrStrLen, "Error initializing Global Configuration."); return FTPP_FATAL_ERR; } return 0; } void FTP_Set_flow_id( void *app_data, uint32_t fid ) { FTP_SESSION *ssn = (FTP_SESSION *)app_data; if( ssn ) ssn->flow_id = fid; } void FTPData_Set_flow_id( void *app_data, uint32_t fid ) { #ifdef TARGET_BASED FTP_DATA_SESSION *ssn = (FTP_DATA_SESSION *)app_data; if( ssn ) ssn->flow_id = fid; #endif } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_ui_server_lookup.c0000644000175000017500000002234114241075767025030 0ustar apoapo/* * ftpp_ui_server_lookup.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains functions to access the SERVER_LOOKUP structure. * * We wrap the access to SERVER_LOOKUP so changing the lookup algorithms * are more modular and independent. This is the only file that would need * to be changed to change the algorithmic lookup. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "hi_util_kmap.h" #include "ftpp_ui_config.h" #include "ftpp_return_codes.h" #include "snort_ftptelnet.h" #include "memory_stats.h" static void serverConfFree(void *pData); /* * Function: ftpp_ui_server_lookup_init(SERVER_LOOKUP **ServerLookup) * * Purpose: Initialize the server_lookup structure. * * We need to initialize the server_lookup structure for * the FTP server configuration. Don't want a NULL pointer * flying around, when we have to look for server configs. * * Arguments: ServerLookup => pointer to the pointer of the server * lookup structure. * * Returns: int => return code indicating error or success * */ #define FTPP_UI_CONFIG_MAX_SERVERS 20 int ftpp_ui_server_lookup_init(SERVER_LOOKUP **ServerLookup) { *ServerLookup = sfrt_new(DIR_16_4x4_16x5_4x4, IPv6, FTPP_UI_CONFIG_MAX_SERVERS, 20); if(*ServerLookup == NULL) { return FTPP_MEM_ALLOC_FAIL; } return FTPP_SUCCESS; } /* * Function: ftpp_ui_server_lookup_cleanup(SERVER_LOOKUP **ServerLookup) * * Purpose: Free the server_lookup structure. * We need to free the server_lookup structure. * * Arguments: ServerLookup => pointer to the pointer of the server * lookup structure. * * Returns: int => return code indicating error or success * */ int ftpp_ui_server_lookup_cleanup(SERVER_LOOKUP **ServerLookup) { if ((ServerLookup == NULL) || (*ServerLookup == NULL)) return FTPP_INVALID_ARG; sfrt_cleanup(*ServerLookup, serverConfFree); sfrt_free(*ServerLookup); *ServerLookup = NULL; return FTPP_SUCCESS; } /* * Function: ftpp_ui_server_lookup_add(SERVER_LOOKUP *ServerLookup, * char *ip, int len, * FTP_SERVER_PROTO_CONF *ServerConf) * * Purpose: Add a server configuration to the list. * We add these keys like you would normally think to add * them, because on low endian machines the least significant * byte is compared first. This is what we want to compare * IPs backward, doesn't work on high endian machines, but oh * well. Our platform is Intel. * * Arguments: ServerLookup => a pointer to the lookup structure * IP => the ftp server address * len => Length of the address * ServerConf => a pointer to the server configuration structure * * Returns: int => return code indicating error or success * */ int ftpp_ui_server_lookup_add( SERVER_LOOKUP *ServerLookup, sfcidr_t* Ip, FTP_SERVER_PROTO_CONF *ServerConf ) { int iRet; if(!ServerLookup || !ServerConf) { return FTPP_INVALID_ARG; } iRet = sfrt_insert(Ip, (unsigned char)Ip->bits, (void *)ServerConf, RT_FAVOR_SPECIFIC, ServerLookup); if (iRet) { return FTPP_MEM_ALLOC_FAIL; } return FTPP_SUCCESS; } /* * Function: ftpp_ui_server_lookup_find(SERVER_LOOKUP *ServerLookup, * sfaddr_t* ip, int *iError) * * Purpose: Find a server configuration given a IP. * We look up a server configuration given an IP and * return a pointer to that server configuration if found. * * Arguments: ServerLookup => a pointer to the lookup structure * IP => the ftp server address * iError => a pointer to an error code * * Returns: int => return code indicating error or success * * Returns: FTP_SERVER_PROTO_CONF* => Pointer to server configuration * structure matching IP if found, NULL otherwise. * */ FTP_SERVER_PROTO_CONF *ftpp_ui_server_lookup_find( SERVER_LOOKUP *ServerLookup, sfaddr_t* Ip, int *iError ) { FTP_SERVER_PROTO_CONF *ServerConf = NULL; if(!iError) { return NULL; } if(!ServerLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; ServerConf = (FTP_SERVER_PROTO_CONF *)sfrt_lookup(Ip, ServerLookup); if (!ServerConf) { *iError = FTPP_NOT_FOUND; } return ServerConf; } /**Iterate over all the stored IP addresses, calling the callback for * all elements. * * @param ServerLookup => a pointer to the lookup structure * @param userfunc => user defined callback function * @param iError => a pointer to an error code * * @returns iError => return code indicating error or success * */ int ftpp_ui_server_iterate( struct _SnortConfig *sc, SERVER_LOOKUP *ServerLookup, sfrt_sc_iterator_callback3 userfunc, int *iError ) { if(!iError) { return 0; } if(!ServerLookup) { *iError = FTPP_INVALID_ARG; return 0; } *iError = FTPP_SUCCESS; return sfrt_iterate2_with_snort_config(sc, ServerLookup, userfunc); } #if 0 /** Obsoleted. After changing underlying KMAP to SFRT. SFRT provides an iterator with * a callback function but does not support getFirst, getNext operations. */ /* * Function: ftpp_ui_server_lookup_first(SERVER_LOOKUP *ServerLookup, * int *iError) * * Purpose: This lookups the first server configuration, so we can * iterate through the configurations. * * Arguments: ServerLookup => pointer to the server lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_SERVER_PROTO_CONF* => Pointer to first server * configuration structure * */ FTP_SERVER_PROTO_CONF *ftpp_ui_server_lookup_first(SERVER_LOOKUP *ServerLookup, int *iError) { FTP_SERVER_PROTO_CONF *ServerConf; if(!iError) { return NULL; } if(!ServerLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; ServerConf = (FTP_SERVER_PROTO_CONF *)KMapFindFirst(ServerLookup); if (!ServerConf) { *iError = FTPP_NOT_FOUND; } return ServerConf; } /* * Function: ftpp_ui_server_lookup_next(SERVER_LOOKUP *ServerLookup, * int *iError) * * Iterates to the next configuration, like a list it just returns * the next config in the config list. * * Purpose: This lookups the next server configuration, so we can * iterate through the configurations. * * Arguments: ServerLookup => pointer to the server lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_SERVER_PROTO_CONF* => Pointer to next server configuration * structure * */ FTP_SERVER_PROTO_CONF *ftpp_ui_server_lookup_next(SERVER_LOOKUP *ServerLookup, int *iError) { FTP_SERVER_PROTO_CONF *ServerConf; if(!iError) { return NULL; } if(!ServerLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; ServerConf = (FTP_SERVER_PROTO_CONF *)KMapFindNext(ServerLookup); if (!ServerConf) { *iError = FTPP_NOT_FOUND; } return ServerConf; } #endif /**Free pData buffer, which may be referenced multiple times. ReferenceCount * is the number of times the buffer is referenced. For freeing the buffer, * we just decrement referenceCount till it reaches 0, at which time the * buffer is also freed. */ static void serverConfFree(void *pData) { FTP_SERVER_PROTO_CONF *serverConf = (FTP_SERVER_PROTO_CONF *)pData; if (serverConf) { serverConf->referenceCount--; if (serverConf->referenceCount == 0) { FTPTelnetCleanupFTPServerConf((void *)serverConf); _dpd.snortFree(serverConf, sizeof(FTP_SERVER_PROTO_CONF), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } } } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_ui_config.c0000644000175000017500000003027714241075764023402 0ustar apoapo/* * ftpp_ui_config.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains library calls to configure FTPTelnet. * * This file deals with configuring FTPTelnet processing. It contains * routines to set a default configuration, add client configurations, etc. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #include #include #include #include #ifndef WIN32 #include #include #include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "ftpp_return_codes.h" #include "ftpp_ui_client_lookup.h" #include "ftpp_ui_server_lookup.h" #include "ftp_cmd_lookup.h" #include "ftp_bounce_lookup.h" #include "ftpp_ui_config.h" #include "memory_stats.h" /* * Function: ftpp_ui_config_init_global_conf(FTPTELNET_GLOBAL_CONF *GlobalConf) * * Purpose: Initialize the FTPTelnet global configuration. * The main point of this function is to initialize the client * lookup type. We also do things like memset, etc. * * Arguments: GlobalConf => pointer to the global configuration * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_init_global_conf(FTPTELNET_GLOBAL_CONF *GlobalConf) { int iRet; iRet = ftpp_ui_client_lookup_init(&GlobalConf->client_lookup); if (iRet) { return iRet; } iRet = ftpp_ui_server_lookup_init(&GlobalConf->server_lookup); if (iRet) { return iRet; } return FTPP_SUCCESS; } /* * Function: ftpp_ui_config_default(FTPTELNET_GLOBAL_CONF *GlobalConf) * * Purpose: This function sets the global and the global_client default * configuration. In order to change the default configuration * of FTPTelnet, you must change this function. * * Arguments: GlobalConf => pointer to the global configuration structure * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_default(FTPTELNET_GLOBAL_CONF *GlobalConf) { if(GlobalConf == NULL) { return FTPP_INVALID_ARG; } /* * Set Global Client Configurations */ ftpp_ui_config_reset_ftp_client(GlobalConf->default_ftp_client, 0); ftpp_ui_config_reset_ftp_server(GlobalConf->default_ftp_server, 0); ftpp_ui_config_reset_telnet_proto(GlobalConf->telnet_config); return FTPP_SUCCESS; } /* * Function: ftpp_ui_config_reset_global(FTPTELNET_GLOBAL_CONF *GlobalConf) * * Purpose: This function resets the global parameters. * THIS IS NOT THE GLOBAL FTP CLIENT CONFIGURATION. * * Arguments: GlobalConf => pointer to the global configuration structure * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_reset_global(FTPTELNET_GLOBAL_CONF *GlobalConf) { int iRet; /* Clean these up before mem setting */ ftp_bounce_lookup_cleanup(&GlobalConf->default_ftp_client->bounce_lookup); ftp_cmd_lookup_cleanup(&(GlobalConf->default_ftp_server->cmd_lookup)); ftpp_ui_client_lookup_cleanup(&GlobalConf->client_lookup); ftpp_ui_server_lookup_cleanup(&GlobalConf->server_lookup); memset(GlobalConf, 0x00, sizeof(FTPTELNET_GLOBAL_CONF)); iRet = ftpp_ui_client_lookup_init(&GlobalConf->client_lookup); if (iRet) { return iRet; } iRet = ftpp_ui_server_lookup_init(&GlobalConf->server_lookup); if (iRet) { return iRet; } return FTPP_SUCCESS; } /* * Function: ftpp_ui_config_reset_telnet_proto(TELNET_PROTO_CONF *TelnetConf) * * Purpose: This function resets a telnet construct. * * Arguments: TelnetConf => pointer to the TELNET_PROTO_CONF structure * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_reset_telnet_proto(TELNET_PROTO_CONF *TelnetConf) { memset(TelnetConf, 0x00, sizeof(TELNET_PROTO_CONF)); TelnetConf->ayt_threshold = FTPP_UI_CONFIG_TELNET_DEF_AYT_THRESHOLD; TelnetConf->proto_ports.port_count = 1; TelnetConf->proto_ports.ports[23] = 1; return FTPP_SUCCESS; } /* * Function: ftpp_ui_config_reset_ftp_cmd_date_format(FTP_DATE_FMT *DateFmt) * * Purpose: This function resets an FTP date parameter construct. * * Arguments: ThisFmt => pointer to the FTP_DATE_FMT structure * * Returns: void * */ void ftpp_ui_config_reset_ftp_cmd_date_format(FTP_DATE_FMT *DateFmt) { if (DateFmt->optional) { ftpp_ui_config_reset_ftp_cmd_date_format(DateFmt->optional); } if (DateFmt->next) { ftpp_ui_config_reset_ftp_cmd_date_format(DateFmt->next); } if (DateFmt->format_string) { _dpd.snortFree(DateFmt->format_string, (strlen(DateFmt->format_string) + 1), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } _dpd.snortFree(DateFmt, sizeof(FTP_DATE_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } /* * Function: ftpp_ui_config_reset_ftp_cmd_format(FTP_PARAM_FMT *ThisFmt) * * Purpose: This function resets an FTP parameter construct. * * Arguments: ThisFmt => pointer to the FTP_PARAM_FMT structure * * Returns: void * */ void ftpp_ui_config_reset_ftp_cmd_format(FTP_PARAM_FMT *ThisFmt) { if (ThisFmt->optional_fmt) { ftpp_ui_config_reset_ftp_cmd_format(ThisFmt->optional_fmt); } if (ThisFmt->numChoices) { int i; for (i=0;inumChoices;i++) { ftpp_ui_config_reset_ftp_cmd_format(ThisFmt->choices[i]); } _dpd.snortFree(ThisFmt->choices, (ThisFmt->numChoices * sizeof(FTP_PARAM_FMT *)), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } if (ThisFmt->next_param_fmt) { /* Don't free this one twice if its after an optional */ FTP_PARAM_FMT *next = ThisFmt->next_param_fmt; ThisFmt->next_param_fmt->prev_param_fmt->next_param_fmt = NULL; ThisFmt->next_param_fmt = NULL; ftpp_ui_config_reset_ftp_cmd_format(next); } if (ThisFmt->type == e_date) { ftpp_ui_config_reset_ftp_cmd_date_format(ThisFmt->format.date_fmt); } if (ThisFmt->type == e_literal) { _dpd.snortFree(ThisFmt->format.literal, (strlen(ThisFmt->format.literal) + 1), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } memset(ThisFmt, 0, sizeof(FTP_PARAM_FMT)); _dpd.snortFree(ThisFmt, sizeof(FTP_PARAM_FMT), PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); } /* * Function: ftpp_ui_config_reset_ftp_cmd(FTP_CMD_CONF *FTPCmd) * * Purpose: This function resets a FTP command construct. * * Arguments: FTPCmd => pointer to the FTP_CMD_CONF structure * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_reset_ftp_cmd(FTP_CMD_CONF *FTPCmd) { FTP_PARAM_FMT *NextCmdFormat = FTPCmd->param_format; if (NextCmdFormat) { ftpp_ui_config_reset_ftp_cmd_format(NextCmdFormat); } return FTPP_SUCCESS; } /* * Function: ftpp_ui_config_reset_ftp_server(FTP_SERVER_PROTO_CONF *ServerConf, * char first) * * Purpose: This function resets a ftp server construct. * * Arguments: ServerConf => pointer to the FTP_SERVER_PROTO_CONF structure * first => indicator whether this is a new conf * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_reset_ftp_server(FTP_SERVER_PROTO_CONF *ServerConf, char first) { int iRet = FTPP_SUCCESS; if (first == 0) { ftp_cmd_lookup_cleanup(&ServerConf->cmd_lookup); } if (ServerConf->serverAddr) { free(ServerConf->serverAddr); } memset(ServerConf, 0x00, sizeof(FTP_SERVER_PROTO_CONF)); ServerConf->proto_ports.port_count = 1; ServerConf->proto_ports.ports[21] = 1; ftp_cmd_lookup_init(&ServerConf->cmd_lookup); ServerConf->def_max_param_len = FTPP_UI_CONFIG_FTP_DEF_CMD_PARAM_MAX; ServerConf->max_cmd_len = MAX_CMD; return iRet; } /* * Function: ftpp_ui_config_add_ftp_server( * FTPTELNET_GLOBAL_CONF *GlobalConf, * unsigned long ServerIP, * FTP_SERVER_PROTO_CONF *ServerConf) * * Purpose: Add a server config to the FTPTelnet configuration. * This function takes an IP address of a server and an FTP * Server configuration, and assigns the configuration to * the IP address in a lookup table. * * Arguments: GlobalConf => pointer to the global configuration * ServerIp => the IP address of the server * ServerConf => pointer to the server configuration * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_add_ftp_server(FTPTELNET_GLOBAL_CONF *GlobalConf, sfcidr_t * ServerIP, FTP_SERVER_PROTO_CONF *ServerConf) { int iRet; iRet = ftpp_ui_server_lookup_add(GlobalConf->server_lookup, ServerIP, ServerConf); if (iRet) { /* * Already added key will return a generic non-fatal * error. */ return iRet; } return FTPP_SUCCESS; } /* * Function: ftpp_ui_config_reset_ftp_client(FTP_CLIENT_PROTO_CONF *ClientConf, * char first) * * Purpose: This function resets a ftp client construct. * * Arguments: ClientConf => pointer to the FTP_CLIENT_PROTO_CONF structure * first => indicator whether this is a new conf * * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_reset_ftp_client(FTP_CLIENT_PROTO_CONF *ClientConf, char first) { int iRet = FTPP_SUCCESS; //FTP_BOUNCE_TO *NextBounceTo = NULL; if (first == 0) { ftp_bounce_lookup_cleanup(&ClientConf->bounce_lookup); } if (ClientConf->clientAddr) { free(ClientConf->clientAddr); } memset(ClientConf, 0x00, sizeof(FTP_CLIENT_PROTO_CONF)); ftp_bounce_lookup_init(&ClientConf->bounce_lookup); ClientConf->max_resp_len = (unsigned int)FTPP_UI_CONFIG_FTP_DEF_RESP_MSG_MAX; return iRet; } /* * Function: ftpp_ui_config_add_ftp_client( * FTPTELNET_GLOBAL_CONF *GlobalConf, * unsigned long ClientIP, * FTP_SERVER_PROTO_CONF *ClientConf) * * Purpose: Add a client config to the FTPTelnet configuration. * This function takes an IP address of a client and an FTP * Client configuration, and assigns the configuration to * the IP address in a lookup table. * * Arguments: GlobalConf => pointer to the global configuration * ClientIP => the IP address of the client * ClientConf => pointer to the client configuration * * Returns: int => return code indicating error or success * */ int ftpp_ui_config_add_ftp_client(FTPTELNET_GLOBAL_CONF *GlobalConf, sfcidr_t * ClientIP, FTP_CLIENT_PROTO_CONF *ClientConf) { int iRet; iRet = ftpp_ui_client_lookup_add(GlobalConf->client_lookup, ClientIP, ClientConf); if (iRet) { /* * Already added key will return a generic non-fatal * error. */ return iRet; } return FTPP_SUCCESS; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_eo_log.c0000644000175000017500000003053114241075754022674 0ustar apoapo/* * ftpp_eo_log.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains the event output functionality that * FTPTelnet uses to log events and data associated with * the events. * * Log events, retrieve events, and select events that HttpInspect * generates. * * Logging Events: * Since the object behind this is no memset()s, we have to rely on the * stack interface to make sure we don't log the same event twice. So * if there are events in the stack we cycle through to make sure that * there are none available before we add a new event and increment the * stack count. Then to reset the event queue, we just need to set the * stack count back to zero. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "ftpp_si.h" #include "ftpp_eo.h" #include "ftpp_eo_events.h" #include "ftpp_return_codes.h" //#include "signature.h" typedef struct _ClassInfo { char *typeName; int id; char *name; int priority; struct _ClassInfo *next; } ClassInfo; /* * The ftp & telnet events and the priorities are listed here. * Any time that a new client event is added, we have to * add the event id and the priority here. If you want to * change either of those characteristics, you have to change * them here. */ static FTPP_EVENT_INFO ftp_event_info[FTP_EO_EVENT_NUM] = { { FTP_EO_TELNET_CMD, FTP_EO_TELNET_CMD_SID, 0, FTPP_EO_LOW_PRIORITY, FTP_EO_TELNET_CMD_STR }, { FTP_EO_INVALID_CMD, FTP_EO_INVALID_CMD_SID, 0, FTPP_EO_LOW_PRIORITY, FTP_EO_INVALID_CMD_STR }, { FTP_EO_PARAMETER_LENGTH_OVERFLOW, FTP_EO_PARAMETER_LENGTH_OVERFLOW_SID, 0, FTPP_EO_HIGH_PRIORITY, FTP_EO_PARAMETER_LENGTH_OVERFLOW_STR }, { FTP_EO_MALFORMED_PARAMETER, FTP_EO_MALFORMED_PARAMETER_SID, 0, FTPP_EO_HIGH_PRIORITY, FTP_EO_MALFORMED_PARAMETER_STR }, { FTP_EO_PARAMETER_STR_FORMAT, FTP_EO_PARAMETER_STR_FORMAT_SID, 0, FTPP_EO_HIGH_PRIORITY, FTP_EO_PARAMETER_STR_FORMAT_STR }, { FTP_EO_RESPONSE_LENGTH_OVERFLOW, FTP_EO_RESPONSE_LENGTH_OVERFLOW_SID, 0, FTPP_EO_LOW_PRIORITY, FTP_EO_RESPONSE_LENGTH_OVERFLOW_STR }, { FTP_EO_ENCRYPTED, FTP_EO_ENCRYPTED_SID, 0, FTPP_EO_LOW_PRIORITY, FTP_EO_ENCRYPTED_STR }, { FTP_EO_BOUNCE, FTP_EO_BOUNCE_SID, 0, FTPP_EO_MED_PRIORITY, FTP_EO_BOUNCE_STR }, { FTP_EO_EVASIVE_TELNET_CMD, FTP_EO_EVASIVE_TELNET_CMD_SID, 0, FTPP_EO_LOW_PRIORITY, FTP_EO_EVASIVE_TELNET_CMD_STR } }; static FTPP_EVENT_INFO telnet_event_info[TELNET_EO_EVENT_NUM] = { { TELNET_EO_AYT_OVERFLOW, TELNET_EO_AYT_OVERFLOW_SID, 0, FTPP_EO_HIGH_PRIORITY, TELNET_EO_AYT_OVERFLOW_STR }, { TELNET_EO_ENCRYPTED, TELNET_EO_ENCRYPTED_SID, 0, FTPP_EO_LOW_PRIORITY, TELNET_EO_ENCRYPTED_STR }, { TELNET_EO_SB_NO_SE, TELNET_EO_SB_NO_SE_SID, 0, FTPP_EO_LOW_PRIORITY, TELNET_EO_SB_NO_SE_STR } }; static int log_initialized = 0; /* * Function: ftpp_eo_event_log_init() * * Purpose: Initialize the event logger. * We need to initialize the event logger for the FTP/Telnet * preprocessor. Initializes the event info objects and class types. * * Arguments: None * * Returns: void * */ void ftpp_eo_event_log_init(void) { if (!log_initialized) { ClassInfo *type = _dpd.getRuleInfoByName("protocol-command-decode"); if (type != NULL) { ftp_event_info[FTP_EO_TELNET_CMD].classification = type->id; ftp_event_info[FTP_EO_TELNET_CMD].priority = type->priority; ftp_event_info[FTP_EO_INVALID_CMD].classification = type->id; ftp_event_info[FTP_EO_INVALID_CMD].priority = type->priority; ftp_event_info[FTP_EO_MALFORMED_PARAMETER].classification = type->id; ftp_event_info[FTP_EO_MALFORMED_PARAMETER].priority = type->priority; ftp_event_info[FTP_EO_ENCRYPTED].classification = type->id; ftp_event_info[FTP_EO_ENCRYPTED].priority = type->priority; ftp_event_info[FTP_EO_EVASIVE_TELNET_CMD].classification = type->id; ftp_event_info[FTP_EO_EVASIVE_TELNET_CMD].priority = type->priority; telnet_event_info[TELNET_EO_ENCRYPTED].classification = type->id; telnet_event_info[TELNET_EO_ENCRYPTED].priority = type->priority; } type = _dpd.getRuleInfoByName("string-detect"); if (type != NULL) { ftp_event_info[FTP_EO_RESPONSE_LENGTH_OVERFLOW].classification = type->id; ftp_event_info[FTP_EO_RESPONSE_LENGTH_OVERFLOW].priority = type->priority; } type = _dpd.getRuleInfoByName("policy-violation"); if (type != NULL) { ftp_event_info[FTP_EO_BOUNCE].classification = type->id; ftp_event_info[FTP_EO_BOUNCE].priority = type->priority; } type = _dpd.getRuleInfoByName("attempted-admin"); if (type != NULL) { ftp_event_info[FTP_EO_PARAMETER_LENGTH_OVERFLOW].classification = type->id; ftp_event_info[FTP_EO_PARAMETER_LENGTH_OVERFLOW].priority = type->priority; ftp_event_info[FTP_EO_PARAMETER_STR_FORMAT].classification = type->id; ftp_event_info[FTP_EO_PARAMETER_STR_FORMAT].priority = type->priority; telnet_event_info[TELNET_EO_AYT_OVERFLOW].classification = type->id; telnet_event_info[TELNET_EO_AYT_OVERFLOW].priority = type->priority; telnet_event_info[TELNET_EO_SB_NO_SE].classification = type->id; telnet_event_info[TELNET_EO_SB_NO_SE].priority= type->priority; } log_initialized = 1; } } /* * Function: ftpp_eo_event_log(FTPP_GEN_EVENTS *gen_events, * FTPP_EVENT_INFO *event_info, * int iEvent, * void *data, void (*free_data)(void *) ) * * Purpose: This function logs events during FTPTelnet processing. * The idea behind this event logging is modularity, but at the * same time performance. We accomplish this utilizing an * optimized stack as an index into the client event array, * instead of walking a list for already logged events. The * problem here is that we can't just log every event that we've * already seen, because this opens us up to a DOS. So by using * this method, we can quickly check if an event has already been * logged and deal appropriately. * * Arguments: gen_events => pointer to the generic event data * event_info => pointer to the event info array * iEvent => index within the event array * data => pointer to user allocated data * free_data => pointer to a function to free the user data * * Returns: int => return code indicating error or success * */ int ftpp_eo_event_log(FTPP_GEN_EVENTS *gen_events, FTPP_EVENT_INFO *event_info, int iEvent, void *data, void (*free_data)(void *) ) { FTPP_EVENT *event; int iCtr; /* * This is where we cycle through the current event stack. If the event * to be logged is already in the queue, then we increment the event * count, before returning. Otherwise, we fall through the loop and * set the event before adding it to the queue and incrementing the * pointer. */ for(iCtr = 0; iCtr < gen_events->stack_count; iCtr++) { if(gen_events->stack[iCtr] == iEvent) { gen_events->events[iEvent].count++; return FTPP_SUCCESS; } } /* * Initialize the event before putting it in the queue. */ event = &(gen_events->events[iEvent]); event->event_info = event_info; event->count = 1; event->data = data; event->free_data = free_data; /* * We now add the event to the stack. */ gen_events->stack[gen_events->stack_count] = iEvent; gen_events->stack_count++; return FTPP_SUCCESS; } /* * Function: telnet_eo_event_log(TELNET_SESSION *Session, * int iEvent, * void *data, void (*free_data)(void *)) * * Purpose: This function logs events for telnet processing. * It invokes ftpp_eo_event_log using a generic event structure * that contains the telnet specific data. * * Arguments: Session => pointer to the Telnet session * iEvent => the event id for the event * data => pointer to the user data of the event * free_data => pointer to a function to free the user data * * Returns: int => return code indicating error or success * */ int telnet_eo_event_log(TELNET_SESSION *Session, int iEvent, void *data, void (*free_data)(void *)) { int iRet; TELNET_EVENTS *telnet_events; FTPP_EVENT_INFO *event_info; FTPP_GEN_EVENTS gen_events; ftpp_eo_event_log_init(); /* * Check the input variables for correctness */ if(!Session || (iEvent >= TELNET_EO_EVENT_NUM)) { return FTPP_INVALID_ARG; } telnet_events = &(Session->event_list); gen_events.events = (FTPP_EVENT *)&(telnet_events->events); gen_events.stack = (int *)&(telnet_events->stack); gen_events.stack_count = telnet_events->stack_count; event_info = &telnet_event_info[iEvent]; iRet = ftpp_eo_event_log(&gen_events, event_info, iEvent, data, free_data); telnet_events->stack_count = gen_events.stack_count; return iRet; } /* * Function: ftp_eo_event_log(FTP_SESSION *Session, * int iEvent, * void *data, void (*free_data)(void *)) * * Purpose: This function logs events for ftp processing. * It invokes ftpp_eo_event_log using a generic event structure * that contains the ftp specific data. * * Arguments: Session => pointer to the FTP session * iEvent => the event id for the event * data => pointer to the user data of the event * free_data => pointer to a function to free the user data * * Returns: int => return code indicating error or success * */ int ftp_eo_event_log(FTP_SESSION *Session, int iEvent, void *data, void (*free_data)(void *)) { int iRet; FTP_EVENTS *ftp_events; FTPP_EVENT_INFO *event_info; FTPP_GEN_EVENTS gen_events; ftpp_eo_event_log_init(); /* * Check the input variables for correctness */ if(!Session || (iEvent >= FTP_EO_EVENT_NUM)) { return FTPP_INVALID_ARG; } ftp_events = &(Session->event_list); gen_events.events = (FTPP_EVENT *)&(ftp_events->events); gen_events.stack = (int *)&(ftp_events->stack); gen_events.stack_count = ftp_events->stack_count; event_info = &ftp_event_info[iEvent]; iRet = ftpp_eo_event_log(&gen_events, event_info, iEvent, data, free_data); ftp_events->stack_count = gen_events.stack_count; return iRet; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftp_bounce_lookup.c0000644000175000017500000001653714241075742024123 0ustar apoapo/* * ftp_bounce_lookup.c * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * Kevin Liu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains functions to access the BOUNCE_LOOKUP structure. * * We wrap the access to BOUNCE_LOOKUP so changing the lookup algorithms * are more modular and independent. This is the only file that would need * to be changed to change the algorithmic lookup. * * NOTES: * - 16.09.04: Initial Development. SAS * */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "hi_util_kmap.h" #include "ftpp_ui_config.h" #include "ftpp_return_codes.h" #include "snort_ftptelnet.h" /* * Function: ftp_bounce_lookup_init(BOUNCE_LOOKUP **BounceLookup) * * Purpose: Initialize the bounce_lookup structure. * * We need to initialize the bounce_lookup structure for * the FTP bounce configuration. Don't want a NULL pointer * flying around, when we have to look for allowable bounces. * * Arguments: BounceLookup => pointer to the pointer of the bounce * lookup structure. * * Returns: int => return code indicating error or success * */ int ftp_bounce_lookup_init(BOUNCE_LOOKUP **BounceLookup) { KMAP *km = KMapNew((KMapUserFreeFunc)FTPTelnetCleanupFTPBounceTo); *BounceLookup = km; if(*BounceLookup == NULL) { return FTPP_MEM_ALLOC_FAIL; } km->nocase = 1; return FTPP_SUCCESS; } /* * Function: ftp_bounce_lookup_cleanup(BOUNCE_LOOKUP **BounceLookup) * * Purpose: Free the bounce_lookup structure. * We need to free the bounce_lookup structure. * * Arguments: BounceLookup => pointer to the pointer of the bounce * lookup structure. * * Returns: int => return code indicating error or success * */ int ftp_bounce_lookup_cleanup(BOUNCE_LOOKUP **BounceLookup) { KMAP *km; if (BounceLookup == NULL) return FTPP_INVALID_ARG; km = *BounceLookup; if (km) { KMapDelete(km); *BounceLookup = NULL; } return FTPP_SUCCESS; } /* * Function: ftp_bounce_lookup_add(BOUNCE_LOOKUP *BounceLookup, * char *ip, int len, * FTP_BOUNCE_TO *BounceTo) * * Purpose: Add a bounce configuration to the list. IP is stored * in dot notation order. When the lookup happens, we * compare up to len bytes of the address. * * Arguments: BounceLookup => a pointer to the lookup structure * IP => the ftp bounce address * BounceTo => a pointer to the bounce configuration structure * * Returns: int => return code indicating error or success * */ int ftp_bounce_lookup_add(BOUNCE_LOOKUP *BounceLookup, sfcidr_t* Ip, FTP_BOUNCE_TO *BounceTo) { int iRet; if(!BounceLookup || !BounceTo) { return FTPP_INVALID_ARG; } iRet = KMapAdd(BounceLookup, (void*)&Ip->addr, sizeof(Ip->addr), (void*)BounceTo); if (iRet) { /* * This means the key has already been added. */ if(iRet == 1) { return FTPP_NONFATAL_ERR; } else { return FTPP_MEM_ALLOC_FAIL; } } return FTPP_SUCCESS; } /* * Function: ftp_bounce_lookup_find(BOUNCE_LOOKUP *BounceLookup, * sfaddr_t ip, int *iError) * * Purpose: Find a bounce configuration given a IP. * We look up a bounce configuration given an IP and * return a pointer to that bounce configuration if found. * * Arguments: BounceLookup => a pointer to the lookup structure * IP => the ftp bounce address * iError => a pointer to an error code * * Returns: int => return code indicating error or success * * Returns: FTP_BOUNCE_TO* => Pointer to bounce configuration structure * matching IP if found, NULL otherwise. * */ FTP_BOUNCE_TO *ftp_bounce_lookup_find( BOUNCE_LOOKUP *BounceLookup, sfaddr_t* Ip, int *iError ) { FTP_BOUNCE_TO *BounceTo = NULL; if(!iError) { return NULL; } if(!BounceLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; BounceTo = (FTP_BOUNCE_TO *)KMapFind(BounceLookup, (void*)Ip, sizeof(*Ip)); if (!BounceTo) { *iError = FTPP_NOT_FOUND; } return BounceTo; } /* * Function: ftp_bounce_lookup_first(BOUNCE_LOOKUP *BounceLookup, * int *iError) * * Purpose: This lookups the first bounce configuration, so we can * iterate through the configurations. * * Arguments: BounceLookup => pointer to the bounce lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_BOUNCE_TO* => Pointer to first bounce configuration structure * */ FTP_BOUNCE_TO *ftp_bounce_lookup_first(BOUNCE_LOOKUP *BounceLookup, int *iError) { FTP_BOUNCE_TO *BounceTo; if(!iError) { return NULL; } if(!BounceLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; BounceTo = (FTP_BOUNCE_TO *)KMapFindFirst(BounceLookup); if (!BounceTo) { *iError = FTPP_NOT_FOUND; } return BounceTo; } /* * Function: ftp_bounce_lookup_next(BOUNCE_LOOKUP *BounceLookup, * int *iError) * * Iterates to the next configuration, like a list it just returns * the next config in the config list. * * Purpose: This lookups the next bounce configuration, so we can * iterate through the configurations. * * Arguments: BounceLookup => pointer to the bounce lookup structure * iError => pointer to the integer to set for errors * * Returns: FTP_BOUNCE_TO* => Pointer to next bounce configuration structure * */ FTP_BOUNCE_TO *ftp_bounce_lookup_next(BOUNCE_LOOKUP *BounceLookup, int *iError) { FTP_BOUNCE_TO *BounceTo; if(!iError) { return NULL; } if(!BounceLookup) { *iError = FTPP_INVALID_ARG; return NULL; } *iError = FTPP_SUCCESS; BounceTo = (FTP_BOUNCE_TO *)KMapFindNext(BounceLookup); if (!BounceTo) { *iError = FTPP_NOT_FOUND; } return BounceTo; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/hi_util_xmalloc.c0000644000175000017500000000506214241075777023561 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* ** util.c */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "ftpp_include.h" #include "memory_stats.h" //#define MDEBUG static unsigned msize=0; void * xmalloc(size_t byteSize) { #ifdef MDEBUG int * data = (int*)_dpd.snortAlloc(1, byteSize+4, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); unsigned m = msize; #else int * data = (int*)_dpd.snortAlloc(1, byteSize, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); #endif if( data == NULL ) { return NULL; } #ifdef MDEBUG msize += byteSize + 4; *data = byteSize+4; //printf("** xmalloc msize=%u, allocbytes=%d, msize=%u %x\n", m, byteSize+4, msize, data); data++; return data; #else msize += byteSize; return data; #endif } void xfree( void * p , int n ) { #ifdef MDEBUG unsigned m = msize; int *q = (int*)p; q--; msize -= *q; _dpd.snortFree(q, n+4, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); #else _dpd.snortFree(p, n, PP_FTPTELNET, PP_MEM_CATEGORY_CONFIG); #endif } void xshowmem(void) { #ifdef MDEBUG printf("xmalloc-mem: %u bytes\n",msize); #endif } char *xstrdup(const char *str) { int data_size; char *data = NULL; data_size = strlen(str) + 1; data = (char *)xmalloc(data_size); if(data == NULL) { return NULL; } strncpy(data, str, data_size - 1); data[data_size - 1] = '\0'; return data; } snort-2.9.20/src/dynamic-preprocessors/ftptelnet/hi_util_xmalloc.h0000644000175000017500000000224114241076000023536 0ustar apoapo/* * util.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * */ #ifndef __HI_UTIL_XMALLOC_H__ #define __HI_UTIL_XMALLOC_H__ #ifdef WIN32 #define snprintf _snprintf #else #include #endif void *xmalloc(size_t byteSize); char *xstrdup(const char *str); void xshowmem(void); void xfree( void * , int ); #endif snort-2.9.20/src/dynamic-preprocessors/ftptelnet/ftpp_si.h0000644000175000017500000001656514241075761022061 0ustar apoapo/* * ftpp_si.h * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * Steven A. Sturges * Daniel J. Roelker * Marc A. Norton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Description: * * This file contains structures and functions for the * Session Inspection Module. * * The Session Inspection Module has several data structures that are * very important to the functionality of the module. The two major * structures are the FTPP_SESSION and the FTPP_SI_INPUT. * * NOTES: * - 20.09.04: Initial Development. SAS * */ #ifndef __FTPP_SI_H__ #define __FTPP_SI_H__ #include #include "ftpp_include.h" #include "ftpp_ui_config.h" #include "ftp_client.h" #include "ftp_server.h" #include "sf_snort_packet.h" #include "ftpp_eo.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "session_api.h" /* * These are the defines for the different types of * inspection modes. We have a server mode and a client mode. */ #define FTPP_SI_NO_MODE 0 #define FTPP_SI_CLIENT_MODE 1 #define FTPP_SI_SERVER_MODE 2 #define FTPP_SI_PROTO_UNKNOWN 0 #define FTPP_SI_PROTO_TELNET 1 #define FTPP_SI_PROTO_FTP 2 #define FTPP_SI_PROTO_FTP_DATA 3 #define FTPP_FILE_IGNORE -1 #define FTPP_FILE_UNKNOWN 0 /* Macros for testing the type of FTP_TELNET_SESSION */ #define FTPP_SI_IS_PROTO(Ssn, Pro) ((Ssn) && ((Ssn)->ft_ssn.proto == (Pro))) #define PROTO_IS_FTP(ssn) FTPP_SI_IS_PROTO(ssn, FTPP_SI_PROTO_FTP) #define PROTO_IS_FTP_DATA(ssn) FTPP_SI_IS_PROTO(ssn, FTPP_SI_PROTO_FTP_DATA) #define PROTO_IS_TELNET(ssn) FTPP_SI_IS_PROTO(ssn, FTPP_SI_PROTO_TELNET) typedef struct s_FTP_TELNET_SESSION { int proto; } FTP_TELNET_SESSION; /* * The TELNET_SESSION structure contains the complete TELNET session. * This structure is the structure that is saved per session in the * Stream Interface Module. This structure gets sent through the * detection engine process (Normalization, Detection). */ typedef struct s_TELNET_SESSION { FTP_TELNET_SESSION ft_ssn; /* The global configuration for this session */ tSfPolicyId policy_id; tSfPolicyUserContextId global_conf; /* The client configuration for this session if its FTP */ TELNET_PROTO_CONF *telnet_conf; /* Number of consecutive are-you-there commands seen. */ int consec_ayt; int encr_state; TELNET_EVENTS event_list; } TELNET_SESSION; /* * These are the state values for determining the FTP data channel. */ #define NO_STATE 0x00 #define LOST_STATE 0xFFFFFFFF #define DATA_CHAN_PORT_CMD_ISSUED 0x01 #define DATA_CHAN_PORT_CMD_ACCEPT 0x02 #define DATA_CHAN_PASV_CMD_ISSUED 0x04 #define DATA_CHAN_PASV_CMD_ACCEPT 0x08 #define DATA_CHAN_XFER_CMD_ISSUED 0x10 #define DATA_CHAN_XFER_STARTED 0x20 #define DATA_CHAN_CLIENT_HELLO_SEEN 0x40 #define DATA_CHAN_REST_CMD_ISSUED 0x80 #define AUTH_TLS_CMD_ISSUED 0x01 #define AUTH_SSL_CMD_ISSUED 0x02 #define AUTH_UNKNOWN_CMD_ISSUED 0x04 #define AUTH_TLS_ENCRYPTED 0x08 #define AUTH_SSL_ENCRYPTED 0x10 #define AUTH_UNKNOWN_ENCRYPTED 0x20 /* * The FTP_SESSION structure contains the complete FTP session, both the * client and the server constructs. This structure is the structure that * is saved per session in the Stream Interface Module. This structure * gets sent through the detection engine process (Normalization, * Detection). */ typedef struct s_FTP_SESSION { FTP_TELNET_SESSION ft_ssn; tSfPolicyId policy_id; /* The client construct contains all the info associated with a * client request. */ FTP_CLIENT client; /* The server construct contains all the info associated with a * server response. */ FTP_SERVER server; /* The client configuration for this session if its FTP */ FTP_CLIENT_PROTO_CONF *client_conf; /* The server configuration for this session if its FTP */ FTP_SERVER_PROTO_CONF *server_conf; /* The global configuration for this session */ tSfPolicyUserContextId global_conf; /* The data channel info */ int data_chan_state; uint32_t data_chan_index; uint32_t data_xfer_index; sfaddr_t clientIP; uint16_t clientPort; sfaddr_t serverIP; uint16_t serverPort; uint32_t ftp_cmd_pipe_index; uint32_t rest_cmd_offset; uint16_t control_clientPort; uint16_t control_serverPort; /* A file is being transfered on ftp-data channel */ char *filename; int file_xfer_info; /* -1: ignore, 0: unknown, >0: filename length */ bool data_xfer_dir; /* Command/data channel encryption */ bool encr_state_chello; unsigned char flags; int encr_state; uint32_t flow_id; /* Alertable event list */ FTP_EVENTS event_list; void *datassn; sfaddr_t control_clientIP; sfaddr_t control_serverIP; } FTP_SESSION; #define FTP_FLG_MALWARE_ENABLED (1<<1) #ifdef TARGET_BASED /* FTP-Data Transfer Modes */ enum { FTPP_XFER_PASSIVE = 0, FTPP_XFER_ACTIVE = 1 }; typedef struct s_FTP_DATA_SESSION { FTP_TELNET_SESSION ft_ssn; StreamSessionKey * ftp_key; void* ftpssn; char *filename; int data_chan; int file_xfer_info; FilePosition position; bool direction; unsigned char mode; unsigned char flags; uint32_t flow_id; uint32_t path_hash; } FTP_DATA_SESSION; #define FTPDATA_FLG_REASSEMBLY_SET (1<<0) #define FTPDATA_FLG_FILENAME_SET (1<<1) #define FTPDATA_FLG_STOP (1<<2) #define FTPDATA_FLG_REST (1<<3) #define FTPDATA_FLG_FLUSH (1<<4) #endif /* * The FTPP_SI_INPUT structure holds the information that the Session * Inspection Module needs to determine the type of inspection mode * (client, server, neither) and to retrieve the appropriate server * configuration. * * The input is the source and destination IP addresses, and the * source and destination ports (since this should always be a * TCP packet). */ typedef struct s_FTPP_SI_INPUT { sfaddr_t sip; sfaddr_t dip; unsigned short sport; unsigned short dport; unsigned char pdir; unsigned char pproto; } FTPP_SI_INPUT; int ftpp_si_determine_proto(SFSnortPacket *p, FTPTELNET_GLOBAL_CONF *GlobalConf, FTP_TELNET_SESSION **, FTPP_SI_INPUT *SiInput, int *piInspectMode); int FTPGetPacketDir(SFSnortPacket *); #ifdef TARGET_BASED /* FTP-Data file processing */ FTP_DATA_SESSION * FTPDataSessionNew(SFSnortPacket *p); void FTPDataSessionFree(void *p_ssn); bool FTPDataDirection(SFSnortPacket *p, FTP_DATA_SESSION *ftpdata); #endif #endif /* ! __FTPP_SI_H__ */ snort-2.9.20/src/dynamic-preprocessors/sf_dynamic_initialize/0000755000175000017500000000000014242725715022561 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/sf_dynamic_initialize/sf_dynamic_initialize.dsp0000444000175000017500000020357014230012554027616 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_dynamic_initialize" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Generic Project" 0x010a CFG=sf_dynamic_initialize - Win32 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_dynamic_initialize.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_dynamic_initialize.mak" CFG="sf_dynamic_initialize - Win32 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_dynamic_initialize - Win32 Release" (based on "Win32 (x86) Generic Project") !MESSAGE "sf_dynamic_initialize - Win32 Debug" (based on "Win32 (x86) Generic Project") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" MTL=midl.exe !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Target_Dir "" !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Target_Dir "" !ENDIF # Begin Target # Name "sf_dynamic_initialize - Win32 Release" # Name "sf_dynamic_initialize - Win32 Debug" # Begin Source File SOURCE=..\..\sfutil\bitop.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\bitop.h InputName=bitop "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\bitop.h InputName=bitop "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\cpuclock.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\cpuclock.h InputName=cpuclock "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\cpuclock.h InputName=cpuclock "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\event.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\event.h InputName=event "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\event.h InputName=event "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\file-process\file_api.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\file-process\file_api.h" InputName=file_api "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\file-process\file_api.h" InputName=file_api "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\file-process\file_mail_common.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\file-process\file_mail_common.h" InputName=file_mail_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\file-process\file_mail_common.h" InputName=file_mail_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\idle_processing.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\idle_processing.h InputName=idle_processing "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\idle_processing.h InputName=idle_processing "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\win32\WIN32-Code\inet_aton.c" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\win32\WIN32-Code\inet_aton.c" InputName=inet_aton "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\win32\WIN32-Code\inet_aton.c" InputName=inet_aton "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\win32\WIN32-Code\inet_pton.c" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\win32\WIN32-Code\inet_pton.c" InputName=inet_pton "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\win32\WIN32-Code\inet_pton.c" InputName=inet_pton "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\ipv6_port.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\ipv6_port.h InputName=ipv6_port BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/->iph->ip_src/->ip4_header->source/" -e "s/->iph->ip_dst/->ip4_header->destination/" -e "s/->iph->/->ip4_header->/" -e "s/->iph$/->ip4_header/" -e "s/orig_iph$/orig_ip4_header/" -e "s/ip_verhl/version_headerlength/" -e "s/ip_tos/type_service/" -e "s/ip_len/data_length/" -e "s/ip_id/identifier/" -e "s/ip_off/offset/" -e "s/ip_ttl/time_to_live/" -e "s/ip_proto/proto/" -e "s/ip_csum/checksum/" -e "s/p->iph$/p->ip4_header/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\ipv6_port.h InputName=ipv6_port BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/->iph->ip_src/->ip4_header->source/" -e "s/->iph->ip_dst/->ip4_header->destination/" -e "s/->iph->/->ip4_header->/" -e "s/->iph$/->ip4_header/" -e "s/orig_iph$/orig_ip4_header/" -e "s/ip_verhl/version_headerlength/" -e "s/ip_tos/type_service/" -e "s/ip_len/data_length/" -e "s/ip_id/identifier/" -e "s/ip_off/offset/" -e "s/ip_ttl/time_to_live/" -e "s/ip_proto/proto/" -e "s/ip_csum/checksum/" -e "s/p->iph$/p->ip4_header/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\mempool.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\mempool.c InputName=mempool BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\mempool.c InputName=mempool BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\mempool.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\mempool.h InputName=mempool BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\mempool.h InputName=mempool BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\mpse_methods.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\mpse_methods.h InputName=mpse_methods "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include\$(InputName).h # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\mpse_methods.h InputName=mpse_methods "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include\$(InputName).h # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\obfuscation.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\obfuscation.h InputName=obfuscation BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\obfuscation.h InputName=obfuscation BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\pcap_pkthdr32.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\pcap_pkthdr32.h InputName=pcap_pkthdr32 "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\pcap_pkthdr32.h InputName=pcap_pkthdr32 "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\plugin_enum.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\plugin_enum.h InputName=plugin_enum "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\plugin_enum.h InputName=plugin_enum "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocids.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocids.h InputName=preprocids "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocids.h InputName=preprocids "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\profiler.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\profiler.h InputName=profiler "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\profiler.h InputName=profiler "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\rule_option_types.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\rule_option_types.h InputName=rule_option_types "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\rule_option_types.h InputName=rule_option_types "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\segment_mem.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\segment_mem.c InputName=segment_mem "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\segment_mem.c InputName=segment_mem "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\segment_mem.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\segment_mem.h InputName=segment_mem "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\segment_mem.h InputName=segment_mem "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocessors\session_api.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\session_api.h InputName=session_api BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\session_api.h InputName=session_api BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocessors\Session\session_common.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\Session\session_common.h InputName=session_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\Session\session_common.h InputName=session_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocessors\Session\session_expect.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\Session\session_expect.h InputName=session_expect "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\Session\session_expect.h InputName=session_expect "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sf_base64decode.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sf_base64decode.c InputName=sf_base64decode "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sf_base64decode.c InputName=sf_base64decode "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sf_base64decode.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sf_base64decode.h InputName=sf_base64decode "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sf_base64decode.h InputName=sf_base64decode "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_dynamic_common.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_common.h" InputName=sf_dynamic_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_common.h" InputName=sf_dynamic_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_dynamic_define.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_define.h" InputName=sf_dynamic_define "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_define.h" InputName=sf_dynamic_define "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_dynamic_engine.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_engine.h" InputName=sf_dynamic_engine "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_engine.h" InputName=sf_dynamic_engine "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_dynamic_meta.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_meta.h" InputName=sf_dynamic_meta "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_meta.h" InputName=sf_dynamic_meta "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_preproc_example\sf_dynamic_preproc_lib.c" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_preproc_example\sf_dynamic_preproc_lib.c" InputName=sf_dynamic_preproc_lib "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_preproc_example\sf_dynamic_preproc_lib.c" InputName=sf_dynamic_preproc_lib "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_preproc_example\sf_dynamic_preproc_lib.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_preproc_example\sf_dynamic_preproc_lib.h" InputName=sf_dynamic_preproc_lib "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_preproc_example\sf_dynamic_preproc_lib.h" InputName=sf_dynamic_preproc_lib "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_dynamic_preprocessor.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_preprocessor.h" InputName=sf_dynamic_preprocessor BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_dynamic_preprocessor.h" InputName=sf_dynamic_preprocessor BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sf_email_attach_decode.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sf_email_attach_decode.c InputName=sf_email_attach_decode BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/SnortStrnStr/_dpd.SnortStrnStr/" -e "/util.h/d" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sf_email_attach_decode.c InputName=sf_email_attach_decode BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/SnortStrnStr/_dpd.SnortStrnStr/" -e "/util.h/d" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sf_email_attach_decode.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sf_email_attach_decode.h InputName=sf_email_attach_decode "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sf_email_attach_decode.h InputName=sf_email_attach_decode "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sf_ip.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sf_ip.c InputName=sf_ip "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sf_ip.c InputName=sf_ip "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sf_ip.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sf_ip.h InputName=sf_ip "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sf_ip.h InputName=sf_ip "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sf_protocols.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sf_protocols.h InputName=sf_protocols "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sf_protocols.h InputName=sf_protocols "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sf_sdlist.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sf_sdlist.c InputName=sf_sdlist BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sf_sdlist.c InputName=sf_sdlist BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sf_sdlist.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sf_sdlist.h InputName=sf_sdlist BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sf_sdlist.h InputName=sf_sdlist BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sf_sdlist_types.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sf_sdlist_types.h InputName=sf_sdlist_types BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sf_sdlist_types.h InputName=sf_sdlist_types BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sf_seqnums.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sf_seqnums.h InputName=sf_seqnums "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sf_seqnums.h InputName=sf_seqnums "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_engine\sf_snort_packet.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\sf_snort_packet.h" InputName=sf_snort_packet "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\sf_snort_packet.h" InputName=sf_snort_packet "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_engine\sf_snort_plugin_api.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\sf_snort_plugin_api.h" InputName=sf_snort_plugin_api "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\sf_snort_plugin_api.h" InputName=sf_snort_plugin_api "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sf_types.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sf_types.h InputName=sf_types "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sf_types.h InputName=sf_types "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\control\sfcontrol.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\control\sfcontrol.h InputName=sfcontrol "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\control\sfcontrol.h InputName=sfcontrol "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfhashfcn.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfhashfcn.h InputName=sfhashfcn "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfhashfcn.h InputName=sfhashfcn "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\libs\sfparser.c # End Source File # Begin Source File SOURCE="..\..\sfutil\sfPolicy.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\sfutil\sfPolicy.h" InputName=sfPolicy BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\sfutil\sfPolicy.h" InputName=sfPolicy BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\sfutil\sfPolicyUserData.c" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\sfutil\sfPolicyUserData.c" InputName=sfPolicyUserData BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\sfutil\sfPolicyUserData.c" InputName=sfPolicyUserData BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).c.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/$(InputName).c.new > ../include/$(InputName).c \ "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).c.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\sfutil\sfPolicyUserData.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\sfutil\sfPolicyUserData.h" InputName=sfPolicyUserData BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\sfutil\sfPolicyUserData.h" InputName=sfPolicyUserData BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt.c InputName=sfrt "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt.c InputName=sfrt "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt.h InputName=sfrt "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt.h InputName=sfrt "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt_dir.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt_dir.c InputName=sfrt_dir "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt_dir.c InputName=sfrt_dir "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt_dir.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt_dir.h InputName=sfrt_dir "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt_dir.h InputName=sfrt_dir "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt_flat.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat.c InputName=sfrt_flat "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat.c InputName=sfrt_flat "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt_flat.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat.h InputName=sfrt_flat "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat.h InputName=sfrt_flat "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt_flat_dir.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat_dir.c InputName=sfrt_flat_dir "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat_dir.c InputName=sfrt_flat_dir "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt_flat_dir.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat_dir.h InputName=sfrt_flat_dir "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt_flat_dir.h InputName=sfrt_flat_dir "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfrt_trie.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfrt_trie.h InputName=sfrt_trie "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfrt_trie.h InputName=sfrt_trie "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_engine\examples\sfsnort_dynamic_detection_lib.c" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\examples\sfsnort_dynamic_detection_lib.c" InputName=sfsnort_dynamic_detection_lib "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\examples\sfsnort_dynamic_detection_lib.c" InputName=sfsnort_dynamic_detection_lib "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\dynamic-plugins\sf_engine\examples\sfsnort_dynamic_detection_lib.h" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\examples\sfsnort_dynamic_detection_lib.h" InputName=sfsnort_dynamic_detection_lib "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\dynamic-plugins\sf_engine\examples\sfsnort_dynamic_detection_lib.h" InputName=sfsnort_dynamic_detection_lib "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\sfxhash.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\sfxhash.h InputName=sfxhash "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\sfxhash.h InputName=sfxhash "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\signature.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\signature.h InputName=signature BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -f ..\treenodes.sed ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\signature.h InputName=signature BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -f ..\treenodes.sed ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocessors\sip_common.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\sip_common.h InputName=sip_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\sip_common.h InputName=sip_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\snort_bounds.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\snort_bounds.h InputName=snort_bounds "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\snort_bounds.h InputName=snort_bounds "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\snort_debug.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\snort_debug.h InputName=snort_debug BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" -e "s/; DebugMessageFunc$/; _dpd.debugMsg/" -e "s/; DebugWideMessageFunc$/; _dpd.debugWideMsg/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\snort_debug.h InputName=snort_debug BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" -e "s/; DebugMessageFunc$/; _dpd.debugMsg/" -e "s/; DebugWideMessageFunc$/; _dpd.debugWideMsg/" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocessors\Session\snort_session.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\Session\snort_session.h InputName=snort_session "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\Session\snort_session.h InputName=snort_session "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\ssl_common\ssl.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_ha.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_ha.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_include.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_inspect.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_inspect.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_session.h # End Source File # Begin Source File SOURCE=..\..\preprocessors\str_search.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\str_search.h InputName=str_search BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\str_search.h InputName=str_search BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocessors\stream_api.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\stream_api.h InputName=stream_api BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\stream_api.h InputName=stream_api BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\preprocessors\Stream6\stream_common.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\preprocessors\Stream6\stream_common.h InputName=stream_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\preprocessors\Stream6\stream_common.h InputName=stream_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE="..\..\win32\WIN32-Code\strtok_r.c" !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath="..\..\win32\WIN32-Code\strtok_r.c" InputName=strtok_r "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath="..\..\win32\WIN32-Code\strtok_r.c" InputName=strtok_r "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\treenodes.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\treenodes.h InputName=treenodes BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -f ..\treenodes.sed ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\treenodes.h InputName=treenodes BuildCmds= \ mkdir ..\include \ copy $(InputPath) ..\include\$(InputName).h.new \ c:\cygwin\bin\sed -f ..\treenodes.sed ../include/$(InputName).h.new > ../include/$(InputName).h \ "..\include\$(InputName).h.new" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\Unified2_common.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\Unified2_common.h InputName=Unified2_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\Unified2_common.h InputName=Unified2_common "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\util_unfold.c !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\util_unfold.c InputName=util_unfold "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\util_unfold.c InputName=util_unfold "..\include\$(InputName).c" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # Begin Source File SOURCE=..\..\sfutil\util_unfold.h !IF "$(CFG)" == "sf_dynamic_initialize - Win32 Release" # Begin Custom Build InputPath=..\..\sfutil\util_unfold.h InputName=util_unfold "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ELSEIF "$(CFG)" == "sf_dynamic_initialize - Win32 Debug" # Begin Custom Build InputPath=..\..\sfutil\util_unfold.h InputName=util_unfold "..\include\$(InputName).h" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" mkdir ..\include copy $(InputPath) ..\include # End Custom Build !ENDIF # End Source File # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/sf_dynamic_initialize/sf_dynamic_initialize.vcxproj0000444000175000017500000032732414230012554030527 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MakeFileProj {E8E29B7C-BDA1-4D36-93E9-B0596C35827F} 10.0.17763.0 Application v141 Application v141 Utility v141 false Utility v141 false Utility v141 false Utility v141 false .\Debug\ .\Debug\ .\Release\ .\Release\ .\Release\ .\Release\ .\Debug\sf_dynamic_initialize.tlb .\Debug\sf_dynamic_initialize.tlb .\Release\sf_dynamic_initialize.tlb .\Release\sf_dynamic_initialize.tlb mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/->iph->ip_src/->ip4_header->source/" -e "s/->iph->ip_dst/->ip4_header->destination/" -e "s/->iph->/->ip4_header->/" -e "s/->iph$/->ip4_header/" -e "s/orig_iph$/orig_ip4_header/" -e "s/ip_verhl/version_headerlength/" -e "s/ip_tos/type_service/" -e "s/ip_len/data_length/" -e "s/ip_id/identifier/" -e "s/ip_off/offset/" -e "s/ip_ttl/time_to_live/" -e "s/ip_proto/proto/" -e "s/ip_csum/checksum/" -e "s/p->iph$/p->ip4_header/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/->iph->ip_src/->ip4_header->source/" -e "s/->iph->ip_dst/->ip4_header->destination/" -e "s/->iph->/->ip4_header->/" -e "s/->iph$/->ip4_header/" -e "s/orig_iph$/orig_ip4_header/" -e "s/ip_verhl/version_headerlength/" -e "s/ip_tos/type_service/" -e "s/ip_len/data_length/" -e "s/ip_id/identifier/" -e "s/ip_off/offset/" -e "s/ip_ttl/time_to_live/" -e "s/ip_proto/proto/" -e "s/ip_csum/checksum/" -e "s/p->iph$/p->ip4_header/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/->iph->ip_src/->ip4_header->source/" -e "s/->iph->ip_dst/->ip4_header->destination/" -e "s/->iph->/->ip4_header->/" -e "s/->iph$/->ip4_header/" -e "s/orig_iph$/orig_ip4_header/" -e "s/ip_verhl/version_headerlength/" -e "s/ip_tos/type_service/" -e "s/ip_len/data_length/" -e "s/ip_id/identifier/" -e "s/ip_off/offset/" -e "s/ip_ttl/time_to_live/" -e "s/ip_proto/proto/" -e "s/ip_csum/checksum/" -e "s/p->iph$/p->ip4_header/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/->iph->ip_src/->ip4_header->source/" -e "s/->iph->ip_dst/->ip4_header->destination/" -e "s/->iph->/->ip4_header->/" -e "s/->iph$/->ip4_header/" -e "s/orig_iph$/orig_ip4_header/" -e "s/ip_verhl/version_headerlength/" -e "s/ip_tos/type_service/" -e "s/ip_len/data_length/" -e "s/ip_id/identifier/" -e "s/ip_off/offset/" -e "s/ip_ttl/time_to_live/" -e "s/ip_proto/proto/" -e "s/ip_csum/checksum/" -e "s/p->iph$/p->ip4_header/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" -e "s/%3b DebugMessageFunc$/%3b _dpd.debugMsg/" -e "s/%3b DebugWideMessageFunc$/%3b _dpd.debugWideMsg/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" -e "s/%3b DebugMessageFunc$/%3b _dpd.debugMsg/" -e "s/%3b DebugWideMessageFunc$/%3b _dpd.debugWideMsg/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" -e "s/%3b DebugMessageFunc$/%3b _dpd.debugMsg/" -e "s/%3b DebugWideMessageFunc$/%3b _dpd.debugWideMsg/" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/DebugMessageFile = /*_dpd.debugMsgFile = /" -e "s/DebugMessageLine = /*_dpd.debugMsgLine = /" -e "s/%3b DebugMessageFunc$/%3b _dpd.debugMsg/" -e "s/%3b DebugWideMessageFunc$/%3b _dpd.debugWideMsg/" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) ..\include\$(InputName).h;..\include\$(InputName).h.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -e "s/Packet /SFSnortPacket /" -e "s/decode.h/sf_snort_packet.h/" -e "/sfportobject\.h/d" -e "s/PortObject/void/g" ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h mkdir ..\include copy %(FullPath) ..\include\%(Filename).h.new c:\cygwin\bin\sed -f ..\treenodes.sed ../include/%(Filename).h.new > ../include/%(Filename).h ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h.new;..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).h;%(Outputs) ..\include\$(InputName).h;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/SnortStrnStr/_dpd.SnortStrnStr/" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/SnortStrnStr/_dpd.SnortStrnStr/" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/SnortStrnStr/_dpd.SnortStrnStr/" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/SnortStrnStr/_dpd.SnortStrnStr/" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" -e "s/ErrorMessage/_dpd.errMsg/" -e "s/LogMessage /_dpd.logMsg /" -e "/util.h/d" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).c.new > ../include/%(Filename).c mkdir ..\include copy %(FullPath) ..\include\%(Filename).c.new c:\cygwin\bin\sed -e "/SharedObjectAddStarts/d" -e "/SharedObjectAddEnds/d" -e "/SharedObjectDeleteBegins/,/SharedObjectDeleteEnds/d" -e "s/getDefaultPolicy()/_dpd.getDefaultPolicy()/" ../include/%(Filename).c.new > ../include/%(Filename).c ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) ..\include\$(InputName).c;..\include\$(InputName).c.new;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) mkdir ..\include copy %(FullPath) ..\include mkdir ..\include copy %(FullPath) ..\include ..\include\$(InputName).c;%(Outputs) ..\include\$(InputName).c;%(Outputs) snort-2.9.20/src/dynamic-preprocessors/pop/0000755000175000017500000000000014242725715017022 5ustar apoaposnort-2.9.20/src/dynamic-preprocessors/pop/snort_pop.h0000644000175000017500000001160714241076151021213 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * **************************************************************************/ /************************************************************************** * * snort_pop.h * * Author: Bhagyashree Bantwal * * Description: * * This file defines everything specific to the POP preprocessor. * **************************************************************************/ #ifndef __POP_H__ #define __POP_H__ /* Includes ***************************************************************/ #include #include "sf_snort_packet.h" #include "pop_config.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "mempool.h" #include "sf_email_attach_decode.h" #include "file_mail_common.h" #include "file_api.h" #ifdef DEBUG #include "sf_types.h" #endif /**************************************************************************/ /* Defines ****************************************************************/ /* Direction packet is coming from, if we can figure it out */ #define POP_PKT_FROM_UNKNOWN 0 #define POP_PKT_FROM_CLIENT 1 #define POP_PKT_FROM_SERVER 2 #define SEARCH_CMD 0 #define SEARCH_RESP 1 #define SEARCH_HDR 2 #define SEARCH_DATA_END 3 #define NUM_SEARCHES 4 #define BOUNDARY 0 #define STATE_DATA 0 /* Data state */ #define STATE_TLS_CLIENT_PEND 1 /* Got STARTTLS */ #define STATE_TLS_SERVER_PEND 2 /* Got STARTTLS */ #define STATE_TLS_DATA 3 /* Successful handshake, TLS encrypted data */ #define STATE_COMMAND 4 #define STATE_UNKNOWN 5 #define STATE_DATA_INIT 0 #define STATE_DATA_HEADER 1 /* Data header section of data state */ #define STATE_DATA_BODY 2 /* Data body section of data state */ #define STATE_MIME_HEADER 3 /* MIME header section within data section */ #define STATE_DATA_UNKNOWN 4 /* session flags */ #define POP_FLAG_NEXT_STATE_UNKNOWN 0x00000004 #define POP_FLAG_GOT_NON_REBUILT 0x00000008 #define POP_FLAG_CHECK_SSL 0x00000010 #define POP_SSL_ERROR_FLAGS (SSL_BOGUS_HS_DIR_FLAG | \ SSL_BAD_VER_FLAG | \ SSL_BAD_TYPE_FLAG | \ SSL_UNKNOWN_FLAG) /* Maximum length of header chars before colon, based on Exim 4.32 exploit */ #define MAX_HEADER_NAME_LEN 64 #define POP_PROTO_REF_STR "pop3" /**************************************************************************/ /* Data structures ********************************************************/ typedef enum _POPCmdEnum { CMD_APOP = 0, CMD_AUTH, CMD_CAPA, CMD_DELE, CMD_LIST, CMD_NOOP, CMD_PASS, CMD_QUIT, CMD_RETR, CMD_RSET, CMD_STAT, CMD_STLS, CMD_TOP, CMD_UIDL, CMD_USER, CMD_LAST } POPCmdEnum; typedef enum _POPRespEnum { RESP_OK = 1, RESP_ERR, RESP_LAST } POPRespEnum; typedef enum _POPHdrEnum { HDR_CONTENT_TYPE = 0, HDR_CONT_TRANS_ENC, HDR_CONT_DISP, HDR_LAST } POPHdrEnum; typedef struct _POPSearchInfo { int id; int index; int length; } POPSearchInfo; typedef struct _POP { int state; int prev_response; int state_flags; int session_flags; int alert_mask; int reassembling; #ifdef DEBUG_MSGS uint64_t session_number; #endif MimeState mime_ssn; tSfPolicyId policy_id; uint32_t flow_id; tSfPolicyUserContextId config; } POP; /**************************************************************************/ /* Function prototypes ****************************************************/ void POP_InitCmds(POPConfig *config); void POP_SearchInit(void); void POP_Free(void); void SnortPOP(SFSnortPacket *); int POP_IsServer(uint16_t); void POP_FreeConfig(POPConfig *); void POP_FreeConfigs(tSfPolicyUserContextId); int POP_GetFilename(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); /**************************************************************************/ #endif /* __POP_H__ */ snort-2.9.20/src/dynamic-preprocessors/pop/pop_util.h0000644000175000017500000000324014241076141021014 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************* * * pop_util.h * * Author: Bhagyashree Bantwal * *************************************************************************/ #ifndef __POP_UTIL_H__ #define __POP_UTIL_H__ #include "sf_snort_packet.h" void POP_GetEOL(const uint8_t *, const uint8_t *, const uint8_t **, const uint8_t **); void POP_DecodeType(const char *start, int length, bool cnt_xf); int POP_Print_Mem_Stats(FILE *fd, char *buffer, PreprocMemInfo *meminfo); #ifdef DEBUG_MSGS const char * POP_PrintBuffer(SFSnortPacket *); #endif #endif /* __POP_UTIL_H__ */ snort-2.9.20/src/dynamic-preprocessors/pop/spp_pop.h0000644000175000017500000000222114241076160020640 0ustar apoapo /* * spp_pop.h * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * Author: Bhagyashree Bantwal * * Description: * * This file defines the publicly available functions for the POP * functionality for Snort. * */ #ifndef __SPP_POP_H__ #define __SPP_POP_H__ void SetupPOP(void); #endif snort-2.9.20/src/dynamic-preprocessors/pop/sf_pop.vcxproj0000444000175000017500000004451514230012554021717 0ustar apoapo Debug Win32 Debug x64 Release Win32 Release x64 Template Win32 Template x64 MFCProj {584E267E-2E58-4388-B56C-3BF198680AF3} 10.0.17763.0 Application v141 Application v141 DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte DynamicLibrary v141 Dynamic MultiByte .\Release\ .\Release\ false false .\Release\ .\Release\ .\Debug\ .\Debug\ true true MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_pop.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_pop.tlb true Win32 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_pop.bsc true true Console .\Release\sf_pop.dll .\Release\sf_pop.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDLL Default true true MaxSpeed true Level3 false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) NDEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Release\ .\Release\sf_pop.pch .\Release\ .\Release\ true NDEBUG;%(PreprocessorDefinitions) .\Release\sf_pop.tlb true 0x0409 NDEBUG;%(PreprocessorDefinitions) true .\Release\sf_pop.bsc true true Console .\Release\sf_pop.dll .\Release\sf_pop.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Release/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 true EditAndContinue false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_pop.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_pop.tlb true Win32 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_pop.bsc true true true Console .\Debug\sf_pop.dll .\Debug\sf_pop.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) MultiThreadedDebugDLL Default false Disabled true Level3 ProgramDatabase false ..\libs;..\ssl_common;..\include;..\..\win32\Win32-Includes;.\;..\..\win32\Win32-Includes\WinPCAP;..\..\..\daq\api;..\..\..\daq\sfbpf;%(AdditionalIncludeDirectories) _DEBUG;DEBUG;ENABLE_PAF;SF_SNORT_PREPROC_DLL;_WINDOWS;_USRDLL;ACTIVE_RESPONSE;GRE;MPLS;TARGET_BASED;PERF_PROFILING;ENABLE_RESPOND;ENABLE_REACT;WIN32;HAVE_CONFIG_H;SIGNAL_SNORT_READ_ATTR_TBL=30;%(PreprocessorDefinitions) .\Debug\ .\Debug\sf_pop.pch .\Debug\ .\Debug\ EnableFastChecks true _DEBUG;%(PreprocessorDefinitions) .\Debug\sf_pop.tlb true 0x0409 _DEBUG;%(PreprocessorDefinitions) true .\Debug\sf_pop.bsc true true true Console .\Debug\sf_pop.dll .\Debug\sf_pop.lib ../../../src/win32/WIN32-Libraries;%(AdditionalLibraryDirectories) pcre.lib;ws2_32.lib;../libs/Debug/sfdynamic_preproc_libs.lib;%(AdditionalDependencies) {ce034b6a-1872-4f3c-bd89-6dde0fc68fe7} false snort-2.9.20/src/dynamic-preprocessors/pop/pop_config.c0000644000175000017500000002557014241076125021313 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*************************************************************************** * pop_config.c * * Author: Bhagyashree Bantwal * * Description: * * Handle configuration of the POP preprocessor * * Entry point functions: * * POP_ParseArgs() * ***************************************************************************/ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_pop.h" #include "pop_config.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" #include "sfPolicy.h" /* Global variable to hold configuration */ extern POPConfig **pop_config; extern const POPToken pop_known_cmds[]; /* Private functions */ static int ProcessPorts(POPConfig *, char *, int, char **); /* * Function: POP_ParseArgs(char *) * * Purpose: Process the preprocessor arguments from the rules file and * initialize the preprocessor's data struct. This function doesn't * have to exist if it makes sense to parse the args in the init * function. * * Arguments: args => argument list * * Returns: void function * */ void POP_ParseArgs(POPConfig *config, char *args) { int ret = 0; char *arg; char *saveptr; char errStr[ERRSTRLEN]; int errStrLen = ERRSTRLEN; if ((config == NULL) || (args == NULL)) return; enablePort( config->ports, POP_DEFAULT_SERVER_PORT ); config->memcap = DEFAULT_POP_MEMCAP; _dpd.fileAPI->set_mime_decode_config_defauts(&(config->decode_conf)); _dpd.fileAPI->set_mime_log_config_defauts(&(config->log_config)); *errStr = '\0'; arg = strtok_r(args, CONF_SEPARATORS, &saveptr); while ( arg != NULL ) { unsigned long value = 0; if ( !strcasecmp(CONF_PORTS, arg) ) { ret = ProcessPorts(config, errStr, errStrLen, &saveptr); } else if ( !strcasecmp(CONF_POP_MEMCAP, arg) ) { ret = _dpd.checkValueInRange(strtok_r(NULL, CONF_SEPARATORS, &saveptr), CONF_POP_MEMCAP, MIN_POP_MEMCAP, MAX_POP_MEMCAP, &value); config->memcap = (uint32_t)value; } else if ( !strcasecmp(CONF_MAX_MIME_MEM, arg) ) { ret = _dpd.checkValueInRange(strtok_r(NULL, CONF_SEPARATORS, &saveptr), CONF_MAX_MIME_MEM, MIN_MIME_MEM, MAX_MIME_MEM, &value); config->decode_conf.max_mime_mem = (int)value; } else if(!_dpd.fileAPI->parse_mime_decode_args(&(config->decode_conf), arg, "POP", &saveptr)) { ret = 0; } else if ( !strcasecmp(CONF_DISABLED, arg) ) { config->disabled = 1; } else { DynamicPreprocessorFatalMessage("%s(%d) => Unknown POP configuration option %s\n", *(_dpd.config_file), *(_dpd.config_line), arg); } if (ret == -1) { /* ** Fatal Error, log error and exit. */ if (*errStr) { DynamicPreprocessorFatalMessage("%s(%d) => %s\n", *(_dpd.config_file), *(_dpd.config_line), errStr); } else { DynamicPreprocessorFatalMessage("%s(%d) => Undefined Error.\n", *(_dpd.config_file), *(_dpd.config_line)); } } /* Get next token */ arg = strtok_r(NULL, CONF_SEPARATORS, &saveptr); } } void POP_CheckConfig(POPConfig *pPolicyConfig, tSfPolicyUserContextId context) { POPConfig *defaultConfig = (POPConfig *)sfPolicyUserDataGetDefault(context); if (pPolicyConfig == defaultConfig) { if (!_dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), &(defaultConfig->decode_conf), "POP")) return; if (!pPolicyConfig->memcap) pPolicyConfig->memcap = DEFAULT_POP_MEMCAP; } else if (defaultConfig == NULL) { _dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), NULL, "POP"); } else { pPolicyConfig->memcap = defaultConfig->memcap; if(pPolicyConfig->disabled) { pPolicyConfig->decode_conf = defaultConfig->decode_conf; return; } _dpd.fileAPI->check_decoding_conf(&(pPolicyConfig->decode_conf), &(defaultConfig->decode_conf), "POP"); } } void POP_PrintConfig(POPConfig *config) { int i; int j = 0; char buf[8192]; if (config == NULL) return; memset(&buf[0], 0, sizeof(buf)); _dpd.logMsg("POP Config:\n"); if(config->disabled) _dpd.logMsg(" POP: INACTIVE\n"); snprintf(buf, sizeof(buf) - 1, " Ports: "); for (i = 0; i < 65536; i++) { if( isPortEnabled( config->ports, i ) ) { j++; _dpd.printfappend(buf, sizeof(buf) - 1, "%d ", i); if(!(j%10)) _dpd.printfappend(buf, sizeof(buf) - 1, "\n "); } } _dpd.logMsg("%s\n", buf); _dpd.logMsg(" POP Memcap: %u\n", config->memcap); _dpd.logMsg(" MIME Max Mem: %d\n", config->decode_conf.max_mime_mem); if(config->decode_conf.b64_depth > -1) { _dpd.logMsg(" Base64 Decoding: %s\n", "Enabled"); switch(config->decode_conf.b64_depth) { case 0: _dpd.logMsg(" Base64 Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Base64 Decoding Depth: %d\n", config->decode_conf.b64_depth); break; } } else _dpd.logMsg(" Base64 Decoding: %s\n", "Disabled"); if(config->decode_conf.qp_depth > -1) { _dpd.logMsg(" Quoted-Printable Decoding: %s\n","Enabled"); switch(config->decode_conf.qp_depth) { case 0: _dpd.logMsg(" Quoted-Printable Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Quoted-Printable Decoding Depth: %d\n", config->decode_conf.qp_depth); break; } } else _dpd.logMsg(" Quoted-Printable Decoding: %s\n", "Disabled"); if(config->decode_conf.uu_depth > -1) { _dpd.logMsg(" Unix-to-Unix Decoding: %s\n","Enabled"); switch(config->decode_conf.uu_depth) { case 0: _dpd.logMsg(" Unix-to-Unix Decoding Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Unix-to-Unix Decoding Depth: %d\n", config->decode_conf.uu_depth); break; } } else _dpd.logMsg(" Unix-to-Unix Decoding: %s\n", "Disabled"); if(config->decode_conf.bitenc_depth > -1) { _dpd.logMsg(" Non-Encoded MIME attachment Extraction: %s\n","Enabled"); switch(config->decode_conf.bitenc_depth) { case 0: _dpd.logMsg(" Non-Encoded MIME attachment Extraction Depth: %s\n", "Unlimited"); break; default: _dpd.logMsg(" Non-Encoded MIME attachment Extraction Depth: %d\n", config->decode_conf.bitenc_depth); break; } } else _dpd.logMsg(" Non-Encoded MIME attachment Extraction: %s\n", "Disabled"); } /* ** NAME ** ProcessPorts:: */ /** ** Process the port list. ** ** This configuration is a list of valid ports and is ended by a ** delimiter. ** ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessPorts(POPConfig *config, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int iPort; int iEndPorts = 0; int num_ports = 0; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "POP config is NULL.\n"); return -1; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { snprintf(ErrorString, ErrStrLen, "Invalid port list format."); return -1; } if(strcmp(CONF_START_LIST, pcToken)) { snprintf(ErrorString, ErrStrLen, "Must start a port list with the '%s' token.", CONF_START_LIST); return -1; } /* Since ports are specified, clear default ports */ disablePort( config->ports, POP_DEFAULT_SERVER_PORT ); while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(CONF_END_LIST, pcToken)) { iEndPorts = 1; break; } iPort = strtol(pcToken, &pcEnd, 10); /* ** Validity check for port */ if(*pcEnd) { snprintf(ErrorString, ErrStrLen, "Invalid port number."); return -1; } if(iPort < 0 || iPort > MAXPORTS-1) { snprintf(ErrorString, ErrStrLen, "Invalid port number. Must be between 0 and 65535."); return -1; } enablePort( config->ports, iPort ); num_ports++; } if(!iEndPorts) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", CONF_PORTS, CONF_END_LIST); return -1; } else if(!num_ports) { snprintf(ErrorString, ErrStrLen, "POP: Empty port list not allowed."); return -1; } return 0; } snort-2.9.20/src/dynamic-preprocessors/pop/pop_buffer_dump.h0000644000175000017500000000335514241076124022345 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file pop_buffer_dump.h ** ** @author Seshaiah Erugu ** ** @brief This file contains structures and functions for ** dumping buffers used during POP inspection. */ #ifndef __POP_BUFFER_DUMP_H__ #define __POP_BUFFER_DUMP_H__ #include "preprocids.h" #ifdef DUMP_BUFFER typedef enum { POP_REQUEST_PAYLOAD_DUMP, POP_REQUEST_COMMAND_DUMP, POP_RESPONSE_PAYLOAD_DUMP, POP_RESPONSE_STATUS_DUMP, POP_STATE_TLS_CLIENT_PEND_DUMP, POP_STATE_TLS_DATA_DUMP, POP_STATE_TLS_SERVER_PEND_DUMP } POP_BUFFER_DUMP; void dumpBuffer(POP_BUFFER_DUMP type, const uint8_t *content, uint16_t len); void dumpBufferInit(void); TraceBuffer *getPOPBuffers(); #endif #endif snort-2.9.20/src/dynamic-preprocessors/pop/pop_paf.h0000644000175000017500000000261714241076136020620 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __POP_PAF_H__ #define __POP_PAF_H__ #include "sfPolicy.h" #include "sfPolicyUserData.h" //void pop_paf_free(void); #ifdef TARGET_BASED void register_pop_paf_service(struct _SnortConfig *sc, int16_t app, tSfPolicyId policy); #endif void register_pop_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy); bool is_data_end (void* ssn); #endif snort-2.9.20/src/dynamic-preprocessors/pop/spp_pop.c0000644000175000017500000007130214241076156020646 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * spp_pop.c * * Author: Bhagyashree Bantwal * * Description: * * This file initializes POP as a Snort preprocessor. * * This file registers the POP initialization function, * adds the POP function into the preprocessor list. * * In general, this file is a wrapper to POP functionality, * by interfacing with the Snort preprocessor functions. The rest * of POP should be separate from the preprocessor hooks. * **************************************************************************/ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "spp_pop.h" #include "sf_preproc_info.h" #include "snort_pop.h" #include "pop_config.h" #include "pop_log.h" #include "pop_paf.h" #include "pop_util.h" #include "preprocids.h" #include "sf_snort_packet.h" #include "sf_dynamic_preprocessor.h" #include "snort_debug.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "profiler.h" #ifdef PERF_PROFILING PreprocStats popPerfStats; PreprocStats popDetectPerfStats; int popDetectCalled = 0; #endif #include "sf_types.h" #include "mempool.h" #include "snort_bounds.h" #include "file_api.h" #ifdef REG_TEST #include "reg_test.h" #endif #ifdef DUMP_BUFFER #include "pop_buffer_dump.h" #endif const int MAJOR_VERSION = 1; const int MINOR_VERSION = 0; const int BUILD_VERSION = 1; const char *PREPROC_NAME = "SF_POP"; const char *PROTOCOL_NAME = "POP"; #define SetupPOP DYNAMIC_PREPROC_SETUP MemPool *pop_mime_mempool = NULL; MemPool *pop_mempool = NULL; POP_Stats pop_stats; tSfPolicyUserContextId pop_config = NULL; POPConfig *pop_eval_config = NULL; extern POP pop_no_session; extern int16_t pop_proto_id; static void POPInit(struct _SnortConfig *, char *); static void POPDetect(void *, void *context); static void POPCleanExitFunction(int, void *); static void POPResetFunction(int, void *); static void POPResetStatsFunction(int, void *); static void registerPortsForDispatch( struct _SnortConfig *sc, POPConfig *policy ); static void registerPortsForReassembly( POPConfig *policy, int direction ); static void _addPortsToStreamFilter(struct _SnortConfig *, POPConfig *, tSfPolicyId); static void POP_PrintStats(int); #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *, tSfPolicyId); #endif static int POPCheckConfig(struct _SnortConfig *); #ifdef SNORT_RELOAD static int POPMempoolFreeUsedBucket(MemPool *memory_pool); static unsigned POPReloadMimeMempoolAdjust(unsigned popMaxWork); static unsigned POPReloadLogMempoolAdjust(unsigned popMaxWork); static bool POPMimeReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData); static bool POPLogReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData); static void POPReload(struct _SnortConfig *, char *, void **); static int POPReloadVerify(struct _SnortConfig *, void *); static void * POPReloadSwap(struct _SnortConfig *, void *); static void POPReloadSwapFree(void *); #endif /* * Function: SetupPOP() * * Purpose: Registers the preprocessor keyword and initialization * function into the preprocessor list. This is the function that * gets called from InitPreprocessors() in plugbase.c. * * Arguments: None. * * Returns: void function * */ void SetupPOP(void) { /* link the preprocessor keyword to the init function in the preproc list */ #ifndef SNORT_RELOAD _dpd.registerPreproc("pop", POPInit); #else _dpd.registerPreproc("pop", POPInit, POPReload, POPReloadVerify, POPReloadSwap, POPReloadSwapFree); #endif #ifdef DUMP_BUFFER _dpd.registerBufferTracer(getPOPBuffers, POP_BUFFER_DUMP_FUNC); #endif } #ifdef REG_TEST static inline void PrintPOPSize(void) { _dpd.logMsg("\nPOP Session Size: %lu\n", (long unsigned int)sizeof(POP)); } #endif /* * Function: POPInit(char *) * * Purpose: Calls the argument parsing function, performs final setup on data * structs, links the preproc function into the function list. * * Arguments: args => ptr to argument string * * Returns: void function * */ static void POPInit(struct _SnortConfig *sc, char *args) { POPToken *tmp; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); POPConfig * pPolicyConfig = NULL; #ifdef REG_TEST PrintPOPSize(); #endif _dpd.registerMemoryStatsFunc(PP_POP, POP_Print_Mem_Stats); if (pop_config == NULL) { //create a context pop_config = sfPolicyConfigCreate(); if (pop_config == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create POP " "configuration.\n"); } /* Initialize the searches not dependent on configuration. * headers, reponsed, data, mime boundary regular expression */ POP_SearchInit(); /* zero out static POP global used for stateless POP or if there * is no session pointer */ memset(&pop_no_session, 0, sizeof(POP)); /* Put the preprocessor function into the function list */ /* _dpd.addPreproc(POPDetect, PRIORITY_APPLICATION, PP_POP, PROTO_BIT__TCP);*/ _dpd.addPreprocExit(POPCleanExitFunction, NULL, PRIORITY_LAST, PP_POP); _dpd.addPreprocReset(POPResetFunction, NULL, PRIORITY_LAST, PP_POP); _dpd.registerPreprocStats(POP_PROTO_REF_STR, POP_PrintStats); _dpd.addPreprocResetStats(POPResetStatsFunction, NULL, PRIORITY_LAST, PP_POP); _dpd.addPreprocConfCheck(sc, POPCheckConfig); #ifdef TARGET_BASED pop_proto_id = _dpd.findProtocolReference(POP_PROTO_REF_STR); if (pop_proto_id == SFTARGET_UNKNOWN_PROTOCOL) pop_proto_id = _dpd.addProtocolReference(POP_PROTO_REF_STR); // register with session to handle applications _dpd.sessionAPI->register_service_handler( PP_POP, pop_proto_id ); DEBUG_WRAP(DebugMessage(DEBUG_POP,"POP: Target-based: Proto id for %s: %u.\n", POP_PROTO_REF_STR, pop_proto_id);); #endif #ifdef PERF_PROFILING _dpd.addPreprocProfileFunc("pop", (void*)&popPerfStats, 0, _dpd.totalPerfStats, NULL); #endif } sfPolicyUserPolicySet (pop_config, policy_id); pPolicyConfig = (POPConfig *)sfPolicyUserDataGetCurrent(pop_config); if (pPolicyConfig != NULL) { DynamicPreprocessorFatalMessage("Can only configure POP preprocessor once.\n"); } pPolicyConfig = (POPConfig *)_dpd.snortAlloc(1, sizeof(POPConfig), PP_POP, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create POP " "configuration.\n"); } sfPolicyUserDataSetCurrent(pop_config, pPolicyConfig); POP_InitCmds(pPolicyConfig); POP_ParseArgs(pPolicyConfig, args); POP_CheckConfig(pPolicyConfig, pop_config); POP_PrintConfig(pPolicyConfig); if(pPolicyConfig->disabled) return; _dpd.addPreproc(sc, POPDetect, PRIORITY_APPLICATION, PP_POP, PROTO_BIT__TCP); if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("Streaming & reassembly must be enabled " "for POP preprocessor\n"); } /* Command search - do this here because it's based on configuration */ pPolicyConfig->cmd_search_mpse = _dpd.searchAPI->search_instance_new(); if (pPolicyConfig->cmd_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate POP " "command search.\n"); } for (tmp = pPolicyConfig->cmds; tmp->name != NULL; tmp++) { pPolicyConfig->cmd_search[tmp->search_id].name = tmp->name; pPolicyConfig->cmd_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(pPolicyConfig->cmd_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(pPolicyConfig->cmd_search_mpse); // register ports with session and stream registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStreamFilter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } /* * Function: POPDetect(void *, void *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ static void POPDetect(void *pkt, void *context) { SFSnortPacket *p = (SFSnortPacket *)pkt; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); PROFILE_VARS; // preconditions - what we registered for assert(IsTCP(p) && p->payload && p->payload_size); PREPROC_PROFILE_START(popPerfStats); DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP Start (((((((((((((((((((((((((((((((((((((((\n");); sfPolicyUserPolicySet (pop_config, policy_id); SnortPOP(p); DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP End )))))))))))))))))))))))))))))))))))))))))\n\n");); PREPROC_PROFILE_END(popPerfStats); #ifdef PERF_PROFILING if (PROFILING_PREPROCS && popDetectCalled) { popPerfStats.ticks -= popDetectPerfStats.ticks; /* And Reset ticks to 0 */ popDetectPerfStats.ticks = 0; popDetectCalled = 0; } #endif } /* * Function: POPCleanExitFunction(int, void *) * * Purpose: This function gets called when Snort is exiting, if there's * any cleanup that needs to be performed (e.g. closing files) * it should be done here. * * Arguments: signal => the code of the signal that was issued to Snort * data => any arguments or data structs linked to this * function when it was registered, may be * needed to properly exit * * Returns: void function */ static void POPCleanExitFunction(int signal, void *data) { POP_Free(); if (mempool_destroy(pop_mime_mempool) == 0) { free(pop_mime_mempool); pop_mime_mempool = NULL; } if (mempool_destroy(pop_mempool) == 0) { free(pop_mempool); pop_mempool = NULL; } } static void POPResetFunction(int signal, void *data) { return; } static void POPResetStatsFunction(int signal, void *data) { return; } static void registerPortsForDispatch( struct _SnortConfig *sc, POPConfig *policy ) { int port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.sessionAPI->enable_preproc_for_port( sc, PP_POP, PROTO_BIT__TCP, port ); } } static void registerPortsForReassembly( POPConfig *policy, int direction ) { uint32_t port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) _dpd.streamAPI->register_reassembly_port( NULL, port, direction ); } } static void _addPortsToStreamFilter(struct _SnortConfig *sc, POPConfig *config, tSfPolicyId policy_id) { unsigned int portNum; if (config == NULL) return; for (portNum = 0; portNum < MAXPORTS; portNum++) { if(config->ports[(portNum/8)] & (1<<(portNum%8))) { //Add port the port _dpd.streamAPI->set_port_filter_status(sc, IPPROTO_TCP, (uint16_t)portNum, PORT_MONITOR_SESSION, policy_id, 1); register_pop_paf_port(sc, portNum, policy_id); } } } #ifdef TARGET_BASED static void _addServicesToStreamFilter(struct _SnortConfig *sc, tSfPolicyId policy_id) { _dpd.streamAPI->set_service_filter_status(sc, pop_proto_id, PORT_MONITOR_SESSION, policy_id, 1); register_pop_paf_service(sc, pop_proto_id, policy_id); } #endif static int CheckFilePolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { POPConfig *context = (POPConfig *)pData; /* Use new Snort config to get the max file depth */ context->decode_conf.file_depth = _dpd.fileAPI->get_max_file_depth(sc, true); if (context->decode_conf.file_depth > -1) context->log_config.log_filename = 1; updateMaxDepth(context->decode_conf.file_depth, &context->decode_conf.max_depth); return 0; } static int POPEnableDecoding(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void *pData) { POPConfig *context = (POPConfig *)pData; if (pData == NULL) return 0; if(context->disabled) return 0; if(_dpd.fileAPI->is_decoding_enabled(&(context->decode_conf))) return 1; return 0; } static int POPLogExtraData(struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void *pData) { POPConfig *context = (POPConfig *)pData; if (pData == NULL) return 0; if(context->disabled) return 0; if(context->log_config.log_filename) return 1; return 0; } static int POPCheckPolicyConfig( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { POPConfig *context = (POPConfig *)pData; _dpd.setParserPolicy(sc, policyId); /* In a multiple-policy setting, the POP preproc can be turned on in a "disabled" state. In this case, we don't require Stream. */ if (context->disabled) return 0; if (_dpd.streamAPI == NULL) { _dpd.errMsg("Streaming & reassembly must be enabled for POP preprocessor\n"); return -1; } return 0; } static int POPCheckConfig(struct _SnortConfig *sc) { int rval; POPConfig *defaultConfig = (POPConfig *)sfPolicyUserDataGetDefault(pop_config); if ((rval = sfPolicyUserDataIterate (sc, pop_config, POPCheckPolicyConfig))) return rval; if ((rval = sfPolicyUserDataIterate (sc, pop_config, CheckFilePolicyConfig))) return rval; if (sfPolicyUserDataIterate(sc, pop_config, POPEnableDecoding) != 0) { if (defaultConfig == NULL) { /*error message */ _dpd.errMsg("POP: Must configure a default " "configuration if you want to pop decoding.\n"); return -1; } pop_mime_mempool = (MemPool *)_dpd.fileAPI->init_mime_mempool(defaultConfig->decode_conf.max_mime_mem, defaultConfig->decode_conf.max_depth, pop_mime_mempool, PROTOCOL_NAME); } if (sfPolicyUserDataIterate(sc, pop_config, POPLogExtraData) != 0) { pop_mempool = (MemPool *)_dpd.fileAPI->init_log_mempool(0, defaultConfig->memcap, pop_mempool, PROTOCOL_NAME); } return 0; } static void POP_PrintStats(int exiting) { _dpd.logMsg("POP Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions : " STDu64 "\n", pop_stats.sessions); _dpd.logMsg(" Max concurrent sessions : " STDu64 "\n", pop_stats.max_conc_sessions); if (pop_stats.sessions > 0) { _dpd.logMsg(" Base64 attachments decoded : " STDu64 "\n", pop_stats.mime_stats.attachments[DECODE_B64]); _dpd.logMsg(" Total Base64 decoded bytes : " STDu64 "\n", pop_stats.mime_stats.decoded_bytes[DECODE_B64]); _dpd.logMsg(" Quoted-Printable attachments decoded : " STDu64 "\n", pop_stats.mime_stats.attachments[DECODE_QP]); _dpd.logMsg(" Total Quoted decoded bytes : " STDu64 "\n", pop_stats.mime_stats.decoded_bytes[DECODE_QP]); _dpd.logMsg(" UU attachments decoded : " STDu64 "\n", pop_stats.mime_stats.attachments[DECODE_UU]); _dpd.logMsg(" Total UU decoded bytes : " STDu64 "\n", pop_stats.mime_stats.decoded_bytes[DECODE_UU]); _dpd.logMsg(" Non-Encoded MIME attachments extracted : " STDu64 "\n", pop_stats.mime_stats.attachments[DECODE_BITENC]); _dpd.logMsg(" Total Non-Encoded MIME bytes extracted : " STDu64 "\n", pop_stats.mime_stats.decoded_bytes[DECODE_BITENC]); if ( pop_stats.mime_stats.memcap_exceeded ) _dpd.logMsg(" Sessions not decoded due to memory unavailability : " STDu64 "\n", pop_stats.mime_stats.memcap_exceeded); if ( pop_stats.log_memcap_exceeded ) _dpd.logMsg(" POP Sessions fastpathed due to memcap exceeded: " STDu64 "\n", pop_stats.log_memcap_exceeded); } } #ifdef SNORT_RELOAD static void POPReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId pop_swap_config = (tSfPolicyUserContextId)*new_config; POPToken *tmp; tSfPolicyId policy_id = _dpd.getParserPolicy(sc); POPConfig *pPolicyConfig = NULL; if (pop_swap_config == NULL) { //create a context pop_swap_config = sfPolicyConfigCreate(); if (pop_swap_config == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create POP " "configuration.\n"); } *new_config = (void *)pop_swap_config; } sfPolicyUserPolicySet (pop_swap_config, policy_id); pPolicyConfig = (POPConfig *)sfPolicyUserDataGetCurrent(pop_swap_config); if (pPolicyConfig != NULL) DynamicPreprocessorFatalMessage("Can only configure POP preprocessor once.\n"); pPolicyConfig = (POPConfig *)_dpd.snortAlloc(1, sizeof(POPConfig), PP_POP, PP_MEM_CATEGORY_CONFIG); if (pPolicyConfig == NULL) { DynamicPreprocessorFatalMessage("Not enough memory to create POP " "configuration.\n"); } sfPolicyUserDataSetCurrent(pop_swap_config, pPolicyConfig); POP_InitCmds(pPolicyConfig); POP_ParseArgs(pPolicyConfig, args); POP_CheckConfig(pPolicyConfig, pop_swap_config); POP_PrintConfig(pPolicyConfig); if( pPolicyConfig->disabled ) return; if (_dpd.streamAPI == NULL) { DynamicPreprocessorFatalMessage("Streaming & reassembly must be enabled " "for POP preprocessor\n"); } /* Command search - do this here because it's based on configuration */ pPolicyConfig->cmd_search_mpse = _dpd.searchAPI->search_instance_new(); if (pPolicyConfig->cmd_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate POP " "command search.\n"); } for (tmp = pPolicyConfig->cmds; tmp->name != NULL; tmp++) { pPolicyConfig->cmd_search[tmp->search_id].name = tmp->name; pPolicyConfig->cmd_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(pPolicyConfig->cmd_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(pPolicyConfig->cmd_search_mpse); _dpd.addPreproc(sc, POPDetect, PRIORITY_APPLICATION, PP_POP, PROTO_BIT__TCP); // register ports with session and stream registerPortsForDispatch( sc, pPolicyConfig ); registerPortsForReassembly( pPolicyConfig, SSN_DIR_FROM_SERVER | SSN_DIR_FROM_CLIENT ); _addPortsToStreamFilter(sc, pPolicyConfig, policy_id); #ifdef TARGET_BASED _addServicesToStreamFilter(sc, policy_id); #endif } static int POPMempoolFreeUsedBucket(MemPool *memory_pool) { MemBucket *lru_bucket = NULL; lru_bucket = mempool_get_lru_bucket(memory_pool); if(lru_bucket) { /* Deleting least recently used POP session data here to adjust to new max_memory */ _dpd.sessionAPI->set_application_data(lru_bucket->scbPtr, PP_POP, NULL, NULL); return 1; } return 0; } static unsigned POPReloadMimeMempoolAdjust(unsigned popMaxWork) { int retVal; /* deleting MemBucket from free list in POP Mime Mempool */ popMaxWork = mempool_prune_freelist(pop_mime_mempool, pop_mime_mempool->max_memory, popMaxWork); if(!popMaxWork) return 0; for( ; popMaxWork && ((pop_mime_mempool->used_memory + pop_mime_mempool->free_memory) > pop_mime_mempool->max_memory); popMaxWork--) { /* deleting least recently used MemBucket from Used list in POP Mime Mempool */ retVal = POPMempoolFreeUsedBucket(pop_mime_mempool); if(!retVal) break; } return popMaxWork; } static unsigned POPReloadLogMempoolAdjust(unsigned popMaxWork) { int retVal; /* deleting MemBucket from free list in POP Log Mempool */ popMaxWork = mempool_prune_freelist(pop_mempool, pop_mempool->max_memory, popMaxWork); if(!popMaxWork) return 0; for( ; popMaxWork && ((pop_mempool->used_memory + pop_mempool->free_memory) > pop_mempool->max_memory); popMaxWork--) { /* deleting least recently used MemBucket from Used list in POP Log Mempool */ retVal = POPMempoolFreeUsedBucket(pop_mempool); if(!retVal) break; } return popMaxWork; } static bool POPMimeReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned initialMaxWork = idle ? 512 : 5; unsigned maxWork; /* If new max_mime_mem is less than old configured max_mime_mem, need to adjust POP Mime Mempool. * In order to adjust to new max_memory of mime mempool, delete buckets from free list. * After deleting buckets from free list, still new max_memory is less than old value , delete buckets * (least recently used i.e head node of used list )from used list till total memory reaches to new max_memory. */ maxWork = POPReloadMimeMempoolAdjust(initialMaxWork); if(maxWork == initialMaxWork) { pop_stats.max_conc_sessions = pop_stats.conc_sessions; pop_stats.mime_stats.memcap_exceeded = 0; return true; } else return false; } static bool POPLogReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned initialMaxWork = idle ? 512 : 5; unsigned maxWork; /* If new memcap is less than old configured memcap, need to adjust POP Log Mempool. * In order to adjust to new max_memory of log mempool, delete buckets from free list. * After deleting buckets from free list, still new max_memory is less than old value , delete buckets * (least recently used i.e head node of used list )from used list till total memory reaches to new max_memory. */ maxWork = POPReloadLogMempoolAdjust(initialMaxWork); if(maxWork == initialMaxWork) { pop_stats.max_conc_sessions = pop_stats.conc_sessions; pop_stats.mime_stats.memcap_exceeded = 0; return true; } else return false; } static int POPReloadVerify(struct _SnortConfig *sc, void *swap_config) { int rval; tSfPolicyUserContextId pop_swap_config = (tSfPolicyUserContextId)swap_config; POPConfig *config = NULL; POPConfig *configNext = NULL; tSfPolicyId policy_id = 0; if (pop_swap_config == NULL) return 0; if (pop_config != NULL) config = (POPConfig *)sfPolicyUserDataGet(pop_config, _dpd.getDefaultPolicy()); configNext = (POPConfig *)sfPolicyUserDataGet(pop_swap_config, _dpd.getDefaultPolicy()); if (config == NULL) return 0; if ((rval = sfPolicyUserDataIterate (sc, pop_swap_config, POPCheckPolicyConfig))) return rval; if ((rval = sfPolicyUserDataIterate (sc, pop_swap_config, CheckFilePolicyConfig))) return rval; policy_id = _dpd.getParserPolicy(sc); if (pop_mime_mempool != NULL) { /* If max_mime_mem changes, mime mempool need to be adjusted bcz mempool max_memory will be changed. * Registering here to adjust Mime memory Pool when max_mime_mem changes. */ if( configNext->decode_conf.max_mime_mem < config->decode_conf.max_mime_mem ) _dpd.reloadAdjustRegister(sc, "POP-MIME-MEMPOOL", policy_id, &POPMimeReloadAdjust, NULL, NULL); } if (pop_mempool != NULL) { if(configNext) { /* If memcap cahnges, log mempool need to be adjusted bcz mempool max_mempory will be changed. * Registering here to adjust Log memory Pool when memcap changes. */ if (configNext->memcap < config->memcap) _dpd.reloadAdjustRegister(sc, "POP-LOG-MEMPOOL", policy_id, &POPLogReloadAdjust, NULL, NULL); } } else if(configNext != NULL) { if (sfPolicyUserDataIterate(sc, pop_swap_config, POPEnableDecoding) != 0) { pop_mime_mempool = (MemPool *)_dpd.fileAPI->init_mime_mempool(configNext->decode_conf.max_mime_mem, configNext->decode_conf.max_depth, pop_mime_mempool, PREPROC_NAME); } if (sfPolicyUserDataIterate(sc, pop_swap_config, POPLogExtraData) != 0) { pop_mempool = (MemPool *)_dpd.fileAPI->init_log_mempool(0, configNext->memcap, pop_mempool, PREPROC_NAME); } if ( configNext->disabled ) return 0; } return 0; } static int POPReloadSwapPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { POPConfig *pPolicyConfig = (POPConfig *)pData; if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); POP_FreeConfig(pPolicyConfig); } return 0; } static void * POPReloadSwap(struct _SnortConfig *sc, void *swap_config) { POPConfig *configNew = NULL, *configOld = NULL; tSfPolicyUserContextId pop_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = pop_config; if (pop_swap_config == NULL) return NULL; pop_config = pop_swap_config; configOld = (POPConfig *)sfPolicyUserDataGet(old_config, _dpd.getDefaultPolicy()); configNew = (POPConfig *)sfPolicyUserDataGet(pop_config, _dpd.getDefaultPolicy()); if(configNew && configOld) { if(pop_mime_mempool) { if((configOld->decode_conf.max_mime_mem != configNew->decode_conf.max_mime_mem) || (configOld->decode_conf.max_depth != configNew->decode_conf.max_depth) ) { #ifdef REG_TEST _dpd.fileAPI->displayMimeMempool(pop_mime_mempool,&(configOld->decode_conf), &(configNew->decode_conf)); #endif /* Update the pop_mime_mempool with new max_memmory and object size when max_mime_mem changes. */ _dpd.fileAPI->update_mime_mempool(pop_mime_mempool, configNew->decode_conf.max_mime_mem, configNew->decode_conf.max_depth); } } if(pop_mempool) { if(configOld->memcap != configNew->memcap ) { #ifdef REG_TEST _dpd.fileAPI->displayLogMempool(pop_mempool, configOld->memcap, configNew->memcap); #endif /* Update the pop_mempool with new max_memory and objest size when memcap changes. */ _dpd.fileAPI->update_log_mempool(pop_mempool, configNew->memcap, 0); pop_stats.log_memcap_exceeded = 0; } } #ifdef REG_TEST _dpd.fileAPI->displayDecodeDepth(&(configOld->decode_conf), &(configNew->decode_conf)); #endif } sfPolicyUserDataFreeIterate (old_config, POPReloadSwapPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) return old_config; return NULL; } static void POPReloadSwapFree(void *data) { if (data == NULL) return; POP_FreeConfigs((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/dynamic-preprocessors/pop/Makefile.in0000644000175000017500000006254514242725546021105 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ @BUILD_BUFFER_DUMP_TRUE@am__append_1 = \ @BUILD_BUFFER_DUMP_TRUE@pop_buffer_dump.c \ @BUILD_BUFFER_DUMP_TRUE@pop_buffer_dump.h subdir = src/dynamic-preprocessors/pop ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ *) f=$$p;; \ esac; am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; am__install_max = 40 am__nobase_strip_setup = \ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` am__nobase_strip = \ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" am__nobase_list = $(am__nobase_strip_setup); \ for p in $$list; do echo "$$p $$p"; done | \ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ if (++n[$$2] == $(am__install_max)) \ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ END { for (dir in files) print dir, files[dir] }' am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' am__uninstall_files_from_dir = { \ test -z "$$files" \ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ $(am__cd) "$$dir" && rm -f $$files; }; \ } am__installdirs = "$(DESTDIR)$(dynamicpreprocessordir)" LTLIBRARIES = $(dynamicpreprocessor_LTLIBRARIES) @SO_WITH_STATIC_LIB_TRUE@libsf_pop_preproc_la_DEPENDENCIES = \ @SO_WITH_STATIC_LIB_TRUE@ ../libsf_dynamic_preproc.la am__libsf_pop_preproc_la_SOURCES_DIST = pop_config.c pop_config.h \ pop_log.c pop_log.h pop_paf.c pop_paf.h pop_util.c pop_util.h \ snort_pop.c snort_pop.h spp_pop.c spp_pop.h pop_buffer_dump.c \ pop_buffer_dump.h @BUILD_BUFFER_DUMP_TRUE@am__objects_1 = pop_buffer_dump.lo am_libsf_pop_preproc_la_OBJECTS = pop_config.lo pop_log.lo pop_paf.lo \ pop_util.lo snort_pop.lo spp_pop.lo $(am__objects_1) @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_pop_preproc_la_OBJECTS = \ @SO_WITH_STATIC_LIB_FALSE@ sf_dynamic_preproc_lib.lo mempool.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_sdlist.lo util_unfold.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_base64decode.lo \ @SO_WITH_STATIC_LIB_FALSE@ sf_email_attach_decode.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfPolicyUserData.lo ssl.lo \ @SO_WITH_STATIC_LIB_FALSE@ ssl_config.lo ssl_inspect.lo \ @SO_WITH_STATIC_LIB_FALSE@ sfparser.lo libsf_pop_preproc_la_OBJECTS = $(am_libsf_pop_preproc_la_OBJECTS) \ $(nodist_libsf_pop_preproc_la_OBJECTS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = libsf_pop_preproc_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ $(AM_CFLAGS) $(CFLAGS) $(libsf_pop_preproc_la_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsf_pop_preproc_la_SOURCES) \ $(nodist_libsf_pop_preproc_la_SOURCES) DIST_SOURCES = $(am__libsf_pop_preproc_la_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_pop_preproc.la libsf_pop_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ @SO_WITH_STATIC_LIB_TRUE@libsf_pop_preproc_la_LIBADD = ../libsf_dynamic_preproc.la @SO_WITH_STATIC_LIB_FALSE@nodist_libsf_pop_preproc_la_SOURCES = \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_dynamic_preproc_lib.c \ @SO_WITH_STATIC_LIB_FALSE@../include/mempool.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_sdlist.c \ @SO_WITH_STATIC_LIB_FALSE@../include/util_unfold.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_base64decode.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sf_email_attach_decode.c \ @SO_WITH_STATIC_LIB_FALSE@../include/sfPolicyUserData.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_config.c \ @SO_WITH_STATIC_LIB_FALSE@../ssl_common/ssl_inspect.c \ @SO_WITH_STATIC_LIB_FALSE@../libs/sfparser.c libsf_pop_preproc_la_SOURCES = pop_config.c pop_config.h pop_log.c \ pop_log.h pop_paf.c pop_paf.h pop_util.c pop_util.h \ snort_pop.c snort_pop.h spp_pop.c spp_pop.h $(am__append_1) EXTRA_DIST = \ sf_pop.vcxproj \ sf_pop.dsp all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/dynamic-preprocessors/pop/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/dynamic-preprocessors/pop/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-dynamicpreprocessorLTLIBRARIES: $(dynamicpreprocessor_LTLIBRARIES) @$(NORMAL_INSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ list2=; for p in $$list; do \ if test -f $$p; then \ list2="$$list2 $$p"; \ else :; fi; \ done; \ test -z "$$list2" || { \ echo " $(MKDIR_P) '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(MKDIR_P) "$(DESTDIR)$(dynamicpreprocessordir)" || exit 1; \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(dynamicpreprocessordir)'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(dynamicpreprocessordir)"; \ } uninstall-dynamicpreprocessorLTLIBRARIES: @$(NORMAL_UNINSTALL) @list='$(dynamicpreprocessor_LTLIBRARIES)'; test -n "$(dynamicpreprocessordir)" || list=; \ for p in $$list; do \ $(am__strip_dir) \ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(dynamicpreprocessordir)/$$f'"; \ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(dynamicpreprocessordir)/$$f"; \ done clean-dynamicpreprocessorLTLIBRARIES: -test -z "$(dynamicpreprocessor_LTLIBRARIES)" || rm -f $(dynamicpreprocessor_LTLIBRARIES) @list='$(dynamicpreprocessor_LTLIBRARIES)'; \ locs=`for p in $$list; do echo $$p; done | \ sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ sort -u`; \ test -z "$$locs" || { \ echo rm -f $${locs}; \ rm -f $${locs}; \ } libsf_pop_preproc.la: $(libsf_pop_preproc_la_OBJECTS) $(libsf_pop_preproc_la_DEPENDENCIES) $(EXTRA_libsf_pop_preproc_la_DEPENDENCIES) $(AM_V_CCLD)$(libsf_pop_preproc_la_LINK) -rpath $(dynamicpreprocessordir) $(libsf_pop_preproc_la_OBJECTS) $(libsf_pop_preproc_la_LIBADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_dynamic_preproc_lib.lo: ../include/sf_dynamic_preproc_lib.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_dynamic_preproc_lib.lo `test -f '../include/sf_dynamic_preproc_lib.c' || echo '$(srcdir)/'`../include/sf_dynamic_preproc_lib.c mempool.lo: ../include/mempool.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mempool.lo `test -f '../include/mempool.c' || echo '$(srcdir)/'`../include/mempool.c sf_sdlist.lo: ../include/sf_sdlist.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_sdlist.lo `test -f '../include/sf_sdlist.c' || echo '$(srcdir)/'`../include/sf_sdlist.c util_unfold.lo: ../include/util_unfold.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o util_unfold.lo `test -f '../include/util_unfold.c' || echo '$(srcdir)/'`../include/util_unfold.c sf_base64decode.lo: ../include/sf_base64decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_base64decode.lo `test -f '../include/sf_base64decode.c' || echo '$(srcdir)/'`../include/sf_base64decode.c sf_email_attach_decode.lo: ../include/sf_email_attach_decode.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_email_attach_decode.lo `test -f '../include/sf_email_attach_decode.c' || echo '$(srcdir)/'`../include/sf_email_attach_decode.c sfPolicyUserData.lo: ../include/sfPolicyUserData.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfPolicyUserData.lo `test -f '../include/sfPolicyUserData.c' || echo '$(srcdir)/'`../include/sfPolicyUserData.c ssl.lo: ../ssl_common/ssl.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl.lo `test -f '../ssl_common/ssl.c' || echo '$(srcdir)/'`../ssl_common/ssl.c ssl_config.lo: ../ssl_common/ssl_config.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_config.lo `test -f '../ssl_common/ssl_config.c' || echo '$(srcdir)/'`../ssl_common/ssl_config.c ssl_inspect.lo: ../ssl_common/ssl_inspect.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o ssl_inspect.lo `test -f '../ssl_common/ssl_inspect.c' || echo '$(srcdir)/'`../ssl_common/ssl_inspect.c sfparser.lo: ../libs/sfparser.c $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sfparser.lo `test -f '../libs/sfparser.c' || echo '$(srcdir)/'`../libs/sfparser.c mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LTLIBRARIES) all-local installdirs: for dir in "$(DESTDIR)$(dynamicpreprocessordir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dynamicpreprocessorLTLIBRARIES install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: uninstall-dynamicpreprocessorLTLIBRARIES .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \ clean-dynamicpreprocessorLTLIBRARIES clean-generic \ clean-libtool cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-dynamicpreprocessorLTLIBRARIES \ install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ install-pdf-am install-ps install-ps-am install-strip \ installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am \ uninstall-dynamicpreprocessorLTLIBRARIES .PRECIOUS: Makefile all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/dynamic-preprocessors/pop/pop_buffer_dump.c0000644000175000017500000000433714241076122022337 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file pop_buffer_dump.c ** ** @author Seshaiah Erugu ** ** @brief This file contains structures and functions for ** dumping buffers used during HTTP inspection. */ #include "pop_buffer_dump.h" #ifdef DUMP_BUFFER TraceBuffer buf[MAX_POP_BUFFER_DUMP] = {{"POP_REQUEST_PAYLOAD_DUMP", "", 0}, {"POP_REQUEST_COMMAND_DUMP", "", 0}, {"POP_RESPONSE_PAYLOAD_DUMP", "", 0}, {"POP_RESPONSE_STATUS_DUMP", "", 0}, {"POP_STATE_TLS_CLIENT_PEND_DUMP", "", 0}, {"POP_STATE_TLS_DATA_DUMP", "", 0}, {"POP_STATE_TLS_SERVER_PEND_DUMP", "", 0} }; void dumpBuffer(POP_BUFFER_DUMP type, const uint8_t *content, uint16_t len){ buf[type].buf_content = (char *) content; buf[type].length = len; } void dumpBufferInit(void) { unsigned int i; for (i = 0; i < MAX_POP_BUFFER_DUMP; i++) { buf[i].buf_content = (char *)""; buf[i].length = 0; } } TraceBuffer *getPOPBuffers() { return buf; } #endif snort-2.9.20/src/dynamic-preprocessors/pop/snort_pop.c0000644000175000017500000010446514241076142021213 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * snort_pop.c * * Author: Bhagyashree Bantwal * * Description: * * This file handles POP protocol checking and normalization. * * Entry point functions: * * SnortPOP() * POP_Init() * POP_Free() * **************************************************************************/ /* Includes ***************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "sf_types.h" #include "snort_pop.h" #include "pop_config.h" #include "pop_util.h" #include "pop_log.h" #include "sf_snort_packet.h" #include "stream_api.h" #include "snort_debug.h" #include "profiler.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" #include "ssl.h" #include "ssl_include.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" #include "file_api.h" #ifdef DEBUG_MSGS #include "sf_types.h" #endif #include "pop_paf.h" #ifdef DUMP_BUFFER #include "pop_buffer_dump.h" #endif /**************************************************************************/ /* Externs ****************************************************************/ #ifdef PERF_PROFILING extern PreprocStats popDetectPerfStats; extern int popDetectCalled; #endif extern tSfPolicyUserContextId pop_config; extern POPConfig *pop_eval_config; extern MemPool *pop_mime_mempool; extern MemPool *pop_mempool; #ifdef DEBUG_MSGS extern char pop_print_buffer[]; #endif /**************************************************************************/ /* Globals ****************************************************************/ const POPToken pop_known_cmds[] = { {"APOP", 4, CMD_APOP}, {"AUTH", 4, CMD_AUTH}, {"CAPA", 4, CMD_CAPA}, {"DELE", 4, CMD_DELE}, {"LIST", 4, CMD_LIST}, {"NOOP", 4, CMD_NOOP}, {"PASS", 4, CMD_PASS}, {"QUIT", 4, CMD_QUIT}, {"RETR", 4, CMD_RETR}, {"RSET", 4, CMD_RSET}, {"STAT", 4, CMD_STAT}, {"STLS", 4, CMD_STLS}, {"TOP", 3, CMD_TOP}, {"UIDL", 4, CMD_UIDL}, {"USER", 4, CMD_USER}, {NULL, 0, 0} }; const POPToken pop_resps[] = { {"+OK", 3, RESP_OK}, /* SUCCESS */ {"-ERR", 4, RESP_ERR}, /* FAILURE */ {NULL, 0, 0} }; POP *pop_ssn = NULL; POP pop_no_session; POPSearchInfo pop_search_info; #ifdef DEBUG_MSGS uint64_t pop_session_counter = 0; #endif #ifdef TARGET_BASED int16_t pop_proto_id; #endif void *pop_resp_search_mpse = NULL; POPSearch pop_resp_search[RESP_LAST]; POPSearch *pop_current_search = NULL; /**************************************************************************/ /* Private functions ******************************************************/ static int POP_Setup(SFSnortPacket *p, POP *ssn); static void POP_ResetState(void); static void POP_SessionFree(void *); static int POP_GetPacketDirection(SFSnortPacket *, int); static void POP_ProcessClientPacket(SFSnortPacket *); static void POP_ProcessServerPacket(SFSnortPacket *); static void POP_DisableDetect(SFSnortPacket *); static const uint8_t * POP_HandleCommand(SFSnortPacket *, const uint8_t *, const uint8_t *); static int POP_SearchStrFound(void *, void *, int, void *, void *); static int POP_Inspect(SFSnortPacket *); void POP_Set_flow_id( void *app_data, uint32_t fid ); MimeMethods mime_methods = {NULL, NULL, POP_DecodeAlert, POP_ResetState, is_data_end}; /**************************************************************************/ void POP_InitCmds(POPConfig *config) { const POPToken *tmp; if (config == NULL) return; /* add one to CMD_LAST for NULL entry */ config->cmds = (POPToken *)_dpd.snortAlloc(CMD_LAST + 1, sizeof(POPToken), PP_POP, PP_MEM_CATEGORY_CONFIG); if (config->cmds == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for pop " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } for (tmp = &pop_known_cmds[0]; tmp->name != NULL; tmp++) { config->cmds[tmp->search_id].name_len = tmp->name_len; config->cmds[tmp->search_id].search_id = tmp->search_id; config->cmds[tmp->search_id].name = strdup(tmp->name); if (config->cmds[tmp->search_id].name == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for pop " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } } /* initialize memory for command searches */ config->cmd_search = (POPSearch *)_dpd.snortAlloc(CMD_LAST, sizeof(POPSearch), PP_POP, PP_MEM_CATEGORY_CONFIG); if (config->cmd_search == NULL) { DynamicPreprocessorFatalMessage("%s(%d) => failed to allocate memory for pop " "command structure\n", *(_dpd.config_file), *(_dpd.config_line)); } config->num_cmds = CMD_LAST; } /* * Initialize POP searches * * @param none * * @return none */ void POP_SearchInit(void) { const POPToken *tmp; /* Response search */ pop_resp_search_mpse = _dpd.searchAPI->search_instance_new(); if (pop_resp_search_mpse == NULL) { DynamicPreprocessorFatalMessage("Could not allocate POP " "response search.\n"); } for (tmp = &pop_resps[0]; tmp->name != NULL; tmp++) { pop_resp_search[tmp->search_id].name = tmp->name; pop_resp_search[tmp->search_id].name_len = tmp->name_len; _dpd.searchAPI->search_instance_add(pop_resp_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } _dpd.searchAPI->search_instance_prep(pop_resp_search_mpse); } /* * Reset POP session state * * @param none * * @return none */ static void POP_ResetState(void) { pop_ssn->state = STATE_COMMAND; pop_ssn->prev_response = 0; pop_ssn->state_flags = 0; } /* * Given a server configuration and a port number, we decide if the port is * in the POP server port list. * * @param port the port number to compare with the configuration * * @return integer * @retval 0 means that the port is not a server port * @retval !0 means that the port is a server port */ int POP_IsServer(uint16_t port) { if( isPortEnabled( pop_eval_config->ports, port ) ) return 1; return 0; } static POP * POP_GetNewSession(SFSnortPacket *p, tSfPolicyId policy_id) { POP *ssn; POPConfig *pPolicyConfig = NULL; int ret = 0; pPolicyConfig = (POPConfig *)sfPolicyUserDataGetCurrent(pop_config); DEBUG_WRAP(DebugMessage(DEBUG_POP, "Creating new session data structure\n");); ssn = (POP *)_dpd.snortAlloc(1, sizeof(POP), PP_POP, PP_MEM_CATEGORY_SESSION); if (ssn == NULL) { DynamicPreprocessorFatalMessage("Failed to allocate POP session data\n"); } pop_ssn = ssn; ssn->prev_response = 0; ssn->session_flags |= POP_FLAG_CHECK_SSL; pop_ssn->mime_ssn.log_config = &(pop_eval_config->log_config); pop_ssn->mime_ssn.decode_conf = &(pop_eval_config->decode_conf); pop_ssn->mime_ssn.mime_mempool = pop_mime_mempool; pop_ssn->mime_ssn.log_mempool = pop_mempool; pop_ssn->mime_ssn.mime_stats = &(pop_stats.mime_stats); pop_ssn->mime_ssn.methods = &(mime_methods); if (( ret = _dpd.fileAPI->set_log_buffers(&(pop_ssn->mime_ssn.log_state), &(pPolicyConfig->log_config),pop_mempool, p->stream_session, PP_POP)) < 0) { if( ret == -1 ) { if(pop_stats.log_memcap_exceeded % 10000 == 0) { _dpd.logMsg("WARNING: POP memcap exceeded.\n"); } pop_stats.log_memcap_exceeded++; } _dpd.snortFree(ssn, sizeof(*ssn), PP_POP, PP_MEM_CATEGORY_SESSION); return NULL; } _dpd.sessionAPI->set_application_data(p->stream_session, PP_POP, ssn, &POP_SessionFree); if (p->flags & SSNFLAG_MIDSTREAM) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "Got midstream packet - " "setting state to unknown\n");); ssn->state = STATE_UNKNOWN; } #ifdef DEBUG_MSGS pop_session_counter++; ssn->session_number = pop_session_counter; #endif if (p->stream_session != NULL) { /* check to see if we're doing client reassembly in stream */ if (_dpd.streamAPI->get_reassembly_direction(p->stream_session) & SSN_DIR_TO_CLIENT) ssn->reassembling = 1; if(!ssn->reassembling) { _dpd.streamAPI->set_reassembly(p->stream_session, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_TO_CLIENT, STREAM_FLPOLICY_SET_ABSOLUTE); ssn->reassembling = 1; } } ssn->policy_id = policy_id; ssn->config = pop_config; ssn->flow_id = 0; pPolicyConfig->ref_count++; pop_stats.sessions++; pop_stats.conc_sessions++; pop_stats.cur_sessions++; if(pop_stats.max_conc_sessions < pop_stats.conc_sessions) pop_stats.max_conc_sessions = pop_stats.conc_sessions; return ssn; } /* * Do first-packet setup * * @param p standard Packet structure * * @return none */ static int POP_Setup(SFSnortPacket *p, POP *ssn) { int flags = 0; int pkt_dir; if (p->stream_session != NULL) { /* set flags to session flags */ flags = _dpd.sessionAPI->get_session_flags(p->stream_session); } /* Figure out direction of packet */ pkt_dir = POP_GetPacketDirection(p, flags); DEBUG_WRAP(DebugMessage(DEBUG_POP, "Session number: "STDu64"\n", ssn->session_number);); if (!(ssn->session_flags & POP_FLAG_CHECK_SSL)) ssn->session_flags |= POP_FLAG_CHECK_SSL; /* Check to see if there is a reassembly gap. If so, we won't know * what state we're in when we get the _next_ reassembled packet */ if ((pkt_dir != POP_PKT_FROM_SERVER) && (p->flags & FLAG_REBUILT_STREAM)) { int missing_in_rebuilt = _dpd.streamAPI->missing_in_reassembled(p->stream_session, SSN_DIR_TO_CLIENT); if (ssn->session_flags & POP_FLAG_NEXT_STATE_UNKNOWN) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "Found gap in previous reassembly buffer - " "set state to unknown\n");); ssn->state = STATE_UNKNOWN; ssn->session_flags &= ~POP_FLAG_NEXT_STATE_UNKNOWN; } if (missing_in_rebuilt == SSN_MISSING_BEFORE) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "Found missing packets before " "in reassembly buffer - set state to unknown\n");); ssn->state = STATE_UNKNOWN; } } return pkt_dir; } /* * Determine packet direction * * @param p standard Packet structure * * @return none */ static int POP_GetPacketDirection(SFSnortPacket *p, int flags) { int pkt_direction = POP_PKT_FROM_UNKNOWN; if (flags & SSNFLAG_MIDSTREAM) { if (POP_IsServer(p->src_port) && !POP_IsServer(p->dst_port)) { pkt_direction = POP_PKT_FROM_SERVER; } else if (!POP_IsServer(p->src_port) && POP_IsServer(p->dst_port)) { pkt_direction = POP_PKT_FROM_CLIENT; } } else { if (p->flags & FLAG_FROM_SERVER) { pkt_direction = POP_PKT_FROM_SERVER; } else if (p->flags & FLAG_FROM_CLIENT) { pkt_direction = POP_PKT_FROM_CLIENT; } /* if direction is still unknown ... */ if (pkt_direction == POP_PKT_FROM_UNKNOWN) { if (POP_IsServer(p->src_port) && !POP_IsServer(p->dst_port)) { pkt_direction = POP_PKT_FROM_SERVER; } else if (!POP_IsServer(p->src_port) && POP_IsServer(p->dst_port)) { pkt_direction = POP_PKT_FROM_CLIENT; } } } return pkt_direction; } /* * Free POP-specific related to this session * * @param v pointer to POP session structure * * * @return none */ static void POP_SessionFree(void *session_data) { POP *pop = (POP *)session_data; #ifdef SNORT_RELOAD POPConfig *pPolicyConfig = NULL; #endif ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); if (pop == NULL) return; #ifdef SNORT_RELOAD pPolicyConfig = (POPConfig *)sfPolicyUserDataGet(pop->config, pop->policy_id); if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (pop->config != pop_config)) { sfPolicyUserDataClear (pop->config, pop->policy_id); POP_FreeConfig(pPolicyConfig); /* No more outstanding policies for this config */ if (sfPolicyUserPolicyGetActive(pop->config) == 0) POP_FreeConfigs(pop->config); } } #endif if(pop->mime_ssn.decode_state != NULL) { mempool_free(pop_mime_mempool, pop->mime_ssn.decode_bkt); _dpd.snortFree(pop->mime_ssn.decode_state, sizeof(Email_DecodeState), PP_POP,PP_MEM_CATEGORY_SESSION); } if(pop->mime_ssn.log_state != NULL) { mempool_free(pop_mempool, pop->mime_ssn.log_state->log_hdrs_bkt); _dpd.snortFree(pop->mime_ssn.log_state, sizeof(MAIL_LogState), PP_POP, PP_MEM_CATEGORY_SESSION); } if ( ssl_cb ) ssl_cb->session_free(pop->flow_id); _dpd.snortFree(pop, sizeof(*pop), PP_POP, PP_MEM_CATEGORY_SESSION); if(pop_stats.cur_sessions) pop_stats.cur_sessions--; if(pop_stats.conc_sessions) pop_stats.conc_sessions--; } static int POP_FreeConfigsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { POPConfig *pPolicyConfig = (POPConfig *)pData; //do any housekeeping before freeing POPConfig sfPolicyUserDataClear (config, policyId); POP_FreeConfig(pPolicyConfig); return 0; } void POP_FreeConfigs(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, POP_FreeConfigsPolicy); sfPolicyConfigDelete(config); } void POP_FreeConfig(POPConfig *config) { if (config == NULL) return; if (config->cmds != NULL) { POPToken *tmp = config->cmds; for (; tmp->name != NULL; tmp++) _dpd.snortFree(tmp->name, sizeof(*(tmp->name)), PP_POP, PP_MEM_CATEGORY_CONFIG); _dpd.snortFree(config->cmds, sizeof(*(config->cmds)), PP_POP, PP_MEM_CATEGORY_CONFIG); } if (config->cmd_search_mpse != NULL) _dpd.searchAPI->search_instance_free(config->cmd_search_mpse); if (config->cmd_search != NULL) _dpd.snortFree(config->cmd_search, sizeof(*(config->cmd_search)), PP_POP, PP_MEM_CATEGORY_CONFIG); _dpd.snortFree(config, sizeof(*config), PP_POP, PP_MEM_CATEGORY_CONFIG); } /* * Free anything that needs it before shutting down preprocessor * * @param none * * @return none */ void POP_Free(void) { POP_FreeConfigs(pop_config); pop_config = NULL; if (pop_resp_search_mpse != NULL) _dpd.searchAPI->search_instance_free(pop_resp_search_mpse); } /* * Callback function for string search * * @param id id in array of search strings from pop_config.cmds * @param index index in array of search strings from pop_config.cmds * @param data buffer passed in to search function * * @return response * @retval 1 commands caller to stop searching */ static int POP_SearchStrFound(void *id, void *unused, int index, void *data, void *unused2) { int search_id = (int)(uintptr_t)id; pop_search_info.id = search_id; pop_search_info.index = index; pop_search_info.length = pop_current_search[search_id].name_len; /* Returning non-zero stops search, which is okay since we only look for one at a time */ return 1; } /* * Handle COMMAND state * * @param p standard Packet structure * @param ptr pointer into p->payload buffer to start looking at data * @param end points to end of p->payload buffer * * @return pointer into p->payload where we stopped looking at data * will be end of line or end of packet */ static const uint8_t * POP_HandleCommand(SFSnortPacket *p, const uint8_t *ptr, const uint8_t *end) { const uint8_t *eol; /* end of line */ const uint8_t *eolm; /* end of line marker */ int cmd_found; /* get end of line and end of line marker */ POP_GetEOL(ptr, end, &eol, &eolm); /* TODO If the end of line marker coincides with the end of payload we can't be * sure that we got a command and not a substring which we could tell through * inspection of the next packet. Maybe a command pending state where the first * char in the next packet is checked for a space and end of line marker */ /* do not confine since there could be space chars before command */ pop_current_search = &pop_eval_config->cmd_search[0]; cmd_found = _dpd.searchAPI->search_instance_find (pop_eval_config->cmd_search_mpse, (const char *)ptr, eolm - ptr, 0, POP_SearchStrFound); /* see if we actually found a command and not a substring */ if (cmd_found > 0) { const uint8_t *tmp = ptr; const uint8_t *cmd_start = ptr + pop_search_info.index; const uint8_t *cmd_end = cmd_start + pop_search_info.length; #ifdef DUMP_BUFFER dumpBuffer(POP_REQUEST_COMMAND_DUMP,ptr,(cmd_end-cmd_start)); #endif /* move past spaces up until start of command */ while ((tmp < cmd_start) && isspace((int)*tmp)) tmp++; /* if not all spaces before command, we found a * substring */ if (tmp != cmd_start) cmd_found = 0; /* if we're before the end of line marker and the next * character is not whitespace, we found a substring */ if ((cmd_end < eolm) && !isspace((int)*cmd_end)) cmd_found = 0; /* there is a chance that end of command coincides with the end of payload * in which case, it could be a substring, but for now, we will treat it as found */ } /* if command not found, alert and move on */ if (!cmd_found) { #ifdef DUMP_BUFFER dumpBuffer(POP_REQUEST_COMMAND_DUMP,ptr,(eolm-ptr)); #endif if (pop_ssn->state == STATE_UNKNOWN && ((pop_ssn->session_flags & POP_FLAG_CHECK_SSL) && (IsSSL(ptr, end - ptr, p->flags)))) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "Command not found, but state is " "unknown - checking for SSL\n");); /* check for encrypted */ DEBUG_WRAP(DebugMessage(DEBUG_POP, "Packet is SSL encrypted\n");); pop_ssn->state = STATE_TLS_DATA; /* Ignore data */ return end; } else { if (pop_ssn->state == STATE_UNKNOWN) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "Not SSL - try data state\n");); /* don't check for ssl again in this packet */ if (pop_ssn->session_flags & POP_FLAG_CHECK_SSL) pop_ssn->session_flags &= ~POP_FLAG_CHECK_SSL; pop_ssn->state = STATE_DATA; POP_GenerateAlert(POP_UNKNOWN_CMD, "%s", POP_UNKNOWN_CMD_STR); DEBUG_WRAP(DebugMessage(DEBUG_POP, "Unknown POP command found\n");); return ptr; } POP_GenerateAlert(POP_UNKNOWN_CMD, "%s", POP_UNKNOWN_CMD_STR); DEBUG_WRAP(DebugMessage(DEBUG_POP, "Unknown POP command found\n");); return eol; } } else if (pop_search_info.id == CMD_TOP) { pop_ssn->state = STATE_DATA; } else { if (pop_ssn->state == STATE_UNKNOWN) pop_ssn->state = STATE_COMMAND; } DEBUG_WRAP(DebugMessage(DEBUG_POP, "%s\n", pop_eval_config->cmds[pop_search_info.id].name);); if(pop_search_info.id == CMD_STLS) { if (eol == end) pop_ssn->state = STATE_TLS_CLIENT_PEND; } /* switch (pop_search_info.id) { case CMD_USER: case CMD_PASS: case CMD_RSET: case CMD_QUIT: case CMD_RETR: break; default: break; }*/ return eol; } /* * Process client packet * * @param packet standard Packet structure * * @return none */ static void POP_ProcessClientPacket(SFSnortPacket *p) { const uint8_t *ptr = p->payload; const uint8_t *end = p->payload + p->payload_size; #ifdef DUMP_BUFFER dumpBuffer(POP_REQUEST_PAYLOAD_DUMP,p->payload,p->payload_size); #endif ptr = POP_HandleCommand(p, ptr, end); } /* * Process server packet * * @param packet standard Packet structure * */ static void POP_ProcessServerPacket(SFSnortPacket *p) { int resp_found; const uint8_t *ptr; const uint8_t *end; const uint8_t *eolm; const uint8_t *eol; int resp_line_len; const char *tmp = NULL; ptr = p->payload; end = p->payload + p->payload_size; #ifdef DUMP_BUFFER dumpBuffer(POP_RESPONSE_PAYLOAD_DUMP,p->payload,p->payload_size); #endif while (ptr < end) { if(pop_ssn->state == STATE_DATA) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");); //ptr = POP_HandleData(p, ptr, end); ptr = _dpd.fileAPI->process_mime_data(p, ptr, end, &(pop_ssn->mime_ssn), 0, true, "POP", PP_POP); continue; } POP_GetEOL(ptr, end, &eol, &eolm); resp_line_len = eol - ptr; /* Check for response code */ pop_current_search = &pop_resp_search[0]; resp_found = _dpd.searchAPI->search_instance_find (pop_resp_search_mpse, (const char *)ptr, resp_line_len, 1, POP_SearchStrFound); if (resp_found > 0) { const uint8_t *cmd_start = ptr + pop_search_info.index; #ifdef DUMP_BUFFER dumpBuffer(POP_RESPONSE_STATUS_DUMP,ptr,(eolm-ptr)); #endif switch (pop_search_info.id) { case RESP_OK: tmp = _dpd.SnortStrcasestr((const char *)cmd_start, (eol - cmd_start), "octets"); if(tmp != NULL) pop_ssn->state = STATE_DATA; else { pop_ssn->prev_response = RESP_OK; pop_ssn->state = STATE_UNKNOWN; } break; default: break; } } else { #ifdef DUMP_BUFFER dumpBuffer(POP_RESPONSE_STATUS_DUMP,ptr, (eolm-ptr)); #endif DEBUG_WRAP(DebugMessage(DEBUG_POP, "Server response not found - see if it's SSL data\n");); if ((pop_ssn->session_flags & POP_FLAG_CHECK_SSL) && (IsSSL(ptr, end - ptr, p->flags))) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "Server response is an SSL packet\n");); pop_ssn->state = STATE_TLS_DATA; return; } else if (pop_ssn->session_flags & POP_FLAG_CHECK_SSL) { pop_ssn->session_flags &= ~POP_FLAG_CHECK_SSL; } if(pop_ssn->prev_response == RESP_OK) { { pop_ssn->state = STATE_DATA; pop_ssn->prev_response = 0; continue; } } else if(*ptr == '+') { POP_GenerateAlert(POP_UNKNOWN_RESP, "%s", POP_UNKNOWN_RESP_STR); DEBUG_WRAP(DebugMessage(DEBUG_POP, "Server response not found\n");); } } ptr = eol; } return; } /* For Target based * If a protocol for the session is already identified and not one POP is * interested in, POP should leave it alone and return without processing. * If a protocol for the session is already identified and is one that POP is * interested in, decode it. * If the protocol for the session is not already identified and the preprocessor * is configured to detect on one of the packet ports, detect. * Returns 0 if we should not inspect * 1 if we should continue to inspect */ static int POP_Inspect(SFSnortPacket *p) { #ifdef TARGET_BASED /* POP could be configured to be stateless. If stream isn't configured, assume app id * will never be set and just base inspection on configuration */ if (p->stream_session == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP: Target-based: No stream session.\n");); if ((POP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (POP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP: Target-based: Configured for this " "traffic, so let's inspect.\n");); return 1; } } else { int16_t app_id = _dpd.sessionAPI->get_application_protocol_id(p->stream_session); if (app_id != 0) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP: Target-based: App id: %u.\n", app_id);); if (app_id == pop_proto_id) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP: Target-based: App id is " "set to \"%s\".\n", POP_PROTO_REF_STR);); return 1; } } else { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP: Target-based: Unknown protocol for " "this session. See if we're configured.\n");); if ((POP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (POP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP: Target-based: POP port is configured.");); return 1; } } } DEBUG_WRAP(DebugMessage(DEBUG_POP,"POP: Target-based: Not inspecting ...\n");); #else /* Make sure it's traffic we're interested in */ if ((POP_IsServer(p->src_port) && (p->flags & FLAG_FROM_SERVER)) || (POP_IsServer(p->dst_port) && (p->flags & FLAG_FROM_CLIENT))) return 1; #endif /* TARGET_BASED */ return 0; } /* * Entry point to snort preprocessor for each packet * * @param packet standard Packet structure * * @return none */ void SnortPOP(SFSnortPacket *p) { int detected = 0; int pkt_dir; tSfPolicyId policy_id = _dpd.getNapRuntimePolicy(); ssl_callback_interface_t *ssl_cb = (ssl_callback_interface_t *)_dpd.getSSLCallback(); PROFILE_VARS; #ifdef DUMP_BUFFER dumpBufferInit(); #endif pop_ssn = (POP *)_dpd.sessionAPI->get_application_data(p->stream_session, PP_POP); if (pop_ssn != NULL) pop_eval_config = (POPConfig *)sfPolicyUserDataGet(pop_ssn->config, pop_ssn->policy_id); else pop_eval_config = (POPConfig *)sfPolicyUserDataGetCurrent(pop_config); if (pop_eval_config == NULL) return; if (pop_ssn == NULL) { if (!POP_Inspect(p)) return; pop_ssn = POP_GetNewSession(p, policy_id); if (pop_ssn == NULL) return; } pkt_dir = POP_Setup(p, pop_ssn); if (pkt_dir == POP_PKT_FROM_CLIENT) { /* This packet should be a tls client hello */ if (pop_ssn->state == STATE_TLS_CLIENT_PEND) { #ifdef DUMP_BUFFER dumpBuffer(POP_STATE_TLS_CLIENT_PEND_DUMP,p->payload,p->payload_size); #endif if (IsTlsClientHello(p->payload, p->payload + p->payload_size)) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "TLS DATA STATE ~~~~~~~~~~~~~~~~~~~~~~~~~\n");); pop_ssn->state = STATE_TLS_SERVER_PEND; if(ssl_cb) ssl_cb->session_initialize(p, pop_ssn, POP_Set_flow_id); return; } else { /* reset state - server may have rejected STARTTLS command */ pop_ssn->state = STATE_UNKNOWN; } } if ((pop_ssn->state == STATE_TLS_DATA) || (pop_ssn->state == STATE_TLS_SERVER_PEND)) { #ifdef DUMP_BUFFER dumpBuffer(POP_STATE_TLS_DATA_DUMP,p->payload,p->payload_size); #endif if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) { pop_ssn->state = STATE_COMMAND; } else { return; } } POP_ProcessClientPacket(p); DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP client packet\n");); } else { #ifdef DEBUG_MSGS if (pkt_dir == POP_PKT_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP server packet\n");); } else { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP packet NOT from client or server! " "Processing as a server packet\n");); } #endif if (pop_ssn->state == STATE_TLS_SERVER_PEND) { #ifdef DUMP_BUFFER dumpBuffer(POP_STATE_TLS_SERVER_PEND_DUMP,p->payload,p->payload_size); #endif if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) { pop_ssn->state = STATE_COMMAND; } else if (IsTlsServerHello(p->payload, p->payload + p->payload_size)) { pop_ssn->state = STATE_TLS_DATA; } else if (!(_dpd.sessionAPI->get_session_flags(p->stream_session) & SSNFLAG_MIDSTREAM) && !_dpd.streamAPI->missed_packets(p->stream_session, SSN_DIR_BOTH)) { /* revert back to command state - assume server didn't accept STARTTLS */ pop_ssn->state = STATE_UNKNOWN; } else return; } if (pop_ssn->state == STATE_TLS_DATA) { #ifdef DUMP_BUFFER dumpBuffer(POP_STATE_TLS_DATA_DUMP,p->payload,p->payload_size); #endif if( _dpd.streamAPI->is_session_decrypted(p->stream_session)) { pop_ssn->state = STATE_COMMAND; } else return; } { if (!_dpd.readyForProcess(p)) { /* Packet will be rebuilt, so wait for it */ DEBUG_WRAP(DebugMessage(DEBUG_POP, "Client packet will be reassembled\n")); return; } else if (pop_ssn->reassembling && !(p->flags & FLAG_REBUILT_STREAM)) { /* If this isn't a reassembled packet and didn't get * inserted into reassembly buffer, there could be a * problem. If we miss syn or syn-ack that had window * scaling this packet might not have gotten inserted * into reassembly buffer because it fell outside of * window, because we aren't scaling it */ pop_ssn->session_flags |= POP_FLAG_GOT_NON_REBUILT; pop_ssn->state = STATE_UNKNOWN; } else if (pop_ssn->reassembling && (pop_ssn->session_flags & POP_FLAG_GOT_NON_REBUILT)) { /* This is a rebuilt packet. If we got previous packets * that were not rebuilt, state is going to be messed up * so set state to unknown. It's likely this was the * beginning of the conversation so reset state */ DEBUG_WRAP(DebugMessage(DEBUG_POP, "Got non-rebuilt packets before " "this rebuilt packet\n");); pop_ssn->state = STATE_UNKNOWN; pop_ssn->session_flags &= ~POP_FLAG_GOT_NON_REBUILT; } /* Process as a server packet */ POP_ProcessServerPacket(p); } } PREPROC_PROFILE_START(popDetectPerfStats); detected = _dpd.detect(p); #ifdef PERF_PROFILING popDetectCalled = 1; #endif PREPROC_PROFILE_END(popDetectPerfStats); /* Turn off detection since we've already done it. */ POP_DisableDetect(p); if (detected) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP vulnerability detected\n");); } } static void POP_DisableDetect(SFSnortPacket *p) { _dpd.disableAllDetect(p); _dpd.enablePreprocessor(p, PP_SDF); } static inline POP *POP_GetSession(void *data) { if(data) return (POP *)_dpd.sessionAPI->get_application_data(data, PP_POP); return NULL; } /* Callback to return the MIME attachment filenames accumulated */ int POP_GetFilename(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { POP *ssn = POP_GetSession(data); if(ssn == NULL) return 0; *buf = ssn->mime_ssn.log_state->file_log.filenames; *len = ssn->mime_ssn.log_state->file_log.file_logged; return 1; } void POP_Set_flow_id( void *app_data, uint32_t fid ) { POP *ssn = (POP *)app_data; if( ssn ) ssn->flow_id = fid; } snort-2.9.20/src/dynamic-preprocessors/pop/pop_config.h0000644000175000017500000000710514241076130021306 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*************************************************************************** * * pop_config.h * * Author: Bhagyashree Bantwal * ***************************************************************************/ #ifndef __POP_CONFIG_H__ #define __POP_CONFIG_H__ #include "sfPolicyUserData.h" #include "file_mail_common.h" #include "sf_email_attach_decode.h" #define CONF_SEPARATORS " \t\n\r" #define CONF_PORTS "ports" #define CONF_POP_MEMCAP "memcap" #define CONF_MAX_MIME_MEM "max_mime_mem" #define CONF_B64_DECODE "b64_decode_depth" #define CONF_QP_DECODE "qp_decode_depth" #define CONF_BITENC_DECODE "bitenc_decode_depth" #define CONF_UU_DECODE "uu_decode_depth" #define CONF_DISABLED "disabled" #define CONF_START_LIST "{" #define CONF_END_LIST "}" /*These are temporary values*/ #define DEFAULT_MAX_MIME_MEM 838860 #define DEFAULT_POP_MEMCAP 838860 #define MAX_POP_MEMCAP 104857600 #define MIN_POP_MEMCAP 3276 #define MAX_MIME_MEM 104857600 #define MIN_MIME_MEM 3276 #define MAX_DEPTH 65535 #define MIN_DEPTH -1 #define POP_DEFAULT_SERVER_PORT 110 /* POP normally runs on port 110 */ #define ERRSTRLEN 512 typedef struct _POPSearch { char *name; int name_len; } POPSearch; typedef struct _POPToken { char *name; int name_len; int search_id; } POPToken; typedef struct _POPCmdConfig { char alert; /* 1 if alert when seen */ char normalize; /* 1 if we should normalize this command */ int max_line_len; /* Max length of this particular command */ } POPCmdConfig; typedef struct _POPConfig { uint8_t ports[8192]; uint32_t memcap; POPToken *cmds; POPSearch *cmd_search; void *cmd_search_mpse; int num_cmds; int disabled; DecodeConfig decode_conf; MAIL_LogConfig log_config; int ref_count; } POPConfig; typedef struct _POP_Stats { uint64_t sessions; uint64_t conc_sessions; uint64_t max_conc_sessions; uint64_t log_memcap_exceeded; uint64_t cur_sessions; MimeStats mime_stats; } POP_Stats; extern POP_Stats pop_stats; /* Function prototypes */ void POP_ParseArgs(POPConfig *, char *); void POP_PrintConfig(POPConfig *config); void POP_CheckConfig(POPConfig *, tSfPolicyUserContextId); #endif snort-2.9.20/src/dynamic-preprocessors/pop/Makefile.am0000444000175000017500000000223514230012554021041 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies INCLUDES = -I../include -I${srcdir}/../ssl_common -I${srcdir}/../libs dynamicpreprocessordir = ${libdir}/snort_dynamicpreprocessor dynamicpreprocessor_LTLIBRARIES = libsf_pop_preproc.la libsf_pop_preproc_la_LDFLAGS = -export-dynamic -module @XCCFLAGS@ if SO_WITH_STATIC_LIB libsf_pop_preproc_la_LIBADD = ../libsf_dynamic_preproc.la else nodist_libsf_pop_preproc_la_SOURCES = \ ../include/sf_dynamic_preproc_lib.c \ ../include/mempool.c \ ../include/sf_sdlist.c \ ../include/util_unfold.c \ ../include/sf_base64decode.c \ ../include/sf_email_attach_decode.c \ ../include/sfPolicyUserData.c \ ../ssl_common/ssl.c \ ../ssl_common/ssl_config.c \ ../ssl_common/ssl_inspect.c \ ../libs/sfparser.c endif libsf_pop_preproc_la_SOURCES = \ pop_config.c \ pop_config.h \ pop_log.c \ pop_log.h \ pop_paf.c \ pop_paf.h \ pop_util.c \ pop_util.h \ snort_pop.c \ snort_pop.h \ spp_pop.c \ spp_pop.h if BUILD_BUFFER_DUMP libsf_pop_preproc_la_SOURCES += \ pop_buffer_dump.c \ pop_buffer_dump.h endif EXTRA_DIST = \ sf_pop.vcxproj \ sf_pop.dsp all-local: $(LTLIBRARIES) $(MAKE) DESTDIR=`pwd`/../build install-dynamicpreprocessorLTLIBRARIES snort-2.9.20/src/dynamic-preprocessors/pop/pop_log.h0000644000175000017500000000457714241076132020636 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * pop_log.h * * Author: Bhagyashree Bantwal * **************************************************************************/ #ifndef __POP_LOG_H__ #define __POP_LOG_H__ #define GENERATOR_SPP_POP 142 /* Events for POP */ #define POP_UNKNOWN_CMD 1 #define POP_UNKNOWN_RESP 2 #define POP_MEMCAP_EXCEEDED 3 #define POP_B64_DECODING_FAILED 4 #define POP_QP_DECODING_FAILED 5 /* Do not delete or reuse this SID. Commenting this SID as this alert is no longer valid.* * #define POP_BITENC_DECODING_FAILED 6 */ #define POP_UU_DECODING_FAILED 7 #define POP_EVENT_MAX 8 /* Messages for each event */ #define POP_UNKNOWN_CMD_STR "(POP) Unknown POP3 command" #define POP_UNKNOWN_RESP_STR "(POP) Unknown POP3 response" #define POP_MEMCAP_EXCEEDED_STR "(POP) No memory available for decoding. Memcap exceeded" #define POP_B64_DECODING_FAILED_STR "(POP) Base64 Decoding failed." #define POP_QP_DECODING_FAILED_STR "(POP) Quoted-Printable Decoding failed." #define POP_UU_DECODING_FAILED_STR "(POP) Unix-to-Unix Decoding failed." #define EVENT_STR_LEN 256 /* Function prototypes */ void POP_GenerateAlert(int, char *, ...); void POP_DecodeAlert(void *); #endif snort-2.9.20/src/dynamic-preprocessors/pop/pop_util.c0000644000175000017500000001620214241076137021016 0ustar apoapo/* * pop_util.c * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * * Author: Bhagyashree Bantwal * * Description: * * This file contains POP helper functions. * * Entry point functions: * * safe_strchr() * safe_strstr() * copy_to_space() * safe_sscanf() * * */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "snort_bounds.h" #include "snort_pop.h" #include "pop_util.h" #include "pop_config.h" #include "sf_dynamic_preprocessor.h" #include "sf_snort_packet.h" #include "Unified2_common.h" #include "memory_stats.h" extern POP *pop_ssn; extern MemPool *pop_mime_mempool; extern MemPool *pop_mempool; extern POP_Stats pop_stats; int POP_Print_Mem_Stats(FILE *fd, char *buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; if (fd) { len = fprintf(fd, ",%lu,%lu,%lu" ",%lu,%u,%u" ",%lu,%u,%u,%lu" , pop_stats.sessions , pop_stats.max_conc_sessions , pop_stats.cur_sessions , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); return len; } if (buffer) { /* * Old buffer output for control socket comm, * like via, "show snort preprocessor-memory-usage" * CLI preserved as is */ len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics of POP on: %s\n" "POP Session Statistics:\n" " Total Sessions seen: " STDu64 "\n" " Max concurrent sessions: " STDu64 "\n" " Current Active sessions: " STDu64 "\n" "\n Memory Pool:\n" " Free Memory:\n" " POP Mime Pool: %14zu bytes\n" " POP Pool: %14zu bytes\n" " Used Memory:\n" " POP Mime Pool: %14zu bytes\n" " POP Pool: %14zu bytes\n" " ------------------- ---------------\n" " Total Memory: %14zu bytes\n" , ctime(&curr_time) , pop_stats.sessions , pop_stats.max_conc_sessions , pop_stats.cur_sessions , (pop_mime_mempool) ? (pop_mime_mempool->max_memory - pop_mime_mempool->used_memory) : 0 , (pop_mempool) ? (pop_mempool->max_memory - pop_mempool->used_memory) : 0 , (pop_mime_mempool) ? pop_mime_mempool->used_memory : 0 , (pop_mempool) ? pop_mempool->used_memory : 0 , ((pop_mime_mempool) ? (pop_mime_mempool->max_memory) : 0) + ((pop_mempool) ? (pop_mempool->max_memory) : 0)); len += PopulateMemStatsBuffTrailer(buffer+len, len, meminfo); } else { _dpd.logMsg("POP Preprocessor Statistics\n"); _dpd.logMsg(" Total sessions : %lu \n", pop_stats.sessions); _dpd.logMsg(" Max concurrent sessions : %lu \n", pop_stats.max_conc_sessions); _dpd.logMsg(" Current sessions : %lu \n", pop_stats.cur_sessions); _dpd.logMsg(" POP Session \n"); _dpd.logMsg(" Used Memory :%14lu\n", meminfo[PP_MEM_CATEGORY_SESSION].used_memory); _dpd.logMsg(" No of Allocs :%14u\n", meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc); _dpd.logMsg(" No of Frees :%14u\n", meminfo[PP_MEM_CATEGORY_SESSION].num_of_free); _dpd.logMsg(" POP Config \n"); _dpd.logMsg(" Used Memory :%14lu\n", meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); _dpd.logMsg(" No of Allocs :%14u\n", meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc); _dpd.logMsg(" No of Frees :%14u\n", meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free); _dpd.logMsg(" Total memory used :%14lu\n", meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); } return len; } void POP_GetEOL(const uint8_t *ptr, const uint8_t *end, const uint8_t **eol, const uint8_t **eolm) { const uint8_t *tmp_eol; const uint8_t *tmp_eolm; /* XXX maybe should fatal error here since none of these * pointers should be NULL */ if (ptr == NULL || end == NULL || eol == NULL || eolm == NULL) return; tmp_eol = (uint8_t *)memchr(ptr, '\n', end - ptr); if (tmp_eol == NULL) { tmp_eol = end; tmp_eolm = end; } else { /* end of line marker (eolm) should point to marker and * end of line (eol) should point to end of marker */ if ((tmp_eol > ptr) && (*(tmp_eol - 1) == '\r')) { tmp_eolm = tmp_eol - 1; } else { tmp_eolm = tmp_eol; } /* move past newline */ tmp_eol++; } *eol = tmp_eol; *eolm = tmp_eolm; } #ifdef DEBUG_MSGS char pop_print_buffer[65537]; const char * POP_PrintBuffer(SFSnortPacket *p) { const uint8_t *ptr = NULL; int len = 0; int iorig, inew; ptr = p->payload; len = p->payload_size; for (iorig = 0, inew = 0; iorig < len; iorig++, inew++) { if ((isascii((int)ptr[iorig]) && isprint((int)ptr[iorig])) || (ptr[iorig] == '\n')) { pop_print_buffer[inew] = ptr[iorig]; } else if (ptr[iorig] == '\r' && ((iorig + 1) < len) && (ptr[iorig + 1] == '\n')) { iorig++; pop_print_buffer[inew] = '\n'; } else if (isspace((int)ptr[iorig])) { pop_print_buffer[inew] = ' '; } else { pop_print_buffer[inew] = '.'; } } pop_print_buffer[inew] = '\0'; return &pop_print_buffer[0]; } #endif snort-2.9.20/src/dynamic-preprocessors/pop/sf_pop.dsp0000444000175000017500000001571014230012554021005 0ustar apoapo# Microsoft Developer Studio Project File - Name="sf_pop" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** DO NOT EDIT ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=sf_pop - Win32 IPv6 Release !MESSAGE This is not a valid makefile. To build this project using NMAKE, !MESSAGE use the Export Makefile command and run !MESSAGE !MESSAGE NMAKE /f "sf_pop.mak". !MESSAGE !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE !MESSAGE NMAKE /f "sf_pop.mak" CFG="sf_pop - Win32 IPv6 Release" !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE !MESSAGE "sf_pop - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE "sf_pop - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "sf_pop - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release" # PROP Intermediate_Dir "Release" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_POP_EXPORTS" /YX /FD /c # ADD CPP /nologo /MD /W3 /GX /O2 /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "NDEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "NDEBUG" # ADD RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 pcre.lib ws2_32.lib ../libs/Release/sfdynamic_preproc_libs.lib /nologo /dll /machine:I386 /libpath:"../../../src/win32/WIN32-Libraries" !ELSEIF "$(CFG)" == "sf_pop - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 2 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug" # PROP Intermediate_Dir "Debug" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "SF_POP_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "..\libs" /I "..\ssl_common" /I "..\include" /I "..\..\win32\Win32-Includes" /I ".\\" /I "..\..\win32\Win32-Includes\WinPCAP" /I "..\..\..\daq\api" /I "..\..\..\daq\sfbpf" /D "_DEBUG" /D "DEBUG" /D "ENABLE_PAF" /D "SF_SNORT_PREPROC_DLL" /D "_WINDOWS" /D "_USRDLL" /D "ACTIVE_RESPONSE" /D "GRE" /D "MPLS" /D "TARGET_BASED" /D "PERF_PROFILING" /D "ENABLE_RESPOND" /D "ENABLE_REACT" /D "_WINDLL" /D "WIN32" /D "_MBCS" /D "HAVE_CONFIG_H" /D "_AFXDLL" /D SIGNAL_SNORT_READ_ATTR_TBL=30 /FD /GZ /c # SUBTRACT CPP /X /YX # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x409 /d "_DEBUG" # ADD RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 pcre.lib ws2_32.lib ../libs/Debug/sfdynamic_preproc_libs.lib /nologo /dll /debug /machine:I386 /pdbtype:sept /libpath:"../../../src/win32/WIN32-Libraries" !ENDIF # Begin Target # Name "sf_pop - Win32 Release" # Name "sf_pop - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=..\include\mempool.c # End Source File # Begin Source File SOURCE=.\pop_config.c # End Source File # Begin Source File SOURCE=.\pop_log.c # End Source File # Begin Source File SOURCE=.\pop_util.c # End Source File # Begin Source File SOURCE=..\include\sf_base64decode.c # End Source File # Begin Source File SOURCE=..\include\sf_dynamic_preproc_lib.c # End Source File # Begin Source File SOURCE=..\include\sf_email_attach_decode.c # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.c # End Source File # Begin Source File SOURCE=..\include\sfPolicyUserData.c # End Source File # Begin Source File SOURCE=..\libs\sfparser.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_config.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_ha.c # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_inspect.c # End Source File # Begin Source File SOURCE=.\snort_pop.c # End Source File # Begin Source File SOURCE=.\pop_paf.c # End Source File # Begin Source File SOURCE=.\spp_pop.c # End Source File # Begin Source File SOURCE=..\include\util_unfold.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=..\ssl_common\ssl.h # End Source File # Begin Source File SOURCE=..\ssl_common\ssl_include.h # End Source File # Begin Source File SOURCE=..\include\mempool.h # End Source File # Begin Source File SOURCE=.\pop_config.h # End Source File # Begin Source File SOURCE=.\pop_log.h # End Source File # Begin Source File SOURCE=.\pop_util.h # End Source File # Begin Source File SOURCE=.\pop_paf.h # End Source File # Begin Source File SOURCE=..\include\sf_base64decode.h # End Source File # Begin Source File SOURCE=..\include\sf_email_attach_decode.h # End Source File # Begin Source File SOURCE=.\sf_preproc_info.h # End Source File # Begin Source File SOURCE=..\include\sf_sdlist.h # End Source File # Begin Source File SOURCE=.\snort_pop.h # End Source File # Begin Source File SOURCE=.\spp_pop.h # End Source File # Begin Source File SOURCE=..\include\util_unfold.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project snort-2.9.20/src/dynamic-preprocessors/pop/pop_log.c0000644000175000017500000000640414241076131020617 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /************************************************************************** * * pop_log.c * * Author: Bhagyashree Bantwal * * Description: * * This file handles POP alerts. * * Entry point functions: * * POP_GenerateAlert() * * **************************************************************************/ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "pop_config.h" #include "pop_log.h" #include "snort_pop.h" #include "sf_dynamic_preprocessor.h" extern POPConfig *pop_eval_config; extern POP *pop_ssn; char pop_event[POP_EVENT_MAX][EVENT_STR_LEN]; void POP_GenerateAlert(int event, char *format, ...) { va_list ap; /* Only log a specific alert once per session */ if (pop_ssn->alert_mask & (1 << event)) { #ifdef DEBUG_MSGS DEBUG_WRAP(DebugMessage(DEBUG_POP, "Already alerted on: %s - " "ignoring event.\n", pop_event[event]);); #endif return; } /* set bit for this alert so we don't alert on again * in this session */ pop_ssn->alert_mask |= (1 << event); va_start(ap, format); pop_event[event][0] = '\0'; vsnprintf(&pop_event[event][0], EVENT_STR_LEN - 1, format, ap); pop_event[event][EVENT_STR_LEN - 1] = '\0'; _dpd.alertAdd(GENERATOR_SPP_POP, event, 1, 0, 3, &pop_event[event][0], 0); DEBUG_WRAP(DebugMessage(DEBUG_POP, "POP Alert generated: %s\n", pop_event[event]);); va_end(ap); } void POP_DecodeAlert(void *ds) { Email_DecodeState *decode_state = (Email_DecodeState *)ds; switch( decode_state->decode_type ) { case DECODE_B64: if (pop_eval_config->decode_conf.b64_depth > -1) POP_GenerateAlert(POP_B64_DECODING_FAILED, "%s", POP_B64_DECODING_FAILED_STR); break; case DECODE_QP: if (pop_eval_config->decode_conf.qp_depth > -1) POP_GenerateAlert(POP_QP_DECODING_FAILED, "%s", POP_QP_DECODING_FAILED_STR); break; case DECODE_UU: if (pop_eval_config->decode_conf.uu_depth > -1) POP_GenerateAlert(POP_UU_DECODING_FAILED, "%s", POP_UU_DECODING_FAILED_STR); break; default: break; } } snort-2.9.20/src/dynamic-preprocessors/pop/pop_paf.c0000644000175000017500000003440114241076134020605 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include #include "sf_types.h" #include "pop_paf.h" #include "spp_pop.h" #include "sf_dynamic_preprocessor.h" #include "stream_api.h" #include "snort_pop.h" #include "pop_config.h" #include "file_api.h" #include "pop_util.h" static uint8_t pop_paf_id = 0; // global variables defined in snort_pop.c extern const POPToken pop_known_cmds[]; /* Structure used to record expected server termination sequence */ typedef enum _PopExpectedResp { POP_PAF_SINGLE_LINE_STATE, /* server response will end with \r\n */ POP_PAF_MULTI_LINE_STATE, /* server response will end with \r\n.\r\n */ POP_PAF_DATA_STATE, /* Indicated MIME will be contained in response */ POP_PAF_HAS_ARG /* Intermediate state when parsing LIST */ } PopExpectedResp; typedef enum _PopParseCmdState { POP_CMD_SEARCH, /* Search for Command */ POP_CMD_FIN, /* Found space. Finished parsing Command */ POP_CMD_ARG /* Parsing command with multi-line response iff arg given */ } PopParseCmdState; /* saves data when parsing client commands */ typedef struct _PopPafParseCmd { char *next_letter; /* a pointer to the current commands data */ PopExpectedResp exp_resp; /* the expected termination sequence for this command */ PopParseCmdState status; /* whether the current has already been found */ } PopPafParseCmd; /* State tracker for POP PAF */ typedef struct _PopPafData { PopExpectedResp pop_state; /* The current POP PAF state. */ DataEndState end_state; /* Current termination sequence state */ PopPafParseCmd cmd_state; /* all of the command parsing data */ MimeDataPafInfo data_info; /* Mime Information */ bool cmd_continued; /* data continued from previous packet? */ bool end_of_data; // uint32_t length; /* TODO --> use length in top and RETR */ } PopPafData; /* * read process_command() description below */ static bool search_for_command(PopPafData *pfdata, const uint8_t ch) { char val = *(pfdata->cmd_state.next_letter); // if end of command && data contains a space or newline if (val == '\0' && (isblank(ch) || ch == '\r' || ch == '\n')) { if (pfdata->cmd_state.exp_resp == POP_PAF_HAS_ARG) { pfdata->cmd_state.status = POP_CMD_ARG; } else { pfdata->cmd_state.status = POP_CMD_FIN; pfdata->pop_state = pfdata->cmd_state.exp_resp; return true; } } else if (toupper(ch) == toupper(val) ) { pfdata->cmd_state.next_letter++; } else { pfdata->cmd_state.status = POP_CMD_FIN; } return false; } /* * read process_command() description below */ static bool init_command_search(PopPafData *pfdata, const uint8_t ch) { pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE; switch(ch) { case 'c': case 'C': pfdata->cmd_state.exp_resp = POP_PAF_MULTI_LINE_STATE; pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_CAPA].name[1]); break; case 'l': case 'L': pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG; pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_LIST].name[1]); break; case 'r': case 'R': pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE; pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_RETR].name[1]); break; case 't': case 'T': pfdata->cmd_state.exp_resp = POP_PAF_DATA_STATE; pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_TOP].name[1]); break; case 'u': case 'U': pfdata->cmd_state.exp_resp = POP_PAF_HAS_ARG; pfdata->cmd_state.next_letter = &(pop_known_cmds[CMD_UIDL].name[1]); break; default: pfdata->cmd_state.status = POP_CMD_FIN; } return false; } /* * Attempts to determine the current command based upon the given character * If another character is required to determine the current command, * sets the function pointer to the correct next state * * PARAMS: * pop_cmd - a pointer to the struct containing all of the * relevant parsing info * ch - the first character from the clients command * RETURNS * true - if the expected response is NOT a single line * false - otherwise */ static inline bool process_command(PopPafData *pfdata, const uint8_t ch) { if (pfdata->cmd_state.next_letter) return search_for_command(pfdata, ch); else return init_command_search(pfdata, ch); } static inline void reset_data_states(PopPafData *pfdata) { // reset MIME info _dpd.fileAPI->reset_mime_paf_state(&(pfdata->data_info)); // reset general pop fields pfdata->cmd_continued = false; pfdata->end_state = PAF_DATA_END_UNKNOWN; pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE; } /* * Checks if the current data is a valid response. * According to RFC 1939, every response begins with either * +OK * -ERR. * * RETURNS: * true - if the character is a + * false - if the character is anything else */ static inline int valid_response(const uint8_t data) { return (data == '+'); } /* * Client PAF calls this command to set the server's state. This is the * function which ensure's the server know the correct expected * DATA */ static inline void set_server_state(void *ssn, PopExpectedResp state) { PopPafData *server_data = *(_dpd.streamAPI->get_paf_user_data(ssn, 0, pop_paf_id)); // ERROR IF SERVER DATA DOES NOT EXIST!! SHOULD NOT BE POSSIBLE!! if (server_data) { reset_data_states(server_data); server_data->end_of_data = false; server_data->pop_state = state; } } /* * A helper function to reset the client's command parsing * information */ static inline void reset_client_cmd_info(PopPafData *pfdata) { pfdata->cmd_state.next_letter = NULL; pfdata->cmd_state.status = POP_CMD_SEARCH; } /* * Statefully search for the termination sequence CRCL.CRLF ("\r\n.\r\n"). * * PARAMS: * mime_data : true if this is mime_data. * * RETURNS: * 0 - if termination sequence not found * 1 - if termination sequence found */ static bool find_data_end_multi_line(PopPafData *pfdata, const uint8_t ch, bool mime_data) { // TODO: This will currently flush on MIME boundary, and one line later at end of PDU if (_dpd.fileAPI->check_data_end(&(pfdata->end_state), ch)) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of Multi-line response found\n");); pfdata->end_of_data = true; pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE; reset_data_states(pfdata); return true; } // if this is a data command, search for MIME ending if (mime_data) { if (_dpd.fileAPI->process_mime_paf_data(&(pfdata->data_info), ch)) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "Mime Boundary found. Flushing data!\n");); pfdata->cmd_continued = true; return true; } } return false; } /* * Statefully search for the termination sequence LF ("\n"). Will also * set the correct response state. * * PARAMS: * * RETURNS: * 0 - if terminatino sequence not found * 1 - if termination sequence found */ static inline bool find_data_end_single_line(PopPafData *pfdata, const uint8_t ch, bool client) { if (ch == '\n') { // reset the correct information if (client) reset_client_cmd_info(pfdata); else reset_data_states(pfdata); DEBUG_WRAP(DebugMessage(DEBUG_POP, "End of single-line response " "found. Flushing data!\n");); return true; } return false; } static PAF_Status pop_paf_server(PopPafData *pfdata, const uint8_t* data, uint32_t len, uint32_t* fp) { uint32_t i; uint32_t boundary_start = 0; // if a negative response was received, it will be a one line response. if (!pfdata->cmd_continued && !valid_response(*data)) pfdata->pop_state = POP_PAF_SINGLE_LINE_STATE; for (i = 0; i < len; i++) { uint8_t ch = data[i]; // find the termination sequence based upon the current state switch (pfdata->pop_state) { case POP_PAF_MULTI_LINE_STATE: if( find_data_end_multi_line(pfdata, ch, false) ) { *fp = i + 1; return PAF_FLUSH; } break; case POP_PAF_DATA_STATE: // TODO --> statefully get length if ( find_data_end_multi_line(pfdata, ch, true) ) { *fp = i + 1; return PAF_FLUSH; } if (pfdata->data_info.boundary_state == MIME_PAF_BOUNDARY_UNKNOWN) boundary_start = i; break; case POP_PAF_SINGLE_LINE_STATE: default: if ( find_data_end_single_line(pfdata, ch, false) ) { *fp = i + 1; return PAF_FLUSH; } break; } } pfdata->cmd_continued = true; if ( scanning_boundary(&pfdata->data_info, boundary_start, fp) ) return PAF_LIMIT; return PAF_SEARCH; } /* * Determine the Client's command and set the response state. * Flush data when "\r\n" is received */ static PAF_Status pop_paf_client(void *ssn, PopPafData *pfdata, const uint8_t* data, uint32_t len, uint32_t* fp) { uint32_t i; // TODO ... ensure current command is smaller than max command length for (i = 0; i < len; i++) { uint8_t ch = data[i]; switch (pfdata->cmd_state.status) { case POP_CMD_SEARCH: if (process_command(pfdata, ch) ) { set_server_state(ssn, pfdata->pop_state); } //break; DO NOT UNCOMMENT!! both cases should check for a LF. case POP_CMD_FIN: if (find_data_end_single_line(pfdata, ch, true) ) { // reset command parsing data *fp = i + 1; return PAF_FLUSH; } break; case POP_CMD_ARG: if (find_data_end_single_line(pfdata, ch, true)) { set_server_state(ssn, POP_PAF_MULTI_LINE_STATE); *fp = i + 1; return PAF_FLUSH; } else if (isdigit(ch)) { pfdata->cmd_state.status = POP_CMD_FIN; } } } return PAF_SEARCH; } /* Function: pop_paf() Purpose: POP PAF callback. Inspects pop traffic. Checks client traffic for the current command and sets correct server termination sequence. Client side data will flush after receiving CRLF ("\r\n"). Server data flushes after finding set termination sequence. Arguments: void * - stream5 session pointer void ** - DNP3 state tracking structure const uint8_t * - payload data to inspect uint32_t - length of payload data uint32_t - flags to check whether client or server uint32_t * - pointer to set flush point uint32_t * - pointer to set header flush point Returns: PAF_Status - PAF_FLUSH if flush point found, PAF_SEARCH otherwise */ static PAF_Status pop_paf(void* ssn, void** ps, const uint8_t* data, uint32_t len, uint64_t *flags, uint32_t* fp, uint32_t* fp_eoh) { PopPafData *pfdata = *(PopPafData **)ps; if (pfdata == NULL) { if (_dpd.fileAPI->check_paf_abort(ssn)) return PAF_ABORT; pfdata = _dpd.snortAlloc(1, sizeof(*pfdata), PP_POP, 0); if (pfdata == NULL) { return PAF_ABORT; } reset_data_states(pfdata); *ps = pfdata; } if (*flags & FLAG_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From server.\n");); return pop_paf_server(pfdata, data, len, fp); } else { DEBUG_WRAP(DebugMessage(DEBUG_POP, "PAF: From client.\n");); return pop_paf_client(ssn, pfdata, data, len, fp); } } bool is_data_end (void* ssn) { if ( ssn ) { PopPafData** s = (PopPafData **)_dpd.streamAPI->get_paf_user_data(ssn, 0, pop_paf_id); if ( s && (*s) ) return ((*s)->end_of_data); } return false; } void pop_paf_cleanup(void *pafData) { if (pafData) { _dpd.snortFree(pafData, sizeof(PopPafData), PP_POP, 0); } } #ifdef TARGET_BASED void register_pop_paf_service (struct _SnortConfig *sc, int16_t app, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { pop_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, true, pop_paf, true); pop_paf_id = _dpd.streamAPI->register_paf_service(sc, policy, app, false,pop_paf, true); _dpd.streamAPI->register_paf_free(pop_paf_id, pop_paf_cleanup); } } #endif void register_pop_paf_port(struct _SnortConfig *sc, unsigned int i, tSfPolicyId policy) { if (_dpd.isPafEnabled()) { pop_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, true, pop_paf, true); pop_paf_id = _dpd.streamAPI->register_paf_port(sc, policy, (uint16_t)i, false, pop_paf, true); _dpd.streamAPI->register_paf_free(pop_paf_id, pop_paf_cleanup); } } snort-2.9.20/src/memory_stats.h0000644000175000017500000000732214241076635014573 0ustar apoapo/* ** ** memory_stats.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Author(s): Puneeth Kumar C V ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef __MEMORY_STATS_H__ #define __MEMORY_STATS_H__ #include #include "sfcontrol.h" #define PP_MEM_CATEGORY_SESSION 0 #define PP_MEM_CATEGORY_CONFIG 1 #define PP_MEM_CATEGORY_MEMPOOL 2 #define PP_MEM_CATEGORY_MISC 3 #define PP_MEM_MAX_CATEGORY 4 #define MEMSTATS_DUMP_INTERVAL 5 * 60 //5 minutes #define MIN_FILE_SIZE_TO_ROTATE (20 * 1024 * 1024) typedef struct _PreprocMemNumAlloc { uint32_t num_of_alloc; uint32_t num_of_free; size_t used_memory; } PreprocMemNumAlloc; typedef struct _PreprocMemInfo { uint32_t num_of_alloc; uint32_t num_of_free; size_t used_memory; } PreprocMemInfo; typedef int (*MemoryStatsDisplayFunc)(FILE *fd, char *buffer, PreprocMemInfo *meminfo); void initMemoryStatsApi(); void rotate_preproc_stats(); void MemoryPostFunction(uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); int MemoryControlFunction(uint16_t type, void *new_context, void **old_context); int MemoryPreFunction(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char *statusBuf, int statusBuf_len); int RegisterMemoryStatsFunction(uint preproc, MemoryStatsDisplayFunc cb); int PPMemoryStatsDumpCfg(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char* statusBuf, int statusBuf_len); void PPMemoryStatsDumpShow(uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); void* SnortPreprocAlloc (int num, unsigned long size, uint32_t preproc, uint32_t sub); void SnortPreprocFree (void *ptr, uint32_t size, uint32_t preproc, uint32_t sub); void memory_stats_periodic_handler(FILE *fd, bool file_dump); void dump_preproc_stats(time_t curr_time); static inline int PopulateMemStatsBuffTrailer(char *buffer, int len, PreprocMemInfo *meminfo) { return snprintf(buffer, CS_STATS_BUF_SIZE-len, "\n Heap Memory:\n" " Session: %14zu bytes\n" " Configuration: %14zu bytes\n" " -------------- ------------\n" " Total Memory: %14zu bytes\n" " No of allocs: %14d times\n" " IP sessions: %14d times\n" "----------------------------------------------------\n" , meminfo[0].used_memory , meminfo[1].used_memory , meminfo[0].used_memory + meminfo[1].used_memory , meminfo[0].num_of_alloc + meminfo[1].num_of_alloc , meminfo[0].num_of_free + meminfo[1].num_of_free); } #endif snort-2.9.20/src/log.c0000644000175000017500000017634514241076630012630 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_STRINGS_H #include #endif #ifndef WIN32 #include #include #include #endif /* !WIN32 */ #include #include #include "log.h" #include "rules.h" #include "treenodes.h" #include "util.h" #include "snort_debug.h" #include "signature.h" #include "util_net.h" #include "snort_bounds.h" #include "obfuscation.h" #include "detection_util.h" #include "detect.h" #include "snort.h" extern OptTreeNode *otn_tmp; /* global ptr to current rule data */ char *data_dump_buffer; /* printout buffer for PrintNetData */ int data_dump_buffer_size = 0;/* size of printout buffer */ int dump_size; /* amount of data to print */ extern int IsGzipData(void *); extern int IsJSNormData(void *); void AllocDumpBuf(); /***************** LOG ASCII ROUTINES *******************/ #ifndef NO_NON_ETHER_DECODER #endif /* * Function: PrintNetData(FILE *, u_char *,int, Packet *) * * Purpose: Do a side by side dump of a buffer, hex dump of buffer bytes on * the left, decoded ASCII on the right. * * Arguments: fp => ptr to stream to print to * start => pointer to buffer data * len => length of data buffer * * Returns: void function */ void PrintNetData(FILE * fp, const u_char * start, const int len, Packet *p) { char *end; /* ptr to buffer end */ int i; /* counter */ int j; /* counter */ int dbuf_size; /* data buffer size */ int done; /* flag */ char *data; /* index pointer */ char *frame_ptr; /* we use 66 byte frames for a printed line */ char *d_ptr; /* data pointer into the frame */ char *c_ptr; /* char pointer into the frame */ char conv[] = "0123456789ABCDEF"; /* xlation lookup table */ int next_layer, ip_start, ip_ob_start, ip_ob_end, byte_pos; next_layer = ip_start = byte_pos = 0; ip_ob_start = ip_ob_end = -1; /* initialization */ done = 0; /* zero, print a and get out */ if(!len) { fputc('\n', fp); return; } if(start == NULL) { printf("Got NULL ptr in PrintNetData()\n"); return; } end = (char*) (start + (len - 1)); /* set the end of buffer ptr */ if(len > IP_MAXPACKET) { if (ScLogVerbose()) { printf("Got bogus buffer length (%d) for PrintNetData, defaulting to 16 bytes!\n", len); } if (ScVerboseByteDump()) { dbuf_size = (FRAME_SIZE + 8) + (FRAME_SIZE + 8) + 1; } else { dbuf_size = FRAME_SIZE + FRAME_SIZE + 1; } /* dbuf_size = 66 + 67; */ end = (char*) (start + 15); } else { if (ScVerboseByteDump()) { /* figure out how big the printout data buffer has to be */ dbuf_size = ((len / 16) * (FRAME_SIZE + 8)) + (FRAME_SIZE + 8) + 1; } else { /* figure out how big the printout data buffer has to be */ dbuf_size = ((len / 16) * FRAME_SIZE) + FRAME_SIZE + 1; } /* dbuf_size = ((len / 16) * 66) + 67; */ } /* generate the buffer */ if (data_dump_buffer == NULL) { AllocDumpBuf(); } if (data_dump_buffer == NULL) FatalError("Failed allocating %X bytes to data_dump_buffer!\n", data_dump_buffer_size); /* clean it out */ memset(data_dump_buffer, 0x20, dbuf_size); /* set the byte buffer pointer to step thru the data buffer */ data = (char*) start; /* set the frame pointer to the start of the printout buffer */ frame_ptr = data_dump_buffer; /* initialize counters and frame index pointers */ i = 0; j = 0; if(p && ScObfuscate() ) { next_layer = p->next_layer; for ( i = 0; i < next_layer; i++ ) { if ( p->layers[i].proto == PROTO_IP4 || p->layers[i].proto == PROTO_IP6 ) { if(p->layers[i].length && p->layers[i].start) break; } } ip_start = p->layers[i].start - start; if(ip_start > 0 ) { ip_ob_start = ip_start + 10; if(p->layers[i].proto == PROTO_IP4) ip_ob_end = ip_ob_start + 2 + 2*(sizeof(struct in_addr)); else ip_ob_end = ip_ob_start + 2 + 2*(sizeof(struct in6_addr)); } i=0; } /* loop thru the whole buffer */ while(!done) { if (ScVerboseByteDump()) { d_ptr = frame_ptr + 8; c_ptr = (frame_ptr + 8 + C_OFFSET); SnortSnprintf(frame_ptr, (data_dump_buffer + data_dump_buffer_size) - frame_ptr, "0x%04X: ", j); j += 16; } else { d_ptr = frame_ptr; c_ptr = (frame_ptr + C_OFFSET); } /* process 16 bytes per frame */ for(i = 0; i < 16; i++, byte_pos++) { if(ScObfuscate() && ((byte_pos >= ip_ob_start) && (byte_pos < ip_ob_end))) { *d_ptr = 'X'; d_ptr++; *d_ptr = 'X'; d_ptr++; *d_ptr = 0x20; d_ptr++; *c_ptr = 'X'; } else { /* * look up the ASCII value of the first nybble of the current * data buffer */ *d_ptr = conv[((*data & 0xFF) >> 4)]; d_ptr++; /* look up the second nybble */ *d_ptr = conv[((*data & 0xFF) & 0x0F)]; d_ptr++; /* put a space in between */ *d_ptr = 0x20; d_ptr++; /* print out the char equivalent */ if(*data > 0x1F && *data < 0x7F) *c_ptr = (char) (*data & 0xFF); else *c_ptr = 0x2E; } c_ptr++; /* increment the pointer or finish up */ if(data < end) data++; else { *c_ptr = '\n'; c_ptr++; *c_ptr = '\n'; c_ptr++; *c_ptr = 0; dump_size = (int) (c_ptr - data_dump_buffer); fwrite(data_dump_buffer, dump_size, 1, fp); //ClearDumpBuf(); return; } } *c_ptr = '\n'; if (ScVerboseByteDump()) { frame_ptr += (FRAME_SIZE + 8); } else { frame_ptr += FRAME_SIZE; } } //ClearDumpBuf(); } /* * Function: PrintCharData(FILE *, char *,int) * * Purpose: Dump the ASCII data from a packet * the left, decoded ASCII on the right. * * Arguments: fp => ptr to stream to print to * data => pointer to buffer data * data_len => length of data buffer * * Returns: void function */ void PrintCharData(FILE * fp, const char *data, int data_len) { int bytes_processed; /* count of bytes in the data buffer * processed so far */ int linecount = 0; /* number of lines in this dump */ const char *index; /* index pointer into the data buffer */ char *ddb_ptr; /* index pointer into the data_dump_buffer */ int size; /* if there's no data, return */ if(data == NULL) { return; } /* setup the pointers and counters */ bytes_processed = data_len; index = data; /* allocate a buffer to print the data to */ //data_dump_buffer = (char *) calloc(data_len + (data_len >> 6) + 2, sizeof(char)); if (data_dump_buffer == NULL) { AllocDumpBuf(); } size = (data_len + (data_len >> 6) + 2) * sizeof(char); /* Based on data_len < 65535, this should never happen, but check just in * case sizeof(char) is big or something. */ if (data_dump_buffer_size < size) { data_dump_buffer_size = size; ClearDumpBuf(); /* Reallocate for a bigger size. */ AllocDumpBuf(); } if (data_dump_buffer == NULL) FatalError("Failed allocating %X bytes to data_dump_buffer!\n", data_dump_buffer_size); /* clean it out */ memset(data_dump_buffer, 0x20, size); ddb_ptr = data_dump_buffer; /* loop thru the bytes in the data buffer */ while(bytes_processed) { if(*index > 0x1F && *index < 0x7F) { *ddb_ptr = *index; } else { *ddb_ptr = '.'; } if(++linecount == 64) { ddb_ptr++; *ddb_ptr = '\n'; linecount = 0; } ddb_ptr++; index++; bytes_processed--; } /* slam a \n on the back */ ddb_ptr++; *ddb_ptr = '\n'; ddb_ptr++; /* setup the globals */ dump_size = (int) (ddb_ptr - data_dump_buffer); fwrite(data_dump_buffer, dump_size, 1, fp); //ClearDumpBuf(); } static int PrintObfuscatedData(FILE* fp, Packet *p) { uint8_t *payload = NULL; uint16_t payload_len = 0; if (obApi->getObfuscatedPayload(p, &payload, (uint16_t *)&payload_len) != OB_RET_SUCCESS) { return -1; } /* dump the application layer data */ if (ScOutputAppData() && !ScVerboseByteDump()) { if (ScOutputCharData()) PrintCharData(fp, (char *)payload, payload_len); else PrintNetData(fp, payload, payload_len, NULL); } else if (ScVerboseByteDump()) { uint8_t buf[UINT16_MAX]; uint16_t dlen = p->data - p->pkt; int ret; ret = SafeMemcpy(buf, p->pkt, dlen, buf, buf + sizeof(buf)); if (ret != SAFEMEM_SUCCESS) { DEBUG_WRAP(DebugMessage(DEBUG_LOG, "%s: SafeMemcpy() Failed !!!", __FUNCTION__);) free(payload); return -1; } ret = SafeMemcpy(buf + dlen, payload, payload_len, buf, buf + sizeof(buf)); if (ret != SAFEMEM_SUCCESS) { DEBUG_WRAP(DebugMessage(DEBUG_LOG, "%s: SafeMemcpy() Failed !!!", __FUNCTION__);) free(payload); return -1; } PrintNetData(fp, buf, dlen + payload_len, NULL); } fprintf(fp, "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+" "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n"); p->packet_flags |= PKT_LOGGED; free(payload); return 0; } /* * Function: PrintIPPkt(FILE *, int, Packet *) * * Purpose: Dump the packet to the stream pointer * * Arguments: fp => pointer to print data to * type => packet protocol * p => pointer to decoded packet struct * * Returns: void function */ void PrintIPPkt(FILE * fp, int type, Packet * p) { char timestamp[TIMEBUF_SIZE]; if (p->packet_flags & PKT_LOGGED) return; DEBUG_WRAP(DebugMessage(DEBUG_LOG, "PrintIPPkt type = %d\n", type);); memset((char *) timestamp, 0, TIMEBUF_SIZE); ts_print((struct timeval *) & p->pkth->ts, timestamp); /* dump the timestamp */ fwrite(timestamp, strlen(timestamp), 1, fp); /* dump the ethernet header if we're doing that sort of thing */ if(ScOutputDataLink()) { Print2ndHeader(fp, p); #ifdef MPLS if(p->mpls) { PrintMPLSHeader(fp, p); } #endif #ifdef GRE if (p->outer_iph) { PrintOuterIPHeader(fp, p); if (p->greh) PrintGREHeader(fp, p); } #endif } PrintIPHeader(fp, p); /* if this isn't a fragment, print the other header info */ if(!p->frag_flag) { switch(GET_IPH_PROTO(p)) { case IPPROTO_TCP: if(p->tcph != NULL) { PrintTCPHeader(fp, p); } else { PrintNetData(fp, (u_char *) (u_char *)p->iph + (GET_IPH_HLEN(p) << 2), GET_IP_PAYLEN(p), NULL); } break; case IPPROTO_UDP: if(p->udph != NULL) { PrintUDPHeader(fp, p); } else { PrintNetData(fp, (u_char *) (u_char *)p->iph + (GET_IPH_HLEN(p) << 2), GET_IP_PAYLEN(p), NULL); } break; case IPPROTO_ICMP: if(p->icmph != NULL) { PrintICMPHeader(fp, p); } else { PrintNetData(fp, (u_char *) ((u_char *)p->iph + (GET_IPH_HLEN(p) << 2)), GET_IP_PAYLEN(p), NULL); } break; default: break; } } if ((p->dsize > 0) && obApi->payloadObfuscationRequired(p) && (PrintObfuscatedData(fp, p) == 0)) { return; } /* dump the application layer data */ if (ScOutputAppData() && !ScVerboseByteDump()) { if (ScOutputCharData()) { PrintCharData(fp, (char*) p->data, p->dsize); if(!IsJSNormData(p->ssnptr)) { fprintf(fp, "%s\n", "Normalized JavaScript for this packet"); PrintCharData(fp, (const char*)file_data_ptr.data, file_data_ptr.len); } else if(!IsGzipData(p->ssnptr)) { fprintf(fp, "%s\n", "Decompressed Data for this packet"); PrintCharData(fp, (const char*)file_data_ptr.data, file_data_ptr.len); } } else { PrintNetData(fp, p->data, p->dsize, NULL); if(!IsJSNormData(p->ssnptr)) { fprintf(fp, "%s\n", "Normalized JavaScript for this packet"); PrintNetData(fp, file_data_ptr.data, file_data_ptr.len, NULL); } else if(!IsGzipData(p->ssnptr)) { fprintf(fp, "%s\n", "Decompressed Data for this packet"); PrintNetData(fp, file_data_ptr.data, file_data_ptr.len, NULL); } } } else if (ScVerboseByteDump()) { PrintNetData(fp, p->pkt, p->pkth->caplen, p); } fprintf(fp, "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+" "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n"); p->packet_flags |= PKT_LOGGED; } /**************************************************************************** * * Function: OpenAlertFile(char *) * * Purpose: Set up the file pointer/file for alerting * * Arguments: filearg => the filename to open * * Returns: file handle * ***************************************************************************/ FILE *OpenAlertFile(const char *filearg) { char filename[STD_BUF+1]; FILE *file; char suffix[5]; /* filename suffix */ #ifdef WIN32 SnortStrncpy(suffix, ".ids", sizeof(suffix)); #else suffix[0] = '\0'; #endif if(filearg == NULL) { if (snort_conf->alert_file == NULL) { if(!ScDaemonMode()) SnortSnprintf(filename, STD_BUF, "%s/alert%s", snort_conf->log_dir, suffix); else SnortSnprintf(filename, STD_BUF, "%s/%s", snort_conf->log_dir, DEFAULT_DAEMON_ALERT_FILE); } else { SnortSnprintf(filename, STD_BUF, "%s/%s%s", snort_conf->log_dir, snort_conf->alert_file, suffix); } } else { SnortSnprintf(filename, STD_BUF, "%s", filearg); } DEBUG_WRAP(DebugMessage(DEBUG_INIT,"Opening alert file: %s\n", filename);); if((file = fopen(filename, "a")) == NULL) { FatalError("OpenAlertFile() => fopen() alert file %s: %s\n", filename, strerror(errno)); } #ifdef WIN32 /* Do not buffer in WIN32 */ setvbuf(file, (char *) NULL, _IONBF, (size_t) 0); #else setvbuf(file, (char *) NULL, _IOLBF, (size_t) 0); #endif return file; } /**************************************************************************** * * Function: RollAlertFile(char *) * * Purpose: rename existing alert file with by appending time to name * * Arguments: filearg => the filename to rename (same as for OpenAlertFile()) * * Returns: 0=success, else errno * ***************************************************************************/ int RollAlertFile(const char *filearg) { char oldname[STD_BUF+1]; char newname[STD_BUF+1]; char suffix[5]; /* filename suffix */ time_t now = time(NULL); #ifdef WIN32 SnortStrncpy(suffix, ".ids", sizeof(suffix)); #else suffix[0] = '\0'; #endif if(filearg == NULL) { if(!ScDaemonMode()) SnortSnprintf(oldname, STD_BUF, "%s/alert%s", snort_conf->log_dir, suffix); else SnortSnprintf(oldname, STD_BUF, "%s/%s", snort_conf->log_dir, DEFAULT_DAEMON_ALERT_FILE); } else { SnortSnprintf(oldname, STD_BUF, "%s", filearg); } SnortSnprintf(newname, sizeof(newname)-1, "%s.%lu", oldname, (unsigned long)now); DEBUG_WRAP(DebugMessage(DEBUG_INIT,"Rolling alert file: %s\n", newname);); if ( rename(oldname, newname) ) { FatalError("RollAlertFile() => rename(%s, %s) = %s\n", oldname, newname, strerror(errno)); } return errno; } /* * * Function: AllocDumpBuf() * * Purpose: Allocate the buffer that PrintNetData() uses * * Arguments: None. * * Returns: void function * */ void AllocDumpBuf(void) { if (data_dump_buffer_size == 0) { if (ScVerboseByteDump()) { data_dump_buffer_size = (((IP_MAXPACKET+1)/16) * (FRAME_SIZE + 8)) + (FRAME_SIZE + 8) + 1; } else { data_dump_buffer_size = ((IP_MAXPACKET+1)/16) * FRAME_SIZE + FRAME_SIZE + 1; } } data_dump_buffer = (char *)calloc( 1,data_dump_buffer_size ); /* make sure it got allocated properly */ if(data_dump_buffer == NULL) { FatalError("AllocDumpBuf(): Failed allocating %X bytes!\n", data_dump_buffer_size); } } /* * * Function: ClearDumpBuf() * * Purpose: Clear out the buffer that PrintNetData() generates * * Arguments: None. * * Returns: void function * */ void ClearDumpBuf(void) { if(data_dump_buffer) free(data_dump_buffer); else return; data_dump_buffer = NULL; dump_size = 0; } /**************************************************************************** * * Function: NoAlert(Packet *, char *) * * Purpose: Don't alert at all * * Arguments: p => pointer to the packet data struct * msg => the message to not print in the alert * * Returns: void function * ***************************************************************************/ void NoAlert(Packet * p, char *msg, void *arg, Event *event) { return; } /**************************************************************************** * * Function: NoLog(Packet *) * * Purpose: Don't log anything * * Arguments: p => packet to not log * * Returns: void function * ***************************************************************************/ void NoLog(Packet * p, char *msg, void *arg, Event *event) { return; } /**************************************************************************** * * Function: Print2ndHeader(FILE *, Packet p) * * Purpose: Print2ndHeader -- prints second layber header info. * * Arguments: fp => file stream to print to * * Returns: void function * ***************************************************************************/ void Print2ndHeader(FILE * fp, Packet * p) { switch(DAQ_GetBaseProtocol()) { case DLT_EN10MB: /* Ethernet */ if(p && p->eh) PrintEthHeader(fp, p); break; #ifndef NO_NON_ETHER_DECODER #ifdef DLT_IEEE802_11 case DLT_IEEE802_11: if(p && p->wifih) PrintWifiHeader(fp, p); break; #endif case DLT_IEEE802: /* Token Ring */ if(p && p->trh) PrintTrHeader(fp, p); break; #ifdef DLT_LINUX_SLL case DLT_LINUX_SLL: if (p && p->sllh) PrintSLLHeader(fp, p); /* Linux cooked sockets */ break; #endif #endif // NO_NON_ETHER_DECODER default: if (ScLogVerbose()) { ErrorMessage("Datalink %i type 2nd layer display is not " "supported\n", DAQ_GetBaseProtocol()); } } } #ifndef NO_NON_ETHER_DECODER /**************************************************************************** * * Function: PrintTrHeader(FILE *, Packet p) & * Purpose: Print the packet TokenRing header to the specified stream * * Arguments: fp => file stream to print to * * Returns: void function ***************************************************************************/ void PrintTrHeader(FILE * fp, Packet * p) { fprintf(fp, "%X:%X:%X:%X:%X:%X -> ", p->trh->saddr[0], p->trh->saddr[1], p->trh->saddr[2], p->trh->saddr[3], p->trh->saddr[4], p->trh->saddr[5]); fprintf(fp, "%X:%X:%X:%X:%X:%X\n", p->trh->daddr[0], p->trh->daddr[1], p->trh->daddr[2], p->trh->daddr[3], p->trh->daddr[4], p->trh->daddr[5]); fprintf(fp, "access control:0x%X frame control:0x%X\n", p->trh->ac, p->trh->fc); if(!p->trhllc) return; fprintf(fp, "DSAP: 0x%X SSAP 0x%X protoID: %X%X%X Ethertype: %X\n", p->trhllc->dsap, p->trhllc->ssap, p->trhllc->protid[0], p->trhllc->protid[1], p->trhllc->protid[2], p->trhllc->ethertype); if(p->trhmr) { fprintf(fp, "RIF structure is present:\n"); fprintf(fp, "bcast: 0x%X length: 0x%X direction: 0x%X largest" "fr. size: 0x%X res: 0x%X\n", TRH_MR_BCAST(p->trhmr), TRH_MR_LEN(p->trhmr), TRH_MR_DIR(p->trhmr), TRH_MR_LF(p->trhmr), TRH_MR_RES(p->trhmr)); fprintf(fp, "rseg -> %X:%X:%X:%X:%X:%X:%X:%X\n", p->trhmr->rseg[0], p->trhmr->rseg[1], p->trhmr->rseg[2], p->trhmr->rseg[3], p->trhmr->rseg[4], p->trhmr->rseg[5], p->trhmr->rseg[6], p->trhmr->rseg[7]); } } #endif // NO_NON_ETHER_DECODER /**************************************************************************** * * Function: PrintEthHeader(FILE *) * * Purpose: Print the packet Ethernet header to the specified stream * * Arguments: fp => file stream to print to * * Returns: void function * ***************************************************************************/ void PrintEthHeader(FILE * fp, Packet * p) { /* src addr */ fprintf(fp, "%02X:%02X:%02X:%02X:%02X:%02X -> ", p->eh->ether_src[0], p->eh->ether_src[1], p->eh->ether_src[2], p->eh->ether_src[3], p->eh->ether_src[4], p->eh->ether_src[5]); /* dest addr */ fprintf(fp, "%02X:%02X:%02X:%02X:%02X:%02X ", p->eh->ether_dst[0], p->eh->ether_dst[1], p->eh->ether_dst[2], p->eh->ether_dst[3], p->eh->ether_dst[4], p->eh->ether_dst[5]); /* protocol and pkt size */ fprintf(fp, "type:0x%X len:0x%X\n", ntohs(p->eh->ether_type), p->pkth->pktlen); } #ifdef MPLS void PrintMPLSHeader(FILE* log, Packet* p) { fprintf(log,"label:0x%05X exp:0x%X bos:0x%X ttl:0x%X\n", p->mplsHdr.label, p->mplsHdr.exp, p->mplsHdr.bos, p->mplsHdr.ttl); } #endif #ifdef GRE void PrintGREHeader(FILE *log, Packet *p) { if (p->greh == NULL) return; fprintf(log, "GRE version:%u flags:0x%02X ether-type:0x%04X\n", GRE_VERSION(p->greh), p->greh->flags, GRE_PROTO(p->greh)); } #endif #ifndef NO_NON_ETHER_DECODER /**************************************************************************** * * Function: PrintSLLHeader(FILE *) * * Purpose: Print the packet SLL (fake) header to the specified stream (piece * partly is borrowed from tcpdump :)) * * Arguments: fp => file stream to print to * * Returns: void function * ***************************************************************************/ void PrintSLLHeader(FILE * fp, Packet * p) { switch (ntohs(p->sllh->sll_pkttype)) { case LINUX_SLL_HOST: (void)fprintf(fp, "< "); break; case LINUX_SLL_BROADCAST: (void)fprintf(fp, "B "); break; case LINUX_SLL_MULTICAST: (void)fprintf(fp, "M "); break; case LINUX_SLL_OTHERHOST: (void)fprintf(fp, "P "); break; case LINUX_SLL_OUTGOING: (void)fprintf(fp, "> "); break; default: (void)fprintf(fp, "? "); break; } /* mac addr */ fprintf(fp, "l/l len: %i l/l type: 0x%X %02X:%02X:%02X:%02X:%02X:%02X\n", htons(p->sllh->sll_halen), ntohs(p->sllh->sll_hatype), p->sllh->sll_addr[0], p->sllh->sll_addr[1], p->sllh->sll_addr[2], p->sllh->sll_addr[3], p->sllh->sll_addr[4], p->sllh->sll_addr[5]); /* protocol and pkt size */ fprintf(fp, "pkt type:0x%X proto: 0x%X len:0x%X\n", ntohs(p->sllh->sll_pkttype), ntohs(p->sllh->sll_protocol), p->pkth->pktlen); } void PrintArpHeader(FILE * fp, Packet * p) { // XXX-IPv6 "NOT YET IMPLEMENTED - printing ARP header" } #endif // NO_NON_ETHER_DECODER /**************************************************************************** * * Function: PrintIPHeader(FILE *) * * Purpose: Dump the IP header info to the specified stream * * Arguments: fp => stream to print to * * Returns: void function * ***************************************************************************/ void PrintIPHeader(FILE * fp, Packet * p) { if(!IPH_IS_VALID(p)) { fprintf(fp, "IP header truncated\n"); return; } PrintIpAddrs(fp, p); if (!ScOutputDataLink()) { fputc('\n', fp); } else { fputc(' ', fp); } fprintf(fp, "%s TTL:%u TOS:0x%X ID:%u IpLen:%u DgmLen:%u", protocol_names[GET_IPH_PROTO(p)], GET_IPH_TTL(p), GET_IPH_TOS(p), IS_IP6(p) ? ntohl(GET_IPH_ID(p)) : ntohs((uint16_t)GET_IPH_ID(p)), GET_IPH_HLEN(p) << 2, GET_IP_DGMLEN(p)); /* print the reserved bit if it's set */ if((uint8_t)((ntohs(GET_IPH_OFF(p)) & 0x8000) >> 15) == 1) fprintf(fp, " RB"); /* printf more frags/don't frag bits */ if((uint8_t)((ntohs(GET_IPH_OFF(p)) & 0x4000) >> 14) == 1) fprintf(fp, " DF"); if((uint8_t)((ntohs(GET_IPH_OFF(p)) & 0x2000) >> 13) == 1) fprintf(fp, " MF"); fputc('\n', fp); /* print IP options */ if(p->ip_option_count != 0) { PrintIpOptions(fp, p); } /* print fragment info if necessary */ if(p->frag_flag) { fprintf(fp, "Frag Offset: 0x%04X Frag Size: 0x%04X\n", (p->frag_offset & 0x1FFF), GET_IP_PAYLEN(p)); } } #ifdef GRE void PrintOuterIPHeader(FILE *fp, Packet *p) { int save_family = p->family; IPH_API *save_api = p->iph_api; const IPHdr *save_iph = p->iph; uint8_t save_ip_option_count = p->ip_option_count; IP4Hdr *save_ip4h = p->ip4h; IP6Hdr *save_ip6h = p->ip6h; uint8_t save_frag_flag = p->frag_flag; uint16_t save_sp = p->sp, save_dp = p->dp; p->family = p->outer_family; p->iph_api = p->outer_iph_api; p->iph = p->outer_iph; p->ip_option_count = 0; p->ip4h = &p->outer_ip4h; p->ip6h = &p->outer_ip6h; p->frag_flag = 0; if (p->proto_bits & PROTO_BIT__TEREDO) { if (p->outer_udph) { p->sp = ntohs(p->outer_udph->uh_sport); p->dp = ntohs(p->outer_udph->uh_dport); } else { p->sp = ntohs(p->udph->uh_sport); p->dp = ntohs(p->udph->uh_dport); } } PrintIPHeader(fp, p); p->family = save_family; p->iph_api = save_api; p->iph = save_iph; p->ip_option_count = save_ip_option_count; p->ip4h = save_ip4h; p->ip6h = save_ip6h; p->frag_flag = save_frag_flag; if (p->proto_bits & PROTO_BIT__TEREDO) { p->sp = save_sp; p->dp = save_dp; } } #endif /**************************************************************************** * * Function: PrintTCPHeader(FILE *) * * Purpose: Dump the TCP header info to the specified stream * * Arguments: fp => file stream to print data to * * Returns: void function * ***************************************************************************/ void PrintTCPHeader(FILE * fp, Packet * p) { char tcpFlags[9]; if(p->tcph == NULL) { fprintf(fp, "TCP header truncated\n"); return; } /* print TCP flags */ CreateTCPFlagString(p, tcpFlags); fwrite(tcpFlags, 8, 1, fp); /* We don't care about the NULL */ /* print other TCP info */ fprintf(fp, " Seq: 0x%lX Ack: 0x%lX Win: 0x%X TcpLen: %d", (u_long) ntohl(p->tcph->th_seq), (u_long) ntohl(p->tcph->th_ack), ntohs(p->tcph->th_win), TCP_OFFSET(p->tcph) << 2); if((p->tcph->th_flags & TH_URG) != 0) { fprintf(fp, " UrgPtr: 0x%X\n", (uint16_t) ntohs(p->tcph->th_urp)); } else { fputc((int) '\n', fp); } /* dump the TCP options */ if(p->tcp_option_count != 0) { PrintTcpOptions(fp, p); } } /* Input is packet and an nine-byte (including NULL) character array. Results * are put into the character array. */ void CreateTCPFlagString(Packet * p, char *flagBuffer) { /* parse TCP flags */ *flagBuffer++ = (char) ((p->tcph->th_flags & TH_RES1) ? '1' : '*'); *flagBuffer++ = (char) ((p->tcph->th_flags & TH_RES2) ? '2' : '*'); *flagBuffer++ = (char) ((p->tcph->th_flags & TH_URG) ? 'U' : '*'); *flagBuffer++ = (char) ((p->tcph->th_flags & TH_ACK) ? 'A' : '*'); *flagBuffer++ = (char) ((p->tcph->th_flags & TH_PUSH) ? 'P' : '*'); *flagBuffer++ = (char) ((p->tcph->th_flags & TH_RST) ? 'R' : '*'); *flagBuffer++ = (char) ((p->tcph->th_flags & TH_SYN) ? 'S' : '*'); *flagBuffer++ = (char) ((p->tcph->th_flags & TH_FIN) ? 'F' : '*'); *flagBuffer = '\0'; } /**************************************************************************** * * Function: PrintUDPHeader(FILE *) * * Purpose: Dump the UDP header to the specified file stream * * Arguments: fp => file stream * * Returns: void function * ***************************************************************************/ void PrintUDPHeader(FILE * fp, Packet * p) { if(p->udph == NULL) { fprintf(fp, "UDP header truncated\n"); return; } /* not much to do here... */ fprintf(fp, "Len: %d\n", ntohs(p->udph->uh_len) - UDP_HEADER_LEN); } /**************************************************************************** * * Function: PrintICMPHeader(FILE *) * * Purpose: Print ICMP header * * Arguments: fp => file stream * * Returns: void function * ***************************************************************************/ void PrintICMPHeader(FILE * fp, Packet * p) { /* 32 digits plus 7 colons and a NULL byte */ char buf[8*4 + 7 + 1]; if(p->icmph == NULL) { fprintf(fp, "ICMP header truncated\n"); return; } fprintf(fp, "Type:%d Code:%d ", p->icmph->type, p->icmph->code); switch(p->icmph->type) { case ICMP_ECHOREPLY: fprintf(fp, "ID:%d Seq:%d ", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); fwrite("ECHO REPLY", 10, 1, fp); break; case ICMP_DEST_UNREACH: fwrite("DESTINATION UNREACHABLE: ", 25, 1, fp); switch(p->icmph->code) { case ICMP_NET_UNREACH: fwrite("NET UNREACHABLE", 15, 1, fp); break; case ICMP_HOST_UNREACH: fwrite("HOST UNREACHABLE", 16, 1, fp); break; case ICMP_PROT_UNREACH: fwrite("PROTOCOL UNREACHABLE", 20, 1, fp); break; case ICMP_PORT_UNREACH: fwrite("PORT UNREACHABLE", 16, 1, fp); break; case ICMP_FRAG_NEEDED: fprintf(fp, "FRAGMENTATION NEEDED, DF SET\n" "NEXT LINK MTU: %u", ntohs(p->icmph->s_icmp_nextmtu)); break; case ICMP_SR_FAILED: fwrite("SOURCE ROUTE FAILED", 19, 1, fp); break; case ICMP_NET_UNKNOWN: fwrite("NET UNKNOWN", 11, 1, fp); break; case ICMP_HOST_UNKNOWN: fwrite("HOST UNKNOWN", 12, 1, fp); break; case ICMP_HOST_ISOLATED: fwrite("HOST ISOLATED", 13, 1, fp); break; case ICMP_PKT_FILTERED_NET: fwrite("ADMINISTRATIVELY PROHIBITED NETWORK FILTERED", 44, 1, fp); break; case ICMP_PKT_FILTERED_HOST: fwrite("ADMINISTRATIVELY PROHIBITED HOST FILTERED", 41, 1, fp); break; case ICMP_NET_UNR_TOS: fwrite("NET UNREACHABLE FOR TOS", 23, 1, fp); break; case ICMP_HOST_UNR_TOS: fwrite("HOST UNREACHABLE FOR TOS", 24, 1, fp); break; case ICMP_PKT_FILTERED: fwrite("ADMINISTRATIVELY PROHIBITED,\nPACKET FILTERED", 44, 1, fp); break; case ICMP_PREC_VIOLATION: fwrite("PREC VIOLATION", 14, 1, fp); break; case ICMP_PREC_CUTOFF: fwrite("PREC CUTOFF", 12, 1, fp); break; default: fwrite("UNKNOWN", 7, 1, fp); break; } PrintICMPEmbeddedIP(fp, p); break; case ICMP_SOURCE_QUENCH: fwrite("SOURCE QUENCH", 13, 1, fp); PrintICMPEmbeddedIP(fp, p); break; case ICMP_REDIRECT: fwrite("REDIRECT", 8, 1, fp); switch(p->icmph->code) { case ICMP_REDIR_NET: fwrite(" NET", 4, 1, fp); break; case ICMP_REDIR_HOST: fwrite(" HOST", 5, 1, fp); break; case ICMP_REDIR_TOS_NET: fwrite(" TOS NET", 8, 1, fp); break; case ICMP_REDIR_TOS_HOST: fwrite(" TOS HOST", 9, 1, fp); break; } /* written this way since inet_ntoa was typedef'ed to use sfip_ntoa * which requires sfcidr_t instead of inaddr's. This call to inet_ntoa * is a rare case that doesn't use sfcidr_t's. */ // XXX-IPv6 NOT YET IMPLEMENTED - IPV6 addresses technically not supported - need to change ICMP header sfip_raw_ntop(AF_INET, (void *)&p->icmph->s_icmp_gwaddr, buf, sizeof(buf)); fprintf(fp, " NEW GW: %s", buf); PrintICMPEmbeddedIP(fp, p); break; case ICMP_ECHO: fprintf(fp, "ID:%d Seq:%d ", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); fwrite("ECHO", 4, 1, fp); break; case ICMP_ROUTER_ADVERTISE: fprintf(fp, "ROUTER ADVERTISMENT: " "Num addrs: %d Addr entry size: %d Lifetime: %u", p->icmph->s_icmp_num_addrs, p->icmph->s_icmp_wpa, ntohs(p->icmph->s_icmp_lifetime)); break; case ICMP_ROUTER_SOLICIT: fwrite("ROUTER SOLICITATION", 19, 1, fp); break; case ICMP_TIME_EXCEEDED: fwrite("TTL EXCEEDED", 12, 1, fp); switch(p->icmph->code) { case ICMP_TIMEOUT_TRANSIT: fwrite(" IN TRANSIT", 11, 1, fp); break; case ICMP_TIMEOUT_REASSY: fwrite(" TIME EXCEEDED IN FRAG REASSEMBLY", 33, 1, fp); break; } PrintICMPEmbeddedIP(fp, p); break; case ICMP_PARAMETERPROB: fwrite("PARAMETER PROBLEM", 17, 1, fp); switch(p->icmph->code) { case ICMP_PARAM_BADIPHDR: fprintf(fp, ": BAD IP HEADER BYTE %u", p->icmph->s_icmp_pptr); break; case ICMP_PARAM_OPTMISSING: fwrite(": OPTION MISSING", 16, 1, fp); break; case ICMP_PARAM_BAD_LENGTH: fwrite(": BAD LENGTH", 12, 1, fp); break; } PrintICMPEmbeddedIP(fp, p); break; case ICMP_TIMESTAMP: fprintf(fp, "ID: %u Seq: %u TIMESTAMP REQUEST", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_TIMESTAMPREPLY: fprintf(fp, "ID: %u Seq: %u TIMESTAMP REPLY:\n" "Orig: %u Rtime: %u Ttime: %u", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq), p->icmph->s_icmp_otime, p->icmph->s_icmp_rtime, p->icmph->s_icmp_ttime); break; case ICMP_INFO_REQUEST: fprintf(fp, "ID: %u Seq: %u INFO REQUEST", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_INFO_REPLY: fprintf(fp, "ID: %u Seq: %u INFO REPLY", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_ADDRESS: fprintf(fp, "ID: %u Seq: %u ADDRESS REQUEST", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_ADDRESSREPLY: fprintf(fp, "ID: %u Seq: %u ADDRESS REPLY: 0x%08X", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq), (u_int) ntohl(p->icmph->s_icmp_mask)); break; default: fwrite("UNKNOWN", 7, 1, fp); break; } putc('\n', fp); } /**************************************************************************** * * Function: PrintICMPEmbeddedIP(FILE *, Packet *) * * Purpose: Prints the original/encapsulated IP header + 64 bits of the * original IP payload in an ICMP packet * * Arguments: fp => file stream * p => packet struct * * Returns: void function * ***************************************************************************/ void PrintICMPEmbeddedIP(FILE *fp, Packet *p) { Packet op; Packet *orig_p; uint32_t orig_ip_hlen; if (fp == NULL || p == NULL) return; memset((char *) &op, 0, sizeof(Packet)); orig_p = &op; orig_p->iph = p->orig_iph; orig_p->tcph = p->orig_tcph; orig_p->udph = p->orig_udph; orig_p->sp = p->orig_sp; orig_p->dp = p->orig_dp; orig_p->icmph = p->orig_icmph; orig_p->iph_api = p->orig_iph_api; orig_p->ip4h = p->orig_ip4h; orig_p->ip6h = p->orig_ip6h; orig_p->family = p->orig_family; if(orig_p->iph != NULL) { fprintf(fp, "\n** ORIGINAL DATAGRAM DUMP:\n"); PrintIPHeader(fp, orig_p); orig_ip_hlen = IP_HLEN(p->orig_iph) << 2; switch(GET_IPH_PROTO(orig_p)) { case IPPROTO_TCP: if(orig_p->tcph != NULL) fprintf(fp, "Seq: 0x%lX\n", (u_long)ntohl(orig_p->tcph->th_seq)); break; case IPPROTO_UDP: if(orig_p->udph != NULL) fprintf(fp, "Len: %d Csum: %d\n", ntohs(orig_p->udph->uh_len) - UDP_HEADER_LEN, ntohs(orig_p->udph->uh_chk)); break; case IPPROTO_ICMP: if(orig_p->icmph != NULL) PrintEmbeddedICMPHeader(fp, orig_p->icmph); break; default: fprintf(fp, "Protocol: 0x%X (unknown or " "header truncated)", GET_IPH_PROTO(orig_p)); break; } /* switch */ /* if more than 8 bytes of original IP payload sent */ if (p->dsize - orig_ip_hlen > 8) { fprintf(fp, "(%d more bytes of original packet)\n", p->dsize - orig_ip_hlen - 8); } fprintf(fp, "** END OF DUMP"); } else { fprintf(fp, "\nORIGINAL DATAGRAM TRUNCATED"); } } /**************************************************************************** * * Function: PrintEmbeddedICMPHeader(FILE *, ICMPHdr *) * * Purpose: Prints the 64 bits of the original IP payload in an ICMP packet * that requires it * * Arguments: fp => file stream * icmph => ICMPHdr struct pointing to original ICMP * * Returns: void function * ***************************************************************************/ void PrintEmbeddedICMPHeader(FILE *fp, const ICMPHdr *icmph) { if (fp == NULL || icmph == NULL) return; fprintf(fp, "Type: %d Code: %d Csum: %u", icmph->type, icmph->code, ntohs(icmph->csum)); switch (icmph->type) { case ICMP_DEST_UNREACH: case ICMP_TIME_EXCEEDED: case ICMP_SOURCE_QUENCH: break; case ICMP_PARAMETERPROB: if (icmph->code == 0) fprintf(fp, " Ptr: %u", icmph->s_icmp_pptr); break; case ICMP_REDIRECT: // XXX-IPv6 "NOT YET IMPLEMENTED - ICMP printing" break; case ICMP_ECHO: case ICMP_ECHOREPLY: case ICMP_TIMESTAMP: case ICMP_TIMESTAMPREPLY: case ICMP_INFO_REQUEST: case ICMP_INFO_REPLY: case ICMP_ADDRESS: case ICMP_ADDRESSREPLY: fprintf(fp, " Id: %u SeqNo: %u", ntohs(icmph->s_icmp_id), ntohs(icmph->s_icmp_seq)); break; case ICMP_ROUTER_ADVERTISE: fprintf(fp, " Addrs: %u Size: %u Lifetime: %u", icmph->s_icmp_num_addrs, icmph->s_icmp_wpa, ntohs(icmph->s_icmp_lifetime)); break; default: break; } fprintf(fp, "\n"); return; } void PrintIpOptions(FILE * fp, Packet * p) { int i; int j; u_long init_offset; u_long print_offset; init_offset = ftell(fp); if(!p->ip_option_count || p->ip_option_count > 40) return; fprintf(fp, "IP Options (%d) => ", p->ip_option_count); for(i = 0; i < (int) p->ip_option_count; i++) { print_offset = ftell(fp); if((print_offset - init_offset) > 60) { fwrite("\nIP Options => ", 15, 1, fp); init_offset = ftell(fp); } switch(p->ip_options[i].code) { case IPOPT_RR: fwrite("RR ", 3, 1, fp); break; case IPOPT_EOL: fwrite("EOL ", 4, 1, fp); break; case IPOPT_NOP: fwrite("NOP ", 4, 1, fp); break; case IPOPT_TS: fwrite("TS ", 3, 1, fp); break; case IPOPT_ESEC: fwrite("ESEC ", 5, 1, fp); break; case IPOPT_SECURITY: fwrite("SEC ", 4, 1, fp); break; case IPOPT_LSRR: case IPOPT_LSRR_E: fwrite("LSRR ", 5, 1, fp); break; case IPOPT_SATID: fwrite("SID ", 4, 1, fp); break; case IPOPT_SSRR: fwrite("SSRR ", 5, 1, fp); break; case IPOPT_RTRALT: fwrite("RTRALT ", 7, 1, fp); break; default: fprintf(fp, "Opt %d: ", p->ip_options[i].code); if(p->ip_options[i].len) { for(j = 0; j < p->ip_options[i].len; j++) { if (p->ip_options[i].data) fprintf(fp, "%02X", p->ip_options[i].data[j]); else fprintf(fp, "%02X", 0); if((j % 2) == 0) fprintf(fp, " "); } } break; } } fwrite("\n", 1, 1, fp); } void PrintTcpOptions(FILE * fp, Packet * p) { int i; int j; u_char tmp[5]; u_long init_offset; u_long print_offset; init_offset = ftell(fp); fprintf(fp, "TCP Options (%d) => ", p->tcp_option_count); if(p->tcp_option_count > 40 || !p->tcp_option_count) return; for(i = 0; i < (int) p->tcp_option_count; i++) { print_offset = ftell(fp); if((print_offset - init_offset) > 60) { fwrite("\nTCP Options => ", 16, 1, fp); init_offset = ftell(fp); } switch(p->tcp_options[i].code) { case TCPOPT_MAXSEG: memset((char *) tmp, 0, 5); fwrite("MSS: ", 5, 1, fp); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 2); fprintf(fp, "%u ", EXTRACT_16BITS(tmp)); break; case TCPOPT_EOL: fwrite("EOL ", 4, 1, fp); break; case TCPOPT_NOP: fwrite("NOP ", 4, 1, fp); break; case TCPOPT_WSCALE: if (p->tcp_options[i].data) fprintf(fp, "WS: %u ", p->tcp_options[i].data[0]); else fprintf(fp, "WS: %u ", 0); break; case TCPOPT_SACK: memset((char *) tmp, 0, 5); if (p->tcp_options[i].data && (p->tcp_options[i].len >= 2)) memcpy(tmp, p->tcp_options[i].data, 2); fprintf(fp, "Sack: %u@", EXTRACT_16BITS(tmp)); memset((char *) tmp, 0, 5); if (p->tcp_options[i].data && (p->tcp_options[i].len >= 4)) memcpy(tmp, (p->tcp_options[i].data) + 2, 2); fprintf(fp, "%u ", EXTRACT_16BITS(tmp)); break; case TCPOPT_SACKOK: fwrite("SackOK ", 7, 1, fp); break; case TCPOPT_TFO: fwrite("TFO ", 4, 1, fp); break; case TCPOPT_ECHO: memset((char *) tmp, 0, 5); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); fprintf(fp, "Echo: %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_ECHOREPLY: memset((char *) tmp, 0, 5); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); fprintf(fp, "Echo Rep: %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_TIMESTAMP: memset((char *) tmp, 0, 5); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); fprintf(fp, "TS: %u ", EXTRACT_32BITS(tmp)); memset((char *) tmp, 0, 5); if (p->tcp_options[i].data) memcpy(tmp, (p->tcp_options[i].data) + 4, 4); fprintf(fp, "%u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_CC: memset((char *) tmp, 0, 5); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); fprintf(fp, "CC %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_CCNEW: memset((char *) tmp, 0, 5); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); fprintf(fp, "CCNEW: %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_CCECHO: memset((char *) tmp, 0, 5); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); fprintf(fp, "CCECHO: %u ", EXTRACT_32BITS(tmp)); break; default: if(p->tcp_options[i].len) { fprintf(fp, "Opt %d (%d): ", p->tcp_options[i].code, (int) p->tcp_options[i].len); for(j = 0; j < p->tcp_options[i].len; j++) { if (p->tcp_options[i].data) fprintf(fp, "%02X", p->tcp_options[i].data[j]); else fprintf(fp, "%02X", 0); if ((j + 1) % 2 == 0) fprintf(fp, " "); } fprintf(fp, " "); } else { fprintf(fp, "Opt %d ", p->tcp_options[i].code); } break; } } fwrite("\n", 1, 1, fp); } /* * Function: PrintPriorityData(FILE *) * * Purpose: Prints out priority data associated with an alert * * Arguments: fp => file descriptor to write the data to * do_newline => tack a \n to the end of the line or not (bool) * * Returns: void function */ void PrintPriorityData(FILE *fp, int do_newline) { if (otn_tmp == NULL) return; if ((otn_tmp->sigInfo.classType != NULL) && (otn_tmp->sigInfo.classType->name != NULL)) { fprintf(fp, "[Classification: %s] ", otn_tmp->sigInfo.classType->name); } fprintf(fp, "[Priority: %d] ", otn_tmp->sigInfo.priority); if (do_newline) fprintf(fp, "\n"); } /* * Function: PrintXrefs(FILE *) * * Purpose: Prints out cross reference data associated with an alert * * Arguments: fp => file descriptor to write the data to * do_newline => tack a \n to the end of the line or not (bool) * * Returns: void function */ void PrintXrefs(FILE *fp, int do_newline) { ReferenceNode *refNode = NULL; if(otn_tmp) { refNode = otn_tmp->sigInfo.refs; while(refNode != NULL) { FPrintReference(fp, refNode); refNode = refNode->next; /* on the last loop through, print a newline in Full mode */ if(do_newline && (refNode == NULL)) fprintf(fp, "\n"); } } } /* This function name is being altered for Win32 because it conflicts with a Win32 SDK function name. However calls to this function from within Snort do not need to change because SetEvent() is defined in log.h to evaluate to SnortSetEvent() on Win32 compiles. */ #ifndef WIN32 void SetEvent #else void SnortSetEvent #endif (Event *event, uint32_t generator, uint32_t id, uint32_t rev, #if !defined(FEAT_OPEN_APPID) uint32_t classification, uint32_t priority, uint32_t event_ref) #else /* defined(FEAT_OPEN_APPID) */ uint32_t classification, uint32_t priority, uint32_t event_ref, char *event_appid) #endif /* defined(FEAT_OPEN_APPID) */ { event->sig_generator = generator; event->sig_id = id; event->sig_rev = rev; event->classification = classification; event->priority = priority; /* this one gets set automatically */ event->event_id = ++event_id | ScEventLogId(); if(event_ref) event->event_reference = event_ref; else event->event_reference = event->event_id; #if defined(FEAT_OPEN_APPID) if (event_appid) memcpy(event->app_name, event_appid, MAX_EVENT_APPNAME_LEN); else event->app_name[0] = 0; #endif /* defined(FEAT_OPEN_APPID) */ event->ref_time.tv_sec = 0; return; } #ifndef NO_NON_ETHER_DECODER /* * Function: PrintEapolPkt(FILE *, Packet *) * * Purpose: Dump the packet to the stream pointer * * Arguments: fp => pointer to print data to * type => packet protocol * p => pointer to decoded packet struct * * Returns: void function */ void PrintEapolPkt(FILE * fp, Packet * p) { char timestamp[TIMEBUF_SIZE]; memset((char *) timestamp, 0, TIMEBUF_SIZE); ts_print((struct timeval *) & p->pkth->ts, timestamp); /* dump the timestamp */ fwrite(timestamp, strlen(timestamp), 1, fp); /* dump the ethernet header if we're doing that sort of thing */ if (ScOutputDataLink()) { Print2ndHeader(fp, p); } PrintEapolHeader(fp, p); if (p->eplh->eaptype == EAPOL_TYPE_EAP) { PrintEAPHeader(fp, p); } else if (p->eplh->eaptype == EAPOL_TYPE_KEY) { PrintEapolKey(fp, p); } /* dump the application layer data */ if(ScOutputAppData() && !ScVerboseByteDump()) { if (ScOutputCharData()) PrintCharData(fp, (char*) p->data, p->dsize); else PrintNetData(fp, p->data, p->dsize, NULL); } else if (ScVerboseByteDump()) { PrintNetData(fp, p->pkt, p->pkth->caplen, p); } fprintf(fp, "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n"); } /**************************************************************************** * * Function: PrintWifiHeader(FILE *) * * Purpose: Print the packet 802.11 header to the specified stream * * Arguments: fp => file stream to print to * * Returns: void function * ***************************************************************************/ void PrintWifiHeader(FILE * fp, Packet * p) { /* This assumes we are printing a data packet, could be changed to print other types as well */ const u_char *da = NULL, *sa = NULL, *bssid = NULL, *ra = NULL, *ta = NULL; /* per table 4, IEEE802.11 section 7.2.2 */ if ((p->wifih->frame_control & WLAN_FLAG_TODS) && (p->wifih->frame_control & WLAN_FLAG_FROMDS)) { ra = p->wifih->addr1; ta = p->wifih->addr2; da = p->wifih->addr3; sa = p->wifih->addr4; } else if (p->wifih->frame_control & WLAN_FLAG_TODS) { bssid = p->wifih->addr1; sa = p->wifih->addr2; da = p->wifih->addr3; } else if (p->wifih->frame_control & WLAN_FLAG_FROMDS) { da = p->wifih->addr1; bssid = p->wifih->addr2; sa = p->wifih->addr3; } else { da = p->wifih->addr1; sa = p->wifih->addr2; bssid = p->wifih->addr3; } /* DO this switch to provide additional info on the type */ switch(p->wifih->frame_control & 0x00ff) { case WLAN_TYPE_MGMT_BEACON: fprintf(fp, "Beacon "); break; /* management frames */ case WLAN_TYPE_MGMT_ASREQ: fprintf(fp, "Assoc. Req. "); break; case WLAN_TYPE_MGMT_ASRES: fprintf(fp, "Assoc. Resp. "); break; case WLAN_TYPE_MGMT_REREQ: fprintf(fp, "Reassoc. Req. "); break; case WLAN_TYPE_MGMT_RERES: fprintf(fp, "Reassoc. Resp. "); break; case WLAN_TYPE_MGMT_PRREQ: fprintf(fp, "Probe Req. "); break; case WLAN_TYPE_MGMT_PRRES: fprintf(fp, "Probe Resp. "); break; case WLAN_TYPE_MGMT_ATIM: fprintf(fp, "ATIM "); break; case WLAN_TYPE_MGMT_DIS: fprintf(fp, "Dissassoc. "); break; case WLAN_TYPE_MGMT_AUTH: fprintf(fp, "Authent. "); break; case WLAN_TYPE_MGMT_DEAUTH: fprintf(fp, "Deauthent. "); break; /* Control frames */ case WLAN_TYPE_CONT_PS: case WLAN_TYPE_CONT_RTS: case WLAN_TYPE_CONT_CTS: case WLAN_TYPE_CONT_ACK: case WLAN_TYPE_CONT_CFE: case WLAN_TYPE_CONT_CFACK: fprintf(fp, "Control "); break; } if (sa != NULL) { fprintf(fp, "%X:%X:%X:%X:%X:%X -> ", sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); } else if (ta != NULL) { fprintf(fp, "ta: %X:%X:%X:%X:%X:%X da: ", ta[0], ta[1], ta[2], ta[3], ta[4], ta[5]); } fprintf(fp, "%X:%X:%X:%X:%X:%X\n", da[0], da[1], da[2], da[3], da[4], da[5]); if (bssid != NULL) { fprintf(fp, "bssid: %X:%X:%X:%X:%X:%X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); } if (ra != NULL) { fprintf(fp, " ra: %X:%X:%X:%X:%X:%X", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]); } fprintf(fp, " Flags:"); if (p->wifih->frame_control & WLAN_FLAG_TODS) fprintf(fp," ToDs"); if (p->wifih->frame_control & WLAN_FLAG_TODS) fprintf(fp," FrDs"); if (p->wifih->frame_control & WLAN_FLAG_FRAG) fprintf(fp," Frag"); if (p->wifih->frame_control & WLAN_FLAG_RETRY) fprintf(fp," Re"); if (p->wifih->frame_control & WLAN_FLAG_PWRMGMT) fprintf(fp," Pwr"); if (p->wifih->frame_control & WLAN_FLAG_MOREDAT) fprintf(fp," MD"); if (p->wifih->frame_control & WLAN_FLAG_WEP) fprintf(fp," Wep"); if (p->wifih->frame_control & WLAN_FLAG_ORDER) fprintf(fp," Ord"); fprintf(fp, "\n"); } /* * Function: PrintWifiPkt(FILE *, Packet *) * * Purpose: Dump the packet to the stream pointer * * Arguments: fp => pointer to print data to * p => pointer to decoded packet struct * * Returns: void function */ void PrintWifiPkt(FILE * fp, Packet * p) { char timestamp[TIMEBUF_SIZE]; memset((char *) timestamp, 0, TIMEBUF_SIZE); ts_print((struct timeval *) & p->pkth->ts, timestamp); /* dump the timestamp */ fwrite(timestamp, strlen(timestamp), 1, fp); /* dump the ethernet header if we're doing that sort of thing */ Print2ndHeader(fp, p); /* dump the application layer data */ if (ScOutputAppData() && !ScVerboseByteDump()) { if (ScOutputCharData()) PrintCharData(fp, (char*) p->data, p->dsize); else PrintNetData(fp, p->data, p->dsize, NULL); } else if (ScVerboseByteDump()) { PrintNetData(fp, p->pkt, p->pkth->caplen, p); } fprintf(fp, "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+" "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+\n\n"); } /**************************************************************************** * * Function: PrintEapolHeader(FILE *, Packet *) * * Purpose: Dump the EAPOL header info to the specified stream * * Arguments: fp => stream to print to * * Returns: void function * ***************************************************************************/ void PrintEapolHeader(FILE * fp, Packet * p) { fprintf(fp, "EAPOL type: "); switch(p->eplh->eaptype) { case EAPOL_TYPE_EAP: fprintf(fp, "EAP"); break; case EAPOL_TYPE_START: fprintf(fp, "Start"); break; case EAPOL_TYPE_LOGOFF: fprintf(fp, "Logoff"); break; case EAPOL_TYPE_KEY: fprintf(fp, "Key"); break; case EAPOL_TYPE_ASF: fprintf(fp, "ASF Alert"); break; default: fprintf(fp, "Unknown"); } fprintf(fp, " Len: %d\n", ntohs(p->eplh->len)); } /**************************************************************************** * * Function: PrintEAPHeader(FILE *) * * Purpose: Dump the EAP header to the specified file stream * * Arguments: fp => file stream * * Returns: void function * ***************************************************************************/ void PrintEAPHeader(FILE * fp, Packet * p) { if(p->eaph == NULL) { fprintf(fp, "EAP header truncated\n"); return; } fprintf(fp, "code: "); switch(p->eaph->code) { case EAP_CODE_REQUEST: fprintf(fp, "Req "); break; case EAP_CODE_RESPONSE: fprintf(fp, "Resp"); break; case EAP_CODE_SUCCESS: fprintf(fp, "Succ"); break; case EAP_CODE_FAILURE: fprintf(fp, "Fail"); break; } fprintf(fp, " id: 0x%x len: %d", p->eaph->id, ntohs(p->eaph->len)); if (p->eaptype != NULL) { fprintf(fp, " type: "); switch(*(p->eaptype)) { case EAP_TYPE_IDENTITY: fprintf(fp, "id"); break; case EAP_TYPE_NOTIFY: fprintf(fp, "notify"); break; case EAP_TYPE_NAK: fprintf(fp, "nak"); break; case EAP_TYPE_MD5: fprintf(fp, "md5"); break; case EAP_TYPE_OTP: fprintf(fp, "otp"); break; case EAP_TYPE_GTC: fprintf(fp, "token"); break; case EAP_TYPE_TLS: fprintf(fp, "tls"); break; default: fprintf(fp, "undef"); break; } } fprintf(fp, "\n"); } /**************************************************************************** * * Function: PrintEapolKey(FILE *) * * Purpose: Dump the EAP header to the specified file stream * * Arguments: fp => file stream * * Returns: void function * ***************************************************************************/ void PrintEapolKey(FILE * fp, Packet * p) { uint16_t length; if(p->eapolk == NULL) { fprintf(fp, "Eapol Key truncated\n"); return; } fprintf(fp, "KEY type: "); if (p->eapolk->type == 1) { fprintf(fp, "RC4"); } memcpy(&length, &p->eapolk->length, 2); length = ntohs(length); fprintf(fp, " len: %d", length); fprintf(fp, " index: %d ", p->eapolk->index & 0x7F); fprintf(fp, p->eapolk->index & 0x80 ? " unicast\n" : " broadcast\n"); } #endif // NO_NON_ETHER_DECODER void PrintIpAddrs(FILE *fp, Packet *p) { if (!IPH_IS_VALID(p)) return; if (p->frag_flag || ((GET_IPH_PROTO(p) != IPPROTO_TCP) && (GET_IPH_PROTO(p) != IPPROTO_UDP))) { char *ip_fmt = "%s -> %s"; if (ScObfuscate()) { fprintf(fp, ip_fmt, ObfuscateIpToText(GET_SRC_ADDR(p)), ObfuscateIpToText(GET_DST_ADDR(p))); } else { fprintf(fp, ip_fmt, inet_ntoax(GET_SRC_ADDR(p)), inet_ntoax(GET_DST_ADDR(p))); } } else { char *ip_fmt = "%s:%d -> %s:%d"; if (ScObfuscate()) { fprintf(fp, ip_fmt, ObfuscateIpToText(GET_SRC_ADDR(p)), p->sp, ObfuscateIpToText(GET_DST_ADDR(p)), p->dp); } else { fprintf(fp, ip_fmt, inet_ntoax(GET_SRC_ADDR(p)), p->sp, inet_ntoax(GET_DST_ADDR(p)), p->dp); } } } snort-2.9.20/src/dump.h0000644000175000017500000000270714241075105013003 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Author(s): Ron Dempster ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __DUMP_H__ #define __DUMP_H__ #include #include #include extern uint16_t packet_dump_address_space_id; extern volatile pcap_dumper_t* packet_dump_file; extern pcap_t* packet_dump_pcap; extern volatile int packet_dump_stop; extern struct sfbpf_program packet_dump_fcode; void PacketDumpClose(void); int PacketDumpCommand(uint16_t type, const uint8_t *data, uint32_t length, void **new_config, char *statusBuf, int statusBuf_len); #endif /* __DUMP_H__ */ snort-2.9.20/src/rule_option_types.h0000644000175000017500000000533314241077165015627 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef RULE_OPTION_TYPES__H #define RULE_OPTION_TYPES__H typedef enum _option_type_t { RULE_OPTION_TYPE_LEAF_NODE, RULE_OPTION_TYPE_ASN1, RULE_OPTION_TYPE_BYTE_TEST, RULE_OPTION_TYPE_BYTE_JUMP, RULE_OPTION_TYPE_BYTE_EXTRACT, RULE_OPTION_TYPE_FLOW, RULE_OPTION_TYPE_CVS, RULE_OPTION_TYPE_DSIZE, RULE_OPTION_TYPE_FLOWBIT, RULE_OPTION_TYPE_FTPBOUNCE, RULE_OPTION_TYPE_ICMP_CODE, RULE_OPTION_TYPE_ICMP_ID, RULE_OPTION_TYPE_ICMP_SEQ, RULE_OPTION_TYPE_ICMP_TYPE, RULE_OPTION_TYPE_IP_FRAGBITS, RULE_OPTION_TYPE_IP_FRAG_OFFSET, RULE_OPTION_TYPE_IP_ID, RULE_OPTION_TYPE_IP_OPTION, RULE_OPTION_TYPE_IP_PROTO, RULE_OPTION_TYPE_IP_SAME, RULE_OPTION_TYPE_IP_TOS, RULE_OPTION_TYPE_IS_DATA_AT, RULE_OPTION_TYPE_FILE_DATA, RULE_OPTION_TYPE_FILE_TYPE, RULE_OPTION_TYPE_BASE64_DECODE, RULE_OPTION_TYPE_BASE64_DATA, RULE_OPTION_TYPE_PKT_DATA, RULE_OPTION_TYPE_CONTENT, RULE_OPTION_TYPE_CONTENT_URI, RULE_OPTION_TYPE_PCRE, #ifdef ENABLE_REACT RULE_OPTION_TYPE_REACT, #endif #ifdef ENABLE_RESPOND RULE_OPTION_TYPE_RESPOND, #endif RULE_OPTION_TYPE_RPC_CHECK, RULE_OPTION_TYPE_SESSION, RULE_OPTION_TYPE_TCP_ACK, RULE_OPTION_TYPE_TCP_FLAG, RULE_OPTION_TYPE_TCP_SEQ, RULE_OPTION_TYPE_TCP_WIN, RULE_OPTION_TYPE_TTL, RULE_OPTION_TYPE_URILEN, RULE_OPTION_TYPE_HDR_OPT_CHECK, RULE_OPTION_TYPE_PREPROCESSOR, #if !defined(FEAT_OPEN_APPID) RULE_OPTION_TYPE_DYNAMIC #else /* defined(FEAT_OPEN_APPID) */ RULE_OPTION_TYPE_DYNAMIC, RULE_OPTION_TYPE_APPID #endif /* defined(FEAT_OPEN_APPID) */ ,RULE_OPTION_TYPE_BYTE_MATH } option_type_t; #endif /* RULE_OPTION_TYPES__H */ snort-2.9.20/src/rules.h0000644000175000017500000001055414241077166013200 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* $Id$ */ #ifndef __RULES_H__ #define __RULES_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "event.h" #include "decode.h" #include "signature.h" #include "parser/IpAddrSet.h" #include "spo_plugbase.h" #include "sf_vartable.h" #include "sf_types.h" #include "plugin_enum.h" #include "sfutil/sfportobject.h" #include "detection_options.h" #define EXCEPT_SRC_IP 0x01 #define EXCEPT_DST_IP 0x02 #define ANY_SRC_PORT 0x04 #define ANY_DST_PORT 0x08 #define ANY_FLAGS 0x10 #define EXCEPT_SRC_PORT 0x20 #define EXCEPT_DST_PORT 0x40 #define BIDIRECTIONAL 0x80 #define ANY_SRC_IP 0x100 #define ANY_DST_IP 0x200 #define EXCEPT_IP 0x01 #define R_FIN 0x01 #define R_SYN 0x02 #define R_RST 0x04 #define R_PSH 0x08 #define R_ACK 0x10 #define R_URG 0x20 #define R_ECE 0x40 /* ECN echo, RFC 3168 */ #define R_CWR 0x80 /* Congestion Window Reduced, RFC 3168 */ #define MODE_EXIT_ON_MATCH 0 #define MODE_FULL_SEARCH 1 #define CHECK_SRC_IP 0x01 #define CHECK_DST_IP 0x02 #define INVERSE 0x04 #define CHECK_SRC_PORT 0x08 #define CHECK_DST_PORT 0x10 #define SESSION_PRINTABLE 1 #define SESSION_ALL 2 #define MODE_EXIT_ON_MATCH 0 #define MODE_FULL_SEARCH 1 #define SRC 0 #define DST 1 #ifndef PARSERULE_SIZE #define PARSERULE_SIZE 65535 #endif /* D A T A S T R U C T U R E S *********************************************/ /* I'm forward declaring the rules structures so that the function pointer lists can reference them internally */ struct _ListHead; /* forward decleartion of ListHead data struct */ typedef enum _RuleType { RULE_TYPE__NONE = 0, RULE_TYPE__ALERT, RULE_TYPE__DROP, RULE_TYPE__LOG, RULE_TYPE__PASS, RULE_TYPE__REJECT, RULE_TYPE__SDROP, RULE_TYPE__MAX } RuleType; #ifndef f_ptr #define f_ptr fptr.fptr #endif #ifndef vf_ptr #define vf_ptr fptr.void_fptr #endif typedef struct _RspFpList { union { int (*fptr)(Packet*, void*); void *vfptr; } fptr; void *params; /* params for the plugin.. type defined by plugin */ struct _RspFpList *next; } RspFpList; typedef struct _TagData { int tag_type; /* tag type (session/host) */ int tag_seconds; /* number of "seconds" units to tag for */ int tag_packets; /* number of "packets" units to tag for */ int tag_bytes; /* number of "type" units to tag for */ int tag_metric; /* (packets | seconds | bytes) units */ int tag_direction; /* source or dest, used for host tagging */ } TagData; struct _RuleListNode; typedef struct _ListHead { struct _OutputFuncNode *LogList; struct _OutputFuncNode *AlertList; struct _RuleListNode *ruleListNode; } ListHead; typedef struct _RuleListNode { ListHead *RuleList; /* The rule list associated with this node */ RuleType mode; /* the rule mode */ int rval; /* 0 == no detection, 1 == detection event */ int evalIndex; /* eval index for this rule set */ char *name; /* name of this rule list (for debugging) */ struct _RuleListNode *next; /* the next RuleListNode */ } RuleListNode; typedef struct _RuleState { uint32_t sid; uint32_t gid; int state; RuleType action; struct _RuleState *next; } RuleState; #endif /* __RULES_H__ */ snort-2.9.20/src/byte_extract.c0000644000175000017500000002146714241074702014534 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2003-2013 Sourcefire, Inc. ** Chris Green ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "snort.h" #include "util.h" #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include "snort_bounds.h" #include "byte_extract.h" #include "snort_debug.h" #define TEXTLEN (PARSELEN + 1) /** * Grab a binary representation of data from a buffer * * This method will read either a big or little endian value in binary * data from the packet and return an uint32_t value. * * @param endianess value to read the byte as * @param bytes_to_grab how many bytes should we grab from the packet * @param data pointer to where to grab the data from * @param start pointer to start range of buffer * @param end pointer to end range of buffer * @param value pointer to store data in * * @returns 0 on success, otherwise failure */ int byte_extract(int endianess, int bytes_to_grab, const uint8_t *ptr, const uint8_t *start, const uint8_t *end, uint32_t *value) { if(endianess != LITTLE && endianess != BIG) { /* we only support 2 byte formats */ return -2; } /* make sure the data to grab stays in bounds */ if(!inBounds(start,end,ptr + (bytes_to_grab - 1))) { return -3; } if(!inBounds(start,end,ptr)) { return -3; } /* * We only support grabbing 1, 2, or 4 bytes of binary data. * And now, due to popular demand, 3 bytes! */ switch(bytes_to_grab) { case 1: *value = (*ptr) & 0xFF; break; case 2: if(endianess == LITTLE) { *value = (*ptr) & 0xFF; *value |= (*(ptr + 1) & 0xFF) << 8; } else { *value = ((*ptr) & 0xFF) << 8; *value |= (*(ptr + 1)) & 0xFF; } break; case 3: if (endianess == LITTLE) { *value = (*ptr) & 0xFF; *value |= ((*(ptr + 1)) & 0xFF) << 8; *value |= ((*(ptr + 2)) & 0xFF) << 16; } else { *value = ((*ptr) & 0xFF) << 16; *value |= ((*(ptr + 1)) & 0xFF) << 8; *value |= (*(ptr + 2)) & 0xFF; } break; case 4: if(endianess == LITTLE) { *value = (*ptr) & 0xFF; *value |= ((*(ptr + 1)) & 0xFF) << 8; *value |= ((*(ptr + 2)) & 0xFF) << 16; *value |= ((*(ptr + 3)) & 0xFF) << 24; } else { *value = ((*ptr) & 0xFF) << 24; *value |= ((*(ptr + 1)) & 0xFF) << 16; *value |= ((*(ptr + 2)) & 0xFF) << 8; *value |= (*(ptr + 3)) & 0xFF; } break; default: /* unknown type */ return -1; } return 0; } /** * Grab a string representation of data from a buffer * * @param base base representation for data: -> man stroul() * @param bytes_to_grab how many bytes should we grab from the packet * @param data pointer to where to grab the data from * @param start pointer to start range of buffer * @param end pointer to end range of buffer * @param value pointer to store data in * * @returns 0 on success, otherwise failure */ int string_extract(int bytes_to_grab, int base, const uint8_t *ptr, const uint8_t *start, const uint8_t *end, uint32_t *value) { char byte_array[TEXTLEN]; char *parse_helper; int x; /* counter */ if(bytes_to_grab > (TEXTLEN - 1) || bytes_to_grab <= 0) { return -1; } /* make sure the data to grab stays in bounds */ if(!inBounds(start,end,ptr + (bytes_to_grab - 1))) { return -3; } if(!inBounds(start,end,ptr)) { return -3; } for(x=0;x void test_extract(void) { int i; uint32_t ret; uint8_t value1[2]; uint8_t value2[2]; uint8_t value3[4]; value1[0] = 0; value1[1] = 0xff; value2[0] = 0xff; value2[1] = 0x01; value3[0] = 0xff; value3[1] = 0xff; value3[2] = 0x00; value3[3] = 0x00; if(byte_extract(BIG, 2, value1, value1, value1 + 2, &ret)) { printf("test 1 failed\n"); } else { printf("test 1: value: %x %u\n", ret, ret); } if(byte_extract(LITTLE, 2, value1, value1, value1 + 2, &ret)) { printf("test 2 failed\n"); } else { printf("test 2: value: %x %u\n", ret, ret); } if(byte_extract(LITTLE, 2, value1 + 2, value1, value1 + 2, &ret)) { printf("test 3 failed correctly\n"); } else { printf("test 3: value: %x %u\n", ret, ret); } if(byte_extract(BIG, 2, value2, value2, value2 + 2, &ret)) { printf("test 1 failed\n"); } else { printf("test 1: value: %x %u\n", ret, ret); } if(byte_extract(LITTLE, 2, value2, value2, value2 + 2, &ret)) { printf("test 2 failed\n"); } else { printf("test 2: value: %x %u\n", ret, ret); } if(byte_extract(LITTLE, 2, value2 + 2, value2, value2 + 2, &ret)) { printf("test 3 failed correctly\n"); } else { printf("test 3: value: %x %u\n", ret, ret); } if(byte_extract(BIG, 4, value3, value3, value3 + 4, &ret)) { printf("test 1 failed\n"); } else { printf("test 1: value: %x %u\n", ret, ret); } if(byte_extract(LITTLE, 4, value3, value3, value3 + 4, &ret)) { printf("test 2 failed\n"); } else { printf("test 2: value: %x %u\n", ret, ret); } if(byte_extract(LITTLE, 4, value3 + 2, value3, value3 + 4, &ret)) { printf("test 3 failed correctly\n"); } else { printf("test 3: value: %x %u\n", ret, ret); } printf("-----------------------------\n"); for(i=0;i<10;i++) { if(byte_extract(LITTLE, 4, value3 + i, value3, value3 + 4, &ret)) { printf("[loop] %d failed correctly\n", i); } else { printf("[loop] value: %x %x\n", ret, *(uint32_t *) &value3); } } } void test_string(void) { char *stringdata = "21212312412"; int datalen = strlen(stringdata); uint32_t ret; if(string_extract(4, 10, stringdata, stringdata, stringdata + datalen, &ret) < 0) { printf("TS1: Failed\n"); } else { printf("TS1: value %x %u\n", ret, ret); } if(string_extract(10, 10, stringdata, stringdata, stringdata + datalen, &ret) < 0) { printf("TS2: Failed\n"); } else { printf("TS2: value %x %u\n", ret, ret); } if(string_extract(9, 10, stringdata, stringdata, stringdata + datalen, &ret) < 0) { printf("TS3: Failed\n"); } else { printf("TS3: value %x %u\n", ret, ret); } if(string_extract(19, 10, stringdata, stringdata, stringdata + datalen, &ret) < 0) { printf("TS4: Failed Normally\n"); } else { printf("TS4: value %x %u\n", ret, ret); } } int main(void) { test_extract(); test_string(); return 0; } #endif /* TEST_BYTE_EXTRACT */ snort-2.9.20/src/sfthreshold.h0000644000175000017500000000322714241077200014357 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef SF_THRESHOLD #define SF_THRESHOLD #include "sfthd.h" #include "ipv6_port.h" typedef struct _ThresholdConfig { int memcap; int enabled; ThresholdObjects *thd_objs; } ThresholdConfig; ThresholdConfig * ThresholdConfigNew(void); void ThresholdConfigFree(ThresholdConfig *); void sfthreshold_reset(void); int sfthreshold_create(struct _SnortConfig *, ThresholdConfig *, THDX_STRUCT *); int sfthreshold_test(unsigned int, unsigned int, sfaddr_t*, sfaddr_t*, long curtime); void print_thresholding(ThresholdConfig*, unsigned shutdown); void sfthreshold_reset_active(void); void sfthreshold_free(void); #endif snort-2.9.20/src/plugbase.h0000644000175000017500000003265614241076730013653 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* $Id$ */ #ifndef __PLUGBASE_H__ #define __PLUGBASE_H__ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include "bitop_funcs.h" #include "rules.h" #include "treenodes.h" #include "sf_types.h" #include "snort_debug.h" #include "preprocids.h" #ifndef WIN32 # include #endif /* !WIN32 */ #ifdef ENABLE_SSL # ifdef Free /* Free macro in radix.h if defined, will conflict with OpenSSL definition */ # undef Free # endif #endif #ifndef WIN32 # include #endif /* !WIN32 */ #ifdef ENABLE_SSL # undef Free #endif #if defined(SOLARIS) || defined(FREEBSD) || defined(OPENBSD) # include #endif #if defined(FREEBSD) || defined(OPENBSD) || defined(NETBSD) || defined(OSF1) # include #endif #ifndef IFNAMSIZ /* IFNAMSIZ is defined in all platforms I checked.. */ # include #endif #include "preprocids.h" /* Macros *********************************************************************/ #define SMALLBUFFER 32 #define DETECTION_KEYWORD 0 #define RESPONSE_KEYWORD 1 #define ENCODING_HEX 0 #define ENCODING_BASE64 1 #define ENCODING_ASCII 2 #define DETAIL_FAST 0 #define DETAIL_FULL 1 /**************************** Rule Option Plugin API **************************/ typedef enum _RuleOptType { OPT_TYPE_ACTION = 0, OPT_TYPE_LOGGING, OPT_TYPE_DETECTION, OPT_TYPE_MAX } RuleOptType; typedef void (*RuleOptConfigFunc)(struct _SnortConfig *, char *, OptTreeNode *, int); typedef void (*RuleOptOtnHandler)(struct _SnortConfig *, OptTreeNode *); typedef void (*RuleOptOverrideFunc)(struct _SnortConfig *, char *, char *, char *, OptTreeNode *, int); typedef void (*RuleOptOverrideInitFunc)(char *, char *, RuleOptOverrideFunc); typedef int (*RuleOptEvalFunc)(void *, Packet *); typedef int (*ResponseFunc)(Packet*, void*); typedef void (*PluginSignalFunc)(int, void *); typedef void (*PluginSignalFuncWithSnortConfig)(struct _SnortConfig *, int, void *); typedef void (*PostConfigFunc)(struct _SnortConfig *, int, void *); typedef void (*RuleOptParseCleanupFunc)(void); typedef int (*RuleOptByteOrderFunc)(void *, int32_t); #define func fptr.fptr #define vfunc fptr.void_fptr typedef struct _RuleOptConfigFuncNode { char *keyword; RuleOptType type; union { RuleOptConfigFunc fptr; void *void_fptr; } fptr; RuleOptOtnHandler otn_handler; struct _RuleOptConfigFuncNode *next; } RuleOptConfigFuncNode; typedef struct _RuleOptOverrideInitFuncNode { char *keyword; RuleOptType type; union { RuleOptOverrideInitFunc fptr; void *void_fptr; } fptr; RuleOptOtnHandler otn_handler; struct _RuleOptOverrideInitFuncNode *next; } RuleOptOverrideInitFuncNode; typedef struct _RuleOptParseCleanupNode { union { RuleOptParseCleanupFunc fptr; void *void_fptr; } fptr; struct _RuleOptParseCleanupNode *next; } RuleOptParseCleanupNode; typedef struct _RuleOptByteOrderFuncNode { char *keyword; union { RuleOptByteOrderFunc fptr; void *void_fptr; } fptr; struct _RuleOptByteOrderFuncNode *next; } RuleOptByteOrderFuncNode; void RegisterRuleOptions(void); void RegisterRuleOption(char *, RuleOptConfigFunc, RuleOptOverrideInitFunc, RuleOptType, RuleOptOtnHandler); void RegisterOverrideKeyword(char *, char *, RuleOptOverrideFunc); void RegisterByteOrderKeyword(char *, RuleOptByteOrderFunc); void DumpRuleOptions(void); OptFpList * AddOptFuncToList(RuleOptEvalFunc, OptTreeNode *); void AddRspFuncToList(ResponseFunc, OptTreeNode *, void *); void FreeRuleOptConfigFuncs(RuleOptConfigFuncNode *); void FreeRuleOptOverrideInitFuncs(RuleOptOverrideInitFuncNode *); void AddFuncToRuleOptParseCleanupList(RuleOptParseCleanupFunc); void RuleOptParseCleanup(void); void FreeRuleOptParseCleanupList(RuleOptParseCleanupNode *); void RegisterByteOrderKeyword(char *, RuleOptByteOrderFunc); RuleOptByteOrderFunc GetByteOrderFunc(char *); void FreeRuleOptByteOrderFuncs(RuleOptByteOrderFuncNode *); /***************************** Buffer Dump API ********************************/ #ifdef DUMP_BUFFER void RegisterBufferTracer(TraceBuffer * (*)(), BUFFER_DUMP_FUNC); #endif /***************************** Non Rule Detection API *************************/ typedef void (*DetectionEvalFunc)(Packet *, void *); typedef struct _DetectionEvalFuncNode { void *context; uint16_t priority; uint32_t detect_id; //uint32_t detect_bit; uint32_t proto_mask; union { DetectionEvalFunc fptr; void *void_fptr; } fptr; struct _DetectionEvalFuncNode *next; } DetectionEvalFuncNode; DetectionEvalFuncNode * AddFuncToDetectionList(struct _SnortConfig *, DetectionEvalFunc, uint16_t, uint32_t, uint32_t); void FreeDetectionEvalFuncs(DetectionEvalFuncNode *); /***************************** Preprocessor API *******************************/ typedef void (*PreprocConfigFunc)(struct _SnortConfig *, char *); typedef void (*PreprocStatsFunc)(int); typedef void (*PreprocEvalFunc)(Packet *, void *); typedef int (*PreprocCheckConfigFunc)(struct _SnortConfig *); typedef void (*PreprocSignalFunc)(int, void *); typedef void (*PreprocPostConfigFunc)(struct _SnortConfig *, void *); typedef void (*PreprocMetaEvalFunc)(int, const uint8_t *); typedef void (*PeriodicFunc)(int, void *); #ifdef SNORT_RELOAD struct _PreprocConfigFuncNode; typedef struct _PreprocessorSwapData { struct _PreprocConfigFuncNode *preprocNode; void *data; struct _PreprocessorSwapData *next; } PreprocessorSwapData; typedef void (*PreprocReloadFunc)(struct _SnortConfig *, char *, void **); typedef int (*PreprocReloadVerifyFunc)(struct _SnortConfig *, void *); typedef void * (*PreprocReloadSwapFunc)(struct _SnortConfig *, void *); typedef void (*PreprocReloadSwapFreeFunc)(void *); #endif #define config_func cfptr.fptr #define config_vfunc cfptr.void_fptr typedef struct _PreprocConfigFuncNode { char *keyword; union { PreprocConfigFunc fptr; void *void_fptr; } cfptr; #ifdef SNORT_RELOAD /* Tells whether we call the config func or reload func */ int initialized; PreprocReloadFunc reload_func; PreprocReloadVerifyFunc reload_verify_func; PreprocReloadSwapFunc reload_swap_func; PreprocReloadSwapFreeFunc reload_swap_free_func; #endif struct _PreprocConfigFuncNode *next; } PreprocConfigFuncNode; typedef struct _PreprocStatsFuncNode { char *keyword; union { PreprocStatsFunc fptr; void *void_fptr; } fptr; struct _PreprocStatsFuncNode *next; } PreprocStatsFuncNode; typedef struct _PreprocEvalFuncNode { void *context; uint16_t priority; uint32_t preproc_id; PreprocEnableMask preproc_bit; uint32_t proto_mask; union { PreprocEvalFunc fptr; void *void_fptr; } fptr; struct _PreprocEvalFuncNode *next; } PreprocEvalFuncNode; typedef struct _PreprocMetaEvalFuncNode { uint16_t priority; uint32_t preproc_id; PreprocEnableMask preproc_bit; union { PreprocMetaEvalFunc fptr; void *void_fptr; } fptr; struct _PreprocMetaEvalFuncNode *next; } PreprocMetaEvalFuncNode; typedef struct _PreprocCheckConfigFuncNode { union { PreprocCheckConfigFunc fptr; void *void_fptr; } fptr; struct _PreprocCheckConfigFuncNode *next; } PreprocCheckConfigFuncNode; typedef struct _PreprocSignalFuncNode { void *arg; uint16_t priority; uint32_t preproc_id; union { PreprocSignalFunc fptr; void *void_fptr; } fptr; struct _PreprocSignalFuncNode *next; } PreprocSignalFuncNode; typedef struct _PreprocPostConfigFuncNode { void *data; union { PreprocPostConfigFunc fptr; void *void_fptr; } fptr; struct _PreprocPostConfigFuncNode *next; } PreprocPostConfigFuncNode; typedef struct _PeriodicCheckFuncNode { void *arg; uint16_t priority; uint32_t preproc_id; uint32_t period; uint32_t time_left; union { PeriodicFunc fptr; void *void_fptr; } fptr; struct _PeriodicCheckFuncNode *next; } PeriodicCheckFuncNode; struct _SnortConfig; void RegisterPreprocessors(void); #ifndef SNORT_RELOAD void RegisterPreprocessor(const char *, PreprocConfigFunc); #else void RegisterPreprocessor(const char *, PreprocConfigFunc, PreprocReloadFunc, PreprocReloadVerifyFunc, PreprocReloadSwapFunc, PreprocReloadSwapFreeFunc); void *GetRelatedReloadData(struct _SnortConfig *, const char *); void *GetReloadStreamConfig(struct _SnortConfig *sc); #endif PreprocConfigFuncNode * GetPreprocConfig(char *); PreprocConfigFunc GetPreprocConfigFunc(char *); void RegisterPreprocStats(const char *, PreprocStatsFunc); void DumpPreprocessors(void); void AddFuncToConfigCheckList(struct _SnortConfig *, PreprocCheckConfigFunc); void AddFuncToPreprocPostConfigList(struct _SnortConfig *, PreprocPostConfigFunc, void *); int CheckPreprocessorsConfig(struct _SnortConfig *); PreprocEvalFuncNode * AddFuncToPreprocList(struct _SnortConfig *, PreprocEvalFunc, uint16_t, uint32_t, uint32_t); void AddFuncToPreprocListAllNapPolicies(struct _SnortConfig *sc, PreprocEvalFunc pp_eval_func, uint16_t priority, uint32_t preproc_id, uint32_t proto_mask); PreprocMetaEvalFuncNode * AddFuncToPreprocMetaEvalList(struct _SnortConfig *, PreprocMetaEvalFunc, uint16_t, uint32_t); void AddFuncToPreprocCleanExitList(PreprocSignalFunc, void *, uint16_t, uint32_t); void AddFuncToPreprocShutdownList(PreprocSignalFunc, void *, uint16_t, uint32_t); void AddFuncToPreprocResetList(PreprocSignalFunc, void *, uint16_t, uint32_t); void AddFuncToPreprocResetStatsList(PreprocSignalFunc, void *, uint16_t, uint32_t); int IsPreprocEnabled(struct _SnortConfig *, uint32_t); void FreePreprocConfigFuncs(void); void FreePreprocCheckConfigFuncs(PreprocCheckConfigFuncNode *); void FreePreprocStatsFuncs(PreprocStatsFuncNode *); void FreePreprocEvalFuncs(PreprocEvalFuncNode *); void FreePreprocMetaEvalFuncs(PreprocMetaEvalFuncNode *); void FreePreprocSigFuncs(PreprocSignalFuncNode *); void FreePreprocPostConfigFuncs(PreprocPostConfigFuncNode *); void PostConfigPreprocessors(struct _SnortConfig *); void FilterConfigPreprocessors(struct _SnortConfig *sc); #ifdef SNORT_RELOAD int VerifyReloadedPreprocessors(struct _SnortConfig *); void SwapPreprocConfigurations(struct _SnortConfig *); void FreeSwappedPreprocConfigurations(struct _SnortConfig *); void FreePreprocessorReloadData(struct _SnortConfig *); #endif void AddFuncToPeriodicCheckList(PeriodicFunc, void *, uint16_t, uint32_t, uint32_t); void FreePeriodicFuncs(PeriodicCheckFuncNode *head); static inline void DisableAppPreprocessors( Packet *p ) { p->preprocessor_bits &= ( PP_CLASS_NETWORK | PP_CLASS_NGFW ); } static inline void DisableAllPreprocessors( Packet *p ) { p->preprocessor_bits = PP_DISABLE_ALL; } static inline int EnablePreprocessor(Packet *p, unsigned int preproc_id) { p->preprocessor_bits |= (UINT64_C(1) << preproc_id); return 0; } static inline void EnablePreprocessors(Packet *p, PreprocEnableMask enabled_pps) { p->preprocessor_bits = enabled_pps; } static inline int IsPreprocessorEnabled(Packet *p, PreprocEnableMask preproc_bit) { return ( ( p->preprocessor_bits & preproc_bit ) != 0 ); } void DisableAllPolicies(struct _SnortConfig *); int ReenablePreprocBit(struct _SnortConfig *, unsigned int preproc_id); /************************** Miscellaneous Functions **************************/ typedef struct _PluginSignalFuncNode { void *arg; union { PluginSignalFunc fptr; void *void_fptr; } fptr; struct _PluginSignalFuncNode *next; } PluginSignalFuncNode; typedef struct _PostConfigFuncNode { void *arg; union { PostConfigFunc fptr; void *void_fptr; } fptr; struct _PostConfigFuncNode *next; } PostConfigFuncNode; /* Used for both rule options and output. Preprocessors have their own */ #ifdef SNORT_RELOAD void AddFuncToReloadList(PostConfigFunc, void *); #endif void AddFuncToCleanExitList(PluginSignalFunc, void *); void AddFuncToShutdownList(PluginSignalFunc, void *); void AddFuncToPostConfigList(struct _SnortConfig *, PostConfigFunc, void *); void AddFuncToSignalList(PluginSignalFunc, void *, PluginSignalFuncNode **); void PostConfigInitPlugins(struct _SnortConfig *, PostConfigFuncNode *); void FreePluginSigFuncs(PluginSignalFuncNode *); void FreePluginPostConfigFuncs(PostConfigFuncNode *); typedef char** (*GetHttpXffFieldsFunc)(int* nFields); char** GetHttpXffFields(int* nFields); void RegisterGetHttpXffFields(GetHttpXffFieldsFunc fn); #endif /* __PLUGBASE_H__ */ snort-2.9.20/src/snort_bounds.h0000644000175000017500000001405614241077407014564 0ustar apoapo#ifndef _BOUNDS_H #define _BOUNDS_H /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2003-2013 Sourcefire, Inc. ** Chris Green ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifdef OSF1 #include #endif #include #include #include #include #include #ifdef DEBUG #include #endif #include #define SAFEMEM_ERROR 0 #define SAFEMEM_SUCCESS 1 #ifdef DEBUG #define ERRORRET assert(0==1) #else #define ERRORRET return SAFEMEM_ERROR; #endif /* DEBUG */ #define MAXPORTS 65536 #define MAXPORTS_STORAGE 8192 /* * Check to make sure that p is less than or equal to the ptr range * pointers * * 1 means it's in bounds, 0 means it's not */ static inline int inBounds(const uint8_t *start, const uint8_t *end, const uint8_t *p) { if ((p >= start) && (p < end)) return 1; return 0; } static inline int SafeMemCheck(void *dst, size_t n, const void *start, const void *end) { void *tmp; if (n < 1) return SAFEMEM_ERROR; if ((dst == NULL) || (start == NULL) || (end == NULL)) return SAFEMEM_ERROR; tmp = ((uint8_t *)dst) + (n - 1); if (tmp < dst) return SAFEMEM_ERROR; if (!inBounds(start, end, dst) || !inBounds(start, end, tmp)) return SAFEMEM_ERROR; return SAFEMEM_SUCCESS; } /** * A Safer Memcpy * * @param dst where to copy to * @param src where to copy from * @param n number of bytes to copy * @param start start of the dest buffer * @param end end of the dst buffer * * @return SAFEMEM_ERROR on failure, SAFEMEM_SUCCESS on success */ static inline int SafeMemcpy(void *dst, const void *src, size_t n, const void *start, const void *end) { if (!n) return SAFEMEM_SUCCESS; if (SafeMemCheck(dst, n, start, end) != SAFEMEM_SUCCESS) ERRORRET; if (src == NULL) ERRORRET; memcpy(dst, src, n); return SAFEMEM_SUCCESS; } /** * A Safer Memmove * dst and src can be in the same buffer * * @param dst where to copy to * @param src where to copy from * @param n number of bytes to copy * @param start start of the dest buffer * @param end end of the dst buffer * * @return SAFEMEM_ERROR on failure, SAFEMEM_SUCCESS on success */ static inline int SafeMemmove(void *dst, const void *src, size_t n, const void *start, const void *end) { if (SafeMemCheck(dst, n, start, end) != SAFEMEM_SUCCESS) ERRORRET; if (src == NULL) ERRORRET; memmove(dst, src, n); return SAFEMEM_SUCCESS; } /** * A Safer Memmove * dst and src can be in the same buffer * * @param dst where to copy to * @param src where to copy from * @param n number of bytes to copy * @param start start of the dest buffer * @param end end of the dst buffer * * @return SAFEMEM_ERROR on failure, SAFEMEM_SUCCESS on success */ static inline int SafeBoundsMemmove(void *dst, const void *src, size_t n, const void *start, const void *end) { size_t overlap = 0; if (SafeMemCheck(dst, n, start, end) != SAFEMEM_SUCCESS) ERRORRET; if (src == NULL) ERRORRET; if( src == dst ) { return SAFEMEM_SUCCESS; } else if(inBounds(dst, ((uint8_t *)dst + n), src)) { overlap = (uint8_t *)src - (uint8_t *)dst; memcpy(dst, src , overlap); memmove(((uint8_t *)dst + overlap), ((uint8_t *)src + overlap), (n - overlap)); } else if(inBounds(src, ((uint8_t *)src + n), dst)) { overlap = (uint8_t *)dst - (uint8_t *)src; memcpy(((uint8_t *)dst + overlap), ((uint8_t *)src + overlap), (n - overlap)); memmove(dst, src, overlap); } else { memcpy(dst, src, n); } return SAFEMEM_SUCCESS; } /** * A Safer Memset * dst and src can be in the same buffer * * @param dst where to copy to * @param c character to set memory with * @param n number of bytes to set * @param start start of the dst buffer * @param end end of the dst buffer * * @return SAFEMEM_ERROR on failure, SAFEMEM_SUCCESS on success */ static inline int SafeMemset(void *dst, uint8_t c, size_t n, const void *start, const void *end) { if (SafeMemCheck(dst, n, start, end) != SAFEMEM_SUCCESS) ERRORRET; memset(dst, c, n); return SAFEMEM_SUCCESS; } /** * A Safer *a = *b * * @param start start of the dst buffer * @param end end of the dst buffer * @param dst the location to write to * @param src the source to read from * * @return 0 on failure, 1 on success */ static inline int SafeWrite(uint8_t *start, uint8_t *end, uint8_t *dst, uint8_t *src) { if(!inBounds(start, end, dst)) { ERRORRET; } *dst = *src; return 1; } static inline int SafeRead(uint8_t *start, uint8_t *end, uint8_t *src, uint8_t *read) { if(!inBounds(start,end, src)) { ERRORRET; } *read = *start; return 1; } /* An wrapper around snprintf to make it safe. * * This wrapper of snprintf returns the number of bytes written to the buffer. */ static inline size_t SafeSnprintf(char *str, size_t size, const char *format, ...) { va_list ap; int ret; if (size == 0) return 0; va_start(ap, format); ret = vsnprintf(str, size, format, ap); va_end(ap); if (ret < 0 || (size_t)ret >= size) return 0; return (size_t)ret; } #endif /* _BOUNDS_H */ snort-2.9.20/src/profiler.c0000644000175000017500000007222614241077151013661 0ustar apoapo/* ** $Id$ ** ** profiler.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** Steven Sturges ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "snort.h" #include "rules.h" #include "treenodes.h" #include "treenodes.h" #include "parser.h" #include "plugin_enum.h" #include "util.h" #include "rules.h" #include "treenodes.h" #include "treenodes.h" #include "profiler.h" #include "sf_types.h" #include "sf_textlog.h" #include "detection_options.h" #ifdef PERF_PROFILING /* Data types *****************************************************************/ typedef struct _OTN_WorstPerformer { OptTreeNode *otn; struct _OTN_WorstPerformer *next; struct _OTN_WorstPerformer *prev; double ticks_per_check; double ticks_per_match; double ticks_per_nomatch; } OTN_WorstPerformer; typedef struct _Preproc_WorstPerformer { PreprocStatsNode *node; struct _Preproc_WorstPerformer *next; struct _Preproc_WorstPerformer *prev; struct _Preproc_WorstPerformer *children; double ticks_per_check; double pct_of_parent; double pct_of_total; } Preproc_WorstPerformer; /* Globals ********************************************************************/ double ticks_per_microsec = 0.0; OTN_WorstPerformer *worstPerformers = NULL; Preproc_WorstPerformer *worstPreprocPerformers = NULL; PreprocStats totalPerfStats; PreprocStats metaPerfStats; static PreprocStatsNode * PreprocStatsNodeList = NULL; int max_layers = 0; /* Externs ********************************************************************/ extern PreprocStats mpsePerfStats, rulePerfStats, ncrulePerfStats; void getTicksPerMicrosec(void) { if (ticks_per_microsec == 0.0) { ticks_per_microsec = get_ticks_per_usec(); } } void ResetRuleProfiling(void) { /* Cycle through all Rules, print ticks & check count for each */ RuleTreeNode *rtn; SFGHASH_NODE *hashNode; OptTreeNode *otn = NULL; tSfPolicyId policyId = 0; SnortConfig *sc = snort_conf; if ((sc == NULL) || (sc->profile_rules.num == 0)) return; for (hashNode = sfghash_findfirst(sc->otn_map); hashNode; hashNode = sfghash_findnext(sc->otn_map)) { otn = (OptTreeNode *)hashNode->data; for ( policyId = 0; policyId < otn->proto_node_num; policyId++ ) { rtn = getRtnFromOtn(otn, policyId); if (rtn == NULL) continue; if ((rtn->proto == IPPROTO_TCP) || (rtn->proto == IPPROTO_UDP) || (rtn->proto == IPPROTO_ICMP) || (rtn->proto == ETHERNET_TYPE_IP)) { //do operation otn->ticks = 0; otn->ticks_match = 0; otn->ticks_no_match = 0; otn->checks = 0; otn->matches = 0; otn->alerts = 0; otn->noalerts = 0; #ifdef PPM_MGR otn->ppm_disable_cnt = 0; #endif } } } } void PrintWorstRules(int numToPrint) { OptTreeNode *otn; OTN_WorstPerformer *node, *tmp; int num = 0; TextLog *log = NULL; time_t cur_time; char fullname[STD_BUF]; int ret; SnortConfig *sc = snort_conf; if (sc == NULL) return; getTicksPerMicrosec(); cur_time = time(NULL); if (sc->profile_rules.filename != NULL) { if (sc->profile_rules.append) { log = TextLog_Init(sc->profile_rules.filename, 512*1024, 512*1024); if (log != NULL) TextLog_Print(log, "\ntimestamp: %u\n", cur_time); } else { ret = SnortSnprintf(fullname, STD_BUF, "%s.%u", sc->profile_rules.filename, (uint32_t)cur_time); if(ret != SNORT_SNPRINTF_SUCCESS) FatalError("profiler: file path+name too long\n"); log = TextLog_Init(fullname, 512*1024, 512*1024); } } if (numToPrint != -1) { if(log) { TextLog_Print(log, "Rule Profile Statistics (worst %d rules)\n", numToPrint); } else { LogMessage("Rule Profile Statistics (worst %d rules)\n", numToPrint); } } else { if(log) { TextLog_Print(log, "Rule Profile Statistics (all rules)\n"); } else { LogMessage("Rule Profile Statistics (all rules)\n"); } } if(log) { TextLog_Print(log, "==========================================================\n"); } else { LogMessage("==========================================================\n"); } if (!worstPerformers) { if(log) { TextLog_Print(log, "No rules were profiled\n"); TextLog_Term(log); } else { LogMessage("No rules were profiled\n"); } return; } if(log) { TextLog_Print(log, #ifdef PPM_MGR "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #else "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #endif 6, "Num", 9, "SID", 4, "GID", 4, "Rev", 11, "Checks", 10, "Matches", 10, "Alerts", 20, "Microsecs", 11, "Avg/Check", 11, "Avg/Match", 13, "Avg/Nonmatch" #ifdef PPM_MGR , 11, "Disabled" #endif ); } else { LogMessage( #ifdef PPM_MGR "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #else "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #endif 6, "Num", 9, "SID", 4, "GID", 4, "Rev", 11, "Checks", 10, "Matches", 10, "Alerts", 20, "Microsecs", 11, "Avg/Check", 11, "Avg/Match", 13, "Avg/Nonmatch" #ifdef PPM_MGR , 11, "Disabled" #endif ); } if(log) { TextLog_Print(log, #ifdef PPM_MGR "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #else "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #endif 6, "===", 9, "===", 4, "===", 4, "===", 11, "======", 10, "=======", 10, "======", 20, "=========", 11, "=========", 11, "=========", 13, "============" #ifdef PPM_MGR , 11, "========" #endif ); } else { LogMessage( #ifdef PPM_MGR "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #else "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", #endif 6, "===", 9, "===", 4, "===", 4, "===", 11, "======", 10, "=======", 10, "======", 20, "=========", 11, "=========", 11, "=========", 13, "============" #ifdef PPM_MGR , 11, "========" #endif ); } for (node = worstPerformers, num=1; node && ((numToPrint < 0) ? 1 : (num <= numToPrint)); node= node->next, num++) { //if (!node) // break; otn = node->otn; if(log) { TextLog_Print(log, #ifdef PPM_MGR "%*d%*d%*d%*d" FMTu64("*") FMTu64("*") FMTu64("*") FMTu64("*") "%*.1f%*.1f%*.1f" FMTu64("*") "\n", #else "%*d%*d%*d%*d" FMTu64("*") FMTu64("*") FMTu64("*") FMTu64("*") "%*.1f%*.1f%*.1f" "\n", #endif 6, num, 9, otn->sigInfo.id, 4, otn->sigInfo.generator, 4, otn->sigInfo.rev, 11, otn->checks, 10, otn->matches, 10, otn->alerts, 20, (uint64_t)(otn->ticks/ticks_per_microsec), 11, node->ticks_per_check/ticks_per_microsec, 11, node->ticks_per_match/ticks_per_microsec, 13, node->ticks_per_nomatch/ticks_per_microsec #ifdef PPM_MGR , 11, otn->ppm_disable_cnt #endif ); } else { LogMessage( #ifdef PPM_MGR "%*d%*d%*d%*d" FMTu64("*") FMTu64("*") FMTu64("*") FMTu64("*") "%*.1f%*.1f%*.1f" FMTu64("*") "\n", #else "%*d%*d%*d%*d" FMTu64("*") FMTu64("*") FMTu64("*") FMTu64("*") "%*.1f%*.1f%*.1f" "\n", #endif 6, num, 9, otn->sigInfo.id, 4, otn->sigInfo.generator, 4, otn->sigInfo.rev, 11, otn->checks, 10, otn->matches, 10, otn->alerts, 20, (uint64_t)(otn->ticks/ticks_per_microsec), 11, node->ticks_per_check/ticks_per_microsec, 11, node->ticks_per_match/ticks_per_microsec, 13, node->ticks_per_nomatch/ticks_per_microsec #ifdef PPM_MGR , 11, otn->ppm_disable_cnt #endif ); } } /* Do some cleanup */ for (node = worstPerformers; node; ) { tmp = node->next; free(node); node = tmp; } if(log) TextLog_Term(log); worstPerformers = NULL; } void CollectRTNProfile(void) { OptTreeNode *otn; OTN_WorstPerformer *new, *node, *last = NULL; char got_position; SFGHASH_NODE *hashNode; SnortConfig *sc = snort_conf; if (sc == NULL) return; for (hashNode = sfghash_findfirst(sc->otn_map); hashNode; hashNode = sfghash_findnext(sc->otn_map)) { otn = (OptTreeNode *)hashNode->data; /* Only log info if OTN has actually been eval'd */ if (otn->checks > 0 && otn->ticks > 0) { double ticks_per_check = (double)otn->ticks/(double)otn->checks; double ticks_per_nomatch; double ticks_per_match; if (otn->matches > otn->checks) otn->checks = otn->matches; if (otn->matches) ticks_per_match = (double)otn->ticks_match/(double)otn->matches; else ticks_per_match = 0.0; if (otn->checks == otn->matches) ticks_per_nomatch = 0.0; else ticks_per_nomatch = (double)otn->ticks_no_match/(double)(otn->checks - otn->matches); /* Find where he goes in the list * Cycle through the list and add * this where it goes */ new = (OTN_WorstPerformer *)SnortAlloc(sizeof(OTN_WorstPerformer)); new->otn = otn; new->ticks_per_check = ticks_per_check; new->ticks_per_match = ticks_per_match; new->ticks_per_nomatch = ticks_per_nomatch; got_position = 0; for (node = worstPerformers; node && !got_position; node = node->next) { last = node; switch (sc->profile_rules.sort) { case PROFILE_SORT_CHECKS: if (otn->checks >= node->otn->checks) { got_position = 1; } break; case PROFILE_SORT_MATCHES: if (otn->matches >= node->otn->matches) { got_position = 1; } break; case PROFILE_SORT_NOMATCHES: if (otn->checks - otn->matches > node->otn->checks - node->otn->matches) { got_position = 1; } break; case PROFILE_SORT_AVG_TICKS_PER_MATCH: if (ticks_per_match >= node->ticks_per_match) { got_position = 1; } break; case PROFILE_SORT_AVG_TICKS_PER_NOMATCH: if (ticks_per_nomatch >= node->ticks_per_nomatch) { got_position = 1; } break; case PROFILE_SORT_TOTAL_TICKS: if (otn->ticks >= node->otn->ticks) { got_position = 1; } break; default: case PROFILE_SORT_AVG_TICKS: if (ticks_per_check >= node->ticks_per_check) { got_position = 1; } break; } if (got_position) break; } if (node) { new->next = node; new->prev = node->prev; node->prev = new; if (new->prev) new->prev->next = new; /* Reset the head of list */ if (node == worstPerformers) worstPerformers = new; } else { if (!last) { worstPerformers = new; } else { new->prev = last; last->next = new; } } } } } void ShowRuleProfiles(void) { /* Cycle through all Rules, print ticks & check count for each */ SnortConfig *sc = snort_conf; if ((sc == NULL) || (sc->profile_rules.num == 0)) return; detection_option_tree_update_otn_stats(sc->detection_option_tree_hash_table); CollectRTNProfile(); /* Specifically call out a top xxx or something? */ PrintWorstRules(sc->profile_rules.num); return; } /* The preprocessor profile list is only accessed for printing stats when * Snort shuts down, so adding new nodes during a reload shouldn't be a * problem. */ void RegisterPreprocessorProfile(const char *keyword, PreprocStats *stats, int layer, PreprocStats *parent, StatsNodeFreeFunc freefn) { PreprocStatsNode *node; if (stats == NULL) return; node = (PreprocStatsNode *)SnortAlloc(sizeof(PreprocStatsNode)); if (PreprocStatsNodeList == NULL) { PreprocStatsNodeList = node; } else { PreprocStatsNode *tmp = PreprocStatsNodeList; PreprocStatsNode *last; do { if (strcasecmp(tmp->name, keyword) == 0) { //FatalError("Duplicate Preprocessor Stats Name (%s)\n", keyword); /* Don't fatal error here since during a reload there are * probably going to be dups - just return */ //multiple policy support free(node); return; } last = tmp; tmp = tmp->next; } while (tmp != NULL); last->next = node; } node->name = SnortStrdup(keyword); node->stats = stats; /* Set the stats reference */ node->parent = parent; node->layer = layer; node->freefn = freefn; if (layer > max_layers) max_layers = layer; } void FreePreprocPerformance(Preproc_WorstPerformer *idx) { Preproc_WorstPerformer *child, *tmp; child = idx->children; while (child) { FreePreprocPerformance(child); tmp = child; child = child->next; free(tmp); } } void PrintPreprocPerformance(TextLog *log, int num, Preproc_WorstPerformer *idx) { Preproc_WorstPerformer *child; int i; /* indent 'Num' based on the layer */ unsigned int indent = 6 - (5 - idx->node->layer); if (num != 0) { indent += 2; if(log) { TextLog_Print(log, "%*d%*s%*d" FMTu64("*") FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", indent, num, 28 - indent, idx->node->name, 6, idx->node->layer, 11, idx->node->stats->checks, 11, idx->node->stats->exits, 20, (uint64_t)(idx->node->stats->ticks/ticks_per_microsec), 11, idx->ticks_per_check/ticks_per_microsec, 14, idx->pct_of_parent, 13, idx->pct_of_total); } else { LogMessage("%*d%*s%*d" FMTu64("*") FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", indent, num, 28 - indent, idx->node->name, 6, idx->node->layer, 11, idx->node->stats->checks, 11, idx->node->stats->exits, 20, (uint64_t)(idx->node->stats->ticks/ticks_per_microsec), 11, idx->ticks_per_check/ticks_per_microsec, 14, idx->pct_of_parent, 13, idx->pct_of_total); } } else { /* The totals */ indent += strlen(idx->node->name); if(log) { TextLog_Print(log, "%*s%*s%*d" FMTu64("*") FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", indent, idx->node->name, 28 - indent, idx->node->name, 6, idx->node->layer, 11, idx->node->stats->checks, 11, idx->node->stats->exits, 20, (uint64_t)(idx->node->stats->ticks/ticks_per_microsec), 11, idx->ticks_per_check/ticks_per_microsec, 14, idx->pct_of_parent, 13, idx->pct_of_parent); } else { LogMessage("%*s%*s%*d" FMTu64("*") FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", indent, idx->node->name, 28 - indent, idx->node->name, 6, idx->node->layer, 11, idx->node->stats->checks, 11, idx->node->stats->exits, 20, (uint64_t)(idx->node->stats->ticks/ticks_per_microsec), 11, idx->ticks_per_check/ticks_per_microsec, 14, idx->pct_of_parent, 13, idx->pct_of_parent); } } child = idx->children; i = 1; while (child) { PrintPreprocPerformance(log, i++, child); child = child->next; } } void CleanupPreprocStatsNodeList(void) { PreprocStatsNode *node, *nxt; node = PreprocStatsNodeList; while (node) { nxt = node->next; if (node->freefn) node->freefn(node->stats); free(node->name); free(node); node = nxt; } PreprocStatsNodeList = NULL; } void CleanupPreprocPerformance(Preproc_WorstPerformer *worst) { Preproc_WorstPerformer *idx, *child, *tmp; idx = worst; while (idx) { tmp = idx->next; child = idx->children; CleanupPreprocPerformance(child); free(idx); idx = tmp; } } void PrintWorstPreprocs(int numToPrint) { Preproc_WorstPerformer *idx; Preproc_WorstPerformer *total = NULL; int num = 0; TextLog *log = NULL; time_t cur_time; char fullname[STD_BUF]; int ret; SnortConfig *sc = snort_conf; getTicksPerMicrosec(); cur_time = time(NULL); if (sc->profile_preprocs.filename != NULL) { if (sc->profile_preprocs.append) { log = TextLog_Init(sc->profile_preprocs.filename, 512*1024, 512*1024); if (log != NULL) TextLog_Print(log, "\ntimestamp: %u\n", cur_time); } else { ret = SnortSnprintf(fullname, STD_BUF, "%s.%u", sc->profile_preprocs.filename, (uint32_t)cur_time); if(ret != SNORT_SNPRINTF_SUCCESS) FatalError("profiler: file path+name too long\n"); log = TextLog_Init(fullname, 512*1024, 512*1024); } } if (numToPrint != -1) { if(log) { TextLog_Print(log, "Preprocessor Profile Statistics (worst %d)\n", numToPrint); } else { LogMessage("Preprocessor Profile Statistics (worst %d)\n", numToPrint); } } else { if(log) { TextLog_Print(log, "Preprocessor Profile Statistics (all)\n"); } else { LogMessage("Preprocessor Profile Statistics (all)\n"); } } if(log) { TextLog_Print(log, "==========================================================\n"); } else { LogMessage("==========================================================\n"); } if (!worstPreprocPerformers) { if(log) { TextLog_Print(log, "No Preprocessors were profiled\n"); TextLog_Term(log); } else { LogMessage("No Preprocessors were profiled\n"); } CleanupPreprocPerformance(worstPreprocPerformers); worstPreprocPerformers = NULL; return; } if(log) { TextLog_Print(log, "%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", 4, "Num", 24, "Preprocessor", 6, "Layer", 11, "Checks", 11, "Exits", 20, "Microsecs", 11, "Avg/Check", 14, "Pct of Caller", 13, "Pct of Total"); } else { LogMessage("%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", 4, "Num", 24, "Preprocessor", 6, "Layer", 11, "Checks", 11, "Exits", 20, "Microsecs", 11, "Avg/Check", 14, "Pct of Caller", 13, "Pct of Total"); } if(log) { TextLog_Print(log, "%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", 4, "===", 24, "============", 6, "=====", 11, "======", 11, "=====", 20, "=========", 11, "=========", 14, "=============", 13, "============"); } else { LogMessage("%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", 4, "===", 24, "============", 6, "=====", 11, "======", 11, "=====", 20, "=========", 11, "=========", 14, "=============", 13, "============"); } for (idx = worstPreprocPerformers, num=1; idx && ((numToPrint < 0) ? 1 : (num <= numToPrint)); idx= idx->next, num++) { /* Skip the total counter */ if (idx->node->stats == &totalPerfStats) { num--; total = idx; continue; } //if (!idx) // break; PrintPreprocPerformance(log, num, idx); //LogMessage("%*d%*s%*d%*d" FMTu64("*") "%*.1f%*.1f\n", // 6, num, 20, idx->node->name, 6, idx->node->layer, // 11, idx->node->stats->checks, // 11, idx->node->stats->exits, // 20, idx->node->stats->ticks, // 11, idx->ticks_per_check, // 14, idx->pct_of_parent, // 14, idx->pct_of_total); } if (total) PrintPreprocPerformance(log, 0, total); if(log) TextLog_Term(log); CleanupPreprocPerformance(worstPreprocPerformers); worstPreprocPerformers = NULL; } Preproc_WorstPerformer *findPerfParent(PreprocStatsNode *node, Preproc_WorstPerformer *top) { Preproc_WorstPerformer *list = top; Preproc_WorstPerformer *parent; if (!list) return NULL; if (list->node->layer > node->layer) return NULL; while (list) { if (list->node->stats == node->parent) { parent = list; return parent; } parent = findPerfParent(node, list->children); if (parent) return parent; list = list->next; } return NULL; } void ResetPreprocProfiling(void) { PreprocStatsNode *idx = NULL; SnortConfig *sc = snort_conf; if ((sc == NULL) || (sc->profile_preprocs.num == 0)) return; for (idx = PreprocStatsNodeList; idx != NULL; idx = idx->next) { idx->stats->ticks = 0; idx->stats->ticks_start = 0; idx->stats->checks = 0; idx->stats->exits = 0; } } void ShowPreprocProfiles(void) { /* Cycle through all Rules, print ticks & check count for each */ PreprocStatsNode *idx; int layer; Preproc_WorstPerformer *parent, *new, *this = NULL, *last = NULL; char got_position; Preproc_WorstPerformer *listhead; double ticks_per_check; SnortConfig *sc = snort_conf; if ((sc == NULL) || (sc->profile_preprocs.num == 0)) return; /* Adjust mpse stats to not include rule evaluation */ mpsePerfStats.ticks -= rulePerfStats.ticks; /* And adjust the rules to include the NC rules */ rulePerfStats.ticks += ncrulePerfStats.ticks; for (layer=0;layer<=max_layers;layer++) { for (idx = PreprocStatsNodeList; idx; idx = idx->next) { if (idx->stats->checks == 0 || idx->stats->ticks == 0) continue; if (idx->layer != layer) continue; last = NULL; ticks_per_check = (double)idx->stats->ticks/(double)idx->stats->checks; new = (Preproc_WorstPerformer *)SnortAlloc(sizeof(Preproc_WorstPerformer)); new->node = idx; new->ticks_per_check = ticks_per_check; if (idx->parent) { /* Find this idx's parent in the list */ parent = findPerfParent(idx, worstPreprocPerformers); if (parent && (parent->node->stats != &totalPerfStats)) { listhead = parent->children; } else { listhead = worstPreprocPerformers; parent = NULL; } new->pct_of_parent = (double)idx->stats->ticks/idx->parent->ticks*100.0; new->pct_of_total = (double)idx->stats->ticks/totalPerfStats.ticks*100.0; } else { parent = NULL; new->pct_of_parent = 0.0; new->pct_of_total = 100.0; listhead = worstPreprocPerformers; } got_position = 0; for (this = listhead; this && !got_position; this = this->next) { last = this; switch (sc->profile_preprocs.sort) { case PROFILE_SORT_CHECKS: if (new->node->stats->checks >= this->node->stats->checks) { got_position = 1; } break; case PROFILE_SORT_TOTAL_TICKS: if (new->node->stats->ticks >= this->node->stats->ticks) { got_position = 1; } break; default: case PROFILE_SORT_AVG_TICKS: if (new->ticks_per_check >= this->ticks_per_check) { got_position = 1; } break; } if (got_position) break; } if (this) { new->next = this; new->prev = this->prev; this->prev = new; if (new->prev) new->prev->next = new; /* Reset the head of the list */ if (this == listhead) { if (parent) { parent->children = new; } else { worstPreprocPerformers = new; } } } else { if (!last) { if (parent) { parent->children = new; } else { worstPreprocPerformers = new; } } else { new->prev = last; last->next = new; } } } } PrintWorstPreprocs(sc->profile_preprocs.num); CleanupPreprocStatsNodeList(); } #endif snort-2.9.20/src/mstring.h0000644000175000017500000000320714241076642013524 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* $Id$ */ #ifndef __MSTRING_H__ #define __MSTRING_H__ /* D E F I N E S *******************************************************/ #define TOKS_BUF_SIZE 100 /* P R O T O T Y P E S *************************************************/ char ** mSplit(const char *, const char *, const int, int *, const char); void mSplitFree(char ***toks, int numtoks); int mContainsSubstr(const char *, int, const char *, int); int mSearch(const char *, int, const char *, int, int *, int *); int mSearchCI(const char *, int, const char *, int, int *, int *); int mSearchREG(const char *, int, const char *, int, int *, int *); int *make_skip(char *, int); int *make_shift(char *, int); #endif /* __MSTRING_H__ */ snort-2.9.20/src/build.h0000644000175000017500000000002314241074701013123 0ustar apoapo#define BUILD "82" snort-2.9.20/src/snort.c0000644000175000017500000052755214241077405013215 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * * Program: Snort * * Purpose: Check out the README file for info on what you can do * with Snort. * * Author: Martin Roesch (roesch@clark.net) * * Comments: Ideas and code stolen liberally from Mike Borella's IP Grab * program. Check out his stuff at http://www.borella.net. I * also have ripped some util functions from TCPdump, plus Mike's * prog is derived from it as well. All hail TCPdump.... * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_GETTID #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_MALLOC_TRIM #include #endif #ifndef WIN32 #include #else #include #endif #ifdef HAVE_GETOPT_LONG //#define _GNU_SOURCE /* A GPL copy of getopt & getopt_long src code is now in sfutil */ # undef HAVE_GETOPT_LONG #endif #include #ifdef HAVE_STRINGS_H # include #endif #ifndef WIN32 # include # include # include # include # include #endif /* !WIN32 */ #if !defined(CATCH_SEGV) && !defined(WIN32) # include #endif #include "decode.h" #include "encode.h" #include "sfdaq.h" #include "active.h" #include "snort.h" #include "rules.h" #include "treenodes.h" #include "plugbase.h" #include "snort_debug.h" #include "util.h" #include "parser.h" #include "tag.h" #include "log.h" #include "detect.h" #include "mstring.h" #include "fpcreate.h" #include "fpdetect.h" #include "sfthreshold.h" #include "rate_filter.h" #include "packet_time.h" #include "detection-plugins/sp_flowbits.h" #include "preprocessors/spp_perfmonitor.h" #include "preprocessors/perf-base.h" #include "preprocessors/perf.h" #include "mempool.h" #include "strlcpyu.h" #include "sflsq.h" #include "sp_replace.h" #include "output-plugins/spo_log_tcpdump.h" #include "event_queue.h" #include "asn1.h" #include "mpse.h" #include "generators.h" #include "ppm.h" #include "profiler.h" #include "dynamic-plugins/sp_dynamic.h" #include "dynamic-plugins/sf_dynamic_define.h" #include "dynamic-output/plugins/output.h" #include "sfutil/strvec.h" #include "detection_util.h" #include "sfcontrol_funcs.h" #include "idle_processing_funcs.h" #include "file_service.h" #include "pkt_tracer.h" #include "session_expect.h" #include "reload.h" #include "reg_test.h" #include "memory_stats.h" #include "pthread.h" #ifdef SIDE_CHANNEL # include "sidechannel.h" #endif #include "dynamic-plugins/sf_dynamic_engine.h" #include "dynamic-plugins/sf_dynamic_detection.h" #define PROFILE_PREPROCS_NOREDEF #include "dynamic-plugins/sf_dynamic_preprocessor.h" #include "dynamic-plugins/sp_preprocopt.h" #ifdef SIDE_CHANNEL # include "dynamic-plugins/sf_dynamic_side_channel.h" #endif #ifdef TARGET_BASED # include "target-based/sftarget_reader.h" #endif #ifdef EXIT_CHECK # include "cpuclock.h" #endif #include "sfActionQueue.h" #ifdef INTEL_SOFT_CPM #include "sfutil/intel-soft-cpm.h" #endif #include "session_api.h" #include "stream_common.h" #include "stream5_ha.h" #ifdef CONTROL_SOCKET #include "dump.h" #endif #ifdef PERF_PROFILING #include "perf_indicators.h" #endif /* Macros *********************************************************************/ #ifndef DLT_LANE8023 /* * Old OPEN BSD Log format is 17. * Define DLT_OLDPFLOG unless DLT_LANE8023 (Suse 6.3) is already * defined in bpf.h. */ # define DLT_OLDPFLOG 17 #endif #define ALERT_MODE_OPT__NONE "none" #define ALERT_MODE_OPT__PKT_CNT "packet-count" #define ALERT_MODE_OPT__FULL "full" #define ALERT_MODE_OPT__FAST "fast" #define ALERT_MODE_OPT__CONSOLE "console" #define ALERT_MODE_OPT__CMG "cmg" #define ALERT_MODE_OPT__JH "jh" #define ALERT_MODE_OPT__DJR "djr" #define ALERT_MODE_OPT__AJK "ajk" #define ALERT_MODE_OPT__UNIX_SOCK "unsock" #define ALERT_MODE_OPT__TEST "test" #define LOG_MODE_OPT__NONE "none" #define LOG_MODE_OPT__PCAP "pcap" #define LOG_MODE_OPT__ASCII "ascii" #ifdef MPLS # define MPLS_PAYLOAD_OPT__IPV4 "ipv4" # define MPLS_PAYLOAD_OPT__IPV6 "ipv6" # define MPLS_PAYLOAD_OPT__ETHERNET "ethernet" #endif #define DEFAULT_PAF_MAX 16384 /* Instead of 16k as Flowcount, We should use a smaller number for idle pruning in SnortIdle() to avoid AAB. * Now in snort Idle case, we will prune the sessions with in AAB timeout (Miminum configurable AAB value). * Tested and found out, on an average 0.2 ms is taking to prune one session. * FlowCount = (AAB timeout / time to prune onesession ) / 3 (tcp+udp+ip) */ #define AAB_THRESHOLD 250 #define TIME_TO_PRUNE_ONE_SESSION 0.2 const uint32_t FLOW_COUNT = (AAB_THRESHOLD/TIME_TO_PRUNE_ONE_SESSION) / 3; volatile int detection_lib_changed = 0; #ifdef SNORT_RELOAD extern volatile bool reloadInProgress; #endif /* Data types *****************************************************************/ typedef enum _GetOptArgType { LONGOPT_ARG_NONE = 0, LONGOPT_ARG_REQUIRED, LONGOPT_ARG_OPTIONAL } GetOptArgType; /* Externs *******************************************************************/ /* Undefine the one from sf_dynamic_preprocessor.h */ #ifdef PERF_PROFILING extern PreprocStats detectPerfStats, decodePerfStats, metaPerfStats, totalPerfStats, eventqPerfStats, rulePerfStats, mpsePerfStats; extern PreprocStats ruleCheckBitPerfStats, ruleSetBitPerfStats, ruleFailedFlowbitsPerfStats; extern PreprocStats ruleRTNEvalPerfStats, ruleOTNEvalPerfStats, ruleHeaderNoMatchPerfStats; extern PreprocStats ruleAddEventQPerfStats, ruleNQEventQPerfStats; extern PreprocStats preprocRuleOptionPerfStats; #endif /* for getopt */ extern char *optarg; extern int optind; extern int opterr; extern int optopt; extern ListHead *head_tmp; /* Globals/Public *************************************************************/ PacketCount pc; /* packet count information */ uint32_t *netmasks = NULL; /* precalculated netmask array */ char **protocol_names = NULL; char *snort_conf_file = NULL; /* -c */ char *snort_conf_dir = NULL; SnortConfig *snort_cmd_line_conf = NULL; SnortConfig *snort_conf = NULL; int internal_log_level = INTERNAL_LOG_LEVEL__MESSAGE; tSfActionQueueId decoderActionQ = NULL; MemPool decoderAlertMemPool; VarNode *cmd_line_var_list = NULL; static pthread_mutex_t cleanup_mutex = PTHREAD_MUTEX_INITIALIZER; #ifdef TARGET_BASED pthread_t attribute_reload_thread_id; pid_t attribute_reload_thread_pid; volatile int attribute_reload_thread_running = 0; volatile int attribute_reload_thread_stop = 0; int reload_attribute_table_flags = 0; #endif volatile bool snort_initializing = true; volatile int snort_exiting = 0; volatile int already_exiting = 0; static pid_t snort_main_thread_pid = 0; #ifndef WIN32 static pthread_t snort_main_thread_id = 0; #endif #if defined(SNORT_RELOAD) && !defined(WIN32) volatile int snort_reload = 0; static pthread_t snort_reload_thread_id; volatile int snort_reload_thread_created = 0; pid_t snort_reload_thread_pid; #endif const struct timespec thread_sleep = { 0, 100 }; #ifdef OPENBSD const struct timespec packet_sleep = { 0, 1 }; #endif #ifdef HAVE_PCAP_LEX_DESTROY extern void pcap_lex_destroy(void); #endif PreprocConfigFuncNode *preproc_config_funcs = NULL; OutputConfigFuncNode *output_config_funcs = NULL; RuleOptConfigFuncNode *rule_opt_config_funcs = NULL; RuleOptOverrideInitFuncNode *rule_opt_override_init_funcs = NULL; RuleOptParseCleanupNode *rule_opt_parse_cleanup_list = NULL; RuleOptByteOrderFuncNode *rule_opt_byte_order_funcs = NULL; PreprocSignalFuncNode *preproc_clean_exit_funcs = NULL; PreprocSignalFuncNode *preproc_shutdown_funcs = NULL; PreprocSignalFuncNode *preproc_reset_funcs = NULL; PreprocSignalFuncNode *preproc_reset_stats_funcs = NULL; PreprocStatsFuncNode *preproc_stats_funcs = NULL; PluginSignalFuncNode *plugin_shutdown_funcs = NULL; PluginSignalFuncNode *plugin_clean_exit_funcs = NULL; #ifdef SNORT_RELOAD PostConfigFuncNode *plugin_reload_funcs = NULL; #endif OutputFuncNode *AlertList = NULL; /* Alert function list */ OutputFuncNode *LogList = NULL; /* Log function list */ PeriodicCheckFuncNode *periodic_check_funcs = NULL; grinder_t grinder; pthread_mutex_t dynamic_rules_lock; #ifdef SIDE_CHANNEL pthread_mutex_t snort_process_lock; static bool snort_process_lock_held = false; #endif uint8_t iprep_current_update_counter; bool periodic_dump_enable = false; /* Locals/Private ************************************************************/ static long int pcap_loop_count = 0; static SF_QUEUE *pcap_save_queue = NULL; #if defined(INLINE_FAILOPEN) && !defined(WIN32) static pthread_t inline_failopen_thread_id; static pid_t inline_failopen_thread_pid; static volatile int inline_failopen_thread_running = 0; static volatile int inline_failopen_initialized = 0; static int inline_failopen_pass_pkt_cnt = 0; static void * SnortPostInitThread(void *); static DAQ_Verdict IgnoreCallback (void*, const DAQ_PktHdr_t*, const uint8_t*); #endif static char signal_error_msg[STD_BUF]; static int exit_signal = 0; static bool dump_stats_signal = false; static bool rotate_stats_signal = false; #ifdef TARGET_BASED static bool no_attr_table_signal = false; #endif #ifndef SNORT_RELOAD static volatile bool reload_signal = false; #else /* reload_signal is incremented in the signal handler for SIGNAL_SNORT_RELOAD * which is handled in the main thread. The reload thread compares the * reload_signal count to reload_total which it increments after an equality * test between reload_signal and reload_total fails (which means we got a new * SIGNAL_SNORT_RELOAD). They need to be the same type and size to do this * comparison. See ReloadConfigThread() */ volatile snort_reload_t reload_signal = 0; snort_reload_t reload_total = 0; #endif static int done_processing = 0; static int exit_logged = 0; static SF_LIST *pcap_object_list = NULL; static SF_QUEUE *pcap_queue = NULL; static char* pcap_filter = NULL; static int snort_argc = 0; static char **snort_argv = NULL; /* command line options for getopt */ static const char *valid_options = "?A:bB:c:CdDeEfF:" #ifndef WIN32 "g:" #endif "G:h:Hi:Ik:K:l:L:" #ifndef WIN32 "m:" #endif "Mn:NOpP:q" #ifndef WIN32 "Q" #endif "r:R:sS:" #ifndef WIN32 "t:" #endif "T" #ifndef WIN32 "u:" #endif "UvVw:" #ifdef WIN32 "W" #endif "XxyZ:z:" ; static struct option long_options[] = { {"logid", LONGOPT_ARG_REQUIRED, NULL, 'G'}, {"perfmon-file", LONGOPT_ARG_REQUIRED, NULL, 'Z'}, {"snaplen", LONGOPT_ARG_REQUIRED, NULL, 'P'}, {"version", LONGOPT_ARG_NONE, NULL, 'V'}, {"help", LONGOPT_ARG_NONE, NULL, '?'}, {"conf-error-out", LONGOPT_ARG_NONE, NULL,'x'}, {"dynamic-engine-lib", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_ENGINE_FILE}, {"dynamic-engine-lib-dir", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_ENGINE_DIRECTORY}, {"dynamic-detection-lib", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_LIBRARY_FILE}, {"dynamic-detection-lib-dir", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_LIBRARY_DIRECTORY}, {"dump-dynamic-rules", LONGOPT_ARG_REQUIRED, NULL, DUMP_DYNAMIC_RULES}, {"dynamic-preprocessor-lib", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_PREPROC_FILE}, {"dynamic-preprocessor-lib-dir", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_PREPROC_DIRECTORY}, {"dynamic-output-lib", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_OUTPUT_FILE}, {"dynamic-output-lib-dir", LONGOPT_ARG_REQUIRED, NULL, DYNAMIC_OUTPUT_DIRECTORY}, {"alert-before-pass", LONGOPT_ARG_NONE, NULL, ALERT_BEFORE_PASS}, {"treat-drop-as-alert", LONGOPT_ARG_NONE, NULL, TREAT_DROP_AS_ALERT}, {"treat-drop-as-ignore", LONGOPT_ARG_NONE, NULL, TREAT_DROP_AS_IGNORE}, {"process-all-events", LONGOPT_ARG_NONE, NULL, PROCESS_ALL_EVENTS}, {"pid-path", LONGOPT_ARG_REQUIRED, NULL, PID_PATH}, {"create-pidfile", LONGOPT_ARG_NONE, NULL, CREATE_PID_FILE}, {"nolock-pidfile", LONGOPT_ARG_NONE, NULL, NOLOCK_PID_FILE}, {"no-interface-pidfile", LONGOPT_ARG_NONE, NULL, NO_IFACE_PID_FILE}, #ifdef INLINE_FAILOPEN {"disable-inline-init-failopen", LONGOPT_ARG_NONE, NULL, DISABLE_INLINE_FAILOPEN}, #endif {"nostamps", LONGOPT_ARG_NONE, NULL, NO_LOGGING_TIMESTAMPS}, #ifdef TARGET_BASED {"disable-attribute-reload-thread", LONGOPT_ARG_NONE, NULL, DISABLE_ATTRIBUTE_RELOAD}, #endif {"pcap-single", LONGOPT_ARG_REQUIRED, NULL, PCAP_SINGLE}, {"pcap-file", LONGOPT_ARG_REQUIRED, NULL, PCAP_FILE_LIST}, {"pcap-list", LONGOPT_ARG_REQUIRED, NULL, PCAP_LIST}, #ifndef WIN32 {"pcap-dir", LONGOPT_ARG_REQUIRED, NULL, PCAP_DIR}, {"pcap-filter", LONGOPT_ARG_REQUIRED, NULL, PCAP_FILTER}, {"pcap-no-filter", LONGOPT_ARG_NONE, NULL, PCAP_NO_FILTER}, #endif {"pcap-loop", LONGOPT_ARG_REQUIRED, NULL, PCAP_LOOP}, {"pcap-reload", LONGOPT_ARG_NONE, NULL, PCAP_RELOAD}, {"pcap-reset", LONGOPT_ARG_NONE, NULL, PCAP_RESET}, {"pcap-show", LONGOPT_ARG_NONE, NULL, PCAP_SHOW}, #ifdef EXIT_CHECK {"exit-check", LONGOPT_ARG_REQUIRED, NULL, ARG_EXIT_CHECK}, #endif {"search-method", LONGOPT_ARG_REQUIRED, NULL, DETECTION_SEARCH_METHOD}, {"man", LONGOPT_ARG_REQUIRED, NULL, DETECTION_SEARCH_METHOD}, #ifdef MPLS {"enable-mpls-multicast", LONGOPT_ARG_NONE, NULL, ENABLE_MPLS_MULTICAST}, {"enable-mpls-overlapping-ip", LONGOPT_ARG_NONE, NULL, ENABLE_OVERLAPPING_IP}, {"max-mpls-labelchain-len", LONGOPT_ARG_REQUIRED, NULL, MAX_MPLS_LABELCHAIN_LEN}, {"mpls-payload-type", LONGOPT_ARG_REQUIRED, NULL, MPLS_PAYLOAD_TYPE}, #endif {"require-rule-sid", LONGOPT_ARG_NONE, NULL, REQUIRE_RULE_SID}, {"daq", LONGOPT_ARG_REQUIRED, NULL, ARG_DAQ_TYPE}, {"daq-mode", LONGOPT_ARG_REQUIRED, NULL, ARG_DAQ_MODE}, {"daq-var", LONGOPT_ARG_REQUIRED, NULL, ARG_DAQ_VAR}, {"daq-dir", LONGOPT_ARG_REQUIRED, NULL, ARG_DAQ_DIR}, {"daq-list", LONGOPT_ARG_OPTIONAL, NULL, ARG_DAQ_LIST}, {"dirty-pig", LONGOPT_ARG_NONE, NULL, ARG_DIRTY_PIG}, {"enable-inline-test", LONGOPT_ARG_NONE, NULL, ENABLE_INLINE_TEST}, {"cs-dir", LONGOPT_ARG_REQUIRED, NULL, ARG_CS_DIR}, {"ha-peer", LONGOPT_ARG_NONE, NULL, ARG_HA_PEER}, {"ha-out", LONGOPT_ARG_REQUIRED, NULL, ARG_HA_OUT}, {"ha-in", LONGOPT_ARG_REQUIRED, NULL, ARG_HA_IN}, {"ha-pdts-in", LONGOPT_ARG_REQUIRED, NULL, ARG_HA_PDTS_IN}, {"suppress-config-log", LONGOPT_ARG_NONE, NULL, SUPPRESS_CONFIG_LOG}, #ifdef DUMP_BUFFER {"buffer-dump", LONGOPT_ARG_OPTIONAL, NULL, BUFFER_DUMP}, {"buffer-dump-alert", LONGOPT_ARG_OPTIONAL, NULL, BUFFER_DUMP_ALERT}, #endif {0, 0, 0, 0} }; #ifdef DUMP_BUFFER bool dump_alert_only; bool dumped_state; bool dump_enabled; TraceBuffer *(*getBuffers[MAX_BUFFER_DUMP_FUNC])(void); BufferDumpEnableMask bdmask; #endif typedef void (*log_func_t)(Packet*); static void LogPacket (Packet* p) { pc.log_pkts++; CallLogPlugins(p, NULL, NULL); } static void IgnorePacket (Packet* p) { } static log_func_t log_func = IgnorePacket; /* Private function prototypes ************************************************/ static void InitNetmasks(void); static void InitProtoNames(void); static const char* GetPacketSource(char**); static void SnortInit(int, char **); static void InitPidChrootAndPrivs(pid_t); static void ParseCmdLine(int, char **); static int ShowUsage(char *); static void PrintVersion(SnortConfig *); static void SetSnortConfDir(void); static void InitGlobals(void); static void InitSignals(void); #if defined(NOCOREFILE) && !defined(WIN32) static void SetNoCores(void); #endif static void SnortCleanup(int); static void ParseCmdLineDynamicLibInfo(SnortConfig *, int, char *); static DynamicLibInfo * DupDynamicLibInfo(DynamicLibInfo *); static void FreeDynamicLibInfo(DynamicLibInfo *); static void FreeDynamicLibInfos(SnortConfig *); static void FreeOutputConfigs(OutputConfig *); #ifdef SIDE_CHANNEL static void FreeSideChannelModuleConfigs(SideChannelModuleConfig *); #endif static void FreePreprocConfigs(SnortConfig *); static void FreeRuleStateList(RuleState *); static void FreeClassifications(ClassType *); static void FreeReferences(ReferenceSystemNode *); static void FreePlugins(SnortConfig *); static void FreePreprocessors(SnortConfig *); static void SnortUnprivilegedInit(void); static int SetPktProcessor(void); static void PacketLoop(void); #if 0 static char * ConfigFileSearch(void); #endif static void SnortReset(void); static void LoadDynamicPlugins(SnortConfig *); static void SnortIdle(void); #ifndef WIN32 static void SnortStartThreads(void); #endif /* Signal handler declarations ************************************************/ static void SigDumpStatsHandler(int); static void SigExitHandler(int); static void SigReloadHandler(int); static void SigRotateStatsHandler(int); #ifdef CONTROL_SOCKET static void SigPipeHandler(int); #endif static void SigOopsHandler(int); int InMainThread () { return ( #ifndef WIN32 pthread_equal(snort_main_thread_id, pthread_self()) #else 1 #endif ); } bool SnortIsInitializing( ) { #if defined(INLINE_FAILOPEN) && !defined(WIN32) return snort_initializing && !inline_failopen_initialized; #else return snort_initializing; #endif } static int IsProcessingPackets(uint16_t type, const uint8_t *data, uint32_t length, void **new_config, char *statusBuf, int statusBuf_len) { return (!snort_initializing && !snort_exiting && !exit_signal) ? 0 : -1; } /* F U N C T I O N D E F I N I T I O N S **********************************/ #define INLINE_FAIL_OPEN_NOT_USED 0 #define INLINE_FAIL_OPEN_COMPLETE 1 #define INLINE_FAIL_OPEN_ERROR 2 static int InlineFailOpen (void) { #if defined(INLINE_FAILOPEN) && !defined(WIN32) int error = 0; if (ScAdapterInlineMode() && !ScReadMode() && !ScDisableInlineFailopen()) { /* If in inline mode, start a thread to handle the rest of snort * initialization, then dispatch packets until that initialization * is complete. */ LogMessage("Fail Open Thread starting..\n"); if (pthread_create(&inline_failopen_thread_id, NULL, SnortPostInitThread, NULL)) { ErrorMessage("Failed to start Fail Open Thread. " "Starting normally\n"); } else { while (!inline_failopen_thread_running) nanosleep(&thread_sleep, NULL); LogMessage("Fail Open Thread started tid=%p (pid=%u)\n", (void*)inline_failopen_thread_id, inline_failopen_thread_pid); # ifdef DEBUG { FILE *tmp = fopen("/var/tmp/fo_threadid", "w"); if ( tmp ) { fprintf(tmp, "Fail Open Thread PID: %u\n", inline_failopen_thread_pid); fclose(tmp); } } # endif DAQ_Start(); SetPktProcessor(); inline_failopen_initialized = 1; /* Passing packets is in the main thread because some systems * may have to refer to packet passing thread via process id * (linuxthreads) */ while (snort_initializing) { error = DAQ_Acquire(1, IgnoreCallback, NULL); if (error) break; } pthread_join(inline_failopen_thread_id, NULL); inline_failopen_thread_running = 0; LogMessage("Fail Open Thread terminated, passed %d packets.\n", inline_failopen_pass_pkt_cnt); if(error) return INLINE_FAIL_OPEN_ERROR; else return INLINE_FAIL_OPEN_COMPLETE; } } #endif return INLINE_FAIL_OPEN_NOT_USED; } /* * * Function: main(int, char *) * * Purpose: Handle program entry and exit, call main prog sections * This can handle both regular (command-line) style * startup, as well as Win32 Service style startup. * * Arguments: See command line args in README file * * Returns: 0 => normal exit, 1 => exit on error * */ int main(int argc, char *argv[]) { #if defined(WIN32) && defined(ENABLE_WIN32_SERVICE) /* Do some sanity checking, because some people seem to forget to * put spaces between their parameters */ if ((argc > 1) && ((_stricmp(argv[1], (SERVICE_CMDLINE_PARAM SERVICE_INSTALL_CMDLINE_PARAM)) == 0) || (_stricmp(argv[1], (SERVICE_CMDLINE_PARAM SERVICE_UNINSTALL_CMDLINE_PARAM)) == 0) || (_stricmp(argv[1], (SERVICE_CMDLINE_PARAM SERVICE_SHOW_CMDLINE_PARAM)) == 0))) { FatalError("You must have a space after the '%s' command-line parameter\n", SERVICE_CMDLINE_PARAM); } /* If the first parameter is "/SERVICE", then start Snort as a Win32 service */ if((argc > 1) && (_stricmp(argv[1],SERVICE_CMDLINE_PARAM) == 0)) { return SnortServiceMain(argc, argv); } #endif /* WIN32 && ENABLE_WIN32_SERVICE */ snort_argc = argc; snort_argv = argv; return SnortMain(argc, argv); } /* * * Function: SnortMain(int, char *) * * Purpose: The real place that the program handles entry and exit. Called * called by main(), or by SnortServiceMain(). * * Arguments: See command line args in README file * * Returns: 0 => normal exit, 1 => exit on error * */ int SnortMain(int argc, char *argv[]) { char* tmp_ptr = NULL; const char* intf; int daqInit; #ifndef WIN32 // must be done now in case of fatal error // and again after daemonization snort_main_thread_id = pthread_self(); #endif SnortInit(argc, argv); intf = GetPacketSource(&tmp_ptr); daqInit = intf || snort_conf->daq_type; if ( daqInit ) { DAQ_Init(snort_conf); DAQ_New(snort_conf, intf); DAQ_UpdateTunnelBypass(snort_conf); } if ( tmp_ptr ) free(tmp_ptr); if ( ScDaemonMode() ) { GoDaemon(); } // this must follow daemonization snort_main_thread_pid = gettid(); #ifndef WIN32 snort_main_thread_id = pthread_self(); #endif #ifndef WIN32 /* Change groups */ InitGroups(ScUid(), ScGid()); #endif #if !defined(HAVE_LINUXTHREADS) && !defined(WIN32) // this could be moved to linux threads location // and only done there SnortStartThreads(); #endif ReloadControlSocketRegister(); /* For SFR CLI*/ ControlSocketRegisterHandler(CS_TYPE_ACTION_STATS, NULL, NULL, &DisplayActionStats); if (ControlSocketRegisterHandler(CS_TYPE_IS_PROCESSING, &IsProcessingPackets, NULL, NULL)) { LogMessage("Failed to register the is processing control handler.\n"); } if (ControlSocketRegisterHandler(CS_TYPE_PKT_TRACER, &DebugPktTracer, NULL, NULL)) { LogMessage("Failed to register the packet tracer control handler.\n"); } #ifdef CONTROL_SOCKET if (ControlSocketRegisterHandler(CS_TYPE_DUMP_PACKETS, &PacketDumpCommand, NULL, NULL)) { LogMessage("Failed to register the packet dump control handler.\n"); } #endif if (ControlSocketRegisterHandler(CS_TYPE_MEM_USAGE, &MemoryPreFunction, &MemoryControlFunction, &MemoryPostFunction)) { LogMessage("Failed to register the memory stats display handler.\n"); } if (ControlSocketRegisterHandler(CS_TYPE_MEM_STATS_CFG, &PPMemoryStatsDumpCfg, NULL, NULL)) { LogMessage("Failed to register the preprocessor memory stats dump enable/disable handler.\n"); } if (ControlSocketRegisterHandler(CS_TYPE_MEM_STATS_SHOW, NULL, NULL, &PPMemoryStatsDumpShow)) { LogMessage("Failed to register the preprocessor memory stats dump show handler.\n"); } if ( ScTestMode() ) { if ( daqInit && DAQ_UnprivilegedStart() ) SetPktProcessor(); SnortUnprivilegedInit(); } else if ( DAQ_UnprivilegedStart() ) { SnortUnprivilegedInit(); SetPktProcessor(); DAQ_Start(); } else { switch(InlineFailOpen()) { case INLINE_FAIL_OPEN_COMPLETE: break; case INLINE_FAIL_OPEN_NOT_USED: DAQ_Start(); SetPktProcessor(); SnortUnprivilegedInit(); break; case INLINE_FAIL_OPEN_ERROR: default: CleanExit(1); return 0; } } #if defined(DAQ_CAPA_CST_TIMEOUT) Daq_Capa_Timeout = DAQ_CanGetTimeout(); if(getDaqCapaTimeoutFnPtr) { getDaqCapaTimeoutFnPtr(Daq_Capa_Timeout); } #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) Daq_Capa_Vrf = DAQ_CanGetVrf(); #endif if(!exit_signal) PacketLoop(); // DAQ is shutdown in CleanExit() since we don't always return here CleanExit(0); return 0; } #ifndef WIN32 /* All threads need to be created after daemonizing. If created in * the parent thread, when it goes away, so will all of the threads. * The child does not "inherit" threads created in the parent. */ static void SnortStartThreads(void) { ControlSocketInit(); #ifdef SIDE_CHANNEL SideChannelStartTXThread(); #endif # ifdef SNORT_RELOAD if (ScIdsMode()) { LogMessage("Reload thread starting...\n"); if (pthread_create(&snort_reload_thread_id, NULL, ReloadConfigThread, NULL) != 0) { ErrorMessage("Could not create configuration reload thread.\n"); CleanExit(1); } while (!snort_reload_thread_created) nanosleep(&thread_sleep, NULL); LogMessage("Reload thread started, thread %p (%u)\n", (void*)snort_reload_thread_id, snort_reload_thread_pid); } # endif # ifdef TARGET_BASED if(IsAdaptiveConfigured() && !ScDisableAttrReload(snort_conf)) SFAT_StartReloadThread(); # endif } #else /* WIN32 */ //------------------------------------------------------------------------------ // interface stuff //------------------------------------------------------------------------------ static void PrintAllInterfaces (void) { char errorbuf[PCAP_ERRBUF_SIZE]; pcap_if_t *alldevs; pcap_if_t *dev; int j = 1; MIB_IFTABLE *iftable = NULL; unsigned int len = 0; unsigned int ret, i; if (pcap_findalldevs(&alldevs, errorbuf) == -1) FatalError("Could not get device list: %s.", errorbuf); /* max of two iterations here -- first to get the * correct length if not big enough. Second to * get the data. */ for (len = sizeof(iftable[0]); ; ) { if (iftable) free(iftable); iftable = SnortAlloc(len); ret = GetIfTable(iftable, &len, TRUE); if (ret == NO_ERROR) break; else if (ret != ERROR_INSUFFICIENT_BUFFER) FatalError("Could not get device list: %s.", errorbuf);; } printf("Index\tPhysical Address\tIP Address\tDevice Name\tDescription\n"); printf("-----\t----------------\t----------\t-----------\t-----------\n"); for (dev = alldevs; dev != NULL; dev = dev->next, j++) { uint8_t *mac_addr = NULL; for (i = 0; idwNumEntries; i++) { if (strncmp(dev->description, iftable->table[i].bDescr, iftable->table[i].dwDescrLen) == 0) { mac_addr = iftable->table[i].bPhysAddr; break; } } printf("%5d\t", j); if (mac_addr) { printf("%02X:%02X:%02X:%02X:%02X:%02X\t", mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); } else { printf("00:00:00:00:00:00\t"); } if (dev->addresses) { struct sockaddr_in* saddr = (struct sockaddr_in*)dev->addresses->addr; sfcidr_t dev_ip; if ((saddr->sin_family == AF_INET) || (saddr->sin_family == AF_INET6)) { sfip_set_raw(&dev_ip, &saddr->sin_addr, saddr->sin_family); printf("%s\t", inet_ntoa(&dev_ip)); } else { printf("disabled\t"); } printf("%s\t%s\n", dev->name, dev->description); } else { printf("disabled\t%s\t%s\n", dev->name, dev->description); } } pcap_freealldevs(alldevs); free(iftable); } #endif /* WIN32 */ // pcap list stuff ... static void PQ_SetFilter (const char* f) { if (pcap_filter != NULL) free(pcap_filter); pcap_filter = f ? SnortStrdup(f) : NULL; } static void PQ_Single (const char* pcap) { PcapReadObject* pro; if (pcap_object_list == NULL) { pcap_object_list = sflist_new(); if (pcap_object_list == NULL) FatalError("Could not allocate list to store pcap\n"); } pro = (PcapReadObject *)SnortAlloc(sizeof(PcapReadObject)); pro->type = PCAP_SINGLE; pro->arg = SnortStrdup(pcap); pro->filter = NULL; if (sflist_add_tail(pcap_object_list, (NODE_DATA)pro) == -1) FatalError("Could not add pcap object to list: %s\n", pcap); } static void PQ_Multi (char type, const char* list) { PcapReadObject* pro; if (pcap_object_list == NULL) { pcap_object_list = sflist_new(); if (pcap_object_list == NULL) FatalError("Could not allocate list to store pcaps\n"); } pro = (PcapReadObject *)SnortAlloc(sizeof(PcapReadObject)); pro->type = type; pro->arg = SnortStrdup(list); if (pcap_filter != NULL) pro->filter = SnortStrdup(pcap_filter); else pro->filter = NULL; if (sflist_add_tail(pcap_object_list, (NODE_DATA)pro) == -1) FatalError("Could not add pcap object to list: %s\n", list); } static void PQ_SetUp (void) { if (pcap_object_list != NULL) { if (sflist_count(pcap_object_list) == 0) { sflist_free_all(pcap_object_list, NULL); FatalError("No pcaps specified.\n"); } pcap_queue = sfqueue_new(); pcap_save_queue = sfqueue_new(); if ((pcap_queue == NULL) || (pcap_save_queue == NULL)) FatalError("Could not allocate pcap queues.\n"); if (GetPcaps(pcap_object_list, pcap_queue) == -1) FatalError("Error getting pcaps.\n"); if (sfqueue_count(pcap_queue) == 0) FatalError("No pcaps found.\n"); /* free pcap list used to get params */ while (sflist_count(pcap_object_list) > 0) { PcapReadObject *pro = (PcapReadObject *)sflist_remove_head(pcap_object_list); if (pro == NULL) FatalError("Failed to remove pcap item from list.\n"); if (pro->arg != NULL) free(pro->arg); if (pro->filter != NULL) free(pro->filter); free(pro); } sflist_free_all(pcap_object_list, NULL); pcap_object_list = NULL; } if (pcap_filter != NULL) { free(pcap_filter); pcap_filter = NULL; } } static int PQ_CleanUp (void) { /* clean up pcap queues */ if (pcap_queue != NULL) sfqueue_free_all(pcap_queue, free); if (pcap_save_queue != NULL) sfqueue_free_all(pcap_save_queue, free); return 0; } static void PQ_Show (const char* pcap) { if ( !ScPcapShow() ) return; if ( !strcmp(pcap, "-") ) pcap = "stdin"; fprintf(stdout, "Reading network traffic from \"%s\" with snaplen = %d\n", pcap, DAQ_GetSnapLen()); } static const char* PQ_First (void) { const char* pcap = (char*)sfqueue_remove(pcap_queue); if ( !pcap ) return pcap; if ( sfqueue_add(pcap_save_queue, (NODE_DATA)pcap) == -1 ) FatalError("Could not add pcap to saved list\n"); return pcap; } // this must follow 2nd or later start and not stop because we force a // reset when the dlt changes even if not enabled with --pcap-reset to // avoid eventually flushing stream packets through a different grinder // than the one they were queued with. static void PQ_Reset () { static int dlt = -1; int new_dlt = DAQ_GetBaseProtocol(); if ( ScPcapReset() || ((dlt != new_dlt) && (dlt != -1)) ) SnortReset(); dlt = new_dlt; /* open a new tcpdump file - necessary because the snaplen and * datalink could be different between pcaps */ if (snort_conf->log_tcpdump) { /* this sleep is to ensure we get a new log file since it has a * time stamp with resolution to the second */ #ifdef WIN32 Sleep(1000); #else sleep(1); #endif LogTcpdumpReset(); } } static int PQ_Next (void) { char reopen_pcap = 0; if (sfqueue_count(pcap_queue) > 0) { reopen_pcap = 1; } else if (pcap_loop_count) { if (pcap_loop_count > 0) pcap_loop_count--; if (pcap_loop_count != 0) { SF_QUEUE *tmp; /* switch pcap lists */ tmp = pcap_queue; pcap_queue = pcap_save_queue; pcap_save_queue = tmp; reopen_pcap = 1; } } if (reopen_pcap) { /* reinitialize pcap */ const char* pcap = PQ_First(); if ( !pcap ) FatalError("Could not get pcap from list\n"); DAQ_Stop(); DAQ_Delete(); DAQ_New(snort_conf, pcap); DAQ_Start(); PQ_Reset(); PQ_Show(pcap); SetPktProcessor(); #if defined(SNORT_RELOAD) && !defined(WIN32) if ( snort_conf->run_flags & RUN_FLAG__PCAP_RELOAD && ScIdsMode()) { /* Awaiting user confirmation */ printf("Hit return to continue.\n"); fflush(stdout); while(getc(stdin) != '\n'); SigReloadHandler(SIGNAL_SNORT_RELOAD); while (!snort_reload) sleep(1); } #endif return 1; } return 0; } static char* GetFirstInterface (void) { char *iface = NULL; char errorbuf[PCAP_ERRBUF_SIZE]; #ifdef WIN32 pcap_if_t *alldevs; if ( (pcap_findalldevs(&alldevs, errorbuf) == -1) || !alldevs ) { FatalError( "Failed to lookup interface: %s. " "Please specify one with -i switch\n", errorbuf); } /* Pick first interface */ iface = SnortStrdup(alldevs->name); pcap_freealldevs(alldevs); #else DEBUG_WRAP(DebugMessage( DEBUG_INIT, "interface is NULL, looking up interface....");); /* look up the device and get the handle */ iface = pcap_lookupdev(errorbuf); if ( !iface ) { FatalError( "Failed to lookup interface: %s. " "Please specify one with -i switch\n", errorbuf); } DEBUG_WRAP(DebugMessage(DEBUG_INIT, "found interface %s\n", PRINT_INTERFACE(iface));); iface = SnortStrdup(iface); #endif return iface; } static const char* GetPacketSource (char** sptr) { const char* intf = "other"; if ( ScReadMode() ) { intf = PQ_First(); PQ_Show(intf); } else if ( !ScVersionMode() && !ScRuleDumpMode() ) { intf = snort_conf->interface; // don't get interface if daq is explicitly configured // since we can't assume that an interface is compatible if ( !intf && !ScTestMode() && (!snort_conf->daq_type || // but we make execptions for these: // TBD make selection based on DAQ_TYPE_XXX !strcasecmp(snort_conf->daq_type, "afpacket") || !strcasecmp(snort_conf->daq_type, "pcap") || !strcasecmp(snort_conf->daq_type, "dump")) ) { intf = GetFirstInterface(); *sptr = (char*)intf; } } return intf; } static void InitPidChrootAndPrivs(pid_t pid) { #ifndef WIN32 /* Drop the Chrooted Settings */ if (snort_conf->chroot_dir) SetChroot(snort_conf->chroot_dir, &snort_conf->log_dir); /* Drop privileges if requested, when initialization is done */ SetUidGid(ScUid(), ScGid()); #endif /* create the PID file */ if ( !ScReadMode() && (ScDaemonMode() || *snort_conf->pidfile_suffix || ScCreatePidFile())) { CreatePidFile(DAQ_GetInterfaceSpec(), pid); } } static void LoadDynamicPlugins(SnortConfig *sc) { unsigned i; if (sc == NULL) return; if (sc->dyn_engines != NULL) { /* Load the dynamic engines */ for (i = 0; i < sc->dyn_engines->count; i++) { switch (sc->dyn_engines->lib_paths[i]->ptype) { case PATH_TYPE__FILE: LoadDynamicEngineLib(sc, sc->dyn_engines->lib_paths[i]->path, 0); break; case PATH_TYPE__DIRECTORY: LoadAllDynamicEngineLibs(sc, sc->dyn_engines->lib_paths[i]->path); break; } } } if (sc->dyn_rules != NULL) { /* Load the dynamic detection libs */ for (i = 0; i < sc->dyn_rules->count; i++) { switch (sc->dyn_rules->lib_paths[i]->ptype) { case PATH_TYPE__FILE: LoadDynamicDetectionLib(sc, sc->dyn_rules->lib_paths[i]->path, 0); break; case PATH_TYPE__DIRECTORY: LoadAllDynamicDetectionLibs(sc, sc->dyn_rules->lib_paths[i]->path); break; } } } if (sc->dyn_preprocs != NULL) { /* Load the dynamic preprocessors */ for (i = 0; i < sc->dyn_preprocs->count; i++) { switch (sc->dyn_preprocs->lib_paths[i]->ptype) { case PATH_TYPE__FILE: LoadDynamicPreprocessor(sc, sc->dyn_preprocs->lib_paths[i]->path, 0); break; case PATH_TYPE__DIRECTORY: LoadAllDynamicPreprocessors(sc, sc->dyn_preprocs->lib_paths[i]->path); break; } } } # ifdef SIDE_CHANNEL if (sc->dyn_side_channels != NULL) { /* Load the dynamic side channels */ for (i = 0; i < sc->dyn_side_channels->count; i++) { switch (sc->dyn_side_channels->lib_paths[i]->ptype) { case PATH_TYPE__FILE: LoadDynamicSideChannelLib(sc, sc->dyn_side_channels->lib_paths[i]->path, 0); break; case PATH_TYPE__DIRECTORY: LoadAllDynamicSideChannelLibs(sc, sc->dyn_side_channels->lib_paths[i]->path); break; } } } # endif /* SIDE_CHANNEL */ ValidateDynamicEngines(sc); } static void DisplayDynamicPluginVersions(SnortConfig *sc) { void *lib = NULL; DynamicPluginMeta *meta; RemoveDuplicateEngines(); RemoveDuplicateDetectionPlugins(sc); RemoveDuplicatePreprocessorPlugins(); #ifdef SIDE_CHANNEL RemoveDuplicateSideChannelPlugins(); #endif /* SIDE_CHANNEL */ lib = GetNextEnginePluginVersion(NULL); while ( lib != NULL ) { meta = GetDetectionPluginMetaData(lib); LogMessage(" Rules Engine: %s Version %d.%d \n", meta->uniqueName, meta->major, meta->minor, meta->build); lib = GetNextEnginePluginVersion(lib); } lib = GetNextDetectionPluginVersion(sc, NULL); while ( lib != NULL ) { meta = GetEnginePluginMetaData(lib); LogMessage(" Rules Object: %s Version %d.%d \n", meta->uniqueName, meta->major, meta->minor, meta->build); lib = GetNextDetectionPluginVersion(sc, lib); } lib = GetNextPreprocessorPluginVersion(NULL); while ( lib != NULL ) { meta = GetPreprocessorPluginMetaData(lib); LogMessage(" Preprocessor Object: %s Version %d.%d \n", meta->uniqueName, meta->major, meta->minor, meta->build); lib = GetNextPreprocessorPluginVersion(lib); } #ifdef SIDE_CHANNEL lib = GetNextSideChannelPluginVersion(NULL); while ( lib != NULL ) { meta = GetSideChannelPluginMetaData(lib); LogMessage(" Side Channel Object: %s Version %d.%d \n", meta->uniqueName, meta->major, meta->minor, meta->build); lib = GetNextSideChannelPluginVersion(lib); } #endif /* SIDE_CHANNEL */ lib = GetNextOutputModule(NULL); while ( lib != NULL ) { LogMessage(" Output Module: %s Version %u\n", GetOutputModuleName(lib), GetOutputModuleVersion(lib)); lib = GetNextOutputModule(lib); } } /* * This function will print versioning information regardless of whether or * not the quiet flag is set. If the quiet flag has been set and we want * to honor it, check it before calling this function. */ static void PrintVersion(SnortConfig *sc) { DisplayBanner(); /* Get and print out library versions. * This information would be printed only for one Snort instance which doesn't * have --suppress-config-log option. For Snort instances with --supress-config-log, * we print only banner to provide some info about Snort process being started/reloaded. * This change is done to avoid duplicate logging of plugin information. */ if (ScSuppressConfigLog()) ScSetInternalLogLevel(INTERNAL_LOG_LEVEL__ERROR); DisplayDynamicPluginVersions(sc); ScRestoreInternalLogLevel(); } static void PrintDaqModules (SnortConfig* sc, char* dir) { if ( dir ) ConfigDaqDir(sc, dir); DAQ_Load(snort_conf); DAQ_PrintTypes(stdout); DAQ_Unload(); } #ifdef EXIT_CHECK static uint64_t exitTime = 0; static void ExitCheckStart (void) { if ( exitTime ) { return; } LogMessage("Exit Check: signaling at " STDu64 "callback\n", pc.total_from_daq); get_clockticks(exitTime); #ifndef WIN32 kill(0, SIGINT); // send to all processes in my process group #else raise(SIGINT); #endif } static void ExitCheckEnd (void) { uint64_t now = 0; double usecs = 0.0; if ( !exitTime ) { LogMessage( "Exit Check: callbacks = " STDu64 "(limit not reached)\n", pc.total_from_daq ); return; } get_clockticks(now); exitTime = now - exitTime; usecs = exitTime / get_ticks_per_usec(); LogMessage("Exit Check: usecs = %f\n", usecs); } #endif #ifdef HAVE_DAQ_ACQUIRE_WITH_META static int MetaCallback( void* user, const DAQ_MetaHdr_t *metahdr, const uint8_t* data) { tSfPolicyId policy_id = getDefaultPolicy(); SnortPolicy *policy; PreprocMetaEvalFuncNode *idx; PROFILE_VARS; #ifdef SIDE_CHANNEL if (ScSideChannelEnabled() && !snort_process_lock_held) { pthread_mutex_lock(&snort_process_lock); snort_process_lock_held = true; } #endif /* First thing we do is process a Usr signal that we caught */ if (SignalCheck()) { #ifndef SNORT_RELOAD /* Got SIGNAL_SNORT_RELOAD */ Restart(); #endif } CheckForReload(); PREPROC_PROFILE_START(metaPerfStats); policy = snort_conf->targeted_policies[policy_id]; idx = policy->preproc_meta_eval_funcs; while (idx != NULL) { idx->func(metahdr->type, data); idx = idx->next; } PREPROC_PROFILE_END(metaPerfStats); Active_Reset(); #if defined(WIN32) && defined(ENABLE_WIN32_SERVICE) if (ScTerminateService() || ScPauseService()) { return 0; // time to go } #endif #ifdef SNORT_RELOAD ReloadAdjust(false, 0); #endif ControlSocketDoWork(0); #ifdef SIDE_CHANNEL SideChannelDrainRX(0); #endif return 0; } #endif void SetupMetadataCallback(void) { #ifdef HAVE_DAQ_ACQUIRE_WITH_META DAQ_Set_MetaCallback(&MetaCallback); #endif } // non-local for easy access from core static Packet s_packet; static DAQ_PktHdr_t s_pkth; static uint8_t s_data[65536]; static DAQ_Verdict PacketCallback( void* user, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt) { int inject = 0; DAQ_Verdict verdict = DAQ_VERDICT_PASS; verdict_reason = VERDICT_REASON_NO_BLOCK; PROFILE_VARS; /* The active_drop_pkt is reset to default value to make sure stale * value from previous session packet processing is not set */ Active_Reset(); PREPROC_PROFILE_START_PI(totalPerfStats); #ifdef SIDE_CHANNEL if (ScSideChannelEnabled() && !snort_process_lock_held) { pthread_mutex_lock(&snort_process_lock); snort_process_lock_held = true; } #endif #ifdef EXIT_CHECK if (snort_conf->exit_check && (pc.total_from_daq >= snort_conf->exit_check)) ExitCheckStart(); #endif /* First thing we do is process a Usr signal that we caught */ if (SignalCheck()) { #ifndef SNORT_RELOAD /* Got SIGNAL_SNORT_RELOAD */ PREPROC_PROFILE_END_PI(totalPerfStats); Restart(); #endif } pc.total_from_daq++; /* Increment counter that we're evaling rules for caching results */ rule_eval_pkt_count++; #ifdef TARGET_BASED /* Load in a new attribute table if we need to... */ AttributeTableReloadCheck(); #endif CheckForReload(); /* Save off the time of each and every packet */ packet_time_update(&pkthdr->ts); #ifdef REG_TEST if ( snort_conf->pkt_skip && pc.total_from_daq <= snort_conf->pkt_skip ) { PREPROC_PROFILE_END_PI(totalPerfStats); return verdict; } #endif #if defined(WIN32) && defined(ENABLE_WIN32_SERVICE) if (ScTerminateService() || ScPauseService()) { PREPROC_PROFILE_END_PI(totalPerfStats); return verdict; // time to go } #endif /* reset the thresholding subsystem checks for this packet */ sfthreshold_reset(); PREPROC_PROFILE_START(eventqPerfStats); SnortEventqReset(); Replace_ResetQueue(); #ifdef ACTIVE_RESPONSE Active_ResetQueue(); #endif PREPROC_PROFILE_END(eventqPerfStats); verdict = ProcessPacket(&s_packet, pkthdr, pkt, NULL); #ifdef ACTIVE_RESPONSE if ( Active_ResponseQueued() ) { Active_SendResponses(&s_packet); } #endif if ( Active_PacketWasDropped() ) { if ( verdict == DAQ_VERDICT_PASS ) { #ifdef HAVE_DAQ_VERDICT_RETRY if ( Active_RetryIsPending() && !(s_packet.packet_flags & PKT_RETRANSMIT) ) verdict = DAQ_VERDICT_RETRY; else verdict = DAQ_VERDICT_BLOCK; #else verdict = DAQ_VERDICT_BLOCK; #endif } } else { Replace_ModifyPacket(&s_packet); if ( s_packet.packet_flags & PKT_MODIFIED ) { // this packet was normalized and/or has replacements Encode_Update(&s_packet); verdict = DAQ_VERDICT_REPLACE; } #ifdef NORMALIZER else if ( s_packet.packet_flags & PKT_RESIZED ) { // we never increase, only trim, but // daq doesn't support resizing wire packet if ( !DAQ_Inject(s_packet.pkth, 0, s_packet.pkt, s_packet.pkth->pktlen) ) { verdict = DAQ_VERDICT_BLOCK; inject = 1; } } #endif else { if ((s_packet.packet_flags & PKT_IGNORE) || (session_api && (session_api->get_ignore_direction(s_packet.ssnptr) == SSN_DIR_BOTH))) { if ( !Active_GetTunnelBypass() ) { verdict = DAQ_VERDICT_WHITELIST; } else { verdict = DAQ_VERDICT_PASS; pc.internal_whitelist++; } } else if ( s_packet.packet_flags & PKT_TRUST ) { if (session_api) session_api->set_ignore_direction(s_packet.ssnptr, SSN_DIR_BOTH); verdict = DAQ_VERDICT_WHITELIST; } else { verdict = DAQ_VERDICT_PASS; } } } #if defined(HAVE_DAQ_LOCALLY_ORIGINATED) && defined(HAVE_DAQ_LOCALLY_DESTINED) /* Don't whitelist packets to/from internal endpoints */ if (verdict == DAQ_VERDICT_WHITELIST && (pkthdr->flags & (DAQ_PKT_FLAG_LOCALLY_DESTINED | DAQ_PKT_FLAG_LOCALLY_ORIGINATED))) { verdict = DAQ_VERDICT_PASS; } #endif #ifdef ENABLE_HA /* This needs to be called here since the session could have been updated anywhere up to this point. :( */ if (session_api) session_api->process_ha(s_packet.ssnptr, pkthdr); #endif /* Collect some "on the wire" stats about packet size, etc */ UpdateWireStats(&sfBase, pkthdr->caplen, Active_PacketWasDropped(), inject); Active_Reset(); Encode_Reset(); if( session_api ) session_api->check_session_timeout(4, pkthdr->ts.tv_sec); /* Reset the active_drop_pkt value as during timeout/EOF the stale value gets set and * need to be reset for next packet processing */ Active_Reset(); #ifdef SNORT_RELOAD ReloadAdjust(false, pkthdr->ts.tv_sec); #endif ControlSocketDoWork(0); #ifdef SIDE_CHANNEL SideChannelDrainRX(0); #endif if ((verdict == DAQ_VERDICT_BLOCK || verdict == DAQ_VERDICT_BLACKLIST) && (verdict_reason == VERDICT_REASON_NO_BLOCK)) verdict_reason = VERDICT_REASON_UNKNOWN; #ifdef HAVE_DAQ_VERDICT_RETRY if (verdict == DAQ_VERDICT_RETRY && verdict_reason == VERDICT_REASON_NO_BLOCK) verdict_reason = VERDICT_REASON_DAQRETRY; #endif if (pkt_trace_enabled) writePktTraceData(verdict, getNapRuntimePolicy(), getIpsRuntimePolicy(), &s_packet); #if defined(HAVE_DAQ_EXT_MODFLOW) && defined(HAVE_DAQ_VERDICT_REASON) if (verdict_reason != VERDICT_REASON_NO_BLOCK) sendReason(&s_packet); #endif #if defined(DAQ_VERSION) && DAQ_VERSION > 9 if (pkthdr->flags & DAQ_PKT_FLAG_DEBUG_ON) { print_pktverdict(&s_packet,verdict); } #endif s_packet.pkth = NULL; // no longer avail on segv PREPROC_PROFILE_END_PI(totalPerfStats); return verdict; } static void PrintPacket(Packet *p) { if (p->iph != NULL) { PrintIPPkt(stdout, GET_IPH_PROTO((p)), p); } #ifndef NO_NON_ETHER_DECODER else if (p->ah != NULL) { PrintArpHeader(stdout, p); } else if (p->eplh != NULL) { PrintEapolPkt(stdout, p); } else if (p->wifih && ScOutputWifiMgmt()) { PrintWifiPkt(stdout, p); } #endif // NO_NON_ETHER_DECODER } DAQ_Verdict ProcessPacket( Packet* p, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt, void* ft) { DAQ_Verdict verdict = DAQ_VERDICT_PASS; // set runtime policy to default...idx is same for both nap & ips setNapRuntimePolicy(getDefaultPolicy()); setIpsRuntimePolicy(getDefaultPolicy()); /* call the packet decoder */ (*grinder) (p, pkthdr, pkt); assert(p->pkth && p->pkt); if (ft) { p->packet_flags |= (PKT_PSEUDO | PKT_REBUILT_FRAG); p->pseudo_type = PSEUDO_PKT_IP; p->fragtracker = ft; Encode_SetPkt(p); } if ( !p->proto_bits ) p->proto_bits = PROTO_BIT__OTHER; // required until decoders are fixed else if ( !p->family && (p->proto_bits & PROTO_BIT__IP) ) p->proto_bits &= ~PROTO_BIT__IP; /***** Policy specific decoding should into this function *****/ p->configPolicyId = snort_conf->targeted_policies[ getNapRuntimePolicy() ]->configPolicyId; #if defined(HAVE_DAQ_EXT_MODFLOW) && defined(HAVE_DAQ_PKT_TRACE) if (pkt_trace_cli_flag || (pkthdr->flags & DAQ_PKT_FLAG_TRACE_ENABLED)) #else if (pkt_trace_cli_flag) #endif { pkt_trace_enabled = pktTracerDebugCheck(p); if (pkt_trace_enabled) { addPktTraceInfo(p); if (p->packet_flags & PKT_IGNORE) addPktTraceData(VERDICT_REASON_SNORT, snprintf(trace_line, MAX_TRACE_LINE, "Snort: packet ignored\n")); } } else pkt_trace_enabled = false; /* just throw away the packet if we are configured to ignore this port */ if ( !(p->packet_flags & PKT_IGNORE) ) { /* start calling the detection processes */ Preprocess(p); log_func(p); } else if (!pkt_trace_enabled) // ignored packet addPktTraceData(VERDICT_REASON_SNORT, 0); if ( Active_SessionWasDropped() ) { if ( !Active_PacketForceDropped() ) Active_DropAction(p); else Active_ForceDropAction(p); if ( Active_GetTunnelBypass() ) pc.internal_blacklist++; else if ( ScIpsInlineMode() || Active_PacketForceDropped() ) { verdict = DAQ_VERDICT_BLACKLIST; } else verdict = DAQ_VERDICT_IGNORE; } #ifdef CONTROL_SOCKET if (packet_dump_stop) PacketDumpClose(); else if (packet_dump_file && #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) ((pkthdr->address_space_id_src == packet_dump_address_space_id) || (pkthdr->address_space_id_dst == packet_dump_address_space_id)) && #else pkthdr->address_space_id == packet_dump_address_space_id && #endif #endif (!packet_dump_fcode.bf_insns || sfbpf_filter(packet_dump_fcode.bf_insns, (uint8_t *)pkt, pkthdr->caplen, pkthdr->pktlen))) { pcap_dump((uint8_t*)packet_dump_file, (const struct pcap_pkthdr*)pkthdr, pkt); pcap_dump_flush((pcap_dumper_t*)packet_dump_file); } #endif return verdict; } Packet *NewGrinderPkt(Packet *p, DAQ_PktHdr_t* phdr, uint8_t *pkt) { if( !p ) { IP6Option* ip6_extensions; p = SnortAlloc(sizeof(*p)); ip6_extensions = SnortAlloc(sizeof(IP6Option) * ScMaxIP6Extensions()); if ( !p || !ip6_extensions ) FatalError("Encode_New() => Failed to allocate packet\n"); p->ip6_extensions = ip6_extensions; } if ( phdr && pkt ) { (*grinder)(p, phdr, pkt); } return p; } void DeleteGrinderPkt( Packet *p) { if(!p) return; if(p->ip6_extensions) free(p->ip6_extensions); free(p); } /* * Function: ShowUsage(char *) * * Purpose: Display the program options and exit * * Arguments: argv[0] => name of the program (argv[0]) * * Returns: 0 => success */ static int ShowUsage(char *program_name) { fprintf(stdout, "USAGE: %s [-options] \n", program_name); #if defined(WIN32) && defined(ENABLE_WIN32_SERVICE) fprintf(stdout, " %s %s %s [-options] \n", program_name , SERVICE_CMDLINE_PARAM , SERVICE_INSTALL_CMDLINE_PARAM); fprintf(stdout, " %s %s %s\n", program_name , SERVICE_CMDLINE_PARAM , SERVICE_UNINSTALL_CMDLINE_PARAM); fprintf(stdout, " %s %s %s\n", program_name , SERVICE_CMDLINE_PARAM , SERVICE_SHOW_CMDLINE_PARAM); #endif #ifdef WIN32 # define FPUTS_WIN32(msg) fputs(msg,stdout) # define FPUTS_UNIX(msg) NULL # define FPUTS_BOTH(msg) fputs(msg,stdout) #else # define FPUTS_WIN32(msg) # define FPUTS_UNIX(msg) fputs(msg,stdout) # define FPUTS_BOTH(msg) fputs(msg,stdout) #endif FPUTS_BOTH ("Options:\n"); FPUTS_BOTH (" -A Set alert mode: fast, full, console, test or none " " (alert file alerts only)\n"); FPUTS_UNIX (" \"unsock\" enables UNIX socket logging (experimental).\n"); FPUTS_BOTH (" -b Log packets in tcpdump format (much faster!)\n"); FPUTS_BOTH (" -B Obfuscated IP addresses in alerts and packet dumps using CIDR mask\n"); FPUTS_BOTH (" -c Use Rules File \n"); FPUTS_BOTH (" -C Print out payloads with character data only (no hex)\n"); FPUTS_BOTH (" -d Dump the Application Layer\n"); FPUTS_UNIX (" -D Run Snort in background (daemon) mode\n"); FPUTS_BOTH (" -e Display the second layer header info\n"); FPUTS_WIN32(" -E Log alert messages to NT Eventlog. (Win32 only)\n"); FPUTS_BOTH (" -f Turn off fflush() calls after binary log writes\n"); FPUTS_BOTH (" -F Read BPF filters from file \n"); FPUTS_UNIX (" -g Run snort gid as group (or gid) after initialization\n"); FPUTS_BOTH (" -G <0xid> Log Identifier (to uniquely id events for multiple snorts)\n"); FPUTS_BOTH (" -h Set home network = \n" " (for use with -l or -B, does NOT change $HOME_NET in IDS mode)\n"); FPUTS_BOTH (" -H Make hash tables deterministic.\n"); FPUTS_BOTH (" -i Listen on interface \n"); FPUTS_BOTH (" -I Add Interface name to alert output\n"); FPUTS_BOTH (" -k Checksum mode (all,noip,notcp,noudp,noicmp,none)\n"); FPUTS_BOTH (" -K Logging mode (pcap[default],ascii,none)\n"); FPUTS_BOTH (" -l Log to directory \n"); FPUTS_BOTH (" -L Log to this tcpdump file\n"); FPUTS_UNIX (" -M Log messages to syslog (not alerts)\n"); FPUTS_UNIX (" -m Set umask = \n"); FPUTS_BOTH (" -n Exit after receiving packets\n"); FPUTS_BOTH (" -N Turn off logging (alerts still work)\n"); FPUTS_BOTH (" -O Obfuscate the logged IP addresses\n"); FPUTS_BOTH (" -p Disable promiscuous mode sniffing\n"); printf (" -P Set explicit snaplen of packet (default: %d)\n", DAQ_GetSnapLen()); FPUTS_BOTH (" -q Quiet. Don't show banner and status report\n"); #ifndef WIN32 FPUTS_BOTH (" -Q Enable inline mode operation.\n"); #endif FPUTS_BOTH (" -r Read and process tcpdump file \n"); FPUTS_BOTH (" -R Include 'id' in snort_intf.pid file name\n"); FPUTS_BOTH (" -s Log alert messages to syslog\n"); FPUTS_BOTH (" -S Set rules file variable n equal to value v\n"); FPUTS_UNIX (" -t Chroots process to after initialization\n"); FPUTS_BOTH (" -T Test and report on the current Snort configuration\n"); FPUTS_UNIX (" -u Run snort uid as user (or uid) after initialization\n"); FPUTS_BOTH (" -U Use UTC for timestamps\n"); FPUTS_BOTH (" -v Be verbose\n"); FPUTS_BOTH (" -V Show version number\n"); FPUTS_WIN32(" -W Lists available interfaces. (Win32 only)\n"); #if defined(NON_ETHER_DECODER) && defined(DLT_IEEE802_11) FPUTS_BOTH (" -w Dump 802.11 management and control frames\n"); #endif FPUTS_BOTH (" -X Dump the raw packet data starting at the link layer\n"); FPUTS_BOTH (" -x Exit if Snort configuration problems occur\n"); FPUTS_BOTH (" -y Include year in timestamp in the alert and log files\n"); FPUTS_BOTH (" -z Set the preproc_memstats file path and name\n"); FPUTS_BOTH (" -Z Set the performonitor preprocessor file path and name\n"); FPUTS_BOTH (" -? Show this information\n"); FPUTS_BOTH (" are standard BPF options, as seen in TCPDump\n"); FPUTS_BOTH ("Longname options and their corresponding single char version\n"); FPUTS_BOTH (" --logid <0xid> Same as -G\n"); FPUTS_BOTH (" --perfmon-file Same as -Z\n"); FPUTS_BOTH (" --pid-path Specify the directory for the Snort PID file\n"); FPUTS_BOTH (" --snaplen Same as -P\n"); FPUTS_BOTH (" --help Same as -?\n"); FPUTS_BOTH (" --version Same as -V\n"); FPUTS_BOTH (" --alert-before-pass Process alert, drop, sdrop, or reject before pass, default is pass before alert, drop,...\n"); FPUTS_BOTH (" --treat-drop-as-alert Converts drop, sdrop, and reject rules into alert rules during startup\n"); FPUTS_BOTH (" --treat-drop-as-ignore Use drop, sdrop, and reject rules to ignore session traffic when not inline.\n"); FPUTS_BOTH (" --process-all-events Process all queued events (drop, alert,...), default stops after 1st action group\n"); FPUTS_BOTH (" --enable-inline-test Enable Inline-Test Mode Operation\n"); FPUTS_BOTH (" --dynamic-engine-lib Load a dynamic detection engine\n"); FPUTS_BOTH (" --dynamic-engine-lib-dir Load all dynamic engines from directory\n"); FPUTS_BOTH (" --dynamic-detection-lib Load a dynamic rules library\n"); FPUTS_BOTH (" --dynamic-detection-lib-dir Load all dynamic rules libraries from directory\n"); FPUTS_BOTH (" --dump-dynamic-rules Creates stub rule files of all loaded rules libraries\n"); FPUTS_BOTH (" --dynamic-preprocessor-lib Load a dynamic preprocessor library\n"); FPUTS_BOTH (" --dynamic-preprocessor-lib-dir Load all dynamic preprocessor libraries from directory\n"); FPUTS_BOTH (" --dynamic-output-lib Load a dynamic output library\n"); FPUTS_BOTH (" --dynamic-output-lib-dir Load all dynamic output libraries from directory\n"); FPUTS_UNIX (" --create-pidfile Create PID file, even when not in Daemon mode\n"); FPUTS_UNIX (" --nolock-pidfile Do not try to lock Snort PID file\n"); FPUTS_UNIX (" --no-interface-pidfile Do not include the interface name in Snort PID file\n"); #ifdef INLINE_FAILOPEN FPUTS_UNIX (" --disable-inline-init-failopen Do not fail open and pass packets while initializing with inline mode.\n"); #endif #ifdef TARGET_BASED FPUTS_UNIX (" --disable-attribute-reload-thread Do not create a thread to reload the attribute table\n"); #endif FPUTS_BOTH (" --pcap-single Same as -r.\n"); FPUTS_BOTH (" --pcap-file file that contains a list of pcaps to read - read mode is implied.\n"); FPUTS_BOTH (" --pcap-list \"\" a space separated list of pcaps to read - read mode is implied.\n"); FPUTS_UNIX (" --pcap-dir a directory to recurse to look for pcaps - read mode is implied.\n"); FPUTS_UNIX (" --pcap-filter filter to apply when getting pcaps from file or directory.\n"); FPUTS_UNIX (" --pcap-no-filter reset to use no filter when getting pcaps from file or directory.\n"); FPUTS_BOTH (" --pcap-loop this option will read the pcaps specified on command line continuously.\n" " for times. A value of 0 will read until Snort is terminated.\n"); FPUTS_BOTH (" --pcap-reset if reading multiple pcaps, reset snort to post-configuration state before reading next pcap.\n"); #if defined(SNORT_RELOAD) && !defined(WIN32) FPUTS_BOTH (" --pcap-reload if reading multiple pcaps, reload snort config between pcaps.\n"); #endif FPUTS_BOTH (" --pcap-show print a line saying what pcap is currently being read.\n"); FPUTS_BOTH (" --exit-check Signal termination after callbacks from DAQ_Acquire(), showing the time it\n" " takes from signaling until DAQ_Stop() is called.\n"); FPUTS_BOTH (" --conf-error-out Same as -x\n"); #ifdef MPLS FPUTS_BOTH (" --enable-mpls-multicast Allow multicast MPLS\n"); FPUTS_BOTH (" --enable-mpls-overlapping-ip Handle overlapping IPs within MPLS clouds\n"); FPUTS_BOTH (" --max-mpls-labelchain-len Specify the max MPLS label chain\n"); FPUTS_BOTH (" --mpls-payload-type Specify the protocol (ipv4, ipv6, ethernet) that is encapsulated by MPLS\n"); #endif FPUTS_BOTH (" --require-rule-sid Require that all snort rules have SID specified.\n"); FPUTS_BOTH (" --daq Select packet acquisition module (default is pcap).\n"); FPUTS_BOTH (" --daq-mode Select the DAQ operating mode.\n"); FPUTS_BOTH (" --daq-var Specify extra DAQ configuration variable.\n"); FPUTS_BOTH (" --daq-dir Tell snort where to find desired DAQ.\n"); FPUTS_BOTH (" --daq-list[=] List packet acquisition modules available in dir. Default is static modules only.\n"); FPUTS_BOTH (" --dirty-pig Don't flush packets and release memory on shutdown.\n"); FPUTS_BOTH (" --cs-dir Directory to use for control socket.\n"); FPUTS_BOTH (" --ha-peer Activate live high-availability state sharing with peer.\n"); FPUTS_BOTH (" --ha-out Write high-availability events to this file.\n"); FPUTS_BOTH (" --ha-in Read high-availability events from this file on startup (warm-start).\n"); FPUTS_BOTH (" --suppress-config-log Suppress configuration information output.\n"); #ifdef DUMP_BUFFER FPUTS_BOTH (" --buffer-dump= Dump buffers for all packets\n"); FPUTS_BOTH (" --buffer-dump-alert= Dump buffers when a rule triggers\n"); #endif #undef FPUTS_WIN32 #undef FPUTS_UNIX #undef FPUTS_BOTH return 0; } static void ParseCmdLineDynamicLibInfo(SnortConfig *sc, int type, char *path) { DynamicLibInfo *dli = NULL; DynamicLibPath *dlp = NULL; if ((sc == NULL) || (path == NULL)) FatalError("%s(%d) NULL arguments.\n", __FILE__, __LINE__); switch (type) { case DYNAMIC_PREPROC_FILE: case DYNAMIC_PREPROC_DIRECTORY: DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Dynamic preprocessor specifier\n");); if (sc->dyn_preprocs == NULL) { sc->dyn_preprocs = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo)); sc->dyn_preprocs->type = DYNAMIC_TYPE__PREPROCESSOR; } else if (sc->dyn_preprocs->count >= MAX_DYNAMIC_LIBS) { FatalError("Maximum number of loaded Dynamic Preprocessor Libs " "(%d) exceeded.\n", MAX_DYNAMIC_LIBS); } dli = sc->dyn_preprocs; break; case DYNAMIC_LIBRARY_FILE: case DYNAMIC_LIBRARY_DIRECTORY: DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Dynamic detection specifier\n");); if (sc->dyn_rules == NULL) { sc->dyn_rules = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo)); sc->dyn_rules->type = DYNAMIC_TYPE__DETECTION; } else if (sc->dyn_rules->count >= MAX_DYNAMIC_LIBS) { FatalError("Maximum number of loaded Dynamic Detection Libs " "(%d) exceeded.\n", MAX_DYNAMIC_LIBS); } dli = sc->dyn_rules; break; case DYNAMIC_ENGINE_FILE: case DYNAMIC_ENGINE_DIRECTORY: DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Dynamic engine specifier\n");); if (sc->dyn_engines == NULL) { sc->dyn_engines = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo)); sc->dyn_engines->type = DYNAMIC_TYPE__ENGINE; } else if (sc->dyn_engines->count >= MAX_DYNAMIC_LIBS) { FatalError("Maximum number of loaded Dynamic Engine Libs " "(%d) exceeded.\n", MAX_DYNAMIC_LIBS); } dli = sc->dyn_engines; break; case DYNAMIC_OUTPUT_FILE: output_load_module(path); return; break; case DYNAMIC_OUTPUT_DIRECTORY: output_load(path); return; break; default: FatalError("%s(%d) Invalid dynamic type: %d\n", __FILE__, __LINE__, type); break; } dlp = (DynamicLibPath *)SnortAlloc(sizeof(DynamicLibPath)); switch (type) { case DYNAMIC_PREPROC_FILE: case DYNAMIC_LIBRARY_FILE: case DYNAMIC_ENGINE_FILE: case DYNAMIC_OUTPUT_FILE: dlp->ptype = PATH_TYPE__FILE; break; case DYNAMIC_PREPROC_DIRECTORY: case DYNAMIC_LIBRARY_DIRECTORY: case DYNAMIC_ENGINE_DIRECTORY: case DYNAMIC_OUTPUT_DIRECTORY: dlp->ptype = PATH_TYPE__DIRECTORY; break; default: FatalError("%s(%d) Invalid dynamic type: %d\n", __FILE__, __LINE__, type); break; } dlp->path = SnortStrdup(path); dli->lib_paths[dli->count] = dlp; dli->count++; } /* * Function: ParseCmdLine(int, char **) * * Parses command line arguments * * Arguments: * int * count of arguments passed to the routine * char ** * 2-D character array, contains list of command line args * * Returns: None * */ static void ParseCmdLine(int argc, char **argv) { int ch; int option_index = -1; char *endptr; /* for SnortStrtol calls */ SnortConfig *sc; int output_logging = 0; int output_alerting = 0; int syslog_configured = 0; #ifndef WIN32 int daemon_configured = 0; #endif int version_flag_parsed = 0; int quiet_flag_parsed = 0; DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Parsing command line...\n");); if (snort_cmd_line_conf != NULL) { FatalError("%s(%d) Trying to parse the command line again.\n", __FILE__, __LINE__); } snort_cmd_line_conf = SnortConfNew(); snort_conf = snort_cmd_line_conf; /* Set the global for log messages */ sc = snort_cmd_line_conf; optind = 1; /* Look for a -D and/or -M switch so we can start logging to syslog * with "snort" tag right away */ while ((ch = getopt_long(argc, argv, valid_options, long_options, &option_index)) != -1) { switch (ch) { case 'M': if (syslog_configured) break; /* If daemon or logging to syslog use "snort" as identifier and * start logging there now */ openlog("snort", LOG_PID | LOG_CONS, LOG_DAEMON); sc->logging_flags |= LOGGING_FLAG__SYSLOG; syslog_configured = 1; break; #ifndef WIN32 case 'E': sc->run_flags |= RUN_FLAG__DAEMON_RESTART; /* Fall through */ case 'D': if (daemon_configured) break; /* If daemon or logging to syslog use "snort" as identifier and * start logging there now */ openlog("snort", LOG_PID | LOG_CONS, LOG_DAEMON); ConfigDaemon(sc, optarg); daemon_configured = 1; break; #endif case 'V': version_flag_parsed = 1; break; case 'q': quiet_flag_parsed = 1; break; case '?': /* show help and exit with 1 */ PrintVersion(sc); ShowUsage(argv[0]); exit(1); break; default: break; } } if (version_flag_parsed) { sc->run_mode_flags |= RUN_MODE_FLAG__VERSION; } else if (quiet_flag_parsed) { ConfigQuiet(sc, NULL); internal_log_level = INTERNAL_LOG_LEVEL__ERROR; } /* ** Set this so we know whether to return 1 on invalid input. ** Snort uses '?' for help and getopt uses '?' for telling us there ** was an invalid option, so we can't use that to tell invalid input. ** Instead, we check optopt and it will tell us. */ optopt = 0; optind = 1; /* loop through each command line var and process it */ while ((ch = getopt_long(argc, argv, valid_options, long_options, &option_index)) != -1) { DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Processing cmd line switch: %c\n", ch);); switch (ch) { case DYNAMIC_ENGINE_FILE: /* Load dynamic engine specified */ case DYNAMIC_ENGINE_DIRECTORY: /* Load dynamic engine specified */ case DYNAMIC_PREPROC_FILE: /* Load dynamic preprocessor lib specified */ case DYNAMIC_PREPROC_DIRECTORY: case DYNAMIC_LIBRARY_FILE: /* Load dynamic detection lib specified */ case DYNAMIC_LIBRARY_DIRECTORY: case DYNAMIC_OUTPUT_FILE: /* Load dynamic output lib specified */ case DYNAMIC_OUTPUT_DIRECTORY: ParseCmdLineDynamicLibInfo(sc, ch, optarg); break; case DUMP_DYNAMIC_RULES: ConfigDumpDynamicRulesPath(sc, optarg); break; case ALERT_BEFORE_PASS: ConfigAlertBeforePass(sc, NULL); break; case PROCESS_ALL_EVENTS: ConfigProcessAllEvents(sc, NULL); break; case TREAT_DROP_AS_ALERT: ConfigTreatDropAsAlert(sc, NULL); break; case TREAT_DROP_AS_IGNORE: ConfigTreatDropAsIgnore(sc, NULL); break; case PID_PATH: ConfigPidPath(sc, optarg); break; case CREATE_PID_FILE: ConfigCreatePidFile(sc, NULL); break; case NOLOCK_PID_FILE: sc->run_flags |= RUN_FLAG__NO_LOCK_PID_FILE; break; case NO_IFACE_PID_FILE: sc->run_flags |= RUN_FLAG__NO_IFACE_PID_FILE; break; #ifdef INLINE_FAILOPEN case DISABLE_INLINE_FAILOPEN: ConfigDisableInlineFailopen(sc, NULL); break; #endif case NO_LOGGING_TIMESTAMPS: ConfigNoLoggingTimestamps(sc, NULL); break; #ifdef EXIT_CHECK case ARG_EXIT_CHECK: { char* endPtr; sc->exit_check = SnortStrtoul(optarg, &endPtr, 0); if ((errno == ERANGE) || (*endPtr != '\0')) FatalError("--exit-check value must be non-negative integer\n"); LogMessage("Exit Check: limit = "STDu64" callbacks\n", sc->exit_check); } break; #endif #ifdef TARGET_BASED case DISABLE_ATTRIBUTE_RELOAD: ConfigDisableAttributeReload(sc, NULL); break; #endif case DETECTION_SEARCH_METHOD: if (sc->fast_pattern_config != NULL) FatalError("Can only configure search method once.\n"); sc->fast_pattern_config = FastPatternConfigNew(); if (fpSetDetectSearchMethod(sc->fast_pattern_config, optarg) == -1) FatalError("Invalid search method: %s.\n", optarg); break; case ARG_DAQ_TYPE: ConfigDaqType(sc, optarg); break; case ARG_DAQ_MODE: ConfigDaqMode(sc, optarg); break; case ARG_DAQ_VAR: ConfigDaqVar(sc, optarg); break; case ARG_DAQ_DIR: ConfigDaqDir(sc, optarg); break; case ARG_DAQ_LIST: PrintDaqModules(sc, optarg); exit(0); break; case ARG_DIRTY_PIG: ConfigDirtyPig(sc, optarg); break; case 'A': /* alert mode */ output_alerting = 1; if (strcasecmp(optarg, ALERT_MODE_OPT__NONE) == 0) { sc->no_alert = 1; } else if (strcasecmp(optarg, ALERT_MODE_OPT__PKT_CNT) == 0) { /* print packet count at start of alert */ sc->output_flags |= OUTPUT_FLAG__ALERT_PKT_CNT; } else if (strcasecmp(optarg, ALERT_MODE_OPT__FULL) == 0) { ParseOutput(sc, NULL, "alert_full"); } else if (strcasecmp(optarg, "console:" ALERT_MODE_OPT__FULL) == 0) { ParseOutput(sc, NULL, "alert_full: stdout"); } else if (strcasecmp(optarg, ALERT_MODE_OPT__FAST) == 0) { ParseOutput(sc, NULL, "alert_fast"); } else if ( strcasecmp(optarg, "console:" ALERT_MODE_OPT__FAST) == 0 || strcasecmp(optarg, ALERT_MODE_OPT__CONSOLE) == 0 ) { ParseOutput(sc, NULL, "alert_fast: stdout"); } else if ((strcasecmp(optarg, ALERT_MODE_OPT__CMG) == 0) || (strcasecmp(optarg, ALERT_MODE_OPT__JH) == 0) || (strcasecmp(optarg, ALERT_MODE_OPT__DJR) == 0)) { ParseOutput(sc, NULL, "alert_fast: stdout packet"); sc->no_log = 1; /* turn on layer2 headers */ sc->output_flags |= OUTPUT_FLAG__SHOW_DATA_LINK; /* turn on data dump */ sc->output_flags |= OUTPUT_FLAG__APP_DATA; } else if (strcasecmp(optarg, ALERT_MODE_OPT__AJK) == 0) { ParseOutput(sc, NULL, "unified2"); } else if (strcasecmp(optarg, ALERT_MODE_OPT__UNIX_SOCK) == 0) { ParseOutput(sc, NULL, "alert_unixsock"); } else if (strcasecmp(optarg, ALERT_MODE_OPT__TEST) == 0) { ParseOutput(sc, NULL, "alert_test"); sc->no_log = 1; } else if (strcasecmp(optarg, "console:" ALERT_MODE_OPT__TEST) == 0) { ParseOutput(sc, NULL, "alert_test: stdout"); sc->no_log = 1; } else { FatalError("Unknown command line alert option: %s\n", optarg); } break; case 'b': /* log packets in binary format for post-processing */ ParseOutput(sc, NULL, "log_tcpdump"); output_logging = 1; break; case 'B': /* obfuscate with a substitution mask */ ConfigObfuscationMask(sc, optarg); break; case 'c': /* use configuration file x */ sc->run_mode_flags |= RUN_MODE_FLAG__IDS; snort_conf_file = SnortStrdup(optarg); break; case 'C': /* dump the application layer as text only */ ConfigDumpCharsOnly(sc, NULL); break; case 'd': /* dump the application layer data */ ConfigDumpPayload(sc, NULL); break; #ifndef WIN32 case 'E': /* Restarting from daemon mode */ case 'D': /* daemon mode */ /* These are parsed at the beginning so as to start logging * to syslog right away */ break; #endif case 'e': /* show second level header info */ ConfigDecodeDataLink(sc, NULL); break; #ifdef WIN32 case 'E': /* log alerts to Event Log */ ParseOutput(sc, NULL, "alert_syslog"); sc->logging_flags &= ~LOGGING_FLAG__SYSLOG_REMOTE; output_alerting = 1; break; #endif case 'f': sc->output_flags |= OUTPUT_FLAG__LINE_BUFFER; break; case 'F': /* read BPF filter in from a file */ ConfigBpfFile(sc, optarg); break; #ifndef WIN32 case 'g': /* setgid */ ConfigSetGid(sc, optarg); break; #endif case 'G': /* snort loG identifier */ sc->event_log_id = SnortStrtoul(optarg, &endptr, 0); if ((errno == ERANGE) || (*endptr != '\0') || (sc->event_log_id > UINT16_MAX)) { FatalError("Snort log identifier invalid: %s. It must " "be between 0 and %u.\n", optarg, UINT16_MAX); } /* Forms upper 2 bytes. Lower two bytes are the event id */ sc->event_log_id <<= 16; break; case 'h': /* set home network to x, this will help determine what to set * logging diectories to */ ConfigReferenceNet(sc, optarg); break; case 'H': sc->run_flags |= RUN_FLAG__STATIC_HASH; break; case 'i': ConfigInterface(sc, optarg); break; case 'I': /* add interface name to alert string */ ConfigAlertWithInterfaceName(sc, NULL); break; case 'k': /* set checksum mode */ ConfigChecksumMode(sc, optarg); break; case 'K': /* log mode */ if (strcasecmp(optarg, LOG_MODE_OPT__NONE) == 0) { sc->no_log = 1; } else if (strcasecmp(optarg, LOG_MODE_OPT__PCAP) == 0) { ParseOutput(sc, NULL, "log_tcpdump"); } else if (strcasecmp(optarg, LOG_MODE_OPT__ASCII) == 0) { ParseOutput(sc, NULL, "log_ascii"); } else { FatalError("Unknown command line log option: %s\n", optarg); } output_logging = 1; break; case 'l': /* use log dir */ ConfigLogDir(sc, optarg); break; case 'L': /* set BinLogFile name */ /* implies tcpdump format logging * 256 is kind of arbitrary but should be more than enough */ if (strlen(optarg) < 256) { ParseOutput(sc, NULL, "log_tcpdump"); sc->pcap_log_file = SnortStrdup(optarg); } else { FatalError("log_tcpdump file name \"%s\" has to be less " "than or equal to 256 characters.\n", optarg); } output_logging = 1; break; case 'M': /* This is parsed at the beginning so as to start logging * to syslog right away */ break; #ifndef WIN32 case 'm': /* set the umask for the output files */ ConfigUmask(sc, optarg); break; #endif case 'n': /* grab x packets and exit */ ConfigPacketCount(sc, optarg); break; case 'N': /* no logging mode */ ConfigNoLog(sc, NULL); break; case 'O': /* obfuscate the logged IP addresses for privacy */ ConfigObfuscate(sc, NULL); break; case 'p': /* disable explicit promiscuous mode */ ConfigNoPromiscuous(sc, NULL); break; case 'P': /* explicitly define snaplength of packets */ ConfigPacketSnaplen(sc, optarg); break; case 'q': /* no stdout output mode */ /* This is parsed at the beginning so as to start logging * in quiet mode right away */ break; #ifndef WIN32 case 'Q': LogMessage("Enabling inline operation\n"); sc->run_flags |= RUN_FLAG__INLINE; break; #endif case ENABLE_INLINE_TEST: LogMessage("Enable Inline Test Mode\n"); sc->run_flags |= RUN_FLAG__INLINE_TEST; break; case 'r': /* read packets from a TCPdump file instead of the net */ case PCAP_SINGLE: PQ_Single(optarg); sc->run_flags |= RUN_FLAG__READ; break; case 'R': /* augment pid file name suffix */ if ((strlen(optarg) >= MAX_PIDFILE_SUFFIX) || (strlen(optarg) <= 0) || (strstr(optarg, "..") != NULL) || (strstr(optarg, "/") != NULL)) { FatalError("Invalid pidfile suffix: %s. Suffix must " "less than %u characters and not have " "\"..\" or \"/\" in the name.\n", optarg, MAX_PIDFILE_SUFFIX); } SnortStrncpy(sc->pidfile_suffix, optarg, sizeof(sc->pidfile_suffix)); break; case 's': /* log alerts to syslog */ #ifndef WIN32 ParseOutput(sc, NULL, "alert_syslog"); #else sc->logging_flags |= LOGGING_FLAG__SYSLOG_REMOTE; #endif output_alerting = 1; break; case 'S': /* set a rules file variable */ { char *equal_ptr = strchr(optarg, '='); VarNode *node; if (equal_ptr == NULL) { FatalError("Format for command line variable definitions " "is:\n -S var=value\n"); } /* Save these and parse when snort conf is parsed so * they can be added to the snort conf configuration */ node = (VarNode *)SnortAlloc(sizeof(VarNode)); node->name = SnortStrndup(optarg, equal_ptr - optarg); /* Make sure it's not already in the list */ if (cmd_line_var_list != NULL) { VarNode *tmp = cmd_line_var_list; while (tmp != NULL) { if (strcasecmp(tmp->name, node->name) == 0) { FreeVarList(cmd_line_var_list); FatalError("Duplicate variable name: %s.\n", tmp->name); } tmp = tmp->next; } } node->value = SnortStrdup(equal_ptr + 1); node->line = SnortStrdup(optarg); node->next = cmd_line_var_list; cmd_line_var_list = node; /* Put line in a parser parsable form - we know the * equals is already there */ equal_ptr = strchr(node->line, '='); *equal_ptr = ' '; } break; #ifndef WIN32 case 't': /* chroot to the user specified directory */ ConfigChrootDir(sc, optarg); break; #endif case 'T': /* test mode, verify that the rules load properly */ sc->run_mode_flags |= RUN_MODE_FLAG__TEST; break; #ifndef WIN32 case 'u': /* setuid */ ConfigSetUid(sc, optarg); break; #endif case 'U': /* use UTC */ ConfigUtc(sc, NULL); break; case 'v': /* be verbose */ ConfigVerbose(sc, NULL); break; case 'V': /* prog ver already gets printed out, so we just exit */ break; #ifdef WIN32 case 'W': PrintVersion(sc); PrintAllInterfaces(); exit(0); /* XXX Should maybe use CleanExit here? */ break; #endif #if !defined(NO_NON_ETHER_DECODER) && defined(DLT_IEEE802_11) case 'w': /* show 802.11 all frames info */ sc->output_flags |= OUTPUT_FLAG__SHOW_WIFI_MGMT; break; #endif case 'X': /* display verbose packet bytecode dumps */ ConfigDumpPayloadVerbose(sc, NULL); break; case 'x': sc->run_flags |= RUN_FLAG__CONF_ERROR_OUT; break; case 'y': /* Add year to timestamp in alert and log files */ ConfigShowYear(sc, NULL); break; case 'Z': /* Set preprocessor perfmon file path/filename */ ConfigPerfFile(sc, optarg); break; case 'z': /* Set preprocessor memory stats path/filename */ ConfigDumpPeriodicMemStatsFile(sc, optarg); if (sc->memdump_file) periodic_dump_enable = true; break; case PCAP_FILE_LIST: case PCAP_LIST: #ifndef WIN32 case PCAP_DIR: #endif PQ_Multi((char)ch, optarg); sc->run_flags |= RUN_FLAG__READ; break; case PCAP_LOOP: { long int loop_count = SnortStrtol(optarg, &endptr, 0); if ((errno == ERANGE) || (*endptr != '\0') || (loop_count < 0) || (loop_count > 2147483647)) { FatalError("Valid values for --pcap-loop are between 0 and 2147483647\n"); } if (loop_count == 0) pcap_loop_count = -1; else pcap_loop_count = loop_count; } break; case PCAP_RESET: sc->run_flags |= RUN_FLAG__PCAP_RESET; break; #if defined(SNORT_RELOAD) && !defined(WIN32) case PCAP_RELOAD: sc->run_flags |= RUN_FLAG__PCAP_RELOAD; break; #endif #ifndef WIN32 case PCAP_FILTER: PQ_SetFilter(optarg); break; case PCAP_NO_FILTER: PQ_SetFilter(NULL); break; #endif case PCAP_SHOW: sc->run_flags |= RUN_FLAG__PCAP_SHOW; break; #ifdef MPLS case ENABLE_MPLS_MULTICAST: ConfigEnableMplsMulticast(sc, NULL); break; case ENABLE_OVERLAPPING_IP: ConfigEnableMplsOverlappingIp(sc, NULL); break; case MAX_MPLS_LABELCHAIN_LEN: ConfigMaxMplsLabelChain(sc, optarg); break; case MPLS_PAYLOAD_TYPE: ConfigMplsPayloadType(sc, optarg); break; #endif case REQUIRE_RULE_SID: sc->run_flags |= RUN_FLAG__REQUIRE_RULE_SID; break; case ARG_CS_DIR: if ( optarg != NULL ) sc->cs_dir = SnortStrdup(optarg); break; #ifdef REG_TEST case ARG_HA_PEER: sc->ha_peer = true; break; case ARG_HA_OUT: sc->ha_out = SnortStrdup(optarg); break; case ARG_HA_IN: sc->ha_in = SnortStrdup(optarg); break; case ARG_HA_PDTS_IN: sc->ha_pdts_in = SnortStrdup(optarg); break; #endif case SUPPRESS_CONFIG_LOG: sc->suppress_config_log = 1; break; #ifdef DUMP_BUFFER case BUFFER_DUMP: dump_alert_only = false; dump_enabled = true; ConfigBufferDump(sc, optarg); ParseOutput(sc, NULL, "log_buffer_dump"); break; case BUFFER_DUMP_ALERT: dump_alert_only = true; dump_enabled = true; ConfigBufferDump(sc, optarg); ParseOutput(sc, NULL, "log_buffer_dump"); break; #endif case '?': /* show help and exit with 1 */ PrintVersion(sc); ShowUsage(argv[0]); /* XXX Should do a clean exit */ exit(1); break; default: FatalError("Invalid option: %c.\n", ch); break; } } sc->bpf_filter = copy_argv(&argv[optind]); if ((sc->run_mode_flags & RUN_MODE_FLAG__TEST) && (sc->run_flags & RUN_FLAG__DAEMON)) { FatalError("Cannot use test mode and daemon mode together.\n" "To verify configuration, run first in test " "mode and then restart in daemon mode.\n"); } if ((sc->run_flags & RUN_FLAG__INLINE) && (sc->run_flags & RUN_FLAG__INLINE_TEST)) { FatalError("Cannot use inline adapter mode and inline test " "mode together. \n"); } // TBD no reason why command line args only can't be checked // marginally useful, perhaps, but why do we go out of our way // to make things hard on the user? if ((sc->run_mode_flags & RUN_MODE_FLAG__TEST) && (snort_conf_file == NULL)) { FatalError("Test mode must be run with a snort configuration " "file. Use the '-c' option on the command line to " "specify a configuration file.\n"); } if (pcap_loop_count && !(sc->run_flags & RUN_FLAG__READ)) { FatalError("--pcap-loop can only be used in combination with pcaps " "on the command line.\n"); } #if defined(SNORT_RELOAD) && !defined(WIN32) if ((sc->run_flags & RUN_FLAG__PCAP_RELOAD) && !(sc->run_flags & RUN_FLAG__READ)) { FatalError("--pcap-reload can only be used in combination with pcaps " "on the command line.\n"); } #endif /* Set the run mode based on what we've got from command line */ /* Version overrides all */ if (sc->run_mode_flags & RUN_MODE_FLAG__VERSION) { sc->run_mode = RUN_MODE__VERSION; } /* Next dumping so rule stubs */ else if (sc->run_mode_flags & RUN_MODE_FLAG__RULE_DUMP) { sc->run_mode = RUN_MODE__RULE_DUMP; } /* Next if we want to test a snort conf */ else if (sc->run_mode_flags & RUN_MODE_FLAG__TEST) { sc->run_mode = RUN_MODE__TEST; } /* Now if there is a snort conf. If a snort conf wasn't given on the * command line, we'll look in a default place if the next ones * don't match */ else if ((sc->run_mode_flags & RUN_MODE_FLAG__IDS) && (snort_conf_file != NULL)) { sc->run_mode = RUN_MODE__IDS; } /* If logging but not alerting or log directory is set */ else if ((output_logging && !output_alerting) || (sc->log_dir != NULL)) { sc->no_alert = 1; sc->run_mode = RUN_MODE__PACKET_LOG; } /* If none of the above and not logging or alerting and verbose */ else if ((!output_logging && !output_alerting) && (sc->logging_flags & LOGGING_FLAG__VERBOSE)) { sc->no_alert = 1; sc->no_log = 1; sc->run_mode = RUN_MODE__PACKET_DUMP; } #if 1 if (!sc->run_mode) { sc->no_alert = 1; sc->no_log = 1; sc->run_mode = RUN_MODE__PACKET_DUMP; } #else if (!sc->run_mode) sc->run_mode = RUN_MODE__IDS; /* If mode mandates a conf and we don't have one, check for default. */ if (((sc->run_mode == RUN_MODE__IDS) || (sc->run_mode == RUN_MODE__TEST)) && (snort_conf_file == NULL)) { snort_conf_file = ConfigFileSearch(); if (snort_conf_file == NULL) { DisplayBanner(); ShowUsage(argv[0]); FatalError("\n\nUh, you need to tell me to do something..."); } } #endif if ((sc->run_mode == RUN_MODE__PACKET_LOG) && (sc->output_configs == NULL)) { ParseOutput(sc, NULL, "log_tcpdump"); } switch ( snort_conf->run_mode ) { case RUN_MODE__IDS: if (ScLogVerbose()) log_func = PrintPacket; break; case RUN_MODE__PACKET_LOG: log_func = LogPacket; break; case RUN_MODE__PACKET_DUMP: log_func = PrintPacket; break; default: break; } SetSnortConfDir(); } /* * Function: SetPktProcessor() * * Purpose: Set root decoder based on datalink */ // TBD add GetDecoder(dlt) to decode module and hide all // protocol decoder functions. static int SetPktProcessor(void) { const char* slink = NULL; const char* extra = NULL; int dlt = DAQ_GetBaseProtocol(); switch ( dlt ) { case DLT_EN10MB: slink = "Ethernet"; grinder = DecodeEthPkt; break; #ifdef DLT_LOOP case DLT_LOOP: #endif case DLT_NULL: /* loopback and stuff.. you wouldn't perform intrusion detection * on it, but it's ok for testing. */ slink = "LoopBack"; extra = "Data link layer header parsing for this network type " "isn't implemented yet"; grinder = DecodeNullPkt; break; case DLT_RAW: case DLT_IPV4: slink = "Raw IP4"; extra = "There's no second layer header available for this datalink"; grinder = DecodeRawPkt; break; case DLT_IPV6: slink = "Raw IP6"; extra = "There's no second layer header available for this datalink"; grinder = DecodeRawPkt6; break; #ifdef DLT_I4L_IP case DLT_I4L_IP: slink = "I4L-ip"; grinder = DecodeEthPkt; break; #endif #ifndef NO_NON_ETHER_DECODER #ifdef DLT_I4L_CISCOHDLC case DLT_I4L_CISCOHDLC: slink = "I4L-cisco-h"; grinder = DecodeI4LCiscoIPPkt; break; #endif case DLT_PPP: slink = "PPP"; extra = "Second layer header parsing for this datalink " "isn't implemented yet"; grinder = DecodePppPkt; break; #ifdef DLT_I4L_RAWIP case DLT_I4L_RAWIP: // you need the I4L modified version of libpcap to get this stuff // working slink = "I4L-rawip"; grinder = DecodeI4LRawIPPkt; break; #endif #ifdef DLT_IEEE802_11 case DLT_IEEE802_11: slink = "IEEE 802.11"; grinder = DecodeIEEE80211Pkt; break; #endif #ifdef DLT_ENC case DLT_ENC: slink = "Encapsulated data"; grinder = DecodeEncPkt; break; #else case 13: #endif /* DLT_ENC */ case DLT_IEEE802: slink = "Token Ring"; grinder = DecodeTRPkt; break; case DLT_FDDI: slink = "FDDI"; grinder = DecodeFDDIPkt; break; #ifdef DLT_CHDLC case DLT_CHDLC: slink = "Cisco HDLC"; grinder = DecodeChdlcPkt; break; #endif case DLT_SLIP: slink = "SLIP"; extra = "Second layer header parsing for this datalink " "isn't implemented yet\n"; grinder = DecodeSlipPkt; break; #ifdef DLT_PPP_SERIAL case DLT_PPP_SERIAL: /* PPP with full HDLC header*/ slink = "PPP Serial"; extra = "Second layer header parsing for this datalink " " isn't implemented yet"; grinder = DecodePppSerialPkt; break; #endif #ifdef DLT_LINUX_SLL case DLT_LINUX_SLL: slink = "Linux SLL"; grinder = DecodeLinuxSLLPkt; break; #endif #ifdef DLT_PFLOG case DLT_PFLOG: slink = "OpenBSD PF log"; grinder = DecodePflog; break; #endif #ifdef DLT_OLDPFLOG case DLT_OLDPFLOG: slink = "Old OpenBSD PF log"; grinder = DecodeOldPflog; break; #endif #endif // NO_NON_ETHER_DECODER default: /* oops, don't know how to handle this one */ FatalError("Cannot decode data link type %d\n", dlt); break; } if ( !ScReadMode() || ScPcapShow() ) { LogMessage("Decoding %s\n", slink); } if (extra && ScOutputDataLink()) { LogMessage("%s\n", extra); snort_conf->output_flags &= ~OUTPUT_FLAG__SHOW_DATA_LINK; } #ifdef ACTIVE_RESPONSE Encode_Init(); #endif return 0; } /* * Handle idle time checks in snort packet processing loop */ static void SnortIdle(void) { /* Rollover of performance log */ if (IsSetRotatePerfFileFlag()) { sfRotateBaseStatsFile(perfmon_config); sfRotateFlowStatsFile(perfmon_config); ClearRotatePerfFileFlag(); } #ifdef OPENBSD #ifdef SNORT_RELOAD else if (reload_signal != reload_total) nanosleep(&packet_sleep, NULL); #endif #endif rotate_preproc_stats(); #ifndef REG_TEST if( session_api ) session_api->check_session_timeout(FLOW_COUNT, time(NULL)); #ifdef SNORT_RELOAD ReloadAdjust(true, time(NULL)); #endif #endif ControlSocketDoWork(1); #ifdef SIDE_CHANNEL SideChannelDrainRX(0); #endif IdleProcessingExecute(); } void PacketLoop (void) { int error = 0; int pkts_to_read = (int)snort_conf->pkt_cnt; time_t curr_time, last_time; curr_time = time(NULL); last_time = curr_time; TimeStart(); while ( !exit_logged ) { error = DAQ_Acquire(pkts_to_read, PacketCallback, NULL); #ifdef CONTROL_SOCKET if (packet_dump_stop) PacketDumpClose(); #endif #ifdef SIDE_CHANNEL /* If we didn't manage to lock the process lock in a DAQ acquire callback, lock it now. */ if (ScSideChannelEnabled() && !snort_process_lock_held) { pthread_mutex_lock(&snort_process_lock); snort_process_lock_held = true; } #endif if ( error ) { //Update the time tracker curr_time = packet_time(); last_time = curr_time; if ( !ScReadMode() || !PQ_Next() ) { /* If not read-mode or no next pcap, we're done */ break; } #ifdef REG_TEST else regTestCheckIPIncrement(); #endif } /* Check for any pending signals when no packets are read*/ else { // TBD SnortIdle() only checks for perf file rotation // and that can only be done after calling SignalCheck() // so either move SnortIdle() to SignalCheck() or directly // set the flag in the signal handler (and then clear it // in SnortIdle()). if ( !ScReadMode() ) { time_t new_time = time(NULL); curr_time += new_time - last_time; last_time = new_time; // Check if its time to dump perf data sfPerformanceStatsOOB(perfmon_config, curr_time); if (periodic_dump_enable) dump_preproc_stats(curr_time); } // check for signals if ( SignalCheck() ) { #ifndef SNORT_RELOAD // Got SIGNAL_SNORT_RELOAD Restart(); #endif } CheckForReload(); } if ( pkts_to_read > 0 ) { if ( snort_conf->pkt_cnt <= pc.total_from_daq ) break; else pkts_to_read = (int)(snort_conf->pkt_cnt - pc.total_from_daq); } // idle time processing..quick things to check or do ... // TBD fix this per above ... and if it stays here, should // prolly change the name if acquire breaks due to a signal // (since in that case we aren't idle here) SnortIdle(); #ifdef SIDE_CHANNEL /* Unlock the Snort process lock once we've hit the DAQ acquire timeout. */ if (snort_process_lock_held) { snort_process_lock_held = false; pthread_mutex_unlock(&snort_process_lock); } #endif } #ifdef CONTROL_SOCKET PacketDumpClose(); #endif #ifdef SIDE_CHANNEL /* Error conditions can lead to exiting the packet loop prior to unlocking the process lock. */ if (snort_process_lock_held) { snort_process_lock_held = false; pthread_mutex_unlock(&snort_process_lock); } #endif if ( !exit_logged && error ) { if ( error == DAQ_READFILE_EOF ) error = 0; else if ( error > 0 ) { SnortShutdownThreads(error); DAQ_Abort(); exit(1); } CleanExit(error); } done_processing = 1; } /* Resets Snort to a post-configuration state */ static void SnortReset(void) { PreprocSignalFuncNode *idxPreprocReset; PreprocSignalFuncNode *idxPreprocResetStats; /* reset preprocessors */ idxPreprocReset = preproc_reset_funcs; while (idxPreprocReset != NULL) { idxPreprocReset->func(-1, idxPreprocReset->arg); idxPreprocReset = idxPreprocReset->next; } SnortEventqReset(); Replace_ResetQueue(); #ifdef ACTIVE_RESPONSE Active_ResetQueue(); #endif sfthreshold_reset_active(); RateFilter_ResetActive(); TagCacheReset(); #ifdef PERF_PROFILING ShowPreprocProfiles(); ShowRuleProfiles(); #endif DropStats(0); /* zero out packet count */ memset(&pc, 0, sizeof(pc)); #ifdef PERF_PROFILING ResetRuleProfiling(); ResetPreprocProfiling(); #endif /* reset preprocessor stats */ idxPreprocResetStats = preproc_reset_stats_funcs; while (idxPreprocResetStats != NULL) { idxPreprocResetStats->func(-1, idxPreprocResetStats->arg); idxPreprocResetStats = idxPreprocResetStats->next; } } #if 0 /* locate one of the possible default config files */ /* allocates memory to hold filename */ static char *ConfigFileSearch(void) { struct stat st; int i; char *conf_files[]={"/etc/snort.conf", "./snort.conf", NULL}; char *fname = NULL; char *rval = NULL; i = 0; /* search the default set of config files */ while(conf_files[i]) { fname = conf_files[i]; if(stat(fname, &st) != -1) { rval = SnortStrdup(fname); break; } i++; } /* search for .snortrc in the HOMEDIR */ if(!rval) { char *home_dir = NULL; if((home_dir = getenv("HOME")) != NULL) { char *snortrc = "/.snortrc"; int path_len; path_len = strlen(home_dir) + strlen(snortrc) + 1; /* create the full path */ fname = (char *)SnortAlloc(path_len); SnortSnprintf(fname, path_len, "%s%s", home_dir, snortrc); if(stat(fname, &st) != -1) rval = fname; else free(fname); } } return rval; } #endif /* Signal Handlers ************************************************************/ static void SigExitHandler(int signal) { if (exit_signal != 0) return; /* If snort received signal to exit before its initialization, * we can just close DAQ interfaces and exit quickly, otherwise * lets follow normal path. Snort will not print stats when * it is asked to exit during initialization. */ if (snort_initializing) { DAQ_Abort(); _exit(0); } exit_signal = signal; } static void SigDumpStatsHandler(int signal) { dump_stats_signal = true; } static void SigRotateStatsHandler(int signal) { rotate_stats_signal = true; } static void SigReloadHandler(int signal) { #if defined(SNORT_RELOAD) && !defined(WIN32) reload_signal++; #else reload_signal = true; #endif } #ifdef CONTROL_SOCKET static void SigPipeHandler(int signal) { } #endif #ifdef TARGET_BASED void SigNoAttributeTableHandler(int signal) { no_attr_table_signal = true; } #endif static void SigOopsHandler(int signal) { if ( s_packet.pkth ) { s_pkth = *s_packet.pkth; if ( s_packet.pkt ) memcpy(s_data, s_packet.pkt, 0xFFFF & s_packet.pkth->caplen); } SnortAddSignal(signal, SIG_DFL, 0); raise(signal); } static void PrintStatistics (void) { if ( ScTestMode() || ScVersionMode() || ScRuleDumpMode() ) return; fpShowEventStats(snort_conf); #ifdef PERF_PROFILING { int saved_internal_log_level = internal_log_level; internal_log_level = INTERNAL_LOG_LEVEL__MESSAGE; ShowPreprocProfiles(); ShowRuleProfiles(); internal_log_level = saved_internal_log_level; } #endif DropStats(2); print_thresholding(snort_conf->threshold_config, 1); } /**************************************************************************** * * Function: CleanExit() * * Purpose: Clean up misc file handles and such and exit * * Arguments: exit value; * * Returns: void function * ****************************************************************************/ void CleanExit(int exit_val) { SnortConfig tmp; #ifdef TARGET_BASED #ifdef DEBUG #if 0 SFLAT_dump(); #endif #endif #endif /* Have to trick LogMessage to log correctly after snort_conf * is freed */ memset(&tmp, 0, sizeof(tmp)); if (snort_conf != NULL) { tmp.internal_log_level = snort_conf->internal_log_level; tmp.run_mode = snort_conf->run_mode; tmp.run_flags |= (snort_conf->run_flags & RUN_FLAG__DAEMON); tmp.logging_flags |= (snort_conf->logging_flags & LOGGING_FLAG__SYSLOG); } SnortCleanup(exit_val); snort_conf = &tmp; if (!ScVersionMode()) { LogMessage("Snort exiting\n"); } #ifndef WIN32 closelog(); #endif if ( !done_processing ) exit(exit_val); } void SnortShutdownThreads(int exit_val) { LogMessage("Snort is shutting down other threads, exit_val %d", exit_val); if (!InMainThread()) { LogMessage("Snort shutdown thread is not called at main thread, so exiting..!"); return; } if (already_exiting != 0) { LogMessage("Exiting shutdown Threads, exit processing by another thread"); return; } if (pthread_mutex_trylock(&cleanup_mutex) != 0) { LogMessage("Exiting shutdown Threads, as someother thread is cleaning!"); return; } already_exiting = 1; snort_exiting = 1; snort_initializing = false; #if defined(INLINE_FAILOPEN) && !defined(WIN32) if (inline_failopen_thread_running) { pthread_kill(inline_failopen_thread_id, SIGKILL); } #endif if (DAQ_WasStarted()) { #ifdef EXIT_CHECK if (snort_conf->exit_check) ExitCheckEnd(); #endif } ControlSocketCleanUp(); #ifdef SIDE_CHANNEL if (ScSideChannelEnabled()) { SideChannelStopTXThread(); SideChannelCleanUp(); } #endif #if defined(SNORT_RELOAD) && !defined(WIN32) if (snort_reload_thread_created) { pthread_join(snort_reload_thread_id, NULL); } #endif #if defined(TARGET_BASED) && !defined(WIN32) if (attribute_reload_thread_running) { attribute_reload_thread_stop = 1; pthread_kill(attribute_reload_thread_id, SIGVTALRM); while (attribute_reload_thread_running) nanosleep(&thread_sleep, NULL); pthread_join(attribute_reload_thread_id, NULL); } #endif PrintStatistics(); pthread_mutex_unlock(&cleanup_mutex); LogMessage("Shutting down the threads -- Done"); } static void SnortCleanup(int exit_val) { PreprocSignalFuncNode *idxPreproc = NULL; PluginSignalFuncNode *idxPlugin = NULL; /* This function can be called more than once. For example, * once from the SIGINT signal handler, and once recursively * as a result of calling pcap_close() below. We only need * to perform the cleanup once. */ if (pthread_mutex_trylock(&cleanup_mutex) == 0) { /* * We have the lock now, make sure no one else called this * function before this thread did. */ if (already_exiting != 0 ) { pthread_mutex_unlock(&cleanup_mutex); return; } } else { /* * Someother thread is cleaning up. Return. */ return; } already_exiting = 1; snort_exiting = 1; snort_initializing = false; /* just in case we cut out early */ Active_Suspend(); // rules that fire now can't actually block #if defined(INLINE_FAILOPEN) && !defined(WIN32) if (inline_failopen_thread_running) pthread_kill(inline_failopen_thread_id, SIGKILL); #endif if ( DAQ_WasStarted() ) { #ifdef EXIT_CHECK if (snort_conf->exit_check) ExitCheckEnd(); #endif DAQ_Stop(); } ControlSocketCleanUp(); #ifdef SIDE_CHANNEL SideChannelStopTXThread(); SideChannelCleanUp(); #endif IdleProcessingCleanUp(); if ( snort_conf->dirty_pig ) { DAQ_Delete(); DAQ_Term(); ScRestoreInternalLogLevel(); PrintStatistics(); pthread_mutex_unlock(&cleanup_mutex); return; } #if defined(SNORT_RELOAD) && !defined(WIN32) /* Setting snort_exiting will cause the thread to break out * of it's loop and exit */ if (snort_reload_thread_created) pthread_join(snort_reload_thread_id, NULL); #endif #if defined(TARGET_BASED) && !defined(WIN32) if (attribute_reload_thread_running) { /* Set the flag to stop the attribute reload thread and * send VTALRM signal to pull it out of the idle sleep. * Thread exits normally on next iteration through its * loop. * * If its doing other processing, that continues post * interrupt and thread exits normally. */ attribute_reload_thread_stop = 1; pthread_kill(attribute_reload_thread_id, SIGVTALRM); while (attribute_reload_thread_running) nanosleep(&thread_sleep, NULL); pthread_join(attribute_reload_thread_id, NULL); } #endif /* Do some post processing on any incomplete Preprocessor Data */ idxPreproc = preproc_shutdown_funcs; while (idxPreproc) { idxPreproc->func(SIGQUIT, idxPreproc->arg); idxPreproc = idxPreproc->next; } /* Do some post processing on any incomplete Plugin Data */ idxPlugin = plugin_shutdown_funcs; while(idxPlugin) { idxPlugin->func(SIGQUIT, idxPlugin->arg); idxPlugin = idxPlugin->next; } if (!ScTestMode() && !ScVersionMode() && !ScRuleDumpMode() ) { if ( !exit_val ) TimeStop(); } /* Exit preprocessors */ idxPreproc = preproc_clean_exit_funcs; while(idxPreproc) { idxPreproc->func(SIGQUIT, idxPreproc->arg); idxPreproc = idxPreproc->next; } /* Do some post processing on any incomplete Plugin Data */ idxPlugin = plugin_clean_exit_funcs; while(idxPlugin) { idxPlugin->func(SIGQUIT, idxPlugin->arg); idxPlugin = idxPlugin->next; } if (decoderActionQ != NULL) { sfActionQueueDestroy (decoderActionQ); mempool_destroy (&decoderAlertMemPool); decoderActionQ = NULL; memset(&decoderAlertMemPool, 0, sizeof(decoderAlertMemPool)); } DAQ_Delete(); DAQ_Term(); ScRestoreInternalLogLevel(); // Do we need this? PrintStatistics(); #ifdef ACTIVE_RESPONSE Active_Term(); Encode_Term(); #endif CleanupProtoNames(); #ifdef TARGET_BASED SFAT_Cleanup(); FreeProtoocolReferenceTable(); #endif PQ_CleanUp(); ClosePidFile(); /* remove pid file */ if (SnortStrnlen(snort_conf->pid_filename, sizeof(snort_conf->pid_filename)) > 0) { int ret; ret = unlink(snort_conf->pid_filename); if (ret != 0) { ErrorMessage("Could not remove pid file %s: %s\n", snort_conf->pid_filename, strerror(errno)); } } #ifdef INTEL_SOFT_CPM //IntelPmPrintBufferStats(); #endif /* free allocated memory */ if (snort_conf == snort_cmd_line_conf) { SnortConfFree(snort_cmd_line_conf); snort_cmd_line_conf = NULL; snort_conf = NULL; } else { SnortConfFree(snort_cmd_line_conf); snort_cmd_line_conf = NULL; #ifdef SNORT_RELOAD if (!reloadInProgress) { SnortConfFree(snort_conf); snort_conf = NULL; } #else SnortConfFree(snort_conf); snort_conf = NULL; #endif } #ifdef SNORT_RELOAD if (snort_conf_new != NULL) { /* If main thread is exiting, it won't swap in the new configuration, * so free it here, really just to quiet valgrind. Note this needs to * be done here since some preprocessors, will potentially need access * to the data here since stream5 flushes out its cache and potentially * sends reassembled packets back through Preprocess */ SnortConfFree(snort_conf_new); snort_conf_new = NULL; } #endif EventTrace_Term(); detection_filter_cleanup(); sfthreshold_free(); RateFilter_Cleanup(); asn1_free_mem(); #ifdef SNORT_RELOAD if (!reloadInProgress) { #endif FreeOutputConfigFuncs(); FreePreprocConfigFuncs(); FreeRuleOptConfigFuncs(rule_opt_config_funcs); rule_opt_config_funcs = NULL; FreeRuleOptOverrideInitFuncs(rule_opt_override_init_funcs); rule_opt_override_init_funcs = NULL; FreeRuleOptByteOrderFuncs(rule_opt_byte_order_funcs); rule_opt_byte_order_funcs = NULL; FreeRuleOptParseCleanupList(rule_opt_parse_cleanup_list); rule_opt_parse_cleanup_list = NULL; #ifdef SNORT_RELOAD } #endif FreeOutputList(AlertList); AlertList = NULL; FreeOutputList(LogList); LogList = NULL; /* Global lists */ FreePreprocStatsFuncs(preproc_stats_funcs); preproc_stats_funcs = NULL; FreePreprocSigFuncs(preproc_shutdown_funcs); preproc_shutdown_funcs = NULL; FreePreprocSigFuncs(preproc_clean_exit_funcs); preproc_clean_exit_funcs = NULL; FreePreprocSigFuncs(preproc_reset_funcs); preproc_reset_funcs = NULL; FreePreprocSigFuncs(preproc_reset_stats_funcs); preproc_reset_stats_funcs = NULL; FreePluginSigFuncs(plugin_shutdown_funcs); plugin_shutdown_funcs = NULL; FreePluginSigFuncs(plugin_clean_exit_funcs); plugin_clean_exit_funcs = NULL; #ifdef SNORT_RELOAD FreePluginPostConfigFuncs(plugin_reload_funcs); plugin_reload_funcs = NULL; #endif FreePeriodicFuncs(periodic_check_funcs); periodic_check_funcs = NULL; ParserCleanup(); CloseDynamicPreprocessorLibs(); CloseDynamicEngineLibs(); #ifdef SIDE_CHANNEL CloseDynamicSideChannelLibs(); #endif output_unload(); CleanupTag(); ClearDumpBuf(); #ifdef PERF_PROFILING CleanupPreprocStatsNodeList(); #endif if (netmasks != NULL) { free(netmasks); netmasks = NULL; } if (protocol_names != NULL) { int i; for (i = 0; i < NUM_IP_PROTOS; i++) { if (protocol_names[i] != NULL) free(protocol_names[i]); } free(protocol_names); protocol_names = NULL; } #ifdef INTEL_SOFT_CPM IntelPmStopInstance(); #endif SynToMulticastDstIpDestroy(); MulticastReservedIpDestroy(); FreeVarList(cmd_line_var_list); if (snort_conf_file != NULL) free(snort_conf_file); if (snort_conf_dir != NULL) free(snort_conf_dir); if (s_packet.ip6_extensions != NULL) free(s_packet.ip6_extensions); close_fileAPI(); pthread_mutex_unlock(&cleanup_mutex); } void Restart(void) { int daemon_mode = ScDaemonMode(); #ifndef WIN32 if ((!ScReadMode() && (getuid() != 0)) || (snort_conf->chroot_dir != NULL)) { LogMessage("Reload via Signal Reload does not work if you aren't root " "or are chroot'ed.\n"); # ifdef SNORT_RELOAD /* We are restarting because of a configuration verification problem */ CleanExit(1); # else return; # endif } #endif /* WIN32 */ LogMessage("\n"); LogMessage("***** Restarting Snort *****\n"); LogMessage("\n"); SnortCleanup(0); if (daemon_mode) { int ch; int option_index = -1; optind = 1; while ((ch = getopt_long(snort_argc, snort_argv, valid_options, long_options, &option_index)) != -1) { switch (ch) { case 'D': { int i = optind-1, j; int index = strlen(snort_argv[i]) - 1; /* 'D' isn't the last option in the opt string so * optind hasn't moved past this option string yet */ if ((snort_argv[i][0] != '-') || ((index > 0) && (snort_argv[i][1] == '-')) || (snort_argv[i][index] != 'D')) { i++; } /* Replace -D with -E to indicate we've already daemonized */ for (j = 0; j < (int)strlen(snort_argv[i]); j++) { if (snort_argv[i][j] == 'D') { snort_argv[i][j] = 'E'; break; } } } break; default: break; } } } #ifdef PARANOID execv(snort_argv[0], snort_argv); #else execvp(snort_argv[0], snort_argv); #endif /* only get here if we failed to restart */ LogMessage("Restarting %s failed: %s\n", snort_argv[0], strerror(errno)); #ifndef WIN32 closelog(); #endif exit(-1); } void print_packet_count(void) { LogMessage("[" STDu64 "]", pc.total_from_daq); } /* * Check for signal activity */ int SignalCheck(void) { switch (exit_signal) { case SIGTERM: if (!exit_logged) { ErrorMessage("*** Caught Term-Signal\n"); exit_logged = 1; if ( DAQ_BreakLoop(DAQ_SUCCESS) ) return 0; } CleanExit(0); break; case SIGINT: if (!exit_logged) { ErrorMessage("*** Caught Int-Signal\n"); exit_logged = 1; if ( DAQ_BreakLoop(DAQ_SUCCESS) ) return 0; } CleanExit(0); break; case SIGQUIT: if (!exit_logged) { ErrorMessage("*** Caught Quit-Signal\n"); exit_logged = 1; if ( DAQ_BreakLoop(DAQ_SUCCESS) ) return 0; } CleanExit(0); break; default: break; } if (dump_stats_signal) { ErrorMessage("*** Caught Dump Stats-Signal\n"); DropStats(0); } dump_stats_signal = false; if (rotate_stats_signal) { ErrorMessage("*** Caught Signal: 'Rotate Perfmonitor Stats'\n"); /* Make sure the preprocessor is enabled - it can only be enabled * in default policy */ if (!ScIsPreprocEnabled(PP_PERFMONITOR, 0)) { ErrorMessage("!!! Cannot rotate stats - Perfmonitor is not configured !!!\n"); } else { SetRotatePerfFileFlag(); } } rotate_stats_signal = false; #ifdef TARGET_BASED if (no_attr_table_signal) ErrorMessage("!!! Cannot reload attribute table - Attribute table is not configured !!!\n"); no_attr_table_signal = false; #endif #ifndef SNORT_RELOAD if (reload_signal ) { ErrorMessage("*** Caught Reload-Signal\n"); reload_signal = false; return 1; } reload_signal = false; #endif return 0; } static void InitGlobals(void) { memset(&pc, 0, sizeof(pc)); InitNetmasks(); InitProtoNames(); #ifdef SIDE_CHANNEL pthread_mutex_init(&snort_process_lock, NULL); #endif pthread_mutex_init(&cleanup_mutex, NULL); pthread_mutex_init(&dynamic_rules_lock, NULL); } /* Alot of this initialization can be skipped if not running in IDS mode * but the goal is to minimize config checks at run time when running in * IDS mode so we keep things simple and enforce that the only difference * among run_modes is how we handle packets via the log_func. */ SnortConfig * SnortConfNew(void) { SnortConfig *sc = (SnortConfig *)SnortAlloc(sizeof(SnortConfig)); sc->pkt_cnt = 0; #ifdef REG_TEST sc->pkt_skip = 0; #endif sc->pkt_snaplen = -1; /*user_id and group_id should be initialized to -1 by default, because * chown() use this later, -1 means no change to user_id/group_id*/ sc->user_id = -1; sc->group_id = -1; sc->checksum_flags = CHECKSUM_FLAG__ALL; sc->tagged_packet_limit = 256; sc->default_rule_state = RULE_STATE_ENABLED; sc->pcre_match_limit = 1500; sc->pcre_match_limit_recursion = 1500; sc->ipv6_max_frag_sessions = 10000; sc->ipv6_frag_timeout = 60; /* This is the default timeout on BSD */ memset(sc->pid_path, 0, sizeof(sc->pid_path)); memset(sc->pid_filename, 0, sizeof(sc->pid_filename)); memset(sc->pidfile_suffix, 0, sizeof(sc->pidfile_suffix)); #ifdef TARGET_BASED /* Default max size of the attribute table */ sc->max_attribute_hosts = DEFAULT_MAX_ATTRIBUTE_HOSTS; sc->max_attribute_services_per_host = DEFAULT_MAX_ATTRIBUTE_SERVICES_PER_HOST; /* Default max number of services per rule */ sc->max_metadata_services = DEFAULT_MAX_METADATA_SERVICES; #endif #if defined(FEAT_OPEN_APPID) #ifdef TARGET_BASED sc->max_metadata_appid = DEFAULT_MAX_METADATA_APPID; #endif #endif /* defined(FEAT_OPEN_APPID) */ #ifdef MPLS sc->mpls_stack_depth = DEFAULT_LABELCHAIN_LENGTH; #endif sc->targeted_policies = NULL; sc->num_policies_allocated = 0; sc->paf_max = DEFAULT_PAF_MAX; /* Default secure hash pattern type */ sc->Default_Protected_Content_Hash_Type = SECHASH_NONE; sc->max_ip6_extensions = DEFAULT_MAX_IP6_EXTENSIONS; sc->internal_log_level = INTERNAL_LOG_LEVEL__MESSAGE; return sc; } void SnortConfFree(SnortConfig *sc) { tSfPolicyId i; if (sc == NULL) return; if (sc->dynamic_rules_path != NULL) free(sc->dynamic_rules_path); if (sc->log_dir != NULL) free(sc->log_dir); if (sc->orig_log_dir != NULL) free(sc->orig_log_dir); if (sc->interface != NULL) free(sc->interface); if (sc->bpf_file != NULL) free(sc->bpf_file); if (sc->pcap_log_file != NULL) free(sc->pcap_log_file); if (sc->chroot_dir != NULL) free(sc->chroot_dir); if (sc->alert_file != NULL) free(sc->alert_file); if (sc->perf_file != NULL) free(sc->perf_file); if (sc->memdump_file!= NULL) free(sc->memdump_file); if (sc->bpf_filter != NULL) free(sc->bpf_filter); if (sc->event_trace_file != NULL) free(sc->event_trace_file); #ifdef PERF_PROFILING if (sc->profile_rules.filename != NULL) free(sc->profile_rules.filename); if (sc->profile_preprocs.filename != NULL) free(sc->profile_preprocs.filename); #endif /* Main Thread only should cleanup if snort is exiting unless dynamic libs have changed */ if (detection_lib_changed || (InMainThread() && snort_exiting)) { pthread_mutex_lock(&dynamic_rules_lock); DynamicRuleListFree(sc->dynamic_rules); sc->dynamic_rules = NULL; pthread_mutex_unlock(&dynamic_rules_lock); CloseDynamicDetectionLibs(sc); detection_lib_changed = false; } FreeDynamicLibInfos(sc); FreeOutputConfigs(sc->output_configs); FreeOutputConfigs(sc->rule_type_output_configs); #ifdef SIDE_CHANNEL FreeSideChannelModuleConfigs(sc->side_channel_config.module_configs); #ifdef REG_TEST if (sc && sc->file_config) FileSSConfigFree(sc->file_config); #endif #endif FreePreprocConfigs(sc); if (sc->config_table != NULL) sfghash_delete(sc->config_table); if (sc->base_version != NULL) free(sc->base_version); for (i = 0; i < sc->num_policies_allocated; i++) { SnortPolicyFree(sc->targeted_policies[i]); } FreeRuleStateList(sc->rule_state_list); FreeClassifications(sc->classifications); FreeReferences(sc->references); FreeRuleLists(sc); SoRuleOtnLookupFree(sc->so_rule_otn_map); OtnLookupFree(sc->otn_map); VarTablesFree(sc); PortTablesFree(sc->port_tables); FastPatternConfigFree(sc->fast_pattern_config); ThresholdConfigFree(sc->threshold_config); RateFilter_ConfigFree(sc->rate_filter_config); DetectionFilterConfigFree(sc->detection_filter_config); FreePlugins(sc); PreprocessorRuleOptionsFree(sc->preproc_rule_options); OtnxMatchDataFree(sc->omd); if (sc->pcre_ovector != NULL) free(sc->pcre_ovector); if ( sc->event_queue_config ) EventQueueConfigFree(sc->event_queue_config); if ( sc->event_queue ) SnortEventqFree(sc->event_queue); if (sc->ip_proto_only_lists != NULL) { unsigned int j; for (j = 0; j < NUM_IP_PROTOS; j++) sflist_free_all(sc->ip_proto_only_lists[j], NULL); free(sc->ip_proto_only_lists); } sfPolicyFini(sc->policy_config); fpDeleteFastPacketDetection(sc); if (sc->rtn_hash_table) sfxhash_delete(sc->rtn_hash_table); for (i = 0; i < sc->num_policies_allocated; i++) { SnortPolicy *p = sc->targeted_policies[i]; if (p != NULL) free(p); if (sc->udp_ips_port_filter_list) { IpsPortFilter *ips_portfilter = sc->udp_ips_port_filter_list[i]; if (ips_portfilter) free(ips_portfilter); } } if (sc->udp_ips_port_filter_list) free (sc->udp_ips_port_filter_list); free(sc->targeted_policies); if ( sc->react_page ) free(sc->react_page); if ( sc->daq_type ) free(sc->daq_type); if ( sc->daq_mode ) free(sc->daq_mode); if ( sc->daq_vars ) StringVector_Delete(sc->daq_vars); if ( sc->daq_dirs ) StringVector_Delete(sc->daq_dirs); #ifdef ACTIVE_RESPONSE if ( sc->respond_device ) free(sc->respond_device); if (sc->eth_dst ) free(sc->eth_dst); #endif if (sc->gtp_ports) free(sc->gtp_ports); if(sc->cs_dir) free(sc->cs_dir); #ifdef REG_TEST if(sc->ha_out) free(sc->ha_out); if(sc->ha_in) free(sc->ha_in); if(sc->ha_pdts_in) free(sc->ha_pdts_in); #endif free_file_config(sc->file_config); #ifdef SIDE_CHANNEL if (sc->side_channel_config.opts) free(sc->side_channel_config.opts); #endif #ifdef DUMP_BUFFER if (sc->buffer_dump_file) StringVector_Delete(sc->buffer_dump_file); #endif #ifdef INTEL_SOFT_CPM IntelPmRelease(sc->ipm_handles); #endif #ifdef SNORT_RELOAD FreePreprocessorReloadData(sc); ReloadFreeAdjusters(sc); #endif FreeMandatoryEarlySessionCreators(sc->mandatoryESCreators); free(sc); #ifdef HAVE_MALLOC_TRIM malloc_trim(0); #endif } /**************************************************************************** * * Function: InitNetMasks() * * Purpose: Loads the netmask struct in network order. Yes, I know I could * just load the array when I define it, but this is what occurred * to me when I wrote this at 3:00 AM. * * Arguments: None. * * Returns: void function * ****************************************************************************/ static void InitNetmasks(void) { if (netmasks == NULL) netmasks = (uint32_t *)SnortAlloc(33 * sizeof(uint32_t)); netmasks[0] = 0x00000000; netmasks[1] = 0x80000000; netmasks[2] = 0xC0000000; netmasks[3] = 0xE0000000; netmasks[4] = 0xF0000000; netmasks[5] = 0xF8000000; netmasks[6] = 0xFC000000; netmasks[7] = 0xFE000000; netmasks[8] = 0xFF000000; netmasks[9] = 0xFF800000; netmasks[10] = 0xFFC00000; netmasks[11] = 0xFFE00000; netmasks[12] = 0xFFF00000; netmasks[13] = 0xFFF80000; netmasks[14] = 0xFFFC0000; netmasks[15] = 0xFFFE0000; netmasks[16] = 0xFFFF0000; netmasks[17] = 0xFFFF8000; netmasks[18] = 0xFFFFC000; netmasks[19] = 0xFFFFE000; netmasks[20] = 0xFFFFF000; netmasks[21] = 0xFFFFF800; netmasks[22] = 0xFFFFFC00; netmasks[23] = 0xFFFFFE00; netmasks[24] = 0xFFFFFF00; netmasks[25] = 0xFFFFFF80; netmasks[26] = 0xFFFFFFC0; netmasks[27] = 0xFFFFFFE0; netmasks[28] = 0xFFFFFFF0; netmasks[29] = 0xFFFFFFF8; netmasks[30] = 0xFFFFFFFC; netmasks[31] = 0xFFFFFFFE; netmasks[32] = 0xFFFFFFFF; } /**************************************************************************** * * Function: InitProtoNames() * * Purpose: Initializes the protocol names * * Arguments: None. * * Returns: void function * ****************************************************************************/ static void InitProtoNames(void) { int i; if (protocol_names == NULL) protocol_names = (char **)SnortAlloc(sizeof(char *) * NUM_IP_PROTOS); for (i = 0; i < NUM_IP_PROTOS; i++) { switch(i) { #ifdef REG_TEST #define PROTO_000 "IP" //Appears as HOPOPT on some systems #define PROTO_004 "IPENCAP" //Appears as IPV4 on some systems #define PROTO_255 "PROTO:255" //Appears as RESERVED on some systems case 0: protocol_names[i] = SnortStrdup(PROTO_000); break; case 4: protocol_names[i] = SnortStrdup(PROTO_004); break; case 255: protocol_names[i] = SnortStrdup(PROTO_255); break; #endif default: { struct protoent *pt = getprotobynumber(i); if (pt != NULL) { size_t j; protocol_names[i] = SnortStrdup(pt->p_name); for (j = 0; j < strlen(protocol_names[i]); j++) protocol_names[i][j] = toupper(protocol_names[i][j]); } else { char protoname[10]; SnortSnprintf(protoname, sizeof(protoname), "PROTO:%03d", i); protocol_names[i] = SnortStrdup(protoname); } } } } } static void SetSnortConfDir(void) { /* extract the config directory from the config filename */ if (snort_conf_file != NULL) { #ifndef WIN32 char *path_sep = strrchr(snort_conf_file, '/'); #else char *path_sep = strrchr(snort_conf_file, '\\'); #endif /* is there a directory seperator in the filename */ if (path_sep != NULL) { path_sep++; /* include path separator */ snort_conf_dir = SnortStrndup(snort_conf_file, path_sep - snort_conf_file); } else { snort_conf_dir = SnortStrdup("./"); } DEBUG_WRAP(DebugMessage(DEBUG_INIT, "Config file = %s, config dir = " "%s\n", snort_conf_file, snort_conf_dir);); } } static void FreePlugins(SnortConfig *sc) { if (sc == NULL) return; FreePreprocessors(sc); FreePluginPostConfigFuncs(sc->plugin_post_config_funcs); sc->plugin_post_config_funcs = NULL; } static void FreePreprocessors(SnortConfig *sc) { tSfPolicyId i; if (sc == NULL) return; FreePreprocCheckConfigFuncs(sc->preproc_config_check_funcs); sc->preproc_config_check_funcs = NULL; for (i = 0; i < sc->num_policies_allocated; i++) { SnortPolicy *p = sc->targeted_policies[i]; if (p == NULL) continue; FreePreprocEvalFuncs(p->preproc_eval_funcs); p->preproc_eval_funcs = NULL; p->num_preprocs = 0; FreePreprocEvalFuncs(p->unused_preproc_eval_funcs); p->unused_preproc_eval_funcs = NULL; FreePreprocMetaEvalFuncs(p->preproc_meta_eval_funcs); p->preproc_meta_eval_funcs = NULL; p->num_meta_preprocs = 0; FreeDetectionEvalFuncs(p->detect_eval_funcs); p->detect_eval_funcs = NULL; p->num_detects = 0; } FreePreprocPostConfigFuncs(sc->preproc_post_config_funcs); sc->preproc_post_config_funcs = NULL; } SnortConfig * MergeSnortConfs(SnortConfig *cmd_line, SnortConfig *config_file) { unsigned int i; /* Move everything from the command line config over to the * config_file config */ if (cmd_line == NULL) { FatalError("%s(%d) Merging snort configs: snort conf is NULL.\n", __FILE__, __LINE__); } ResolveOutputPlugins(cmd_line, config_file); if (config_file == NULL) { if (cmd_line->log_dir == NULL) cmd_line->log_dir = SnortStrdup(DEFAULT_LOG_DIR); } else if ((cmd_line->log_dir == NULL) && (config_file->log_dir == NULL)) { config_file->log_dir = SnortStrdup(DEFAULT_LOG_DIR); } else if (cmd_line->log_dir != NULL) { if (config_file->log_dir != NULL) free(config_file->log_dir); config_file->log_dir = SnortStrdup(cmd_line->log_dir); } if (config_file == NULL) return cmd_line; /* Used because of a potential chroot */ config_file->orig_log_dir = SnortStrdup(config_file->log_dir); config_file->run_mode = cmd_line->run_mode; config_file->run_mode_flags |= cmd_line->run_mode_flags; if ((cmd_line->run_mode == RUN_MODE__TEST) && (config_file->run_flags & RUN_FLAG__DAEMON)) { /* Just ignore deamon setting in conf file */ config_file->run_flags &= ~RUN_FLAG__DAEMON; } config_file->run_flags |= cmd_line->run_flags; config_file->output_flags |= cmd_line->output_flags; config_file->logging_flags |= cmd_line->logging_flags; config_file->internal_log_level = cmd_line->internal_log_level; config_file->suppress_config_log = cmd_line->suppress_config_log; /* Merge checksum flags. If command line modified them, use from the * command line, else just use from config_file. */ for (i = 0; i < config_file->num_policies_allocated; i++) { if (config_file->targeted_policies[i] != NULL) { if (cmd_line->checksum_flags_modified) config_file->targeted_policies[i]->checksum_flags = cmd_line->checksum_flags; if (cmd_line->checksum_drop_flags_modified) config_file->targeted_policies[i]->checksum_drop_flags = cmd_line->checksum_drop_flags; } } config_file->event_log_id = cmd_line->event_log_id; if (cmd_line->dynamic_rules_path != NULL) { if(strcmp(cmd_line->dynamic_rules_path, "") != 0) { if( config_file->dynamic_rules_path != NULL ) free(config_file->dynamic_rules_path); config_file->dynamic_rules_path = SnortStrdup(cmd_line->dynamic_rules_path); } } if (cmd_line->dyn_engines != NULL) { FreeDynamicLibInfo(config_file->dyn_engines); config_file->dyn_engines = DupDynamicLibInfo(cmd_line->dyn_engines); } if (cmd_line->dyn_rules != NULL) { FreeDynamicLibInfo(config_file->dyn_rules); config_file->dyn_rules = DupDynamicLibInfo(cmd_line->dyn_rules); } if (cmd_line->dyn_preprocs != NULL) { FreeDynamicLibInfo(config_file->dyn_preprocs); config_file->dyn_preprocs = DupDynamicLibInfo(cmd_line->dyn_preprocs); } if (cmd_line->pid_path[0] != '\0') ConfigPidPath(config_file, cmd_line->pid_path); config_file->exit_check = cmd_line->exit_check; /* Command line only configures search method */ if (cmd_line->fast_pattern_config != NULL) config_file->fast_pattern_config->search_method = cmd_line->fast_pattern_config->search_method; if (sfip_is_set(&cmd_line->obfuscation_net)) memcpy(&config_file->obfuscation_net, &cmd_line->obfuscation_net, sizeof(sfcidr_t)); if (sfip_is_set(&cmd_line->homenet)) memcpy(&config_file->homenet, &cmd_line->homenet, sizeof(sfcidr_t)); if (cmd_line->interface != NULL) { if (config_file->interface != NULL) free(config_file->interface); config_file->interface = SnortStrdup(cmd_line->interface); } if (cmd_line->bpf_file != NULL) { if (config_file->bpf_file != NULL) free(config_file->bpf_file); config_file->bpf_file = SnortStrdup(cmd_line->bpf_file); } if (cmd_line->bpf_filter != NULL) config_file->bpf_filter = SnortStrdup(cmd_line->bpf_filter); if (cmd_line->pkt_snaplen != -1) config_file->pkt_snaplen = cmd_line->pkt_snaplen; if (cmd_line->pkt_cnt != 0) config_file->pkt_cnt = cmd_line->pkt_cnt; #ifdef REG_TEST if (cmd_line->pkt_skip != 0) config_file->pkt_skip = cmd_line->pkt_skip; #endif if (cmd_line->group_id != -1) config_file->group_id = cmd_line->group_id; if (cmd_line->user_id != -1) config_file->user_id = cmd_line->user_id; /* Only configurable on command line */ if (cmd_line->pcap_log_file != NULL) config_file->pcap_log_file = SnortStrdup(cmd_line->pcap_log_file); if (cmd_line->file_mask != 0) config_file->file_mask = cmd_line->file_mask; if (cmd_line->pidfile_suffix[0] != '\0') { SnortStrncpy(config_file->pidfile_suffix, cmd_line->pidfile_suffix, sizeof(config_file->pidfile_suffix)); } if (cmd_line->chroot_dir != NULL) { if (config_file->chroot_dir != NULL) free(config_file->chroot_dir); config_file->chroot_dir = SnortStrdup(cmd_line->chroot_dir); } if (cmd_line->perf_file != NULL) { if (config_file->perf_file != NULL) free(config_file->perf_file); config_file->perf_file = SnortStrdup(cmd_line->perf_file); } if (cmd_line->memdump_file != NULL) { if (config_file->memdump_file != NULL) free(config_file->memdump_file); config_file->memdump_file = SnortStrdup(cmd_line->memdump_file); } if ( cmd_line->daq_type ) config_file->daq_type = SnortStrdup(cmd_line->daq_type); if ( cmd_line->daq_mode ) config_file->daq_mode = SnortStrdup(cmd_line->daq_mode); if ( cmd_line->dirty_pig ) config_file->dirty_pig = cmd_line->dirty_pig; if ( cmd_line->daq_vars ) { /* Command line overwrites daq_vars */ if (config_file->daq_vars) StringVector_Delete(config_file->daq_vars); config_file->daq_vars = StringVector_New(); StringVector_AddVector(config_file->daq_vars, cmd_line->daq_vars); } if ( cmd_line->daq_dirs ) { /* Command line overwrites daq_dirs */ if (config_file->daq_dirs) StringVector_Delete(config_file->daq_dirs); config_file->daq_dirs = StringVector_New(); StringVector_AddVector(config_file->daq_dirs, cmd_line->daq_dirs); } #ifdef MPLS if (cmd_line->mpls_stack_depth != DEFAULT_LABELCHAIN_LENGTH) config_file->mpls_stack_depth = cmd_line->mpls_stack_depth; /* Set MPLS payload type here if it hasn't been defined */ if ((cmd_line->mpls_payload_type == 0) && (config_file->mpls_payload_type == 0)) { config_file->mpls_payload_type = DEFAULT_MPLS_PAYLOADTYPE; } else if (cmd_line->mpls_payload_type != 0) { config_file->mpls_payload_type = cmd_line->mpls_payload_type; } #endif if (cmd_line->run_flags & RUN_FLAG__PROCESS_ALL_EVENTS) config_file->event_queue_config->process_all_events = 1; if (cmd_line->cs_dir != NULL) { if (config_file->cs_dir != NULL) free(config_file->cs_dir); config_file->cs_dir = SnortStrdup(cmd_line->cs_dir); } if (config_file->cs_dir) { #ifndef WIN32 /* * If an absolute path is specified, then use that. * otherwise, relative to pid path */ if ((config_file->cs_dir[0] != '/') && config_file->pid_path[0]) { char fullpath[PATH_MAX]; if (config_file->pid_path[strlen(config_file->pid_path) - 1] == '/') { SnortSnprintf(fullpath, sizeof(fullpath), "%s%s", config_file->pid_path, config_file->cs_dir); } else { SnortSnprintf(fullpath, sizeof(fullpath), "%s/%s", config_file->pid_path, config_file->cs_dir); } free (config_file->cs_dir); config_file->cs_dir = SnortStrdup(fullpath); } #else /*Not supported in WINDOWS*/ free (config_file->cs_dir); config_file->cs_dir = NULL; #endif ControlSocketConfigureDirectory(config_file->cs_dir); } #ifdef REG_TEST config_file->ha_peer = cmd_line->ha_peer; if ( cmd_line->ha_out ) { if(config_file->ha_out != NULL) free(config_file->ha_out); config_file->ha_out = strdup(cmd_line->ha_out); } if ( cmd_line->ha_in ) { if(config_file->ha_in != NULL) free(config_file->ha_in); config_file->ha_in = strdup(cmd_line->ha_in); } if ( cmd_line->ha_pdts_in ) { if(config_file->ha_pdts_in != NULL) free(config_file->ha_pdts_in); config_file->ha_pdts_in = strdup(cmd_line->ha_pdts_in); } #endif #ifdef DUMP_BUFFER /* Command line overwrites daq_dirs */ if (config_file->buffer_dump_file) StringVector_Delete(config_file->buffer_dump_file); config_file->buffer_dump_file = StringVector_New(); StringVector_AddVector(config_file->buffer_dump_file, cmd_line->buffer_dump_file); #endif return config_file; } static void FreeDynamicLibInfos(SnortConfig *sc) { if (sc == NULL) return; if (sc->dyn_engines != NULL) { FreeDynamicLibInfo(sc->dyn_engines); sc->dyn_engines = NULL; } if (sc->dyn_rules != NULL) { FreeDynamicLibInfo(sc->dyn_rules); sc->dyn_rules = NULL; } if (sc->dyn_preprocs != NULL) { FreeDynamicLibInfo(sc->dyn_preprocs); sc->dyn_preprocs = NULL; } #ifdef SIDE_CHANNEL if (sc->dyn_side_channels != NULL) { FreeDynamicLibInfo(sc->dyn_side_channels); sc->dyn_side_channels = NULL; } #endif } static void FreeDynamicLibInfo(DynamicLibInfo *lib_info) { unsigned i; if (lib_info == NULL) return; for (i = 0; i < lib_info->count; i++) { free(lib_info->lib_paths[i]->path); free(lib_info->lib_paths[i]); } free(lib_info); } static DynamicLibInfo * DupDynamicLibInfo(DynamicLibInfo *src) { DynamicLibInfo *dst; unsigned i; if (src == NULL) return NULL; dst = (DynamicLibInfo *)SnortAlloc(sizeof(DynamicLibInfo)); dst->type = src->type; dst->count = src->count; for (i = 0; i < src->count; i++) { DynamicLibPath *dylib_path = (DynamicLibPath *)SnortAlloc(sizeof(DynamicLibPath)); dylib_path->ptype = src->lib_paths[i]->ptype; dylib_path->path = SnortStrdup(src->lib_paths[i]->path); dst->lib_paths[i] = dylib_path; } return dst; } void FreeVarList(VarNode *head) { while (head != NULL) { VarNode *tmp = head; head = head->next; if (tmp->name != NULL) free(tmp->name); if (tmp->value != NULL) free(tmp->value); if (tmp->line != NULL) free(tmp->line); free(tmp); } } void SnortInit(int argc, char **argv) { #ifdef WIN32 char dllSearchPath[PATH_MAX]; #endif InitSignals(); #if defined(NOCOREFILE) && !defined(WIN32) SetNoCores(); #else StoreSnortInfoStrings(); #endif #ifdef WIN32 if(GetSystemDirectory(dllSearchPath, PATH_MAX)) { LogMessage("System directory is: %s\n", dllSearchPath); if (!SetDllDirectory(dllSearchPath)) FatalError("Failed to set Windows DLL search path.\n"); } else FatalError("Could not find the Windows System directory.\n"); if (!init_winsock()) // TBD moves to windows daq FatalError("Could not Initialize Winsock!\n"); #endif InitGlobals(); /* chew up the command line */ ParseCmdLine(argc, argv); switch (snort_conf->run_mode) { case RUN_MODE__VERSION: break; case RUN_MODE__RULE_DUMP: LogMessage("Running in Rule Dump mode\n"); break; case RUN_MODE__IDS: LogMessage("Running in IDS mode\n"); break; case RUN_MODE__TEST: LogMessage("Running in Test mode\n"); break; case RUN_MODE__PACKET_LOG: LogMessage("Running in packet logging mode\n"); break; case RUN_MODE__PACKET_DUMP: LogMessage("Running in packet dump mode\n"); break; default: break; } if (ScSuppressConfigLog() || ScVersionMode()) ScSetInternalLogLevel(INTERNAL_LOG_LEVEL__ERROR); LogMessage("\n"); LogMessage(" --== Initializing Snort ==--\n"); if (SnortStrnlen(signal_error_msg, STD_BUF)> 0) { ErrorMessage("%s", signal_error_msg); } if (!ScVersionMode()) { /* Every run mode except version will potentially need output * If output plugins should become dynamic, this needs to move */ RegisterOutputPlugins(); #ifdef DEBUG DumpOutputPlugins(); #endif } init_fileAPI(); initMemoryStatsApi(); /* if we're using the rules system, it gets initialized here */ if (snort_conf_file != NULL) { SnortConfig *sc; /* initialize all the plugin modules */ RegisterPreprocessors(); RegisterRuleOptions(); InitTag(); #ifdef DEBUG DumpPreprocessors(); DumpRuleOptions(); #endif #ifdef PERF_PROFILING /* Register the main high level perf stats */ RegisterPreprocessorProfile("detect", &detectPerfStats, 0, &totalPerfStats, NULL); RegisterPreprocessorProfile("mpse", &mpsePerfStats, 1, &detectPerfStats, NULL); RegisterPreprocessorProfile("rule eval", &rulePerfStats, 1, &detectPerfStats, NULL); RegisterPreprocessorProfile("rtn eval", &ruleRTNEvalPerfStats, 2, &rulePerfStats, NULL); RegisterPreprocessorProfile("rule tree eval", &ruleOTNEvalPerfStats, 2, &rulePerfStats, NULL); RegisterPreprocessorProfile("preproc_rule_options", &preprocRuleOptionPerfStats, 3, &ruleOTNEvalPerfStats, NULL); RegisterPreprocessorProfile("decode", &decodePerfStats, 0, &totalPerfStats, NULL); RegisterPreprocessorProfile("eventq", &eventqPerfStats, 0, &totalPerfStats, NULL); RegisterPreprocessorProfile("total", &totalPerfStats, 0, NULL, NULL); RegisterPreprocessorProfile("daq meta", &metaPerfStats, 0, NULL, NULL); (void)PerfIndicator_RegisterPreprocStat( &totalPerfStats, Perf_Indicator_Type_Packet_Latency ); #endif LogMessage("Parsing Rules file \"%s\"\n", snort_conf_file); sc = ParseSnortConf(); /* Merge the command line and config file confs to take care of * command line overriding config file. * Set the global snort_conf that will be used during run time */ snort_conf = MergeSnortConfs(snort_cmd_line_conf, sc); InitSynToMulticastDstIp(snort_conf); InitMulticastReservedIp(snort_conf); #ifdef TARGET_BASED /* Parse attribute table stuff here since config max_attribute_hosts * is apart from attribute table configuration. * Only attribute table in default policy is processed. Attribute table in * other policies indicates that attribute table in default table should * be used. Filenames for attribute_table should be same across all policies. */ { tSfPolicyId defaultPolicyId = sfGetDefaultPolicy(snort_conf->policy_config); TargetBasedConfig *tbc = &snort_conf->targeted_policies[defaultPolicyId]->target_based_config; if (tbc->args != NULL) { char *saved_file_name = file_name; int saved_file_line = file_line; file_name = tbc->file_name; file_line = tbc->file_line; SFAT_ParseAttributeTable(tbc->args, snort_conf); #ifndef WIN32 if (!ScDisableAttrReload(snort_conf)) { /* Register signal handler for attribute table. */ SnortAddSignal(SIGNAL_SNORT_READ_ATTR_TBL,SigAttributeTableReloadHandler,0); if(errno != 0) errno = 0; } #endif file_name = saved_file_name; file_line = saved_file_line; } } #endif if (snort_conf->asn1_mem != 0) asn1_init_mem(snort_conf->asn1_mem); else asn1_init_mem(256); if (snort_conf->alert_file != NULL) { char *tmp = snort_conf->alert_file; snort_conf->alert_file = ProcessFileOption(snort_conf, snort_conf->alert_file); free(tmp); } #ifdef PERF_PROFILING /* Parse profiling here because of file option and potential * dependence on log directory */ { char *opts = NULL; int in_table; in_table = sfghash_find2(snort_conf->config_table, CONFIG_OPT__PROFILE_PREPROCS, (void *)&opts); if (in_table) ConfigProfilePreprocs(snort_conf, opts); in_table = sfghash_find2(snort_conf->config_table, CONFIG_OPT__PROFILE_RULES, (void *)&opts); if (in_table) ConfigProfileRules(snort_conf, opts); } #endif if (ScAlertBeforePass()) { OrderRuleLists(snort_conf, "activation dynamic drop sdrop reject alert pass log"); } LogMessage("Tagged Packet Limit: %ld\n", snort_conf->tagged_packet_limit); /* Handles Fatal Errors itself. */ SnortEventqNew(snort_conf->event_queue_config, snort_conf->event_queue); } else if (ScPacketLogMode() || ScPacketDumpMode()) { /* Make sure there is a log directory */ /* This will return the cmd line conf and resolve the output * configuration */ SnortConfig* sc = ParseSnortConf(); snort_conf = MergeSnortConfs(snort_cmd_line_conf, sc); InitTag(); SnortEventqNew(snort_conf->event_queue_config, snort_conf->event_queue); } /* Allocate an array for IP6 extensions for the main Packet struct */ // Make sure this memory is freed on exit. s_packet.ip6_extensions = SnortAlloc(sizeof(*s_packet.ip6_extensions) * ScMaxIP6Extensions()); /* Finish up the pcap list and put in the queues */ PQ_SetUp(); if ((snort_conf->bpf_filter == NULL) && (snort_conf->bpf_file != NULL)) { LogMessage("Reading filter from bpf file: %s\n", snort_conf->bpf_file); snort_conf->bpf_filter = read_infile(snort_conf->bpf_file); } if (snort_conf->bpf_filter != NULL) LogMessage("Snort BPF option: %s\n", snort_conf->bpf_filter); LoadDynamicPlugins(snort_conf); /* Display snort version information here so that we can also show dynamic * plugin versions, if loaded. */ if (ScVersionMode()) { ScRestoreInternalLogLevel(); PrintVersion(snort_conf); CleanExit(0); } /* Validate the log directory for logging packets - probably should * add test mode as well, but not expected behavior */ if ( !(ScNoLog() && ScNoAlert()) ) { if (ScPacketLogMode()) CheckLogDir(); LogMessage("Log directory = %s\n", snort_conf->log_dir); } if (ScOutputUseUtc()) snort_conf->thiszone = 0; else snort_conf->thiszone = gmt2local(0); /* ripped from tcpdump */ ConfigureOutputPlugins(snort_conf); /* Have to split up configuring preprocessors between internal and dynamic * because the dpd structure has a pointer to the stream api and stream5 * needs to be configured first to set this */ ConfigurePreprocessors(snort_conf, 0); InitDynamicEngines(snort_conf->dynamic_rules_path); if (ScRuleDumpMode()) { if( snort_conf->dynamic_rules_path == NULL ) { FatalError("%s(%d) Please specify the directory path for dumping the dynamic rules \n", __FILE__, __LINE__); } DumpDetectionLibRules(snort_conf); CleanExit(0); } /* This will load each dynamic preprocessor module specified and set * the _dpd structure for each */ InitDynamicPreprocessors(); /* Now configure the dynamic preprocessors since the dpd structure * should be filled in and have the correct values */ ConfigurePreprocessors(snort_conf, 1); ParseRules(snort_conf); RuleOptParseCleanup(); InitDynamicDetectionPlugins(snort_conf); EventTrace_Init(); if (ScIdsMode() || ScTestMode()) { detection_filter_print_config(snort_conf->detection_filter_config); RateFilter_PrintConfig(snort_conf->rate_filter_config); print_thresholding(snort_conf->threshold_config, 0); PrintRuleOrder(snort_conf->rule_lists); /* Check rule state lists, enable/disabled * and err on 'special' GID without OTN. */ /* * Modified toi use sigInfo.shared in otn instead of the GENERATOR ID - man */ SetRuleStates(snort_conf); /* Verify the preprocessors are configured properly */ if (CheckPreprocessorsConfig(snort_conf)) { SnortFatalExit(); } /* Remove disabled preprocessors if policies are disabled */ FilterConfigPreprocessors(snort_conf); /* Need to do this after dynamic detection stuff is initialized, too */ FlowBitsVerify(); } snort_conf->udp_ips_port_filter_list = ParseIpsPortList(snort_conf, IPPROTO_UDP); if (snort_conf->file_mask != 0) umask(snort_conf->file_mask); else umask(077); /* set default to be sane */ // the following was moved from unpriv init; hopefully it can live here. decoderActionQ = sfActionQueueInit(snort_conf->event_queue_config->max_events*2); if (mempool_init(&decoderAlertMemPool, snort_conf->event_queue_config->max_events*2, sizeof(EventNode)) != 0) { FatalError("%s(%d) Could not initialize decoder action queue memory pool.\n", __FILE__, __LINE__); } fpCreateFastPacketDetection(snort_conf); #ifdef INTEL_SOFT_CPM if (snort_conf->fast_pattern_config->search_method == MPSE_INTEL_CPM) IntelPmActivate(snort_conf); #endif #ifdef PPM_MGR PPM_PRINT_CFG(&snort_conf->ppm_cfg); #endif #if defined(DAQ_VERSION) && DAQ_VERSION > 9 // This is needed when PPM is disabled and enabling snort-engine debugs if (!ppm_tpu) ppm_tpu = (PPM_TICKS)get_ticks_per_usec(); #endif #ifdef SIDE_CHANNEL RegisterSideChannelModules(); InitDynamicSideChannelPlugins(); ConfigureSideChannelModules(snort_conf); SideChannelConfigure(snort_conf); SideChannelInit(); #ifndef REG_TEST if (snort_conf && snort_conf->file_config) check_sidechannel_enabled(snort_conf->file_config); #endif #endif FileServiceInstall(); // If we suppressed output at the beginning of SnortInit(), // then restore it now. ScRestoreInternalLogLevel(); } #if defined(INLINE_FAILOPEN) && !defined(WIN32) static void * SnortPostInitThread(void *data) { sigset_t mtmask; inline_failopen_thread_pid = gettid(); inline_failopen_thread_running = 1; /* Don't handle any signals here */ sigfillset(&mtmask); pthread_sigmask(SIG_BLOCK, &mtmask, NULL); while (!inline_failopen_initialized) nanosleep(&thread_sleep, NULL); SnortUnprivilegedInit(); pthread_exit((void *)NULL); } static DAQ_Verdict IgnoreCallback ( void *user, const DAQ_PktHdr_t* pkthdr, const uint8_t* pkt) { /* Empty function -- do nothing with the packet we just read */ inline_failopen_pass_pkt_cnt++; #ifdef DEBUG { FILE *tmp = fopen("/var/tmp/fo_threadid", "a"); if ( tmp ) { fprintf(tmp, "Packet Count %d\n", inline_failopen_pass_pkt_cnt); fclose(tmp); } } #endif return DAQ_VERDICT_PASS; } #endif /* defined(INLINE_FAILOPEN) && !defined(WIN32) */ // this function should only include initialization that must be done as a // non-root user such as creating log files. other initialization stuff should // be in the main initialization function since, depending on platform and // configuration, this may be running in a background thread while passing // packets in a fail open mode in the main thread. we don't want big delays // here to cause excess latency or dropped packets in that thread which may // be the case if all threads are pinned to a single cpu/core. // // clarification: once snort opens/starts the DAQ, packets are queued for snort // and must be disposed of quickly or the queue will overflow and packets will // be dropped so the fail open thread does the remaining initialization while // the main thread passes packets. prior to opening and starting the DAQ, // packet passing is done by the driver/hardware. the goal then is to put as // much initialization stuff in SnortInit() as possible and to restrict this // function to those things that depend on DAQ startup or non-root user/group. static void SnortUnprivilegedInit(void) { #ifndef REG_TEST struct rusage ru; #endif #ifdef ACTIVE_RESPONSE // this depends on instantiated daq capabilities // so it is done here instead of SnortInit() Active_Init(snort_conf); #endif InitPidChrootAndPrivs(snort_main_thread_pid); #if defined(HAVE_LINUXTHREADS) && !defined(WIN32) // this must be done after dropping privs for linux threads // to ensure that child threads can communicate with parent SnortStartThreads(); #endif // perfmon, for one, opens a log file for writing here PostConfigPreprocessors(snort_conf); // log_tcpdump opens a log file for writing here; also ... // note that things like opening log_tcpdump will fail here if the // user specified -u (we dropped privileges) and the log defaults // to /var/log/snort. in this case they must override log path. PostConfigInitPlugins(snort_conf, snort_conf->plugin_post_config_funcs); #ifdef SIDE_CHANNEL SideChannelPostInit(); #endif LogMessage("\n"); LogMessage(" --== Initialization Complete ==--\n"); /* Tell 'em who wrote it, and what "it" is */ PrintVersion(snort_conf); if (ScTestMode()) { #ifndef REG_TEST LogMessage("\n"); getrusage(RUSAGE_SELF, &ru); LogMessage("Total snort Fixed Memory Cost - MaxRss:%li", ru.ru_maxrss); #endif LogMessage("\n"); LogMessage("Snort successfully validated the configuration!\n"); CleanExit(0); } LogMessage("Commencing packet processing (pid=%u)\n", snort_main_thread_pid); snort_initializing = false; } #if defined(NOCOREFILE) && !defined(WIN32) static void SetNoCores(void) { struct rlimit rlim; getrlimit(RLIMIT_CORE, &rlim); rlim.rlim_max = 0; setrlimit(RLIMIT_CORE, &rlim); } #endif /* Add a signal handler * * If check needed, also check whether previous signal_handler is neither SIG_IGN nor SIG_DFL * * Return: * 0: error * 1: success */ int SnortAddSignal(int sig, sighandler_t signal_handler, int check_needed) { sighandler_t pre_handler; #ifdef HAVE_SIGACTION struct sigaction action; struct sigaction old_action; sigemptyset(&action.sa_mask); action.sa_flags = 0; action.sa_handler = signal_handler; sigaction(sig, &action, &old_action); pre_handler = old_action.sa_handler; #else pre_handler = signal(sig, signal_handler); #endif if (SIG_ERR == pre_handler) { SnortSnprintfAppend(signal_error_msg, STD_BUF, "Could not add handler for signal %d \n", sig); return 0; } else if (check_needed && (SIG_IGN != pre_handler) && (SIG_DFL!= pre_handler)) { SnortSnprintfAppend(signal_error_msg, STD_BUF, "WARNING: Handler is already installed for signal %d.\n", sig); } return 1; } static void InitSignals(void) { #ifndef WIN32 # if defined(LINUX) || defined(FREEBSD) || defined(OPENBSD) || \ defined(SOLARIS) || defined(BSD) || defined(MACOS) sigset_t set; sigemptyset(&set); # if defined(INLINE_FAILOPEN) || \ defined(TARGET_BASED) || defined(SNORT_RELOAD) pthread_sigmask(SIG_SETMASK, &set, NULL); # else sigprocmask(SIG_SETMASK, &set, NULL); # endif /* INLINE_FAILOPEN */ # else sigsetmask(0); # endif /* LINUX, BSD, SOLARIS */ #endif /* !WIN32 */ /* Make this prog behave nicely when signals come along. * Windows doesn't like all of these signals, and will * set errno for some. Ignore/reset this error so it * doesn't interfere with later checks of errno value. */ signal_error_msg[0] = '\0'; SnortAddSignal(SIGTERM, SigExitHandler, 1); SnortAddSignal(SIGINT, SigExitHandler, 1); #ifndef WIN32 SnortAddSignal(SIGQUIT, SigExitHandler, 1); SnortAddSignal(SIGNAL_SNORT_DUMP_STATS, SigDumpStatsHandler, 1); SnortAddSignal(SIGNAL_SNORT_RELOAD, SigReloadHandler, 1); SnortAddSignal(SIGNAL_SNORT_ROTATE_STATS, SigRotateStatsHandler, 1); #endif #ifdef CONTROL_SOCKET SnortAddSignal(SIGPIPE, SigPipeHandler, 1); #endif #ifdef TARGET_BASED #ifndef WIN32 /* Used to print warning if attribute table is not configured * When it is, it will set new signal handler */ SnortAddSignal(SIGNAL_SNORT_READ_ATTR_TBL, SigNoAttributeTableHandler, 1); #endif #endif SnortAddSignal(SIGABRT, SigOopsHandler, 1); SnortAddSignal(SIGSEGV, SigOopsHandler, 1); #ifndef WIN32 SnortAddSignal(SIGBUS, SigOopsHandler, 1); #endif errno = 0; } static void FreeOutputConfigs(OutputConfig *head) { while (head != NULL) { OutputConfig *tmp = head; head = head->next; if (tmp->keyword != NULL) free(tmp->keyword); if (tmp->opts != NULL) free(tmp->opts); if (tmp->file_name != NULL) free(tmp->file_name); /* Don't free listhead as it's just a pointer to the user defined * rule's rule list node's list head */ free(tmp); } } #ifdef SIDE_CHANNEL static void FreeSideChannelModuleConfigs(SideChannelModuleConfig *head) { while (head != NULL) { SideChannelModuleConfig *tmp = head; head = head->next; if (tmp->keyword != NULL) free(tmp->keyword); if (tmp->opts != NULL) free(tmp->opts); if (tmp->file_name != NULL) free(tmp->file_name); free(tmp); } } #endif static void FreePreprocConfigs(SnortConfig *sc) { tSfPolicyId i; if (sc == NULL) return; for (i = 0; i < sc->num_policies_allocated; i++) { SnortPolicy *p = sc->targeted_policies[i]; PreprocConfig *head; if (p == NULL) continue; head = p->preproc_configs; while (head != NULL) { PreprocConfig *tmp = head; head = head->next; if (tmp->keyword != NULL) free(tmp->keyword); if (tmp->opts != NULL) free(tmp->opts); if (tmp->file_name != NULL) free(tmp->file_name); free(tmp); } } } static void FreeRuleStateList(RuleState *head) { while (head != NULL) { RuleState *tmp = head; head = head->next; free(tmp); } } static void FreeClassifications(ClassType *head) { while (head != NULL) { ClassType *tmp = head; head = head->next; if (tmp->name != NULL) free(tmp->name); if (tmp->type != NULL) free(tmp->type); free(tmp); } } static void FreeReferences(ReferenceSystemNode *head) { while (head != NULL) { ReferenceSystemNode *tmp = head; head = head->next; if (tmp->name != NULL) free(tmp->name); if (tmp->url != NULL) free(tmp->url); free(tmp); } } #if defined(DAQ_VERSION) && DAQ_VERSION > 9 void print_pktverdict (Packet *p,uint64_t verdict) { static const char* pktverdict[7] = { "ALLOW", "BLOCK", "REPLACE", "ALLOWFLOW", "BLOCKFLOW", "IGNORE", "RETRY" }; uint8_t log_level = (verdict == DAQ_VERDICT_BLOCK || verdict == DAQ_VERDICT_BLACKLIST)?DAQ_DEBUG_PKT_LEVEL_INFO:DAQ_DEBUG_PKT_LEVEL_DEBUG; DEBUG_SNORT_ENGINE(p,log_level,"Packet Verdict:%s\n",pktverdict[verdict]); } void print_flow(Packet *p,char *str,uint32_t id,uint64_t start,uint64_t end) { static const char* preproc[50] = { "PP_BO", "PP_APP_ID", "PP_DNS", "PP_FRAG", "PP_FTPTELNET", "PP_HTTPINSPECT", "PP_PERFMONITOR", "PP_RPCDECODE", "PP_SHARED_RULES", "PP_SFPORTSCAN", "PP_SMTP", "PP_SSH", "PP_SSL", "PP_STREAM", "PP_TELNET", "PP_ARPSPOOF", "PP_DCE", "PP_SDF", "PP_NORMALIZE", "PP_ISAKMP", "PP_SESSION", "PP_SIP", "PP_POP", "PP_IMAP", "PP_NETWORK_DISCOVERY", "PP_FW_RULE_ENGINE", "PP_REPUTATION", "PP_GTP", "PP_MODBUS", "PP_DNP ", "PP_FILE", "PP_FILE_INSPECT", "PP_NAP_RULE_ENGINE", "PP_REFILTER_RULE_ENGINE", "PP_HTTPMOD", "PP_HTTP ", "PP_CIP", "PP_MAX" }; const char* preproc_info = (str == NULL)?preproc[id]:str; uint64_t diff =0; if (ppm_tpu) { diff = (end-start)/ppm_tpu; } DEBUG_SNORT_ENGINE(p,DAQ_DEBUG_PKT_LEVEL_DEBUG,"%s processing time %u usec\n",preproc_info,diff); } #endif snort-2.9.20/src/reload.h0000644000175000017500000000775714241077163013324 0ustar apoapo/* ** ** reload.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef __RELOAD_H__ #define __RELOAD_H__ #include #include "snort.h" #include "util.h" #include "reload_api.h" #include "sf_types.h" void CheckForReload(void); void ReloadControlSocketRegister(void); bool SnortDynamicLibsChanged(void); #if defined(SNORT_RELOAD) && !defined(WIN32) void * ReloadConfigThread(void *); #endif #if defined(SNORT_RELOAD) typedef struct _ReloadAdjustEntry { struct _ReloadAdjustEntry* raNext; const char* raName; tSfPolicyId raPolicyId; ReloadAdjustFunc raFunc; void* raUserData; ReloadAdjustUserFreeFunc raUserFreeFunc; } ReloadAdjustEntry; int ReloadAdjustRegister(SnortConfig* sc, const char* raName, tSfPolicyId raPolicyId, ReloadAdjustFunc raFunc, void* raUserData, ReloadAdjustUserFreeFunc raUserFreeFunc); int ReloadAdjustSessionRegister(SnortConfig* sc, const char* raName, tSfPolicyId raPolicyId, ReloadAdjustFunc raFunc, void* raUserData, ReloadAdjustUserFreeFunc raUserFreeFunc); void ReloadFreeAdjusters(SnortConfig* sc); static inline void ReloadAdjust(bool idle, time_t tv_sec) { ReloadAdjustEntry* rae = snort_conf->raEntry; if (rae) { static unsigned num_idle_callbacks = 0; static unsigned num_packet_callbacks = 0; static struct timeval adjust_started_at; if (rae != snort_conf->raCurrentEntry) { snort_conf->raCurrentEntry = rae; snort_conf->raLastLog = tv_sec; memset(&adjust_started_at, 0 , sizeof(adjust_started_at)); gettimeofday(&adjust_started_at, NULL); LogMessage("Adjusting %s during reload ( Started at : (%"PRIu64") milliseconds.\n", rae->raName, (uint64_t)(adjust_started_at.tv_sec * 1000 + adjust_started_at.tv_usec / 1000)); } if (idle) num_idle_callbacks++; else num_packet_callbacks++; if (rae->raFunc(idle, rae->raPolicyId, rae->raUserData)) { struct timeval curr_time; gettimeofday(&curr_time, NULL); LogMessage("Finished adjusting memory for %s : Total calls (packet loop=%u, idle loop=%u), Elapsed time = (%"PRIu64") milliseconds.\n", rae->raName, num_packet_callbacks, num_idle_callbacks, (uint64_t)((curr_time.tv_sec * 1000 + curr_time.tv_usec / 1000) - (adjust_started_at.tv_sec * 1000 + adjust_started_at.tv_usec / 1000)) ); num_idle_callbacks = 0; num_packet_callbacks = 0; memset(&adjust_started_at, 0 , sizeof(adjust_started_at)); snort_conf->raEntry = rae->raNext; free(rae); } else if (!snort_conf->raLastLog) snort_conf->raLastLog = tv_sec; else if ((unsigned)(tv_sec - snort_conf->raLastLog) >= 10) { snort_conf->raLastLog = tv_sec; WarningMessage("Waiting for %s to adjust for reload (packet loop=%u, idle loop=%u).\n", rae->raName, num_packet_callbacks, num_idle_callbacks); } } } #endif extern SnortConfig *snort_conf_new; #endif snort-2.9.20/src/log_text.c0000644000175000017500000015245514241076632013672 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ // @file log_text.c // @author Russ Combs #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifdef HAVE_STRINGS_H #include #endif #ifndef WIN32 #include #include #include #endif /* !WIN32 */ #include #include #include "log.h" #include "rules.h" #include "treenodes.h" #include "util.h" #include "snort_debug.h" #include "signature.h" #include "util_net.h" #include "snort.h" #include "log_text.h" #include "sfutil/sf_textlog.h" #include "snort_bounds.h" #include "obfuscation.h" #include "detection_util.h" #include "sfutil/sf_ip.h" extern OptTreeNode *otn_tmp; /* global ptr to current rule data */ extern int IsGzipData(void *); extern int IsJSNormData(void *); /*-------------------------------------------------------------------- * utility functions *-------------------------------------------------------------------- */ void LogTimeStamp(TextLog* log, Packet* p) { char timestamp[TIMEBUF_SIZE]; ts_print((struct timeval*)&p->pkth->ts, timestamp); TextLog_Puts(log, timestamp); } /*-------------------------------------------------------------------- * alert stuff cloned from log.c *-------------------------------------------------------------------- */ /*-------------------------------------------------------------------- * Function: LogPriorityData() * * Purpose: Prints out priority data associated with an alert * * Arguments: log => pointer to TextLog to write the data to * doNewLine => tack a \n to the end of the line or not (bool) * * Returns: void function *-------------------------------------------------------------------- */ void LogPriorityData(TextLog* log, bool doNewLine) { if (otn_tmp == NULL) return; if ((otn_tmp->sigInfo.classType != NULL) && (otn_tmp->sigInfo.classType->name != NULL)) { TextLog_Print(log, "[Classification: %s] ", otn_tmp->sigInfo.classType->name); } TextLog_Print(log, "[Priority: %d] ", otn_tmp->sigInfo.priority); if (doNewLine) TextLog_NewLine(log); } #if defined(FEAT_OPEN_APPID) /*-------------------------------------------------------------------- * Function: LogAppID() * * Purpose: Prints out AppID data associated with an alert * * Arguments: log => pointer to TextLog to write the data to * appName => name of app ID detected (if any) * doNewLine => tack a \n to the end of the line or not (bool) * * Returns: void function *-------------------------------------------------------------------- */ void LogAppID(TextLog* log, const char* appName, bool doNewLine) { if (!appName || !appName[0]) return; TextLog_Print(log, "[AppID: %s] ", appName); if (doNewLine) TextLog_NewLine(log); } #endif /*-------------------------------------------------------------------- * Layer 2 header stuff cloned from log.c *-------------------------------------------------------------------- */ #ifndef NO_NON_ETHER_DECODER /*-------------------------------------------------------------------- * Function: LogTrHeader(TextLog*, Packet*) * * Purpose: Print the packet TokenRing header to the given TextLog * * Arguments: log => pointer to TextLog to print to * * Returns: void function *-------------------------------------------------------------------- */ void LogTrHeader(TextLog* log, Packet* p) { TextLog_Print(log, "%X:%X:%X:%X:%X:%X -> ", p->trh->saddr[0], p->trh->saddr[1], p->trh->saddr[2], p->trh->saddr[3], p->trh->saddr[4], p->trh->saddr[5]); TextLog_Print(log, "%X:%X:%X:%X:%X:%X\n", p->trh->daddr[0], p->trh->daddr[1], p->trh->daddr[2], p->trh->daddr[3], p->trh->daddr[4], p->trh->daddr[5]); TextLog_Print(log, "access control:0x%X frame control:0x%X\n", p->trh->ac, p->trh->fc); if(!p->trhllc) return; TextLog_Print(log, "DSAP: 0x%X SSAP 0x%X protoID: %X%X%X Ethertype: %X\n", p->trhllc->dsap, p->trhllc->ssap, p->trhllc->protid[0], p->trhllc->protid[1], p->trhllc->protid[2], p->trhllc->ethertype); if(p->trhmr) { TextLog_Print(log, "RIF structure is present:\n"); TextLog_Print(log, "bcast: 0x%X length: 0x%X direction: 0x%X largest" "fr. size: 0x%X res: 0x%X\n", TRH_MR_BCAST(p->trhmr), TRH_MR_LEN(p->trhmr), TRH_MR_DIR(p->trhmr), TRH_MR_LF(p->trhmr), TRH_MR_RES(p->trhmr)); TextLog_Print(log, "rseg -> %X:%X:%X:%X:%X:%X:%X:%X\n", p->trhmr->rseg[0], p->trhmr->rseg[1], p->trhmr->rseg[2], p->trhmr->rseg[3], p->trhmr->rseg[4], p->trhmr->rseg[5], p->trhmr->rseg[6], p->trhmr->rseg[7]); } } #endif // NO_NON_ETHER_DECODER /*-------------------------------------------------------------------- * Function: LogEthHeader() * * Purpose: Print the packet Ethernet header to the given TextLog * * Arguments: log => pointer to TextLog to print to * * Returns: void function *-------------------------------------------------------------------- */ static void LogEthHeader(TextLog* log, Packet* p) { /* src addr */ TextLog_Print(log, "%02X:%02X:%02X:%02X:%02X:%02X -> ", p->eh->ether_src[0], p->eh->ether_src[1], p->eh->ether_src[2], p->eh->ether_src[3], p->eh->ether_src[4], p->eh->ether_src[5]); /* dest addr */ TextLog_Print(log, "%02X:%02X:%02X:%02X:%02X:%02X ", p->eh->ether_dst[0], p->eh->ether_dst[1], p->eh->ether_dst[2], p->eh->ether_dst[3], p->eh->ether_dst[4], p->eh->ether_dst[5]); /* protocol and pkt size */ TextLog_Print(log, "type:0x%X len:0x%X\n", ntohs(p->eh->ether_type), p->pkth->pktlen); } #ifdef MPLS static void LogMPLSHeader(TextLog* log, Packet* p) { TextLog_Print(log,"label:0x%05X exp:0x%X bos:0x%X ttl:0x%X\n", p->mplsHdr.label, p->mplsHdr.exp, p->mplsHdr.bos, p->mplsHdr.ttl); } #endif #ifdef GRE static void LogGREHeader(TextLog *log, Packet *p) { if (p->greh == NULL) return; TextLog_Print(log, "GRE version:%u flags:0x%02X ether-type:0x%04X\n", GRE_VERSION(p->greh), p->greh->flags, GRE_PROTO(p->greh)); } #endif #ifndef NO_NON_ETHER_DECODER /*-------------------------------------------------------------------- * Function: LogSLLHeader(TextLog* ) * * Purpose: Print the packet SLL (fake) header to the given TextLog * (piece partly is borrowed from tcpdump :)) * * Arguments: log => pointer to TextLog to print to * * Returns: void function *-------------------------------------------------------------------- */ #ifdef DLT_LINUX_SLL static void LogSLLHeader(TextLog* log, Packet* p) { switch (ntohs(p->sllh->sll_pkttype)) { case LINUX_SLL_HOST: TextLog_Puts(log, "< "); break; case LINUX_SLL_BROADCAST: TextLog_Puts(log, "B "); break; case LINUX_SLL_MULTICAST: TextLog_Puts(log, "M "); break; case LINUX_SLL_OTHERHOST: TextLog_Puts(log, "P "); break; case LINUX_SLL_OUTGOING: TextLog_Puts(log, "> "); break; default: TextLog_Puts(log, "? "); break; } /* mac addr */ TextLog_Print(log, "l/l len: %i l/l type: 0x%X %02X:%02X:%02X:%02X:%02X:%02X\n", htons(p->sllh->sll_halen), ntohs(p->sllh->sll_hatype), p->sllh->sll_addr[0], p->sllh->sll_addr[1], p->sllh->sll_addr[2], p->sllh->sll_addr[3], p->sllh->sll_addr[4], p->sllh->sll_addr[5]); /* protocol and pkt size */ TextLog_Print(log, "pkt type:0x%X proto: 0x%X len:0x%X\n", ntohs(p->sllh->sll_pkttype), ntohs(p->sllh->sll_protocol), p->pkth->pktlen); } #endif /*-------------------------------------------------------------------- * Function: LogWifiHeader(TextLog* ) * * Purpose: Print the packet 802.11 header to the given TextLog * * Arguments: log => pointer to TextLog to print to * * Returns: void function *-------------------------------------------------------------------- */ static void LogWifiHeader(TextLog* log, Packet * p) { /* This assumes we are printing a data packet, could be changed to print other types as well */ const u_char *da = NULL, *sa = NULL, *bssid = NULL, *ra = NULL, *ta = NULL; /* per table 4, IEEE802.11 section 7.2.2 */ if ((p->wifih->frame_control & WLAN_FLAG_TODS) && (p->wifih->frame_control & WLAN_FLAG_FROMDS)) { ra = p->wifih->addr1; ta = p->wifih->addr2; da = p->wifih->addr3; sa = p->wifih->addr4; } else if (p->wifih->frame_control & WLAN_FLAG_TODS) { bssid = p->wifih->addr1; sa = p->wifih->addr2; da = p->wifih->addr3; } else if (p->wifih->frame_control & WLAN_FLAG_FROMDS) { da = p->wifih->addr1; bssid = p->wifih->addr2; sa = p->wifih->addr3; } else { da = p->wifih->addr1; sa = p->wifih->addr2; bssid = p->wifih->addr3; } /* DO this switch to provide additional info on the type */ switch(p->wifih->frame_control & 0x00ff) { case WLAN_TYPE_MGMT_BEACON: TextLog_Puts(log, "Beacon "); break; /* management frames */ case WLAN_TYPE_MGMT_ASREQ: TextLog_Puts(log, "Assoc. Req. "); break; case WLAN_TYPE_MGMT_ASRES: TextLog_Puts(log, "Assoc. Resp. "); break; case WLAN_TYPE_MGMT_REREQ: TextLog_Puts(log, "Reassoc. Req. "); break; case WLAN_TYPE_MGMT_RERES: TextLog_Puts(log, "Reassoc. Resp. "); break; case WLAN_TYPE_MGMT_PRREQ: TextLog_Puts(log, "Probe Req. "); break; case WLAN_TYPE_MGMT_PRRES: TextLog_Puts(log, "Probe Resp. "); break; case WLAN_TYPE_MGMT_ATIM: TextLog_Puts(log, "ATIM "); break; case WLAN_TYPE_MGMT_DIS: TextLog_Puts(log, "Dissassoc. "); break; case WLAN_TYPE_MGMT_AUTH: TextLog_Puts(log, "Authent. "); break; case WLAN_TYPE_MGMT_DEAUTH: TextLog_Puts(log, "Deauthent. "); break; /* Control frames */ case WLAN_TYPE_CONT_PS: case WLAN_TYPE_CONT_RTS: case WLAN_TYPE_CONT_CTS: case WLAN_TYPE_CONT_ACK: case WLAN_TYPE_CONT_CFE: case WLAN_TYPE_CONT_CFACK: TextLog_Puts(log, "Control "); break; } if (sa != NULL) { TextLog_Print(log, "%X:%X:%X:%X:%X:%X -> ", sa[0], sa[1], sa[2], sa[3], sa[4], sa[5]); } else if (ta != NULL) { TextLog_Print(log, "ta: %X:%X:%X:%X:%X:%X da: ", ta[0], ta[1], ta[2], ta[3], ta[4], ta[5]); } TextLog_Print(log, "%X:%X:%X:%X:%X:%X\n", da[0], da[1], da[2], da[3], da[4], da[5]); if (bssid != NULL) { TextLog_Print(log, "bssid: %X:%X:%X:%X:%X:%X", bssid[0], bssid[1], bssid[2], bssid[3], bssid[4], bssid[5]); } if (ra != NULL) { TextLog_Print(log, " ra: %X:%X:%X:%X:%X:%X", ra[0], ra[1], ra[2], ra[3], ra[4], ra[5]); } TextLog_Puts(log, " Flags:"); if (p->wifih->frame_control & WLAN_FLAG_TODS) TextLog_Puts(log," ToDs"); if (p->wifih->frame_control & WLAN_FLAG_TODS) TextLog_Puts(log," FrDs"); if (p->wifih->frame_control & WLAN_FLAG_FRAG) TextLog_Puts(log," Frag"); if (p->wifih->frame_control & WLAN_FLAG_RETRY) TextLog_Puts(log," Re"); if (p->wifih->frame_control & WLAN_FLAG_PWRMGMT) TextLog_Puts(log," Pwr"); if (p->wifih->frame_control & WLAN_FLAG_MOREDAT) TextLog_Puts(log," MD"); if (p->wifih->frame_control & WLAN_FLAG_WEP) TextLog_Puts(log," Wep"); if (p->wifih->frame_control & WLAN_FLAG_ORDER) TextLog_Puts(log," Ord"); TextLog_NewLine(log); } #endif // NO_NON_ETHER_DECODER /*-------------------------------------------------------------------- * Function: Log2ndHeader(TextLog* , Packet p) * * Purpose: Log2ndHeader -- prints second layber header info. * * Arguments: log => pointer to TextLog to print to * * Returns: void function *-------------------------------------------------------------------- */ void Log2ndHeader(TextLog* log, Packet* p) { switch(DAQ_GetBaseProtocol()) { case DLT_EN10MB: /* Ethernet */ if(p && p->eh) LogEthHeader(log, p); break; #ifndef NO_NON_ETHER_DECODER #ifdef DLT_IEEE802_11 case DLT_IEEE802_11: if(p && p->wifih) LogWifiHeader(log, p); break; #endif case DLT_IEEE802: /* Token Ring */ if(p && p->trh) LogTrHeader(log, p); break; #ifdef DLT_LINUX_SLL case DLT_LINUX_SLL: if (p && p->sllh) LogSLLHeader(log, p); /* Linux cooked sockets */ break; #endif #endif // NO_NON_ETHER_DECODER default: if (ScLogVerbose()) { // FIXTHIS should only be output once! ErrorMessage("Datalink %i type 2nd layer display is not " "supported\n", DAQ_GetBaseProtocol()); } } } /*------------------------------------------------------------------- * IP stuff cloned from log.c *------------------------------------------------------------------- */ static void LogIpOptions(TextLog* log, Packet * p) { int i; int j; u_long init_offset; u_long print_offset; init_offset = TextLog_Tell(log); if(!p->ip_option_count || p->ip_option_count > 40) return; TextLog_Print(log, "IP Options (%d) => ", p->ip_option_count); for(i = 0; i < (int) p->ip_option_count; i++) { print_offset = TextLog_Tell(log); if((print_offset - init_offset) > 60) { TextLog_Puts(log, "\nIP Options => "); init_offset = TextLog_Tell(log); } switch(p->ip_options[i].code) { case IPOPT_RR: TextLog_Puts(log, "RR "); break; case IPOPT_EOL: TextLog_Puts(log, "EOL "); break; case IPOPT_NOP: TextLog_Puts(log, "NOP "); break; case IPOPT_TS: TextLog_Puts(log, "TS "); break; case IPOPT_ESEC: TextLog_Puts(log, "ESEC "); break; case IPOPT_SECURITY: TextLog_Puts(log, "SEC "); break; case IPOPT_LSRR: case IPOPT_LSRR_E: TextLog_Puts(log, "LSRR "); break; case IPOPT_SATID: TextLog_Puts(log, "SID "); break; case IPOPT_SSRR: TextLog_Puts(log, "SSRR "); break; case IPOPT_RTRALT: TextLog_Puts(log, "RTRALT "); break; default: TextLog_Print(log, "Opt %d: ", p->ip_options[i].code); if(p->ip_options[i].len) { for(j = 0; j < p->ip_options[i].len; j++) { if (p->ip_options[i].data) TextLog_Print(log, "%02X", p->ip_options[i].data[j]); else TextLog_Print(log, "%02X", 0); if((j % 2) == 0) TextLog_Putc(log, ' '); } } break; } } TextLog_NewLine(log); } /*-------------------------------------------------------------------- * Function: LogIPAddrs(TextLog* ) * * Purpose: Dump the IP addresses to the given TextLog * Handles obfuscation * * Arguments: log => TextLog to print to * p => packet structure * * Returns: void function *-------------------------------------------------------------------- */ void LogIpAddrs(TextLog *log, Packet *p) { if (!IPH_IS_VALID(p)) return; if (p->frag_flag || ((GET_IPH_PROTO(p) != IPPROTO_TCP) && (GET_IPH_PROTO(p) != IPPROTO_UDP))) { char *ip_fmt = "%s -> %s"; if (ScObfuscate()) { TextLog_Print(log, ip_fmt, ObfuscateIpToText(GET_SRC_ADDR(p)), ObfuscateIpToText(GET_DST_ADDR(p))); } else { TextLog_Print(log, ip_fmt, inet_ntoax(GET_SRC_ADDR(p)), inet_ntoax(GET_DST_ADDR(p))); } } else { char *ip_fmt = "%s:%d -> %s:%d"; if (ScObfuscate()) { TextLog_Print(log, ip_fmt, ObfuscateIpToText(GET_SRC_ADDR(p)), p->sp, ObfuscateIpToText(GET_DST_ADDR(p)), p->dp); } else { TextLog_Print(log, ip_fmt, inet_ntoax(GET_SRC_ADDR(p)), p->sp, inet_ntoax(GET_DST_ADDR(p)), p->dp); } } } /*-------------------------------------------------------------------- * Function: LogIPHeader(TextLog* ) * * Purpose: Dump the IP header info to the given TextLog * * Arguments: log => TextLog to print to * * Returns: void function *-------------------------------------------------------------------- */ void LogIPHeader(TextLog* log, Packet * p) { if(!IPH_IS_VALID(p)) { TextLog_Print(log, "IP header truncated\n"); return; } LogIpAddrs(log, p); if(!ScOutputDataLink()) { TextLog_NewLine(log); } else { TextLog_Putc(log, ' '); } TextLog_Print(log, "%s TTL:%u TOS:0x%X ID:%u IpLen:%u DgmLen:%u", protocol_names[GET_IPH_PROTO(p)], GET_IPH_TTL(p), GET_IPH_TOS(p), IS_IP6(p) ? ntohl(GET_IPH_ID(p)) : ntohs((uint16_t)GET_IPH_ID(p)), GET_IPH_HLEN(p) << 2, GET_IP_DGMLEN(p)); /* print the reserved bit if it's set */ if((uint8_t)((ntohs(GET_IPH_OFF(p)) & 0x8000) >> 15) == 1) TextLog_Puts(log, " RB"); /* printf more frags/don't frag bits */ if((uint8_t)((ntohs(GET_IPH_OFF(p)) & 0x4000) >> 14) == 1) TextLog_Puts(log, " DF"); if((uint8_t)((ntohs(GET_IPH_OFF(p)) & 0x2000) >> 13) == 1) TextLog_Puts(log, " MF"); TextLog_NewLine(log); /* print IP options */ if(p->ip_option_count != 0) { LogIpOptions(log, p); } /* print fragment info if necessary */ if(p->frag_flag) { TextLog_Print(log, "Frag Offset: 0x%04X Frag Size: 0x%04X\n", (p->frag_offset & 0x1FFF), GET_IP_PAYLEN(p)); } } #ifdef GRE static void LogOuterIPHeader(TextLog *log, Packet *p) { int save_family = p->family; IPH_API *save_api = p->iph_api; const IPHdr *save_iph = p->iph; uint8_t save_ip_option_count = p->ip_option_count; IP4Hdr *save_ip4h = p->ip4h; IP6Hdr *save_ip6h = p->ip6h; uint8_t save_frag_flag = p->frag_flag; uint16_t save_sp = 0, save_dp = 0; p->family = p->outer_family; p->iph_api = p->outer_iph_api; p->iph = p->outer_iph; p->ip_option_count = 0; p->ip4h = &p->outer_ip4h; p->ip6h = &p->outer_ip6h; p->frag_flag = 0; if (p->proto_bits & PROTO_BIT__TEREDO) { save_sp = p->sp; save_dp = p->dp; if (p->outer_udph) { p->sp = ntohs(p->outer_udph->uh_sport); p->dp = ntohs(p->outer_udph->uh_dport); } else { p->sp = ntohs(p->udph->uh_sport); p->dp = ntohs(p->udph->uh_dport); } } LogIPHeader(log, p); p->family = save_family; p->iph_api = save_api; p->iph = save_iph; p->ip_option_count = save_ip_option_count; p->ip4h = save_ip4h; p->ip6h = save_ip6h; p->frag_flag = save_frag_flag; if (p->proto_bits & PROTO_BIT__TEREDO) { p->sp = save_sp; p->dp = save_dp; } } #endif /*------------------------------------------------------------------- * TCP stuff cloned from log.c *------------------------------------------------------------------- */ static void LogTcpOptions(TextLog* log, Packet * p) { int i; int j; u_char tmp[5]; #if 0 u_long init_offset; u_long print_offset; init_offset = TextLog_Tell(log); #endif TextLog_Print(log, "TCP Options (%d) => ", p->tcp_option_count); if(p->tcp_option_count > 40 || !p->tcp_option_count) return; for(i = 0; i < (int) p->tcp_option_count; i++) { #if 0 print_offset = TextLog_Tell(log); if((print_offset - init_offset) > 60) { TextLog_Puts(log, "\nTCP Options => "); init_offset = TextLog_Tell(log); } #endif switch(p->tcp_options[i].code) { case TCPOPT_MAXSEG: memset((char*)tmp, 0, sizeof(tmp)); TextLog_Puts(log, "MSS: "); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 2); TextLog_Print(log, "%u ", EXTRACT_16BITS(tmp)); break; case TCPOPT_EOL: TextLog_Puts(log, "EOL "); break; case TCPOPT_TFO: TextLog_Puts(log, "TFO "); break; case TCPOPT_NOP: TextLog_Puts(log, "NOP "); break; case TCPOPT_WSCALE: if (p->tcp_options[i].data) TextLog_Print(log, "WS: %u ", p->tcp_options[i].data[0]); else TextLog_Print(log, "WS: %u ", 0); break; case TCPOPT_SACK: memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data && (p->tcp_options[i].len >= 2)) memcpy(tmp, p->tcp_options[i].data, 2); TextLog_Print(log, "Sack: %u@", EXTRACT_16BITS(tmp)); memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data && (p->tcp_options[i].len >= 4)) memcpy(tmp, (p->tcp_options[i].data) + 2, 2); TextLog_Print(log, "%u ", EXTRACT_16BITS(tmp)); break; case TCPOPT_SACKOK: TextLog_Puts(log, "SackOK "); break; case TCPOPT_ECHO: memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); TextLog_Print(log, "Echo: %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_ECHOREPLY: memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); TextLog_Print(log, "Echo Rep: %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_TIMESTAMP: memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); TextLog_Print(log, "TS: %u ", EXTRACT_32BITS(tmp)); memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data) memcpy(tmp, (p->tcp_options[i].data) + 4, 4); TextLog_Print(log, "%u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_CC: memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); TextLog_Print(log, "CC %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_CCNEW: memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); TextLog_Print(log, "CCNEW: %u ", EXTRACT_32BITS(tmp)); break; case TCPOPT_CCECHO: memset((char*)tmp, 0, sizeof(tmp)); if (p->tcp_options[i].data) memcpy(tmp, p->tcp_options[i].data, 4); TextLog_Print(log, "CCECHO: %u ", EXTRACT_32BITS(tmp)); break; default: if(p->tcp_options[i].len) { TextLog_Print(log, "Opt %d (%d): ", p->tcp_options[i].code, (int) p->tcp_options[i].len); for(j = 0; j < p->tcp_options[i].len; j++) { if (p->tcp_options[i].data) TextLog_Print(log, "%02X", p->tcp_options[i].data[j]); else TextLog_Print(log, "%02X", 0); if ((j + 1) % 2 == 0) TextLog_Putc(log, ' '); } TextLog_Putc(log, ' '); } else { TextLog_Print(log, "Opt %d ", p->tcp_options[i].code); } break; } } TextLog_NewLine(log); } /*-------------------------------------------------------------------- * Function: LogTCPHeader(TextLog* ) * * Purpose: Dump the TCP header info to the given TextLog * * Arguments: log => pointer to TextLog to print data to * * Returns: void function *-------------------------------------------------------------------- */ void LogTCPHeader(TextLog* log, Packet * p) { char tcpFlags[9]; if(p->tcph == NULL) { TextLog_Print(log, "TCP header truncated\n"); return; } /* print TCP flags */ CreateTCPFlagString(p, tcpFlags); TextLog_Puts(log, tcpFlags); /* We don't care about the NULL */ /* print other TCP info */ TextLog_Print(log, " Seq: 0x%lX Ack: 0x%lX Win: 0x%X TcpLen: %d", (u_long) ntohl(p->tcph->th_seq), (u_long) ntohl(p->tcph->th_ack), ntohs(p->tcph->th_win), TCP_OFFSET(p->tcph) << 2); if((p->tcph->th_flags & TH_URG) != 0) { TextLog_Print(log, " UrgPtr: 0x%X\n", (uint16_t) ntohs(p->tcph->th_urp)); } else { TextLog_NewLine(log); } /* dump the TCP options */ if(p->tcp_option_count != 0) { LogTcpOptions(log, p); } } /*------------------------------------------------------------------- * UDP stuff cloned from log.c *------------------------------------------------------------------- */ /*-------------------------------------------------------------------- * Function: LogUDPHeader(TextLog* ) * * Purpose: Dump the UDP header to the given TextLog * * Arguments: log => pointer to TextLog * * Returns: void function *-------------------------------------------------------------------- */ void LogUDPHeader(TextLog* log, Packet* p) { if(p->udph == NULL) { TextLog_Print(log, "UDP header truncated\n"); return; } /* not much to do here... */ TextLog_Print(log, "Len: %d\n", ntohs(p->udph->uh_len) - UDP_HEADER_LEN); } /*-------------------------------------------------------------------- * ICMP stuff cloned from log.c *-------------------------------------------------------------------- */ /*-------------------------------------------------------------------- * Function: LogEmbeddedICMPHeader(TextLog* , ICMPHdr *) * * Purpose: Prints the 64 bits of the original IP payload in an ICMP packet * that requires it * * Arguments: log => pointer to TextLog * icmph => ICMPHdr struct pointing to original ICMP * * Returns: void function *-------------------------------------------------------------------- */ static void LogEmbeddedICMPHeader(TextLog* log, const ICMPHdr *icmph) { if (log == NULL || icmph == NULL) return; TextLog_Print(log, "Type: %d Code: %d Csum: %u", icmph->type, icmph->code, ntohs(icmph->csum)); switch (icmph->type) { case ICMP_DEST_UNREACH: case ICMP_TIME_EXCEEDED: case ICMP_SOURCE_QUENCH: break; case ICMP_PARAMETERPROB: if (icmph->code == 0) TextLog_Print(log, " Ptr: %u", icmph->s_icmp_pptr); break; case ICMP_REDIRECT: // XXX-IPv6 "NOT YET IMPLEMENTED - ICMP printing" break; case ICMP_ECHO: case ICMP_ECHOREPLY: case ICMP_TIMESTAMP: case ICMP_TIMESTAMPREPLY: case ICMP_INFO_REQUEST: case ICMP_INFO_REPLY: case ICMP_ADDRESS: case ICMP_ADDRESSREPLY: TextLog_Print(log, " Id: %u SeqNo: %u", ntohs(icmph->s_icmp_id), ntohs(icmph->s_icmp_seq)); break; case ICMP_ROUTER_ADVERTISE: TextLog_Print(log, " Addrs: %u Size: %u Lifetime: %u", icmph->s_icmp_num_addrs, icmph->s_icmp_wpa, ntohs(icmph->s_icmp_lifetime)); break; default: break; } TextLog_NewLine(log); return; } /*-------------------------------------------------------------------- * Function: LogICMPEmbeddedIP(TextLog* , Packet *) * * Purpose: Prints the original/encapsulated IP header + 64 bits of the * original IP payload in an ICMP packet * * Arguments: log => pointer to TextLog * p => packet struct * * Returns: void function *-------------------------------------------------------------------- */ static void LogICMPEmbeddedIP(TextLog* log, Packet *p) { Packet op; Packet *orig_p; uint32_t orig_ip_hlen; if (log == NULL || p == NULL) return; memset((char*)&op, 0, sizeof(op)); orig_p = &op; orig_p->iph = p->orig_iph; orig_p->tcph = p->orig_tcph; orig_p->udph = p->orig_udph; orig_p->sp = p->orig_sp; orig_p->dp = p->orig_dp; orig_p->icmph = p->orig_icmph; orig_p->iph_api = p->orig_iph_api; orig_p->ip4h = p->orig_ip4h; orig_p->ip6h = p->orig_ip6h; orig_p->family = p->orig_family; if(orig_p->iph != NULL) { TextLog_Print(log, "\n** ORIGINAL DATAGRAM DUMP:\n"); LogIPHeader(log, orig_p); orig_ip_hlen = IP_HLEN(p->orig_iph) << 2; switch(GET_IPH_PROTO(orig_p)) { case IPPROTO_TCP: if(orig_p->tcph != NULL) TextLog_Print(log, "Seq: 0x%lX\n", (u_long)ntohl(orig_p->tcph->th_seq)); break; case IPPROTO_UDP: if(orig_p->udph != NULL) TextLog_Print(log, "Len: %d Csum: %d\n", ntohs(orig_p->udph->uh_len) - UDP_HEADER_LEN, ntohs(orig_p->udph->uh_chk)); break; case IPPROTO_ICMP: if(orig_p->icmph != NULL) LogEmbeddedICMPHeader(log, orig_p->icmph); break; default: TextLog_Print(log, "Protocol: 0x%X (unknown or " "header truncated)", GET_IPH_PROTO(orig_p)); break; } /* switch */ /* if more than 8 bytes of original IP payload sent */ if (p->dsize - orig_ip_hlen > 8) { TextLog_Print(log, "(%d more bytes of original packet)\n", p->dsize - orig_ip_hlen - 8); } TextLog_Puts(log, "** END OF DUMP"); } else { TextLog_Puts(log, "\nORIGINAL DATAGRAM TRUNCATED"); } } /*-------------------------------------------------------------------- * Function: LogICMPHeader(TextLog* ) * * Purpose: Print ICMP header * * Arguments: log => pointer to TextLog * * Returns: void function *-------------------------------------------------------------------- */ void LogICMPHeader(TextLog* log, Packet * p) { /* 32 digits plus 7 colons and a NULL byte */ char buf[8*4 + 7 + 1]; if(p->icmph == NULL) { TextLog_Puts(log, "ICMP header truncated\n"); return; } TextLog_Print(log, "Type:%d Code:%d ", p->icmph->type, p->icmph->code); switch(p->icmph->type) { case ICMP_ECHOREPLY: TextLog_Print(log, "ID:%d Seq:%d ", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); TextLog_Puts(log, "ECHO REPLY"); break; case ICMP_DEST_UNREACH: TextLog_Puts(log, "DESTINATION UNREACHABLE: "); switch(p->icmph->code) { case ICMP_NET_UNREACH: TextLog_Puts(log, "NET UNREACHABLE"); break; case ICMP_HOST_UNREACH: TextLog_Puts(log, "HOST UNREACHABLE"); break; case ICMP_PROT_UNREACH: TextLog_Puts(log, "PROTOCOL UNREACHABLE"); break; case ICMP_PORT_UNREACH: TextLog_Puts(log, "PORT UNREACHABLE"); break; case ICMP_FRAG_NEEDED: TextLog_Print(log, "FRAGMENTATION NEEDED, DF SET\n" "NEXT LINK MTU: %u", ntohs(p->icmph->s_icmp_nextmtu)); break; case ICMP_SR_FAILED: TextLog_Puts(log, "SOURCE ROUTE FAILED"); break; case ICMP_NET_UNKNOWN: TextLog_Puts(log, "NET UNKNOWN"); break; case ICMP_HOST_UNKNOWN: TextLog_Puts(log, "HOST UNKNOWN"); break; case ICMP_HOST_ISOLATED: TextLog_Puts(log, "HOST ISOLATED"); break; case ICMP_PKT_FILTERED_NET: TextLog_Puts(log, "ADMINISTRATIVELY PROHIBITED NETWORK FILTERED"); break; case ICMP_PKT_FILTERED_HOST: TextLog_Puts(log, "ADMINISTRATIVELY PROHIBITED HOST FILTERED"); break; case ICMP_NET_UNR_TOS: TextLog_Puts(log, "NET UNREACHABLE FOR TOS"); break; case ICMP_HOST_UNR_TOS: TextLog_Puts(log, "HOST UNREACHABLE FOR TOS"); break; case ICMP_PKT_FILTERED: TextLog_Puts(log, "ADMINISTRATIVELY PROHIBITED,\nPACKET FILTERED"); break; case ICMP_PREC_VIOLATION: TextLog_Puts(log, "PREC VIOLATION"); break; case ICMP_PREC_CUTOFF: TextLog_Puts(log, "PREC CUTOFF"); break; default: TextLog_Puts(log, "UNKNOWN"); break; } LogICMPEmbeddedIP(log, p); break; case ICMP_SOURCE_QUENCH: TextLog_Puts(log, "SOURCE QUENCH"); LogICMPEmbeddedIP(log, p); break; case ICMP_REDIRECT: TextLog_Puts(log, "REDIRECT"); switch(p->icmph->code) { case ICMP_REDIR_NET: TextLog_Puts(log, " NET"); break; case ICMP_REDIR_HOST: TextLog_Puts(log, " HOST"); break; case ICMP_REDIR_TOS_NET: TextLog_Puts(log, " TOS NET"); break; case ICMP_REDIR_TOS_HOST: TextLog_Puts(log, " TOS HOST"); break; } /* written this way since inet_ntoa was typedef'ed to use sfip_ntoa * which requires sfcidr_t instead of inaddr's. This call to inet_ntoa * is a rare case that doesn't use sfcidr_t's. */ // XXX-IPv6 NOT YET IMPLEMENTED - IPV6 addresses technically not supported - need to change ICMP /* no inet_ntop in Windows */ sfip_raw_ntop(AF_INET, (const void *)(&p->icmph->s_icmp_gwaddr.s_addr), buf, sizeof(buf)); TextLog_Print(log, " NEW GW: %s", buf); LogICMPEmbeddedIP(log, p); break; case ICMP_ECHO: TextLog_Print(log, "ID:%d Seq:%d ", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); TextLog_Puts(log, "ECHO"); break; case ICMP_ROUTER_ADVERTISE: TextLog_Print(log, "ROUTER ADVERTISMENT: " "Num addrs: %d Addr entry size: %d Lifetime: %u", p->icmph->s_icmp_num_addrs, p->icmph->s_icmp_wpa, ntohs(p->icmph->s_icmp_lifetime)); break; case ICMP_ROUTER_SOLICIT: TextLog_Puts(log, "ROUTER SOLICITATION"); break; case ICMP_TIME_EXCEEDED: TextLog_Puts(log, "TTL EXCEEDED"); switch(p->icmph->code) { case ICMP_TIMEOUT_TRANSIT: TextLog_Puts(log, " IN TRANSIT"); break; case ICMP_TIMEOUT_REASSY: TextLog_Puts(log, " TIME EXCEEDED IN FRAG REASSEMBLY"); break; } LogICMPEmbeddedIP(log, p); break; case ICMP_PARAMETERPROB: TextLog_Puts(log, "PARAMETER PROBLEM"); switch(p->icmph->code) { case ICMP_PARAM_BADIPHDR: TextLog_Print(log, ": BAD IP HEADER BYTE %u", p->icmph->s_icmp_pptr); break; case ICMP_PARAM_OPTMISSING: TextLog_Puts(log, ": OPTION MISSING"); break; case ICMP_PARAM_BAD_LENGTH: TextLog_Puts(log, ": BAD LENGTH"); break; } LogICMPEmbeddedIP(log, p); break; case ICMP_TIMESTAMP: TextLog_Print(log, "ID: %u Seq: %u TIMESTAMP REQUEST", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_TIMESTAMPREPLY: TextLog_Print(log, "ID: %u Seq: %u TIMESTAMP REPLY:\n" "Orig: %u Rtime: %u Ttime: %u", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq), p->icmph->s_icmp_otime, p->icmph->s_icmp_rtime, p->icmph->s_icmp_ttime); break; case ICMP_INFO_REQUEST: TextLog_Print(log, "ID: %u Seq: %u INFO REQUEST", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_INFO_REPLY: TextLog_Print(log, "ID: %u Seq: %u INFO REPLY", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_ADDRESS: TextLog_Print(log, "ID: %u Seq: %u ADDRESS REQUEST", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq)); break; case ICMP_ADDRESSREPLY: TextLog_Print(log, "ID: %u Seq: %u ADDRESS REPLY: 0x%08X", ntohs(p->icmph->s_icmp_id), ntohs(p->icmph->s_icmp_seq), (u_int) ntohl(p->icmph->s_icmp_mask)); break; default: TextLog_Puts(log, "UNKNOWN"); break; } TextLog_NewLine(log); } /*-------------------------------------------------------------------- * reference stuff cloned from signature.c *-------------------------------------------------------------------- */ /* print a reference node */ static void LogReference(TextLog* log, ReferenceNode *refNode) { if(refNode) { if(refNode->system) { if(refNode->system->url) TextLog_Print(log, "[Xref => %s%s]", refNode->system->url, refNode->id); else TextLog_Print(log, "[Xref => %s %s]", refNode->system->name, refNode->id); } else { TextLog_Print(log, "[Xref => %s]", refNode->id); } } return; } /* * Function: LogXrefs(TextLog* ) * * Purpose: Prints out cross reference data associated with an alert * * Arguments: log => pointer to TextLog to write the data to * doNewLine => tack a \n to the end of the line or not (bool) * * Returns: void function */ void LogXrefs(TextLog* log, bool doNewLine) { ReferenceNode *refNode = NULL; if(otn_tmp) { refNode = otn_tmp->sigInfo.refs; while(refNode != NULL) { LogReference(log, refNode); refNode = refNode->next; /* on the last loop through, print a newline in Full mode */ if(doNewLine && (refNode == NULL)) TextLog_NewLine(log); } } } /*-------------------------------------------------------------------- * payload stuff cloned from log.c *-------------------------------------------------------------------- */ /*-------------------------------------------------------------------- * Function: ScOutputCharData(TextLog*, char*, int) * * Purpose: Dump the printable ASCII data from a packet * * Arguments: log => ptr to TextLog to print to * data => pointer to buffer data * len => length of data buffer * * Returns: void function *-------------------------------------------------------------------- */ static void LogCharData(TextLog* log, const char *data, int len) { const char* pb = data; const char* end = data + len; int lineCount = 0; if ( !data ) { return; } while ( pb < end ) { if ( *pb > 0x1F && *pb < 0x7F) { /* printable */ TextLog_Putc(log, *pb); } else { /* not printable */ TextLog_Putc(log, '.'); } if ( ++lineCount == 64 ) { TextLog_Putc(log, ' '); TextLog_NewLine(log); lineCount = 0; } pb++; } /* slam a \n on the back */ TextLog_Putc(log, ' '); TextLog_NewLine(log); TextLog_Putc(log, ' '); } /* * Function: LogNetData(TextLog*, u_char *,int, Packet *) * * Purpose: Do a side by side dump of a buffer, hex on * the left, decoded ASCII on the right. * * Arguments: log => ptr to TextLog to print to * data => pointer to buffer data * len => length of data buffer * * Returns: void function */ #define BYTES_PER_FRAME 16 /* middle of packet:"41 02 43 04 45 06 47 08 49 0A 4B 0C 4D 0E 4F 0F A.C.E.G.I.K.M.O."*/ /* at end of packet:"41 02 43 04 45 06 47 08 A.C.E.G."*/ static char* pad3 = " "; static void LogNetData (TextLog* log, const u_char* data, const int len, Packet *p) { const u_char* pb = data; const u_char* end = data + len; int offset = 0; char conv[] = "0123456789ABCDEF"; /* xlation lookup table */ int next_layer, ip_start, ip_ob_start, ip_ob_end, byte_pos, char_pos; int i; next_layer = ip_start = byte_pos = char_pos = 0; ip_ob_start = ip_ob_end = -1; if ( !len ) { TextLog_NewLine(log); return; } if ( !data ) { TextLog_Print(log, "Got NULL ptr in LogNetData()\n"); return; } if ( len > IP_MAXPACKET ) { if (ScLogVerbose()) { TextLog_Print( log, "Got bogus buffer length (%d) for LogNetData, " "defaulting to %d bytes!\n", len, BYTES_PER_FRAME ); } end = data + BYTES_PER_FRAME; } if(p && ScObfuscate() ) { next_layer = p->next_layer; for ( i = 0; i < next_layer; i++ ) { if ( p->layers[i].proto == PROTO_IP4 || p->layers[i].proto == PROTO_IP6 ) { if(p->layers[i].length && p->layers[i].start) break; } } ip_start = p->layers[i].start - data; if(ip_start > 0 ) { ip_ob_start = ip_start + 10; if(p->layers[i].proto == PROTO_IP4) ip_ob_end = ip_ob_start + 2 + 2*(sizeof(struct in_addr)); else ip_ob_end = ip_ob_start + 2 + 2*(sizeof(struct in6_addr)); } } /* loop thru the whole buffer */ while ( pb < end ) { i = 0; if (ScVerboseByteDump()) { TextLog_Print(log, "0x%04X: ", offset); offset += BYTES_PER_FRAME; } /* process one frame */ /* first print the binary as ascii hex */ for (i = 0; i < BYTES_PER_FRAME && pb+i < end; i++, byte_pos++) { if(ScObfuscate() && ((byte_pos >= ip_ob_start) && (byte_pos < ip_ob_end))) { TextLog_Putc(log, 'X'); TextLog_Putc(log, 'X'); TextLog_Putc(log, ' '); } else { char b = pb[i]; TextLog_Putc(log, conv[(b & 0xFF) >> 4]); TextLog_Putc(log, conv[(b & 0xFF) & 0x0F]); TextLog_Putc(log, ' '); } } /* print ' ' past end of packet and before ascii */ TextLog_Puts(log, pad3+(3*i)); /* then print the actual ascii chars */ /* or a '.' for control chars */ for (i = 0; i < BYTES_PER_FRAME && pb+i < end; i++, char_pos++) { if(ScObfuscate() && ((char_pos >= ip_ob_start) && (char_pos < ip_ob_end))) { TextLog_Putc(log, 'X'); } else { char b = pb[i]; if ( b > 0x1F && b < 0x7F) TextLog_Putc(log, (char)(b & 0xFF)); else TextLog_Putc(log, '.'); } } pb += BYTES_PER_FRAME; TextLog_NewLine(log); } TextLog_NewLine(log); } #define SEPARATOR \ "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+" static int LogObfuscatedData(TextLog* log, Packet *p) { uint8_t *payload = NULL; uint16_t payload_len = 0; if (obApi->getObfuscatedPayload(p, &payload, (uint16_t *)&payload_len) != OB_RET_SUCCESS) { return -1; } /* dump the application layer data */ if (ScOutputAppData() && !ScVerboseByteDump()) { if (ScOutputCharData()) LogCharData(log, (char *)payload, payload_len); else LogNetData(log, payload, payload_len, NULL); } else if (ScVerboseByteDump()) { uint8_t buf[UINT16_MAX]; uint16_t dlen = p->data - p->pkt; int ret; ret = SafeMemcpy(buf, p->pkt, dlen, buf, buf + sizeof(buf)); if (ret != SAFEMEM_SUCCESS) { DEBUG_WRAP(DebugMessage(DEBUG_LOG, "%s: SafeMemcpy() Failed !!!", __FUNCTION__);) free(payload); return -1; } ret = SafeMemcpy(buf + dlen, payload, payload_len, buf, buf + sizeof(buf)); if (ret != SAFEMEM_SUCCESS) { DEBUG_WRAP(DebugMessage(DEBUG_LOG, "%s: SafeMemcpy() Failed !!!", __FUNCTION__);) free(payload); return -1; } LogNetData(log, buf, dlen + payload_len, NULL); } TextLog_Print(log, "%s\n\n", SEPARATOR); free(payload); return 0; } /*-------------------------------------------------------------------- * Function: LogIPPkt(TextLog*, int, Packet *) * * Purpose: Dump the packet to the given TextLog * * Arguments: log => pointer to print data to * type => packet protocol * p => pointer to decoded packet struct * * Returns: void function *-------------------------------------------------------------------- */ #define DATA_PTR(p) \ ((u_char*)p->iph + (GET_IPH_HLEN(p) << 2)) #define DATA_LEN(p) \ (p->actual_ip_len - (GET_IPH_HLEN(p) << 2)) void LogIPPkt(TextLog* log, int type, Packet * p) { DEBUG_WRAP(DebugMessage(DEBUG_LOG, "LogIPPkt type = %d\n", type);); /* dump the timestamp */ LogTimeStamp(log, p); /* dump the ethernet header if we're doing that sort of thing */ if ( ScOutputDataLink() ) { Log2ndHeader(log, p); #ifdef MPLS if ( p->mpls ) { LogMPLSHeader(log, p); } #endif #ifdef GRE if ( p->outer_iph ) { LogOuterIPHeader(log, p); if ( p->greh ) LogGREHeader(log, p); } #endif } LogIPHeader(log, p); /* if this isn't a fragment, print the other header info */ if ( !p->frag_flag ) { switch (GET_IPH_PROTO(p)) { case IPPROTO_TCP: if ( p->tcph != NULL ) { LogTCPHeader(log, p); } else { LogNetData(log, DATA_PTR(p), DATA_LEN(p), NULL); } break; case IPPROTO_UDP: if ( p->udph != NULL ) { LogUDPHeader(log, p); } else { LogNetData(log, DATA_PTR(p), DATA_LEN(p), NULL); } break; case IPPROTO_ICMP: if ( p->icmph != NULL ) { LogICMPHeader(log, p); } else { LogNetData(log, DATA_PTR(p), GET_IP_PAYLEN(p), NULL); } break; default: break; } } if ((p->dsize > 0) && obApi->payloadObfuscationRequired(p) && (LogObfuscatedData(log, p) == 0)) { return; } /* dump the application layer data */ if (ScOutputAppData() && !ScVerboseByteDump()) { if (ScOutputCharData()) { LogCharData(log, (char *)p->data, p->dsize); if(!IsJSNormData(p->ssnptr)) { TextLog_Print(log, "%s\n", "Normalized JavaScript for this packet"); LogCharData(log, (const char*)file_data_ptr.data, file_data_ptr.len); } else if(!IsGzipData(p->ssnptr)) { TextLog_Print(log, "%s\n", "Decompressed Data for this packet"); LogCharData(log, (const char *)file_data_ptr.data, file_data_ptr.len); } } else { LogNetData(log, p->data, p->dsize, NULL); if(!IsJSNormData(p->ssnptr)) { TextLog_Print(log, "%s\n", "Normalized JavaScript for this packet"); LogNetData(log, file_data_ptr.data, file_data_ptr.len, NULL); } else if(!IsGzipData(p->ssnptr)) { TextLog_Print(log, "%s\n", "Decompressed Data for this packet"); LogNetData(log, file_data_ptr.data, file_data_ptr.len, NULL); } } } else if (ScVerboseByteDump()) { LogNetData(log, p->pkt, p->pkth->caplen, p); } TextLog_Print(log, "%s\n\n", SEPARATOR); } #ifndef NO_NON_ETHER_DECODER /*-------------------------------------------------------------------- * ARP stuff cloned from log.c *-------------------------------------------------------------------- */ void LogArpHeader(TextLog* log, Packet * p) { // XXX-IPv6 "NOT YET IMPLEMENTED - printing ARP header" } #endif // NO_NON_ETHER_DECODER #ifdef DUMP_BUFFER /*-------------------------------------------------------------------- * Function to dump the buffers used by snort during packet * processing and inspection *-------------------------------------------------------------------- */ void LogBuffer(TextLog *log, char *name, char *data, const int len) { int i = 0, j; char conv[] = "0123456789ABCDEF"; TextLog_Print(log, "\n%s, %d\n", name, len); while (i < len) { TextLog_Print(log, "\n%.8x ",i); for (j = 0; j < BYTES_PER_FRAME; j++) { if (j == (BYTES_PER_FRAME/2)) { TextLog_Putc(log, ' '); } if ((i+j) < len) { char b = data[j + i]; TextLog_Putc(log, conv[(b & 0xFF) >> 4]); TextLog_Putc(log, conv[(b & 0xFF) & 0x0F]); TextLog_Putc(log, ' '); } else { TextLog_Putc(log, ' '); TextLog_Putc(log, ' '); TextLog_Putc(log, ' '); } } TextLog_Putc(log, ' '); TextLog_Putc(log, '|'); for (j = 0; j < BYTES_PER_FRAME; j++) { if ((i+j) < len) { char b = data[j + i]; if ( b > 0x1F && b < 0x7F) { TextLog_Putc(log, (char)(b & 0xFF)); } else { TextLog_Putc(log, '.'); } } else { TextLog_Putc(log, ' '); } } TextLog_Putc(log, '|'); i = i + j; } TextLog_Putc(log, '\n'); } #endif snort-2.9.20/src/event.h0000644000175000017500000000454014241076512013157 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* D E F I N E S ************************************************************/ #ifndef __EVENT_H__ #define __EVENT_H__ #ifdef OSF1 #include #endif #include #include "pcap_pkthdr32.h" #if defined(FEAT_OPEN_APPID) #define MAX_EVENT_APPNAME_LEN 64 #endif /* defined(FEAT_OPEN_APPID) */ typedef struct _Event { uint32_t sig_generator; /* which part of snort generated the alert? */ uint32_t sig_id; /* sig id for this generator */ uint32_t sig_rev; /* sig revision for this id */ uint32_t classification; /* event classification */ uint32_t priority; /* event priority */ uint32_t event_id; /* event ID */ uint32_t event_reference; /* reference to other events that have gone off, * such as in the case of tagged packets... */ struct sf_timeval32 ref_time; /* reference time for the event reference */ #if defined(FEAT_OPEN_APPID) char app_name[MAX_EVENT_APPNAME_LEN]; #endif /* defined(FEAT_OPEN_APPID) */ /* Don't add to this structure because this is the serialized data * struct for unified logging. */ } Event; #if 0 typedef struct _EventID { uint32_t sequence; uint32_t seconds; } EventID; typedef struct _Event { EventID id; uint32_t uSeconds; SigInfo sigInfo; } Event; #endif #endif /* __EVENT_H__ */ snort-2.9.20/src/file-process/0000755000175000017500000000000014242725720014260 5ustar apoaposnort-2.9.20/src/file-process/file_segment_process.c0000644000175000017500000005707114241076556020642 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * Author(s): Hui Cao ****************************************************************************/ #include "file_segment_process.h" #include "parser.h" #include "file_resume_block.h" #include "file_config.h" #include "memory_stats.h" #ifdef REG_TEST #include "file_stats.h" #include "reg_test.h" #include #endif #define UNKNOWN_FILE_SIZE ~0 extern FileSession* get_file_session(void *ssnptr); static inline void file_segment_free(FileCache *fileCache, FileSegment* file_segment) { if (!file_segment) return; if (fileCache) { if(fileCache->status.segment_mem_in_use >= file_segment->segment_size) fileCache->status.segment_mem_in_use -= file_segment->segment_size; else fileCache->status.segment_mem_in_use = 0; } SnortPreprocFree(file_segment, sizeof(FileSegment), PP_FILE, PP_MEM_CATEGORY_SESSION); } static inline void file_segments_free (FileEntry *file_entry) { FileSegment *current_segment; current_segment = file_entry->segments; while (current_segment) { FileSegment *previous_segment = current_segment; current_segment = current_segment->next; file_segment_free(file_entry->file_cache, previous_segment); } file_entry->segments = NULL; file_entry->offset = 0; } static inline void file_entry_free(FileEntry *file_entry) { if (!file_entry) return; if (file_entry->file_name) { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "File name: %s released (%p)\n", file_entry->file_name, file_entry->file_name)); SnortPreprocFree(file_entry->file_name, file_entry->file_name_size, PP_FILE, PP_MEM_CATEGORY_SESSION); file_entry->file_name = NULL; file_entry->file_name_size = 0; } if (file_entry->context) { file_entry->context->attached_file_entry = NULL; file_context_free(file_entry->context); file_entry->context = NULL; } file_entry->file_size = 0; file_segments_free(file_entry); } static int file_entry_free_func(void *option_key, void *data) { FileEntry *file_entry = ( FileEntry *)data; file_entry_free(file_entry); return 0; } /* Prune file entries based on LRU */ static int pruneFileCache(FileCache *fileCache, FileEntry *file) { SFXHASH_NODE *lru_node = NULL; int pruned = 0; int mustdie = fileCache->cleanup_files; while (pruned < mustdie && (sfxhash_count(fileCache->hashTable) > 0)) { if ((lru_node = sfxhash_lru_node(fileCache->hashTable)) != NULL) { if (lru_node->data == file) break; if (sfxhash_free_node(fileCache->hashTable, lru_node) != SFXHASH_OK) { LogMessage("WARNING: failed to remove file entry from hash.\n"); break; } pruned++; } } fileCache->status.prunes += pruned; return pruned; } FileEntry *file_cache_get(FileCache *fileCache, void* p, uint64_t file_id, bool can_create) { SFXHASH_NODE *hnode; FileKey fileKey; Packet *pkt = (Packet *)p; sfaddr_t* srcIP; sfaddr_t* dstIP; SAVE_DAQ_PKT_HDR(p); if ((fileCache == NULL) || (fileCache->hashTable == NULL)) { FILE_WARNING("Failed to get file cache info"); return NULL; } if ((pkt->packet_flags & PKT_FROM_CLIENT)) { srcIP = GET_SRC_IP(pkt); dstIP = GET_DST_IP(pkt); } else { srcIP = GET_DST_IP(pkt); dstIP = GET_SRC_IP(pkt); } sfaddr_copy_to_raw(&fileKey.dip, dstIP); sfaddr_copy_to_raw(&fileKey.sip, srcIP); fileKey.file_id = file_id; if (!can_create) { hnode = sfxhash_find_node(fileCache->hashTable, &fileKey); } else { hnode = sfxhash_get_node(fileCache->hashTable, &fileKey); if (!hnode) { /*No more file entries, free up some old ones*/ if(pruneFileCache(fileCache, NULL) == 0) { FILE_WARNING("No free node available"); return NULL; } /* Should have some freed nodes now */ hnode = sfxhash_get_node(fileCache->hashTable, &fileKey); #ifdef DEBUG_MSGS if (!hnode) LogMessage("%s(%d) Problem, no freed nodes\n", __FILE__, __LINE__); #endif } } if (hnode && hnode->data) { FileEntry *file_entry = (FileEntry *)hnode->data; return file_entry; } else { FILE_WARNING("No free node available"); return NULL; } } /* Initialize file cache based on memcap * File cache cost includes three part: * 1) file hash table * 2) file context * 3) file segment * * Both 1) and 2) can be limited by maximal files, 3) can be limited by memcap. */ static inline uint32_t get_max_files_from_memcap (uint64_t memcap) { /* Per file cost*/ uint32_t per_file_cost = sizeof(FileContext) + sizeof(FileKey) + sizeof(FileEntry); return (memcap/per_file_cost); } static inline FileSegment* file_segment_alloc (FileCache *fileCache, const uint8_t* file_data, int data_size, uint64_t offset, FileEntry *file) { FileSegment* ss; unsigned int size = sizeof(*ss); if ( data_size > 0 ) size += (uint64_t)data_size - 1; /* ss contains 1st byte */ else return NULL; /* Check against memcap here*/ if ((fileCache->status.segment_mem_in_use + size) > fileCache->file_segment_memcap) { /* make more memory available by pruning. Return NULL if nothing is pruned*/ if(pruneFileCache(fileCache, file) == 0) return NULL; else if ((fileCache->status.segment_mem_in_use + size) > fileCache->file_segment_memcap) return NULL; } fileCache->status.segment_mem_in_use += size; ss = (FileSegment*) SnortPreprocAlloc(1, size, PP_FILE, PP_MEM_CATEGORY_SESSION); ss->segment_size = size; ss->size = data_size; ss->offset = offset; memcpy(ss->data, file_data, data_size); if (fileCache->status.segment_mem_in_use_max < fileCache->status.segment_mem_in_use) { fileCache->status.segment_mem_in_use_max = fileCache->status.segment_mem_in_use; } return ss; } /* Update the segment list based on new data * Use the original data if possible * Input: * offset: offset in file for the new segment * data_size: size of new segment * file: the file entry */ static inline int _file_segments_update( FileCache *fileCache, const uint8_t* file_data, uint64_t offset, int data_size, FileEntry *file) { FileSegment *current_segment = file->segments; uint64_t start = offset; uint64_t end = offset + data_size; FileSegment *new_segment; /* left points to segment that "next" pointer needs to be updated */ FileSegment *left = NULL; FileSegment *previous = NULL; bool find_left = false; /* Find left boundary, left points to segment that needs update*/ while (current_segment) { if (current_segment->offset > start) { find_left = true; left = previous; break; } previous = current_segment; current_segment = current_segment->next; } if (find_left) { if (!left) { /* Need to insert at begining */ if (end > file->segments->offset) { /* Overlap, trim off exrta data from end */ data_size = file->segments->offset - offset; } } else { /* Need to insert in the middle */ if (left->offset + left->size > start) { /* Overlap, trim begining */ offset = left->offset +left->size; data_size = end - offset; file_data = file_data + offset - start; } if (left->next->offset < end) { /* Overlap, trim end of data */ data_size = left->next->offset - offset; } } } else if (previous) { /* Need to insert at end */ left = previous; if (left->offset + left->size > start) { /* Overlap, trim begining */ offset = left->offset + left->size; data_size = end - offset; file_data = file_data + offset - start; } } if (data_size > 0) { new_segment = file_segment_alloc(fileCache, file_data, data_size, offset, file); if (!new_segment) return 0; } else { /* data_size <= 0 means a complete coverlapped segment we have already seen */ FILE_DEBUG("Complete overlapped segment, discarding."); return 0; } if (!left) { /* inserting at begining or list is empty */ new_segment->next = file->segments; file->segments = new_segment; } else { /* inserting at end or middle */ new_segment->next = left->next; left->next = new_segment; } return 1; } static inline FilePosition get_file_position(uint64_t file_size, int data_size, uint64_t offset) { if (offset == 0) { if (file_size == (uint64_t) data_size) return SNORT_FILE_FULL; else return SNORT_FILE_START; } if (file_size <= data_size + offset) return SNORT_FILE_END; return SNORT_FILE_MIDDLE; } static inline int _process_one_file_segment (void* p, FileEntry *fileEntry, const uint8_t* file_data, int *data_size, uint64_t file_size) { int ret; if(fileEntry->offset < file_size && fileEntry->offset + *data_size > file_size) *data_size = file_size - fileEntry->offset; FilePosition position = get_file_position(file_size, *data_size, fileEntry->offset); FILE_DEBUG("Processing segment, File size: %u, data size: %d, File position: %d",file_size,*data_size,position); if(fileEntry->file_size != UNKNOWN_FILE_SIZE) { if (position == SNORT_FILE_END && fileEntry->context && fileEntry->context->smb_unknown_file_size) { if((fileEntry->context->file_state.sig_state == FILE_SIG_FLUSH) && fileEntry->context->sha256) { SnortPreprocFree(fileEntry->context->sha256, SHA256_HASH_SIZE, PP_FILE, PP_MEM_CATEGORY_SESSION); fileEntry->context->sha256 = NULL; } fileEntry->context->smb_unknown_file_size = false; } ret = file_api->process_file(fileEntry->context, p, (uint8_t *)file_data, *data_size, position, false); } else { Packet *pkt = (Packet *)p; if((fileEntry->context->file_state.sig_state == FILE_SIG_FLUSH) && fileEntry->context && fileEntry->context->sha256) { SnortPreprocFree(fileEntry->context->sha256, SHA256_HASH_SIZE, PP_FILE, PP_MEM_CATEGORY_SESSION); fileEntry->context->sha256 = NULL; } if(pkt->packet_flags & PKT_PDU_TAIL) { fileEntry->context->file_state.sig_state = FILE_SIG_FLUSH; fileEntry->context->smb_unknown_file_size = true; } else fileEntry->context->file_state.sig_state = FILE_SIG_PROCESSING; ret = file_api->process_file(fileEntry->context, p, (uint8_t *)file_data, *data_size, position, false); } return ret; } static inline int _process_file_segments(FileCache *fileCache, void* p, FileEntry *fileEntry, uint64_t file_size) { int ret = 1; /*Process the packet update the offset */ FileSegment *current_segment = fileEntry->segments; while (current_segment && (fileEntry->offset == current_segment->offset)) { ret = _process_one_file_segment(p, fileEntry, current_segment->data, (int *)¤t_segment->size, file_size); if (!ret) { file_segments_free(fileEntry); break; } fileEntry->offset += current_segment->size; fileEntry->segments = current_segment->next; file_segment_free(fileCache, current_segment); current_segment = fileEntry->segments; } return ret; } /* Create file cache */ FileCache *file_cache_create(uint64_t memcap, uint32_t cleanup_files) { FileCache *fileCache = NULL; int max_files = 0; uint64_t file_segment_memcap = memcap/2; if( !memcap ) { WarningMessage("%s(%d) File cache memory unlimited!\n", file_name, file_line); } /* Half for file segment, half for file context tracking*/ max_files = get_max_files_from_memcap(memcap - file_segment_memcap); fileCache = SnortPreprocAlloc(1, sizeof(*fileCache), PP_FILE, PP_MEM_CATEGORY_SESSION); if( fileCache ) { fileCache->max_files = max_files; /* Okay, now create the table */ fileCache->hashTable = sfxhash_new(max_files, sizeof(FileKey), sizeof(FileEntry), 0, 0, NULL, file_entry_free_func, 1 ); if (!fileCache->hashTable) { FatalError( "%s(%d) Unable to create a file cache.\n", file_name, file_line); } sfxhash_set_max_nodes( fileCache->hashTable, max_files ); fileCache->file_segment_memcap = file_segment_memcap; fileCache->cleanup_files = cleanup_files; } else { FatalError( "%s(%d) Unable to create a file cache.\n", file_name, file_line); } return fileCache; } /* Release file cache */ void file_cache_free( FileCache *fileCache ) { if (fileCache) { sfxhash_delete(fileCache->hashTable); SnortPreprocFree(fileCache, sizeof(FileCache), PP_FILE, PP_MEM_CATEGORY_SESSION); } } /* Add/update a file entry specified by file_id in the file cache*/ void *file_cache_update_entry (FileCache *fileCache, void* p, uint64_t file_id, uint8_t *file_name, uint32_t file_name_size, uint64_t file_size, bool reset, bool no_update_size) { FileEntry *fileEntry; fileEntry = file_cache_get(fileCache, p, file_id, true); if (!fileEntry) { FILE_DEBUG("Failed to add file entry: file_id %d not found in cache",file_id); return NULL; } if(fileEntry->context && reset) file_entry_free(fileEntry); /* If no context, set resume check */ if(!fileEntry->context) fileEntry->file_resume_check = true; if (file_name) { FILE_DEBUG("Add file: %s (%p) with file id %d", file_name,file_name,file_id); if (fileEntry->file_name && fileEntry->file_name != file_name) { FILE_DEBUG("File name: %s released (%p)", fileEntry->file_name,fileEntry->file_name); SnortPreprocFree(fileEntry->file_name, fileEntry->file_name_size, PP_FILE, PP_MEM_CATEGORY_SESSION); } fileEntry->file_name = file_name; fileEntry->file_name_size = file_name_size; } if (file_size) { if(no_update_size) { if(!fileEntry->file_size) fileEntry->file_size = file_size; } else { fileEntry->file_size = file_size; } } return fileEntry; } static inline void update_file_session(void *ssnptr, FileCache *fileCache, uint64_t file_id, FileContext *context) { FileSession *file_session; if (!file_api->set_current_file_context(ssnptr, context)) return; file_session = get_file_session (ssnptr); if (!file_session->file_cache) file_session->file_cache = fileCache; file_session->file_id = file_id; FILE_DEBUG("Updated file_id: %u, file cache %p in file session %p",file_id, fileCache, file_session); } /* * Process file segment, do file segment reassemble if the file segment is * out of order. file_id is unique, used as a key to find the file entity. * Return: * 1: continue processing/log/block this file * 0: ignore this file segment */ int file_segment_process( FileCache *fileCache, void* p, uint64_t file_id, uint64_t file_size, const uint8_t* file_data, int data_size, uint64_t offset, bool upload) { FileEntry *fileEntry; int ret = 0; Packet *pkt = (Packet *)p; void *ssnptr = pkt->ssnptr; File_Verdict verdict = FILE_VERDICT_UNKNOWN; uint32_t file_sig = 0; SAVE_DAQ_PKT_HDR(p); FILE_DEBUG("Processing segment: file_id: %u, file_size: %u, data_size: %d, offset: %u, direction: %d",file_id, file_size, data_size, offset, upload); fileEntry = file_cache_get(fileCache, p, file_id, true); if (fileEntry == NULL) { FILE_ERROR("Processing segment failed: no file entry in cache"); return 0; } if (!fileEntry->context) fileEntry->file_resume_check = true; if(fileEntry->file_resume_check) { if(fileEntry->file_name_size > 0) { file_sig = file_api->str_to_hash(fileEntry->file_name, fileEntry->file_name_size); } else if(fileEntry->context && fileEntry->context->file_name_size > 0 && fileEntry->context->file_name) { file_sig = file_api->str_to_hash(fileEntry->context->file_name, fileEntry->context->file_name_size); } if(file_sig) verdict = file_resume_block_check(p, file_sig); if (verdict == FILE_VERDICT_BLOCK || verdict == FILE_VERDICT_REJECT || verdict == FILE_VERDICT_PENDING) { #ifdef HAVE_DAQ_DP_ADD_DC DAQ_DC_Params params; sfaddr_t *srcIP = GET_SRC_IP(pkt); sfaddr_t *dstIP = GET_DST_IP(pkt); memset(¶ms, 0, sizeof(params)); params.flags = DAQ_DC_ALLOW_MULTIPLE; params.timeout_ms = 15 * 60 * 1000; /* 15 minutes */ if (pkt->packet_flags & PKT_FROM_CLIENT) DAQ_Add_Dynamic_Protocol_Channel(pkt, srcIP, 0, dstIP, pkt->dp, GET_IPH_PROTO(pkt), ¶ms); else if (pkt->packet_flags & PKT_FROM_SERVER) DAQ_Add_Dynamic_Protocol_Channel(pkt, dstIP, 0, srcIP, pkt->sp, GET_IPH_PROTO(pkt), ¶ms); #endif FILE_INFO("Processing segment stopped, verdict: %d\n",verdict); return 1; } fileEntry->file_resume_check = false; } if (fileEntry->file_size) file_size = fileEntry->file_size; else { FILE_ERROR("Processing segment failed: file size 0"); return 0; } if (!fileEntry->file_cache) fileEntry->file_cache = fileCache; if (!fileEntry->context) { fileEntry->context = file_api->create_file_context(ssnptr); file_api->init_file_context(ssnptr, upload, fileEntry->context); fileEntry->context->file_id = (uint32_t)file_id; fileEntry->context->attached_file_entry = fileEntry; update_file_session(ssnptr, fileCache, file_id, fileEntry->context); file_name_set(fileEntry->context, fileEntry->file_name, fileEntry->file_name_size, true); } else if (fileEntry->context->verdict != FILE_VERDICT_UNKNOWN && !fileEntry->context->smb_unknown_file_size) { /*A new file session, but policy might be different*/ if (((fileEntry->context->sha256)) || !fileEntry->context->file_signature_enabled ) { /* Just check file type and signature */ return _process_one_file_segment(p, fileEntry, file_data, &data_size, file_size); } } if(offset < fileEntry->offset && offset + data_size > fileEntry->offset) { file_data += (fileEntry->offset - offset); data_size = (offset + data_size)-fileEntry->offset; offset = fileEntry->offset; } /* Walk through the segments that can be flushed*/ if (fileEntry->offset == offset) { /*Process the packet update the offset */ ret = _process_one_file_segment(p, fileEntry, file_data, &data_size, file_size); fileEntry->offset += data_size; if (!ret) { file_segments_free(fileEntry); return 0; } ret = _process_file_segments(fileCache, p, fileEntry, file_size); } else if ((fileEntry->offset < file_size) && (fileEntry->offset < offset)) { ret = _file_segments_update(fileCache, file_data, offset, data_size, fileEntry); } #ifdef REG_TEST if(ret && fileEntry->file_name_size) { FILE_REG_DEBUG_WRAP(printFileContext(fileEntry->context)); fileEntry->file_name_size = 0; } #endif return ret; } /* Return the status of file cache */ FileCacheStatus *file_cache_status(FileCache *fileCache) { return (&(fileCache->status)); } static bool file_cache_prune_files(FileCache *fileCache, uint8_t *pWork) { #ifdef REG_TEST if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags()) printf("file-cache prunefiles-before %u \n", sfxhash_count(fileCache->hashTable)); #endif for (; *pWork > 0 && sfxhash_count(fileCache->hashTable) > fileCache->hashTable->max_nodes; (*pWork)--) pruneFileCache(fileCache,NULL); #ifdef REG_TEST if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags()) printf("file-cache prunefiles-after %u \n", sfxhash_count(fileCache->hashTable)); #endif return sfxhash_count(fileCache->hashTable) <= fileCache->hashTable->max_nodes; } static bool file_cache_prune_segment(FileCache *fileCache, uint8_t *pWork) { #ifdef REG_TEST if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags()) printf("file-cache prunesegment-before %"PRIu64" \n", fileCache->status.segment_mem_in_use); #endif for (; *pWork > 0 && fileCache->status.segment_mem_in_use > fileCache->file_segment_memcap; (*pWork)--) pruneFileCache(fileCache,NULL); #ifdef REG_TEST if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags()) printf("file-cache prunesegment-after %"PRIu64" \n", fileCache->status.segment_mem_in_use); #endif return fileCache->status.segment_mem_in_use <= fileCache->file_segment_memcap; } bool file_cache_shrink_to_memcap(FileCache *fileCache, uint8_t *pWork) { if (fileCache == NULL) return true; bool cache_shrunk = file_cache_prune_files(fileCache, pWork) && file_cache_prune_segment(fileCache, pWork); #ifdef REG_TEST if (cache_shrunk && REG_TEST_FLAG_FILE_CACHE & getRegTestFlags()) printf("file-cache done 1\n"); #endif return cache_shrunk; } void file_cache_set_memcap(FileCache *fileCache, uint64_t memcap) { if (fileCache == NULL) return; sfxhash_set_max_nodes(fileCache->hashTable, get_max_files_from_memcap(memcap/2)); fileCache->file_segment_memcap = memcap/2; #ifdef REG_TEST if (REG_TEST_FLAG_FILE_CACHE & getRegTestFlags()) { printf("file-cache mem-files %"PRIu32" \n", get_max_files_from_memcap(memcap/2)); printf("file-cache mem-segment %"PRIu64" \n", memcap/2); } #endif } snort-2.9.20/src/file-process/file_stats.h0000644000175000017500000000447614241076575016607 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.13 - Initial Source Code. Hcao */ #ifndef __FILE_STATS_H__ #define __FILE_STATS_H__ #ifdef TARGET_BASED #include "sftarget_protocol_reference.h" #include "sftarget_reader.h" #endif #include "file_config.h" #include "file_api.h" #include #include typedef struct _File_Stats { uint64_t files_total; uint64_t files_processed[FILE_ID_MAX + 1][2]; uint64_t signatures_processed[FILE_ID_MAX + 1][2]; uint64_t verdicts_type[FILE_VERDICT_MAX]; uint64_t verdicts_signature[FILE_VERDICT_MAX]; #ifdef TARGET_BASED uint64_t files_by_proto[MAX_PROTOCOL_ORDINAL + 1]; uint64_t signatures_by_proto[MAX_PROTOCOL_ORDINAL + 1]; #endif uint64_t data_processed[FILE_ID_MAX + 1][2]; uint64_t file_data_total; uint64_t files_sig_depth; } FileStats; extern FileStats file_stats; #ifdef REG_TEST #define FILE_REG_DEBUG_WRAP(code) code #else #ifdef DEBUG_MSGS #define FILE_REG_DEBUG_WRAP(code) if (DEBUG_FILE & GetDebugLevel()){code} #else #define FILE_REG_DEBUG_WRAP(code) #endif #endif #define FILE_DEBUG_MSGS(msg) DEBUG_WRAP(DebugMessage(DEBUG_FILE, msg);) #if defined(DEBUG_MSGS) || defined (REG_TEST) void printFileContext(FileContext* context); void DumpHexFile(FILE *fp, const uint8_t *data, unsigned int len); #endif /* * Print out file statistics */ void print_file_stats(int exiting); #endif snort-2.9.20/src/file-process/file_mempool.c0000644000175000017500000002063114241076545017100 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** This mempool implementation has very efficient alloc/free operations. ** In addition, it provides thread-safe alloc/free for one allocation/free ** thread and one release thread. One more bonus: Double free detection is ** also added into this library ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.13 - Initial Source Code. Hui Cao ** ** A */ #include #include #include #include "file_mempool.h" #include "memory_stats.h" #include "preprocids.h" #include "util.h" /*This magic is used for double free detection*/ #define FREE_MAGIC 0x2525252525252525 typedef uint64_t MagicType; #ifdef DEBUG_MSGS static inline void safe_mempool_verify(SafeMemPool *mempool) { uint64_t free_size; uint64_t release_size; free_size = cbuffer_used(mempool->free_list); release_size = cbuffer_used(mempool->released_list); if (free_size > cbuffer_size(mempool->free_list)) { ErrorMessage("%s(%d) safe_mempool: failed to verify free list!\n", __FILE__, __LINE__ ); } if (release_size > cbuffer_size(mempool->released_list)) { ErrorMessage("%s(%d) safe_mempool: failed to verify release list!\n", __FILE__, __LINE__ ); } /* The free mempool and size of release mempool should be smaller than * or equal to the size of mempool */ if (free_size + release_size > mempool->total) { ErrorMessage("%s(%d) safe_mempool: failed to verify mempool size!\n", __FILE__, __LINE__ ); } } #endif static inline void safe_mempool_free_pools(SafeMemPool *mempool) { if (mempool == NULL) return; if (mempool->datapool != NULL) { SnortPreprocFree(mempool->datapool, mempool->obj_size * mempool->total, PP_FILE, PP_MEM_CATEGORY_MEMPOOL); mempool->datapool = NULL; } cbuffer_free(mempool->free_list); cbuffer_free(mempool->released_list); } /* Function: int safe_mempool_init(SafeMemPool *SafeMemPool, * PoolCount num_objects, size_t obj_size) * * Purpose: initialize a SafeMemPool object and allocate memory for it * Args: * SafeMemPool - pointer to a SafeMemPool struct * num_objects - number of items in this pool * obj_size - size of the items * * Returns: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_init(SafeMemPool *mempool, uint64_t num_objects, size_t obj_size) { unsigned int i; if ((mempool == NULL) || (num_objects < 1) || (obj_size < 1)) return SAFE_MEM_FAIL; mempool->obj_size = obj_size; /* this is the basis pool that represents all the *data pointers * in the list */ mempool->datapool = SnortPreprocAlloc(num_objects, obj_size, PP_FILE, PP_MEM_CATEGORY_MEMPOOL); if(mempool->datapool == NULL) { ErrorMessage("%s(%d) safe_mempool_init(): Failed to init datapool\n", __FILE__, __LINE__); safe_mempool_free_pools(mempool); return SAFE_MEM_FAIL; } /* sets up the memory list */ mempool->free_list = cbuffer_init(num_objects); if (!mempool->free_list) { ErrorMessage("%s(%d) safe_mempool_init(): Failed to init free list\n", __FILE__, __LINE__); safe_mempool_free_pools(mempool); return SAFE_MEM_FAIL; } mempool->released_list = cbuffer_init(num_objects); if (!mempool->released_list) { ErrorMessage("%s(%d) safe_mempool_init(): " "Failed to init release list\n", __FILE__, __LINE__); safe_mempool_free_pools(mempool); return SAFE_MEM_FAIL; } for(i=0; idatapool) + (i * mempool->obj_size); if (cbuffer_write(mempool->free_list, data)) { ErrorMessage("%s(%d) safe_mempool_init(): " "Failed to add to free list\n", __FILE__, __LINE__); safe_mempool_free_pools(mempool); return SAFE_MEM_FAIL; } *(MagicType *)data = (uint64_t)FREE_MAGIC; mempool->total++; } return SAFE_MEM_SUCCESS; } /* * Destroy a set of SafeMemPool objects * * Args: * SafeMemPool: pointer to a SafeMemPool struct * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_destroy(SafeMemPool *mempool) { if(mempool == NULL) return SAFE_MEM_FAIL; safe_mempool_free_pools(mempool); return SAFE_MEM_SUCCESS; } /* * Allocate a new object from the SafeMemPool * * Args: * SafeMemPool: pointer to a SafeMemPool struct * * Returns: a pointer to the SafeMemPool object on success, NULL on failure */ void *safe_mempool_alloc(SafeMemPool *mempool) { void *b = NULL; if(mempool == NULL) { return NULL; } if(cbuffer_read(mempool->free_list, &b)) { if(cbuffer_read(mempool->released_list, &b)) { return NULL; } } if (*(MagicType *)b != ((uint64_t)FREE_MAGIC)) { ErrorMessage("%s(%d) safe_mempool_alloc(): Possible memory corruption! \n", __FILE__, __LINE__); } DEBUG_WRAP(safe_mempool_verify(mempool);); return b; } /* * Free a new object from the buffer * We use circular buffer to synchronize one reader and one writer * * Args: * SafeMemPool: pointer to a circular buffer struct * void *obj : memory object * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ static inline int _safe__mempool_remove(CircularBuffer *cb, void *obj) { if (obj == NULL) return SAFE_MEM_FAIL; if (*(MagicType *)obj == ((uint64_t)FREE_MAGIC)) { DEBUG_WRAP(ErrorMessage("%s(%d) safe_mempool_remove(): Double free! \n", __FILE__, __LINE__);); return SAFE_MEM_FAIL; } if (cbuffer_write(cb, obj)) { return SAFE_MEM_FAIL; } *(MagicType *)obj = (uint64_t)FREE_MAGIC; return SAFE_MEM_SUCCESS; } /* * Free a new object from the SafeMemPool * * Args: * SafeMemPool: pointer to a SafeMemPool struct * void *obj : memory object * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_free(SafeMemPool *mempool, void *obj) { int ret; assert(mempool); ret = _safe__mempool_remove(mempool->free_list, obj); DEBUG_WRAP(safe_mempool_verify(mempool);); return ret; } /* * Release a new object from the SafeMemPool * This can be called by a different thread calling * safe_mempool_alloc() * * * Args: * SafeMemPool: pointer to a SafeMemPool struct * void *obj : memory object * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_release(SafeMemPool *mempool, void *obj) { int ret; if (mempool == NULL) return SAFE_MEM_FAIL; /*A writer that might from different thread*/ ret = _safe__mempool_remove(mempool->released_list, obj); DEBUG_WRAP(safe_mempool_verify(mempool);); return ret; } /* Returns number of elements allocated in current buffer*/ uint64_t safe_mempool_allocated(SafeMemPool *mempool) { uint64_t total_freed = safe_mempool_released(mempool) + safe_mempool_freed(mempool); return (mempool->total - total_freed); } /* Returns number of elements freed in current buffer*/ uint64_t safe_mempool_freed(SafeMemPool *mempool) { return (cbuffer_used(mempool->free_list)); } /* Returns number of elements released in current buffer*/ uint64_t safe_mempool_released(SafeMemPool *mempool) { return (cbuffer_used(mempool->released_list)); } snort-2.9.20/src/file-process/file_service_config.c0000644000175000017500000004215114241076567020422 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.2012 - Initial Source Code. Hcao */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include "sf_types.h" #include "util.h" #include "mstring.h" #include "memory_stats.h" #include "parser.h" #include "file_service_config.h" #include "file_config.h" #include "file_lib.h" #include "file_capture.h" #include "file_ss.h" #define FILE_SERVICE_OPT__TYPE_DEPTH "file_type_depth" #define FILE_SERVICE_OPT__SIG_DEPTH "file_signature_depth" #define FILE_SERVICE_OPT__BLOCK_TIMEOUT "file_block_timeout" #define FILE_SERVICE_OPT__LOOKUP_TIMEOUT "file_lookup_timeout" #define FILE_SERVICE_OPT__BLOCK_TIMEOUT_LOOKUP "block_timeout_lookup" #define FILE_SERVICE_OPT__CAPTURE_MEMCAP "file_capture_memcap" #define FILE_SERVICE_OPT__CAPTURE_MAX_SIZE "file_capture_max" #define FILE_SERVICE_OPT__CAPTURE_MIN_SIZE "file_capture_min" #define FILE_SERVICE_OPT__CAPTURE_BLOCK_SIZE "file_capture_block_size" #define FILE_SERVICE_TYPE_DEPTH_MIN 0 #define FILE_SERVICE_TYPE_DEPTH_MAX UINT32_MAX #define FILE_SERVICE_SIG_DEPTH_MIN 0 #define FILE_SERVICE_SIG_DEPTH_MAX UINT32_MAX #define FILE_SERVICE_BLOCK_TIMEOUT_MIN 0 #define FILE_SERVICE_BLOCK_TIMEOUT_MAX UINT32_MAX #define FILE_SERVICE_LOOKUP_TIMEOUT_MIN 0 #define FILE_SERVICE_LOOKUP_TIMEOUT_MAX UINT32_MAX #define FILE_SERVICE_CAPTURE_MEMCAP_MIN 1 #define FILE_SERVICE_CAPTURE_MEMCAP_MAX UINT32_MAX #define FILE_SERVICE_CAPTURE_MAX_SIZE_MIN 0 #define FILE_SERVICE_CAPTURE_MAX_SIZE_MAX UINT32_MAX #define FILE_SERVICE_CAPTURE_MIN_SIZE_MIN 0 #define FILE_SERVICE_CAPTURE_MIN_SIZE_MAX UINT32_MAX #define FILE_SERVICE_CAPTURE_BLOCK_SIZE_MIN 8 /*at least 64bits*/ #define FILE_SERVICE_CAPTURE_BLOCK_SIZE_MAX UINT32_MAX #define DEFAULT_FILE_TYPE_DEPTH 1460 // 1460 B #define DEFAULT_FILE_SIGNATURE_DEPTH 10485760 // 10 MiB #define DEFAULT_FILE_SHOW_DATA_DEPTH 100 // 100 B #define DEFAULT_FILE_BLOCK_TIMEOUT 86400 // 1 day #define DEFAULT_FILE_LOOKUP_TIMEOUT 2 // 2 seconds #define DEFAULT_FILE_CAPTURE_MEM 100 // 100 MiB #define DEFAULT_FILE_CAPTURE_MAX_SIZE 1048576 // 1 MiB #define DEFAULT_FILE_CAPTURE_MIN_SIZE 0 // 0 #define DEFAULT_FILE_CAPTURE_BLOCK_SIZE 32768 // 32 KiB #if defined(DEBUG_MSGS) || defined (REG_TEST) #define FILE_SERVICE_OPT__TYPE "type_id" #define FILE_SERVICE_OPT__SIG "signature" #define FILE_SERVICE_OPT__SHOW_DATA_DEPTH "show_data_depth" #include "file_api.h" #endif /*Set default values for file config*/ static inline void file_service_config_defaults(FileConfig *file_config) { file_config->file_type_depth = DEFAULT_FILE_TYPE_DEPTH; file_config->file_signature_depth = DEFAULT_FILE_SIGNATURE_DEPTH; file_config->file_block_timeout = DEFAULT_FILE_BLOCK_TIMEOUT; file_config->file_lookup_timeout = DEFAULT_FILE_LOOKUP_TIMEOUT; file_config->block_timeout_lookup = false; #if defined(DEBUG_MSGS) || defined (REG_TEST) file_config->show_data_depth = DEFAULT_FILE_SHOW_DATA_DEPTH; #endif file_config->file_capture_memcap = DEFAULT_FILE_CAPTURE_MEM; file_config->file_capture_max_size = DEFAULT_FILE_CAPTURE_MAX_SIZE; file_config->file_capture_min_size = DEFAULT_FILE_CAPTURE_MIN_SIZE; file_config->file_capture_block_size = DEFAULT_FILE_CAPTURE_BLOCK_SIZE; } FileConfig* file_service_config_create(void) { FileConfig *file_config = SnortPreprocAlloc(1, sizeof(*file_config), PP_FILE, PP_MEM_CATEGORY_CONFIG); file_service_config_defaults(file_config); return file_config; } #if defined (SIDE_CHANNEL) void check_sidechannel_enabled(void *config) { FileConfig *file_config = (FileConfig *)config; if(ScSideChannelEnabled()) { file_config->use_side_channel = true; LogMessage("File service config: \n"); LogMessage(" File side channel enabled = %d \n",file_config->use_side_channel); } return; } #endif #if defined (SIDE_CHANNEL) && defined (REG_TEST) void FileSSConfigFree(void *config) { FileConfig *file_config = (FileConfig *)config; FileSSConfig *file_ss_config = file_config->file_ss_config; if (file_ss_config == NULL) return; /* Not changing the free here because memory is allocated using strdup */ if (file_ss_config->startup_input_file) free(file_ss_config->startup_input_file); /* Not changing the free here because memory is allocated using strdup */ if (file_ss_config->runtime_output_file) free(file_ss_config->runtime_output_file); SnortPreprocFree(file_ss_config, sizeof(FileSSConfig), PP_FILE, PP_MEM_CATEGORY_CONFIG); return; } #endif #ifdef SNORT_RELOAD /* Verify the file service configuration, changing memory settings and depth * settings requires snort restart */ int file_sevice_config_verify(SnortConfig *old, SnortConfig *new) { FileConfig *curr = (FileConfig *)old->file_config; FileConfig *next = (FileConfig *)new->file_config; FileConfig tmp; /*no file config on both*/ if ((!curr) && (!next)) return 0; /* use default value if nothing sets*/ if (!curr) { curr = &tmp; file_service_config_defaults(curr); } if (!next) { next = &tmp; file_service_config_defaults(next); } #if defined (SIDE_CHANNEL) && !defined (REG_TEST) check_sidechannel_enabled(curr); #endif /* check configurations */ if (curr->file_capture_memcap != next->file_capture_memcap) { ErrorMessage("File service: Changing file capture memcap" " requires a restart.\n"); return -1; } if (curr->file_capture_block_size != next->file_capture_block_size) { ErrorMessage("File service: Changing file capture block size" " requires a restart.\n"); return -1; } return 0; } #endif /* Display the configuration for the File preprocessor. * * PARAMETERS: None. * * RETURNS: Nothing. */ static void file_service_display_conf(FileConfig *config) { if (config == NULL) return; LogMessage("\n"); LogMessage("File service config: \n"); LogMessage(" File type depth: "STDi64" %s \n", config->file_type_depth, config->file_type_depth == DEFAULT_FILE_TYPE_DEPTH ? "(Default) bytes" : " bytes" ); LogMessage(" File signature depth: "STDi64" %s \n", config->file_signature_depth, config->file_signature_depth == DEFAULT_FILE_SIGNATURE_DEPTH ? "(Default) bytes" : " bytes" ); LogMessage(" File block timeout: "STDi64" %s \n", config->file_block_timeout, config->file_block_timeout == DEFAULT_FILE_BLOCK_TIMEOUT ? "(Default) seconds" : " seconds" ); LogMessage(" File lookup timeout: "STDi64" %s \n", config->file_lookup_timeout, config->file_lookup_timeout == DEFAULT_FILE_LOOKUP_TIMEOUT ? "(Default) seconds" : " seconds" ); LogMessage(" Action for lookup timeout: %s\n", config->block_timeout_lookup ? "BLOCKED":"ALLOWED (Default)"); LogMessage(" File capture memcap: "STDi64" %s \n", config->file_capture_memcap, config->file_capture_memcap == DEFAULT_FILE_CAPTURE_MEM ? "(Default) megabytes" : " megabytes" ); LogMessage(" File capture block size: "STDi64" %s \n", config->file_capture_block_size, config->file_capture_block_size == DEFAULT_FILE_CAPTURE_BLOCK_SIZE ? "(Default) bytes" : " bytes" ); LogMessage(" File capture max size: "STDi64" %s \n", config->file_capture_max_size, config->file_capture_max_size == DEFAULT_FILE_CAPTURE_MAX_SIZE ? "(Default) bytes" : " bytes" ); LogMessage(" File capture min size: "STDi64" %s \n", config->file_capture_min_size, config->file_capture_min_size == DEFAULT_FILE_CAPTURE_MIN_SIZE ? "(Default) bytes" : " bytes" ); #if defined (SIDE_CHANNEL) && defined (REG_TEST) FilePrintSSConfig(config->file_ss_config); #endif LogMessage("\n"); } /*The main function for parsing rule option*/ void file_service_config(struct _SnortConfig* sc, char *args, void *conf) { char **toks; int num_toks; int i; FileConfig *file_config = (FileConfig *)conf; #if defined(DEBUG_MSGS) || defined (REG_TEST) bool file_type_enabled = false; bool file_signature_enabled = false; #endif DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Loading file service configuration: %s\n", args);); if (!file_config) { return; } file_service_config_defaults(file_config); #if defined (SIDE_CHANNEL) && defined (REG_TEST) if (NULL == (file_config->file_ss_config = (FileSSConfig *)SnortPreprocAlloc(1, sizeof(FileSSConfig), PP_FILE, PP_MEM_CATEGORY_CONFIG))) return; else if(ScSideChannelEnabled()) file_config->use_side_channel = true; #endif toks = mSplit(args, ",", 0, &num_toks, 0); /* get rule option pairs */ for (i = 0; i < num_toks; i++) { char **opts; int num_opts; char *option_args = NULL; unsigned long value = 0; DEBUG_WRAP(DebugMessage(DEBUG_FILE," option: %s\n", toks[i]);); /* break out the option name from its data */ opts = mSplit(toks[i], " ", 3, &num_opts, '\\'); DEBUG_WRAP(DebugMessage(DEBUG_FILE," option name: %s\n", opts[0]);); if (num_opts == 2) { option_args = opts[1]; DEBUG_WRAP(DebugMessage(DEBUG_FILE," option args: %s\n", option_args);); } if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__TYPE_DEPTH )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__TYPE_DEPTH, FILE_SERVICE_TYPE_DEPTH_MIN, FILE_SERVICE_TYPE_DEPTH_MAX, &value); file_config->file_type_depth = (int64_t)value; if (file_config->file_type_depth == 0) file_config->file_type_depth = FILE_SERVICE_TYPE_DEPTH_MAX; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__SIG_DEPTH )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__SIG_DEPTH, FILE_SERVICE_SIG_DEPTH_MIN, FILE_SERVICE_SIG_DEPTH_MAX, &value); file_config->file_signature_depth = (int64_t)value; if (file_config->file_signature_depth == 0) file_config->file_signature_depth = FILE_SERVICE_SIG_DEPTH_MAX; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__BLOCK_TIMEOUT )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__BLOCK_TIMEOUT, FILE_SERVICE_BLOCK_TIMEOUT_MIN, FILE_SERVICE_BLOCK_TIMEOUT_MAX, &value); file_config->file_block_timeout = (int64_t)value; if (file_config->file_block_timeout == 0) file_config->file_block_timeout = FILE_SERVICE_BLOCK_TIMEOUT_MAX; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__LOOKUP_TIMEOUT )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__LOOKUP_TIMEOUT, FILE_SERVICE_LOOKUP_TIMEOUT_MIN, FILE_SERVICE_LOOKUP_TIMEOUT_MAX, &value); file_config->file_lookup_timeout = (int64_t)value; if (file_config->file_lookup_timeout == 0) file_config->file_lookup_timeout = FILE_SERVICE_LOOKUP_TIMEOUT_MAX; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__BLOCK_TIMEOUT_LOOKUP )) { file_config->block_timeout_lookup = true; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__CAPTURE_MEMCAP )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__CAPTURE_MEMCAP, FILE_SERVICE_CAPTURE_MEMCAP_MIN, FILE_SERVICE_CAPTURE_MEMCAP_MAX, &value); file_config->file_capture_memcap = (int64_t) value; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__CAPTURE_MAX_SIZE )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__CAPTURE_MAX_SIZE, FILE_SERVICE_CAPTURE_MAX_SIZE_MIN, FILE_SERVICE_CAPTURE_MAX_SIZE_MAX, &value); file_config->file_capture_max_size = (int64_t) value; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__CAPTURE_MIN_SIZE )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__CAPTURE_MIN_SIZE, FILE_SERVICE_CAPTURE_MIN_SIZE_MIN, FILE_SERVICE_CAPTURE_MIN_SIZE_MAX, &value); file_config->file_capture_min_size = (int64_t)value; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__CAPTURE_BLOCK_SIZE )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__CAPTURE_BLOCK_SIZE, FILE_SERVICE_CAPTURE_BLOCK_SIZE_MIN, FILE_SERVICE_CAPTURE_BLOCK_SIZE_MAX, &value); file_config->file_capture_block_size = (int64_t) value; } #if defined(DEBUG_MSGS) || defined (REG_TEST) else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__TYPE )) { file_type_enabled = true; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__SIG )) { file_signature_enabled = true; } else if ( !strcasecmp( opts[0], FILE_SERVICE_OPT__SHOW_DATA_DEPTH )) { CheckValueInRange(option_args, FILE_SERVICE_OPT__SHOW_DATA_DEPTH, FILE_SERVICE_SIG_DEPTH_MIN, FILE_SERVICE_SIG_DEPTH_MAX, &value); file_config->show_data_depth = (int64_t)value; if (file_config->show_data_depth == 0) file_config->show_data_depth = FILE_SERVICE_SIG_DEPTH_MAX; } #endif #if defined (SIDE_CHANNEL) && defined (REG_TEST) else if(!strcasecmp(opts[0], "ss_startup_input_file")) { file_config->file_ss_config->startup_input_file = strdup(opts[1]); } else if(!strcasecmp(opts[0], "ss_runtime_output_file")) { file_config->file_ss_config->runtime_output_file = strdup(opts[1]); } #endif else { ParseError("File service: Invalid argument: %s\n", opts[0]); return; } mSplitFree(&opts, num_opts); } #if defined(DEBUG_MSGS) || defined (REG_TEST) if (file_type_enabled) file_api->enable_file_type(sc, NULL); if (file_signature_enabled) file_api->enable_file_signature(sc, NULL); #endif /* file capture memcap should not be larger file capture block size*/ if (file_config->file_capture_block_size > (file_config->file_capture_memcap << 20)) { ParseError("File service: file capture block size ("STDi64")" " is larger than file capture memcap ("STDi64") bytes.", file_config->file_capture_block_size, file_config->file_capture_memcap << 20); } /* file capture size should not be larger file signature depth*/ if (file_config->file_capture_max_size > file_config->file_signature_depth) { ParseError("File service: file capture max size ("STDi64") " "is larger than file signature depth ("STDi64").", file_config->file_capture_max_size, file_config->file_signature_depth); } /* file capture min size should not be larger file capture max size*/ if (file_config->file_capture_min_size > file_config->file_capture_max_size) { ParseError("File service: file capture min size ("STDi64") " "is larger than file capture max size ("STDi64").", file_config->file_capture_min_size, file_config->file_capture_max_size); } file_service_display_conf(file_config); mSplitFree(&toks, num_toks); } snort-2.9.20/src/file-process/circular_buffer.h0000644000175000017500000000660314241076537017577 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Circular buffer is thread safe for one writer and one reader thread ** ** This implementaton is inspired by one slot open approach. ** See http://en.wikipedia.org/wiki/Circular_buffer ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.13 - Initial Source Code. Hcao */ #ifndef __CIRCULAR_BUFFER_H__ #define __CIRCULAR_BUFFER_H__ #define CB_SUCCESS 0 #define CB_FAIL -1 /* Opaque buffer element type. This would be defined by the application. */ typedef void * ElemType; struct _CircularBuffer; typedef struct _CircularBuffer CircularBuffer; /* * Initialize buffer based on number of elements * * Args: * uint64_t size: number of elements * * Return: * CircularBuffer *: pointer to the buffer * NULL: failed * */ CircularBuffer * cbuffer_init(uint64_t size); /* Release all memory used*/ void cbuffer_free(CircularBuffer *cb); /* * Check whether buffer is full * * Return: * 1: full * 0: not full */ int cbuffer_is_full(CircularBuffer *cb); /* * Check whether buffer is empty * * Return: * 1: empty * 0: not empty */ int cbuffer_is_empty(CircularBuffer *cb); /* Returns number of elements in use*/ uint64_t cbuffer_used(CircularBuffer *cb); /* Returns number of free elements*/ uint64_t cbuffer_available(CircularBuffer *cb); /* Returns total number of elements*/ uint64_t cbuffer_size(CircularBuffer *cb); /* * Add one element to the buffer * * Args: * CircularBuffer *: buffer * ElemType elem: the element to be added * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_write(CircularBuffer *cb, const ElemType elem); /* * Read one element from the buffer and remove it from buffer * * Args: * CircularBuffer *: buffer * ElemType *elem: the element pointer to be stored * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_read(CircularBuffer *cb, ElemType *elem); /* * Read one element from the buffer and no change on buffer * * Args: * CircularBuffer *: buffer * ElemType *elem: the element pointer to be stored * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_peek(CircularBuffer *cb, ElemType *elem); /* Returns total number of reads*/ uint64_t cbuffer_num_reads(CircularBuffer *cb); /* Returns total number of writes*/ uint64_t cbuffer_num_writes(CircularBuffer *cb); /* Returns total number of writer overruns*/ uint64_t cbuffer_num_over_runs(CircularBuffer *cb); /* Returns total number of reader overruns*/ uint64_t cbuffer_num_under_runs(CircularBuffer *cb); #endif snort-2.9.20/src/file-process/file_service.c0000644000175000017500000013620314241076560017070 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.12 - Initial Source Code. Hui Cao */ #ifdef HAVE_CONFIG_H #include #endif #include "sf_types.h" #include #include "file_api.h" #include "file_config.h" #include "file_mime_config.h" #include "file_capture.h" #include "file_stats.h" #include "session_api.h" #include "stream_api.h" #include "mstring.h" #include "preprocids.h" #include "detect.h" #include "plugbase.h" #include "active.h" #include "file_mime_process.h" #include "file_resume_block.h" #include "snort_httpinspect.h" #include "file_service.h" #include "file_segment_process.h" static bool file_type_force = false; static uint32_t file_config_version = 0; FileServiceConfig cur_config; static FileServiceConfig new_config; static FileServiceConfig init_config; /*Main File Processing functions */ static int file_process(void* ssnptr, uint8_t* file_data, int data_size, FilePosition position, bool upload, bool suspend_block_verdict, bool do_flush); /*File properties*/ static int get_file_name(void* ssnptr, uint8_t **fname, uint32_t *name_size); static uint64_t get_file_size(void* ssnptr); static uint64_t get_file_processed_size(void* ssnptr); static bool get_file_direction(void* ssnptr); static uint8_t *get_file_sig_sha256(void* ssnptr); static void set_file_name(void* ssnptr, uint8_t * fname, uint32_t name_size, bool save_in_context); static void set_file_direction(void* ssnptr, bool upload); static void set_file_policy_callback(File_policy_callback_func); static void enable_file_type(struct _SnortConfig* sc, File_type_callback_func); static void enable_file_signature(struct _SnortConfig* sc, File_signature_callback_func); static void enable_file_capture(struct _SnortConfig* sc, File_signature_callback_func ); static void set_file_action_log_callback(Log_file_action_func); static int64_t get_max_file_depth(struct _SnortConfig *snort_conf, bool next); static bool is_file_signature_enabled(void); static uint32_t str_to_hash(uint8_t *str, int length ); static void file_signature_lookup(void* p, bool is_retransmit); static void file_signature_callback(Packet* p); static inline void finish_signature_lookup(FileContext *context); static File_Verdict get_file_verdict(void *ssnptr); static void render_block_verdict(void *ctx, void *p); static bool is_file_service_enabled(void); static uint32_t get_file_type_id(void *ssnptr); static uint32_t get_new_file_instance(void *ssnptr); static void set_file_partial(void *p, FilePosition position, bool upload, bool is_partial); /* File context based file processing*/ FileContext* create_file_context(void *ssnptr); static void init_file_context(void *ssnptr, bool upload, FileContext *context); bool set_current_file_context(void *ssnptr, FileContext *ctx); FileContext* get_current_file_context(void *ssnptr); FileContext* get_main_file_context(void *ssnptr); static int process_file_context(FileContext *ctx, void *p, uint8_t *file_data, int data_size, FilePosition position, bool suspend_block_verdict); static FilePosition get_file_position(void *pkt); static bool check_paf_abort(void* ssn); static int64_t get_max_file_capture_size(void *ssn); static void file_session_free(void *session_data); extern FileEntry *file_cache_get(FileCache *fileCache, void* p, uint64_t file_id, bool can_create); static FileCharEncoding get_character_encoding(uint8_t *buffer, uint32_t length); void file_event_log_dump(FileCache *fileCache, void* p, uint64_t file_id); static void file_signature_reset(void* ssnptr); static char* file_get_filetype (void *ssnptr); FileAPI fileAPI; FileAPI* file_api = NULL; static unsigned s_cb_id = 0; void init_fileAPI(void) { fileAPI.version = FILE_API_VERSION; fileAPI.is_file_service_enabled = &is_file_service_enabled; fileAPI.file_process = &file_process; fileAPI.get_file_name = &get_file_name; fileAPI.get_file_size = &get_file_size; fileAPI.get_file_processed_size = &get_file_processed_size; fileAPI.get_file_direction = &get_file_direction; fileAPI.get_sig_sha256 = &get_file_sig_sha256; fileAPI.set_file_name = &set_file_name; fileAPI.set_file_direction = &set_file_direction; fileAPI.set_file_policy_callback = &set_file_policy_callback; fileAPI.enable_file_type = &enable_file_type; fileAPI.enable_file_signature = &enable_file_signature; fileAPI.enable_file_capture = &enable_file_capture; fileAPI.set_file_action_log_callback = &set_file_action_log_callback; fileAPI.install_file_service = &FileServiceInstall; fileAPI.get_max_file_depth = &get_max_file_depth; fileAPI.is_file_signature_enabled = &is_file_signature_enabled; fileAPI.set_log_buffers = &set_log_buffers; #ifdef SNORT_RELOAD fileAPI.update_mime_mempool = &update_mime_mempool; fileAPI.update_log_mempool = &update_log_mempool; #ifdef REG_TEST fileAPI.displayMimeMempool = &displayMimeMempool; fileAPI.displayLogMempool = &displayLogMempool; fileAPI.displayDecodeDepth = &displayDecodeDepth; #endif #endif fileAPI.init_mime_mempool = &init_mime_mempool; fileAPI.init_log_mempool= &init_log_mempool; fileAPI.file_resume_block_add_file = &file_resume_block_add_file; fileAPI.file_resume_block_check = &file_resume_block_check; fileAPI.str_to_hash = &str_to_hash; fileAPI.file_signature_lookup = &file_signature_lookup; fileAPI.set_mime_decode_config_defauts = &set_mime_decode_config_defauts; fileAPI.set_mime_log_config_defauts = &set_mime_log_config_defauts; fileAPI.parse_mime_decode_args = &parse_mime_decode_args; fileAPI.process_mime_data = &process_mime_data; fileAPI.free_mime_session = &free_mime_session; fileAPI.is_decoding_enabled = &is_decoding_enabled; fileAPI.check_decoding_conf = &check_decode_config; fileAPI.is_mime_log_enabled = &is_mime_log_enabled; fileAPI.finalize_mime_position = &finalize_mime_position; fileAPI.get_file_verdict = &get_file_verdict; fileAPI.render_block_verdict = &render_block_verdict; fileAPI.reserve_file = &file_capture_reserve; fileAPI.read_file = &file_capture_read; fileAPI.release_file = &file_capture_release; fileAPI.get_file_capture_size = &file_capture_size; fileAPI.get_file_type_id = &get_file_type_id; fileAPI.get_new_file_instance = &get_new_file_instance; fileAPI.create_file_context = &create_file_context; fileAPI.init_file_context = &init_file_context; fileAPI.set_current_file_context = &set_current_file_context; fileAPI.get_current_file_context = &get_current_file_context; fileAPI.get_main_file_context = &get_main_file_context; fileAPI.process_file = &process_file_context; fileAPI.get_file_position = &get_file_position; fileAPI.reset_mime_paf_state = &reset_mime_paf_state; fileAPI.process_mime_paf_data = &process_mime_paf_data; fileAPI.check_data_end = check_data_end; fileAPI.check_paf_abort = &check_paf_abort; fileAPI.get_max_file_capture_size = get_max_file_capture_size; fileAPI.file_cache_update_entry = &file_cache_update_entry; fileAPI.file_segment_process = &file_segment_process; fileAPI.file_cache_create = &file_cache_create; fileAPI.file_cache_free = &file_cache_free; fileAPI.file_cache_status = &file_cache_status; fileAPI.file_config_malware_check = &file_config_malware_check; fileAPI.get_character_encoding = &get_character_encoding; fileAPI.file_cache_shrink_to_memcap = &file_cache_shrink_to_memcap; fileAPI.file_cache_set_memcap = &file_cache_set_memcap; fileAPI.file_event_log_dump = &file_event_log_dump; fileAPI.file_signature_reset= &file_signature_reset; fileAPI.set_file_partial = &set_file_partial; fileAPI.file_get_filetype = &file_get_filetype; file_api = &fileAPI; init_mime(); RegisterMemoryStatsFunction(PP_FILE, FilePrintMemStats); } #if defined(DEBUG_MSGS) || defined (REG_TEST) static void printFileServiceChanges() { FileConfig *file_config = (FileConfig *)(snort_conf->file_config); if(cur_config.file_signature_enabled != new_config.file_signature_enabled) printf("File service Install: file_signature %s\n",new_config.file_signature_enabled ? "enabled":"disabled"); if(cur_config.file_type_id_enabled != new_config.file_type_id_enabled) printf("File service Install: file_type %s\n",new_config.file_type_id_enabled ? "enabled":"disabled"); if(cur_config.file_capture_enabled != new_config.file_capture_enabled) printf("File service Install: file_capture %s\n",new_config.file_capture_enabled ? "enabled":"disabled"); if (!file_config) return; if(new_config.file_type_id_enabled) printf("File service Install: file_type_depth:%u \n",(unsigned)file_config->file_type_depth); if(new_config.file_signature_enabled) printf("File service Install: file_signature_depth:%u \n",(unsigned)file_config->file_signature_depth); fflush(stdout); } #endif void FileServiceInstall(void) { #if defined(DEBUG_MSGS) || defined (REG_TEST) printFileServiceChanges(); #endif cur_config = new_config; new_config = init_config; if (stream_api) { if(cur_config.file_signature_enabled && !s_cb_id ) s_cb_id = stream_api->register_event_handler(file_signature_callback); } } static void start_file_processing(struct _SnortConfig* sc, bool capture) { static bool file_processing_initiated = false; if (!file_processing_initiated) { file_resume_block_init(); RegisterPreprocStats("file", print_file_stats); file_processing_initiated = true; } if (!sc) sc = snort_conf; if (!sc->file_config) sc->file_config = file_service_config_create(); if (capture) { FileConfig* file_config = sc->file_config; file_capture_init_mempool(file_config->file_capture_memcap, file_config->file_capture_block_size); } } void free_file_config(void *conf) { file_config_version++; file_rule_free(conf); file_identifiers_free(conf); SnortPreprocFree(conf, sizeof(FileConfig), PP_FILE, PP_MEM_CATEGORY_CONFIG); } void close_fileAPI(void) { file_resume_block_cleanup(); free_mime(); file_caputure_close(); } FileSession* get_file_session(void *ssnptr) { return ((FileSession*)session_api->get_application_data(ssnptr, PP_FILE)); } static inline FileSession* get_create_file_session(void *ssnptr) { FileSession *file_session = get_file_session(ssnptr); if(!file_session) { file_session = (FileSession *)SnortPreprocAlloc(1, sizeof(*file_session), PP_FILE, PP_MEM_CATEGORY_SESSION); if (session_api->set_application_data(ssnptr, PP_FILE, file_session, file_session_free)) { SnortPreprocFree(file_session, sizeof(*file_session), PP_FILE, PP_MEM_CATEGORY_SESSION); return NULL; } } return(file_session); } /*Get the current working file context*/ FileContext* get_current_file_context(void *ssnptr) { FileSession *file_session = get_file_session (ssnptr); if (file_session) return file_session->current_context; else { FILE_WARNING("Failed to get current file context: file session not found"); return NULL; } } /*Get the current main file context*/ FileContext* get_main_file_context(void *ssnptr) { FileSession *file_session = get_file_session (ssnptr); if (file_session) return file_session->main_context; else { FILE_WARNING("Failed to get main file context: file session not found"); return NULL; } } /*Get the current working file context*/ static inline void save_to_pending_context(void *ssnptr) { FileSession *file_session = get_create_file_session (ssnptr); /* Save to pending_context */ if (!file_session) return; if (file_session->main_context) { if (file_session->pending_context != file_session->main_context) file_context_free(file_session->pending_context); file_session->pending_context = file_session->main_context; } else { file_session->pending_context = file_session->current_context; } } /*Set the current working file context*/ bool set_current_file_context(void *ssnptr, FileContext *ctx) { FileSession *file_session = get_create_file_session (ssnptr); if (!file_session) { FILE_WARNING("Failed to set current file context: file session not found"); return false; } file_session->current_context = ctx; return true; } static void file_session_free(void *session_data) { FileSession *file_session = (FileSession *)session_data; if (!file_session) return; /*Clean up all the file contexts*/ if (file_session->main_context) { if ( file_session->pending_context && (file_session->main_context != file_session->pending_context)) { file_context_free(file_session->pending_context); } file_context_free(file_session->main_context); } SnortPreprocFree(file_session, sizeof(FileSession), PP_FILE, PP_MEM_CATEGORY_SESSION); } static void init_file_context(void *ssnptr, bool upload, FileContext *context) { context->file_type_enabled = cur_config.file_type_id_enabled; context->file_signature_enabled = cur_config.file_signature_enabled; context->file_capture_enabled = cur_config.file_capture_enabled; context->file_config = snort_conf->file_config; context->file_config_version = file_config_version; context->smb_unknown_file_size = false; context->partial_file = false; context->attached_file_entry = NULL; file_direction_set(context,upload); file_stats.files_total++; #ifdef TARGET_BASED /* Check file policy to see whether we want to do either file type, file * signature, or file capture * Note: this happen only on the start of session*/ if (cur_config.file_policy_cb) { uint32_t policy_flags = 0; context->app_id = session_api->get_application_protocol_id(ssnptr); policy_flags = cur_config.file_policy_cb(ssnptr, context->app_id, upload); if ( !file_type_force && !(policy_flags & ENABLE_FILE_TYPE_IDENTIFICATION) ) context->file_type_enabled = false; if ( !(policy_flags & ENABLE_FILE_SIGNATURE_SHA256) ) context->file_signature_enabled = false; if ( !(policy_flags & ENABLE_FILE_CAPTURE) ) context->file_capture_enabled = false; } #endif } FileContext* create_file_context(void *ssnptr) { FileContext *context = file_context_create(); /* Create file session if not yet*/ get_create_file_session (ssnptr); FILE_DEBUG("Successfully created file context %p",context); return context; } static inline FileContext* find_main_file_context(void* p, FilePosition position, bool upload) { FileContext* context = NULL; Packet *pkt = (Packet *)p; void *ssnptr = pkt->ssnptr; FileSession *file_session = get_file_session (ssnptr); /* Attempt to get a previously allocated context. */ if (file_session) context = file_session->main_context; if (context && (((position == SNORT_FILE_MIDDLE) || (position == SNORT_FILE_END)) || ((context->partial_file) && (SNORT_FILE_START == position)))) return context; else if (context) { /*Push file event when there is another file in the same packet*/ if (pkt->packet_flags & PKT_FILE_EVENT_SET) { SnortEventqLog(snort_conf->event_queue, p); SnortEventqReset(); pkt->packet_flags &= ~PKT_FILE_EVENT_SET; } if (context->verdict != FILE_VERDICT_PENDING) { /* Reuse the same context */ file_context_reset(context); init_file_context(ssnptr, upload, context); context->file_id = file_session->max_file_id++; FILE_DEBUG("Reusing existing context from last session"); return context; } } context = create_file_context(ssnptr); file_session = get_create_file_session (ssnptr); file_session->main_context = context; init_file_context(ssnptr, upload, context); context->file_id = file_session->max_file_id++; return context; } static inline void updateFileSize(FileContext* context, int data_size, FilePosition position) { context->processed_bytes += data_size; if ((position == SNORT_FILE_END) || (position == SNORT_FILE_FULL) || (context->file_state.sig_state == FILE_SIG_FLUSH)) { if (get_max_file_depth(snort_conf, false) == (int64_t)context->processed_bytes) context->file_size = 0; else context->file_size = context->processed_bytes; } FILE_DEBUG("Processed bytes: %u, Updated file size is: %u " ,context->processed_bytes, context->file_size); if((SNORT_FILE_FULL == position) || (SNORT_FILE_END == position)) { context->processed_bytes = 0; } } int file_eventq_add(uint32_t gid, uint32_t sid, char *msg, RuleType type) { OptTreeNode *otn; RuleTreeNode *rtn; otn = GetApplicableOtn(gid, sid, 1, 0, 3, msg); if (otn == NULL) { FILE_ERROR("Failed to add event: no otn"); return 0; } rtn = getRtnFromOtn(otn, getIpsRuntimePolicy()); if (rtn == NULL) { FILE_ERROR("Failed to add event: no rtn"); return 0; } rtn->type = type; return SnortEventqAdd(gid, sid, 1, 0, 3, msg, otn); } static inline void add_file_to_block(Packet *p, FileContext* context, bool signature_available) { uint8_t *buf = NULL; uint32_t len = 0; uint32_t type = 0; uint32_t file_sig = 0; uint8_t* signature = signature_available ? context->sha256 : NULL; Packet *pkt = (Packet *)p; FileConfig *file_config = (FileConfig *)(snort_conf->file_config); Active_ForceDropPacket(); DisableAllDetect( p ); pkt->packet_flags |= PKT_FILE_EVENT_SET; /*Use URI as the identifier for file*/ if (GetHttpUriData(p->ssnptr, &buf, &len, &type)) { file_sig = str_to_hash(buf, len); file_resume_block_add_file(p, file_sig, (uint32_t)file_config->file_block_timeout, context->verdict, context->file_type_id, signature, 0, 0, true, 0); } /*use the file name for smb2*/ else if(context->attached_file_entry && context->file_name_size > 0) { file_sig = str_to_hash(context->file_name, context->file_name_size); file_resume_block_add_file(p, file_sig, (uint32_t)file_config->file_block_timeout, context->verdict, context->file_type_id, signature, 0, 0, true, 0); /*We cant call file_entry_free directly as that will delete the context, but we still may be using it. So we are unlinking the context from the file entry. this way the context will not be deleted now, but it will be deleted as part of tcp cleanup. As no context is linked to the file entry now, it will be set to do resume check. */ ((FileEntry*)(context->attached_file_entry))->context = NULL; context->attached_file_entry = NULL; } FILE_INFO("File blocked"); if (pkt_trace_enabled) addPktTraceData(VERDICT_REASON_FILE, snprintf(trace_line, MAX_TRACE_LINE, "File Process: %s %s\n", getPktTraceActMsg(), (buf && len)? (char *)buf : "")); else addPktTraceData(VERDICT_REASON_FILE, 0); } /* * Check HTTP partial content header * Return: 1: partial content header * 0: not http partial content header */ static inline int check_http_partial_content(Packet *p) { uint8_t *buf = NULL; uint32_t len = 0; uint32_t type = 0; uint32_t file_sig; const HttpBuffer* hb = GetHttpBuffer(HTTP_BUFFER_STAT_CODE); uint8_t partial_cont = isHttpRespPartialCont(p->ssnptr); int is_not_partial_ret_code = 0; /* Not partial content, return */ if (hb) { if (hb->length != 3) { is_not_partial_ret_code = 1; } else { is_not_partial_ret_code = strncmp((const char*)hb->buf, "206", 3); } if (((is_not_partial_ret_code) && !(partial_cont &= PARTIAL_CONTENT)) || ((!is_not_partial_ret_code) && (partial_cont &= FULL_CONTENT))) { return 0; } } else if (!(partial_cont &= PARTIAL_CONTENT)) { return 0; } /*Use URI as the identifier for file*/ if (GetHttpUriData(p->ssnptr, &buf, &len, &type)) { file_sig = str_to_hash(buf, len); file_resume_block_check(p, file_sig); } FILE_DEBUG("HTTP partial content header found"); return 1; } /* File signature lookup at the end of file * File signature callback can be used for malware lookup, file capture etc */ static inline void _file_signature_lookup(FileContext* context, void* p, bool is_retransmit, bool suspend_block_verdict) { File_Verdict verdict = FILE_VERDICT_UNKNOWN; Packet *pkt = (Packet *)p; void *ssnptr = pkt->ssnptr; if (cur_config.file_signature_cb) { FILE_DEBUG("Doing file signature callback..."); verdict = cur_config.file_signature_cb(p, ssnptr, context->sha256, context->file_size, &(context->file_state), context->upload, context->file_id, context->partial_file); if(context->file_state.sig_state != FILE_SIG_FLUSH) file_stats.verdicts_signature[verdict]++; } FILE_INFO("File Signature lookup verdict: %d", verdict); if (suspend_block_verdict) context->suspend_block_verdict = true; context->verdict = verdict; if ((verdict == FILE_VERDICT_LOG ) && (context->file_state.sig_state != FILE_SIG_FLUSH)) { file_eventq_add(GENERATOR_FILE_SIGNATURE, FILE_SIGNATURE_SHA256, FILE_SIGNATURE_SHA256_STR, RULE_TYPE__ALERT); pkt->packet_flags |= PKT_FILE_EVENT_SET; context->file_signature_enabled = false; } else if (verdict == FILE_VERDICT_PENDING) { /*Can't decide verdict, drop packet and waiting...*/ if (is_retransmit) { FileConfig *file_config = (FileConfig *)context->file_config; /*Drop packets if not timeout*/ if (pkt->pkth->ts.tv_sec <= context->expires) { if( !Active_DAQRetryPacket(pkt) ) Active_ForceDropPacket(); if (pkt_trace_enabled) { addPktTraceData(VERDICT_REASON_FILE, snprintf(trace_line, MAX_TRACE_LINE, "File Process: malware detected, gid %u, sid %u, %s\n", GENERATOR_FILE_SIGNATURE, FILE_SIGNATURE_SHA256, getPktTraceActMsg())); } else addPktTraceData(VERDICT_REASON_FILE, 0); FILE_INFO("Malware detected"); return; } /*Timeout, let packet go through OR block based on config*/ context->file_signature_enabled = false; if (pkt_trace_enabled) { addPktTraceData(VERDICT_REASON_FILE, snprintf(trace_line, MAX_TRACE_LINE, "File Process: file signature lookup verdict pending timeout, %s\n", getPktTraceActMsg())); } else addPktTraceData(VERDICT_REASON_FILE, 0); if (file_config && file_config->block_timeout_lookup) file_eventq_add(GENERATOR_FILE_SIGNATURE, FILE_SIGNATURE_SHA256, FILE_SIGNATURE_SHA256_STR, RULE_TYPE__REJECT); else file_eventq_add(GENERATOR_FILE_SIGNATURE, FILE_SIGNATURE_SHA256, FILE_SIGNATURE_SHA256_STR, RULE_TYPE__ALERT); pkt->packet_flags |= PKT_FILE_EVENT_SET; } else { FileConfig *file_config = (FileConfig *)context->file_config; if (file_config) context->expires = (time_t)(file_config->file_lookup_timeout + pkt->pkth->ts.tv_sec); if( !Active_DAQRetryPacket(pkt) ) Active_ForceDropPacket(); if (pkt_trace_enabled) { addPktTraceData(VERDICT_REASON_FILE, snprintf(trace_line, MAX_TRACE_LINE, "File Process: can't decide verdict and waiting, %s\n", getPktTraceActMsg())); } else addPktTraceData(VERDICT_REASON_FILE, 0); if (!context->suspend_block_verdict) stream_api->set_event_handler(ssnptr, s_cb_id, SE_REXMIT); save_to_pending_context(ssnptr); return; } } else if ((verdict == FILE_VERDICT_BLOCK) || (verdict == FILE_VERDICT_REJECT)) { if (!context->suspend_block_verdict) render_block_verdict(context, p); context->file_signature_enabled = false; return; } if(context->file_state.sig_state != FILE_SIG_FLUSH) finish_signature_lookup(context); } static inline void finish_signature_lookup(FileContext *context) { if (context->sha256) { context->file_signature_enabled = false; file_capture_stop(context); file_stats.signatures_processed[context->file_type_id][context->upload]++; #ifdef TARGET_BASED file_stats.signatures_by_proto[context->app_id]++; #endif } } static File_Verdict get_file_verdict(void *ssnptr) { FileContext *context = get_current_file_context(ssnptr); if (context == NULL) return FILE_VERDICT_UNKNOWN; return context->verdict; } static void render_block_verdict(void *ctx, void *p) { FileContext *context = (FileContext *)ctx; Packet *pkt = (Packet *)p; SAVE_DAQ_PKT_HDR(p); if (p == NULL) return; if (context == NULL) { context = get_current_file_context(pkt->ssnptr); if (context == NULL) return; } if (context->verdict == FILE_VERDICT_BLOCK) { file_eventq_add(GENERATOR_FILE_SIGNATURE, FILE_SIGNATURE_SHA256, FILE_SIGNATURE_SHA256_STR, RULE_TYPE__DROP); add_file_to_block(p, context, true); } else if (context->verdict == FILE_VERDICT_REJECT) { file_eventq_add(GENERATOR_FILE_SIGNATURE, FILE_SIGNATURE_SHA256, FILE_SIGNATURE_SHA256_STR, RULE_TYPE__REJECT); add_file_to_block(p, context, true); } finish_signature_lookup(context); } static uint32_t get_file_type_id(void *ssnptr) { // NOTE: 'ssnptr' NULL checked in get_application_data FileContext *context = get_current_file_context(ssnptr); if ( !context ) return FILE_VERDICT_UNKNOWN; return context->file_type_id; } static uint32_t get_new_file_instance(void *ssnptr) { FileSession *file_session = get_create_file_session (ssnptr); if (file_session) { return file_session->max_file_id++; } else { return 0; } } static void file_signature_lookup(void* p, bool is_retransmit) { Packet *pkt = (Packet *)p; SAVE_DAQ_PKT_HDR(p); FileContext* context = get_current_file_context(pkt->ssnptr); if (context && context->file_signature_enabled && context->sha256) { _file_signature_lookup(context, p, is_retransmit, false); } } static void file_signature_callback(Packet* p) { /* During retransmission */ Packet *pkt = (Packet *)p; void *ssnptr = pkt->ssnptr; FileSession *file_session; FileEntry *fileEntry; SAVE_DAQ_PKT_HDR(p); if (!ssnptr) { FILE_ERROR("Signature callback failed: no session"); return; } file_session = get_file_session (ssnptr); if (!file_session) { FILE_ERROR("Signature callback failed: no file session"); return; } if(file_session->file_cache) { fileEntry = file_cache_get(file_session->file_cache, p, file_session->file_id, false); if (fileEntry && fileEntry->context && (fileEntry->context->verdict == FILE_VERDICT_PENDING)) { file_session->current_context = fileEntry->context; file_signature_lookup(p, 1); } } else { if(file_session->pending_context) { file_session->current_context = file_session->pending_context; } file_signature_lookup(p, 1); } } static bool is_file_service_enabled() { return (cur_config.file_type_id_enabled || cur_config.file_signature_enabled); } /* * Return: * 1: continue processing/log/block this file * 0: ignore this file */ static int process_file_context(FileContext *context, void *p, uint8_t *file_data, int data_size, FilePosition position, bool suspend_block_verdict) { Packet *pkt = (Packet *)p; void *ssnptr = pkt->ssnptr; bool file_capture_enabled = false; SAVE_DAQ_PKT_HDR(p); if (!context) return 0; file_capture_enabled = context->file_capture_enabled; set_current_file_context(ssnptr, context); file_stats.file_data_total += data_size; /* if file config is changed, update it*/ if ((context->file_config != snort_conf->file_config) || (context->file_config_version != file_config_version)) { context->file_config = snort_conf->file_config; context->file_config_version = file_config_version; /* Reset file type context that relies on file_conf. * File type id will become UNKNOWN after file_type_id() * if in the middle of file and file type is CONTINUE (undecided) */ context->file_type_context = NULL; FILE_DEBUG("Updated file config."); } if ((!context->file_type_enabled) && (!context->file_signature_enabled)) { updateFileSize(context, data_size, position); FILE_DEBUG("Signature and Type lookup not enabled"); return 0; } if(check_http_partial_content(p)) { context->file_type_enabled = false; context->file_signature_enabled = false; return 0; } /*file type id*/ if (context->file_type_enabled) { File_Verdict verdict = FILE_VERDICT_UNKNOWN; file_type_id(context, file_data, data_size, position); FILE_DEBUG("File type ID: %u", context->file_type_id); /*Don't care unknown file type*/ if (context->file_type_id == SNORT_FILE_TYPE_UNKNOWN) { context->file_type_enabled = false; context->file_signature_enabled = false; updateFileSize(context, data_size, position); file_capture_stop(context); return 0; } if (context->file_type_id != SNORT_FILE_TYPE_CONTINUE) { if (cur_config.file_type_cb) { FILE_DEBUG("Doing file type callback..."); verdict = cur_config.file_type_cb(p, ssnptr, context->file_type_id, context->upload, context->file_id); FILE_INFO("File type verdict: %d",verdict); file_stats.verdicts_type[verdict]++; context->verdict = verdict; } context->file_type_enabled = false; file_stats.files_processed[context->file_type_id][context->upload]++; #ifdef TARGET_BASED file_stats.files_by_proto[context->app_id]++; #endif } if (verdict == FILE_VERDICT_LOG ) { file_eventq_add(GENERATOR_FILE_TYPE, context->file_type_id, file_type_name(context->file_config, context->file_type_id), RULE_TYPE__ALERT); context->file_signature_enabled = false; pkt->packet_flags |= PKT_FILE_EVENT_SET; } else if (verdict == FILE_VERDICT_BLOCK) { file_eventq_add(GENERATOR_FILE_TYPE, context->file_type_id, file_type_name(context->file_config, context->file_type_id), RULE_TYPE__DROP); updateFileSize(context, data_size, position); context->file_signature_enabled = false; add_file_to_block(p, context, false); return 1; } else if (verdict == FILE_VERDICT_REJECT) { file_eventq_add(GENERATOR_FILE_TYPE, context->file_type_id, file_type_name(context->file_config, context->file_type_id), RULE_TYPE__REJECT); updateFileSize(context, data_size, position); context->file_signature_enabled = false; add_file_to_block(p, context, false); return 1; } else if (verdict == FILE_VERDICT_STOP) { context->file_signature_enabled = false; } else if (verdict == FILE_VERDICT_STOP_CAPTURE) { file_capture_stop(context); } } /* file signature calculation */ if (context->file_signature_enabled) { if (!context->sha256) file_signature_sha256(context, file_data, data_size, position); file_stats.data_processed[context->file_type_id][context->upload] += data_size; updateFileSize(context, data_size, position); /*Fails to capture, when out of memory or size limit, need lookup*/ if (context->file_capture_enabled && file_capture_process(context, file_data, data_size, position)) { file_capture_stop(context); _file_signature_lookup(context, p, false, suspend_block_verdict); if (context->verdict != FILE_VERDICT_UNKNOWN) return 1; } /*Either get SHA or exceeding the SHA limit, need lookup*/ if (context->file_state.sig_state == FILE_SIG_DEPTH_FAIL) { file_stats.files_sig_depth++; _file_signature_lookup(context, p, false, suspend_block_verdict); } else if ((context->file_state.sig_state == FILE_SIG_DONE) && isFileEnd(position)) { FILE_REG_DEBUG_WRAP(if (context->sha256) file_sha256_print(context->sha256);) _file_signature_lookup(context, p, false, suspend_block_verdict); } else if(context->file_state.sig_state == FILE_SIG_FLUSH) { _file_signature_lookup(context, p, false, suspend_block_verdict); context->file_signature_enabled = true; context->file_capture_enabled = file_capture_enabled; if((context->verdict == FILE_VERDICT_BLOCK) || (context->verdict == FILE_VERDICT_REJECT)) { FILE_REG_DEBUG_WRAP(if (context->sha256) file_sha256_print(context->sha256);) } } } else { updateFileSize(context, data_size, position); } return 1; } /* * Return: * 1: continue processing/log/block this file * 0: ignore this file */ static int file_process( void* p, uint8_t* file_data, int data_size, FilePosition position, bool upload, bool suspend_block_verdict, bool do_flush) { FileContext* context; SAVE_DAQ_PKT_HDR(p); int fileverdict; #if defined(DAQ_VERSION) && DAQ_VERSION > 9 uint64_t start = 0, end = 0; #endif FILE_DEBUG("Processing file data:: size:%d, position:%d, direction:%d, flush:%d",data_size,position,upload,do_flush); /* if both disabled, return immediately*/ if (!is_file_service_enabled()) { FILE_DEBUG("File service not enabled."); return 0; } if (position == SNORT_FILE_POSITION_UNKNOWN) return 0; FILE_REG_DEBUG_WRAP(DumpHexFile(stdout, file_data, data_size);) context = find_main_file_context(p, position, upload); if((context->file_state.sig_state == FILE_SIG_FLUSH) && context->sha256) { SnortPreprocFree(context->sha256, sizeof(SHA256_HASH_SIZE), PP_FILE, PP_MEM_CATEGORY_SESSION); context->sha256 = NULL; } if(do_flush) context->file_state.sig_state = FILE_SIG_FLUSH; else { context->file_state.sig_state = FILE_SIG_PROCESSING; } #if defined(DAQ_VERSION) && DAQ_VERSION > 9 Packet *pkt = (Packet *)p; if ( pkt && pkt->pkth && (pkt->pkth->flags & DAQ_PKT_FLAG_DEBUG_ON)) { get_clockticks(start); fileverdict = process_file_context(context, p, file_data, data_size, position,suspend_block_verdict); get_clockticks(end); print_flow(p,"PROCESS_FILE_CONTEXT",0,start,end); } else fileverdict = process_file_context(context, p, file_data, data_size, position,suspend_block_verdict); #else fileverdict = process_file_context(context, p, file_data, data_size, position,suspend_block_verdict); #endif return fileverdict; } static void set_file_name (void* ssnptr, uint8_t* fname, uint32_t name_size, bool save_in_context) { FileContext* context = get_current_file_context(ssnptr); file_name_set(context, fname, name_size, save_in_context); FILE_REG_DEBUG_WRAP(printFileContext(context);) } /* * set_file_partial API, used to mark a file partial/incomplete. * This information is required by FW signature lookup for FTP PP * See CSCvi28409 for more details. */ static void set_file_partial(void *p, FilePosition position,bool upload, bool is_partial) { SAVE_DAQ_PKT_HDR(p); FileContext *context = find_main_file_context(p,position,upload); FILE_DEBUG("Partial file: %d",is_partial); context->partial_file = is_partial; } /* Return 1: file name available, * 0: file name is unavailable */ static int get_file_name (void* ssnptr, uint8_t **fname, uint32_t *name_size) { return file_name_get(get_current_file_context(ssnptr), fname, name_size); } static uint64_t get_file_size(void* ssnptr) { return file_size_get(get_current_file_context(ssnptr)); } static uint64_t get_file_processed_size(void* ssnptr) { FileContext *context = get_main_file_context(ssnptr); if (context) return (context->processed_bytes); else return 0; } static void set_file_direction(void* ssnptr, bool upload) { file_direction_set(get_current_file_context(ssnptr),upload); } static bool get_file_direction(void* ssnptr) { return file_direction_get(get_current_file_context(ssnptr)); } static uint8_t *get_file_sig_sha256(void* ssnptr) { return file_sig_sha256_get(get_current_file_context(ssnptr)); } static void set_file_policy_callback(File_policy_callback_func policy_func_cb) { new_config.file_policy_cb = policy_func_cb; } /* * - Only accepts 1 (ONE) callback being registered. * * - Call with NULL callback to "force" (guarantee) file type identification. * * TBD: Remove per-context "file_type_enabled" checking to simplify implementation. * */ static void enable_file_type(struct _SnortConfig* sc, File_type_callback_func callback) { new_config.file_type_id_enabled = true; start_file_processing(sc, false); LogMessage("File service: file type enabled.\n"); if (!callback) { file_type_force = true; } else if(!new_config.file_type_cb) { new_config.file_type_cb = callback; } else if(new_config.file_type_cb != callback) { FatalError("Attempt to register multiple file_type callbacks."); } } /* set file signature callback function*/ static void enable_file_signature(struct _SnortConfig* sc, File_signature_callback_func callback) { new_config.file_signature_enabled = true; start_file_processing(sc, false); if(!new_config.file_signature_cb) { new_config.file_signature_cb = callback; LogMessage("File service: file signature enabled.\n"); } else if(new_config.file_signature_cb != callback) { WarningMessage("File service: signature callback redefined.\n"); } } /* Enable file capture, also enable file signature */ static void enable_file_capture(struct _SnortConfig* sc, File_signature_callback_func callback) { new_config.file_capture_enabled = true; LogMessage("File service: file capture enabled.\n"); start_file_processing(sc, true); /* Enable file signature*/ enable_file_signature(sc, callback); } static void set_file_action_log_callback(Log_file_action_func log_func) { new_config.log_file_action = log_func; } /* Get maximal file depth based on configuration * This function must be called after all file services are configured/enabled. */ static int64_t get_max_file_depth(struct _SnortConfig *sc, bool next) { FileConfig *file_config; FileServiceConfig *fs_config; if (!sc) sc = snort_conf; file_config = (FileConfig *)(sc->file_config); fs_config = next ? &new_config:&cur_config; if (!file_config) return -1; /* If next is set, proceed further to check the depth */ if (!next && file_config->file_depth) return file_config->file_depth; file_config->file_depth = -1; if (fs_config->file_type_id_enabled) { file_config->file_depth = file_config->file_type_depth; } if (fs_config->file_signature_enabled) { if (file_config->file_signature_depth > file_config->file_depth) file_config->file_depth = file_config->file_signature_depth; } if (file_config->file_depth > 0) { /*Extra byte for deciding whether file data will be over limit*/ file_config->file_depth++; return (file_config->file_depth); } else { return -1; } } static bool is_file_signature_enabled() { return cur_config.file_signature_enabled; } static FilePosition get_file_position(void *pkt) { FilePosition position = SNORT_FILE_POSITION_UNKNOWN; Packet *p = (Packet *)pkt; SAVE_DAQ_PKT_HDR(p); if(ScPafEnabled()) { if (PacketHasFullPDU(p)) position = SNORT_FILE_FULL; else if (PacketHasStartOfPDU(p)) position = SNORT_FILE_START; else if (p->packet_flags & PKT_PDU_TAIL) position = SNORT_FILE_END; else if (get_file_processed_size(p->ssnptr)) position = SNORT_FILE_MIDDLE; } return position; } /* * This function determines whether we shold abort PAF. Will return * true if the current packet is midstream, or unestablisted session * * PARAMS: * uint32_t - session flags passed in to callback. * * RETURNS: * true - if we should abort paf * false - if we should continue using paf */ static bool check_paf_abort(void* ssn) { uint32_t flags = session_api->get_session_flags(ssn); if (flags & SSNFLAG_MIDSTREAM) { FILE_DEBUG("Aborting PAF because of midstream pickup."); return true; } else if (!(flags & SSNFLAG_ESTABLISHED)) { FILE_DEBUG("Aborting PAF because of unestablished session."); return true; } return false; } static int64_t get_max_file_capture_size(void *ssn) { if (snort_conf->file_config) return snort_conf->file_config->file_capture_max_size; return 0; } static uint32_t str_to_hash(uint8_t *str, int length ) { uint32_t a,b,c,tmp; int i,j,k,l; a = b = c = 0; for (i=0,j=0;i 4) k=4; for (l=0;l UTF_16_LE_BOM_LEN) { if(memcmp(buffer, UTF_16_LE_BOM, UTF_16_LE_BOM_LEN) == 0) encoding = SNORT_CHAR_ENCODING_UTF_16LE; else if(memcmp(buffer, UTF_16_BE_BOM, UTF_16_BE_BOM_LEN) == 0) encoding = SNORT_CHAR_ENCODING_UTF_16BE; } return encoding; } /* It generates the file event if event logging is enabled. */ void file_event_log_dump(FileCache *fileCache, void* p, uint64_t file_id) { FileEntry *fileEntry; fileEntry = file_cache_get(fileCache, p, file_id, true); if (NULL != fileEntry && fileEntry->context) { Packet *pkt = (Packet *)p; if (FILE_VERDICT_LOG == fileEntry->context->verdict && !(pkt->packet_flags & PKT_FILE_EVENT_SET)) { file_eventq_add(GENERATOR_FILE_SIGNATURE, FILE_SIGNATURE_SHA256, FILE_SIGNATURE_SHA256_STR, RULE_TYPE__ALERT); pkt->packet_flags |= PKT_FILE_EVENT_SET; fileEntry->context->file_signature_enabled = false; } } } /* file_signature_reset API to restore the file detection state. * This is done because once we flush and we get a cloud verdict, all the detection states are erased. * To ensure that we continue the detection SSL has to use this API */ static void file_signature_reset (void *ssnptr) { FileContext* context = NULL; FileSession *file_session = get_file_session (ssnptr); if (file_session) context = file_session->main_context; if (!context) { return; } if (context->file_state.sig_state == FILE_SIG_FLUSH) { context->file_signature_enabled = true; context->file_state.sig_state = FILE_SIG_PROCESSING; context->verdict = FILE_VERDICT_UNKNOWN; if (context->sha256) { SnortPreprocFree(context->sha256, sizeof(SHA256_HASH_SIZE), PP_FILE, PP_MEM_CATEGORY_SESSION); context->sha256 = NULL; } } return; } static char* file_get_filetype (void *ssnptr) { FileContext *context = get_current_file_context(ssnptr); if (!context) { return NULL; } #ifdef TARGET_BASED if (cur_config.file_policy_cb) { bool policy_flags; policy_flags = cur_config.file_policy_cb(ssnptr, context->app_id, context->upload); if (!(policy_flags & ENABLE_FILE_TYPE_IDENTIFICATION)) { return NULL; } } #endif return file_type_name (context->file_config, context->file_type_id); } snort-2.9.20/src/file-process/file_mempool.h0000644000175000017500000000701614241076546017110 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** This mempool implementation has very efficient alloc/free operations. ** In addition, it provides thread-safe alloc/free for one allocation/free ** thread and one release thread. ** One more bonus: Double free detection is also added into this library ** ** NOTES ** 5.25.13 - Initial Source Code. Hcao ** ** This is a thread safe version of memory pool for one writer and one reader thread */ #ifndef __FILE_MEMPOOL_H__ #define __FILE_MEMPOOL_H__ #include "sf_types.h" #include "circular_buffer.h" #define SAFE_MEM_SUCCESS 0 #define SAFE_MEM_FAIL -1 typedef struct _SafeMemPool { void **datapool; /* memory buffer */ uint64_t total; CircularBuffer* free_list; CircularBuffer* released_list; size_t obj_size; } SafeMemPool; /* Initialize mempool * * Args: * SafeMemPool: pointer to a SafeMemPool struct * uint64_t num_objects: number of objects * size_t obj_size: size of object * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_init(SafeMemPool *mempool, uint64_t num_objects, size_t obj_size); /* Free mempool memory objects * * Args: * SafeMemPool: pointer to a SafeMemPool struct * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_destroy(SafeMemPool *mempool); /* * Allocate a new object from the SafeMemPool * Memory block will not be zeroed for performance * * Args: * SafeMemPool: pointer to a SafeMemPool struct * * Returns: a pointer to the SafeMemPool object on success, NULL on failure */ void *safe_mempool_alloc(SafeMemPool *mempool); /* * Free a new object from the SafeMemPool * This must be called by the same thread calling * safe_mempool_alloc() * * Args: * SafeMemPool: pointer to a SafeMemPool struct * void *obj : memory object * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_free(SafeMemPool *mempool, void *obj); /* * Release a new object from the SafeMemPool * This can be called by a different thread calling * safe_mempool_alloc() * * Args: * SafeMemPool: pointer to a SafeMemPool struct * void *obj : memory object * * Return: * SAFE_MEM_SUCCESS * SAFE_MEM_FAIL */ int safe_mempool_release(SafeMemPool *mempool, void *obj); /* Returns number of elements allocated in current buffer*/ uint64_t safe_mempool_allocated(SafeMemPool *mempool); /* Returns number of elements freed in current buffer*/ uint64_t safe_mempool_freed(SafeMemPool *mempool); /* Returns number of elements released in current buffer*/ uint64_t safe_mempool_released(SafeMemPool *mempool); #endif snort-2.9.20/src/file-process/file_mail_common.h0000644000175000017500000001150714241076544017730 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * ** Copyright (C) 2012-2013 Sourcefire, Inc. * ** AUTHOR: Hui Cao * ** * ** This program is free software; you can redistribute it and/or modify * ** it under the terms of the GNU General Public License Version 2 as * ** published by the Free Software Foundation. You may not use, modify or * ** distribute this program under any other version of the GNU General * ** Public License. * ** * ** 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. * */ /* * Purpose: To be used for mail protocols and mime processing * * Author(s): Hui Cao * */ #ifndef FILE_MAIL_COMMON_H_ #define FILE_MAIL_COMMON_H_ #ifdef HAVE_CONFIG_H #include #endif #include #include "sfPolicy.h" typedef struct s_FILE_LogState { uint8_t *filenames; uint16_t file_logged; uint16_t file_current; uint16_t file_name; } FILE_LogState; typedef struct s_MAIL_LogState { void *log_hdrs_bkt; unsigned char *emailHdrs; uint32_t log_depth; uint32_t hdrs_logged; uint8_t *recipients; uint16_t rcpts_logged; uint8_t *senders; uint16_t snds_logged; FILE_LogState file_log; }MAIL_LogState; /* log flags */ #define FLAG_MAIL_FROM_PRESENT 0x00000001 #define FLAG_RCPT_TO_PRESENT 0x00000002 #define FLAG_FILENAME_PRESENT 0x00000004 #define FLAG_EMAIL_HDRS_PRESENT 0x00000008 #define FLAG_FILENAME_IN_HEADER 0x00000010 typedef struct s_MAIL_LogConfig { uint32_t memcap; char log_mailfrom; char log_rcptto; char log_filename; char log_email_hdrs; uint32_t email_hdrs_log_depth; }MAIL_LogConfig; /* State tracker for data */ typedef enum _MimeDataState { MIME_PAF_FINDING_BOUNDARY_STATE, MIME_PAF_FOUND_BOUNDARY_STATE } MimeDataState; /* State tracker for Boundary Signature */ typedef enum _MimeBoundaryState { MIME_PAF_BOUNDARY_UNKNOWN = 0, /* UNKNOWN */ MIME_PAF_BOUNDARY_LF, /* '\n' */ MIME_PAF_BOUNDARY_HYPEN_FIRST, /* First '-' */ MIME_PAF_BOUNDARY_HYPEN_SECOND /* Second '-' */ } MimeBoundaryState; /* State tracker for end of pop/smtp command */ typedef enum _DataEndState { PAF_DATA_END_UNKNOWN, /* Start or UNKNOWN */ PAF_DATA_END_FIRST_CR, /* First '\r' */ PAF_DATA_END_FIRST_LF, /* First '\n' */ PAF_DATA_END_DOT, /* '.' */ PAF_DATA_END_SECOND_CR, /* Second '\r' */ PAF_DATA_END_SECOND_LF /* Second '\n' */ } DataEndState; #define MAX_BOUNDARY_LEN 70 /* Max length of boundary string, defined in RFC 2046 */ typedef struct _MimeDataPafInfo { MimeDataState data_state; char boundary[ MAX_BOUNDARY_LEN + 1]; /* MIME boundary string + '\0' */ uint32_t boundary_len; char* boundary_search; MimeBoundaryState boundary_state; } MimeDataPafInfo; typedef int (*Handle_header_line_func)(void *pkt, const uint8_t *ptr, const uint8_t *eol, int max_header_len, void *mime_ssn); typedef int (*Normalize_data_func)(void *pkt, const uint8_t *ptr, const uint8_t *data_end); typedef void (*Decode_alert_func)(void *decode_state); typedef void (*Reset_state_func)(void); typedef bool (*Is_end_of_data_func)(void* ssn); typedef struct _MimeMethods { Handle_header_line_func handle_header_line; Normalize_data_func normalize_data; Decode_alert_func decode_alert; Reset_state_func reset_state; Is_end_of_data_func is_end_of_data; } MimeMethods; typedef struct _DecodeConfig { char ignore_data; int max_mime_mem; int max_depth; int b64_depth; int qp_depth; int bitenc_depth; int uu_depth; int64_t file_depth; } DecodeConfig; typedef struct _MimeState { int data_state; int state_flags; int log_flags; void *decode_state; MimeDataPafInfo mime_boundary; DecodeConfig *decode_conf; MAIL_LogConfig *log_config; MAIL_LogState *log_state; void *mime_stats; void *decode_bkt; void *mime_mempool; void *log_mempool; MimeMethods *methods; } MimeState; static inline bool scanning_boundary(MimeDataPafInfo *mime_info, uint32_t boundary_start, uint32_t* fp) { if (boundary_start && mime_info->data_state == MIME_PAF_FOUND_BOUNDARY_STATE && mime_info->boundary_state != MIME_PAF_BOUNDARY_UNKNOWN) { *fp = boundary_start; return true; } return false; } #endif /* FILE_MAIL_COMMON_H_ */ snort-2.9.20/src/file-process/file_mime_config.c0000644000175000017500000003177114241076547017715 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "file_mail_common.h" #include "file_mime_config.h" #include "file_api.h" #include "sf_email_attach_decode.h" #include "util.h" #include "parser.h" #define CONF_SEPARATORS " \t\n\r" #define CONF_MAX_MIME_MEM "max_mime_mem" #define CONF_B64_DECODE "b64_decode_depth" #define CONF_QP_DECODE "qp_decode_depth" #define CONF_BITENC_DECODE "bitenc_decode_depth" #define CONF_UU_DECODE "uu_decode_depth" extern char *file_name; extern int file_line; /*These are temporary values*/ #define DEFAULT_MAX_MIME_MEM 838860 #define DEFAULT_MIME_MEMCAP 838860 #define DEFAULT_DEPTH 1460 #define MAX_LOG_MEMCAP 104857600 #define MIN_LOG_MEMCAP 3276 #define MAX_MIME_MEM 104857600 #define MIN_MIME_MEM 3276 #define MAX_DEPTH 65535 #define MIN_DEPTH -1 #define ERRSTRLEN 512 static int ProcessDecodeDepth(DecodeConfig *config, char *ErrorString, int ErrStrLen, char *decode_type, DecodeType type, const char *preproc_name, char **strtok_saveptr) { char *endptr; char *value; int decode_depth = 0; int neg = 0, pos = 0; if (config == NULL) { snprintf(ErrorString, ErrStrLen, "%s config is NULL.\n", preproc_name); return -1; } value = strtok_r(NULL, CONF_SEPARATORS, strtok_saveptr); if ( value == NULL ) { snprintf(ErrorString, ErrStrLen, "Invalid format for %s config option '%s'.", preproc_name, decode_type); return -1; } while (isspace(*value)) value++; neg = (*value == '-'); pos = (*value == '+'); decode_depth = strtol(value, &endptr, 10); if(*endptr) { snprintf(ErrorString, ErrStrLen, "Invalid format for %s config option '%s'.", preproc_name, decode_type); return -1; } if(decode_depth < MIN_DEPTH || decode_depth > MAX_DEPTH) { snprintf(ErrorString, ErrStrLen, "Invalid value for %s config option '%s'." "It should range between %d and %d.", preproc_name, decode_type, MIN_DEPTH, MAX_DEPTH); return -1; } if(!decode_depth && (pos || neg)) { snprintf(ErrorString, ErrStrLen, "-0 and +0 are invalid values for %s config option '%s'. " "Use 0 for unlimited or a signed integer between %d and %d\n", preproc_name, decode_type, MIN_DEPTH, MAX_DEPTH); return -1; } switch(type) { case DECODE_B64: if((decode_depth > 0) && (decode_depth & 3)) { decode_depth += 4 - (decode_depth & 3); if(decode_depth > MAX_DEPTH ) { decode_depth = decode_depth - 4; } LogMessage("WARNING: %s(%d) => %s: 'b64_decode_depth' is not a multiple of 4. " "Rounding up to the next multiple of 4. The new 'b64_decode_depth' is %d.\n", file_name, file_line, preproc_name, decode_depth); } config->b64_depth = decode_depth; break; case DECODE_QP: config->qp_depth = decode_depth; break; case DECODE_UU: config->uu_depth = decode_depth; break; case DECODE_BITENC: config->bitenc_depth = decode_depth; break; default: return -1; } return 0; } void set_mime_decode_config_defauts(DecodeConfig *decode_conf) { decode_conf->max_mime_mem = DEFAULT_MAX_MIME_MEM; decode_conf->b64_depth = DEFAULT_DEPTH; decode_conf->qp_depth = DEFAULT_DEPTH; decode_conf->uu_depth = DEFAULT_DEPTH; decode_conf->bitenc_depth = DEFAULT_DEPTH; decode_conf->max_depth = DEFAULT_DEPTH; } void set_mime_log_config_defauts(MAIL_LogConfig *log_config) { log_config->memcap = DEFAULT_MIME_MEMCAP; log_config->log_filename = 0; log_config->log_mailfrom = 0; log_config->log_rcptto = 0; log_config->log_email_hdrs = 0; log_config->email_hdrs_log_depth = 0; } bool is_decoding_enabled(DecodeConfig *decode_conf) { if( (decode_conf->b64_depth > -1) || (decode_conf->qp_depth > -1) || (decode_conf->uu_depth > -1) || (decode_conf->bitenc_depth > -1) || (decode_conf->file_depth > -1)) { return true; } else return false; } bool is_mime_log_enabled(MAIL_LogConfig *log_config) { if(log_config->log_email_hdrs || log_config->log_filename || log_config->log_mailfrom || log_config->log_rcptto) return true; return false; } /* * * Purpose: Process the configuration * * Arguments: args => argument list * * Returns: -1: error or not found * 0: no error * */ int parse_mime_decode_args(DecodeConfig *decode_conf, char *arg, const char *preproc_name, char **strtok_saveptr) { int ret = 0; char errStr[ERRSTRLEN]; int errStrLen = ERRSTRLEN; unsigned long value = 0; if ((decode_conf == NULL) || (arg == NULL)) return 0; *errStr = '\0'; if ( !strcasecmp(CONF_MAX_MIME_MEM, arg) ) { ret = CheckValueInRange(strtok_r(NULL, CONF_SEPARATORS, strtok_saveptr), CONF_MAX_MIME_MEM, MIN_MIME_MEM, MAX_MIME_MEM, &value); decode_conf->max_mime_mem = (int)value; } else if ( !strcasecmp(CONF_B64_DECODE, arg) ) { ret = ProcessDecodeDepth(decode_conf, errStr, errStrLen, CONF_B64_DECODE, DECODE_B64, preproc_name, strtok_saveptr); } else if ( !strcasecmp(CONF_QP_DECODE, arg) ) { ret = ProcessDecodeDepth(decode_conf, errStr, errStrLen, CONF_QP_DECODE, DECODE_QP, preproc_name, strtok_saveptr); } else if ( !strcasecmp(CONF_UU_DECODE, arg) ) { ret = ProcessDecodeDepth(decode_conf, errStr, errStrLen, CONF_UU_DECODE, DECODE_UU, preproc_name, strtok_saveptr); } else if ( !strcasecmp(CONF_BITENC_DECODE, arg) ) { ret = ProcessDecodeDepth(decode_conf, errStr, errStrLen, CONF_BITENC_DECODE, DECODE_BITENC, preproc_name, strtok_saveptr); } else { return -1; } if (ret == -1) { /* ** Fatal Error, log error and exit. */ if (*errStr) { FatalError("%s(%d) => %s\n", file_name, file_line, errStr); } else { FatalError("%s(%d) => Undefined Error.\n", file_name, file_line); } } return ret; } bool check_decode_config(DecodeConfig *currentConfig, DecodeConfig *defaultConfig, const char *preproc_name) { int max = -1; if (currentConfig == defaultConfig) { if (!currentConfig->max_mime_mem) currentConfig->max_mime_mem = DEFAULT_MAX_MIME_MEM; if(!currentConfig->b64_depth || !currentConfig->qp_depth || !currentConfig->uu_depth || !currentConfig->bitenc_depth) { currentConfig->max_depth = MAX_DEPTH; return false; } else { if(max < currentConfig->b64_depth) max = currentConfig->b64_depth; if(max < currentConfig->qp_depth) max = currentConfig->qp_depth; if(max < currentConfig->bitenc_depth) max = currentConfig->bitenc_depth; if(max < currentConfig->uu_depth) max = currentConfig->uu_depth; currentConfig->max_depth = max; } } else if (defaultConfig == NULL) { if (currentConfig->max_mime_mem) { FatalError("%s(%d) => %s: max_mime_mem must be " "configured in the default config.\n", file_name, file_line, preproc_name); } if (currentConfig->b64_depth > -1) { FatalError("%s(%d) => %s: b64_decode_depth must be " "configured in the default config.\n", file_name, file_line, preproc_name); } if (currentConfig->qp_depth > -1) { FatalError("%s(%d) => %s: qp_decode_depth must be " "configured in the default config.\n", file_name, file_line, preproc_name); } if (currentConfig->uu_depth > -1) { FatalError("%s(%d) => %s: uu_decode_depth must be " "configured in the default config.\n", file_name, file_line, preproc_name); } if (currentConfig->bitenc_depth > -1) { FatalError("%s(%d) => %s: bitenc_decode_depth must be " "configured in the default config.\n", file_name, file_line, preproc_name); } } else { currentConfig->max_mime_mem = defaultConfig->max_mime_mem; currentConfig->max_depth = defaultConfig->max_depth; if(!currentConfig->b64_depth && defaultConfig->b64_depth) { FatalError("%s(%d) => %s: Cannot enable unlimited Base64 decoding" " in non-default config without turning on unlimited Base64 decoding in the default " " config.\n", file_name, file_line, preproc_name); } else if(defaultConfig->b64_depth && (currentConfig->b64_depth > defaultConfig->b64_depth)) { FatalError("%s(%d) => %s: b64_decode_depth value %d in non-default config" " cannot exceed default config's value %d.\n", file_name, file_line, preproc_name, currentConfig->b64_depth, defaultConfig->b64_depth); } if(!currentConfig->qp_depth && defaultConfig->qp_depth) { FatalError("%s(%d) => %s: Cannot enable unlimited Quoted-Printable decoding" " in non-default config without turning on unlimited Quoted-Printable decoding in the default " " config.\n", file_name, file_line, preproc_name); } else if(defaultConfig->qp_depth && (currentConfig->qp_depth > defaultConfig->qp_depth)) { FatalError("%s(%d) => %s: qp_decode_depth value %d in non-default config" " cannot exceed default config's value %d.\n", file_name, file_line, preproc_name, currentConfig->qp_depth, defaultConfig->qp_depth); } if(!currentConfig->uu_depth && defaultConfig->uu_depth ) { FatalError("%s(%d) => %s: Cannot enable unlimited Unix-to-Unix decoding" " in non-default config without turning on unlimited Unix-to-Unix decoding in the default " " config.\n", file_name, file_line, preproc_name); } else if(defaultConfig->uu_depth && (currentConfig->uu_depth > defaultConfig->uu_depth)) { FatalError("%s(%d) => %s: uu_decode_depth value %d in non-default config" " cannot exceed default config's value %d.\n", file_name, file_line, preproc_name, currentConfig->uu_depth, defaultConfig->uu_depth); } if(!currentConfig->bitenc_depth && defaultConfig->bitenc_depth) { FatalError("%s(%d) => %s: Cannot enable unlimited Non-Encoded MIME attachment extraction" " in non-default config without turning on unlimited Non-Encoded MIME attachment extraction in the default " " config.\n", file_name, file_line, preproc_name); } else if(defaultConfig->bitenc_depth && (currentConfig->bitenc_depth > defaultConfig->bitenc_depth)) { FatalError("%s(%d) => %s: bitenc_decode_depth value %d in non-default config " " cannot exceed default config's value %d.\n", file_name, file_line, preproc_name, currentConfig->bitenc_depth, defaultConfig->bitenc_depth); } } return true; } snort-2.9.20/src/file-process/file_mime_process.h0000644000175000017500000000513314241076553020121 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifndef _FILE_MIME_PROCESS_H_ #define _FILE_MIME_PROCESS_H_ #include "file_api.h" #include "sf_email_attach_decode.h" #include "mempool.h" #include "sfPolicy.h" #include "file_mail_common.h" #include "memory_stats.h" int set_log_buffers(MAIL_LogState **log_state, MAIL_LogConfig *conf, void *mempool, void* scbPtr, uint32_t preproc_id); void* init_mime_mempool(int max_mime_mem, int max_depth, void *mempool, const char *preproc_name); void* init_log_mempool(uint32_t email_hdrs_log_depth, uint32_t memcap, void *mempool, const char *preproc_name); void init_mime(void); void free_mime(void); const uint8_t* process_mime_data(void *packet, const uint8_t *start, const uint8_t *end, MimeState *mime_ssn, bool upload, bool paf_enabled, char *preproc_name, uint32_t preproc_id); void free_mime_session(MimeState *mime_ssn); void finalize_mime_position(void *ssnptr, void *decode_state, FilePosition *position); void force_flush_stream (void *ssn); void reset_mime_paf_state(MimeDataPafInfo *data_info); /* Process data boundary and flush each file based on boundary*/ bool process_mime_paf_data(MimeDataPafInfo *data_info, uint8_t val); bool check_data_end(void *end_state, uint8_t val); #ifdef SNORT_RELOAD void update_mime_mempool(void*, int, int); void update_log_mempool(void*, int, int); #ifdef REG_TEST void displayMimeMempool(void *memory_pool, DecodeConfig *decode_conf_old, DecodeConfig *decode_conf_new); void displayLogMempool(void *memory_pool, unsigned memcap_old, unsigned memcap_new); void displayDecodeDepth(DecodeConfig *decode_conf_old, DecodeConfig *decode_conf_new); #endif #endif #endif snort-2.9.20/src/file-process/file_resume_block.c0000644000175000017500000004277514241076554020117 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "file_resume_block.h" #include "sf_types.h" #include "file_api.h" #include "snort_bounds.h" #include "ipv6_port.h" #include "sfxhash.h" #include "util.h" #include "decode.h" #include "active.h" #include "sf_sechash.h" #include "file_config.h" #include "file_service.h" #include "file_ss.h" #include "sidechannel.h" #include "file_lib.h" /* The hash table of expected files */ static SFXHASH *fileHash = NULL; extern FileServiceConfig cur_config; extern FileContext* get_main_file_context(void *ssnptr); static FileState sig_file_state = { FILE_CAPTURE_SUCCESS, FILE_SIG_DONE }; /* this file_cache_mutex is used to synchronize multiple add's to SFXHASH */ static pthread_mutex_t file_cache_mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct _FileHashKey { struct in6_addr sip; struct in6_addr dip; uint32_t file_sig; } FileHashKey; typedef struct _FileNode { time_t expires; File_Verdict verdict; uint32_t file_type_id; uint8_t sha256[SHA256_HASH_SIZE]; } FileNode; #define MAX_FILES_TRACKED 16384 void file_resume_block_init(void) { /* number of entries * overhead per entry */ unsigned long maxmem = sfxhash_calc_maxmem(MAX_FILES_TRACKED, sizeof(FileHashKey) + sizeof(FileNode)); fileHash = sfxhash_new(MAX_FILES_TRACKED, sizeof(FileHashKey), sizeof(FileNode), maxmem, 1, NULL, NULL, 1); if (!fileHash) FatalError("Failed to create the expected channel hash table.\n"); #ifdef SIDE_CHANNEL FileSSConfigInit(); #endif } void file_resume_block_cleanup(void) { if (fileHash) { pthread_mutex_lock(&file_cache_mutex); sfxhash_delete(fileHash); fileHash = NULL; pthread_mutex_unlock(&file_cache_mutex); } #if defined (SIDE_CHANNEL) && defined (REG_TEST) FileCleanSS(); #endif } static inline void updateFileNode(FileNode *node, File_Verdict verdict, uint32_t file_type_id, uint8_t *signature) { node->verdict = verdict; node->file_type_id = file_type_id; if (signature) { memcpy(node->sha256, signature, SHA256_HASH_SIZE); } } #ifdef SIDE_CHANNEL static int ProduceSSFileCache(FileHashKey *hk, FileNode *hv) { uint32_t offset = 0; void *msg_handle = NULL; void *hdr_ptr = NULL; void *data_ptr = NULL; if (!hk || !hv) { return -1; } if (CreateFileSSUpdate(&msg_handle, &hdr_ptr, &data_ptr, SC_MSG_TYPE_FILE_SS_HOST_CACHE, sizeof(*hk) + sizeof(*hv)) != 0) { FILE_ERROR("Side channel: Failed to create side channel update"); return -1; } if (data_ptr) { memcpy(data_ptr, hk, sizeof(*hk)); offset += sizeof(*hk); memcpy((uint8_t *)data_ptr + offset, hv, sizeof(*hv)); offset += sizeof(*hv); SendFileSSUpdate(msg_handle, hdr_ptr, data_ptr, SC_MSG_TYPE_FILE_SS_HOST_CACHE, offset); } #ifdef REG_TEST LogMessage("produce verdict =%d file id =%d \n",hv->verdict,hv->file_type_id); file_sha256_print(hv->sha256); #endif FILE_DEBUG("Side channel: Produce verdict: %d, file id: %d",hv->verdict,hv->file_type_id); return 0; } int ConsumeSSFileCache(const uint8_t *buf, uint32_t len) { FileHashKey *hk; FileNode *hv; SFXHASH_NODE *hash_node; FileNode *node; if( !buf ) { LogMessage("Side channel: No buffer\n"); return -1; } if( len < sizeof(*hk) + sizeof(*hv) ) { LogMessage("Side channel: length too small\n"); return -1; } hk = (FileHashKey *)buf; hv = (FileNode *)(buf + sizeof(*hk)); pthread_mutex_lock(&file_cache_mutex); hash_node = sfxhash_find_node(fileHash, hk); if (hash_node) { if (!(node = hash_node->data)) { sfxhash_free_node(fileHash, hash_node); } } else node = NULL; if (node) { node->expires = hv->expires ;/* 20 minuts timeout*/ updateFileNode(node, hv->verdict, hv->file_type_id, hv->sha256); } else if (sfxhash_add(fileHash, hk, hv) != SFXHASH_OK) { /* Uh, shouldn't get here... * There is already a node or couldn't alloc space * for key. This means bigger problems, but fail * gracefully. */ LogMessage("Failed to add file node to hash table\n"); pthread_mutex_unlock(&file_cache_mutex); return -1; } pthread_mutex_unlock(&file_cache_mutex); #ifdef REG_TEST LogMessage("consume verdict =%d file id =%d \n",hv->verdict,hv->file_type_id); file_sha256_print(hv->sha256); #endif /* REG_TEST */ return 0; } #endif /*Message will be logged within 600 seconds*/ static ThrottleInfo error_throttleInfo = {0,600,0}; /** * * @param sip - source IP address * @param dip - destination IP address * @param sport - server sport number * @param file_sig - file signature * @param expiry - session expiry in seconds. */ int file_resume_block_add_file(void *pkt, uint32_t file_sig, uint32_t timeout, File_Verdict verdict, uint32_t file_type_id, uint8_t *signature, uint16_t cli_port, uint16_t srv_port, bool create_pinhole, bool direction) { FileHashKey hashKey; SFXHASH_NODE *hash_node = NULL; FileNode *node; FileNode new_node; #ifdef HAVE_DAQ_DP_ADD_DC bool use_other_port = false; #endif sfaddr_t* srcIP; sfaddr_t* dstIP; Packet *p = (Packet*)pkt; time_t now = p->pkth->ts.tv_sec; SAVE_DAQ_PKT_HDR(p); srcIP = GET_SRC_IP(p); dstIP = GET_DST_IP(p); sfaddr_copy_to_raw(&hashKey.dip, dstIP); sfaddr_copy_to_raw(&hashKey.sip, srcIP); hashKey.file_sig = file_sig; pthread_mutex_lock(&file_cache_mutex); hash_node = sfxhash_find_node(fileHash, &hashKey); if (hash_node) { if (!(node = hash_node->data)) { sfxhash_free_node(fileHash, hash_node); } } else node = NULL; pthread_mutex_unlock(&file_cache_mutex); if ( timeout == 0 && verdict == 0 && file_type_id == 0) { FileConfig *file_config; FileContext *context; file_config = (FileConfig *)(snort_conf->file_config); context = get_main_file_context(p->ssnptr); if (!context && !node) { FILE_ERROR("Resume block: context and node not found"); return -1; } timeout = (uint32_t)file_config->file_block_timeout; if(context) { verdict = context->verdict; file_type_id = context->file_type_id; signature = context->sha256; } else { verdict = node->verdict; file_type_id = node->file_type_id; signature = node->sha256; } #ifdef HAVE_DAQ_DP_ADD_DC use_other_port = true; #endif } #ifdef HAVE_DAQ_DP_ADD_DC if (create_pinhole) { DAQ_DC_Params params; memset(¶ms, 0, sizeof(params)); params.flags = DAQ_DC_ALLOW_MULTIPLE; params.timeout_ms = 5 * 60 * 1000; /* 5 minutes */ if (p->packet_flags & PKT_FROM_CLIENT) { if(use_other_port) { if(direction) DAQ_Add_Dynamic_Protocol_Channel(p, srcIP, 0, dstIP, srv_port, GET_IPH_PROTO(p), ¶ms); else DAQ_Add_Dynamic_Protocol_Channel(p, dstIP, 0, srcIP, srv_port, GET_IPH_PROTO(p), ¶ms); } else DAQ_Add_Dynamic_Protocol_Channel(p, srcIP, 0, dstIP, p->dp, GET_IPH_PROTO(p), ¶ms); FILE_DEBUG("Pinhole created from client packet, direction: %d, time: %d",direction, now); } else if (p->packet_flags & PKT_FROM_SERVER) { if(use_other_port) { if(direction) DAQ_Add_Dynamic_Protocol_Channel(p, srcIP, 0, dstIP, srv_port, GET_IPH_PROTO(p), ¶ms); else DAQ_Add_Dynamic_Protocol_Channel(p, dstIP, 0, srcIP, srv_port, GET_IPH_PROTO(p), ¶ms); } else DAQ_Add_Dynamic_Protocol_Channel(p, dstIP, 0, srcIP, p->sp, GET_IPH_PROTO(p), ¶ms); FILE_DEBUG("Pinhole created from server packet, direction: %d, time: %d",direction, now); } } #endif if (node) { FILE_DEBUG("Resume block: Updating file node"); node->expires = now + (timeout * 4);/* 20 minuts timeout*/ updateFileNode(node, verdict, file_type_id, signature); #ifdef SIDE_CHANNEL if ((ProduceSSFileCache(&hashKey, node) < 0) && ScSideChannelEnabled()) { ErrorMessageThrottled(&error_throttleInfo, "Failed to add Side channel message\n"); } #endif } else { FILE_DEBUG("Resume block: Adding file node"); updateFileNode(&new_node, verdict, file_type_id, signature); /* * use the time that we keep files around * since this info would effectively be invalid * after that anyway because the file that * caused this will be gone. */ new_node.expires = now + (timeout * 4);/* 20 minuts timeout*/ /* Add it to the table */ #ifdef SIDE_CHANNEL if ((ProduceSSFileCache(&hashKey, &new_node) < 0) && ScSideChannelEnabled()) { ErrorMessageThrottled(&error_throttleInfo, "Failed to add Side channel message\n"); } #endif pthread_mutex_lock(&file_cache_mutex); if (sfxhash_add(fileHash, &hashKey, &new_node) != SFXHASH_OK) { /* Uh, shouldn't get here... * There is already a node or couldn't alloc space * for key. This means bigger problems, but fail * gracefully. */ FILE_ERROR("Resume block: Failed to add file node to hash table"); pthread_mutex_unlock(&file_cache_mutex); return -1; } pthread_mutex_unlock(&file_cache_mutex); } if (signature) { FILE_DEBUG("Resume block: Added file node with verdict: %d, file signature: %d, hash:" "%02X%02X %02X%02X %02X%02X %02X%02X" "%02X%02X %02X%02X %02X%02X %02X%02X " "%02X%02X %02X%02X %02X%02X %02X%02X " "%02X%02X %02X%02X %02X%02X %02X%02X", verdict, file_sig, signature[0], signature[1], signature[2], signature[3], signature[4], signature[5], signature[6], signature[7], signature[8], signature[9], signature[10], signature[11], signature[12], signature[13], signature[14], signature[15], signature[16], signature[17], signature[18], signature[19], signature[20], signature[21], signature[22], signature[23], signature[24], signature[25], signature[26], signature[27], signature[28], signature[29], signature[30], signature[31]); } else { FILE_DEBUG("Resume block: Added file node with verdict: %d, file signature: %d", verdict, file_sig); } return 0; } static inline File_Verdict checkVerdict(Packet *p, FileNode *node, SFXHASH_NODE *hash_node) { File_Verdict verdict = FILE_VERDICT_UNKNOWN; FileContext *context = NULL; bool partialFile = false; /*Query the file policy in case verdict has been changed*/ /*Check file type first*/ if (cur_config.file_type_cb) { verdict = cur_config.file_type_cb(p, p->ssnptr, node->file_type_id, 0, DEFAULT_FILE_ID); FILE_DEBUG("Checking verdict, node verdict: %d, file type verdict: %d",node->verdict, verdict); } if ((verdict == FILE_VERDICT_UNKNOWN) || (verdict == FILE_VERDICT_STOP_CAPTURE)) { if (cur_config.file_signature_cb) { context = get_main_file_context(p->ssnptr); if(NULL != context) { partialFile = context->partial_file; } verdict = cur_config.file_signature_cb(p, p->ssnptr, node->sha256, 0, &sig_file_state, 0, DEFAULT_FILE_ID, partialFile ); FILE_DEBUG("Checking verdict, node verdict: %d, file signature verdict: %d",node->verdict, verdict); } } if ((verdict == FILE_VERDICT_UNKNOWN) || (verdict == FILE_VERDICT_STOP_CAPTURE)) { verdict = node->verdict; } if (verdict == FILE_VERDICT_LOG) { pthread_mutex_lock(&file_cache_mutex); sfxhash_free_node(fileHash, hash_node); pthread_mutex_unlock(&file_cache_mutex); if (cur_config.log_file_action) { cur_config.log_file_action(p->ssnptr, FILE_RESUME_LOG); } } else if (verdict == FILE_VERDICT_BLOCK) { Active_ForceDropPacket(); Active_DropSession(p); if (cur_config.log_file_action) { cur_config.log_file_action(p->ssnptr, FILE_RESUME_BLOCK); } node->verdict = verdict; } else if (verdict == FILE_VERDICT_REJECT) { Active_ForceDropPacket(); Active_DropSession(p); #ifdef ACTIVE_RESPONSE Active_QueueReject(); #endif if (cur_config.log_file_action) { cur_config.log_file_action(p->ssnptr, FILE_RESUME_BLOCK); } node->verdict = verdict; } else if (verdict == FILE_VERDICT_PENDING) { /*Take the cached verdict*/ Active_ForceDropPacket(); Active_DropSession(p); #ifdef ACTIVE_RESPONSE if (FILE_VERDICT_REJECT == node->verdict) Active_QueueReject(); #endif if (cur_config.log_file_action) { cur_config.log_file_action(p->ssnptr, FILE_RESUME_BLOCK); } verdict = node->verdict; } FILE_DEBUG("Verdict checked for file node with hash: " "%02X%02X %02X%02X %02X%02X %02X%02X" "%02X%02X %02X%02X %02X%02X %02X%02X " "%02X%02X %02X%02X %02X%02X %02X%02X " "%02X%02X %02X%02X %02X%02X %02X%02X", node->sha256[0], node->sha256[1], node->sha256[2], node->sha256[3], node->sha256[4], node->sha256[5], node->sha256[6], node->sha256[7], node->sha256[8], node->sha256[9], node->sha256[10], node->sha256[11], node->sha256[12], node->sha256[13], node->sha256[14], node->sha256[15], node->sha256[16], node->sha256[17], node->sha256[18], node->sha256[19], node->sha256[20], node->sha256[21], node->sha256[22], node->sha256[23], node->sha256[24], node->sha256[25], node->sha256[26], node->sha256[27], node->sha256[28], node->sha256[29], node->sha256[30], node->sha256[31]); return verdict; } File_Verdict file_resume_block_check(void *pkt, uint32_t file_sig) { File_Verdict verdict = FILE_VERDICT_UNKNOWN; sfaddr_t* srcIP; sfaddr_t* dstIP; SFXHASH_NODE *hash_node; FileHashKey hashKey; FileNode *node; Packet *p = (Packet*)pkt; SAVE_DAQ_PKT_HDR(pkt); /* No hash table, or its empty? Get out of dodge. */ if (!fileHash || !sfxhash_count(fileHash)) { FILE_DEBUG("No expected sessions"); return verdict; } srcIP = GET_SRC_IP(p); dstIP = GET_DST_IP(p); sfaddr_copy_to_raw(&hashKey.dip, dstIP); sfaddr_copy_to_raw(&hashKey.sip, srcIP); hashKey.file_sig = file_sig; pthread_mutex_lock(&file_cache_mutex); hash_node = sfxhash_find_node(fileHash, &hashKey); if (hash_node) { if (!(node = hash_node->data)) { sfxhash_free_node(fileHash, hash_node); } } else { pthread_mutex_unlock(&file_cache_mutex); FILE_DEBUG("File not found"); return verdict; } pthread_mutex_unlock(&file_cache_mutex); if (node) { FILE_DEBUG("Found resumed file"); if (node->expires && p->pkth->ts.tv_sec > node->expires) { FILE_DEBUG("File expired, verdict: %d",verdict); pthread_mutex_lock(&file_cache_mutex); sfxhash_free_node(fileHash, hash_node); pthread_mutex_unlock(&file_cache_mutex); return verdict; } /*Query the file policy in case verdict has been changed*/ verdict = checkVerdict(p, node, hash_node); } if (verdict == FILE_VERDICT_BLOCK || verdict == FILE_VERDICT_REJECT || verdict == FILE_VERDICT_PENDING) { if (pkt_trace_enabled) addPktTraceData(VERDICT_REASON_FILE, snprintf(trace_line, MAX_TRACE_LINE, "File Process: file not resumed, %s\n", getPktTraceActMsg())); else addPktTraceData(VERDICT_REASON_FILE, 0); FILE_INFO("File not resumed, verdict: %d",verdict); } else FILE_INFO("File resumed, verdict: %d",verdict); return verdict; } snort-2.9.20/src/file-process/file_service.h0000644000175000017500000000415614241076566017104 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.12 - Initial Source Code. Hcao */ #ifndef __FILE_SERVICE_H__ #define __FILE_SERVICE_H__ #include "file_service_config.h" #include "file_lib.h" typedef struct _FileServiceConfig { File_policy_callback_func file_policy_cb; File_type_callback_func file_type_cb; File_signature_callback_func file_signature_cb; Log_file_action_func log_file_action; bool file_type_id_enabled; bool file_signature_enabled; bool file_capture_enabled; }FileServiceConfig; /* Initialize file API, this must be called when snort restarts */ void init_fileAPI(void); /* Free file configuration, this must be called when snort reloads/restarts*/ void free_file_config(void*); /* Swap the new config with current config */ void FileServiceInstall(void); /* Close file API, this must be called when snort exits */ void close_fileAPI(void); /* Get current file context */ FileContext* get_current_file_context(void *ssnptr); /* Check and set the sidechannel to file cache sync */ #if defined (SIDE_CHANNEL) void check_sidechannel_enabled(void *file_config); #ifdef REG_TEST void FileSSConfigFree(void *file_config); #endif #endif #endif snort-2.9.20/src/file-process/Makefile.in0000644000175000017500000005377714242725547016357 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/file-process ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libfileAPI_a_AR = $(AR) $(ARFLAGS) libfileAPI_a_LIBADD = am_libfileAPI_a_OBJECTS = file_service.$(OBJEXT) \ file_service_config.$(OBJEXT) file_mime_process.$(OBJEXT) \ file_resume_block.$(OBJEXT) file_mime_config.$(OBJEXT) \ file_capture.$(OBJEXT) file_stats.$(OBJEXT) \ file_segment_process.$(OBJEXT) circular_buffer.$(OBJEXT) \ file_mempool.$(OBJEXT) sf_email_attach_decode.$(OBJEXT) \ file_ss.$(OBJEXT) libfileAPI_a_OBJECTS = $(am_libfileAPI_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libfileAPI_a_SOURCES) DIST_SOURCES = $(libfileAPI_a_SOURCES) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies noinst_LIBRARIES = libfileAPI.a libfileAPI_a_SOURCES = \ file_service.c \ file_service.h \ file_service_config.c \ file_service_config.h \ file_api.h \ file_mime_process.c \ file_mime_process.h \ file_resume_block.c \ file_resume_block.h \ file_mime_config.c \ file_mime_config.h \ file_capture.c \ file_capture.h \ file_stats.c \ file_stats.h \ file_segment_process.c \ file_segment_process.h \ circular_buffer.c \ circular_buffer.h \ file_mempool.c \ file_mempool.h \ ../sfutil/sf_email_attach_decode.c \ ../sfutil/sf_email_attach_decode.h \ file_mail_common.h\ file_ss.c\ file_ss.h SUBDIRS = libs all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/file-process/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/file-process/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libfileAPI.a: $(libfileAPI_a_OBJECTS) $(libfileAPI_a_DEPENDENCIES) $(EXTRA_libfileAPI_a_DEPENDENCIES) $(AM_V_at)-rm -f libfileAPI.a $(AM_V_AR)$(libfileAPI_a_AR) libfileAPI.a $(libfileAPI_a_OBJECTS) $(libfileAPI_a_LIBADD) $(AM_V_at)$(RANLIB) libfileAPI.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< sf_email_attach_decode.o: ../sfutil/sf_email_attach_decode.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_email_attach_decode.o `test -f '../sfutil/sf_email_attach_decode.c' || echo '$(srcdir)/'`../sfutil/sf_email_attach_decode.c sf_email_attach_decode.obj: ../sfutil/sf_email_attach_decode.c $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sf_email_attach_decode.obj `if test -f '../sfutil/sf_email_attach_decode.c'; then $(CYGPATH_W) '../sfutil/sf_email_attach_decode.c'; else $(CYGPATH_W) '$(srcdir)/../sfutil/sf_email_attach_decode.c'; fi` mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LIBRARIES) installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool \ clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/file-process/file_ss.c0000644000175000017500000002473314241076572016064 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * *****************************************************************************/ /************************************************************************** * * file_ss.c * * Authors: Bhargava Jandhyala * * Description: * * File cache sharing support. * **************************************************************************/ #ifdef SIDE_CHANNEL #include "file_ss.h" #include "sidechannel.h" #include "file_resume_block.h" #include "packet_time.h" #include #include #ifdef REG_TEST typedef struct _MsgHeader { uint32_t type; uint32_t data_length; } MsgHeader; #endif typedef struct _FileSSStats { uint32_t messages_received; uint32_t messages_sent; } FileSSStats; static uint8_t file_io_buffer[UINT16_MAX]; static FileSSStats file_ss_stats; #ifdef REG_TEST static int runtime_output_fd = -1; static uint32_t WriteFileSSMsgHeader(uint8_t *, uint32_t , uint32_t ); static inline ssize_t Read(int , void *, size_t ); static inline ssize_t Write(int , const void *, size_t ); static int ReadFileSSMessagesFromFile(const char *); static uint32_t WriteFileSSMsgHeader(uint8_t *, uint32_t , uint32_t ); #endif void FilePrintSSStats(void) { LogMessage(" File Cache Sharing:\n"); LogMessage(" Messages Received: %u\n", file_ss_stats.messages_received); LogMessage(" Messages Sent: %u\n", file_ss_stats.messages_sent); } static int ConsumeFileSSMsg(uint32_t type, const uint8_t *msg, uint32_t msglen) { int rval = 1; switch(type) { case SC_MSG_TYPE_FILE_SS_HOST_CACHE: rval = ConsumeSSFileCache(msg, msglen); break; default: break; } file_ss_stats.messages_received++; return rval; } #ifndef REG_TEST static int FileSSSCMsgHandler(SCMsgHdr *hdr, const uint8_t *msg, uint32_t msglen) { int rval = 1; if(!hdr) return rval; rval = ConsumeFileSSMsg(hdr->type, msg, msglen); return rval; } /*Message will be logged within 600 seconds*/ static ThrottleInfo error_throttleInfo = {0,600,0}; /* Caller should proceed writing to the 'data_ptr' ONLY upon successful return i.e 0 */ int CreateFileSSUpdate(void **msg_handle, void **hdr_ptr, void **data_ptr, uint32_t type, uint32_t data_len) { FileConfig *file_config; SCMsgHdr *schdr; uint8_t *msg; int rval; file_config = (FileConfig *)(snort_conf->file_config); if (!msg_handle || !hdr_ptr || !data_ptr) { ErrorMessage("Param %s is null for side channel update\n", msg_handle ? (hdr_ptr ? "data pointer" : "header pointer") : "Message handle"); return -1; } if (file_config->use_side_channel) { /* Allocate space for the message. */ if ((rval = SideChannelPreallocMessageTX(data_len, &schdr, &msg, msg_handle)) != 0) { ErrorMessage("Unable to allocate memory for enqueing File SS message\n"); return -1; } *hdr_ptr = (void *)schdr; *data_ptr = (void *)msg; } else { #if defined(DAQ_VERSION) && DAQ_VERSION > 6 if(ScSideChannelEnabled()) { file_config->use_side_channel = true; ErrorMessage("Enabling file side channel runtime \n"); /* Allocate space for the message. */ if ((rval = SideChannelPreallocMessageTX(data_len, &schdr, &msg, msg_handle)) != 0) { ErrorMessage("Unable to allocate memory for enqueing File SS message after at runtime\n"); return -1; } *hdr_ptr = (void *)schdr; *data_ptr = (void *)msg; } else #endif { ErrorMessageThrottled(&error_throttleInfo, "side channel infra is not up\n"); return -1; } } return 0; } int SendFileSSUpdate(void *msg_handle, void *hdr_ptr, void *data_ptr, uint32_t type, uint32_t data_len) { FileConfig *file_config; file_config = (FileConfig *)(snort_conf->file_config); SCMsgHdr *schdr; uint8_t *msg; if (!msg_handle || !hdr_ptr || !data_ptr) { return -1; } if (file_config->use_side_channel) { schdr = (SCMsgHdr *)hdr_ptr; msg = (uint8_t *)data_ptr; schdr->type = type; schdr->timestamp = packet_time(); SideChannelEnqueueMessageTX(schdr, msg, data_len, msg_handle, NULL); file_ss_stats.messages_sent++; } return 0; } void FileSSConfigInit(void) { int rval; FileConfig *file_config; file_config = (FileConfig *)(snort_conf->file_config); if ((rval = SideChannelRegisterRXHandler(SC_MSG_TYPE_FILE_SS_HOST_CACHE, FileSSSCMsgHandler, NULL)) != 0) { ErrorMessage("Unable to register File SS message handler\n"); } return; } #endif #ifdef REG_TEST /* * File I/O for regression only */ void FileCleanSS(void) { if (runtime_output_fd >= 0) { close(runtime_output_fd); runtime_output_fd = -1; } } void FilePrintSSConfig(FileSSConfig *file_ss_config) { if (file_ss_config == NULL) return; LogMessage(" File SS config:\n"); if (file_ss_config->startup_input_file) LogMessage(" Startup Input File: %s\n", file_ss_config->startup_input_file); if (file_ss_config->runtime_output_file) LogMessage(" Runtime Output File: %s\n", file_ss_config->runtime_output_file); return; } int CreateFileSSUpdate(void **msg_handle, void **hdr_ptr, void **data_ptr, uint32_t type, uint32_t data_len) { if (!data_ptr) return -1; if (runtime_output_fd >= 0) { WriteFileSSMsgHeader(file_io_buffer, type, data_len); *data_ptr = (void *)(&file_io_buffer[0] + sizeof(MsgHeader)); } else { return -1; } return 0; } int SendFileSSUpdate(void *msg_handle, void *hdr_ptr, void *data_ptr, uint32_t type, uint32_t data_len) { if (runtime_output_fd >= 0) { if (Write(runtime_output_fd, file_io_buffer, sizeof(MsgHeader) + data_len) == -1) { /* Already reported Error inside the 'Write' call */ } else { file_ss_stats.messages_sent++; } } return 0; } void FileSSConfigInit(void) { int rval; FileConfig *file_config; file_config = (FileConfig *)(snort_conf->file_config); /* Probably need to do this through a different function for Reload. But we're okay for now. */ if (file_config && file_config->file_ss_config) { if (file_config->file_ss_config->startup_input_file) { if ((rval = ReadFileSSMessagesFromFile(file_config->file_ss_config->startup_input_file)) != 0) { ErrorMessage("Errors were encountered while reading File SS messages from file '%s'\n", file_config->file_ss_config->startup_input_file); } } if (file_config->file_ss_config->runtime_output_file) { runtime_output_fd = open(file_config->file_ss_config->runtime_output_file, O_WRONLY | O_CREAT | O_TRUNC, 0664); if (runtime_output_fd < 0) { ErrorMessage("Could not open %s for writing File SS messages: %s (%d)\n", file_config->file_ss_config->runtime_output_file, strerror(errno), errno); } } } } static inline ssize_t Read(int fd, void *buf, size_t count) { ssize_t n; errno = 0; while ((n = read(fd, buf, count)) <= (ssize_t) count) { if (n == (ssize_t) count) return 0; if (n > 0) { buf = (uint8_t *) buf + n; count -= n; } else if (n == 0) break; else if (errno != EINTR) { ErrorMessage("Error reading from File SS message file: %s (%d)\n", strerror(errno), errno); break; } } return -1; } static inline ssize_t Write(int fd, const void *buf, size_t count) { ssize_t n; errno = 0; while ((n = write(fd, buf, count)) <= (ssize_t) count) { if (n == (ssize_t) count) return 0; if (n > 0) count -= n; else if (errno != EINTR) { ErrorMessage("Error writing to File SS message file: %s (%d)\n", strerror(errno), errno); break; } } return -1; } static int ReadFileSSMessagesFromFile(const char *filename) { MsgHeader *msg_header; uint8_t *msg; int rval = 0, fd, offset = 0; fd = open(filename, O_RDONLY , 0664); if (fd < 0) { if (errno == ENOENT) return 0; ErrorMessage("Could not open %s for reading File SS messages from: %s (%d)\n", filename, strerror(errno), errno); } msg = file_io_buffer; while ((rval = Read(fd, msg, sizeof(*msg_header))) == 0) { msg_header = (MsgHeader *) msg; offset = sizeof(*msg_header); if ((rval = Read(fd, msg + offset, msg_header->data_length)) != 0) { ErrorMessage("Error reading the remaining %u bytes of File SS message from file: %s (%d)\n", msg_header->data_length, strerror(errno), errno); close(fd); return rval; } if ((rval = ConsumeFileSSMsg(msg_header->type, msg + offset, msg_header->data_length) != 0)) { close(fd); return rval; } offset += msg_header->data_length; msg += offset; } close(fd); return 0; } static uint32_t WriteFileSSMsgHeader(uint8_t *msg, uint32_t type, uint32_t data_len) { MsgHeader *msg_hdr; msg_hdr = (MsgHeader *) msg; msg_hdr->type = type; msg_hdr->data_length = data_len; return 0; } #endif #endif /* SIDE_CHANNEL */ snort-2.9.20/src/file-process/file_api.h0000644000175000017500000006474414241076540016216 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * ** Copyright (C) 2012-2013 Sourcefire, Inc. * ** AUTHOR: Hui Cao * ** * ** This program is free software; you can redistribute it and/or modify * ** it under the terms of the GNU General Public License Version 2 as * ** published by the Free Software Foundation. You may not use, modify or * ** distribute this program under any other version of the GNU General * ** Public License. * ** * ** 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. * */ /* file_api.h * * Purpose: Definition of the FileAPI. To be used as a common interface * for file process access for other preprocessors and detection * plugins. * * Author(s): Hui Cao * * NOTES * 5.25.12 - Initial Source Code. Hcao */ #ifndef FILE_API_H_ #define FILE_API_H_ #ifdef HAVE_CONFIG_H #include #endif #include #include "sfPolicy.h" #define ENABLE_FILE_TYPE_IDENTIFICATION 0x1 #define ENABLE_FILE_SIGNATURE_SHA256 0x2 #define ENABLE_FILE_CAPTURE 0x4 #define FILE_ALL_ON 0xFFFFFFFF #define FILE_ALL_OFF 0x00000000 #define MAX_FILE 1024 #define MAX_EMAIL 1024 #define MAX_UNICODE_FILE_NAME 1024 #define FILE_RESUME_BLOCK 0x01 #define FILE_RESUME_LOG 0x02 /* * Generator id. Define here the same as the official register * in generators.h */ #define GENERATOR_FILE_TYPE 146 #define GENERATOR_FILE_SIGNATURE 147 #define FILE_SIGNATURE_SHA256 1 #define FILE_SIGNATURE_SHA256_STR "(file) malware detected" #define UTF_16_BE_BOM "\xFE\xFF" #define UTF_16_LE_BOM "\xFF\xFE" #define UTF_16_BE_BOM_LEN 2 #define UTF_16_LE_BOM_LEN 2 typedef enum _File_Verdict { FILE_VERDICT_UNKNOWN = 0, FILE_VERDICT_LOG, FILE_VERDICT_STOP, FILE_VERDICT_BLOCK, FILE_VERDICT_REJECT, FILE_VERDICT_PENDING, FILE_VERDICT_STOP_CAPTURE, FILE_VERDICT_MAX } File_Verdict; typedef enum _FilePosition { SNORT_FILE_POSITION_UNKNOWN, SNORT_FILE_START, SNORT_FILE_MIDDLE, SNORT_FILE_END, SNORT_FILE_FULL } FilePosition; typedef enum _FileCaptureState { FILE_CAPTURE_SUCCESS = 0, FILE_CAPTURE_MIN, /*smaller than file capture min*/ FILE_CAPTURE_MAX, /*larger than file capture max*/ FILE_CAPTURE_MEMCAP, /*memcap reached, no more file buffer*/ FILE_CAPTURE_FAIL /*Other file capture failures*/ } FileCaptureState; typedef enum _FileSigState { FILE_SIG_PROCESSING = 0, FILE_SIG_DEPTH_FAIL, /*larger than file signature depth*/ FILE_SIG_FLUSH, FILE_SIG_DONE } FileSigState; typedef enum _FileProcessType { SNORT_FILE_TYPE_ID, SNORT_FILE_SHA256, SNORT_FILE_CAPTURE } FileProcessType; typedef enum _FileCharEncoding { SNORT_CHAR_ENCODING_ASCII = 0, SNORT_CHAR_ENCODING_UTF_16LE, SNORT_CHAR_ENCODING_UTF_16BE, }FileCharEncoding; typedef struct _FileState { FileCaptureState capture_state; FileSigState sig_state; } FileState; typedef struct _FileCacheStatus { uint64_t prunes; /* number of file entries pruned due to memcap*/ uint64_t segment_mem_in_use; /* memory used currently */ uint64_t segment_mem_in_use_max; /* Maximal memory usage */ } FileCacheStatus; struct s_MAIL_LogState; struct _DecodeConfig; struct s_MAIL_LogConfig; struct _MimeDataPafInfo; struct _MimeState; struct _FileCaptureInfo; typedef struct _FileCaptureInfo FileCaptureInfo; struct _SnortConfig; struct _FileContext; struct _FileCache; struct _MemPool; typedef struct _FileSession { struct _FileContext *current_context; struct _FileContext *main_context; struct _FileContext *pending_context; uint32_t max_file_id; struct _FileCache *file_cache; uint64_t file_id; } FileSession; #define FILE_API_VERSION 5 #define DEFAULT_FILE_ID 0 typedef uint32_t (*File_policy_callback_func) (void* ssnptr, int16_t app_id, bool upload); typedef File_Verdict (*File_type_callback_func) (void* p, void* ssnptr, uint32_t file_type_id, bool upload, uint32_t file_id); typedef File_Verdict (*File_signature_callback_func) (void* p, void* ssnptr, uint8_t* file_sig, uint64_t file_size, FileState *state, bool upload, uint32_t file_id, bool partial_file); typedef void (*Log_file_action_func) (void* ssnptr, int action); typedef int (*File_process_func)( void* p, uint8_t* file_data, int data_size, FilePosition position, bool upload, bool suspend_block_verdict, bool do_flush); typedef int (*Get_file_name_func) (void* ssnptr, uint8_t **file_name, uint32_t *name_len); typedef uint64_t (*Get_file_size_func) (void* ssnptr); typedef bool (*Get_file_direction_func) (void* ssnptr); typedef uint8_t *(*Get_file_sig_sha256_func) (void* ssnptr); typedef void (*Set_file_name_func) (void* ssnptr, uint8_t *, uint32_t, bool); typedef void (*Set_file_direction_func) (void* ssnptr, bool); typedef int64_t (*Get_file_depth_func) (struct _SnortConfig *snort_conf, bool next); typedef bool (*Is_file_signature_enabled_func) (void); typedef void (*Set_file_policy_func)(File_policy_callback_func); typedef void (*Enable_file_type_func)(struct _SnortConfig* sc, File_type_callback_func); typedef void (*Enable_file_signature_func)(struct _SnortConfig* sc, File_signature_callback_func); typedef void (*Enable_file_capture_func)(struct _SnortConfig* sc, File_signature_callback_func); typedef void (*Set_file_action_log_func)(Log_file_action_func); typedef void (*Install_file_service_func)(void); typedef int (*Set_log_buffers_func)(struct s_MAIL_LogState **log_state, struct s_MAIL_LogConfig *conf, void *mempool, void* scbPtr, uint32_t preproc_id); typedef void (*Update_mime_mempool_func)(void*, int, int); typedef void (*Update_log_mempool_func)(void*, int, int); typedef void (*Display_mime_mempool_func)(void *memory_pool, struct _DecodeConfig *decode_conf_old, struct _DecodeConfig *decode_conf_new); typedef void (*Display_log_mempool_func)(void *memory_pool, unsigned memcap_old, unsigned memcap_new); typedef void (*Display_decode_depth_func)(struct _DecodeConfig *decode_conf_old, struct _DecodeConfig *decode_conf_new); typedef void* (*Init_mime_mempool_func)(int max_mime_mem, int max_depth, void *mempool, const char *preproc_name); typedef void* (*Init_log_mempool_func)(uint32_t email_hdrs_log_depth, uint32_t memcap, void *mempool, const char *preproc_name); typedef int (*File_resume_block_add_file_func)(void *pkt, uint32_t file_sig, uint32_t timeout, File_Verdict verdict, uint32_t file_type_id, uint8_t *signature, uint16_t cli_port, uint16_t srv_port, bool create_pinhole, bool direction); typedef File_Verdict (*File_resume_block_check_func)(void *pkt, uint32_t file_sig); typedef uint32_t (*Str_to_hash_func)(uint8_t *str, int length ); typedef void (*File_signature_lookup_func)(void* p, bool is_retransmit); typedef void (*Set_mime_decode_config_defaults_func)(struct _DecodeConfig *decode_conf); typedef void (*Set_mime_log_config_defaults_func)(struct s_MAIL_LogConfig *log_config); typedef int (*Parse_mime_decode_args_func)(struct _DecodeConfig *decode_conf, char *arg, const char *preproc_name, char **saveptr); typedef const uint8_t * (*Process_mime_data_func)(void *packet, const uint8_t *start, const uint8_t *end, struct _MimeState *mime_ssn, bool upload, bool paf_enabled, char *protocol, uint32_t preproc_id); typedef void (*Free_mime_session_func)(struct _MimeState *mime_ssn); typedef bool (*Is_decoding_enabled_func)(struct _DecodeConfig *decode_conf); typedef bool (*Check_decoding_conf_func)(struct _DecodeConfig *configNext, struct _DecodeConfig *config, const char *preproc_name); typedef bool (*Is_mime_log_enabled_func)(struct s_MAIL_LogConfig *log_config); typedef void (*Finalize_mime_position_func)(void *ssnptr, void *decode_state, FilePosition *position); typedef File_Verdict (*Get_file_verdict_func)(void *ssnptr); typedef void (*Render_block_verdict_func)(void *ctx, void *p); typedef FileCaptureState (*Reserve_file_func)(void *ssnptr, FileCaptureInfo **file_mem); typedef void* (*Get_file_func)(FileCaptureInfo *file_mem, uint8_t **buff, int *size); typedef void (*Release_file_func)(FileCaptureInfo *data); typedef size_t (*File_capture_size_func)(FileCaptureInfo *file_mem); typedef bool (*Is_file_service_enabled)(void); typedef bool (*Check_paf_abort_func)(void* ssn); typedef void (*Update_file_name_func) (struct s_MAIL_LogState *log_state); typedef FilePosition (*GetFilePosition)(void *pkt); typedef void (*Reset_mime_paf_state_func)(struct _MimeDataPafInfo *data_info); /* Process data boundary and flush each file based on boundary*/ typedef bool (*Process_mime_paf_data_func)(struct _MimeDataPafInfo *data_info, uint8_t data); typedef bool (*Check_data_end_func)(void *end_state, uint8_t data); typedef uint32_t (*Get_file_type_id)(void *); typedef uint32_t (*Get_new_file_instance)(void *); /*Context based file process functions*/ typedef struct _FileContext* (*Create_file_context_func)(void *ssnptr); typedef void (*Init_file_context_func)(void *ssnptr, bool upload, struct _FileContext *ctx); typedef struct _FileContext* (*Get_file_context_func)(void *ssnptr); typedef bool (*Set_file_context_func)(void *ssnptr, struct _FileContext *ctx); typedef int (*Process_file_func)( struct _FileContext *ctx, void *p, uint8_t *file_data, int data_size, FilePosition position, bool suspend_block_verdict); typedef void *(*File_cache_update_entry_func) (struct _FileCache *fileCache, void* p, uint64_t file_id, uint8_t *file_name, uint32_t file_name_size, uint64_t file_size, bool reset, bool no_update_size); typedef int (*File_segment_process_func)( struct _FileCache *fileCache, void* p, uint64_t file_id, uint64_t file_size, const uint8_t* file_data, int data_size, uint64_t offset, bool upload); typedef struct _FileCache * (*File_cache_create_func)(uint64_t memcap, uint32_t cleanup_files); typedef void (*File_cache_free_func)(struct _FileCache *fileCache); typedef FileCacheStatus * (*File_cache_status_func)(struct _FileCache *fileCache); typedef int64_t (*Get_max_file_capture_size)(void *ssn); typedef bool (*File_config_malware_check)(void *ssn, uint16_t app_id); typedef FileCharEncoding (*Get_character_encoding)(uint8_t *, uint32_t); typedef bool (*File_cache_mem_adjust_func)(struct _FileCache *fileCache, uint8_t *pWork); typedef void (*File_cache_mem_set_func)(struct _FileCache *fileCache, uint64_t memcap); typedef void (*File_event_log_dump_func)( struct _FileCache *fileCache, void* p, uint64_t file_id); typedef void (*File_signature_reset)(void *ssnptr); typedef void (*Set_file_partial_func)(void *p, FilePosition position, bool upload, bool is_partial); typedef char* (*File_get_filetype_func) (void *ssnptr); typedef struct _file_api { int version; /* Check if file type id is enabled. * * Arguments: None * * Returns: * (bool) true file processing is enabled * (bool) false file processing is disabled */ Is_file_service_enabled is_file_service_enabled; /* File process function, called by preprocessors that provides file data * * Arguments: * void* p: packet pointer * uint8_t* file_data: file data * int data_size: file data size * FilePosition: file position * bool upload: upload or not * Returns: * 1: continue processing/log/block this file * 0: ignore this file (no further processing needed) */ File_process_func file_process; /*-----File property functions--------*/ /* Get file name and the length of file name * Note: this is updated after file processing. It will be available * for file event logging, but might not be available during file type * callback or file signature callback, because those callbacks are called * during file processing. * * Arguments: * void* ssnptr: session pointer * uint8_t **file_name: address for file name to be saved * uint32_t *name_len: address to save file name length * Returns * 1: file name available, * 0: file name is unavailable */ Get_file_name_func get_file_name; /* Get file size * Note: this is updated after file processing. It will be available * for file event logging, but might not be available during file type * callback or file signature callback, because those callbacks are called * during file processing. * * Arguments: * void* ssnptr: session pointer * * Returns * uint64_t: file size * Note: 0 means file size is unavailable */ Get_file_size_func get_file_size; /* Get number of bytes processed * * Arguments: * void* ssnptr: session pointer * * Returns * uint64_t: processed file data size */ Get_file_size_func get_file_processed_size; /* Get file direction * * Arguments: * void* ssnptr: session pointer * * Returns * 1: upload * 0: download */ Get_file_direction_func get_file_direction; /* Get file signature sha256 * * Arguments: * void* ssnptr: session pointer * * Returns * char *: pointer to sha256 * NULL: sha256 is not available */ Get_file_sig_sha256_func get_sig_sha256; /* Set file name and the length of file name * * Arguments: * void* ssnptr: session pointer * uint8_t *file_name: file name to be saved * uint32_t name_len: file name length * bool save_in_context: true if file name is saved in context * instead of session * Returns * None */ Set_file_name_func set_file_name; /* Get file direction * * Arguments: * void* ssnptr: session pointer * bool: * 1 - upload * 0 - download * Returns * None */ Set_file_direction_func set_file_direction; /*----------File call backs--------------*/ /* Set file policy callback. This callback is called in the beginning * of session. This callback will decide whether to do file type ID, * file signature, or file capture * * Arguments: * File_policy_callback_func * Returns * None */ Set_file_policy_func set_file_policy_callback; /* Enable file type ID and set file type callback. * File type callback is called when file type is identified. Callback * will return a verdict based on file type * * Arguments: * File_type_callback_func * Returns * None */ Enable_file_type_func enable_file_type; /* Enable file signature and set file signature callback. * File signature callback is called when file signature is calculated. * Callback will return a verdict based on file signature. * SHA256 is calculated after file transfer is finished. * * Arguments: * File_signature_callback_func * Returns * None */ Enable_file_signature_func enable_file_signature; /* Enable file capture and set file signature callback. * File signature callback is called when file signature is calculated. * Callback will return a verdict based on file signature. * SHA256 is calculated after file transfer is finished. * * Note: file signature and file capture will use the same callback, but * enabled separately. * * Arguments: * File_signature_callback_func * Returns * None */ Enable_file_signature_func enable_file_capture; /* Set file action log callback. * File action log callback is called when file resume is detected. * It allows file events to be generated for a resumed file download * * Arguments: * Log_file_action_func * Returns * None */ Set_file_action_log_func set_file_action_log_callback; /* Install file service. * This must be called in band with packets. * It makes the functions set in the other enable calls active. * * Arguments: * None * Returns * None */ Install_file_service_func install_file_service; /*--------------File configurations-------------*/ /* Get file depth required for all file processings enabled * * Arguments: * None * * Returns: * int64_t: file depth in bytes */ Get_file_depth_func get_max_file_depth; /* Is file signature enabled * * Arguments: * None * * Returns: * bool: true if file_signature_enabled is set */ Is_file_signature_enabled_func is_file_signature_enabled; /*--------------Common functions used for MIME processing-------------*/ Set_log_buffers_func set_log_buffers; Update_mime_mempool_func update_mime_mempool; Update_log_mempool_func update_log_mempool; Display_mime_mempool_func displayMimeMempool; Display_log_mempool_func displayLogMempool; Display_decode_depth_func displayDecodeDepth; Init_mime_mempool_func init_mime_mempool; Init_log_mempool_func init_log_mempool; Set_mime_decode_config_defaults_func set_mime_decode_config_defauts; Set_mime_log_config_defaults_func set_mime_log_config_defauts; Parse_mime_decode_args_func parse_mime_decode_args; Process_mime_data_func process_mime_data; Free_mime_session_func free_mime_session; Is_decoding_enabled_func is_decoding_enabled; Check_decoding_conf_func check_decoding_conf; Is_mime_log_enabled_func is_mime_log_enabled; Finalize_mime_position_func finalize_mime_position; Reset_mime_paf_state_func reset_mime_paf_state; Process_mime_paf_data_func process_mime_paf_data; Check_data_end_func check_data_end; Check_paf_abort_func check_paf_abort; /*--------------Other helper functions-------------*/ File_resume_block_add_file_func file_resume_block_add_file; File_resume_block_check_func file_resume_block_check; Str_to_hash_func str_to_hash; File_signature_lookup_func file_signature_lookup; Get_file_verdict_func get_file_verdict; Render_block_verdict_func render_block_verdict; /* * Preserve the file in memory until it is released * This function must be called in packet processing thread * Arguments: * void *ssnptr: session pointer * void **file_mem: the pointer to store the memory block * that stores file and its metadata. * It will set NULL if no memory or fail to store * * Returns: * FileCaptureState: * FILE_CAPTURE_SUCCESS = 0, * FILE_CAPTURE_MIN, * FILE_CAPTURE_MAX, * FILE_CAPTURE_MEMCAP, * FILE_CAPTURE_FAIL */ Reserve_file_func reserve_file; /* * Get the file that is reserved in memory. To get a full file, * this function must be called iteratively until NULL is returned * This function can be called in out of band thread * * Arguments: * void *file_mem: the memory block working on * uint8_t **buff: address to store buffer address * int *size: address to store size of file * * Returns: * the next memory block * If NULL: no memory or fail to get file */ Get_file_func read_file; /* * Get the file size captured in the file buffer * This function can be called in out of band thread * * Arguments: * void *file_mem: the first memory block of file buffer * * Returns: * the size of file * If 0: no memory or fail to read file */ File_capture_size_func get_file_capture_size; /* * Release the file that is reserved in memory. * This function can be called in out of band thread. * * Arguments: * void *data: the memory block that stores file and its metadata * * Returns: * None */ Release_file_func release_file; /* Return the file rule id associated with a session. * * Arguments: * void *ssnptr: session pointer * * Returns: * (u32) file-rule id on session; FILE_TYPE_UNKNOWN otherwise. */ Get_file_type_id get_file_type_id; /* Create a file context to use * * Arguments: * void* ssnptr: session pointer * Returns: * FileContext *: file context created. */ Create_file_context_func create_file_context; /* Intialize a file context * * Arguments: * void* ssnptr: session pointer * Returns: * FileContext *: file context. */ Init_file_context_func init_file_context; /* Set file context to be the current * * Arguments: * void* ssnptr: session pointer * FileContext *: file context that will be current * Returns: * True: changed successfully * False: fail to change */ Set_file_context_func set_current_file_context; /* Get current file context * * Arguments: * void* ssnptr: session pointer * Returns: * FileContext *: current file context */ Get_file_context_func get_current_file_context; /* Get main file context that used by preprocessors * * Arguments: * void* ssnptr: session pointer * Returns: * FileContext *: main file context */ Get_file_context_func get_main_file_context; /* Process file function, called by preprocessors that provides file data * * Arguments: * void* ctx: file context that will be processed * void* p: packet pointer * uint8_t* file_data: file data * int data_size: file data size * FilePosition: file position * bool suspend_block_verdict: used for smb to allow file pass * Returns: * 1: continue processing/log/block this file * 0: ignore this file (no further processing needed) */ Process_file_func process_file; /* Create the file cache that store file segments and properties. * * Arguments: * uint64_t: total memory available for file cache, including file contexts * uint32_t: maximal number of files pruned when memcap is reached * Returns: * struct _FileCache *: file cache pointer */ File_cache_create_func file_cache_create; /* Free the file cache that store file segments and properties. * * Arguments: * struct _FileCache *: file cache pointer * Returns: * None */ File_cache_free_func file_cache_free; /* Get the status of file cache for troubleshooting. * * Arguments: * struct _FileCache *: file cache pointer * Returns: * FileCacheStatus *: status of file cache */ File_cache_status_func file_cache_status; /* Get a new file entry in the file cache, if already exists, update file name * * Arguments: * struct _FileCache *: file cache that stores file segments * void* : packet pointer * uint64_t: file id that is unique * uint8_t *: file name * uint32_t: file name size * Returns: * None */ File_cache_update_entry_func file_cache_update_entry; /* Process file segment, when file segment is in order, file data will be * processed; otherwise it is stored. * * Arguments: * struct _FileCache *: file cache that stores file segments * void* : packet pointer * uint64_t: file id that is unique * uint64_t: total file size, * const uint8_t*: file data * int: file data size * uint64_t: file data offset in the file * bool: true for upload, false for download * Returns: * 1: continue processing/log/block this file * 0: ignore this file (no further processing needed) */ File_segment_process_func file_segment_process; /* Return a unique file instance number * * Arguments: * void *ssnptr: session pointer * Returns: * (u32) a unique file instance id. */ Get_new_file_instance get_new_file_instance; GetFilePosition get_file_position; Get_max_file_capture_size get_max_file_capture_size; File_config_malware_check file_config_malware_check; /* Return the character encoding of a buffer * Arguments: * uint8 *: input buffer * uint32 : input buffer length * Returns: * FileCharEncoding SNORT_CHAR_ENCODING_ASCII = 0, SNORT_CHAR_ENCODING_UTF_16LE, SNORT_CHAR_ENCODING_UTF_16BE */ Get_character_encoding get_character_encoding; File_cache_mem_adjust_func file_cache_shrink_to_memcap; File_cache_mem_set_func file_cache_set_memcap; File_signature_reset file_signature_reset; /* Return a char string that indicates the file type * Arguments: * void * ssnptr: session pointer * Returns: * File Type name */ File_get_filetype_func file_get_filetype; /* Logging a file event */ File_event_log_dump_func file_event_log_dump; Set_file_partial_func set_file_partial; } FileAPI; /* To be set by Stream */ extern FileAPI *file_api; static inline void initFilePosition(FilePosition *position, uint64_t processed_size) { *position = SNORT_FILE_START; if (processed_size) *position = SNORT_FILE_MIDDLE; } static inline void updateFilePosition(FilePosition *position, uint64_t processed_size) { if ((*position == SNORT_FILE_END) || (*position == SNORT_FILE_FULL)) *position = SNORT_FILE_START; else if (processed_size) *position = SNORT_FILE_MIDDLE; } static inline void finalFilePosition(FilePosition *position) { if (*position == SNORT_FILE_START) *position = SNORT_FILE_FULL; else if (*position != SNORT_FILE_FULL) *position = SNORT_FILE_END; } static inline bool isFileStart(FilePosition position) { return ((position == SNORT_FILE_START) || (position == SNORT_FILE_FULL)); } static inline bool isFileEnd(FilePosition position) { return ((position == SNORT_FILE_END) || (position == SNORT_FILE_FULL)); } #endif /* FILE_API_H_ */ snort-2.9.20/src/file-process/file_service_config.h0000644000175000017500000000365314241076570020425 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.2012 - Initial Source Code. Hcao */ #ifndef __FILE_SERVICE_CONFIG_H__ #define __FILE_SERVICE_CONFIG_H__ /* Default file type/signature/capture values. */ /* configure file services * * Args: * struct _SnortConfig* sc: the snort configuration * char *args: configuration string * void *file_config: pointer to file config */ struct _SnortConfig; void file_service_config(struct _SnortConfig* sc, char *args, void *file_config); /* Create file service configuration and set default values * * Return: * void *: pointer to file configuration */ struct _fileConfig; struct _fileConfig* file_service_config_create(void); # ifdef SNORT_RELOAD /* Verify whether file configuration is valid * changing memory settings and depth settings * requires snort restart * * Return * 0: valid * -1: invalid */ int file_sevice_config_verify(SnortConfig *old, SnortConfig *new); # endif /* ifdef SNORT_RELOAD */ #endif /* #ifndef __FILE_SERVICE_CONFIG_H__ */ snort-2.9.20/src/file-process/file_capture.c0000644000175000017500000005250514241076541017074 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.05.2013 - Initial Source Code. Hcao */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "file_capture.h" #include "file_mempool.h" #include "util.h" #include #include "sf_sechash.h" #include "snort.h" #include "stream_api.h" #include "file_config.h" #include "file_stats.h" #include "file_service.h" #include "memory_stats.h" #include #include #include #include #include SafeMemPool *file_mempool = NULL; File_Capture_Stats file_capture_stats; /* * Verify file capture information and file context information matched * This is used for debug purpose */ #ifdef DEBUG_MSGS static void verify_file_capture_info(FileContext* context, FileCaptureInfo *fileInfo) { /* file capture length should be one of two possible values */ if (context->processed_bytes) { if ((fileInfo->file_size != context->processed_bytes) && (fileInfo->file_size + context->current_data_len != context->processed_bytes)) { FILE_DEBUG("File capture size failed w.r.t processed size!"); } } else { if ((fileInfo->file_size != context->file_size) && (fileInfo->file_size + context->current_data_len != context->file_size)) { FILE_DEBUG("File capture size failed w.r.t final file size!"); } } } static void verifiy_file_capture(FileContext* context, FileCaptureInfo *fileInfo) { SHA256CONTEXT sha_ctx; uint8_t *buff; int size; FileCaptureInfo *file_mem = fileInfo; uint8_t sha256[SHA256_HASH_SIZE + 1]; int i; memset(&sha_ctx, 0, sizeof(sha_ctx)); /*Calculator the SHA*/ SHA256INIT(&sha_ctx); while (file_mem) { file_mem = file_capture_read(file_mem, &buff, &size); SHA256UPDATE(&sha_ctx, buff, size); } SHA256FINAL(sha256, &sha_ctx); for (i = 0; i < SHA256_HASH_SIZE; i++) { if (sha256[i] != context->sha256[i]) { FILE_DEBUG("File capture buffer is wrong! SHA256 mismatch"); break; } } } #endif /* * Initialize the file memory pool * * Returns: * void *: pointer to mempool * NULL : fail to initialize file mempool */ static void* _init_file_mempool(int64_t max_file_mem, int64_t block_size) { int max_files; SafeMemPool *file_mempool; int64_t max_file_mem_in_bytes; /*Convert megabytes to bytes*/ max_file_mem_in_bytes = max_file_mem * 1024 * 1024; if (block_size <= 0) return NULL; if (block_size & 7) block_size += (8 - (block_size & 7)); max_files = max_file_mem_in_bytes / block_size ; file_mempool = (SafeMemPool *)SnortPreprocAlloc(1, sizeof(SafeMemPool), PP_FILE, PP_MEM_CATEGORY_MEMPOOL); if ((!file_mempool)|| (safe_mempool_init(file_mempool, max_files, block_size) != 0)) { FatalError( "File capture: Could not allocate file buffer mempool.\n"); } return file_mempool; } /* * Initialize the file memory pool * * Arguments: * int64_t max_file_mem: memcap in megabytes * int64_t block_size: file block size (metadata size excluded) * * Returns: NONE */ void file_capture_init_mempool(int64_t max_file_mem, int64_t block_size) { int64_t metadata_size = sizeof (FileCaptureInfo); if(!file_mempool) file_mempool = _init_file_mempool(max_file_mem, block_size + metadata_size); } /* Free file buffer list*/ static inline void _free_file_buffer(FileCaptureInfo *fileInfo) { file_capture_stats.files_freed_total++; while (fileInfo) { if (safe_mempool_free(file_mempool, fileInfo) != SAFE_MEM_SUCCESS) file_capture_stats.file_buffers_free_errors++; fileInfo = fileInfo->next; file_capture_stats.file_buffers_freed_total++; } } /* Release file buffer list*/ static inline void _release_file_buffer(FileCaptureInfo *fileInfo) { file_capture_stats.files_released_total++; while (fileInfo) { if (safe_mempool_release(file_mempool, fileInfo) != SAFE_MEM_SUCCESS) file_capture_stats.file_buffers_release_errors++; fileInfo = fileInfo->next; file_capture_stats.file_buffers_released_total++; } } /* * Stop file capture, memory resource will be released if not reserved * * Returns: NONE */ void file_capture_stop( FileContext* context) { FileCaptureInfo *fileInfo = context->file_capture; if (fileInfo) { /*free mempool*/ if(!fileInfo->reserved) { _free_file_buffer(fileInfo); } context->file_capture = NULL; } context->file_capture_enabled = false; } /* * Create file buffer in file mempool * * Args: * SafeMemPool *file_mempool: file mempool * FileContext* context: file context * * Returns: * FileCaptureInfo *: memory block that starts with file capture information */ static inline FileCaptureInfo * _create_file_buffer(SafeMemPool *file_mempool) { FileCaptureInfo *fileInfo; uint64_t num_files_queued; fileInfo = (FileCaptureInfo*)safe_mempool_alloc(file_mempool); if(fileInfo == NULL) { FILE_DEBUG("Failed to get file capture memory!"); file_capture_stats.file_memcap_failures_total++; return NULL; } file_capture_stats.file_buffers_allocated_total++; fileInfo->length = 0; fileInfo->reserved = false; fileInfo->next = NULL; /*Only one block initially*/ fileInfo->last = fileInfo; fileInfo->file_size = 0; num_files_queued = safe_mempool_allocated(file_mempool); if (file_capture_stats.file_buffers_used_max < num_files_queued) file_capture_stats.file_buffers_used_max = num_files_queued; return fileInfo; } /* * Save file to the buffer * If file needs to be extracted, buffer will be reserved * If file buffer isn't sufficient, need to add another buffer. * * Returns: * 0: successful or file capture is disabled * 1: fail to capture the file */ static inline int _save_to_file_buffer(SafeMemPool *file_mempool, FileContext* context, uint8_t* file_data, int data_size, int64_t max_size) { FileCaptureInfo *fileInfo = (FileCaptureInfo *) context->file_capture; FileCaptureInfo *lastBlock = fileInfo->last; uint64_t available_bytes; FileConfig *file_config = (FileConfig *)(snort_conf->file_config); DEBUG_WRAP(verify_file_capture_info(context, fileInfo);); if ( data_size + (signed)fileInfo->file_size > max_size) { FILE_DEBUG("Exceeding max file capture size!"); file_capture_stats.file_size_exceeded++; context->file_state.capture_state = FILE_CAPTURE_MAX; return -1; } /* Check whether current file block can hold file data*/ available_bytes = file_config->file_capture_block_size - lastBlock->length; if ( available_bytes > file_config->file_capture_block_size) { context->file_state.capture_state = FILE_CAPTURE_MEMCAP; return -1; } if ( data_size > available_bytes) { FileCaptureInfo *new_block; uint8_t* file_current = file_data; uint8_t* file_end = file_data + data_size; /*can't hold all, use current block first*/ memcpy((uint8_t *)lastBlock + lastBlock->length + sizeof (*lastBlock), file_current, available_bytes ); lastBlock->length = file_config->file_capture_block_size; file_current += available_bytes; /* We can support any file capture block size */ while (1) { /*get another block*/ new_block = (FileCaptureInfo *)_create_file_buffer(file_mempool); if(new_block == NULL) { FILE_ERROR("Failed to save in file buffer: FILE_CAPTURE_MEMCAP"); context->file_state.capture_state = FILE_CAPTURE_MEMCAP; return -1; } fileInfo->last->next = new_block; fileInfo->last = new_block; /*Save data to the new block*/ if (file_current + file_config->file_capture_block_size < file_end) { memcpy((uint8_t *)fileInfo->last + sizeof(*new_block), file_current, file_config->file_capture_block_size); new_block->length = file_config->file_capture_block_size; file_current += file_config->file_capture_block_size; } else { memcpy((uint8_t *)fileInfo->last + sizeof(*new_block), file_current, file_end - file_current); new_block->length = file_end - file_current; break; } } } else { memcpy((uint8_t *)lastBlock + lastBlock->length + sizeof(*lastBlock), file_data, data_size); lastBlock->length += data_size; } fileInfo->file_size += data_size; DEBUG_WRAP(verify_file_capture_info(context, fileInfo);) return 0; } /* * Save files to the local buffer first for files transferred * by multiple reassembled packets. For files within a packet, * simply using the packet buffer. * If file needs to be extracted, buffer will be reserved * * Arguments: * FileContext* context: current file context * uint8_t *file_data: current file data * int data_size: current file data size * FilePosition position: position of file data * Returns: * 0: successful * 1: fail to capture the file or file capture is disabled */ int file_capture_process( FileContext* context, uint8_t* file_data, int data_size, FilePosition position ) { FileCaptureInfo *fileInfo = (FileCaptureInfo *) context->file_capture; FileConfig *file_config = (FileConfig *)(snort_conf->file_config); context->current_data = file_data; context->current_data_len = data_size; FILE_DEBUG("Processing capture: context: %p, data_size: %d, position: %d", context, data_size, position); switch (position) { case SNORT_FILE_FULL: file_capture_stats.file_within_packet++; break; case SNORT_FILE_END: break; case SNORT_FILE_START: case SNORT_FILE_MIDDLE: if(FILE_SIG_FLUSH == context->file_state.sig_state){ break; } default: /* For File position is either SNORT_FILE_START * or SNORT_FILE_MIDDLE, the file is larger than one packet, * we need to store them into buffer. */ if(!context->file_capture) { fileInfo = _create_file_buffer(file_mempool); if (!fileInfo) { FILE_ERROR("Processing file capture: Can't get file capture memory, FILE_CAPTURE_MEMCAP"); context->file_state.capture_state = FILE_CAPTURE_MEMCAP; return -1; } file_capture_stats.files_buffered_total++; context->file_capture = fileInfo; } if (!fileInfo) { FILE_ERROR("Processing file capture: File info not available"); return -1; } if (_save_to_file_buffer(file_mempool, context, file_data, data_size, file_config->file_capture_max_size)) { FILE_ERROR("Processing file capture: Can't save to file buffer!"); return -1; } } return 0; } /*Helper function for error*/ static inline FileCaptureState ERROR_capture(FileCaptureState state) { file_capture_stats.file_reserve_failures++; return state; } /* * Preserve the file in memory until it is released * * Arguments: * void *ssnptr: session pointer * void **file_mem: the pointer to store the memory block * that stores file and its metadata. * It will set NULL if no memory or fail to store * * Returns: * FileCaptureState * */ FileCaptureState file_capture_reserve(void *ssnptr, FileCaptureInfo **file_mem) { FileContext* context; FileCaptureInfo *fileInfo; uint64_t fileSize; FileConfig *file_config = (FileConfig *)(snort_conf->file_config); if (!ssnptr||!file_config||!file_mem) { FILE_ERROR("Capture failed: No session/memory/config"); return ERROR_capture(FILE_CAPTURE_FAIL); } context = get_current_file_context(ssnptr); if (!context || !context->file_capture_enabled) { FILE_ERROR("Capture failed: Not enabled"); return ERROR_capture(FILE_CAPTURE_FAIL); } if (context->file_state.capture_state != FILE_CAPTURE_SUCCESS) { FILE_ERROR("Capture failed: %d",context->file_state.capture_state); return ERROR_capture(context->file_state.capture_state); } fileInfo = (FileCaptureInfo *)(context->file_capture); /* * Note: file size is updated at this point */ fileSize = context->file_size; if ((!context->partial_file) && (fileSize < (unsigned) file_config->file_capture_min_size)) { file_capture_stats.file_size_min++; FILE_ERROR("Capture failed: FILE_CAPTURE_MIN, size: %d",fileSize); return ERROR_capture(FILE_CAPTURE_MIN); } if ( fileSize > (unsigned) file_config->file_capture_max_size) { file_capture_stats.file_size_max++; FILE_ERROR("Capture failed: FILE_CAPTURE_MAX, size: %d",fileSize); return ERROR_capture(FILE_CAPTURE_MAX); } /* Create a file buffer if it is not done yet, * This is the case for small file */ if(!fileInfo && context->file_capture_enabled) { fileInfo = _create_file_buffer(file_mempool); if (!fileInfo) { file_capture_stats.file_memcap_failures_reserve++; FILE_ERROR("Capture failed: FILE_CAPTURE_MEMCAP"); return ERROR_capture(FILE_CAPTURE_MEMCAP); } file_capture_stats.files_buffered_total++; context->file_capture = fileInfo; DEBUG_WRAP(verify_file_capture_info(context, fileInfo);); } if (!fileInfo) { FILE_ERROR("Capture failed: FILE_CAPTURE_MEMCAP"); return ERROR_capture(FILE_CAPTURE_MEMCAP); } DEBUG_WRAP(verify_file_capture_info(context, fileInfo);); /*Copy the last piece of file to file buffer*/ if (_save_to_file_buffer(file_mempool, context, context->current_data, context->current_data_len, file_config->file_capture_max_size) ) { FILE_ERROR("Capture failed: %d",context->file_state.capture_state); return ERROR_capture(context->file_state.capture_state); } file_capture_stats.files_captured_total++; *file_mem = fileInfo; fileInfo->reserved = true; /* Clear file capture information on file context * Without this, the next file within the same session * might use this information to change shared memory buffer * that might be released and then used by other sessions */ if(context->file_state.sig_state != FILE_SIG_FLUSH) { context->file_capture = NULL; context->file_capture_enabled = false; } DEBUG_WRAP(verifiy_file_capture(context, fileInfo);); FILE_INFO("Capture successful"); return FILE_CAPTURE_SUCCESS; } /* * Get the file that is reserved in memory * * Arguments: * void *: the memory block that stores file and its metadata * uint8_t **buff: address to store buffer address * int *size: address to store size of file * * Returns: * the next memory block that stores file and its metadata * NULL: no more file data or fail to get file */ void* file_capture_read(FileCaptureInfo *fileInfo, uint8_t **buff, int *size) { if (!fileInfo || !buff || !size) { FILE_ERROR("Failed to read capture"); return NULL; } *buff = (uint8_t *)fileInfo + sizeof(*fileInfo); *size = fileInfo->length; return (fileInfo->next); } /* * Get the file size captured in the file buffer * * Arguments: * void *file_mem: the first memory block of file buffer * * Returns: * the size of file * 0: not the first file block or fail to get file */ size_t file_capture_size(FileCaptureInfo *fileInfo) { if (!fileInfo) return 0; return fileInfo->file_size; } /* * Release the file that is reserved in memory, this function might be * called in a different thread. * * Arguments: * void *data: the memory block that stores file and its metadata */ void file_capture_release(FileCaptureInfo *fileInfo) { if (!fileInfo) return; fileInfo->reserved = false; _release_file_buffer(fileInfo); } /*Log file capture mempool usage*/ void file_capture_mem_usage(void) { if (file_mempool) { LogMessage("Maximum buffers can allocate: "FMTu64("-10")" \n", file_mempool->total); LogMessage("Number of buffers in use: "FMTu64("-10")" \n", safe_mempool_allocated(file_mempool)); LogMessage("Number of buffers in free list: "FMTu64("-10")" \n", safe_mempool_freed(file_mempool)); LogMessage("Number of buffers in release list: "FMTu64("-10")" \n", safe_mempool_released(file_mempool)); } } /* * Release all file capture memory etc, * this must be called when snort exits */ void file_caputure_close(void) { if (safe_mempool_destroy(file_mempool) == 0) { SnortPreprocFree(file_mempool, sizeof(SafeMemPool), PP_FILE, PP_MEM_CATEGORY_MEMPOOL); file_mempool = NULL; } } int FilePrintMemStats(FILE *fd, char* buffer, PreprocMemInfo *meminfo) { time_t curr_time = time(NULL); int len = 0; size_t total_heap_memory = meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory + meminfo[PP_MEM_CATEGORY_MEMPOOL].used_memory; if (fd) { len = fprintf(fd, ",%lu,%u,%u" ",%lu,%u,%u" ",%lu,%u,%u,%lu" , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_MEMPOOL].used_memory , meminfo[PP_MEM_CATEGORY_MEMPOOL].num_of_alloc , meminfo[PP_MEM_CATEGORY_MEMPOOL].num_of_free , total_heap_memory); return len; } if (buffer) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics for File at: %s\n" "Total buffers allocated: "FMTu64("-10")" \n" "Total buffers freed: "FMTu64("-10")" \n" "Total buffers released: "FMTu64("-10")" \n" "Total file mempool: "FMTu64("-10")" \n" "Total allocated file mempool: "FMTu64("-10")" \n" "Total freed file mempool: "FMTu64("-10")" \n" "Total released file mempool: "FMTu64("-10")" \n" , ctime(&curr_time) , file_capture_stats.file_buffers_allocated_total , file_capture_stats.file_buffers_freed_total , file_capture_stats.file_buffers_released_total , (file_mempool)?(file_mempool->total):0 , (file_mempool)?(safe_mempool_allocated(file_mempool)):0 , (file_mempool)?(safe_mempool_freed(file_mempool)):0 , (file_mempool)?(safe_mempool_released(file_mempool)):0); } else { LogMessage("\n"); LogMessage("Memory Statistics for File at:%s\n" , ctime(&curr_time)); LogMessage("Total buffers allocated: "FMTu64("-10")" \n", file_capture_stats.file_buffers_allocated_total); LogMessage("Total buffers freed: "FMTu64("-10")" \n", file_capture_stats.file_buffers_freed_total); LogMessage("Total buffers released: "FMTu64("-10")" \n", file_capture_stats.file_buffers_released_total); LogMessage("Total file mempool: "FMTu64("-10")" \n", (file_mempool)?(file_mempool->total):0); LogMessage("Total allocated file mempool: "FMTu64("-10")" \n", (file_mempool)?(safe_mempool_allocated(file_mempool)):0); LogMessage("Total freed file mempool: "FMTu64("-10")" \n", (file_mempool)?(safe_mempool_freed(file_mempool)):0); LogMessage("Total released file mempool: "FMTu64("-10")" \n", (file_mempool)?(safe_mempool_released(file_mempool)):0); } return len; } snort-2.9.20/src/file-process/file_mime_process.c0000644000175000017500000014321614241076552020120 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "file_mail_common.h" #include "file_mime_process.h" #include "mempool.h" #include "file_api.h" #include "snort_bounds.h" #include "util.h" #include "str_search.h" #include "decode.h" #include "detection_util.h" #include "memory_stats.h" #include "stream_api.h" #include "reg_test.h" #include #ifdef HAVE_STRINGS_H #include #endif /* state flags */ #define MIME_FLAG_FOLDING 0x00000001 #define MIME_FLAG_IN_CONTENT_TYPE 0x00000002 #define MIME_FLAG_GOT_BOUNDARY 0x00000004 #define MIME_FLAG_DATA_HEADER_CONT 0x00000008 #define MIME_FLAG_IN_CONT_TRANS_ENC 0x00000010 #define MIME_FLAG_EMAIL_ATTACH 0x00000020 #define MIME_FLAG_MULTIPLE_EMAIL_ATTACH 0x00000040 #define MIME_FLAG_MIME_END 0x00000080 #define MIME_FLAG_IN_CONT_DISP 0x00000200 #define MIME_FLAG_IN_CONT_DISP_CONT 0x00000400 #define STATE_DATA_INIT 0 #define STATE_DATA_HEADER 1 /* Data header section of data state */ #define STATE_DATA_BODY 2 /* Data body section of data state */ #define STATE_MIME_HEADER 3 /* MIME header section within data section */ #define STATE_DATA_UNKNOWN 4 /* Maximum length of header chars before colon, based on Exim 4.32 exploit */ #define MAX_HEADER_NAME_LEN 64 typedef struct _MimeToken { char *name; int name_len; int search_id; } MimeToken; typedef enum _MimeHdrEnum { HDR_CONTENT_TYPE = 0, HDR_CONT_TRANS_ENC, HDR_CONT_DISP, HDR_LAST } MimeHdrEnum; const MimeToken mime_hdrs[] = { {"Content-type:", 13, HDR_CONTENT_TYPE}, {"Content-Transfer-Encoding:", 26, HDR_CONT_TRANS_ENC}, {"Content-Disposition:", 20, HDR_CONT_DISP}, {NULL, 0, 0} }; typedef struct _MIMESearch { char *name; int name_len; } MIMESearch; typedef struct _MIMESearchInfo { int id; int index; int length; } MIMESearchInfo; MIMESearchInfo mime_search_info; void *mime_hdr_search_mpse = NULL; MIMESearch mime_hdr_search[HDR_LAST]; MIMESearch *mime_current_search = NULL; static const char *boundary_str = "boundary="; static char *preprocessor = NULL; /* Extract the filename from the header */ static inline int extract_file_name(const char **start, int length, bool *disp_cont) { const char *tmp = NULL; const char *end = *start+length; if (length <= 0) return -1; if (!(*disp_cont)) { tmp = SnortStrcasestr(*start, length, "filename"); if( tmp == NULL ) return -1; tmp = tmp + 8; while( (tmp < end) && ((isspace(*tmp)) || (*tmp == '=') )) { tmp++; } } else tmp = *start; if(tmp < end) { if(*tmp == '"' || (*disp_cont)) { if(*tmp == '"') { if(*disp_cont) { *disp_cont = false; return (tmp - *start); } tmp++; } *start = tmp; tmp = SnortStrnPbrk(*start ,(end - tmp),"\""); if(tmp == NULL ) { if ((end - tmp) > 0 ) { tmp = end; *disp_cont = true; } else return -1; } else *disp_cont = false; end = tmp; } else { *start = tmp; } return (end - *start); } else { return -1; } } /* accumulate MIME attachment filenames. The filenames are appended by commas * start - If extract_fname is true,start is ptr in MIME header * to extract file names from. Else, if extract_fname * is false, start is the filename * length - If extract_fname is false , length is the strlen of * filename contained in start * * log_state - file log state * disp_cont * extract_fname - false, when filename is already extracted * and passed to the first argument */ int log_file_name(const uint8_t *start, int length, FILE_LogState *log_state, bool *disp_cont, bool extract_fname) { uint8_t *alt_buf; int alt_size; uint16_t *alt_len; int ret=0; int cont =0; int log_avail = 0; if ( extract_fname ) { if(!start || (length <= 0)) { *disp_cont = false; return -1; } if(*disp_cont) cont = 1; ret = extract_file_name((const char **)(&start), length, disp_cont); } else { if(*disp_cont) cont = 1; /* Since the file name is already passed as parameter and the length as well, * just set ret here*/ ret = length; } if (ret == -1) return ret; length = ret; alt_buf = log_state->filenames; alt_size = MAX_FILE; alt_len = &(log_state->file_logged); log_avail = alt_size - *alt_len; if(!alt_buf || (log_avail <= 0)) return -1; if ( *alt_len > 0 && ((*alt_len + 1) < alt_size)) { if(!cont) { alt_buf[*alt_len] = ','; *alt_len = *alt_len + 1; } } ret = SafeMemcpy(alt_buf + *alt_len, start, length, alt_buf, alt_buf + alt_size); if (ret != SAFEMEM_SUCCESS) { if(*alt_len != 0) *alt_len = *alt_len - 1; return -1; } log_state->file_current = *alt_len; *alt_len += length; return 0; } static void update_file_name(MAIL_LogState *log_state) { if (log_state) log_state->file_log.file_name = log_state->file_log.file_current; } static void set_file_name_from_log(FILE_LogState *log_state, void *ssn) { if ((log_state) && (log_state->file_logged > log_state->file_current)) { if (log_state->file_current > log_state->file_name) file_api->set_file_name(ssn, log_state->filenames + log_state->file_name, log_state->file_current -log_state->file_name - 1, false); else file_api->set_file_name(ssn, log_state->filenames + log_state->file_current, log_state->file_logged -log_state->file_current, false); } else { file_api->set_file_name(ssn, NULL, 0, false); } } /* * Return: 0: success * -1: fail * */ int set_log_buffers(MAIL_LogState **log_state, MAIL_LogConfig *conf, void *mempool, void* scbPtr, uint32_t preproc_id) { MemPool *log_mempool = (MemPool *)mempool; if((*log_state == NULL) && (conf->log_email_hdrs || conf->log_filename || conf->log_mailfrom || conf->log_rcptto)) { MemBucket *bkt = mempool_alloc(log_mempool); if(bkt == NULL) return -1; *log_state = (MAIL_LogState *)SnortPreprocAlloc(1, sizeof(MAIL_LogState), preproc_id, 0); if((*log_state) != NULL) { bkt->scbPtr = scbPtr; (*log_state)->log_hdrs_bkt = bkt; (*log_state)->log_depth = conf->email_hdrs_log_depth; (*log_state)->recipients = (uint8_t *)bkt->data; (*log_state)->rcpts_logged = 0; (*log_state)->senders = (uint8_t *)bkt->data + MAX_EMAIL; (*log_state)->snds_logged = 0; (*log_state)->file_log.filenames = (uint8_t *)bkt->data + (2*MAX_EMAIL); (*log_state)->file_log.file_logged = 0; (*log_state)->file_log.file_current = 0; (*log_state)->file_log.file_name = 0; (*log_state)->emailHdrs = (unsigned char *)bkt->data + (2*MAX_EMAIL) + MAX_FILE; (*log_state)->hdrs_logged = 0; } else { /*free bkt if calloc fails*/ mempool_free(log_mempool, bkt); return -2; } } return 0; } static void set_mime_buffers(MimeState *ssn, void* scbPtr, uint32_t preproc_id) { if ((ssn != NULL) && (ssn->decode_state == NULL)) { MemBucket *bkt = mempool_alloc(ssn->mime_mempool); DecodeConfig *conf= ssn->decode_conf; if (bkt != NULL) { ssn->decode_state = SnortPreprocAlloc(1, sizeof(Email_DecodeState), preproc_id, 0); if((ssn->decode_state) != NULL ) { bkt->scbPtr = scbPtr; ssn->decode_bkt = bkt; SetEmailDecodeState((Email_DecodeState *)(ssn->decode_state), bkt->data, conf->max_depth, conf->b64_depth, conf->qp_depth, conf->uu_depth, conf->bitenc_depth, conf->file_depth); } else { /*free mempool if calloc fails*/ mempool_free(ssn->mime_mempool, bkt); } } else { if (ssn->mime_stats) { if(((MimeStats *)ssn->mime_stats)->memcap_exceeded % 10000 == 0 && preprocessor ) LogMessage("WARNING: %s max_mime_mem exceeded",preprocessor); ((MimeStats *)ssn->mime_stats)->memcap_exceeded++; } DEBUG_WRAP(DebugMessage(DEBUG_FILE, "No memory available for decoding. Memcap exceeded: %s preprocessor \n", preprocessor);); } } } void* init_mime_mempool(int max_mime_mem, int max_depth, void *mempool, const char *preproc_name) { int encode_depth; int max_sessions; MemPool *mime_mempool = (MemPool *)mempool; if (mime_mempool != NULL) return mime_mempool ; if (max_depth <= 0) return NULL; encode_depth = max_depth; if (encode_depth & 7) encode_depth += (8 - (encode_depth & 7)); max_sessions = max_mime_mem / ( 2 * encode_depth); mime_mempool = (MemPool *)SnortPreprocAlloc(1, sizeof(MemPool), PP_FILE, PP_MEM_CATEGORY_MEMPOOL); if ((!mime_mempool)||(mempool_init(mime_mempool, max_sessions, (2 * encode_depth)) != 0)) { FatalError( "%s: Could not allocate %s mime mempool.\n", preproc_name, preproc_name); } return mime_mempool; } void* init_log_mempool(uint32_t email_hdrs_log_depth, uint32_t memcap, void *mempool, const char *preproc_name) { uint32_t max_bkt_size; uint32_t max_sessions_logged; MemPool *log_mempool = (MemPool *) mempool; if (log_mempool != NULL) return log_mempool; max_bkt_size = ((2* MAX_EMAIL) + MAX_FILE + email_hdrs_log_depth); max_sessions_logged = memcap/max_bkt_size; log_mempool = (MemPool *)SnortPreprocAlloc(1, sizeof(*log_mempool), PP_FILE, PP_MEM_CATEGORY_MEMPOOL); if ((!log_mempool)||(mempool_init(log_mempool, max_sessions_logged, max_bkt_size) != 0)) { if(!max_sessions_logged) { FatalError( "%s: Could not allocate %s mempool.\n", preproc_name, preproc_name); } else { FatalError( "%s: Error setting the \"memcap\" \n", preproc_name); } } return log_mempool; } void get_mime_eol(const uint8_t *ptr, const uint8_t *end, const uint8_t **eol, const uint8_t **eolm) { const uint8_t *tmp_eol; const uint8_t *tmp_eolm; /* XXX maybe should fatal error here since none of these * pointers should be NULL */ if (ptr == NULL || end == NULL || eol == NULL || eolm == NULL) return; tmp_eol = (uint8_t *)memchr(ptr, '\n', end - ptr); if (tmp_eol == NULL) { tmp_eol = end; tmp_eolm = end; } else { /* end of line marker (eolm) should point to marker and * end of line (eol) should point to end of marker */ if ((tmp_eol > ptr) && (*(tmp_eol - 1) == '\r')) { tmp_eolm = tmp_eol - 1; } else { tmp_eolm = tmp_eol; } /* move past newline */ tmp_eol++; } *eol = tmp_eol; *eolm = tmp_eolm; } /* * Callback function for string search * * @param id id in array of search strings from mime_config.cmds * @param index index in array of search strings from mime_config.cmds * @param data buffer passed in to search function * * @return response * @retval 1 commands caller to stop searching */ static int search_str_found(void *id, void *unused, int index, void *data, void *unused2) { int search_id = (int)(uintptr_t)id; mime_search_info.id = search_id; mime_search_info.index = index; mime_search_info.length = mime_current_search[search_id].name_len; /* Returning non-zero stops search, which is okay since we only look for one at a time */ return 1; } static inline void process_decode_type(const char *start, int length, bool cnt_xf, MimeState *mime_ssn) { const char *tmp = NULL; Email_DecodeState *decode_state = (Email_DecodeState *)(mime_ssn->decode_state); if(cnt_xf) { if(decode_state->b64_state.encode_depth > -1) { tmp = SnortStrcasestr(start, length, "base64"); if( tmp != NULL ) { decode_state->decode_type = DECODE_B64; if (mime_ssn->mime_stats) ((MimeStats *)mime_ssn->mime_stats)->attachments[DECODE_B64]++; return; } } if(decode_state->qp_state.encode_depth > -1) { tmp = SnortStrcasestr(start, length, "quoted-printable"); if( tmp != NULL ) { decode_state->decode_type = DECODE_QP; if (mime_ssn->mime_stats) ((MimeStats *)mime_ssn->mime_stats)->attachments[DECODE_QP]++; return; } } if(decode_state->uu_state.encode_depth > -1) { tmp = SnortStrcasestr(start, length, "uuencode"); if( tmp != NULL ) { decode_state->decode_type = DECODE_UU; if (mime_ssn->mime_stats) ((MimeStats *)mime_ssn->mime_stats)->attachments[DECODE_UU]++; return; } } } if(decode_state->bitenc_state.depth > -1) { decode_state->decode_type = DECODE_BITENC; if (mime_ssn->mime_stats) ((MimeStats *)mime_ssn->mime_stats)->attachments[DECODE_BITENC]++; return; } return; } static inline void setup_decode(const char *data, int size, bool cnt_xf, MimeState *mime_ssn, void* scbPtr, uint32_t preproc_id) { /* Check for Encoding Type */ if( file_api->is_decoding_enabled(mime_ssn->decode_conf) && !mime_ssn->decode_conf->ignore_data) { set_mime_buffers(mime_ssn, scbPtr, preproc_id); if(mime_ssn->decode_state != NULL) { ResetBytesRead((Email_DecodeState *)(mime_ssn->decode_state)); process_decode_type(data, size, cnt_xf, mime_ssn ); mime_ssn->state_flags |= MIME_FLAG_EMAIL_ATTACH; /* check to see if there are other attachments in this packet */ if( ((Email_DecodeState *)(mime_ssn->decode_state))->decoded_bytes ) mime_ssn->state_flags |= MIME_FLAG_MULTIPLE_EMAIL_ATTACH; } } } /* * Handle Headers - Data or Mime * * @param packet standard Packet structure * * @param i index into p->payload buffer to start looking at data * * @return i index into p->payload where we stopped looking at data */ static const uint8_t * process_mime_header(Packet *p, const uint8_t *ptr, const uint8_t *data_end_marker, MimeState *mime_ssn, uint32_t preproc_id) { const uint8_t *eol; const uint8_t *eolm; const uint8_t *colon; const uint8_t *content_type_ptr = NULL; const uint8_t *cont_trans_enc = NULL; const uint8_t *cont_disp = NULL; int header_found; const uint8_t *start_hdr; start_hdr = ptr; /* if we got a content-type in a previous packet and are * folding, the boundary still needs to be checked for */ if (mime_ssn->state_flags & MIME_FLAG_IN_CONTENT_TYPE) content_type_ptr = ptr; if (mime_ssn->state_flags & MIME_FLAG_IN_CONT_TRANS_ENC) cont_trans_enc = ptr; if (mime_ssn->state_flags & MIME_FLAG_IN_CONT_DISP) cont_disp = ptr; while (ptr < data_end_marker) { int header_name_len; int max_header_name_len = 0; get_mime_eol(ptr, data_end_marker, &eol, &eolm); /* got a line with only end of line marker should signify end of header */ if (eolm == ptr) { /* reset global header state values */ mime_ssn->state_flags &= ~(MIME_FLAG_FOLDING | MIME_FLAG_IN_CONTENT_TYPE | MIME_FLAG_DATA_HEADER_CONT | MIME_FLAG_IN_CONT_TRANS_ENC | MIME_FLAG_IN_CONT_DISP ); mime_ssn->data_state = STATE_DATA_BODY; /* no header seen */ if (ptr == start_hdr) { setup_decode((const char *)ptr, eolm - (const uint8_t *)NULL, false, mime_ssn, p->ssnptr, preproc_id); } return eol; } /* if we're not folding, see if we should interpret line as a data line * instead of a header line */ if (!(mime_ssn->state_flags & (MIME_FLAG_FOLDING | MIME_FLAG_DATA_HEADER_CONT))) { char got_non_printable_in_header_name = 0; /* if we're not folding and the first char is a space or * colon, it's not a header */ if (isspace((int)*ptr) || *ptr == ':') { mime_ssn->data_state = STATE_DATA_BODY; return ptr; } /* look for header field colon - if we're not folding then we need * to find a header which will be all printables (except colon) * followed by a colon */ colon = ptr; while ((colon < eolm) && (*colon != ':')) { if (((int)*colon < 33) || ((int)*colon > 126)) got_non_printable_in_header_name = 1; colon++; } /* Check for Exim 4.32 exploit where number of chars before colon is greater than 64 */ header_name_len = colon - ptr; if ((mime_ssn->data_state != STATE_DATA_UNKNOWN) && (colon < eolm) && (header_name_len > MAX_HEADER_NAME_LEN)) { max_header_name_len = header_name_len; } /* If the end on line marker and end of line are the same, assume * header was truncated, so stay in data header state */ if ((eolm != eol) && ((colon == eolm) || got_non_printable_in_header_name)) { /* no colon or got spaces in header name (won't be interpreted as a header) * assume we're in the body */ mime_ssn->state_flags &= ~(MIME_FLAG_FOLDING | MIME_FLAG_IN_CONTENT_TYPE | MIME_FLAG_DATA_HEADER_CONT | MIME_FLAG_IN_CONT_TRANS_ENC | MIME_FLAG_IN_CONT_DISP); mime_ssn->data_state = STATE_DATA_BODY; return ptr; } if(tolower((int)*ptr) == 'c') { mime_current_search = &mime_hdr_search[0]; header_found =search_api->search_instance_find (mime_hdr_search_mpse, (const char *)ptr, eolm - ptr, 1, search_str_found); /* Headers must start at beginning of line */ if ((header_found > 0) && (mime_search_info.index == 0)) { switch (mime_search_info.id) { case HDR_CONTENT_TYPE: content_type_ptr = ptr + mime_search_info.length; mime_ssn->state_flags |= MIME_FLAG_IN_CONTENT_TYPE; break; case HDR_CONT_TRANS_ENC: cont_trans_enc = ptr + mime_search_info.length; mime_ssn->state_flags |= MIME_FLAG_IN_CONT_TRANS_ENC; break; case HDR_CONT_DISP: cont_disp = ptr + mime_search_info.length; mime_ssn->state_flags |= MIME_FLAG_IN_CONT_DISP; break; default: break; } } } else if(tolower((int)*ptr) == 'e') { if((eolm - ptr) >= 9) { if(strncasecmp((const char *)ptr, "Encoding:", 9) == 0) { cont_trans_enc = ptr + 9; mime_ssn->state_flags |= MIME_FLAG_IN_CONT_TRANS_ENC; } } } } else { mime_ssn->state_flags &= ~MIME_FLAG_DATA_HEADER_CONT; } if (mime_ssn->methods && mime_ssn->methods->handle_header_line) { int ret = mime_ssn->methods->handle_header_line(p, ptr, eol, max_header_name_len, mime_ssn); if (ret < 0) return NULL; else if (ret > 0) { /* assume we guessed wrong and are in the body */ mime_ssn->data_state = STATE_DATA_BODY; mime_ssn->state_flags &= ~(MIME_FLAG_FOLDING | MIME_FLAG_IN_CONTENT_TYPE | MIME_FLAG_DATA_HEADER_CONT | MIME_FLAG_IN_CONT_TRANS_ENC | MIME_FLAG_IN_CONT_DISP); return ptr; } } /* check for folding * if char on next line is a space and not \n or \r\n, we are folding */ if ((eol < data_end_marker) && isspace((int)eol[0]) && (eol[0] != '\n')) { if ((eol < (data_end_marker - 1)) && (eol[0] != '\r') && (eol[1] != '\n')) { mime_ssn->state_flags |= MIME_FLAG_FOLDING; } else { mime_ssn->state_flags &= ~MIME_FLAG_FOLDING; } } else if (eol != eolm) { mime_ssn->state_flags &= ~MIME_FLAG_FOLDING; } /* check if we're in a content-type header and not folding. if so we have the whole * header line/lines for content-type - see if we got a multipart with boundary * we don't check each folded line, but wait until we have the complete header * because boundary=BOUNDARY can be split across mulitple folded lines before * or after the '=' */ if ((mime_ssn->state_flags & (MIME_FLAG_IN_CONTENT_TYPE | MIME_FLAG_FOLDING)) == MIME_FLAG_IN_CONTENT_TYPE) { if ((mime_ssn->data_state == STATE_MIME_HEADER) && !(mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH)) { setup_decode((const char *)content_type_ptr, (eolm - content_type_ptr), false, mime_ssn, p->ssnptr, preproc_id); } mime_ssn->state_flags &= ~MIME_FLAG_IN_CONTENT_TYPE; content_type_ptr = NULL; } else if ((mime_ssn->state_flags & (MIME_FLAG_IN_CONT_TRANS_ENC | MIME_FLAG_FOLDING)) == MIME_FLAG_IN_CONT_TRANS_ENC) { setup_decode((const char *)cont_trans_enc, (eolm - cont_trans_enc), true, mime_ssn, p->ssnptr, preproc_id); mime_ssn->state_flags &= ~MIME_FLAG_IN_CONT_TRANS_ENC; cont_trans_enc = NULL; } else if (((mime_ssn->state_flags & (MIME_FLAG_IN_CONT_DISP | MIME_FLAG_FOLDING)) == MIME_FLAG_IN_CONT_DISP) && cont_disp) { bool disp_cont = (mime_ssn->state_flags & MIME_FLAG_IN_CONT_DISP_CONT)? true: false; if(mime_ssn->log_config->log_filename && mime_ssn->log_state ) { if(!log_file_name(cont_disp, eolm - cont_disp, &(mime_ssn->log_state->file_log), &disp_cont, true) ) { mime_ssn->log_flags |= FLAG_FILENAME_PRESENT; } mime_ssn->log_flags |= FLAG_FILENAME_IN_HEADER; } if (disp_cont) { mime_ssn->state_flags |= MIME_FLAG_IN_CONT_DISP_CONT; } else { if ((mime_ssn->data_state == STATE_MIME_HEADER) && !(mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH)) { // setting up decode assuming possible file data after content-disposition header setup_decode(NULL, eolm - (const uint8_t *)NULL, false, mime_ssn, p->ssnptr, preproc_id); } mime_ssn->state_flags &= ~MIME_FLAG_IN_CONT_DISP; mime_ssn->state_flags &= ~MIME_FLAG_IN_CONT_DISP_CONT; } cont_disp = NULL; } else { // unknown header if ((mime_ssn->data_state == STATE_MIME_HEADER) && !(mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH)) { // setting up decode assuming possible file data after unknown header setup_decode(NULL, eolm - (const uint8_t *)NULL, false, mime_ssn, p->ssnptr, preproc_id); } } /* if state was unknown, at this point assume we know */ if (mime_ssn->data_state == STATE_DATA_UNKNOWN) mime_ssn->data_state = STATE_DATA_HEADER; ptr = eol; if (ptr == data_end_marker) mime_ssn->state_flags |= MIME_FLAG_DATA_HEADER_CONT; } return ptr; } /* Get the end of data body (excluding boundary)*/ static const uint8_t * GetDataEnd(const uint8_t *data_start, const uint8_t *data_end_marker) { /* '\r\n' + '--' + MIME boundary string */ const int Max_Search = 4 + MAX_BOUNDARY_LEN; uint8_t *start; /*Exclude 2 bytes because either \r\n or '--' at the end */ uint8_t *end = (uint8_t *) data_end_marker - 2; /*Search for the start of boundary, should be less than boundary length*/ if (end > data_start + Max_Search) start = end - Max_Search; else start = (uint8_t *)data_start; while (end > start) { if (*(--end) != '\n') continue; if ((*(end+1) == '-') && (*(end+2) == '-')) { if ((end > start) && (*(end-1) == '\r')) return (end - 1); else return end; } break; } return data_end_marker; } /* * Handle DATA_BODY state * * @param packet standard Packet structure * * @param i index into p->payload buffer to start looking at data * * @return i index into p->payload where we stopped looking at data */ static const uint8_t * process_mime_body(Packet *p, const uint8_t *ptr, const uint8_t *data_end, MimeState *mime_ssn, bool is_data_end) { Email_DecodeState *decode_state = (Email_DecodeState *)(mime_ssn->decode_state); if ( mime_ssn->state_flags & MIME_FLAG_EMAIL_ATTACH ) { const uint8_t *attach_start = ptr; const uint8_t *attach_end; uint8_t filename[MAX_UNICODE_FILE_NAME] ; uint32_t file_name_size = 0; if (is_data_end ) { attach_end = GetDataEnd(ptr, data_end); } else { attach_end = data_end; } if( attach_start < attach_end ) { bool filename_in_mime_header = (mime_ssn->log_flags & FLAG_FILENAME_IN_HEADER) ? true: false; if(EmailDecode( attach_start, attach_end, decode_state, filename, &file_name_size, filename_in_mime_header ) < DECODE_SUCCESS ) { if (mime_ssn->methods && mime_ssn->methods->decode_alert) mime_ssn->methods->decode_alert(mime_ssn->decode_state); } else { if ( !filename_in_mime_header && (decode_state->decode_type == DECODE_UU) && file_name_size && mime_ssn->log_state) { bool disp_cont = (mime_ssn->state_flags & MIME_FLAG_IN_CONT_DISP_CONT)? true: false; if ( !log_file_name((const uint8_t *) filename, \ file_name_size , \ &(mime_ssn->log_state->file_log), &disp_cont, false) ) { mime_ssn->log_flags |= FLAG_FILENAME_PRESENT; } } } } } mime_ssn->log_flags &= ~FLAG_FILENAME_IN_HEADER; if (is_data_end) { mime_ssn->data_state = STATE_MIME_HEADER; mime_ssn->state_flags &= ~MIME_FLAG_EMAIL_ATTACH; } return data_end; } /* * Reset MIME session state */ static void reset_mime_state(MimeState *mime_ssn) { Email_DecodeState *decode_state = (Email_DecodeState *)(mime_ssn->decode_state); mime_ssn->data_state = STATE_DATA_INIT; mime_ssn->state_flags = 0; ClearEmailDecodeState(decode_state); } /* * Assume PAF is enabled */ const uint8_t * process_mime_data_paf(void *packet, const uint8_t *start, const uint8_t *end, MimeState *mime_ssn, bool upload, FilePosition position, uint32_t preproc_id) { Packet *p = (Packet *)packet; bool done_data = false; if (mime_ssn->methods && mime_ssn->methods->is_end_of_data) { done_data = mime_ssn->methods->is_end_of_data(p->ssnptr); } /* if we've just entered the data state, check for a dot + end of line * if found, no data */ if ((mime_ssn->data_state == STATE_DATA_INIT) || (mime_ssn->data_state == STATE_DATA_UNKNOWN)) { if ((start < end) && (*start == '.')) { const uint8_t *eol = NULL; const uint8_t *eolm = NULL; get_mime_eol(start, end, &eol, &eolm); /* this means we got a real end of line and not just end of payload * and that the dot is only char on line */ if ((eolm != end) && (eolm == (start + 1))) { /* if we're normalizing and not ignoring data copy data end marker * and dot to alt buffer */ if (mime_ssn->methods && mime_ssn->methods->normalize_data) { if (mime_ssn->methods->normalize_data(p, start, end) < 0) return NULL; } reset_mime_state(mime_ssn); return eol; } } if (mime_ssn->data_state == STATE_DATA_INIT) mime_ssn->data_state = STATE_DATA_HEADER; /* XXX A line starting with a '.' that isn't followed by a '.' is * deleted (RFC 821 - 4.5.2. TRANSPARENCY). If data starts with * '. text', i.e a dot followed by white space then text, some * servers consider it data header and some data body. * Postfix and Qmail will consider the start of data: * . text\r\n * . text\r\n * to be part of the header and the effect will be that of a * folded line with the '.' deleted. Exchange will put the same * in the body which seems more reasonable. */ } if ( mime_ssn->decode_conf && !mime_ssn->decode_conf->ignore_data) setFileDataPtr(start, (uint16_t)(end - start)); if ((mime_ssn->data_state == STATE_DATA_HEADER) || (mime_ssn->data_state == STATE_DATA_UNKNOWN)) { #ifdef DEBUG_MSGS if (mime_ssn->data_state == STATE_DATA_HEADER) { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "DATA HEADER STATE ~~~~~~~~~~~~~~~~~~~~~~\n");); } else { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "DATA UNKNOWN STATE ~~~~~~~~~~~~~~~~~~~~~\n");); } #endif start = process_mime_header(p, start, end, mime_ssn, preproc_id); if (start == NULL) return NULL; } if (mime_ssn->methods && mime_ssn->methods->normalize_data) { if (mime_ssn->methods->normalize_data(p, start, end) < 0) return NULL; } /* now we shouldn't have to worry about copying any data to the alt buffer * only mime headers if we find them and only if we're ignoring data */ while ((start != NULL) && (start < end)) { switch (mime_ssn->data_state) { case STATE_MIME_HEADER: DEBUG_WRAP(DebugMessage(DEBUG_FILE, "MIME HEADER STATE ~~~~~~~~~~~~~~~~~~~~~~\n");); start = process_mime_header(p, start, end, mime_ssn, preproc_id); update_file_name(mime_ssn->log_state); break; case STATE_DATA_BODY: DEBUG_WRAP(DebugMessage(DEBUG_FILE, "DATA BODY STATE ~~~~~~~~~~~~~~~~~~~~~~~~\n");); start = process_mime_body(p, start, end, mime_ssn, isFileEnd(position) ); update_file_name(mime_ssn->log_state); break; } } /* We have either reached the end of MIME header or end of MIME encoded data*/ if((mime_ssn->decode_state) != NULL) { DecodeConfig *conf= mime_ssn->decode_conf; Email_DecodeState *ds = (Email_DecodeState *)(mime_ssn->decode_state); if (conf) { int detection_size = getDetectionSize(conf->b64_depth, conf->qp_depth, conf->uu_depth, conf->bitenc_depth, ds ); setFileDataPtr((const uint8_t*)ds->decodePtr, (uint16_t)detection_size); } if (file_api->file_process(p,(uint8_t *)ds->decodePtr, (uint16_t)ds->decoded_bytes, position, upload, false, false) && (isFileStart(position))&& mime_ssn->log_state) { set_file_name_from_log(&(mime_ssn->log_state->file_log), p->ssnptr); } if (mime_ssn->mime_stats) ((MimeStats *)mime_ssn->mime_stats)->decoded_bytes[ds->decode_type] += ds->decoded_bytes; ResetDecodedBytes((Email_DecodeState *)(mime_ssn->decode_state)); } /* if we got the data end reset state, otherwise we're probably still in the data * to expect more data in next packet */ if (done_data) { reset_mime_state(mime_ssn); if (mime_ssn->methods && mime_ssn->methods->reset_state) mime_ssn->methods->reset_state(); } return end; } /* * Main function for mime processing * * This should be called when mime data is available */ const uint8_t * process_mime_data(void *packet, const uint8_t *start, const uint8_t *data_end_marker, MimeState *mime_ssn, bool upload, bool paf_enabled, char *preproc_name, uint32_t preproc_id) { const uint8_t *attach_start = start; const uint8_t *attach_end; Packet *p = (Packet *)packet; FilePosition position = SNORT_FILE_START; preprocessor = preproc_name; SAVE_DAQ_PKT_HDR(p); if (paf_enabled) { position = file_api->get_file_position(p); process_mime_data_paf(packet, attach_start, data_end_marker, mime_ssn, upload, position, preproc_id); return data_end_marker; } initFilePosition(&position, file_api->get_file_processed_size(p->ssnptr)); /* look for boundary */ while (start < data_end_marker) { /*Found the boundary, start processing data*/ if (process_mime_paf_data(&(mime_ssn->mime_boundary), *start)) { attach_end = start; finalFilePosition(&position); process_mime_data_paf(packet, attach_start, attach_end, mime_ssn, upload, position, preproc_id); position = SNORT_FILE_START; attach_start = start + 1; } start++; } if ((start == data_end_marker) && (attach_start < data_end_marker)) { updateFilePosition(&position, file_api->get_file_processed_size(p->ssnptr)); process_mime_data_paf(packet, attach_start, data_end_marker, mime_ssn, upload, position, preproc_id); } preprocessor = 0; return data_end_marker; } /* * This is the initialization funcation for mime processing. * This should be called when snort initializes */ void init_mime(void) { const MimeToken *tmp; /* Header search */ mime_hdr_search_mpse = search_api->search_instance_new(); if (mime_hdr_search_mpse == NULL) { FatalError("Could not allocate MIME " "header search.\n"); } for (tmp = &mime_hdrs[0]; tmp->name != NULL; tmp++) { mime_hdr_search[tmp->search_id].name = tmp->name; mime_hdr_search[tmp->search_id].name_len = tmp->name_len; search_api->search_instance_add(mime_hdr_search_mpse, tmp->name, tmp->name_len, tmp->search_id); } search_api->search_instance_prep(mime_hdr_search_mpse); } /* * Free anything that needs it before shutting down preprocessor * * @param none * * @return none */ void free_mime(void) { if (mime_hdr_search_mpse != NULL) search_api->search_instance_free(mime_hdr_search_mpse); } void free_mime_session(MimeState *mime_ssn) { if (!mime_ssn) return; if(mime_ssn->decode_state != NULL) { mempool_free(mime_ssn->mime_mempool, mime_ssn->decode_bkt); SnortPreprocFree(mime_ssn->decode_state, sizeof(Email_DecodeState), PP_FILE, PP_MEM_CATEGORY_SESSION); } if(mime_ssn->log_state != NULL) { mempool_free(mime_ssn->log_mempool, mime_ssn->log_state->log_hdrs_bkt); SnortPreprocFree(mime_ssn->log_state, sizeof(FILE_LogState), PP_FILE, PP_MEM_CATEGORY_SESSION); } SnortPreprocFree(mime_ssn, sizeof(MimeState), PP_FILE, PP_MEM_CATEGORY_SESSION); } /* * When the file ends (a MIME boundary detected), position are updated */ void finalize_mime_position(void *ssnptr, void *decode_state, FilePosition *position) { /* check to see if there are file data in the session or * new decoding data waiting for processing */ if( file_api->get_file_processed_size(ssnptr) || (decode_state && ((Email_DecodeState *)decode_state)->decoded_bytes) ) finalFilePosition(position); } /* Save the bounday string into paf state*/ static inline bool store_boundary(MimeDataPafInfo *data_info, uint8_t val) { if (!data_info->boundary_search) { if ((val == '.') || isspace (val)) data_info->boundary_search = (char *)&boundary_str[0]; return 0; } if (*(data_info->boundary_search) == '=') { /*Skip spaces for the end of boundary*/ if (val == '=') data_info->boundary_search++; else if (!isspace(val)) data_info->boundary_search = NULL; } else if (*(data_info->boundary_search) == '\0') { /*get boundary string*/ if (isspace(val) || (val == '"')) { if (!data_info->boundary_len) return 0; else { /*Found boundary string*/ data_info->boundary[data_info->boundary_len] = '\0'; return 1; } } if (data_info->boundary_len < sizeof (data_info->boundary)) { data_info->boundary[data_info->boundary_len++] = val; } else { /*Found boundary string*/ data_info->boundary[data_info->boundary_len -1] = '\0'; return 1; } } else if ((val == *(data_info->boundary_search)) || (val == *(data_info->boundary_search) - 'a' + 'A')) { data_info->boundary_search++; } else { if ((val == '.') || isspace (val)) data_info->boundary_search = (char *)&boundary_str[0]; else data_info->boundary_search = NULL; } return 0; } /* check the bounday string in the mail body*/ static inline bool check_boundary(MimeDataPafInfo *data_info, uint8_t data) { /* Search for boundary signature "--"*/ switch (data_info->boundary_state) { case MIME_PAF_BOUNDARY_UNKNOWN: if (data == '\n') data_info->boundary_state = MIME_PAF_BOUNDARY_LF; break; case MIME_PAF_BOUNDARY_LF: if (data == '-') data_info->boundary_state = MIME_PAF_BOUNDARY_HYPEN_FIRST; else if (data != '\n') data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN; break; case MIME_PAF_BOUNDARY_HYPEN_FIRST: if (data == '-') { data_info->boundary_state = MIME_PAF_BOUNDARY_HYPEN_SECOND; data_info->boundary_search = data_info->boundary; } else if (data == '\n') data_info->boundary_state = MIME_PAF_BOUNDARY_LF; else data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN; break; case MIME_PAF_BOUNDARY_HYPEN_SECOND: /* Compare with boundary string stored */ if (*(data_info->boundary_search) == '\0') { if (data == '\n') { /*reset boundary search etc.*/ data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN; return 1; } else if ((data != '\r') && ((data != '-'))) data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN; } else if (*(data_info->boundary_search) == data) data_info->boundary_search++; else data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN; break; } return 0; } void reset_mime_paf_state(MimeDataPafInfo *data_info) { data_info->boundary_search = NULL; data_info->boundary_len = 0; data_info->boundary[0] = '\0'; data_info->boundary_state = MIME_PAF_BOUNDARY_UNKNOWN; data_info->data_state = MIME_PAF_FINDING_BOUNDARY_STATE; } /* Process data boundary and flush each file based on boundary*/ bool process_mime_paf_data(MimeDataPafInfo *data_info, uint8_t data) { switch (data_info->data_state) { case MIME_PAF_FINDING_BOUNDARY_STATE: /* Search for boundary*/ /* Store bounday string in PAF state*/ if (store_boundary(data_info, data)) { /* End of boundary, move to MIME_PAF_FOUND_BOUNDARY_STATE*/ DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Create boudary string: %s\n", data_info->boundary);); data_info->data_state = MIME_PAF_FOUND_BOUNDARY_STATE; } break; case MIME_PAF_FOUND_BOUNDARY_STATE: if (check_boundary(data_info, data)) { /* End of boundary, move to MIME_PAF_FOUND_BOUNDARY_STATE*/ DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Found Boudary string: %s\n", data_info->boundary);); return 1; } break; default: break; } return 0; } bool check_data_end(void *data_end_state, uint8_t val) { DataEndState state = *((DataEndState *)data_end_state); switch (state) { case PAF_DATA_END_UNKNOWN: if (val == '\n') { state = PAF_DATA_END_FIRST_LF; } break; case PAF_DATA_END_FIRST_LF: if (val == '.') { state = PAF_DATA_END_DOT; } else if ((val != '\r') && (val != '\n')) { state = PAF_DATA_END_UNKNOWN; } break; case PAF_DATA_END_DOT: if (val == '\n') { *((DataEndState *)data_end_state) = PAF_DATA_END_UNKNOWN; return 1; } else if (val != '\r') { state = PAF_DATA_END_UNKNOWN; } break; default: state = PAF_DATA_END_UNKNOWN; break; } *((DataEndState *)data_end_state) = state; return 0; } #ifdef SNORT_RELOAD void update_mime_mempool(void *mempool, int new_max_memory, int encode_depth) { size_t obj_size = 0; unsigned num_objects = 0; MemPool *memory_pool = (MemPool*)mempool; if (encode_depth & 7) encode_depth += (8 - (encode_depth & 7)); if(encode_depth) { obj_size = (2*encode_depth); num_objects = new_max_memory / obj_size; } #ifdef REG_TEST if (REG_TEST_EMAIL_FLAG_MIME_MEMPOOL_ADJUST & getRegTestFlagsForEmail()) { printf("\n========== START# NEW MIME MEMPOOL VALUES ==============================\n"); printf("Mime mempool object size: NEW VALUE # %zu \n", obj_size); printf("Mime mempool max memory : NEW VALUE # (%u * %zu = %zu) \n", num_objects,obj_size,(num_objects * obj_size)); printf("Mime mempool total number of buckets: NEW VALUE # %u \n", num_objects); printf("========== END# NEW MIME MEMPOOL VALUES ==============================\n"); fflush(stdout); } #endif mempool_setObjectSize(memory_pool, num_objects, obj_size ); } void update_log_mempool(void *mempool, int new_max_memory , int email_hdrs_log_depth) { size_t obj_size = 0; unsigned num_objects = 0; MemPool *memory_pool = (MemPool*)mempool; if(email_hdrs_log_depth) { if (email_hdrs_log_depth & 7) email_hdrs_log_depth += (8 - (email_hdrs_log_depth & 7)); } obj_size = ((2* MAX_EMAIL) + MAX_FILE + email_hdrs_log_depth); num_objects = new_max_memory / obj_size; #ifdef REG_TEST if (REG_TEST_EMAIL_FLAG_LOG_MEMPOOL_ADJUST & getRegTestFlagsForEmail()) { printf("\n========== START# NEW LOG MEMPOOL VALUES ==============================\n"); printf("Log mempool object size: NEW VALUE # %zu \n", obj_size); printf("Log mempool max memory : NEW VALUE # (%u * %zu = %zu) \n",num_objects, obj_size, (num_objects * obj_size)); printf("Log mempool total number of buckets: NEW VALUE # %u \n",num_objects); printf("========== END# NEW LOG MEMPOOL VALUES ==============================\n"); fflush(stdout); } #endif mempool_setObjectSize(memory_pool, num_objects, obj_size ); } #ifdef REG_TEST void displayMimeMempool(void *mempool, DecodeConfig *decode_conf_old, DecodeConfig *decode_conf_new) { MemPool *memory_pool = (MemPool*)mempool; if (REG_TEST_EMAIL_FLAG_MIME_MEMPOOL_ADJUST & getRegTestFlagsForEmail()) { printf("\nmax_mime_mem is : OLD VALUE # %u \n",decode_conf_old->max_mime_mem); printf("max_depth is# OLD VALUE %u\n",decode_conf_old->max_depth); printf("\n=========START# OLD MIME MEMPOOL VALUES ===============================\n"); printf("Mime mempool object size: OLD VALUE # %zu \n",memory_pool->obj_size); printf("Mime mempool max memory : OLD VALUE # %zu \n",memory_pool->max_memory); printf("Mime mempool total number of buckets: OLD VALUE # %u \n",mempool_numTotalBuckets(memory_pool)); printf("=========END# OLD MIME MEMPOOL VALUES ===================================== \n"); printf("\nSetting max_mime_mem to # ( NEW VALUE ) %u \n",decode_conf_new->max_mime_mem); printf("Setting max_depth to # ( NEW VALUE )%u\n",decode_conf_new->max_depth); fflush(stdout); } } void displayLogMempool(void *mempool, unsigned memcap_old, unsigned memcap_new) { MemPool *memory_pool = (MemPool*)mempool; if (REG_TEST_EMAIL_FLAG_LOG_MEMPOOL_ADJUST & getRegTestFlagsForEmail()) { printf("\nmemcap is : OLD VALUE # %u \n", memcap_old); printf("\n=========START# OLD LOG MEMPOOL VALUES ==================================\n "); printf("Log mempool object size: OLD VALUE # %zu \n",memory_pool->obj_size); printf("Log mempool max memory : OLD VALUE # %zu \n",memory_pool->max_memory); printf("Log mempool total number of buckets: OLD VALUE # %u \n",mempool_numTotalBuckets(memory_pool)); printf("=========END# OLD LOG MEMPOOL VALUES ================================== \n"); printf("\nSetting memcap to# (NEW VALUE ) %u \n", memcap_new); fflush(stdout); } } void displayDecodeDepth(DecodeConfig *decode_conf_old, DecodeConfig *decode_conf_new) { if(REG_TEST_EMAIL_FLAG_DECODE_DEPTH_ADJUST & getRegTestFlagsForEmail()) { if(decode_conf_old->b64_depth != decode_conf_new->b64_depth ) { printf("\nBase64 decode depth: OLD VALUE # %d",decode_conf_old->b64_depth); printf("\nSetting Base64 decoding depth to # (new value)%d \n\n", decode_conf_new->b64_depth); } if(decode_conf_old->qp_depth != decode_conf_new->qp_depth ) { printf("\nQuoted-Printable decoding depth: OLD VALUE # %d",decode_conf_old->qp_depth); printf("\nSetting Quoted-Printable decoding depth to # (new value)%d \n\n",decode_conf_new->qp_depth); } if(decode_conf_old->bitenc_depth != decode_conf_new->bitenc_depth ) { printf("\nNon-encoded MIME extraction depth (bitec_depth): OLD VALUE # %d",decode_conf_old->bitenc_depth); printf("\nSetting bitenc decoding depth to # (new value)%d \n\n", decode_conf_new->bitenc_depth); } if(decode_conf_old->uu_depth != decode_conf_new->uu_depth ) { printf("\nUnix-to-Unix decoding depth: OLD VALUE # %d",decode_conf_old->uu_depth); printf("\nSetting Unix-to-Unix decoding depth depth to # (new value)%d \n\n", decode_conf_new->uu_depth); } } } #endif #endif snort-2.9.20/src/file-process/Makefile.am0000555000175000017500000000127114230012554016305 0ustar apoapoAUTOMAKE_OPTIONS=foreign no-dependencies noinst_LIBRARIES = libfileAPI.a libfileAPI_a_SOURCES = \ file_service.c \ file_service.h \ file_service_config.c \ file_service_config.h \ file_api.h \ file_mime_process.c \ file_mime_process.h \ file_resume_block.c \ file_resume_block.h \ file_mime_config.c \ file_mime_config.h \ file_capture.c \ file_capture.h \ file_stats.c \ file_stats.h \ file_segment_process.c \ file_segment_process.h \ circular_buffer.c \ circular_buffer.h \ file_mempool.c \ file_mempool.h \ ../sfutil/sf_email_attach_decode.c \ ../sfutil/sf_email_attach_decode.h \ file_mail_common.h\ file_ss.c\ file_ss.h INCLUDES = @INCLUDES@ SUBDIRS = libs snort-2.9.20/src/file-process/libs/0000755000175000017500000000000014242725720015211 5ustar apoaposnort-2.9.20/src/file-process/libs/file_identifier.c0000644000175000017500000003450214241076600020475 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.2012 - Initial Source Code. Hcao */ #ifdef HAVE_CONFIG_H #include #endif #include "sf_types.h" #include "file_identifier.h" #include #include #include #include "parser.h" #include "util.h" #include "mstring.h" #include "sfghash.h" #include "file_config.h" #include "memory_stats.h" uint32_t memory_used = 0; /*Track memory usage*/ static SFGHASH *identifier_merge_hash = NULL; typedef struct _IdentifierSharedNode { IdentifierNode *shared_node; /*the node that is shared*/ IdentifierNode *append_node; /*the node that is added*/ } IdentifierSharedNode; static IdentifierMemoryBlock *id_memory_root = NULL; static IdentifierMemoryBlock *id_memory_current = NULL; #ifdef DEBUG_MSGS static char *file_type_test(void *conf); #endif static void identifierMergeHashFree(void) { if (identifier_merge_hash != NULL) { sfghash_delete(identifier_merge_hash); identifier_merge_hash = NULL; } } static void identifierMergeHashInit(void) { if (identifier_merge_hash != NULL) identifierMergeHashFree(); identifier_merge_hash = sfghash_new(1000, sizeof(IdentifierSharedNode), 0, NULL); if (identifier_merge_hash == NULL) { FatalError("%s(%d) Could not create identifier merge hash.\n", __FILE__, __LINE__); } } static inline void *calloc_mem(size_t size) { void *ret; IdentifierMemoryBlock *new = NULL; ret = SnortPreprocAlloc(1, size, PP_FILE, PP_MEM_CATEGORY_SESSION); memory_used += size; /*For memory management*/ size = sizeof(*new); new = (IdentifierMemoryBlock *)SnortPreprocAlloc(1, size, PP_FILE, PP_MEM_CATEGORY_SESSION); new->mem_block = ret; if (!id_memory_root) { id_memory_root = new; } else { id_memory_current->next = new; } id_memory_current = new; memory_used += size; DEBUG_WRAP(DebugMessage(DEBUG_FILE,"calloc: %p (%d).\n", ret, size);); return ret; } static void set_node_state_shared(IdentifierNode *start) { int i; if (!start) return; if (start->state == ID_NODE_SHARED) return; if (start->state == ID_NODE_USED) start->state = ID_NODE_SHARED; else { start->state = ID_NODE_USED; } for(i = 0; i < MAX_BRANCH; i++) { set_node_state_shared(start->next[i]); } } /*Clone a trie*/ static IdentifierNode *clone_node(IdentifierNode *start) { int index; IdentifierNode *new; if (!start) return NULL; new = calloc_mem(sizeof(*new)); new->offset = start->offset; new->type_id = start->type_id; for(index = 0; index < MAX_BRANCH; index++) { if (start->next[index]) { new->next[index] = start->next[index]; } } return new; } static void verify_magic_offset(MagicData *parent, MagicData *current) { if ((parent) && (parent->content_len + parent->offset > current->offset)) { ParseError(" Magic content at offset %d overlaps with offset %d.", parent->offset, current->offset); } if ((current->next) && (current->content_len + current->offset > current->next->offset)) { ParseError(" Magic content at offset %d overlaps with offset %d.", current->offset, current->next->offset); } } /* Add a new node to the sorted magic list */ static void add_to_sorted_magic(MagicData **head, MagicData *new ) { MagicData *current = *head; /*Avoid adding the same magic*/ if (!new || (current == new)) return; if (new->offset < current->offset) { /*current becomes new head*/ new->next = current; *head = new; verify_magic_offset(NULL, new); return; } /*Find the parent for the new magic*/ while (current) { MagicData *next = current->next; if ((!next) || (new->offset < next->offset)) { /*current is the parent*/ current->next = new; new->next = next; verify_magic_offset(current, new); return; } current = next; } } /* Content magics are sorted based on offset, this * will help compile the file magic trio */ static void sort_magics(MagicData **head) { MagicData *current = *head; /*Add one magic at a time to sorted magics*/ while (current) { MagicData *next = current->next; current->next = NULL; add_to_sorted_magic(head, current); current = next; } } /*Create a trie for the magic*/ static inline IdentifierNode *create_trie_from_magic(MagicData **head, uint32_t type_id) { int i; IdentifierNode *current; IdentifierNode *root = NULL; MagicData *magic; if (!head || !(*head)||(0 == (*head)->content_len) || !type_id) return NULL; sort_magics(head); magic = *head; current = calloc_mem(sizeof(*current)); current->state = ID_NODE_NEW; root = current; while (magic) { current->offset = magic->offset; for(i = 0; i < magic->content_len; i++) { IdentifierNode *new = calloc_mem(sizeof(*new)); new->offset = magic->offset + i + 1; new->state = ID_NODE_NEW; current->next[magic->content[i]] = new; current = new; } magic = magic->next; } /*Last node has type name*/ current->type_id = type_id; DEBUG_WRAP( if (DEBUG_FILE & GetDebugLevel()){file_identifiers_print(root);}) return root; } /*This function examines whether to update the trie based on shared state*/ static inline bool updateNext(IdentifierNode *start,IdentifierNode **next_ptr, IdentifierNode *append) { IdentifierNode *next = (*next_ptr); IdentifierSharedNode sharedIdentifier; IdentifierNode *result; if (!append || (next == append)) return false; sharedIdentifier.append_node = append; sharedIdentifier.shared_node = next; if (!next) { /*reuse the append*/ *next_ptr = append; set_node_state_shared(append); return false; } else if ((result = sfghash_find(identifier_merge_hash, &sharedIdentifier))) { /*the same pointer has been processed, reuse it*/ *next_ptr = result; set_node_state_shared(result); return false; } else { if ((start->offset < append->offset) && (next->offset > append->offset)) { /*offset could have gap when non 0 offset is allowed */ int index; IdentifierNode *new = calloc_mem(sizeof(*new)); sharedIdentifier.shared_node = next; sharedIdentifier.append_node = append; new->offset = append->offset; for(index = 0; index < MAX_BRANCH; index++) { new->next[index] = next; } set_node_state_shared(next); next = new; sfghash_add(identifier_merge_hash, &sharedIdentifier, next); } else if (next->state == ID_NODE_SHARED) { /*shared, need to clone one*/ IdentifierNode *current_next = next; sharedIdentifier.shared_node = current_next; sharedIdentifier.append_node = append; next = clone_node(current_next); set_node_state_shared(next); sfghash_add(identifier_merge_hash, &sharedIdentifier, next); } *next_ptr = next; } return true; } /* * Append magic to existing trie * */ static void update_trie(IdentifierNode *start, IdentifierNode *append) { int i; if ((!start )||(!append)||(start == append)) return ; DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Working on %p -> %p at offset %d.\n", start, append, append->offset);); if (start->offset == append->offset ) { /* when we come here, make sure this tree is not shared * Update start trie using append information*/ if (start->state == ID_NODE_SHARED) { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Something is wrong ...");); } if (append->type_id) { if(start->type_id) LogMessage("Duplicated type definition '%d -> %d at offset %d.\n", start->type_id, append->type_id, append->offset); start->type_id = append->type_id; } for(i = 0; i < MAX_BRANCH; i++) { if (updateNext(start,&start->next[i], append->next[i])) { update_trie(start->next[i], append->next[i]); } } } else if (start->offset < append->offset ) { for(i = 0; i < MAX_BRANCH; i++) { if (updateNext(start,&start->next[i], append)) update_trie(start->next[i], append); } } else /*This is impossible*/ { DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Something is wrong .....");); } return; } void file_identifers_update(RuleInfo *rule, void *conf) { IdentifierNode *new; FileConfig *file_config = NULL; file_config = (FileConfig *) conf; if (!file_config->identifier_root) { file_identifers_init(); file_config->identifier_root = calloc_mem(sizeof(*file_config->identifier_root)); file_config->id_memory_root = id_memory_root; identifierMergeHashInit(); } new = create_trie_from_magic(&(rule->magics), rule->id); update_trie(file_config->identifier_root, new); DEBUG_WRAP( if (DEBUG_FILE & GetDebugLevel()){file_type_test(file_config);}); } void file_identifers_init(void) { memory_used = 0; id_memory_root = NULL; id_memory_current = NULL; } uint32_t file_identifiers_usage(void) { return memory_used; } /* * This is the main function to find file type * Find file type is to traverse the tries. * Context is saved to continue file type identification * when more data are available */ uint32_t file_identifiers_match(uint8_t *buf, int len, FileContext *context) { IdentifierNode * current; uint64_t end; if ( !context ) return SNORT_FILE_TYPE_UNKNOWN; if ( !buf || len <= 0 ) return SNORT_FILE_TYPE_CONTINUE; if ( !context->file_type_context ) { FileConfig * file_config = (FileConfig *)context->file_config; context->file_type_context = file_config->identifier_root; } current = (IdentifierNode*)context->file_type_context; end = context->processed_bytes + len; while ( current && ( current->offset >= context->processed_bytes ) ) { /* Found file id, save and continue */ if ( current->type_id ) context->file_type_id = current->type_id; if ( current->offset >= end ) { /* Save current state */ context->file_type_context = current; return SNORT_FILE_TYPE_CONTINUE; } /* Move to the next level */ current = current->next[buf[current->offset - context->processed_bytes]]; } /*Either end of magics or passed the current offset*/ context->file_type_context = NULL; if ( context->file_type_id == SNORT_FILE_TYPE_CONTINUE ) context->file_type_id = SNORT_FILE_TYPE_UNKNOWN; return context->file_type_id; } void file_identifiers_free(void *conf) { IdentifierMemoryBlock *id_memory_next; FileConfig *file_config = (FileConfig *)conf; if (!file_config) return; /*Release memory used for identifiers*/ id_memory_current = file_config->id_memory_root; while (id_memory_current) { id_memory_next = id_memory_current->next; SnortPreprocFree(id_memory_current->mem_block, sizeof(IdentifierNode), PP_FILE, PP_MEM_CATEGORY_SESSION); SnortPreprocFree(id_memory_current, sizeof(IdentifierMemoryBlock), PP_FILE, PP_MEM_CATEGORY_SESSION); id_memory_current = id_memory_next; } file_config->id_memory_root = NULL; identifierMergeHashFree(); } #ifdef DEBUG_MSGS void file_identifiers_print(IdentifierNode* current) { int i; DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Working on pointer %p, offset:%d\n", (void *) current, current->offset);); for (i = 0; i < MAX_BRANCH; i++) { if (current->next[i]) { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Magic number: %x ", i);); file_identifiers_print(current->next[i]); } } if (current->type_id) { DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Type: %d\n", current->type_id);); } return; } char *file_type_test(void *conf) { uint8_t str[100] = {0x4d, 0x5a, 0x46, 0x38, 0x66, 0x72, 0x65, 0x65, 0}; unsigned int i; uint32_t type_id; FileContext *context = SnortPreprocAlloc(1, sizeof (*context), PP_FILE, PP_MEM_CATEGORY_SESSION); static const char *file_type = "MSEXE"; printf("Check string:"); for (i = 0; i < strlen((char*)str); i++) { printf(" %x", str[i]); } printf("\n"); context->file_config = conf; type_id = file_identifiers_match(str, strlen((char *)str), context); if (SNORT_FILE_TYPE_UNKNOWN == type_id) { printf("File type is unknown\n"); } else if (SNORT_FILE_TYPE_CONTINUE != type_id) printf("File type is: %s (%d)\n", file_type_name(conf, type_id), type_id); SnortPreprocFree(context, sizeof(FileContext), PP_FILE, PP_MEM_CATEGORY_SESSION); return ((char *)file_type); } #endif snort-2.9.20/src/file-process/libs/file_config.h0000644000175000017500000000644614241076577017650 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.2012 - Initial Source Code. Hcao */ #ifndef __FILE_CONFIG_H__ #define __FILE_CONFIG_H__ #include "file_lib.h" #include "file_identifier.h" #define FILE_ID_MAX 1024 #define IS_RULE_TYPE_IDENT(c) (isalnum(c) || (c) == '.' || (c) == '_') typedef struct _IdentifierMemoryBlock { void *mem_block; /*the node that is shared*/ struct _IdentifierMemoryBlock *next; /*next node*/ }IdentifierMemoryBlock; #if defined (SIDE_CHANNEL) && defined (REG_TEST) typedef struct _FileSSConfig { char *startup_input_file; char *runtime_output_file; }FileSSConfig; #endif typedef struct _fileConfig { IdentifierNode *identifier_root; /*Root of magic tries*/ IdentifierMemoryBlock *id_memory_root; /*root of memory used*/ RuleInfo *FileRules[FILE_ID_MAX + 1]; int64_t file_type_depth; int64_t file_signature_depth; int64_t file_block_timeout; int64_t file_lookup_timeout; bool block_timeout_lookup; int64_t file_capture_memcap; int64_t file_capture_max_size; int64_t file_capture_min_size; int64_t file_capture_block_size; #if defined(DEBUG_MSGS) || defined (REG_TEST) int64_t show_data_depth; #endif int64_t file_depth; #ifdef SIDE_CHANNEL bool use_side_channel; #ifdef REG_TEST FileSSConfig *file_ss_config; #endif #endif } FileConfig; /* Return all rule id's that match a a given "type" string. */ bool get_ids_from_type( const FileConfig* conf, const char * type, uint32_t ** ids, uint32_t * count ); /* Return all rule id's that match a a given "type" and "version" strings. */ bool get_ids_from_type_version( const FileConfig* conf, const char * type, const char * version, uint32_t ** ids, uint32_t * count ); /* Return all rule id's in a given file rule group. */ bool get_ids_from_group( const FileConfig* conf, const char * group, uint32_t ** ids, uint32_t * count ); /* * Parse file magic rule * * Args: * char *args: file magic rule string * void *file_config: pointer to file config */ void file_rule_parse(char *args, FileConfig* file_config); /* * Get rule information * * Args: * void *file_config: pointer to file config * uint32_t rule_id: file rule ID */ RuleInfo *file_rule_get(FileConfig* conf, uint32_t rule_id); /* Free resource used by file rules * * Args: * void *file_config: pointer to file config */ void file_rule_free(FileConfig* conf); #endif snort-2.9.20/src/file-process/libs/Makefile.in0000644000175000017500000004053514242725547017274 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/file-process/libs ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libfile_a_AR = $(AR) $(ARFLAGS) libfile_a_LIBADD = am_libfile_a_OBJECTS = file_lib.$(OBJEXT) file_config.$(OBJEXT) \ file_identifier.$(OBJEXT) libfile_a_OBJECTS = $(am_libfile_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libfile_a_SOURCES) DIST_SOURCES = $(libfile_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies noinst_LIBRARIES = libfile.a libfile_a_SOURCES = \ file_lib.c \ file_lib.h \ file_config.c \ file_config.h \ file_identifier.c \ file_identifier.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/file-process/libs/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/file-process/libs/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libfile.a: $(libfile_a_OBJECTS) $(libfile_a_DEPENDENCIES) $(EXTRA_libfile_a_DEPENDENCIES) $(AM_V_at)-rm -f libfile.a $(AM_V_AR)$(libfile_a_AR) libfile.a $(libfile_a_OBJECTS) $(libfile_a_LIBADD) $(AM_V_at)$(RANLIB) libfile.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/file-process/libs/file_lib.c0000644000175000017500000003037614241076603017131 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.12 - Initial Source Code. Hcao */ #ifdef HAVE_CONFIG_H #include #endif #include "sf_types.h" #include "file_lib.h" #include "file_identifier.h" #include "file_config.h" #include #include #include #include #include "sf_sechash.h" #include "memory_stats.h" #include "util.h" #include "file_capture.h" static inline int get_data_size_from_depth_limit(FileContext* context, FileProcessType type, int data_size) { FileConfig *file_config = (FileConfig *)context->file_config; uint64_t max_depth; if (!file_config) return data_size; switch(type) { case SNORT_FILE_TYPE_ID: max_depth = file_config->file_type_depth; break; case SNORT_FILE_SHA256: max_depth = file_config->file_signature_depth; break; default: return data_size; } if (context->processed_bytes > max_depth) data_size = -1; else if (context->processed_bytes + data_size > max_depth) data_size = (int)(max_depth - context->processed_bytes); return data_size; } /* stop file type identification */ static inline void _finalize_file_type (FileContext* context) { if (SNORT_FILE_TYPE_CONTINUE == context->file_type_id) context->file_type_id = SNORT_FILE_TYPE_UNKNOWN; context->file_type_context = NULL; } /* * Main File type processing function * We use file type context to decide file type across packets * * File type detection is completed either when * 1) file is completed or * 2) file type depth is reached or * 3) file magics are exhausted in depth * */ void file_type_id( FileContext* context, uint8_t* file_data, int size, FilePosition position) { int data_size; if (!context) return; /* file type already found and no magics to continue*/ if (context->file_type_id && !context->file_type_context) return; /* Check whether file type depth is reached*/ data_size = get_data_size_from_depth_limit(context, SNORT_FILE_TYPE_ID, size); if (data_size < 0) { _finalize_file_type(context); return; } file_identifiers_match(file_data, data_size, context); /* Check whether file transfer is done or type depth is reached*/ if ( (position == SNORT_FILE_END) || (position == SNORT_FILE_FULL) || (data_size != size) ) { _finalize_file_type(context); } } void file_signature_sha256(FileContext* context, uint8_t* file_data, int size, FilePosition position) { int data_size; if (!context) return; data_size = get_data_size_from_depth_limit(context, SNORT_FILE_SHA256, size); if (data_size != size) { context->file_state.sig_state = FILE_SIG_DEPTH_FAIL; return; } switch (position) { case SNORT_FILE_START: if (!context->file_signature_context) context->file_signature_context = SnortPreprocAlloc(1, sizeof(SHA256CONTEXT), PP_FILE, PP_MEM_CATEGORY_SESSION); SHA256INIT((SHA256CONTEXT *)context->file_signature_context); SHA256UPDATE((SHA256CONTEXT *)context->file_signature_context, file_data, data_size); if(context->file_state.sig_state == FILE_SIG_FLUSH) { static uint8_t file_signature_context_backup[sizeof(SHA256CONTEXT)]; context->sha256 = SnortPreprocAlloc(1, SHA256_HASH_SIZE, PP_FILE, PP_MEM_CATEGORY_SESSION); memcpy(file_signature_context_backup, context->file_signature_context, sizeof(SHA256CONTEXT)); SHA256FINAL(context->sha256, (SHA256CONTEXT *)context->file_signature_context); memcpy(context->file_signature_context, file_signature_context_backup, sizeof(SHA256CONTEXT)); } break; case SNORT_FILE_MIDDLE: if (!context->file_signature_context) context->file_signature_context = SnortPreprocAlloc(1, sizeof(SHA256CONTEXT), PP_FILE, PP_MEM_CATEGORY_SESSION); SHA256UPDATE((SHA256CONTEXT *)context->file_signature_context, file_data, data_size); if(context->file_state.sig_state == FILE_SIG_FLUSH) { static uint8_t file_signature_context_backup[sizeof(SHA256CONTEXT)]; if(!context->sha256) context->sha256 = SnortPreprocAlloc(1, SHA256_HASH_SIZE, PP_FILE, PP_MEM_CATEGORY_SESSION); memcpy(file_signature_context_backup, context->file_signature_context, sizeof(SHA256CONTEXT)); SHA256FINAL(context->sha256, (SHA256CONTEXT *)context->file_signature_context); memcpy(context->file_signature_context, file_signature_context_backup, sizeof(SHA256CONTEXT)); } break; case SNORT_FILE_END: if (!context->file_signature_context) context->file_signature_context = SnortPreprocAlloc(1, sizeof(SHA256CONTEXT), PP_FILE, PP_MEM_CATEGORY_SESSION); if (context->processed_bytes == 0) SHA256INIT((SHA256CONTEXT *)context->file_signature_context); SHA256UPDATE((SHA256CONTEXT *)context->file_signature_context, file_data, data_size); if(!context->sha256) context->sha256 = SnortPreprocAlloc(1, SHA256_HASH_SIZE, PP_FILE, PP_MEM_CATEGORY_SESSION); SHA256FINAL(context->sha256, (SHA256CONTEXT *)context->file_signature_context); context->file_state.sig_state = FILE_SIG_DONE; break; case SNORT_FILE_FULL: if (!context->file_signature_context) context->file_signature_context = SnortPreprocAlloc(1, sizeof(SHA256CONTEXT), PP_FILE, PP_MEM_CATEGORY_SESSION); SHA256INIT((SHA256CONTEXT *)context->file_signature_context); SHA256UPDATE((SHA256CONTEXT *)context->file_signature_context, file_data, data_size); if(!context->sha256) context->sha256 = SnortPreprocAlloc(1, SHA256_HASH_SIZE, PP_FILE, PP_MEM_CATEGORY_SESSION); SHA256FINAL(context->sha256, (SHA256CONTEXT *)context->file_signature_context); context->file_state.sig_state = FILE_SIG_DONE; break; default: break; } } /*File context management*/ FileContext *file_context_create(void) { FileContext *context = (FileContext *)SnortPreprocAlloc(1, sizeof(*context), PP_FILE, PP_MEM_CATEGORY_SESSION); return (context); } static inline void cleanDynamicContext (FileContext *context) { if (context->file_signature_context) SnortPreprocFree(context->file_signature_context, sizeof(SHA256CONTEXT), PP_FILE, PP_MEM_CATEGORY_SESSION); if(context->sha256) SnortPreprocFree(context->sha256, SHA256_HASH_SIZE, PP_FILE, PP_MEM_CATEGORY_SESSION); if(context->file_capture) file_capture_stop(context); if(context->file_name && context->file_name_saved) SnortPreprocFree(context->file_name, context->file_name_size, PP_FILE, PP_MEM_CATEGORY_SESSION); } void file_context_reset(FileContext *context) { cleanDynamicContext(context); memset(context, 0, sizeof(*context)); } void file_context_free(void *ctx) { FileContext *context = (FileContext *)ctx; if (!context || context->attached_file_entry) return; cleanDynamicContext(context); SnortPreprocFree(context, sizeof(FileContext), PP_FILE, PP_MEM_CATEGORY_SESSION); } /*File properties*/ /*Only set the pointer for performance, no deep copy*/ void file_name_set (FileContext *context, uint8_t *file_name, uint32_t name_size, bool save_in_context) { uint8_t *name = file_name; if (!context) { FILE_ERROR("Failed to set file name: no context"); return; } if (save_in_context) { if (context->file_name && context->file_name_saved) SnortPreprocFree(context->file_name, sizeof(name_size), PP_FILE, PP_MEM_CATEGORY_SESSION); name = SnortPreprocAlloc(1, name_size, PP_FILE, PP_MEM_CATEGORY_SESSION); memcpy(name, file_name, name_size); context->file_name_saved = true; } context->file_name = name; context->file_name_size = name_size; } /* Return 1: file name available, * 0: file name is unavailable */ int file_name_get (FileContext *context, uint8_t **file_name, uint32_t *name_size) { if (!context) { FILE_ERROR("Failed to fetch file name: no context"); return 0; } if (file_name) *file_name = context->file_name; else { FILE_ERROR("Failed to fetch file name: name parameter NULL"); return 0; } if (name_size) *name_size = context->file_name_size; else { FILE_ERROR("Failed to fetch file name: size parameter NULL"); return 0; } FILE_DEBUG("File name fetched from context: %s, size %d",(char*)(*file_name),*name_size); return 1; } void file_size_set (FileContext *context, uint64_t file_size) { if (!context) { FILE_ERROR("Failed to set file size: no context"); return; } context->file_size = file_size; } uint64_t file_size_get (FileContext *context) { if (!context) { FILE_ERROR("Failed to fetch file size: no context"); return 0; } FILE_DEBUG("File size fetched from context: %d",context->file_size); return (context->file_size); } void file_direction_set (FileContext *context, bool upload) { if (!context) return; context->upload = upload; } bool file_direction_get (FileContext *context) { if (!context) return false; return (context->upload); } void file_sig_sha256_set (FileContext *context, uint8_t * signature) { if (!context) return; context->sha256= signature; } uint8_t* file_sig_sha256_get (FileContext *context) { if (!context) return NULL; return (context->sha256); } char* file_type_name(void* conf, uint32_t id) { RuleInfo *info; if (SNORT_FILE_TYPE_UNKNOWN == id) return ("Unknown file type, done!"); else if (SNORT_FILE_TYPE_CONTINUE == id) return ("Undecided file type, continue..."); info = file_rule_get(conf, id); if (info != NULL) return info->type; else return NULL; } bool file_IDs_from_type(const void *conf, const char *type, uint32_t **ids, uint32_t *count) { if ( !type ) return false; return get_ids_from_type(conf, type, ids, count); } bool file_IDs_from_type_version(const void *conf, const char *type, const char *version, uint32_t **ids, uint32_t *count ) { if ( !type || !version ) return false; return get_ids_from_type_version(conf, type, version, ids, count); } bool file_IDs_from_group(const void *conf, const char *group, uint32_t **ids, uint32_t *count) { if ( !group ) return false; return get_ids_from_group(conf, group, ids, count); } #if defined(DEBUG_MSGS) || defined (REG_TEST) /* * Print a 32-byte hash value. */ void file_sha256_print(unsigned char *hash) { printf("SHA256: %02X%02X %02X%02X %02X%02X %02X%02X " "%02X%02X %02X%02X %02X%02X %02X%02X " "%02X%02X %02X%02X %02X%02X %02X%02X " "%02X%02X %02X%02X %02X%02X %02X%02X\n", hash[0], hash[1], hash[2], hash[3], hash[4], hash[5], hash[6], hash[7], hash[8], hash[9], hash[10], hash[11], hash[12], hash[13], hash[14], hash[15], hash[16], hash[17], hash[18], hash[19], hash[20], hash[21], hash[22], hash[23], hash[24], hash[25], hash[26], hash[27], hash[28], hash[29], hash[30], hash[31]); } #endif snort-2.9.20/src/file-process/libs/file_config.c0000644000175000017500000005061714241076576017641 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.2012 - Initial Source Code. Hcao */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "sf_types.h" #include "util.h" #include "mstring.h" #include "parser.h" #include "memory_stats.h" #include "sfutil/strvec.h" #include "file_lib.h" #include "file_identifier.h" #include "file_config.h" typedef void (*ParseFileOptFunc)(RuleInfo*, char *); typedef struct _FileOptFunc { char *name; int args_required; int only_once; /*the option is one per file rule*/ ParseFileOptFunc parse_func; } FileOptFunc; #define FILE_OPT__TYPE "type" #define FILE_OPT__ID "id" #define FILE_OPT__VERSION "ver" #define FILE_OPT__CATEGORY "category" #define FILE_OPT__MSG "msg" #define FILE_OPT__REVISION "rev" #define FILE_OPT__CONTENT "content" #define FILE_OPT__OFFSET "offset" #define FILE_OPT__GROUP "group" #define FILE_REVISION_MAX UINT32_MAX #define FILE_OFFSET_MAX UINT32_MAX static void ParseFileRuleType(RuleInfo *, char *); static void ParseFileRuleID(RuleInfo *, char *); static void ParseFileRuleVersion(RuleInfo *, char *); static void ParseFileRuleCategory(RuleInfo *, char *); static void ParseFileRuleMessage(RuleInfo *, char *); static void ParseFileRevision(RuleInfo *, char *); static void ParseFileContent(RuleInfo *, char *); static void ParseFileOffset(RuleInfo *, char *); static void ParseFileGroup(RuleInfo *, char *); static const FileOptFunc file_options[] = { { FILE_OPT__TYPE, 1, 1, ParseFileRuleType }, { FILE_OPT__ID, 1, 1, ParseFileRuleID }, { FILE_OPT__VERSION, 0, 1, ParseFileRuleVersion }, { FILE_OPT__CATEGORY, 1, 1, ParseFileRuleCategory }, { FILE_OPT__MSG, 0, 1, ParseFileRuleMessage }, { FILE_OPT__REVISION, 0, 1, ParseFileRevision }, { FILE_OPT__CONTENT, 1, 0, ParseFileContent }, { FILE_OPT__OFFSET, 1, 0, ParseFileOffset }, { FILE_OPT__GROUP, 1, 0, ParseFileGroup }, { NULL, 0, 0, NULL } /* Marks end of array */ }; /* Used for content modifiers that are used as rule options - need to get the * last magic which is the one they are modifying. If there isn't a last magic * error that a content must be specified before the modifier */ static inline MagicData * GetLastMagic(RuleInfo *rule, const char *option) { MagicData *mdata; MagicData *lastMagic = NULL; if ((rule) && (rule->magics)) { for (mdata = rule->magics; mdata->next != NULL; mdata = mdata->next); lastMagic = mdata; } if (lastMagic == NULL) { ParseError("Please place \"content\" rules before \"%s\" modifier", option == NULL ? "unknown" : option); } return lastMagic; } static inline bool valid_rule_type_str( const char *src ) { assert( src ); assert( *src ); while ( *src ) { if ( !IS_RULE_TYPE_IDENT((int)*src) ) return false; src++; } return true; } static void ParseFileRuleType(RuleInfo *rule, char *args) { DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Type args: %s\n", args);); if (args == NULL) ParseError("Type rule option requires an argument."); if ( !valid_rule_type_str(args) ) { ParseError("Invalid argument to 'type' rule option: '%s'. " "Can only contain '0-9','A-Z','a-z','_' and '.' characters.", args); } rule->type = SnortStrdup(args); } static void ParseFileRuleID(RuleInfo *rule, char *args) { unsigned long int id; char *endptr; DEBUG_WRAP(DebugMessage(DEBUG_FILE,"ID args: %s\n", args);); if (args == NULL) ParseError("ID rule option requires an argument."); id = SnortStrtoul(args, &endptr, 0); if ((errno == ERANGE) || (*endptr != '\0')||(id > FILE_ID_MAX)) { ParseError("Invalid argument to 'id' rule option: %s. " "Must be a positive integer.", args); } rule->id = (uint32_t)id; } static void ParseFileRuleCategory(RuleInfo *rule, char *args) { DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Category args: %s\n", args);); if (args == NULL) ParseError("Category rule option requires an argument."); rule->category = SnortStrdup(args); } static void ParseFileRuleVersion(RuleInfo *rule, char *args) { DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Version args: %s\n", args);); if (args == NULL) ParseError("Version rule option requires an argument."); if ( !valid_rule_type_str(args) ) { ParseError("Invalid argument to 'ver' rule option: '%s'. " "Can only contain '0-9','A-Z','a-z','_' and '.' characters.", args); } rule->version = SnortStrdup(args); } static void ParseFileRuleMessage(RuleInfo *rule, char *args) { size_t i; int escaped = 0; char msg_buf[2048]; /* Arbitrary length, but should be enough */ if (args == NULL) ParseError("Message rule option requires an argument."); DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Msg args: %s\n", args);); if (*args == '"') { /* Have to have at least quote, char, quote */ if (strlen(args) < 3) ParseError("Empty argument passed to rule option 'msg'."); if (args[strlen(args) - 1] != '"') { ParseError("Unmatch quote in rule option 'msg'."); } /* Move past first quote and NULL terminate last quote */ args++; args[strlen(args) - 1] = '\0'; /* If last quote is escaped, fatal error. * Make sure the backslash is not escaped */ if ((args[strlen(args) - 1] == '\\') && (strlen(args) > 1) && (args[strlen(args) - 2] != '\\')) { ParseError("Unmatch quote in rule option 'msg'."); } } /* Only valid escaped chars are ';', '"' and '\' */ /* Would be ok except emerging threats rules are escaping other chars */ for (i = 0; (i < sizeof(msg_buf)) && (*args != '\0');) { if (escaped) { msg_buf[i++] = *args; escaped = 0; } else if (*args == '\\') { escaped = 1; } else { msg_buf[i++] = *args; } args++; } if (escaped) { ParseError("Message in 'msg' rule option has invalid escape character." "\n"); } if (i == sizeof(msg_buf)) { ParseError("Message in 'msg' rule option too long. Please limit " "to %d characters.", sizeof(msg_buf)); } msg_buf[i] = '\0'; DEBUG_WRAP(DebugMessage(DEBUG_FILE, "Message: %s\n", msg_buf);); rule->message = SnortStrdup(msg_buf); } static void ParseFileRevision(RuleInfo *rule, char *args) { unsigned long int rev; char *endptr; DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Revision args: %s\n", args);); if (args == NULL) ParseError("Revision rule option requires an argument."); rev = SnortStrtoul(args, &endptr, 0); if ((errno == ERANGE) || (*endptr != '\0') || (rev > FILE_REVISION_MAX)) { ParseError("Invalid argument to 'rev' rule option: %s. " "Must be a positive integer.", args); } rule->rev = (uint32_t)rev; } static uint8_t* convertTextToHex(char *text, int *size) { int i; char **toks; int num_toks; char hex_buf[3]; uint8_t *hex; toks = mSplit(text, " ", 0, &num_toks, 0); if (num_toks <= 0) { ParseError("No hexmode argument."); } hex = (uint8_t*) SnortPreprocAlloc(1, num_toks, PP_FILE, PP_MEM_CATEGORY_SESSION); *size = num_toks; memset(hex_buf, 0, sizeof(hex_buf)); for (i = 0; i < num_toks; i++) { char *current_ptr = toks[i]; if (2 != strlen(current_ptr)) { ParseError("Content hexmode argument has invalid " "number of hex digits. The argument '%s' " "must contain a full even byte string.", current_ptr); } if(isxdigit((int) *current_ptr)) { hex_buf[0] = *current_ptr; } else { ParseError("\"%c\" is not a valid hex value, " "please input hex values (0x0 - 0xF)", (char) *current_ptr); } current_ptr++; if(isxdigit((int) *current_ptr)) { hex_buf[1] = *current_ptr; } else { ParseError("\"%c\" is not a valid hex value, " "please input hex values (0x0 - 0xF)", (char) *current_ptr); } DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Hex buffer: %s\n", hex_buf);); hex[i] = (uint8_t) strtol(hex_buf, (char **) NULL, 16)&0xFF; memset(hex_buf, 0, sizeof(hex_buf)); DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Hex value: %x\n", hex[i]);); } mSplitFree(&toks, num_toks); return hex; } static void ParseFileContent(RuleInfo *rule, char *args) { MagicData *predata = NULL; MagicData *newdata; char *start_ptr; char *end_ptr; char *tmp; if (args == NULL) ParseError("Parse File Magic Got Null enclosed in vertical bar (|)!"); DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Content args: %s\n", args);); while(isspace((int)*args)) args++; /* find the start of the data */ start_ptr = strchr(args, '|'); if (start_ptr != args) ParseError("Content data needs to be enclosed in vertical bar (|)!"); /* move the start up from the beggining quotes */ start_ptr++; /* find the end of the data */ end_ptr = strrchr(start_ptr, '|'); if (end_ptr == NULL) ParseError("Content data needs to be enclosed in vertical bar (|)!"); /* Move the null termination up a bit more */ *end_ptr = '\0'; /* Is there anything other than whitespace after the trailing * double quote? */ tmp = end_ptr + 1; while (*tmp != '\0' && isspace ((int)*tmp)) tmp++; if (strlen (tmp) > 0) { ParseError("Bad data (possibly due to missing semicolon) after " "trailing double quote."); } if (rule->magics) { for (predata = rule->magics; predata->next != NULL; predata = predata->next); } newdata = SnortPreprocAlloc(1, sizeof(*newdata), PP_FILE, PP_MEM_CATEGORY_SESSION); DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Content args: %s\n", start_ptr);); newdata->content = convertTextToHex(start_ptr, &(newdata->content_len)); if (predata) { predata->next = newdata; } else { rule->magics = newdata; } } static void ParseFileOffset(RuleInfo *rule, char *args) { unsigned long int offset; char *endptr; MagicData *mdata; DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Offset args: %s\n", args);); if (args == NULL) ParseError("Offset rule option requires an argument."); offset = SnortStrtoul(args, &endptr, 0); if ((errno == ERANGE) || (*endptr != '\0')|| (offset > FILE_OFFSET_MAX)) { ParseError("Invalid argument to 'offset' rule option: %s. " "Must be a positive integer.", args); } mdata = GetLastMagic(rule, "offset"); mdata->offset = (uint32_t)offset; } static void ParseFileGroup(RuleInfo * rule, char * args) { char **toks; int num_toks, i; DEBUG_WRAP( DebugMessage(DEBUG_FILE,"Group args: %s\n", args); ); toks = mSplit(args, ",", 0, &num_toks, 0); if (num_toks < 1) { ParseError("Group rule option requires an argument."); } rule->groups = StringVector_New(); for (i = 0; i < num_toks; i++) StringVector_Add(rule->groups, toks[i]); mSplitFree(&toks, num_toks); } static void parse_options(char *option_name, char *option_args, char *configured, RuleInfo *rule) { int i; for (i = 0; file_options[i].name != NULL; i++) { if (strcasecmp(option_name, file_options[i].name)) continue; if (configured[i] && file_options[i].only_once) { ParseError("Only one '%s' rule option per rule.", option_name); } if ((option_args == NULL) && file_options[i].args_required) { ParseError("No argument passed to keyword \"%s\". " "Make sure you didn't forget a ':' or the " "argument to this keyword.\n",option_name); } file_options[i].parse_func(rule, option_args); configured[i] = 1; return; } /* Unrecognized rule option */ ParseError("Unknown rule option: '%s'.", option_name); } #ifdef DEBUG_MSGS static int file_rule_print(RuleInfo *rule) { MagicData *mdata; if (!rule) { DebugMessage(DEBUG_FILE,"Rule is NULL!\n"); return 0; } DebugMessage(DEBUG_FILE,"File type Id: %d\n", rule->id); DebugMessage(DEBUG_FILE,"File type name: %s\n", rule->type); DebugMessage(DEBUG_FILE,"File type Category: %s\n", rule->category); DebugMessage(DEBUG_FILE,"Rule revision: %d\n", rule->rev); DebugMessage(DEBUG_FILE,"Rule message: %s\n", rule->message); if (!rule->magics) { DebugMessage(DEBUG_FILE,"No megic defined in rule!\n"); } for (mdata = rule->magics; mdata != NULL; mdata = mdata->next) { int i; int buff_size = mdata->content_len * 2 + 1; char *buff = SnortPreprocAlloc(1, buff_size, PP_FILE, PP_MEM_CATEGORY_SESSION); char *start_ptr = buff; DebugMessage(DEBUG_FILE,"Magic offset: %d\n", mdata->offset); DebugMessage(DEBUG_FILE,"Magic length: %d\n", mdata->content_len); for (i = 0; (i < mdata->content_len) && (buff_size > 0); i++) { int num_read; num_read = snprintf(start_ptr, buff_size, "%x",mdata->content[i]); start_ptr += num_read; buff_size -= num_read; } DebugMessage(DEBUG_FILE,"Magic content: %s\n", buff); SnortPreprocFree(buff, buff_size, PP_FILE, PP_MEM_CATEGORY_SESSION); } return rule->id; } #endif static inline void __add_id_to_list( uint32_t **list, uint32_t *list_size, const uint32_t id ) { uint32_t *_temp; (*list_size)++; _temp = *list; /* Not accounting this realloc and free for memory serviceability because of infra limitation*/ if ( (*list = realloc(_temp, sizeof(**list)*(*list_size))) == NULL ) { free(_temp); FatalError("Failed realloc!"); } (*list)[(*list_size)-1] = id; } bool get_ids_from_type(const FileConfig* file_config, const char *type, uint32_t **ids, uint32_t *count) { bool status = false; int i; if ( !file_config ) return NULL; /* Search for the matching rules */ for ( i = 0; i <= FILE_ID_MAX; i++ ) { const RuleInfo * rule = file_config->FileRules[i]; if ( !rule ) continue; if ( strcmp(rule->type, type) ) continue; __add_id_to_list( ids, count, rule->id ); status = true; } return status; } bool get_ids_from_type_version(const FileConfig* file_config, const char *type, const char *version, uint32_t **ids, uint32_t *count) { bool status = false; int i; if ( !file_config ) return NULL; /* Search for the matching rules */ for ( i = 0; i <= FILE_ID_MAX; i++ ) { const RuleInfo *rule = file_config->FileRules[i]; if ( !rule || !rule->version ) continue; if ( strcmp(rule->type, type) ) continue; if ( strcmp(rule->version, version) ) continue; __add_id_to_list( ids, count, rule->id ); status = true; } return status; } bool get_ids_from_group(const FileConfig* file_config, const char *group, uint32_t **ids, uint32_t *count) { bool status = false; int i; if ( !file_config ) return NULL; /* Search for the matching rules */ for ( i = 0; i <= FILE_ID_MAX; i++ ) { const RuleInfo *rule = file_config->FileRules[i]; const char *_group; int j = 0; if ( !rule || !rule->groups ) continue; /* Check if this rule belongs to the caller provided group */ while( (_group = StringVector_Get(rule->groups, j++)) ) { if ( _group && !strcmp(_group, group) ) break; } if ( !_group ) continue; __add_id_to_list( ids, count, rule->id ); status = true; } return status; } /*The main function for parsing rule option*/ void file_rule_parse(char *args, FileConfig* file_config) { char **toks; int num_toks; int i; char configured[sizeof(file_options) / sizeof(FileOptFunc)]; RuleInfo *rule; if (!file_config) { return; } rule = SnortPreprocAlloc(1, sizeof (*rule), PP_FILE, PP_MEM_CATEGORY_SESSION); DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Loading file configuration: %s\n", args);); toks = mSplit(args, ";", 0, &num_toks, 0); /* get rule option pairs */ /* Used to determine if a rule option has already been configured * in the rule. Some can only be configured once */ memset(configured, 0, sizeof(configured)); for (i = 0; i < num_toks; i++) { char **opts; int num_opts; char *option_args = NULL; DEBUG_WRAP(DebugMessage(DEBUG_FILE," option: %s\n", toks[i]);); /* break out the option name from its data */ opts = mSplit(toks[i], ":", 2, &num_opts, '\\'); DEBUG_WRAP(DebugMessage(DEBUG_FILE," option name: %s\n", opts[0]);); if (num_opts == 2) { option_args = opts[1]; DEBUG_WRAP(DebugMessage(DEBUG_FILE," option args: %s\n", option_args);); } parse_options(opts[0], option_args, configured, rule); mSplitFree(&opts, num_opts); } if (file_config->FileRules[rule->id]) { ParseError("File type: duplicated rule id %d defined!", rule->id); } file_config->FileRules[rule->id] = rule; DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Rule parsed: %d\n", file_rule_print(rule));); file_identifers_update(rule,file_config); DEBUG_WRAP(DebugMessage(DEBUG_FILE,"Total memory used for identifiers: " "%d\n", file_identifiers_usage());); mSplitFree(&toks, num_toks); } RuleInfo *file_rule_get(FileConfig* file_config, uint32_t id) { if (file_config) { return (file_config->FileRules[id]); } return NULL; } static void _free_file_magic (MagicData *magics) { if (!magics) return; _free_file_magic(magics->next); SnortPreprocFree(magics->content, sizeof(magics->content_len), PP_FILE, PP_MEM_CATEGORY_SESSION); SnortPreprocFree(magics , sizeof(MagicData), PP_FILE, PP_MEM_CATEGORY_SESSION); } static void _free_file_rule(RuleInfo *rule) { if ( !rule ) return; /* Not changing the free here because memory is allocated using strdup */ if ( rule->category ) free(rule->category); if ( rule->message ) free(rule->message); if ( rule->type ) free(rule->type); if ( rule->version ) free(rule->version); if ( rule->groups ) StringVector_Delete(rule->groups); _free_file_magic(rule->magics); SnortPreprocFree(rule, sizeof(RuleInfo), PP_FILE, PP_MEM_CATEGORY_SESSION); } void file_rule_free(FileConfig* file_config) { int id; if (!file_config) return; for (id = 0; id < FILE_ID_MAX + 1; id++) { _free_file_rule (file_config->FileRules[id]); file_config->FileRules[id] = NULL; } } snort-2.9.20/src/file-process/libs/Makefile.am0000555000175000017500000000033514230012554017236 0ustar apoapoAUTOMAKE_OPTIONS=foreign no-dependencies noinst_LIBRARIES = libfile.a libfile_a_SOURCES = \ file_lib.c \ file_lib.h \ file_config.c \ file_config.h \ file_identifier.c \ file_identifier.h INCLUDES = @INCLUDES@ snort-2.9.20/src/file-process/libs/file_identifier.h0000644000175000017500000000550114241076602020501 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.2012 - Initial Source Code. Hcao */ #ifndef __FILE_IDENTIFIER_H__ #define __FILE_IDENTIFIER_H__ #include "file_lib.h" #define MAX_BRANCH (UINT8_MAX + 1) /* 256 identifiers pointers*/ typedef enum _IdNodeState { ID_NODE_NEW, ID_NODE_USED, ID_NODE_SHARED } IdNodeState; typedef struct _IdentifierNode { uint32_t type_id; /* magic content to match*/ IdNodeState state; uint32_t offset; /* offset from file start */ struct _IdentifierNode *next[MAX_BRANCH]; /* next levels*/ } IdentifierNode; typedef struct _IdentifierNodeHead { int offset; /* offset from file start */ IdentifierNode *start; /* start node for the trie at this offset*/ struct _IdentifierNodeHead *nextHead; /* pointer to next offset head*/ } IdentifierNodeHead; /* Initialize file type id states * * Args: None * Return: None */ void file_identifers_init(void); /* Insert one file rule to file identifiers trie, update file identifiers * * Args: * RuleInfo *rule: file magic rule information * void *conf: file configuration * * Return: * None */ void file_identifers_update(RuleInfo *rule, void *conf); /* Memory usage for all file magics * * Args: None * Return: * uint32_t: memory usage in bytes */ uint32_t file_identifiers_usage(void); /* Main process for file type identification. * This identification is stateful * * Args: * uint8_t *buf: data buffer pointer * int len: length of data buffer * FileContext *context: context for saving state * Return: * uint32_t: file type ID */ uint32_t file_identifiers_match(uint8_t *buf, int len, FileContext *cxt); /* Free file identifiers stored in conf * * Args: * void *conf: file configuration * * Return: * None */ void file_identifiers_free(void* conf); #ifdef DEBUG_MSGS void file_identifiers_print(IdentifierNode*); #endif #endif snort-2.9.20/src/file-process/libs/file_lib.h0000644000175000017500000001423714241076604017135 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.12 - Initial Source Code. Hcao */ #ifndef __FILE_LIB_H__ #define __FILE_LIB_H__ #ifdef HAVE_CONFIG_H #include #endif #include #include "sf_types.h" /* for bool */ #include "file_api.h" #include "sfdaq.h" #define SNORT_FILE_TYPE_UNKNOWN 1024 /**/ #define SNORT_FILE_TYPE_CONTINUE 0 /**/ typedef struct _MagicData { uint8_t *content; /* magic content to match*/ int content_len; /* length of magic content */ uint32_t offset; /* pattern search start offset */ /* Used in ds_list - do not try to iterate after parsing a rule * since the detection option tree will eliminate duplicates and * the list may have missing pmds */ struct _MagicData *next; /* ptr to next match struct */ } MagicData; typedef struct _RuleInfo { char *message; char *type; char *category; char *version; MagicData *magics; void *groups; uint32_t id; uint32_t rev; } RuleInfo; typedef struct _FileContext { bool file_type_enabled; bool file_signature_enabled; bool file_name_saved; bool upload; uint32_t file_name_size; uint8_t *file_name; uint64_t file_size; uint64_t processed_bytes; uint8_t *sha256; void * file_type_context; void * file_signature_context; void * file_config; time_t expires; uint16_t app_id; bool file_capture_enabled; bool partial_file; uint32_t file_type_id; FileCaptureInfo *file_capture; uint8_t *current_data; /*current file data*/ uint32_t current_data_len; File_Verdict verdict; bool suspend_block_verdict; /* for some SMB upload cases, file size is not known during SMB negotiation. We are setting * SIG_FLUSH during end of stream, but END of file is not set in case of SMB unknown * file size upload. This causing file capture to fail. This flag is used to set End of file, * only for SMB upload cases. */ bool smb_unknown_file_size; void* attached_file_entry; FileState file_state; uint32_t file_id; uint32_t file_config_version; } FileContext; /*Main File Processing functions */ void file_type_id( FileContext* context, uint8_t* file_data, int data_size, FilePosition position); void file_signature_sha256( FileContext* context, uint8_t* file_data, int data_size, FilePosition position); /*File context management*/ FileContext *file_context_create(void); void file_context_reset(FileContext *context); void file_context_free(void *context); /*File properties*/ void file_name_set (FileContext *context, uint8_t *file_name, uint32_t name_size, bool save_in_context); int file_name_get (FileContext *context, uint8_t **file_name, uint32_t *name_size); void file_size_set (FileContext *context, uint64_t file_size); uint64_t file_size_get (FileContext *context); void file_direction_set (FileContext *context, bool upload); bool file_direction_get (FileContext *context); void file_sig_sha256_set (FileContext *context, uint8_t *signature); uint8_t* file_sig_sha256_get (FileContext *context); char* file_type_name(void *conf, uint32_t); bool file_IDs_from_type(const void *conf, const char *type, uint32_t **ids, uint32_t *count); bool file_IDs_from_type_version(const void *conf, const char *type, const char *version, uint32_t **ids, uint32_t *count); bool file_IDs_from_group(const void *conf, const char *group, uint32_t **ids, uint32_t *count); extern int64_t file_type_depth; extern int64_t file_signature_depth; #if defined(DEBUG_MSGS) || defined (REG_TEST) void file_sha256_print(unsigned char *hash); #endif #if defined (DAQ_VERSION) && DAQ_VERSION > 9 const DAQ_PktHdr_t* daq_pktHdr; #define SAVE_DAQ_PKT_HDR(p) daq_pktHdr = ((Packet*)(p))->pkth #define FILE_PKT_DEBUG(logLevel, msg, args...)\ SNORT_DEBUG_PKT_LOG(daq_pktHdr,DAQ_DEBUG_PKT_MODULE_SNORTFILEPP,logLevel, msg, ##args) #define FILE_EMERGENCY(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_EMERGENCY,msg,##args) #define FILE_ALERT(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_ALERT,msg,##args) #define FILE_CRITICAL(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_CRITICAL,msg,##args) #define FILE_ERROR(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_ERROR,msg,##args) #define FILE_WARNING(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_WARNING,msg,##args) #define FILE_NOTICE(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_NOTICE,msg,##args) #define FILE_INFO(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_INFO,msg,##args) #define FILE_DEBUG(msg,args...) FILE_PKT_DEBUG(DAQ_DEBUG_PKT_LEVEL_DEBUG,msg,##args) #else #define FILE_LOG_MSGS(msg, args...) DEBUG_WRAP(DebugMessage(DEBUG_FILE, msg"\n", ##args);) #define FILE_EMERGENCY(msg,args...) FILE_LOG_MSGS(msg,##args) #define FILE_ALERT(msg,args...) FILE_LOG_MSGS(msg,##args) #define FILE_CRITICAL(msg,args...) FILE_LOG_MSGS(msg,##args) #define FILE_ERROR(msg,args...) FILE_LOG_MSGS(msg,##args) #define FILE_WARNING(msg,args...) FILE_LOG_MSGS(msg,##args) #define FILE_NOTICE(msg,args...) FILE_LOG_MSGS(msg,##args) #define FILE_INFO(msg,args...) FILE_LOG_MSGS(msg,##args) #define FILE_DEBUG(msg,args...) FILE_LOG_MSGS(msg,##args) #define SAVE_DAQ_PKT_HDR(p) #endif #endif snort-2.9.20/src/file-process/file_mime_config.h0000644000175000017500000000314714241076550017710 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifndef __FILE_MIME_CONFIG_H__ #define __FILE_MIME_CONFIG_H__ #include "file_api.h" #include "file_mail_common.h" /* Function prototypes */ void set_mime_decode_config_defauts(DecodeConfig *decode_conf); void set_mime_log_config_defauts(MAIL_LogConfig *log_config); int parse_mime_decode_args(DecodeConfig *decode_conf, char *arg, const char *preproc_name, char **strtok_saveptr); bool is_decoding_enabled(DecodeConfig *decode_conf); bool is_mime_log_enabled(MAIL_LogConfig *log_config); bool check_decode_config(DecodeConfig *currentConfig, DecodeConfig *defaultConfig, const char *preproc_name); #endif snort-2.9.20/src/file-process/circular_buffer.c0000644000175000017500000001206214241076536017565 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** ** Circular buffer is thread safe for one writer and one reader thread ** ** This implementaton is inspired by one slot open approach. ** See http://en.wikipedia.org/wiki/Circular_buffer ** ** 5.25.13 - Initial Source Code. Hui Cao */ #include "sf_types.h" #include "circular_buffer.h" #include #include /* Circular buffer object */ struct _CircularBuffer{ uint64_t size; /* maximum number of elements */ uint64_t start; /* index of oldest element, reader update only */ uint64_t end; /* index to write new element, writer update only*/ uint64_t under_run; uint64_t over_run; ElemType *elems; /* vector of elements */ uint64_t total_write; uint64_t total_read; }; /* This approach adds one bit to end and start pointers */ CircularBuffer * cbuffer_init(uint64_t size) { CircularBuffer* cb = calloc(1, sizeof(*cb)); if ( !cb ) return NULL; cb->size = size + 1; cb->elems = (ElemType *)calloc(cb->size, sizeof(ElemType)); if (!cb->elems) { free(cb); return NULL; } return cb; } void cbuffer_free(CircularBuffer *cb) { if(cb && cb->elems) { free(cb->elems); cb->elems = NULL; } free(cb); } /* We use mirror flag to detection full or empty efficiently*/ int cbuffer_is_full(CircularBuffer *cb) { uint64_t next = cb->end + 1; if ( next == cb->size ) next = 0; return (next == cb->start); } /* We use mirror flag to detection full or empty efficiently*/ int cbuffer_is_empty(CircularBuffer *cb) { return (cb->end == cb->start); } /* Returns number of elements in use*/ uint64_t cbuffer_used(CircularBuffer *cb) { /* cb->end < cb->start means passing the end of buffer */ if (cb->end < cb->start) { return (cb->size + cb->end - cb->start); } else { return (cb->end - cb->start); } } /* Returns number of free elements*/ uint64_t cbuffer_available(CircularBuffer *cb) { return (cbuffer_size(cb) - cbuffer_used(cb)); } /* Returns total number of elements*/ uint64_t cbuffer_size(CircularBuffer *cb) { return (cb->size - 1); } /* * Add one element to the buffer, * * Args: * CircularBuffer *: buffer * ElemType elem: the element to be added * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_write(CircularBuffer *cb, const ElemType elem) { uint64_t w = cb->end; if ( cbuffer_is_full (cb)) /* full, return error */ { cb->over_run++; return CB_FAIL; } cb->elems[w++] = elem; if ( w == cb->size ) w = 0; cb->end = w; cb->total_write++; return CB_SUCCESS; } /* * Read one element from the buffer and remove it from buffer, * * Args: * CircularBuffer *: buffer * ElemType *elem: the element pointer to be stored * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_read(CircularBuffer *cb, ElemType *elem) { uint64_t r = cb->start; if (cbuffer_is_empty(cb)) /* Empty, return error */ { cb->under_run++; return CB_FAIL; } *elem = cb->elems[r++]; if ( r == cb->size ) r = 0; cb->start = r; cb->total_read++; return CB_SUCCESS; } /* * Read one element from the buffer and no change on buffer * * Args: * CircularBuffer *: buffer * ElemType *elem: the element pointer to be stored * Return: * CB_FAIL * CB_SUCCESS */ int cbuffer_peek(CircularBuffer *cb, ElemType *elem) { if (cbuffer_is_empty(cb)) /* Empty, return error */ return CB_FAIL; *elem = cb->elems[cb->start]; return CB_SUCCESS; } /* Returns total number of reads*/ uint64_t cbuffer_num_reads(CircularBuffer *cb) { return (cb->total_read); } /* Returns total number of writes*/ uint64_t cbuffer_num_writes(CircularBuffer *cb) { return (cb->total_write); } /* Returns total number of writer overruns*/ uint64_t cbuffer_num_over_runs(CircularBuffer *cb) { return (cb->over_run); } /* Returns total number of reader overruns*/ uint64_t cbuffer_num_under_runs(CircularBuffer *cb) { return (cb->under_run); } snort-2.9.20/src/file-process/file_segment_process.h0000644000175000017500000000673014241076557020644 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * **************************************************************************** * ****************************************************************************/ /* * File data reassemble and processing * Author(s): Hui Cao * */ #ifndef _FILE_PROCESS_H_ #define _FILE_PROCESS_H_ #include #include "sf_types.h" #include "sfxhash.h" #include "file_lib.h" #include "decode.h" #include "ipv6_port.h" #include "util.h" typedef struct _FileCache { SFXHASH *hashTable; uint32_t max_files; uint32_t cleanup_files; FileCacheStatus status; uint64_t file_segment_memcap; } FileCache; typedef struct _FileSegment { /*Use single list for simplicity*/ struct _FileSegment *next; uint32_t offset; uint32_t size; /* actual data size*/ uint32_t segment_size; /* memory allocated, including headers */ uint8_t data[1]; /* variable length */ } FileSegment; typedef struct _FileKey { struct in6_addr sip; struct in6_addr dip; uint64_t file_id; } FileKey; typedef struct _FileEntry { FileContext *context; uint8_t *file_name; uint64_t offset; FileSegment *segments; uint64_t file_size; FileCache *file_cache; uint32_t file_name_size; bool file_resume_check; } FileEntry; /* Create file cache to store file segments and track file * memcap: total memory available for file cache, including file contexts * cleanup_files: maximal number of files pruned when memcap reached */ FileCache *file_cache_create(uint64_t memcap, uint32_t cleanup_files); /* Free file cache */ void file_cache_free(FileCache *fileCache); /* Get the status of file cache*/ FileCacheStatus *file_cache_status(FileCache *fileCache); bool file_cache_shrink_to_memcap(FileCache *fileCache, uint8_t *pWork); void file_cache_set_memcap(FileCache *fileCache, uint64_t memcap); /* Add/update a file entry in the file cache*/ void *file_cache_update_entry (FileCache *fileCache, void* p, uint64_t file_id, uint8_t *file_name, uint32_t file_name_size, uint64_t file_size, bool reset, bool no_update_size); /* Process file segments with offset specified. If file segment is out of order, * it will be put into the file seglist. Otherwise, it will be processed. * * Return: * 1: continue processing/log/block this file * 0: ignore this file */ int file_segment_process( FileCache *fileCache, void* p, uint64_t file_id, uint64_t file_size, const uint8_t* file_data, int data_size, uint64_t offset, bool upload); #endif /* _FILE_PROCESS_H_ */ snort-2.9.20/src/file-process/file_ss.h0000644000175000017500000000344514241076573016067 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ****************************************************************************/ /************************************************************************** * * file_ss.h * * Authors: Bhargava Jandhyala * * Description: * * File state sharing exported functionality. * **************************************************************************/ #ifndef __FILE_SS_H__ #define __FILE_SS_H__ #ifdef SIDE_CHANNEL #include "file_config.h" void FilePrintSSStats(void); void FileSSConfigInit(void); #ifdef REG_TEST void FilePrintSSConfig(FileSSConfig *file_ss_config); void FileCleanSS(void); #endif int CreateFileSSUpdate(void **msg_handle, void **hdr_ptr, void **data_ptr, uint32_t type, uint32_t data_len); int SendFileSSUpdate(void *msg_handle, void *hdr_ptr, void *data_ptr, uint32_t type, uint32_t data_len); #endif /* SIDE_CHANNEL */ #endif /* __FILE_SS_H__ */ snort-2.9.20/src/file-process/file_resume_block.h0000644000175000017500000000320714241076555020110 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2012-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifndef _FILE_RESUME_BLOCK_H_ #define _FILE_RESUME_BLOCK_H_ #include "decode.h" #include "file_api.h" void file_resume_block_init(void); void file_resume_block_cleanup(void); int file_resume_block_add_file(void *pkt, uint32_t file_sig, uint32_t timeout, File_Verdict verdict, uint32_t file_type_id, uint8_t *signature, uint16_t cli_port, uint16_t srv_port, bool create_pinhole, bool direction); File_Verdict file_resume_block_check(void *pkt, uint32_t file_sig); bool file_config_malware_check(void *ssnptr, uint16_t app_id); #ifdef SIDE_CHANNEL int ConsumeSSFileCache(const uint8_t *buf, uint32_t len); #endif #endif snort-2.9.20/src/file-process/file_stats.c0000644000175000017500000003144014241076574016570 0ustar apoapo/* ** ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 5.25.13 - Initial Source Code. Hui Cao */ #ifdef HAVE_CONFIG_H #include #endif #include "sf_types.h" #include #include #include "file_config.h" #include "file_stats.h" #include "file_capture.h" #include "snort.h" /* for extern SnortConfig *snort_conf */ FileStats file_stats; #if defined(DEBUG_MSGS) || defined (REG_TEST) #include #define MAX_CONTEXT_INFO_LEN 1024 void printFileContext(FileContext* context) { char buf[MAX_CONTEXT_INFO_LEN + 1]; int unused; char *cur = buf; int used = 0; if (!context) { printf("File context is NULL.\n"); return; } unused = sizeof(buf) - 1; printf("File name: "); if(context->file_name && (context->file_name_size > 0)) { FileCharEncoding encoding = file_api->get_character_encoding(context->file_name, context->file_name_size); if((encoding == SNORT_CHAR_ENCODING_UTF_16LE) || (encoding == SNORT_CHAR_ENCODING_UTF_16BE)) { setlocale(LC_ALL, ""); int i; for (i = 2; i < context->file_name_size; i+=2) { uint16_t value = 0; if(encoding == SNORT_CHAR_ENCODING_UTF_16LE) value = context->file_name[i] + (context->file_name[i + 1] << 8); else if(encoding == SNORT_CHAR_ENCODING_UTF_16BE) value = (context->file_name[i] << 8) + context->file_name[i + 1]; if(0 == value) break; printf("%lc", value); } } else if(encoding == SNORT_CHAR_ENCODING_ASCII) { if (unused > (int) context->file_name_size) { uint32_t i; uint32_t size = context->file_name_size; for (i = 0; i < size; i++) { if (isprint((int)context->file_name[i])) cur[i] = (char)context->file_name[i]; else cur[i] = '.'; } if (!context->file_name[size-1]) size--; unused -= size; cur += size; } } else { printf("<>"); } } if (unused > 0) { used = snprintf(cur, unused, "\nFile type: %s(%d)", file_type_name(context->file_config, context->file_type_id), context->file_type_id); unused -= used; cur += used; } if (unused > 0) { used = snprintf(cur, unused, "\nFile size: %u", (unsigned int)context->file_size); unused -= used; cur += used; } if (unused > 0) { used = snprintf(cur, unused, "\nProcessed size: %u\n", (unsigned int)context->processed_bytes); unused -= used; cur += used; } buf[sizeof(buf) - 1] = '\0'; printf("%s", buf); } #include "sfdebug.h" void DumpHexFile(FILE *fp, const uint8_t *data, unsigned len) { FileConfig *file_config = (FileConfig *)(snort_conf->file_config); if (file_config->show_data_depth < (int64_t)len) len = file_config->show_data_depth; fprintf(fp,"Show length: %d \n", len); DumpHex(fp, data, len); } #endif void print_file_stats(int exiting) { int i; uint64_t processed_total[2]; uint64_t processed_data_total[2]; uint64_t verdicts_total; if(!file_stats.files_total) { LogMessage("Files processed: none\n"); return; } LogMessage("File type stats:\n"); LogMessage(" Type Download (Bytes) Upload (Bytes)\n"); processed_total[0] = 0; processed_total[1] = 0; processed_data_total[0] = 0; processed_data_total[1] = 0; for (i = 0; i < FILE_ID_MAX; i++) { char* type_name = file_type_name(snort_conf->file_config, i); if (type_name && (file_stats.files_processed[i][0] || file_stats.files_processed[i][1] )) { LogMessage("%12s(%3d) "FMTu64("-10")" "FMTu64("-12")" "FMTu64("-10")" "FMTu64("-10")" \n", type_name, i, file_stats.files_processed[i][0], file_stats.data_processed[i][0], file_stats.files_processed[i][1], file_stats.data_processed[i][1]); processed_total[0]+= file_stats.files_processed[i][0]; processed_total[1]+= file_stats.files_processed[i][1]; processed_data_total[0]+= file_stats.data_processed[i][0]; processed_data_total[1]+= file_stats.data_processed[i][1]; } } LogMessage(" Total "FMTu64("-10")" "FMTu64("-12")" "FMTu64("-10")" "FMTu64("-10")" \n", processed_total[0], processed_data_total[0], processed_total[1], processed_data_total[1]); LogMessage("\nFile signature stats:\n"); LogMessage(" Type Download Upload \n"); processed_total[0] = 0; processed_total[1] = 0; for (i = 0; i < FILE_ID_MAX; i++) { char* type_name = file_type_name(snort_conf->file_config, i); if (type_name && (file_stats.signatures_processed[i][0] || file_stats.signatures_processed[i][1] )) { LogMessage("%12s(%3d) "FMTu64("-10")" "FMTu64("-10")" \n", type_name, i, file_stats.signatures_processed[i][0], file_stats.signatures_processed[i][1]); processed_total[0]+= file_stats.signatures_processed[i][0]; processed_total[1]+= file_stats.signatures_processed[i][1]; } } LogMessage(" Total "FMTu64("-10")" "FMTu64("-10")" \n", processed_total[0], processed_total[1]); LogMessage("\nFile type verdicts:\n"); verdicts_total = 0; for (i = 0; i < FILE_VERDICT_MAX; i++) { verdicts_total+=file_stats.verdicts_type[i]; switch (i) { case FILE_VERDICT_UNKNOWN: LogMessage(" %12s: "FMTu64("-10")" \n", "UNKNOWN", file_stats.verdicts_type[i]); break; case FILE_VERDICT_LOG: LogMessage(" %12s: "FMTu64("-10")" \n", "LOG", file_stats.verdicts_type[i]); break; case FILE_VERDICT_STOP: LogMessage(" %12s: "FMTu64("-10")" \n", "STOP", file_stats.verdicts_type[i]); break; case FILE_VERDICT_BLOCK: LogMessage(" %12s: "FMTu64("-10")" \n", "BLOCK", file_stats.verdicts_type[i]); break; case FILE_VERDICT_REJECT: LogMessage(" %12s: "FMTu64("-10")" \n", "REJECT", file_stats.verdicts_type[i]); break; case FILE_VERDICT_PENDING: LogMessage(" %12s: "FMTu64("-10")" \n", "PENDING", file_stats.verdicts_type[i]); break; case FILE_VERDICT_STOP_CAPTURE: LogMessage(" %12s: "FMTu64("-10")" \n", "STOP CAPTURE", file_stats.verdicts_type[i]); break; default: break; } } LogMessage(" %12s: "FMTu64("-10")" \n", "Total",verdicts_total); LogMessage("\nFile signature verdicts:\n"); verdicts_total = 0; for (i = 0; i < FILE_VERDICT_MAX; i++) { verdicts_total+=file_stats.verdicts_signature[i]; switch (i) { case FILE_VERDICT_UNKNOWN: LogMessage(" %12s: "FMTu64("-10")" \n", "UNKNOWN", file_stats.verdicts_signature[i]); break; case FILE_VERDICT_LOG: LogMessage(" %12s: "FMTu64("-10")" \n", "LOG", file_stats.verdicts_signature[i]); break; case FILE_VERDICT_STOP: LogMessage(" %12s: "FMTu64("-10")" \n", "STOP", file_stats.verdicts_signature[i]); break; case FILE_VERDICT_BLOCK: LogMessage(" %12s: "FMTu64("-10")" \n", "BLOCK", file_stats.verdicts_signature[i]); break; case FILE_VERDICT_REJECT: LogMessage(" %12s: "FMTu64("-10")" \n", "REJECT", file_stats.verdicts_signature[i]); break; case FILE_VERDICT_PENDING: LogMessage(" %12s: "FMTu64("-10")" \n", "PENDING", file_stats.verdicts_signature[i]); break; case FILE_VERDICT_STOP_CAPTURE: LogMessage(" %12s: "FMTu64("-10")" \n", "STOP CAPTURE", file_stats.verdicts_signature[i]); break; default: break; } } LogMessage(" %12s: "FMTu64("-10")" \n", "Total",verdicts_total); #ifdef TARGET_BASED if (IsAdaptiveConfigured()) { LogMessage("\nFiles processed by protocol IDs:\n"); for (i = 0; i < MAX_PROTOCOL_ORDINAL; i++) { if (file_stats.files_by_proto[i]) { LogMessage(" %12d: "FMTu64("-10")" \n", i ,file_stats.files_by_proto[i]); } } LogMessage("\nFile signatures processed by protocol IDs:\n"); for (i = 0; i < MAX_PROTOCOL_ORDINAL; i++) { if (file_stats.signatures_by_proto[i]) { LogMessage(" %12d: "FMTu64("-10")" \n", i ,file_stats.signatures_by_proto[i]); } } } #endif LogMessage("\n"); LogMessage("Total files processed: "FMTu64("-10")" \n", file_stats.files_total); LogMessage("Total files data processed: "FMTu64("-10")"bytes \n", file_stats.file_data_total); LogMessage("Total files buffered: "FMTu64("-10")" \n", file_capture_stats.files_buffered_total); LogMessage("Total files released: "FMTu64("-10")" \n", file_capture_stats.files_released_total); LogMessage("Total files freed: "FMTu64("-10")" \n", file_capture_stats.files_freed_total); LogMessage("Total files captured: "FMTu64("-10")" \n", file_capture_stats.files_captured_total); LogMessage("Total files within one packet: "FMTu64("-10")" \n", file_capture_stats.file_within_packet); LogMessage("Total buffers allocated: "FMTu64("-10")" \n", file_capture_stats.file_buffers_allocated_total); LogMessage("Total buffers freed: "FMTu64("-10")" \n", file_capture_stats.file_buffers_freed_total); LogMessage("Total buffers released: "FMTu64("-10")" \n", file_capture_stats.file_buffers_released_total); LogMessage("Maximum file buffers used: "FMTu64("-10")" \n", file_capture_stats.file_buffers_used_max); LogMessage("Total buffers free errors: "FMTu64("-10")" \n", file_capture_stats.file_buffers_free_errors); LogMessage("Total buffers release errors: "FMTu64("-10")" \n", file_capture_stats.file_buffers_release_errors); LogMessage("Total memcap failures: "FMTu64("-10")" \n", file_capture_stats.file_memcap_failures_total); LogMessage("Total memcap failures at reserve: "FMTu64("-10")" \n", file_capture_stats.file_memcap_failures_reserve); LogMessage("Total reserve failures: "FMTu64("-10")" \n", file_capture_stats.file_reserve_failures); LogMessage("Total file capture size min: "FMTu64("-10")" \n", file_capture_stats.file_size_min); LogMessage("Total file capture size max: "FMTu64("-10")" \n", file_capture_stats.file_size_max); LogMessage("Total capture max before reserve: "FMTu64("-10")" \n", file_capture_stats.file_size_exceeded); LogMessage("Total file signature max: "FMTu64("-10")" \n", file_stats.files_sig_depth); file_capture_mem_usage(); } snort-2.9.20/src/file-process/file_capture.h0000644000175000017500000001157614241076543017106 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** Author(s): Hui Cao ** ** NOTES ** 9.25.2012 - Initial Source Code. Hcao */ #ifndef _FILE_CAPTURE_H_ #define _FILE_CAPTURE_H_ #include "file_api.h" #include "file_lib.h" #include "memory_stats.h" struct _FileCaptureInfo { uint32_t length; bool reserved; struct _FileCaptureInfo *last; /* last block of file data */ struct _FileCaptureInfo *next; /* next block of file data */ uint64_t file_size; /*file_size*/ }; typedef struct _File_Capture_Stats { uint64_t files_buffered_total; uint64_t files_released_total; uint64_t files_freed_total; uint64_t files_captured_total; uint64_t file_memcap_failures_total; uint64_t file_memcap_failures_reserve; /*This happens during reserve*/ uint64_t file_reserve_failures; /*This happens during reserve*/ uint64_t file_size_exceeded; uint64_t file_size_min; /*This happens during reserve*/ uint64_t file_size_max; /*This happens during reserve*/ uint64_t file_within_packet; uint64_t file_buffers_used_max; /* maximum buffers used simultaneously*/ uint64_t file_buffers_allocated_total; uint64_t file_buffers_freed_total; uint64_t file_buffers_released_total; uint64_t file_buffers_free_errors; uint64_t file_buffers_release_errors; } File_Capture_Stats; extern File_Capture_Stats file_capture_stats; /* * Initialize the file memory pool * * Arguments: * int64_t max_file_mem: memcap in bytes * int64_t block_size: file block size * * Returns: NONE */ void file_capture_init_mempool(int64_t max_file_mem, int64_t block_size); /* * Capture file data to local buffer * This is the main function call to enable file capture * * Arguments: * FileContext* context: current file context * uint8_t *file_data: current file data * int data_size: current file data size * FilePosition position: position of file data * * Returns: * 0: successful * 1: fail to capture the file or file capture is disabled */ int file_capture_process( FileContext* context, uint8_t* file_data, int data_size, FilePosition position); /* * Stop file capture, memory resource will be released if not reserved * * Returns: NONE */ void file_capture_stop( FileContext* context); /* * Preserve the file in memory until it is released * * Arguments: * void *ssnptr: session pointer * FileCaptureInfo **file_mem: the pointer to store the memory block * that stores file and its metadata. * It will set NULL if no memory or fail to store * * Returns: * FileCaptureState: * FILE_CAPTURE_SUCCESS = 0, * FILE_CAPTURE_MIN, * FILE_CAPTURE_MAX, * FILE_CAPTURE_MEMCAP, * FILE_CAPTURE_FAIL */ FileCaptureState file_capture_reserve(void *ssnptr, FileCaptureInfo **file_mem); /* * Get the file that is reserved in memory * * Arguments: * FileCaptureInfo *file_mem: the memory block working on * uint8_t **buff: address to store buffer address * int *size: address to store size of file * * Returns: * the next memory block * NULL: end of file or fail to get file */ void* file_capture_read(FileCaptureInfo *file_mem, uint8_t **buff, int *size); /* * Get the file size captured in the file buffer * * Arguments: * FileCaptureInfo *file_mem: the first memory block of file buffer * * Returns: * the size of file * 0: no memory or fail to get file */ size_t file_capture_size(FileCaptureInfo *file_mem); /* * Release the file that is reserved in memory, this function might be * called in a different thread. * * Arguments: * FileCaptureInfo *data: the memory block that stores file and its metadata */ void file_capture_release(FileCaptureInfo *data); /*Log file capture mempool usage*/ void file_capture_mem_usage(void); /* * Exit file capture, release all file capture memory etc, * this must be called when snort exits */ void file_caputure_close(void); int FilePrintMemStats(FILE *fd, char* buffer, PreprocMemInfo *meminfo); #endif snort-2.9.20/src/rate_filter.c0000644000175000017500000001662114241077153014336 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* @file rate_filter.c * @brief rate filter interface for Snort * @ingroup rate_filter * @author Dilbagh Chahal */ /* @ingroup rate_filter * @{ */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "mstring.h" #include "util.h" #include "parser.h" #include "rate_filter.h" #include "sfrf.h" #include "snort.h" #include "sfthd.h" #ifndef WIN32 #include #include #include #endif #include #include "stream_api.h" #include "generators.h" static int _logConfigNode(tSFRFConfigNode*); static int _printThresholdContext(RateFilterConfig*); RateFilterConfig* RateFilter_ConfigNew(void) { RateFilterConfig *rf_config = (RateFilterConfig *)SnortAlloc(sizeof(*rf_config)); rf_config->memcap = 1024 * 1024; return rf_config; } /* Free threshold context * @param pContext pointer to global threshold context. */ void RateFilter_ConfigFree(RateFilterConfig *config) { int i; if (config == NULL) return; for (i = 0; i < SFRF_MAX_GENID; i++) { if (config->genHash[i] != NULL) sfghash_delete(config->genHash[i]); } free(config); } void RateFilter_Cleanup(void) { SFRF_Delete(); } /* * Create and Add a Thresholding Event Object */ int RateFilter_Create(SnortConfig *sc, RateFilterConfig *rf_config, tSFRFConfigNode *thdx) { int error; if (rf_config == NULL) return -1; #ifdef RF_DBG printf( "THRESHOLD: gid=%u, sid=%u, tracking=%d, count=%d, seconds=%d \n", thdx->gid, thdx->sid, thdx->tracking, thdx->count, thdx->seconds); } #endif /* Add the object to the table - */ error = SFRF_ConfigAdd(sc, rf_config, thdx); // enable internal events as required if ( !error && EventIsInternal(thdx->gid) ) { EnableInternalEvent(rf_config, thdx->sid); if ( thdx->sid == INTERNAL_EVENT_SESSION_ADD ) EnableInternalEvent(rf_config, INTERNAL_EVENT_SESSION_DEL); } return error; } /* Test an event against the threshold object table to determine if the new_action should be applied. returns 1 - rate threshold reached 0 - rate threshold not reached */ int RateFilter_Test( OptTreeNode* otn, Packet* p) { unsigned gid = otn->event_data.sig_generator; unsigned sid = otn->event_data.sig_id; sfaddr_t* sip; sfaddr_t* dip; sfaddr_t cleared; if ( IPH_IS_VALID(p) ) { sip = GET_SRC_IP(p); dip = GET_DST_IP(p); } else { IP_CLEAR(cleared); sip = IP_ARG(cleared); dip = IP_ARG(cleared); } if ((snort_conf == NULL) || (snort_conf->rate_filter_config == NULL)) { /* this should not happen, see the create fcn */ return -1; } if ( EventIsInternal(gid) ) { // at present stream5 connection events are the only internal // events and these require: src -> client, dst -> server. if ( p->packet_flags & PKT_FROM_SERVER ) { return SFRF_TestThreshold( snort_conf->rate_filter_config, gid, sid, dip, sip, p->pkth->ts.tv_sec, SFRF_COUNT_INCREMENT); } } return SFRF_TestThreshold( snort_conf->rate_filter_config, gid, sid, sip, dip, p->pkth->ts.tv_sec, SFRF_COUNT_INCREMENT); } /* empty out active entries */ void RateFilter_ResetActive (void) { SFRF_Flush(); } /* * Startup Display Of Thresholding */ void RateFilter_PrintConfig(RateFilterConfig *config) { if (config == NULL) return; LogMessage("\n"); LogMessage("+-----------------------[rate-filter-config]" "-----------------------------------\n"); LogMessage("| memory-cap : %d bytes\n", config->memcap); LogMessage("+-----------------------[rate-filter-rules]-" "-----------------------------------\n"); if (config->count == 0) { LogMessage("| none\n"); } else { _printThresholdContext(config); } LogMessage("--------------------------------------------" "-----------------------------------\n"); } static int _logConfigNode( tSFRFConfigNode* p) { char* trackBy = "?"; char buf[STD_BUF+1]; *buf = '\0'; // SnortSnprintfAppend(buf, STD_BUF, "| thd-id=%d", p->thd_id ); if ( p->gid == 0 ) { SnortSnprintfAppend(buf, STD_BUF, "| gen-id=global"); } else { SnortSnprintfAppend(buf, STD_BUF, "| gen-id=%-6d", p->gid ); } if ( p->sid == 0 ) { SnortSnprintfAppend(buf, STD_BUF, " sig-id=global" ); } else { SnortSnprintfAppend(buf, STD_BUF, " sig-id=%-10d", p->sid ); } SnortSnprintfAppend(buf, STD_BUF, " policyId=%-10d", p->policyId ); switch ( p->tracking ) { case SFRF_TRACK_BY_SRC : trackBy = "src"; break; case SFRF_TRACK_BY_DST : trackBy = "dst"; break; case SFRF_TRACK_BY_RULE: trackBy = "rule"; break; default: break; } SnortSnprintfAppend(buf, STD_BUF, " tracking=%s", trackBy); SnortSnprintfAppend(buf, STD_BUF, " count=%-3d", p->count); SnortSnprintfAppend(buf, STD_BUF, " seconds=%-3d", p->seconds); LogMessage("%s\n", buf); return 1; } static int _printThresholdContext(RateFilterConfig *config) { int gid; int lcnt=0; if (config == NULL) return 0; for ( gid=0; gid < SFRF_MAX_GENID; gid++ ) { SFGHASH_NODE* item_hash_node; SFGHASH* sfrf_hash = config->genHash [ gid ]; if ( !sfrf_hash ) { continue; } for ( item_hash_node = sfghash_findfirst( sfrf_hash ); item_hash_node != 0; item_hash_node = sfghash_findnext( sfrf_hash ) ) { tSFRFSidNode* sfrf_item; tSFRFConfigNode* sfrf_node; /* Check for any Permanent sid objects for this gid */ sfrf_item = (tSFRFSidNode*)item_hash_node->data; for ( sfrf_node = (tSFRFConfigNode*)sflist_first(sfrf_item->configNodeList); sfrf_node != 0; sfrf_node = (tSFRFConfigNode*)sflist_next(sfrf_item->configNodeList) ) { if ( _logConfigNode( sfrf_node) != 0 ) lcnt++; } } } if ( ! lcnt ) LogMessage("| none\n"); return 0; } snort-2.9.20/src/cdefs.h0000644000175000017500000000676014241074704013131 0ustar apoapo/* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. * * $Id$ * @(#)cdefs.h 8.8 (Berkeley) 1/9/95 */ #ifndef _CDEFS_H_ # define _CDEFS_H_ # if defined(__cplusplus) # define __BEGIN_DECLS extern "C" { # define __END_DECLS }; # else /* defined(__cplusplus) */ # define __BEGIN_DECLS # define __END_DECLS # endif /* defined(__cplusplus) */ /* * The __CONCAT macro is used to concatenate parts of symbol names, e.g. * with "#define OLD(foo) __CONCAT(old,foo)", OLD(foo) produces oldfoo. * The __CONCAT macro is a bit tricky -- make sure you don't put spaces * in between its arguments. __CONCAT can also concatenate double-quoted * strings produced by the __STRING macro, but this only works with ANSI C. */ # if defined(__STDC__) || defined(__cplusplus) # define __P(protos) protos /* full-blown ANSI C */ # ifndef __CONCAT # define __CONCAT(x,y) x ## y # endif /* ! __CONCAT */ # define __STRING(x) #x # ifndef __const # define __const const /* define reserved names to standard */ # endif /* ! __const */ # define __signed signed # define __volatile volatile # if defined(__cplusplus) # define __inline inline /* convert to C++ keyword */ # else /* defined(__cplusplus) */ # ifndef __GNUC__ # define __inline /* delete GCC keyword */ # endif /* ! __GNUC__ */ # endif /* defined(__cplusplus) */ # else /* defined(__STDC__) || defined(__cplusplus) */ # define __P(protos) () /* traditional C preprocessor */ # ifndef __CONCAT # define __CONCAT(x,y) x/**/y # endif /* ! __CONCAT */ # define __STRING(x) "x" # ifndef __GNUC__ # define __const /* delete pseudo-ANSI C keywords */ # define __inline # define __signed # define __volatile /* * In non-ANSI C environments, new programs will want ANSI-only C keywords * deleted from the program and old programs will want them left alone. * When using a compiler other than gcc, programs using the ANSI C keywords * const, inline etc. as normal identifiers should define -DNO_ANSI_KEYWORDS. * When using "gcc -traditional", we assume that this is the intent; if * __GNUC__ is defined but __STDC__ is not, we leave the new keywords alone. */ # ifndef NO_ANSI_KEYWORDS # define const /* delete ANSI C keywords */ # define inline # define signed # define volatile # endif /* ! NO_ANSI_KEYWORDS */ # endif /* ! __GNUC__ */ # endif /* defined(__STDC__) || defined(__cplusplus) */ /* * GCC1 and some versions of GCC2 declare dead (non-returning) and * pure (no side effects) functions using "volatile" and "const"; * unfortunately, these then cause warnings under "-ansi -pedantic". * GCC2 uses a new, peculiar __attribute__((attrs)) style. All of * these work for GNU C++ (modulo a slight glitch in the C++ grammar * in the distribution version of 2.5.5). */ # if !defined(__GNUC__) || __GNUC__ < 2 || \ (__GNUC__ == 2 && __GNUC_MINOR__ < 5) # define __attribute__(x) /* delete __attribute__ if non-gcc or gcc1 */ # if defined(__GNUC__) && !defined(__STRICT_ANSI__) # define __dead __volatile # define __pure __const # endif /* defined(__GNUC__) && !defined(__STRICT_ANSI__) */ # endif /* !defined(__GNUC__) || __GNUC__ < 2 || \ */ /* Delete pseudo-keywords wherever they are not available or needed. */ # ifndef __dead # define __dead # define __pure # endif /* ! __dead */ #endif /* ! _CDEFS_H_ */ snort-2.9.20/src/plugin_enum.h0000644000175000017500000000421714241076732014365 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* Purpose: Enumerate all the various detection plugins entries for otn->ds_list[] No more grepping to make your own plugin! */ #ifndef _PLUGIN_ENUM_H #define _PLUGIN_ENUM_H enum { PLUGIN_CLIENTSERVER, PLUGIN_DSIZE_CHECK, PLUGIN_FRAG_BITS, PLUGIN_FRAG_OFFSET, PLUGIN_ICMP_CODE, PLUGIN_ICMP_ID_CHECK, PLUGIN_ICMP_SEQ_CHECK, PLUGIN_ICMP_TYPE, PLUGIN_IPOPTION_CHECK, PLUGIN_IP_ID_CHECK, PLUGIN_IP_PROTO_CHECK, PLUGIN_IP_SAME_CHECK, PLUGIN_IP_TOS_CHECK, PLUGIN_PATTERN_MATCH, /* AND match */ PLUGIN_PATTERN_MATCH_OR, PLUGIN_PATTERN_MATCH_URI, PLUGIN_RESPONSE, PLUGIN_RPC_CHECK, PLUGIN_SESSION, PLUGIN_TCP_ACK_CHECK, PLUGIN_TCP_FLAG_CHECK, PLUGIN_TCP_SEQ_CHECK, PLUGIN_TCP_WIN_CHECK, PLUGIN_TTL_CHECK, PLUGIN_BYTE_TEST, PLUGIN_PCRE, PLUGIN_URILEN_CHECK, PLUGIN_DYNAMIC, PLUGIN_FLOWBIT, PLUGIN_FILE_DATA, PLUGIN_BASE64_DECODE, #if defined(FEAT_OPEN_APPID) PLUGIN_APPID, #endif /* defined(FEAT_OPEN_APPID) */ PLUGIN_MAX /* sentinel value */ }; #endif /* _PLUGIN_ENUM_H */ snort-2.9.20/src/control/0000755000175000017500000000000014242725720013345 5ustar apoaposnort-2.9.20/src/control/sfcontrol.h0000644000175000017500000000576414241074710015536 0ustar apoapo/* ** ** sfcontrol.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Author(s): Ron Dempster ** ** NOTES ** 5.16.11 - Initial Source Code. Dempster ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef __SF_CONTROL_H__ #define __SF_CONTROL_H__ #define CONTROL_FILE "SNORT.sock" #define CS_TYPE_HUP_DAQ 0x0001 #define CS_TYPE_RELOAD 0x0002 #define CS_TYPE_IS_PROCESSING 0x0003 #define CS_TYPE_DUMP_PACKETS 0x0004 #define CS_TYPE_MEM_USAGE 0x0005 #define CS_TYPE_ACTION_STATS 0x0006 #define CS_TYPE_STREAM_STATS 0x0007 #define CS_TYPE_SMTP_STATS 0x0008 #define CS_TYPE_SSL_STATS 0x0009 #define CS_TYPE_SIP_STATS 0x000A #define CS_TYPE_FLOWIP_START 0x000D #define CS_TYPE_FLOWIP_STOP 0x000E #define CS_TYPE_FLOWIP_SHOW 0x000F #define CS_TYPE_MEM_STATS_CFG 0x0011 #define CS_TYPE_MEM_STATS_SHOW 0x0012 #define CS_TYPE_MAX 0x1FFF #define CS_HEADER_VERSION 0x0001 #define CS_HEADER_SUCCESS 0x0000 #define CS_HEADER_ERROR 0x0001 #define CS_HEADER_DATA 0x0009 #ifdef REG_TEST #define CS_CONFIG_SWAP 0x000A #endif /* For SFR Display */ #define MEM_STATS_BUF_SIZE 7680 //CS_STATS_BUF_SIZE * (no. of preprocessors) * 2 = 1280 * 3 * 2 #define CS_STATS_BUF_SIZE 1280 #pragma pack(1) typedef struct _CS_MESSAGE_DATA_HEADER { /* All values must be in network byte order */ int32_t code; uint16_t length; /* Data length. Does not include this header */ } CSMessageDataHeader; #pragma pack() typedef struct _CS_MESSAGE_HEADER { /* All values must be in network byte order */ uint16_t version; uint16_t type; uint32_t length; /* Does not include the header */ } CSMessageHeader; struct _THREAD_ELEMENT; typedef int (*ControlDataSendFunc)(struct _THREAD_ELEMENT *te, const uint8_t *data, uint16_t length); typedef int (*OOBPreControlFunc)(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char *statusBuf, int statusBuf_len); typedef int (*IBControlFunc)(uint16_t type, void *new_context, void **old_context); typedef void (*OOBPostControlFunc)(uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); #endif snort-2.9.20/src/control/Makefile.in0000644000175000017500000004045414242725545015426 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/control ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libsfcontrol_a_AR = $(AR) $(ARFLAGS) libsfcontrol_a_LIBADD = am_libsfcontrol_a_OBJECTS = sfcontrol.$(OBJEXT) libsfcontrol_a_OBJECTS = $(am_libsfcontrol_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsfcontrol_a_SOURCES) DIST_SOURCES = $(libsfcontrol_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies noinst_LIBRARIES = libsfcontrol.a libsfcontrol_a_SOURCES = sfcontrol.c sfcontrol.h sfcontrol_funcs.h all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/control/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/control/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libsfcontrol.a: $(libsfcontrol_a_OBJECTS) $(libsfcontrol_a_DEPENDENCIES) $(EXTRA_libsfcontrol_a_DEPENDENCIES) $(AM_V_at)-rm -f libsfcontrol.a $(AM_V_AR)$(libsfcontrol_a_AR) libsfcontrol.a $(libsfcontrol_a_OBJECTS) $(libsfcontrol_a_LIBADD) $(AM_V_at)$(RANLIB) libsfcontrol.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/control/sfcontrol_funcs.h0000644000175000017500000000301414241074711016717 0ustar apoapo/* ** ** sfcontrol.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Author(s): Ron Dempster ** ** NOTES ** 5.16.11 - Initial Source Code. Dempster ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef __SF_CONTROL_FUNCS_H__ #define __SF_CONTROL_FUNCS_H__ #include "sfcontrol.h" void ControlSocketConfigureDirectory(const char *optarg); void ControlSocketInit(void); void ControlSocketCleanUp(void); int ControlSocketRegisterHandler(uint16_t type, OOBPreControlFunc oobpre, IBControlFunc ib, OOBPostControlFunc oobpost); #ifdef CONTROL_SOCKET void ControlSocketDoWork(int idle); #else #define ControlSocketDoWork(idle) do {} while(0) #endif #endif snort-2.9.20/src/control/sfcontrol.c0000644000175000017500000006722014241074706015531 0ustar apoapo/* ** ** sfcontrol.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Author(s): Ron Dempster ** ** NOTES ** 5.16.11 - Initial Source Code. Dempster ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #define _GNU_SOURCE #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include "snort.h" #include "sfcontrol_funcs.h" #include "sfcontrol.h" #ifdef REG_TEST #include "reg_test.h" #endif #ifdef CONTROL_SOCKET #ifndef WIN32 #include #include #include #include #include #include #include #include #include #include #endif static char config_unix_socket_fn[PATH_MAX]; static int config_unix_socket; static volatile int stop_processing = 0; #pragma pack(1) typedef struct _CS_RESPONSE_MESSAGE { CSMessageHeader hdr; CSMessageDataHeader msg_hdr; char msg[1024]; } CSResponseMessage; #pragma pack() #pragma pack(1) typedef struct _CS_RESPONSE_MESSAGE_HEADER { CSMessageHeader hdr; CSMessageDataHeader msg_hdr; } CSResponseMessageHeader; #pragma pack() typedef struct _CS_MESSAGE { CSMessageHeader hdr; uint8_t *data; } CSMessage; typedef struct _CS_MESSAGE_HANDLER { struct _CS_MESSAGE_HANDLER *next; uint32_t type; OOBPreControlFunc oobpre; IBControlFunc ibcontrol; OOBPostControlFunc oobpost; pthread_mutex_t mutex; void *new_context; void *old_context; volatile int handled; volatile int ib_rval; } CSMessageHandler; #define CS_MAX_WORK 3 #define CS_MAX_IDLE_WORK 10 static unsigned s_work_to_do = 0; static unsigned s_work_done = 0; static pthread_mutex_t work_mutex = PTHREAD_MUTEX_INITIALIZER; static CSMessageHandler *work_queue; static CSMessageHandler *work_queue_tail; static CSMessageHandler *msg_handlers[CS_TYPE_MAX]; static pthread_mutex_t msg_handler_mutex = PTHREAD_MUTEX_INITIALIZER; typedef struct _THREAD_ELEMENT { struct _THREAD_ELEMENT *next; int socket_fd; pthread_t tid; volatile int stop_processing; } ThreadElement; static ThreadElement *thread_list; static pthread_mutex_t thread_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_t thread_id; static pthread_t *p_thread_id; void ControlSocketConfigureDirectory(const char *optarg) { const char *sep; ssize_t len; if (!optarg || config_unix_socket_fn[0]) return; len = strlen(optarg); if (len && optarg[len - 1] == '/') sep = ""; else sep = "/"; snprintf(config_unix_socket_fn, sizeof(config_unix_socket_fn), "%s%s%s", optarg, sep, CONTROL_FILE); } int ControlSocketRegisterHandler(uint16_t type, OOBPreControlFunc oobpre, IBControlFunc ib, OOBPostControlFunc oobpost) { if (type >= CS_TYPE_MAX) return -1; pthread_mutex_lock(&msg_handler_mutex); if (msg_handlers[type]) { pthread_mutex_unlock(&msg_handler_mutex); return -1; } if ((msg_handlers[type] = calloc(1, sizeof(*msg_handlers[type]))) == NULL) { pthread_mutex_unlock(&msg_handler_mutex); return -1; } pthread_mutex_init(&msg_handlers[type]->mutex, NULL); msg_handlers[type]->type = type; msg_handlers[type]->oobpre = oobpre; msg_handlers[type]->ibcontrol = ib; msg_handlers[type]->oobpost = oobpost; pthread_mutex_unlock(&msg_handler_mutex); return 0; } static void SendResponse(ThreadElement *t, const CSResponseMessage *resp, uint32_t len) { ssize_t numsent; unsigned total_len = sizeof(resp->hdr) + len; unsigned total = 0; do { numsent = write(t->socket_fd, ((unsigned char *)resp) + total, total_len - total); if (!numsent) return; else if (numsent > 0) total += numsent; else if (errno != EINTR && errno != EAGAIN) return; } while (total < total_len && !t->stop_processing); } static void SendErrorResponse(ThreadElement *t, const char * const msg) { CSResponseMessage response; uint32_t len; response.hdr.version = htons(CS_HEADER_VERSION); response.hdr.type = htons(CS_HEADER_ERROR); response.msg_hdr.code = -1; len = snprintf(response.msg, sizeof(response.msg), "%s", msg); response.msg_hdr.length = htons(len); len += sizeof(response.msg_hdr); response.hdr.length = htonl(len); SendResponse(t, &response, len); } static int ReadHeader(ThreadElement *t, CSMessageHeader *hdr) { ssize_t numread; unsigned total = 0; do { numread = read(t->socket_fd, ((unsigned char *)hdr) + total, sizeof(*hdr) - total); if (!numread) return 0; else if (numread > 0) total += numread; else if (errno != EINTR && errno != EAGAIN) return -1; } while (total < sizeof(*hdr) && !t->stop_processing); if (total < sizeof(*hdr)) return 0; hdr->length = ntohl(hdr->length); hdr->type = ntohs(hdr->type); hdr->version = ntohs(hdr->version); return 1; } static int ReadData(ThreadElement *t, uint8_t *buffer, uint32_t length) { ssize_t numread; unsigned total = 0; do { numread = read(t->socket_fd, buffer + total, length - total); if (!numread) return 0; else if (numread > 0) total += numread; else if (errno != EINTR && errno != EAGAIN) return -1; } while (total < length && !t->stop_processing); if (total < length) return 0; return 1; } static int SendResponseSeparateData(ThreadElement *t, const CSResponseMessageHeader *resp, const uint8_t *data, uint16_t len) { ssize_t numsent; unsigned total_len; unsigned total; total_len = sizeof(*resp); total = 0; do { numsent = write(t->socket_fd, ((unsigned char *)resp) + total, total_len - total); if (!numsent) return -1; else if (numsent > 0) total += numsent; else if (errno != EINTR && errno != EAGAIN) return -1; } while (total < total_len && !t->stop_processing); if (!t->stop_processing && len) { total_len = (unsigned)len; total = 0; do { numsent = write(t->socket_fd, data + total, total_len - total); if (!numsent) return -1; else if (numsent > 0) total += numsent; else if (errno != EINTR && errno != EAGAIN) return -1; } while (total < total_len && !t->stop_processing); } return 0; } static int ControlDataSend(ThreadElement *t, const uint8_t *data, uint16_t length) { CSResponseMessageHeader response; uint32_t len; response.hdr.version = htons(CS_HEADER_VERSION); response.hdr.type = htons(CS_HEADER_DATA); response.msg_hdr.code = 0; len = (uint32_t)length; response.msg_hdr.length = htons(length); len += sizeof(response.msg_hdr); response.hdr.length = htonl(len); return SendResponseSeparateData(t, &response, data, length); } static void *ControlSocketProcessThread(void *arg) { CSResponseMessage response; ThreadElement *t = (ThreadElement *)arg; int fd; pthread_t tid = pthread_self(); CSMessageHeader hdr; uint32_t len; uint8_t *data = NULL; ThreadElement **it; int rval; if (t == NULL) { ErrorMessage("Control Socket: Invalid process thread parameter\n"); return NULL; } if ((fd = t->socket_fd) == -1) { ErrorMessage("Control Socket: Invalid process thread socket\n"); return NULL; } response.hdr.version = htons(CS_HEADER_VERSION); while (!t->stop_processing) { if ((rval = ReadHeader(t, &hdr)) == 0) goto done; else if (rval < 0) { DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Failed to read %d\n", t->socket_fd, rval);); goto done; } if (hdr.version != CS_HEADER_VERSION) { static const char * const bad_version = "Bad message header version"; SendErrorResponse(t, bad_version); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Invalid header version %u\n", t->socket_fd, hdr.version);); goto done; } if (hdr.length > 4096) { static const char * const bad_data = "Bad message data"; SendErrorResponse(t, bad_data); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Meassge too long - %u\n", t->socket_fd, hdr.length);); goto done; } if (hdr.length) { if ((data = malloc(hdr.length)) == NULL) { DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Failed to allocate %u bytes\n", t->socket_fd, hdr.length);); goto done; } DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Reading %u bytes\n", t->socket_fd, hdr.length);); if ((rval = ReadData(t, data, hdr.length)) == 0) { DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Socket closed before data read\n", t->socket_fd);); goto done; } else if (rval < 0) { DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Failed to read %d\n", t->socket_fd, rval);); goto done; } } if (hdr.type >= CS_TYPE_MAX) { static const char invalid_type[] = "Invalid type. Must be 0-8190 inclusive."; SendErrorResponse(t, invalid_type); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Invalid message type - %u\n", t->socket_fd, hdr.type);); } else { CSMessageHandler *handler; pthread_mutex_lock(&msg_handler_mutex); handler = msg_handlers[hdr.type]; pthread_mutex_unlock(&msg_handler_mutex); if (handler) { static const char failed[] = "Failed to process the command."; DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Processing message type - %u\n", t->socket_fd, hdr.type);); pthread_mutex_lock(&handler->mutex); if (t->stop_processing) { pthread_mutex_unlock(&handler->mutex); response.hdr.type = htons(CS_HEADER_SUCCESS); response.hdr.length = 0; SendResponse(t, &response, 0); goto next; } handler->handled = 0; handler->new_context = NULL; handler->old_context = NULL; handler->next = NULL; response.msg[0] = '\0'; if (handler->oobpre && (rval = handler->oobpre(hdr.type, data, hdr.length, &handler->new_context, response.msg, sizeof(response.msg)))) { response.hdr.type = htons(CS_HEADER_ERROR); response.msg_hdr.code = -1; if (!response.msg[0]) { len = snprintf(response.msg, sizeof(response.msg), "%s", failed); } else { len = strlen(response.msg); } response.msg_hdr.length = htons(len); len += sizeof(response.msg_hdr); response.hdr.length = htonl(len); SendResponse(t, &response, len); pthread_mutex_unlock(&handler->mutex); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: oobpre failed %d\n", t->socket_fd, rval);); goto next; } if (response.msg[0]) { response.hdr.type = htons(CS_HEADER_DATA); response.msg_hdr.code = 0; len = strlen(response.msg); response.msg_hdr.length = htons(len); len += sizeof(response.msg_hdr); response.hdr.length = htonl(len); SendResponse(t, &response, len); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Sent %u response bytes\n", t->socket_fd, len);); } if (handler->ibcontrol) { if (t->stop_processing) { if (handler->oobpost) handler->oobpost(hdr.type, handler->new_context, t, ControlDataSend); pthread_mutex_unlock(&handler->mutex); response.hdr.type = htons(CS_HEADER_SUCCESS); response.hdr.length = 0; SendResponse(t, &response, 0); goto next; } pthread_mutex_lock(&work_mutex); if (work_queue_tail) work_queue_tail->next = handler; work_queue_tail = handler; if (!work_queue) work_queue = handler; s_work_to_do++; pthread_mutex_unlock(&work_mutex); #ifdef REG_TEST if (REG_TEST_FLAG_SESSION_FORCE_RELOAD & getRegTestFlags()) { char responseTempStr[] = "--== Reloading Snort: new config ready ==--"; memset(response.msg, 0, sizeof(response.msg)); len = snprintf(response.msg, sizeof(response.msg), "%s", responseTempStr); response.hdr.type = htons(CS_HEADER_DATA); response.msg_hdr.code = 0; len = strlen(response.msg); response.msg_hdr.length = htons(len); len += sizeof(response.msg_hdr); response.hdr.length = htonl(len); SendResponse(t, &response, len); } #endif DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Waiting for ibcontrol\n", t->socket_fd);); while (!handler->handled && !t->stop_processing) usleep(100000); if (handler->handled) { if (handler->ib_rval) { if (handler->oobpost) handler->oobpost(hdr.type, handler->new_context, t, ControlDataSend); pthread_mutex_unlock(&handler->mutex); SendErrorResponse(t, failed); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: ibcontrol failed %d\n", t->socket_fd, handler->ib_rval);); goto next; } #ifdef REG_TEST else if (REG_TEST_FLAG_SESSION_FORCE_RELOAD & getRegTestFlags()) { char responseStr[] = "--== Reloading Snort: config swap completed ==--"; memset(response.msg, 0, sizeof(response.msg)); len = snprintf(response.msg, sizeof(response.msg), "%s", responseStr); response.hdr.type = htons(CS_CONFIG_SWAP); response.msg_hdr.code = 0; len = strlen(response.msg); response.msg_hdr.length = htons(len); len += sizeof(response.msg_hdr); response.hdr.length = htonl(len); SendResponse(t, &response, len); } #endif } else { // The only way to get here is if stop_processing is set. // This happens during CleanExit which means that the swap will never happen. // If the entry is no longer on the work_queue, the swap already happened and we can continue normally. CSMessageHandler *iHandler; CSMessageHandler *prevHandler = NULL; pthread_mutex_lock(&work_mutex); for (iHandler = work_queue; iHandler && iHandler != handler; iHandler = iHandler->next) prevHandler = iHandler; if (iHandler) { if (handler == work_queue_tail) work_queue_tail = prevHandler; if (prevHandler) prevHandler->next = handler->next; else work_queue = handler->next; } pthread_mutex_unlock(&work_mutex); if (iHandler) { if (handler->oobpost) handler->oobpost(hdr.type, handler->new_context, t, ControlDataSend); pthread_mutex_unlock(&handler->mutex); SendErrorResponse(t, failed); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: ibcontrol failed %d\n", t->socket_fd, handler->ib_rval);); goto next; } } } if (handler->oobpost) { handler->oobpost(hdr.type, handler->old_context, t, ControlDataSend); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: oobpost finished\n", t->socket_fd);); } pthread_mutex_unlock(&handler->mutex); response.hdr.type = htons(CS_HEADER_SUCCESS); response.hdr.length = 0; SendResponse(t, &response, 0); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Sent success\n", t->socket_fd);); } else { static const char no_handler[] = "No handler for the command."; SendErrorResponse(t, no_handler); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: No handler for message type - %u\n", t->socket_fd, hdr.type);); } } next:; if (data) { free(data); data = NULL; } } done:; if (data) free(data); close(fd); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket %d: Closed socket\n", t->socket_fd);); pthread_mutex_lock(&thread_mutex); for (it=&thread_list; *it; it=&(*it)->next) { if (t == *it) { *it = t->next; free(t); break; } } pthread_mutex_unlock(&thread_mutex); pthread_detach(tid); return NULL; } static void *ControlSocketThread(void *arg) { ThreadElement *t; fd_set rfds; int rval; struct timeval to; int socket; struct sockaddr_un sunaddr; socklen_t addrlen = sizeof(sunaddr); if (config_unix_socket < 0) { ErrorMessage("Control Socket: Invalid socket in thread - %d\n", config_unix_socket); goto bail; } nice(2); while (!stop_processing) { to.tv_sec = 2; to.tv_usec = 0; FD_ZERO(&rfds); FD_SET(config_unix_socket, &rfds); rval = select(config_unix_socket + 1, &rfds, NULL, NULL, &to); if (rval > 0) { memset(&sunaddr, 0, sizeof(sunaddr)); if ((socket = accept(config_unix_socket, (struct sockaddr *)&sunaddr, &addrlen)) == -1) { if (errno != EINTR) { ErrorMessage("Control Socket: Accept failed: %s\n", strerror(errno)); goto bail; } } else { DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket: Creating a processing thread for %d\n", socket);); if ((t = calloc(1, sizeof(*t))) == NULL) { close(socket); ErrorMessage("Control Socket: Failed to allocate a thread struct"); goto bail; } t->socket_fd = socket; pthread_mutex_lock(&thread_mutex); t->next = thread_list; thread_list = t; if ((rval = pthread_create(&t->tid, NULL, &ControlSocketProcessThread, (void *)t)) != 0) { thread_list = thread_list->next; close(t->socket_fd); free(t); pthread_mutex_unlock(&thread_mutex); ErrorMessage("Control Socket: Unable to create a processing thread: %s", strerror(rval)); goto bail; } pthread_mutex_unlock(&thread_mutex); } } else if (rval < 0) { if (errno != EINTR) { ErrorMessage("Control Socket: Select failed: %s\n", strerror(errno)); goto bail; } } } bail:; close(config_unix_socket); DEBUG_WRAP( DebugMessage(DEBUG_CONTROL, "Control Socket: Thread exiting\n");); return NULL; } static void SetupUnixSocket(const char * const name, int * const psock, const int listen_backlog) { struct sockaddr_un sunaddr; int sock = -1; int yes = 1; int rval; memset(&sunaddr, 0, sizeof(sunaddr)); rval = snprintf(sunaddr.sun_path, sizeof(sunaddr.sun_path), "%s", name); if (rval < 0 || (size_t)rval >= sizeof(sunaddr.sun_path)) FatalError("Control Socket: Socket name '%s' is too long\n", name); sunaddr.sun_family = AF_UNIX; unlink(name); /* remove existing file */ /* open the socket */ if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { FatalError("Control Socket: Error opening socket %s: %s\n", name, strerror(errno)); } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) { WarningMessage("Control Socket: setsockopt failed for %s: %s", name, strerror(errno)); } if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) == -1) { rval = errno; close(sock); FatalError("Control Socket: Unable to bind to %s: %s\n", name, strerror(rval)); } if (chmod(name, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP)) { rval = errno; close(sock); FatalError("Control Socket: Error changing the mode for socket %s: %s", name, strerror(rval)); } /* listen on the socket */ if (listen(sock, listen_backlog) == -1) { rval = errno; close(sock); FatalError("Control Socket: Unable to listen on UNIX socket %s: %s\n", name, strerror(rval)); } *psock = sock; } void ControlSocketInit(void) { int rval; sigset_t mask; if (!config_unix_socket_fn[0]) return; SetupUnixSocket(config_unix_socket_fn, &config_unix_socket, 10); sigemptyset(&mask); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGQUIT); sigaddset(&mask, SIGPIPE); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGNAL_SNORT_RELOAD); sigaddset(&mask, SIGNAL_SNORT_DUMP_STATS); sigaddset(&mask, SIGUSR1); sigaddset(&mask, SIGUSR2); sigaddset(&mask, SIGNAL_SNORT_ROTATE_STATS); sigaddset(&mask, SIGNAL_SNORT_CHILD_READY); #ifdef TARGET_BASED sigaddset(&mask, SIGNAL_SNORT_READ_ATTR_TBL); sigaddset(&mask, SIGVTALRM); #endif pthread_sigmask(SIG_SETMASK, &mask, NULL); if((rval=pthread_create(&thread_id, NULL, &ControlSocketThread, NULL)) != 0) { sigemptyset(&mask); pthread_sigmask(SIG_SETMASK, &mask, NULL); FatalError("Control Socket: Unable to create thread: %s\n", strerror(rval)); } p_thread_id = &thread_id; sigemptyset(&mask); pthread_sigmask(SIG_SETMASK, &mask, NULL); } void ControlSocketCleanUp(void) { ThreadElement *t,*it; int rval; int done = 0; int i; int counter = 0; if (p_thread_id != NULL) { stop_processing = 1; if ((rval = pthread_join(*p_thread_id, NULL)) != 0) WarningMessage("Thread termination returned an error: %s\n", strerror(rval)); p_thread_id = NULL; } if (config_unix_socket_fn[0]) { unlink(config_unix_socket_fn); config_unix_socket_fn[0] = 0; } pthread_mutex_lock(&thread_mutex); for (t = thread_list; t; t = t->next) t->stop_processing = 1; pthread_mutex_unlock(&thread_mutex); do { pthread_mutex_lock(&thread_mutex); done = thread_list ? 0:1; #ifdef HAVE_PTHREAD_TRYJOIN_NP if(!done) { t = thread_list; while (t) { if (pthread_tryjoin_np(t->tid,NULL) == 0) { it = t; t = t->next; if(thread_list == it) thread_list = t; free(it); } else t=t->next; } } done = thread_list ? 0:1; #endif pthread_mutex_unlock(&thread_mutex); if (!done) { ++counter; usleep(100000); } if(counter >= 100) break; } while (!done); if(!done) ErrorMessage("Control Socket threads did not clearly exit within stipulated time\n"); pthread_mutex_lock(&work_mutex); if (work_queue) WarningMessage("%s\n", "Work queue is not emtpy during termination"); pthread_mutex_unlock(&work_mutex); for (i = 0; i < CS_TYPE_MAX; i++) { if (msg_handlers[i]) free(msg_handlers[i]); } } void ControlSocketDoWork(int idle) { unsigned max_work; CSMessageHandler *handler; if ( s_work_done == s_work_to_do ) return; max_work = idle ? CS_MAX_IDLE_WORK : CS_MAX_WORK; pthread_mutex_lock(&work_mutex); for (; work_queue && max_work; max_work--) { handler = work_queue; work_queue = handler->next; if (!work_queue) work_queue_tail = NULL; handler->ib_rval = handler->ibcontrol(handler->type, handler->new_context, &handler->old_context); handler->handled = 1; s_work_done++; } pthread_mutex_unlock(&work_mutex); } #else void ControlSocketConfigureDirectory(const char *optarg) { FatalError("%s\n", "Control socket is not available."); } int ControlSocketRegisterHandler(uint16_t type, OOBPreControlFunc oobpre, IBControlFunc ib, OOBPostControlFunc oobpost) { return 0; } void ControlSocketInit(void) { } void ControlSocketCleanUp(void) { } #endif snort-2.9.20/src/control/Makefile.am0000444000175000017500000000025014230012554015363 0ustar apoapoAUTOMAKE_OPTIONS=foreign no-dependencies noinst_LIBRARIES = libsfcontrol.a libsfcontrol_a_SOURCES = sfcontrol.c sfcontrol.h sfcontrol_funcs.h INCLUDES = @INCLUDES@ snort-2.9.20/src/event_queue.h0000644000175000017500000000423514241076521014364 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __EVENT_QUEUE_H__ #define __EVENT_QUEUE_H__ #include "decode.h" #include "sfutil/sfeventq.h" #include "treenodes.h" #define SNORT_EVENTQ_PRIORITY 1 #define SNORT_EVENTQ_CONTENT_LEN 2 struct _OptTreeNode; typedef struct _EventQueueConfig { int max_events; int log_events; int order; int process_all_events; } EventQueueConfig; typedef struct s_SNORT_EVENTQ_USER { char rule_alert; void *pkt; } SNORT_EVENTQ_USER; typedef struct _EventNode { unsigned int gid; unsigned int sid; unsigned int rev; unsigned int classification; unsigned int priority; const char *msg; OptTreeNode *rule_info; } EventNode; EventQueueConfig * EventQueueConfigNew(void); void EventQueueConfigFree(EventQueueConfig *); void SnortEventqNew(EventQueueConfig *, SF_EVENTQ*[]); void SnortEventqFree(SF_EVENTQ *[]); void SnortEventqReset(void); void SnortEventqResetCounts(void); int SnortEventqLog(SF_EVENTQ *[], Packet *); int SnortEventqAdd(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, const char *, void *); void SnortEventqPush(void); void SnortEventqPop(void); #endif snort-2.9.20/src/detection_util.c0000644000175000017500000001236114241075102015036 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "detection_util.h" #include "sfutil/sf_textlog.h" #include "rules.h" #include "snort.h" uint8_t base64_decode_buf[DECODE_BLEN]; uint32_t base64_decode_size; uint8_t mime_present; const uint8_t *doe_ptr; uint8_t doe_buf_flags; uint16_t detect_flags; uint32_t http_mask; HttpBuffer http_buffer[HTTP_BUFFER_MAX]; DataPointer DetectBuffer; DataPointer file_data_ptr; DataBuffer DecodeBuffer; void *global_ssl_callback = NULL; const char* http_buffer_name[HTTP_BUFFER_MAX] = { "error/unset", "http_uri", "http_header", "http_client_body", "http_method", "http_cookie", "http_stat_code", "http_stat_msg", "http_raw_uri", "http_raw_header", "http_raw_cookie", }; static const char* rule_type[RULE_TYPE__MAX] = { "none", "alert", "drop", "log", "pass", "reject", "sdrop" }; #define LOG_CHARS 16 static TextLog* tlog = NULL; static unsigned nEvents = 0; static void LogBuffer (const char* s, const uint8_t* p, unsigned n) { char hex[(3*LOG_CHARS)+1]; char txt[LOG_CHARS+1]; unsigned odx = 0, idx = 0, at = 0; if ( !p ) return; if ( n > snort_conf->event_trace_max ) n = snort_conf->event_trace_max; for ( idx = 0; idx < n; idx++) { uint8_t byte = p[idx]; sprintf(hex + 3*odx, "%2.02X ", byte); txt[odx++] = isprint(byte) ? byte : '.'; if ( odx == LOG_CHARS ) { txt[odx] = hex[3*odx] = '\0'; TextLog_Print(tlog, "%s[%2u] %s %s\n", s, at, hex, txt); at = idx + 1; odx = 0; } } if ( odx ) { txt[odx] = hex[3*odx] = '\0'; TextLog_Print(tlog, "%s[%2u] %-48.48s %s\n", s, at, hex, txt); } } void EventTrace_Log (const Packet* p, OptTreeNode* otn, int action) { int i; const char* acts = (action < RULE_TYPE__MAX) ? rule_type[action] : "ERROR"; if ( !tlog ) return; TextLog_Print(tlog, "\nEvt=%u, Gid=%u, Sid=%u, Rev=%u, Act=%s\n", event_id, otn->sigInfo.generator, otn->sigInfo.id, otn->sigInfo.rev, acts ); TextLog_Print(tlog, "Pkt=%lu, Sec=%u.%6u, Len=%u, Cap=%u\n", pc.total_from_daq, p->pkth->ts.tv_sec, p->pkth->ts.tv_usec, p->pkth->pktlen, p->pkth->caplen ); TextLog_Print(tlog, "Pkt Bits: Flags=0x%X, PP=0x%llx, Proto=0x%X" ", Err=0x%X\n", p->packet_flags, p->preprocessor_bits, (unsigned)p->proto_bits, (unsigned)p->error_flags ); TextLog_Print(tlog, "Pkt Cnts: Dsz=%u, Alt=%u, Uri=0x%X\n", (unsigned)p->dsize, (unsigned)p->alt_dsize, http_mask ); TextLog_Print(tlog, "Detect: DoeFlags=0x%X, DetectFlags=0x%X, DetBuf=%u, B64=%u\n", doe_buf_flags, detect_flags, DetectBuffer.len, base64_decode_size ); LogBuffer("Decode", DecodeBuffer.data, DecodeBuffer.len); LogBuffer("Detect", DetectBuffer.data, DetectBuffer.len); LogBuffer("FileData", file_data_ptr.data, file_data_ptr.len); LogBuffer("Base64", base64_decode_buf, base64_decode_size); if(mime_present) LogBuffer("Mime", file_data_ptr.data, file_data_ptr.len); for ( i = 0; i < HTTP_BUFFER_MAX; i++ ) { const HttpBuffer* hb = GetHttpBuffer(i); if ( !hb ) continue; TextLog_Print(tlog, "%s[%u] = 0x%X\n", http_buffer_name[i], hb->length, hb->encode_type); LogBuffer(http_buffer_name[i], hb->buf, hb->length); } nEvents++; } void EventTrace_Init (void) { if ( snort_conf->event_trace_max > 0 ) { time_t now = time(NULL); const char* ts = ctime(&now); char buf[STD_BUF]; const char* dir = snort_conf->log_dir ? snort_conf->log_dir : "."; snprintf(buf, sizeof(buf), "%s/%s", dir, snort_conf->event_trace_file); tlog = TextLog_Init (buf, 128, 8*1024*1024); TextLog_Print(tlog, "\nTrace started at %s", ts); TextLog_Print(tlog, "Trace max_data is %u bytes\n", snort_conf->event_trace_max); } } void EventTrace_Term (void) { if ( tlog ) { time_t now = time(NULL); const char* ts = ctime(&now); TextLog_Print(tlog, "\nTraced %u events\n", nEvents); TextLog_Print(tlog, "Trace stopped at %s", ts); TextLog_Term(tlog); } } snort-2.9.20/src/reg_test.h0000644000175000017500000000415714241077157013664 0ustar apoapo/* ** ** reg_test.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef __REG_TEST_H__ #define __REG_TEST_H__ #ifdef REG_TEST #define REG_TEST_VARIABLE "SNORT_REG_TEST" #define REG_TEST_EMAIL_VARIABLE "SNORT_EMAIL_REG_TEST" #include #define REG_TEST_FLAG_SESSION_RELOAD (1 << 0) #define REG_TEST_FLAG_INCREMENT_IP_ADDRESS (1 << 1) #define REG_TEST_FLAG_RELOAD (1 << 2) #define REG_TEST_FLAG_PERFMON_RELOAD (1 << 3) #define REG_TEST_FLAG_APPDATA_ADJUSTER_RELOAD (1 << 4) #define REG_TEST_FLAG_FILE_CACHE (1 << 5) #define REG_TEST_FLAG_PORTSCAN_RELOAD (1 << 6) #define REG_TEST_FLAG_SESSION_FORCE_RELOAD (1 << 7) #define REG_TEST_FLAG_REPUTATION (1 << 8) #define REG_TEST_FLAG_STREAM_DECODE (1 << 9) #define REG_TEST_EMAIL_FLAG_MIME_MEMPOOL_ADJUST (1 << 0) #define REG_TEST_EMAIL_FLAG_LOG_MEMPOOL_ADJUST (1 << 1) #define REG_TEST_EMAIL_FLAG_DECODE_DEPTH_ADJUST (1 << 2) #define REG_TEST_EMAIL_FLAG_FD_MEMPOOL_ADJUST (1 << 3) #define REG_TEST_EMAIL_FLAG_GZIP_MEMPOOL_ADJUST (1 << 4) #define REG_TEST_EMAIL_FLAG_HTTP_MEMPOOL_ADJUST (1 << 5) extern uint32_t rt_ip_increment; uint64_t getRegTestFlags(void); uint64_t getRegTestFlagsForEmail(void); void regTestCheckIPIncrement(void); #endif #endif snort-2.9.20/src/sfutil/0000755000175000017500000000000014242725710013172 5ustar apoaposnort-2.9.20/src/sfutil/sfhashfcn.h0000644000175000017500000000516614241077267015324 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* sfhashfcn.h */ #ifndef SFHASHFCN_INCLUDE #define SFHASHFCN_INCLUDE #include #include #include #include #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) #define mix(a,b,c) \ { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } typedef struct _SFHASHFCN { unsigned seed; unsigned scale; unsigned hardener; unsigned (*hash_fcn)(struct _SFHASHFCN * p, unsigned char *d, int n ); int (*keycmp_fcn)( const void *s1, const void *s2, size_t n); } SFHASHFCN; SFHASHFCN * sfhashfcn_new( int nrows ); void sfhashfcn_free( SFHASHFCN * p ); void sfhashfcn_static( SFHASHFCN * p ); unsigned sfhashfcn_hash( SFHASHFCN * p, unsigned char *d, int n ); int sfhashfcn_set_keyops( SFHASHFCN * p, unsigned (*hash_fcn)( SFHASHFCN * p, unsigned char *d, int n), int (*keycmp_fcn)( const void *s1, const void *s2, size_t n)); #endif snort-2.9.20/src/sfutil/sfthd.h0000644000175000017500000001565414241077337014472 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*! \file sfthd.h */ #ifndef _SF_THRESHOLDING_ #define _SF_THRESHOLDING_ #include "ipv6_port.h" #include "parser/IpAddrSet.h" #include "sflsq.h" #include "sfghash.h" #include "sfxhash.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" /*! Max GEN_ID value - Set this to the Max Used by Snort, this is used for the dimensions of the gen_id lookup array. Rows in each hash table, by gen_id. */ #define THD_MAX_GENID 8129 #define THD_GEN_ID_1_ROWS 4096 #define THD_GEN_ID_ROWS 512 #define THD_NO_THRESHOLD -1 #define THD_TOO_MANY_THDOBJ -15 /*! Type of Thresholding */ enum { THD_TYPE_LIMIT, THD_TYPE_THRESHOLD, THD_TYPE_BOTH, THD_TYPE_SUPPRESS, THD_TYPE_DETECT }; /* Very high priority for suppression objects users priorities are limited to this minus one */ #define THD_PRIORITY_SUPPRESS 1000000 /*! Tracking by src, or by dst */ enum { THD_TRK_NONE, // suppress only THD_TRK_SRC, THD_TRK_DST }; /*! THD_IP_NODE Dynamic hashed node data - added and deleted during runtime These are added during run-time, and recycled if we max out memory usage. */ typedef struct { unsigned count; unsigned prev; time_t tstart; time_t tlast; } THD_IP_NODE; /*! THD_IP_NODE_KEY HASH Key to lookup and store Ip nodes. The structure now tracks thresholds for different policies. This destroys locality of reference and may cause poor performance. */ typedef struct{ int thd_id; struct in6_addr ip; tSfPolicyId policyId; } THD_IP_NODE_KEY ; typedef struct{ unsigned gen_id; unsigned sig_id; struct in6_addr ip; tSfPolicyId policyId; } THD_IP_GNODE_KEY ; /*! THD_NODE A Thresholding Object These are created at program startup, and remain static. The THD_IP_NODE elements are dynamic. */ typedef struct { int thd_id; /* Id of this node */ unsigned gen_id; /* Keep these around if needed */ unsigned sig_id; int tracking; /* by_src, by_dst */ int type; int priority; int count; unsigned seconds; uint64_t filtered; IpAddrSet* ip_address; } THD_NODE; /*! THD_ITEM The THD_ITEM acts as a container of gen_id+sig_id based threshold objects, this allows multiple threshold objects to be applied to a single gen_id+sig_id pair. The sflist is created using the priority field, so highest priority objects are first in the list. When processing the highest priority object will trigger first. These are static data elements, built at program startup. */ typedef struct { tSfPolicyId policyId; unsigned gen_id; /* just so we know what gen_id we are */ unsigned sig_id; /* * List of THD_NODE's - walk this list and hash the * 'THD_NODE->sfthd_id + src_ip or dst_ip' to get the correct THD_IP_NODE. */ SF_LIST* sfthd_node_list; } THD_ITEM; // Temporary structure useful when parsing the Snort rules typedef struct _THDX_STRUCT { unsigned gen_id; unsigned sig_id; int type; int tracking; int priority; int count; unsigned int seconds; IpAddrSet* ip_address; } THDX_STRUCT; typedef struct { tSfPolicyId policyId; unsigned sig_id; } tThdItemKey; /*! THD_STRUCT The main thresholding data structure. Local and global threshold thd_id's are all unqiue, so we use just one ip_nodes lookup table */ typedef struct _THD_STRUCT { SFXHASH *ip_nodes; /* Global hash of active IP's key=THD_IP_NODE_KEY, data=THD_IP_NODE */ SFXHASH *ip_gnodes; /* Global hash of active IP's key=THD_IP_GNODE_KEY, data=THD_IP_GNODE */ } THD_STRUCT; typedef struct _ThresholdObjects { int count; /* Total number of thresholding/suppression objects */ SFGHASH *sfthd_array[THD_MAX_GENID]; /* Local Hash of THD_ITEM nodes, lookup by key=sig_id */ /* Double array of THD_NODE pointers. First index is policyId and therefore variable length. * Second index is genId and of fixed length, A simpler definition could be * THD_NODE * sfthd_garray[0][THD_MAX_GENID] and we could intentionally overflow first index, * but we may have to deal with compiler warnings if the array is indexed by value known at * compile time. */ //THD_NODE * (*sfthd_garray)[THD_MAX_GENID]; THD_NODE* **sfthd_garray; tSfPolicyId numPoliciesAllocated; } ThresholdObjects; /* * Prototypes */ // lbytes = local threshold memcap // gbytes = global threshold memcap (0 to disable global) THD_STRUCT * sfthd_new(unsigned lbytes, unsigned gbytes); SFXHASH * sfthd_local_new(unsigned bytes); SFXHASH * sfthd_global_new(unsigned bytes); void sfthd_free(THD_STRUCT *); ThresholdObjects * sfthd_objs_new(void); void sfthd_objs_free(ThresholdObjects *); int sfthd_test_rule(SFXHASH *rule_hash, THD_NODE *sfthd_node, sfaddr_t* sip, sfaddr_t* dip, long curtime, detection_option_eval_data_t *eval_data); void * sfthd_create_rule_threshold( int id, int tracking, int type, int count, unsigned int seconds ); struct _SnortConfig; int sfthd_create_threshold( struct _SnortConfig *, ThresholdObjects *, unsigned gen_id, unsigned sig_id, int tracking, int type, int priority, int count, int seconds, IpAddrSet* ip_address ); // 1: don't log due to event_filter // 0: log // -1: don't log due to suppress int sfthd_test_threshold( ThresholdObjects *, THD_STRUCT *, unsigned gen_id, unsigned sig_id, sfaddr_t* sip, sfaddr_t* dip, long curtime ) ; SFXHASH * sfthd_new_hash(unsigned, size_t, size_t); int sfthd_test_local( SFXHASH *local_hash, THD_NODE * sfthd_node, sfaddr_t* sip, sfaddr_t* dip, time_t curtime, detection_option_eval_data_t *eval_data); #ifdef THD_DEBUG int sfthd_show_objects( THD_STRUCT * thd ); #endif #endif snort-2.9.20/src/sfutil/sfsnprintfappend.h0000644000175000017500000000247214241077335016736 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * * sfsnprintfappend.h * * snprintf that appends to destination buffer * * * Author: Steven Sturges * */ #ifndef _SFSNPRINTF_APPEND_H_ #define _SFSNPRINTF_APPEND_H_ int sfsnprintfappend(char *dest, int dsize, const char *format, ...); #endif /* _SFSNPRINTF_APPEND_H_ */ snort-2.9.20/src/sfutil/util_unfold.h0000644000175000017500000000242714241077361015675 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Writen by Bhagyashree Bantwal ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _UTIL_UNFOLD_H_ #define _UTIL_UNFOLD_H_ #include "sf_types.h" int sf_unfold_header(const uint8_t*, uint32_t, uint8_t*, uint32_t, uint32_t*, int, int * ); int sf_strip_CRLF(const uint8_t*, uint32_t, uint8_t*, uint32_t, uint32_t*); int sf_strip_LWS(const uint8_t*, uint32_t, uint8_t*, uint32_t, uint32_t*); #endif snort-2.9.20/src/sfutil/sf_ipvar.h0000644000175000017500000001100414241077246015153 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Adam Keeton * sf_ipvar.h * 11/17/06 */ #ifndef SF_IPVAR_H #define SF_IPVAR_H /* Flags */ #define SFIP_NEGATED 1 #define SFIP_ANY 2 #include #include "sf_ip.h" /* Selects which mode a given variable is using to * store and lookup IP addresses */ typedef enum _modes { SFIP_LIST, SFIP_TABLE } MODES; /* Used by the "list" mode. A doubly linked list of sfcidr_t objects. */ typedef struct _ip_node { sfcidr_t *ip; struct _ip_node *next; int flags; // XXX int addr_flags; /* Flags used exlusively by Snort */ /* Keeping these variables seperate keeps * this from stepping on Snort's toes. */ /* Should merge them later */ } sfip_node_t; /* An IP variable onkect */ typedef struct _var_t { /* Selects whether or not to use the list, the table, * or any other method added later */ MODES mode; /* Linked lists. Switch to something faster later */ sfip_node_t *head; sfip_node_t *neg_head; /* The mode above will select whether to use the sfip_node_t linked list * or the IP routing table */ // sfrt rt; /* Linked list of IP variables for the variable table */ struct _var_t *next; uint16_t head_count; uint16_t neg_head_count; uint32_t id; char *name; char *value; } sfip_var_t; /* A variable table for storing and looking up variables */ /* Expand later to use a faster data structure */ typedef struct _vartable_t { sfip_var_t *head; uint32_t id; } vartable_t; /* Creates a new variable that is an alias of another variable * Does a "deep" copy so it owns it's own pointers */ sfip_var_t * sfvar_create_alias(const sfip_var_t *alias_from, const char *alias_to); /* Returns 1 if the two variables are aliases of each other, 0 otherwise */ int sfvar_is_alias(const sfip_var_t *one, const sfip_var_t *two); /* Allocates a new variable as according to "str" */ sfip_var_t *sfvar_alloc(vartable_t *table, char *str, SFIP_RET *status); /* Makes sure there are no IP address conflicts in the variable */ /* Returns SFIP_CONFLICT if so */ SFIP_RET sfvar_validate(sfip_var_t *var); /* Parses an IP list described by 'str' and saves the results in 'var'. */ SFIP_RET sfvar_parse_iplist(vartable_t *table, sfip_var_t *var, char *str, int negation); /* Allocaties and returns an IP node described by 'str' */ sfip_node_t *sfipnode_alloc(char *str, SFIP_RET *status); /* Adds a deep copy of src to dst */ /* Ordering is not necessarily preserved */ SFIP_RET sfvar_add(sfip_var_t *dst, sfip_var_t *src); /* Adds the nodes in 'src' to the variable 'dst' */ /* The mismatch of types is for ease-of-supporting Snort4 and * Snort6 simultaneously */ SFIP_RET sfvar_add_node(sfip_var_t *dst, sfip_node_t *src, int negated); /* Compares two variables. Necessary when building RTN structure */ SFIP_RET sfvar_compare(const sfip_var_t *one, const sfip_var_t *two); /* Deep copy. Returns identical, new, linked list of sfipnodes. */ sfip_var_t *sfvar_deep_copy(const sfip_var_t *src); /* Free an allocated variable */ void sfvar_free(sfip_var_t *var); /* Returns non-zero if ip is contained in 'var', 0 otherwise */ /* If either argument is NULL, 0 is returned. */ int sfvar_ip_in(sfip_var_t *var, sfaddr_t *ip); /* Prints the variable "var" to the file descriptor 'f' */ void sfvar_print(const char *prefix, sfip_var_t *var); void sfip_set_print(const char *prefix, sfip_node_t *head); void sfvar_print_to_file(FILE *f, sfip_var_t *var); void sfip_set_print_to_file(FILE *f, sfip_node_t *head); /* Returns the node's flags */ int sfvar_flags(sfip_node_t *node); #endif snort-2.9.20/src/sfutil/sfActionQueue.c0000644000175000017500000000656114241077256016125 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "stdlib.h" #include "stdio.h" #include "string.h" #include "util.h" #include "sfActionQueue.h" #include "mempool.h" #include "active.h" #include "pkt_tracer.h" tSfActionQueueId sfActionQueueInit( int queueLength ) { tSfActionQueue *queue = SnortAlloc(sizeof(tSfActionQueue)); if (queue) { if (mempool_init(&queue->mempool, queueLength, sizeof(tSfActionNode)) != 0) { FatalError("%s(%d) Could not initialize action queue memory pool.\n", __FILE__, __LINE__); } } return queue; } int sfActionQueueAdd( tSfActionQueueId actionQ, void (*callback)(void *), void *data ) { MemBucket *bucket = mempool_alloc(&actionQ->mempool); if (bucket != NULL) { tSfActionNode *node = bucket->data; node->callback = callback; node->data = data; //Using used_list in mempool for tracking allocated MemBucket return 0; } return -1; } void sfActionQueueExecAll( tSfActionQueueId actionQ ) { //drain while (mempool_numUsedBuckets(&actionQ->mempool)) { sfActionQueueExec(actionQ); } if (Active_PacketWasDropped() || Active_PacketWouldBeDropped()) { if (pkt_trace_enabled) addPktTraceData(VERDICT_REASON_SNORT, snprintf(trace_line, MAX_TRACE_LINE, "Snort: processed decoder alerts or actions queue, %s\n", getPktTraceActMsg())); else addPktTraceData(VERDICT_REASON_SNORT, 0); } } void sfActionQueueExec( tSfActionQueueId actionQ ) { MemBucket *firstUsedBucket = mempool_oldestUsedBucket(&actionQ->mempool); if (firstUsedBucket) { tSfActionNode *node = (tSfActionNode *)firstUsedBucket->data; (node->callback)(node->data); mempool_free(&actionQ->mempool, firstUsedBucket); } } /**Destroys action queue. All memory allocated by the actionQueue module is * freed. Since the queued actions are not executed, any memory freed in the action * will be lost. User should do a execAll if there is a potential memory leak * or the actions must be completed. */ void sfActionQueueDestroy( tSfActionQueueId actionQ ) { mempool_destroy(&actionQ->mempool); free(actionQ); } snort-2.9.20/src/sfutil/strvec.h0000644000175000017500000000253214241077346014657 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _STRVEC_H_ #define _STRVEC_H_ void* StringVector_New(void); void StringVector_Delete(void*); int StringVector_Add(void*, const char*); char* StringVector_Get(void*, unsigned index); int StringVector_AddVector(void* dst, void* src); const char** StringVector_GetVector(void*); #endif // _STRVEC_H_ snort-2.9.20/src/sfutil/Unified2_common.h0000644000175000017500000001607414241077347016375 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* PLEASE DO NOT EDIT THIS FILE */ #ifndef __UNIFIED2_COMMON_H__ #define __UNIFIED2_COMMON_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifndef WIN32 #ifdef LINUX #include #endif #include #endif /*! \defgroup Unified2 */ /** \addtogroup Unified2 */ /*@{*/ //SNORT DEFINES //Long time ago... #define UNIFIED2_EVENT 1 //CURRENT #define UNIFIED2_PACKET 2 #define UNIFIED2_IDS_EVENT 7 #define UNIFIED2_IDS_EVENT_IPV6 72 #define UNIFIED2_IDS_EVENT_MPLS 99 #define UNIFIED2_IDS_EVENT_IPV6_MPLS 100 #define UNIFIED2_IDS_EVENT_VLAN 104 #define UNIFIED2_IDS_EVENT_IPV6_VLAN 105 #define UNIFIED2_EXTRA_DATA 110 #if defined(FEAT_OPEN_APPID) #define UNIFIED2_IDS_EVENT_APPID 111 #define UNIFIED2_IDS_EVENT_APPID_IPV6 112 #define UNIFIED2_IDS_EVENT_APPSTAT 113 #define MAX_EVENT_APPNAME_LEN 64 #endif /* defined(FEAT_OPEN_APPID) */ /* Data structure used for serialization of Unified2 Records */ typedef struct _Serial_Unified2_Header { uint32_t type; uint32_t length; } Serial_Unified2_Header; //UNIFIED2_IDS_EVENT_VLAN = type 104 //comes from SFDC to EStreamer archive in serialized form with the extended header typedef struct _Unified2IDSEvent { uint32_t sensor_id; uint32_t event_id; uint32_t event_second; uint32_t event_microsecond; uint32_t signature_id; uint32_t generator_id; uint32_t signature_revision; uint32_t classification_id; uint32_t priority_id; uint32_t ip_source; uint32_t ip_destination; uint16_t sport_itype; uint16_t dport_icode; uint8_t protocol; uint8_t impact_flag;//overloads packet_action uint8_t impact; uint8_t blocked; uint32_t mpls_label; uint16_t vlanId; uint16_t pad2;//Policy ID #if defined(FEAT_OPEN_APPID) char app_name[MAX_EVENT_APPNAME_LEN]; #endif /* defined(FEAT_OPEN_APPID) */ } Unified2IDSEvent; //UNIFIED2_IDS_EVENT_IPV6_VLAN = type 105 typedef struct _Unified2IDSEventIPv6 { uint32_t sensor_id; uint32_t event_id; uint32_t event_second; uint32_t event_microsecond; uint32_t signature_id; uint32_t generator_id; uint32_t signature_revision; uint32_t classification_id; uint32_t priority_id; struct in6_addr ip_source; struct in6_addr ip_destination; uint16_t sport_itype; uint16_t dport_icode; uint8_t protocol; uint8_t impact_flag; uint8_t impact; uint8_t blocked; uint32_t mpls_label; uint16_t vlanId; uint16_t pad2;/*could be IPS Policy local id to support local sensor alerts*/ #if defined(FEAT_OPEN_APPID) char app_name[MAX_EVENT_APPNAME_LEN]; #endif /* defined(FEAT_OPEN_APPID) */ } Unified2IDSEventIPv6; //UNIFIED2_PACKET = type 2 typedef struct _Serial_Unified2Packet { uint32_t sensor_id; uint32_t event_id; uint32_t event_second; uint32_t packet_second; uint32_t packet_microsecond; uint32_t linktype; uint32_t packet_length; uint8_t packet_data[4]; } Serial_Unified2Packet; typedef struct _Unified2ExtraDataHdr{ uint32_t event_type; uint32_t event_length; }Unified2ExtraDataHdr; //UNIFIED2_EXTRA_DATA - type 110 typedef struct _SerialUnified2ExtraData{ uint32_t sensor_id; uint32_t event_id; uint32_t event_second; uint32_t type; /* EventInfo */ uint32_t data_type; /*EventDataType */ uint32_t blob_length; /* Length of the data + sizeof(blob_length) + sizeof(data_type)*/ } SerialUnified2ExtraData; typedef struct _Data_Blob { uint32_t length; const uint8_t *data; } Data_Blob; //UNIFIED2_EXTRA_DATA - type 110 typedef struct _Serial_Unified2ExtraData{ uint32_t sensor_id; uint32_t event_id; uint32_t event_second; uint32_t type; Data_Blob data; } Unified2ExtraData; typedef enum _EventInfoEnum { EVENT_INFO_XFF_IPV4 = 1, EVENT_INFO_XFF_IPV6, EVENT_INFO_REVIEWED_BY, EVENT_INFO_GZIP_DATA, EVENT_INFO_SMTP_FILENAME, EVENT_INFO_SMTP_MAILFROM, EVENT_INFO_SMTP_RCPTTO, EVENT_INFO_SMTP_EMAIL_HDRS, EVENT_INFO_HTTP_URI, EVENT_INFO_HTTP_HOSTNAME, EVENT_INFO_IPV6_SRC, EVENT_INFO_IPV6_DST, EVENT_INFO_JSNORM_DATA }EventInfoEnum; typedef enum _EventDataType { EVENT_DATA_TYPE_BLOB = 1, EVENT_DATA_TYPE_MAX }EventDataType; #define EVENT_TYPE_EXTRA_DATA 4 #define MAX_XFF_WRITE_BUF_LENGTH (sizeof(Serial_Unified2_Header) + \ sizeof(Unified2ExtraDataHdr) + sizeof(SerialUnified2ExtraData) \ + sizeof(struct in6_addr)) #define Serial_Unified2IDSEvent Unified2IDSEvent #define Serial_Unified2IDSEventIPv6 Unified2IDSEventIPv6 //---------------LEGACY, type '7' //These structures are not used anymore in the product typedef struct _Serial_Unified2IDSEvent_legacy { uint32_t sensor_id; uint32_t event_id; uint32_t event_second; uint32_t event_microsecond; uint32_t signature_id; uint32_t generator_id; uint32_t signature_revision; uint32_t classification_id; uint32_t priority_id; uint32_t ip_source; uint32_t ip_destination; uint16_t sport_itype; uint16_t dport_icode; uint8_t protocol; uint8_t impact_flag;//sets packet_action uint8_t impact; uint8_t blocked; } Serial_Unified2IDSEvent_legacy; //----------LEGACY, type '72' typedef struct _Serial_Unified2IDSEventIPv6_legacy { uint32_t sensor_id; uint32_t event_id; uint32_t event_second; uint32_t event_microsecond; uint32_t signature_id; uint32_t generator_id; uint32_t signature_revision; uint32_t classification_id; uint32_t priority_id; struct in6_addr ip_source; struct in6_addr ip_destination; uint16_t sport_itype; uint16_t dport_icode; uint8_t protocol; uint8_t impact_flag; uint8_t impact; uint8_t blocked; } Serial_Unified2IDSEventIPv6_legacy; #if defined(FEAT_OPEN_APPID) //UNIFIED2_IDS_EVENT_IPV6_VLAN = type 200 typedef struct _Serial_Unified2AppStat { uint32_t event_second; uint32_t AppCnt; } Serial_Unified2AppStat; #endif /* defined(FEAT_OPEN_APPID) */ ////////////////////-->LEGACY /*@}*/ #endif /* __UNIFIED2_COMMON_H__ */ snort-2.9.20/src/sfutil/sf_email_attach_decode.c0000644000175000017500000004634514241077235017761 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Author: Bhagyashree Bantwal ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" /*SharedObjectAddStarts #include "sf_dynamic_preprocessor.h" SharedObjectAddEnds */ #include "util.h" #include "sf_email_attach_decode.h" #define UU_DECODE_CHAR(c) (((c) - 0x20) & 0x3f) int sf_qpdecode(char *src, uint32_t slen, char *dst, uint32_t dlen, uint32_t *bytes_read, uint32_t *bytes_copied ) { char ch; if(!src || !slen || !dst || !dlen || !bytes_read || !bytes_copied ) return -1; *bytes_read = 0; *bytes_copied = 0; while( (*bytes_read < slen) && (*bytes_copied < dlen)) { ch = src[*bytes_read]; *bytes_read += 1; if( ch == '=' ) { if( (*bytes_read < slen)) { if(src[*bytes_read] == '\n') { *bytes_read += 1; continue; } else if( *bytes_read < (slen - 1) ) { char ch1 = src[*bytes_read]; char ch2 = src[*bytes_read + 1]; if( ch1 == '\r' && ch2 == '\n') { *bytes_read += 2; continue; } if (isxdigit((int)ch1) && isxdigit((int)ch2)) { char hexBuf[3]; char *eptr; hexBuf[0] = ch1; hexBuf[1] = ch2; hexBuf[2] = '\0'; dst[*bytes_copied]= (char)strtoul(hexBuf, &eptr, 16); if((*eptr != '\0')) { return -1; } *bytes_read += 2; *bytes_copied +=1; continue; } dst[*bytes_copied] = ch; *bytes_copied +=1; continue; } else { *bytes_read -= 1; return 0; } } else { *bytes_read -= 1; return 0; } } else if (isprint(ch) || isblank(ch) || ch == '\r' || ch == '\n' ) { dst[*bytes_copied] = ch; *bytes_copied +=1; } } return 0; } int sf_uudecode(uint8_t *src, uint32_t slen, uint8_t *dst, uint32_t dlen, uint32_t *bytes_read, uint32_t *bytes_copied, uint8_t *begin_found, uint8_t *end_found, uint8_t *file_name, uint32_t *fname_len, bool fname_present_already) { uint8_t *sod; int sol = 1, length = 0; uint8_t *ptr, *end, *dptr, *dend, *tptr = NULL; uint8_t *filename = NULL, *space = NULL; uint32_t tlen = 0, fname_length = 0; if(!src || !slen || !dst || !dlen || !bytes_read || !bytes_copied || !begin_found || !end_found ) return -1; ptr = src; end = src + slen; dptr = dst; dend = dst + dlen; /* begin not found. Search for begin */ if( !(*begin_found) ) { if( slen < 5 ) { /* Not enough data to search */ *bytes_read = 0; *bytes_copied = 0; return 0; } else { sod = (uint8_t *)SnortStrnStr((const char *)src, 5 , "begin"); if(sod) { *begin_found = 1; /* If NULL pointers are passed, or file name is alreayd in header * skip the file name parsing */ if ( !fname_present_already && file_name && fname_len ) { /* Initialize file and length */ *fname_len = 0; /* filename for uuencoded is present in mime body as * begin mode filename\r\n , extract the file name after * begin is found */ tptr = (uint8_t *) memchr( (const char *)sod, '\r', (end - sod) ); if ( !tptr ) { /* On some implementation its a just new line char \n */ tptr = (uint8_t *) memchr( (const char *)sod, '\n', (end - sod) ); } if ( tptr ) { tlen = (tptr - sod) ; space = (uint8_t *) memchr ( sod , ' ', tlen ); if ( space ) { ++space; //skip past the space filename = (uint8_t *) memchr ( space , ' ', ( ( sod + tlen) - space ) ); if ( filename ) { ++filename; // skip past the space /*filename now points to the file name */ fname_length = ( sod + tlen ) - (uint8_t *)(filename); if ( fname_length <= MAX_UNICODE_FILE_NAME ) { memcpy ( file_name, (uint8_t *) filename, fname_length ); *fname_len = fname_length; } } } } } /*begin str found. Move to the actual data*/ ptr = (uint8_t *)SnortStrnStr((const char *)(sod), (end - sod), "\n"); if( !ptr ) { *bytes_read = slen; *bytes_copied = 0; return 0; } } else { /*Encoded data for UUencode should start with begin. Error encountered.*/ return -1; } } } while( (ptr < end) && (dptr < dend)) { if(*ptr == '\n') { length = 0; sol = 1; ptr++; continue; } if(sol) { sol = 0; length = UU_DECODE_CHAR(*ptr); if( length <= 0 ) { /* empty line with no encoded characters indicates end of output */ break; } else if( length == 5 ) { if(*ptr == 'e') { *end_found = 1; break; } } /* check if destination buffer is big enough */ if(( dend - dptr) < length) { length = dend - dptr; } length = (length * 4) / 3 ; /*check if src buffer has enough encoded data*/ if( (end - (ptr + 1)) < length) { /*not enough data to decode. We will wait for the next packet*/ break; } ptr++; while( length > 0 ) { *dptr++ = (UU_DECODE_CHAR(ptr[0]) << 2) | (UU_DECODE_CHAR(ptr[1]) >> 4); ptr++; if(--length == 0 ) break; *dptr++ = (UU_DECODE_CHAR(ptr[0]) << 4) | (UU_DECODE_CHAR(ptr[1]) >> 2); ptr++; if (--length == 0) break; *dptr++ = (UU_DECODE_CHAR(ptr[0]) << 6) | (UU_DECODE_CHAR(ptr[1])); ptr += 2; length -= 2; } } else { /* probably padding. skip over it.*/ ptr++; } } if(*end_found) *bytes_read = end - src; else *bytes_read = ptr - src; *bytes_copied = dptr - dst; return 0; } int Base64Decode(const uint8_t *start, const uint8_t *end, Email_DecodeState *ds) { uint32_t encode_avail = 0, decode_avail = 0 ; uint8_t *encode_buf, *decode_buf; uint32_t act_encode_size = 0, act_decode_size = 0; uint32_t prev_bytes = 0; uint32_t i = 0; if (!(ds->b64_state.encode_depth)) { encode_avail = MAX_BUF; decode_avail = MAX_BUF; } else if ((ds->b64_state.encode_depth) < 0) { return DECODE_EXCEEDED; } else { encode_avail = ds->b64_state.encode_depth - ds->b64_state.encode_bytes_read; decode_avail = ds->b64_state.decode_depth - ds->b64_state.decode_bytes_read; } encode_buf = ds->encodeBuf; decode_buf = ds->decodeBuf; /* 1. Stop decoding when we have reached either the decode depth or encode depth. * 2. Stop decoding when we are out of memory */ if(encode_avail ==0 || decode_avail ==0 || (!encode_buf) || (!decode_buf)) { ResetEmailDecodeState(ds); return DECODE_EXCEEDED; } /*The non decoded encoded data in the previous packet is required for successful decoding * in case of base64 data spanned across packets*/ if( ds->prev_encoded_bytes ) { if(ds->prev_encoded_bytes > encode_avail) ds->prev_encoded_bytes = encode_avail; if(ds->prev_encoded_buf) { prev_bytes = ds->prev_encoded_bytes; encode_avail = encode_avail - prev_bytes; while(ds->prev_encoded_bytes) { /* Since this data cannot be more than 3 bytes*/ encode_buf[i] = ds->prev_encoded_buf[i]; i++; ds->prev_encoded_bytes--; } } } if(sf_strip_CRLF(start, (end-start), encode_buf + prev_bytes, encode_avail, &act_encode_size) != 0) { ResetEmailDecodeState(ds); return DECODE_FAIL; } act_encode_size = act_encode_size + prev_bytes; i = (act_encode_size)%4 ; /* Encoded data should be in multiples of 4. Then we need to wait for the remainder encoded data to * successfully decode the base64 data. This happens when base64 data is spanned across packets*/ if(i) { ds->prev_encoded_bytes = i; act_encode_size = act_encode_size - i; ds->prev_encoded_buf = encode_buf + act_encode_size; } if(sf_base64decode(encode_buf, act_encode_size, decode_buf, decode_avail, &act_decode_size) != 0) { ResetEmailDecodeState(ds); return DECODE_FAIL; } else if(!act_decode_size && !encode_avail) { ResetEmailDecodeState(ds); return DECODE_FAIL; } ds->decode_present = 1; ds->decodePtr = decode_buf; ds->decoded_bytes = act_decode_size; ds->b64_state.encode_bytes_read += act_encode_size; ds->b64_state.decode_bytes_read += act_decode_size; return DECODE_SUCCESS; } int QPDecode(const uint8_t *start, const uint8_t *end, Email_DecodeState *ds) { uint32_t encode_avail = 0, decode_avail = 0 ; uint8_t *encode_buf, *decode_buf; uint32_t act_encode_size = 0, act_decode_size = 0, bytes_read = 0; uint32_t prev_bytes = 0; uint32_t i = 0; if (!(ds->qp_state.encode_depth)) { encode_avail = MAX_BUF; decode_avail = MAX_BUF; } else if ((ds->qp_state.encode_depth) < 0) { return DECODE_EXCEEDED; } else { encode_avail = ds->qp_state.encode_depth - ds->qp_state.encode_bytes_read; decode_avail = ds->qp_state.decode_depth - ds->qp_state.decode_bytes_read; } encode_buf = ds->encodeBuf; decode_buf = ds->decodeBuf; /* 1. Stop decoding when we have reached either the decode depth or encode depth. * 2. Stop decoding when we are out of memory */ if(encode_avail ==0 || decode_avail ==0 || (!encode_buf) || (!decode_buf)) { ResetEmailDecodeState(ds); return DECODE_EXCEEDED; } /*The non decoded encoded data in the previous packet is required for successful decoding * in case of base64 data spanned across packets*/ if( ds->prev_encoded_bytes ) { if(ds->prev_encoded_bytes > encode_avail) ds->prev_encoded_bytes = encode_avail; if(ds->prev_encoded_buf) { prev_bytes = ds->prev_encoded_bytes; encode_avail = encode_avail - prev_bytes; while(ds->prev_encoded_bytes) { /* Since this data cannot be more than 3 bytes*/ encode_buf[i] = ds->prev_encoded_buf[i]; i++; ds->prev_encoded_bytes--; } } } if(sf_strip_LWS(start, (end-start), encode_buf + prev_bytes, encode_avail, &act_encode_size) != 0) { ResetEmailDecodeState(ds); return DECODE_FAIL; } act_encode_size = act_encode_size + prev_bytes; if(sf_qpdecode((char *)encode_buf, act_encode_size, (char *)decode_buf, decode_avail, &bytes_read, &act_decode_size) != 0) { ResetEmailDecodeState(ds); return DECODE_FAIL; } else if(!act_decode_size && !encode_avail) { ResetEmailDecodeState(ds); return DECODE_FAIL; } if(bytes_read < act_encode_size) { ds->prev_encoded_bytes = (act_encode_size - bytes_read); ds->prev_encoded_buf = encode_buf + bytes_read; act_encode_size = bytes_read; } ds->decode_present = 1; ds->decodePtr = decode_buf; ds->decoded_bytes = act_decode_size; ds->qp_state.encode_bytes_read += act_encode_size; ds->qp_state.decode_bytes_read += act_decode_size; return DECODE_SUCCESS; } int UUDecode(const uint8_t *start, const uint8_t *end, Email_DecodeState *ds, uint8_t *filename, uint32_t *fname_length, bool fname_present) { uint32_t encode_avail = 0, decode_avail = 0 ; uint8_t *encode_buf, *decode_buf; uint32_t act_encode_size = 0, act_decode_size = 0, bytes_read = 0; uint32_t prev_bytes = 0; uint32_t i = 0; if (!(ds->uu_state.encode_depth)) { encode_avail = MAX_BUF; decode_avail = MAX_BUF; } else if ((ds->uu_state.encode_depth) < 0) { ds->uu_state.begin_found = 0; return DECODE_EXCEEDED; } else { encode_avail = ds->uu_state.encode_depth - ds->uu_state.encode_bytes_read; decode_avail = ds->uu_state.decode_depth - ds->uu_state.decode_bytes_read; } encode_buf = ds->encodeBuf; decode_buf = ds->decodeBuf; /* 1. Stop decoding when we have reached either the decode depth or encode depth. * 2. Stop decoding when we are out of memory */ if(encode_avail ==0 || decode_avail ==0 || (!encode_buf) || (!decode_buf)) { ds->uu_state.begin_found = 0; ResetEmailDecodeState(ds); return DECODE_EXCEEDED; } /*The non decoded encoded data in the previous packet is required for successful decoding * in case of base64 data spanned across packets*/ if( ds->prev_encoded_bytes ) { if(ds->prev_encoded_bytes > encode_avail) ds->prev_encoded_bytes = encode_avail; if(ds->prev_encoded_buf) { prev_bytes = ds->prev_encoded_bytes; encode_avail = encode_avail - prev_bytes; while(ds->prev_encoded_bytes) { /* Since this data cannot be more than 3 bytes*/ encode_buf[i] = ds->prev_encoded_buf[i]; i++; ds->prev_encoded_bytes--; } } } if((uint32_t)(end- start) > encode_avail) act_encode_size = encode_avail; else act_encode_size = end - start; if(encode_avail > 0) { if(SafeMemcpy((encode_buf + prev_bytes), start, act_encode_size, encode_buf, (encode_buf+ encode_avail + prev_bytes)) != SAFEMEM_SUCCESS) { ResetEmailDecodeState(ds); return DECODE_FAIL; } } act_encode_size = act_encode_size + prev_bytes; if(sf_uudecode(encode_buf, act_encode_size, decode_buf, decode_avail, &bytes_read, &act_decode_size, &(ds->uu_state.begin_found), &(ds->uu_state.end_found), filename, fname_length, fname_present ) != 0) { ResetEmailDecodeState(ds); return DECODE_FAIL; } else if(!act_decode_size && !encode_avail) { /* Have insufficient data to decode */ ResetEmailDecodeState(ds); return DECODE_FAIL; } /* Found the end. No more encoded data */ if(ds->uu_state.end_found) { ds->uu_state.end_found = 0; ds->uu_state.begin_found = 0; } if(bytes_read < act_encode_size) { ds->prev_encoded_bytes = (act_encode_size - bytes_read); ds->prev_encoded_buf = encode_buf + bytes_read; act_encode_size = bytes_read; } ds->decode_present = 1; ds->decoded_bytes = act_decode_size; ds->decodePtr = decode_buf; ds->uu_state.encode_bytes_read += act_encode_size; ds->uu_state.decode_bytes_read += act_decode_size; return DECODE_SUCCESS; } int BitEncExtract(const uint8_t *start, const uint8_t *end, Email_DecodeState *ds) { uint32_t bytes_avail = 0; uint32_t act_size = 0; ClearPrevEncodeBuf(ds); if (!(ds->bitenc_state.depth)) { bytes_avail = MAX_BUF; } else if ((ds->bitenc_state.depth) < 0) { return DECODE_EXCEEDED; } else { bytes_avail = ds->bitenc_state.depth - ds->bitenc_state.bytes_read; } /* 1. Stop decoding when we have reached either the decode depth or encode depth. * 2. Stop decoding when we are out of memory */ if(bytes_avail ==0) { ResetEmailDecodeState(ds); return DECODE_EXCEEDED; } if( (uint32_t)(end-start) < bytes_avail ) { act_size = ( end - start); } else { act_size = bytes_avail; } ds->decode_present = 1; ds->decodePtr = (uint8_t *)start; ds->decoded_bytes = act_size; ds->bitenc_state.bytes_read += act_size; return DECODE_SUCCESS; } int EmailDecode(const uint8_t *start, const uint8_t *end, Email_DecodeState *ds, uint8_t *filename, uint32_t *fname_size, bool filename_present) { int iRet = DECODE_FAIL; switch(ds->decode_type) { case DECODE_B64: iRet = Base64Decode(start, end, ds); break; case DECODE_QP: iRet = QPDecode(start, end, ds ); break; case DECODE_UU: iRet = UUDecode(start, end, ds, filename, fname_size, filename_present); break; case DECODE_BITENC: iRet = BitEncExtract(start, end, ds); break; default: break; } return iRet; } snort-2.9.20/src/sfutil/sfsnprintfappend.c0000644000175000017500000000471614241077334016733 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * * sfsnprintfappend.h * * snprintf that appends to destination buffer * * * Author: Steven Sturges * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "util.h" /**************************************************************************** * * Function: sfsnprintfappend * * Purpose: snprintf that appends to destination buffer * * Appends the snprintf format string and arguments to dest * without going beyond dsize bounds. Assumes dest has * been properly allocated, and is of dsize in length. * * Arguments: dest ==> pointer to string buffer to append to * dsize ==> size of buffer dest * format ==> snprintf format string * ... ==> arguments for printf * * Returns: number of characters added to the buffer * ****************************************************************************/ int sfsnprintfappend(char *dest, int dsize, const char *format, ...) { int currLen, appendLen; va_list ap; if (!dest || dsize == 0) return -1; currLen = SnortStrnlen(dest, dsize); if (currLen == -1) return -1; va_start(ap, format); appendLen = vsnprintf(dest+currLen, dsize-currLen, format, ap); va_end(ap); dest[dsize-1]=0;/* guarantee a null tremination */ return appendLen; } snort-2.9.20/src/sfutil/util_jsnorm.c0000644000175000017500000010042614241077350015705 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Writen by Bhagyashree Bantwal ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include"util_jsnorm.h" #define INVALID_HEX_VAL -1 #define MAX_BUF 8 #define NON_ASCII_CHAR 0xff //Return values #define RET_OK 0 #define RET_QUIT -1 #define RET_INV -2 #define IS_OCT 0x1 #define IS_DEC 0X2 #define IS_HEX 0x4 #define IS_PERCENT 0x8 #define IS_UPERCENT 0x10 #define IS_BACKSLASH 0x20 #define IS_UBACKSLASH 0x40 #define ANY '\0' typedef enum { PNORM_ACT_DQUOTES, PNORM_ACT_NOP, PNORM_ACT_PLUS, PNORM_ACT_SPACE, PNORM_ACT_SQUOTES, PNORM_ACT_WITHIN_QUOTES } ActionPNorm; // Actions for SFCC typedef enum { SFCC_ACT_COMMA, SFCC_ACT_DEC, SFCC_ACT_HEX, SFCC_ACT_INV, SFCC_ACT_NOP, SFCC_ACT_OCT, SFCC_ACT_QUIT, SFCC_ACT_SPACE } ActionSFCC; // Actions for Unescape typedef enum { UNESC_ACT_BACKSLASH, UNESC_ACT_CONV, UNESC_ACT_NOP, UNESC_ACT_PAREN, UNESC_ACT_PERCENT, UNESC_ACT_QUIT, UNESC_ACT_SAVE, UNESC_ACT_SAVE_NOP, UNESC_ACT_SPACE, UNESC_ACT_UBACKSLASH, UNESC_ACT_UPERCENT, UNESC_ACT_UNESCAPE } ActionUnsc; // Actions for Javascript norm typedef enum { ACT_NOP, ACT_QUIT, ACT_SAVE, ACT_SFCC, ACT_SPACE, ACT_UNESCAPE } ActionJSNorm; static int hex_lookup[256]; int valid_chars[256]; char decoded_out[6335]; typedef struct { uint8_t state; uint8_t event; uint8_t match; uint8_t other; uint8_t action; } JSNorm; typedef struct { char *data; uint16_t size; uint16_t len; }Dbuf; typedef struct { uint8_t fsm; uint8_t fsm_other; uint8_t prev_event; uint8_t d_quotes; uint8_t s_quotes; uint16_t num_spaces; char *overwrite; Dbuf output; }PNormState; typedef struct { uint8_t fsm; uint8_t buf[MAX_BUF]; uint8_t buflen; uint16_t cur_flags; uint16_t alert_flags; Dbuf output; } SFCCState; typedef struct { uint8_t fsm; uint8_t prev_event; uint16_t num_spaces; uint8_t *unicode_map; char *overwrite; Dbuf dest; } JSNormState; typedef struct { uint8_t fsm; uint8_t multiple_levels; uint8_t prev_event; uint16_t alert_flags; uint16_t num_spaces; int iNorm; int paren_count; uint8_t *unicode_map; char *overwrite; ActionUnsc prev_action; Dbuf output; } UnescapeState; // STATES for SFCC #define S0 0 #define S1 S0+3 #define S2 S1+1 #define S3 S2+1 #define S4 S3+1 static JSNorm sfcc_norm[] = { { S0+0, '(', S0+1, S0+1, SFCC_ACT_NOP }, { S0+1, '0', S0+2, S1+0, SFCC_ACT_NOP }, { S0+2, 'X', S3+0, S2+0, SFCC_ACT_NOP }, //decimal { S1+0, IS_DEC, S1+0, S4+0, SFCC_ACT_DEC }, //Octal { S2+0, IS_OCT, S2+0, S1+0, SFCC_ACT_OCT }, //Hex { S3+0, IS_HEX, S3+0, S4+0, SFCC_ACT_HEX }, { S4+0, ',', S0+1, S4+1, SFCC_ACT_COMMA }, { S4+1, ')', S0+1, S4+2, SFCC_ACT_QUIT }, { S4+2, ANY, S4+1, S0+1, SFCC_ACT_INV } }; #define U0 0 #define U1 U0+1 #define U2 U1+8 #define U3 U2+9 #define U4 U3+8 #define U5 U4+19 #define U6 U5+18 #define U7 U6+1 static JSNorm unescape_norm[] = { { U0+ 0, '(', U1+ 0, U1+ 0, UNESC_ACT_PAREN }, { U1+ 0, '%', U1+ 1, U2+ 0, UNESC_ACT_SAVE }, { U1+ 1, IS_HEX, U1+ 2, U1+ 3, UNESC_ACT_CONV }, { U1+ 2, IS_HEX, U0+ 0, U0+ 0, UNESC_ACT_PERCENT }, { U1+ 3, 'U', U1+ 4, U0+ 0, UNESC_ACT_SAVE_NOP }, { U1+ 4, IS_HEX, U1+ 5, U0+ 0, UNESC_ACT_CONV }, { U1+ 5, IS_HEX, U1+ 6, U0+ 0, UNESC_ACT_CONV }, { U1+ 6, IS_HEX, U1+ 7, U0+ 0, UNESC_ACT_CONV }, { U1+ 7, IS_HEX, U0+ 0, U0+ 0, UNESC_ACT_UPERCENT }, { U2+ 0, '\\', U2+ 1, U3+ 0, UNESC_ACT_SAVE }, { U2+ 1, 'X', U2+ 2, U2+ 4, UNESC_ACT_SAVE_NOP }, { U2+ 2, IS_HEX, U2+ 3, U0+ 0, UNESC_ACT_CONV }, { U2+ 3, IS_HEX, U0+ 0, U0+ 0, UNESC_ACT_BACKSLASH }, { U2+ 4, 'U', U2+ 5, U0+ 0, UNESC_ACT_CONV }, { U2+ 5, IS_HEX, U2+ 6, U0+ 0, UNESC_ACT_CONV }, { U2+ 6, IS_HEX, U2+ 7, U0+ 0, UNESC_ACT_CONV }, { U2+ 7, IS_HEX, U2+ 8, U0+ 0, UNESC_ACT_CONV }, { U2+ 8, IS_HEX, U0+ 0, U0+ 0, UNESC_ACT_UBACKSLASH }, { U3+ 0, 'U', U3+ 1, U4+ 0, UNESC_ACT_NOP }, { U3+ 1, 'N', U3+ 2, U0+ 0, UNESC_ACT_NOP }, { U3+ 2, 'E', U3+ 3, U0+ 0, UNESC_ACT_NOP }, { U3+ 3, 'S', U3+ 4, U0+ 0, UNESC_ACT_NOP }, { U3+ 4, 'C', U3+ 5, U0+ 0, UNESC_ACT_NOP }, { U3+ 5, 'A', U3+ 6, U0+ 0, UNESC_ACT_NOP }, { U3+ 6, 'P', U3+ 7, U0+ 0, UNESC_ACT_NOP }, { U3+ 7, 'E', U0+ 0, U0+ 0, UNESC_ACT_UNESCAPE }, { U4+ 0, 'S', U4+ 1, U5+ 0, UNESC_ACT_NOP }, { U4+ 1, 'T', U4+ 2, U0+ 0, UNESC_ACT_NOP }, { U4+ 2, 'R', U4+ 3, U0+ 0, UNESC_ACT_NOP }, { U4+ 3, 'I', U4+ 4, U0+ 0, UNESC_ACT_NOP }, { U4+ 4, 'N', U4+ 5, U0+ 0, UNESC_ACT_NOP }, { U4+ 5, 'G', U4+ 6, U0+ 0, UNESC_ACT_NOP }, { U4+ 6, '.', U4+ 7, U0+ 0, UNESC_ACT_NOP }, { U4+ 7, 'F', U4+ 8, U0+ 0, UNESC_ACT_NOP }, { U4+ 8, 'R', U4+ 9, U0+ 0, UNESC_ACT_NOP }, { U4+ 9, 'O', U4+10, U0+ 0, UNESC_ACT_NOP }, { U4+10, 'M', U4+11, U0+ 0, UNESC_ACT_NOP }, { U4+11, 'C', U4+12, U0+ 0, UNESC_ACT_NOP }, { U4+12, 'H', U4+13, U0+ 0, UNESC_ACT_NOP }, { U4+13, 'A', U4+14, U0+ 0, UNESC_ACT_NOP }, { U4+14, 'R', U4+15, U0+ 0, UNESC_ACT_NOP }, { U4+15, 'C', U4+16, U0+ 0, UNESC_ACT_NOP }, { U4+16, 'O', U4+17, U0+ 0, UNESC_ACT_NOP }, { U4+17, 'D', U4+18, U0+ 0, UNESC_ACT_NOP }, { U4+18, 'E', U0+ 0, U0+ 0, UNESC_ACT_UNESCAPE }, { U5+ 0, 'D', U5+ 1, U6+ 0, UNESC_ACT_NOP }, { U5+ 1, 'E', U5+ 2, U0+ 0, UNESC_ACT_NOP }, { U5+ 2, 'C', U5+ 3, U0+ 0, UNESC_ACT_NOP }, { U5+ 3, 'O', U5+ 4, U0+ 0, UNESC_ACT_NOP }, { U5+ 4, 'D', U5+ 5, U0+ 0, UNESC_ACT_NOP }, { U5+ 5, 'E', U5+ 6, U0+ 0, UNESC_ACT_NOP }, { U5+ 6, 'U', U5+ 7, U0+ 0, UNESC_ACT_NOP }, { U5+ 7, 'R', U5+ 8, U0+ 0, UNESC_ACT_NOP }, { U5+ 8, 'I', U5+ 9, U0+ 0, UNESC_ACT_UNESCAPE }, { U5+ 9, 'C', U5+10, U0+ 0, UNESC_ACT_NOP }, { U5+10, 'O', U5+11, U0+ 0, UNESC_ACT_NOP }, { U5+11, 'M', U5+12, U0+ 0, UNESC_ACT_NOP }, { U5+12, 'P', U5+13, U0+ 0, UNESC_ACT_NOP }, { U5+13, 'O', U5+14, U0+ 0, UNESC_ACT_NOP }, { U5+14, 'N', U5+15, U0+ 0, UNESC_ACT_NOP }, { U5+15, 'E', U5+16, U0+ 0, UNESC_ACT_NOP }, { U5+16, 'N', U5+17, U0+ 0, UNESC_ACT_NOP }, { U5+17, 'T', U0+ 0, U0+ 0, UNESC_ACT_UNESCAPE }, { U6+ 0, ')', U0+ 0, U7+ 0, UNESC_ACT_QUIT }, { U7+ 0, ANY, U0+ 0, U0+ 0, UNESC_ACT_NOP } }; #define P0 0 #define P1 P0+3 #define P2 P1+2 #define P3 P2+2 #define P4 P3+1 static JSNorm plus_norm[]= { { P0+ 0, ' ', P0+ 0, P0+ 1, PNORM_ACT_SPACE }, { P0+ 1, '"', P1+ 0, P0+ 2, PNORM_ACT_DQUOTES }, { P0+ 2, '\'', P2+ 0, P3+ 0, PNORM_ACT_SQUOTES }, { P1+ 0, '"', P0+ 0, P1+ 1, PNORM_ACT_DQUOTES }, { P1+ 1, ANY, P1+ 0, P1+ 0, PNORM_ACT_WITHIN_QUOTES }, { P2+ 0, '\'', P0+ 0, P2+ 1, PNORM_ACT_SQUOTES }, { P2+ 1, ANY, P2+ 0, P2+ 0, PNORM_ACT_WITHIN_QUOTES }, { P3+ 0, '+', P0+ 0, P4+ 0, PNORM_ACT_PLUS }, { P4+ 0, ANY, P0+ 0, P0+ 0, PNORM_ACT_NOP } }; #define Z0 0 #define Z1 Z0+9 #define Z2 Z1+20 #define Z3 Z2+19 #define Z6 Z3+10 static JSNorm javascript_norm[] = { { Z0+ 0, 'U', Z0+ 1, Z1+ 0, ACT_SAVE }, { Z0+ 1, 'N', Z0+ 2, Z0+ 0, ACT_NOP }, { Z0+ 2, 'E', Z0+ 3, Z0+ 0, ACT_NOP }, { Z0+ 3, 'S', Z0+ 4, Z0+ 0, ACT_NOP }, { Z0+ 4, 'C', Z0+ 5, Z0+ 0, ACT_NOP }, { Z0+ 5, 'A', Z0+ 6, Z0+ 0, ACT_NOP }, { Z0+ 6, 'P', Z0+ 7, Z0+ 0, ACT_NOP }, { Z0+ 7, 'E', Z0+ 8, Z0+ 0, ACT_NOP }, { Z0+ 8, '(', Z0+ 0, Z0+ 0, ACT_UNESCAPE }, { Z1+ 0, 'S', Z1+ 1, Z2+ 0, ACT_SAVE }, { Z1+ 1, 'T', Z1+ 2, Z0+ 0, ACT_NOP }, { Z1+ 2, 'R', Z1+ 3, Z0+ 0, ACT_NOP }, { Z1+ 3, 'I', Z1+ 4, Z0+ 0, ACT_NOP }, { Z1+ 4, 'N', Z1+ 5, Z0+ 0, ACT_NOP }, { Z1+ 5, 'G', Z1+ 6, Z0+ 0, ACT_NOP }, { Z1+ 6, '.', Z1+ 7, Z0+ 0, ACT_NOP }, { Z1+ 7, 'F', Z1+ 8, Z0+ 0, ACT_NOP }, { Z1+ 8, 'R', Z1+ 9, Z0+ 0, ACT_NOP }, { Z1+ 9, 'O', Z1+10, Z0+ 0, ACT_NOP }, { Z1+10, 'M', Z1+11, Z0+ 0, ACT_NOP }, { Z1+11, 'C', Z1+12, Z0+ 0, ACT_NOP }, { Z1+12, 'H', Z1+13, Z0+ 0, ACT_NOP }, { Z1+13, 'A', Z1+14, Z0+ 0, ACT_NOP }, { Z1+14, 'R', Z1+15, Z0+ 0, ACT_NOP }, { Z1+15, 'C', Z1+16, Z0+ 0, ACT_NOP }, { Z1+16, 'O', Z1+17, Z0+ 0, ACT_NOP }, { Z1+17, 'D', Z1+18, Z0+ 0, ACT_NOP }, { Z1+18, 'E', Z1+19, Z0+ 0, ACT_NOP }, { Z1+19, '(', Z0+ 0, Z0+ 0, ACT_SFCC }, { Z2+ 0, 'D', Z2+ 1, Z3+ 0, ACT_SAVE }, { Z2+ 1, 'E', Z2+ 2, Z0+ 0, ACT_NOP }, { Z2+ 2, 'C', Z2+ 3, Z0+ 0, ACT_NOP }, { Z2+ 3, 'O', Z2+ 4, Z0+ 0, ACT_NOP }, { Z2+ 4, 'D', Z2+ 5, Z0+ 0, ACT_NOP }, { Z2+ 5, 'E', Z2+ 6, Z0+ 0, ACT_NOP }, { Z2+ 6, 'U', Z2+ 7, Z0+ 0, ACT_NOP }, { Z2+ 7, 'R', Z2+ 8, Z0+ 0, ACT_NOP }, { Z2+ 8, 'I', Z2+ 9, Z0+ 0, ACT_NOP }, { Z2+ 9, 'C', Z2+10, Z2+18, ACT_NOP }, { Z2+10, 'O', Z2+11, Z0+ 0, ACT_NOP }, { Z2+11, 'M', Z2+12, Z0+ 0, ACT_NOP }, { Z2+12, 'P', Z2+13, Z0+ 0, ACT_NOP }, { Z2+13, 'O', Z2+14, Z0+ 0, ACT_NOP }, { Z2+14, 'N', Z2+15, Z0+ 0, ACT_NOP }, { Z2+15, 'E', Z2+16, Z0+ 0, ACT_NOP }, { Z2+16, 'N', Z2+17, Z0+ 0, ACT_NOP }, { Z2+17, 'T', Z2+18, Z0+ 0, ACT_NOP }, { Z2+18, '(', Z0+ 0, Z0+ 0, ACT_UNESCAPE }, { Z3+ 0, '<', Z3+ 1, Z6+ 0, ACT_NOP }, { Z3+ 1, '/', Z3+ 2, Z0+ 0, ACT_NOP }, { Z3+ 2, 'S', Z3+ 3, Z0+ 0, ACT_NOP }, { Z3+ 3, 'C', Z3+ 4, Z0+ 0, ACT_NOP }, { Z3+ 4, 'R', Z3+ 5, Z0+ 0, ACT_NOP }, { Z3+ 5, 'I', Z3+ 6, Z0+ 0, ACT_NOP }, { Z3+ 6, 'P', Z3+ 7, Z0+ 0, ACT_NOP }, { Z3+ 7, 'T', Z3+ 8, Z0+ 0, ACT_NOP }, { Z3+ 8, '>', Z3+ 0, Z3+ 9, ACT_QUIT }, { Z3+ 9, ANY, Z3+ 8, Z3+ 8, ACT_NOP }, { Z6+ 0, ANY, Z0+ 0, Z0+ 0, ACT_NOP } }; void UnescapeDecode(char *, uint16_t , char **, char **, uint16_t *, JSState *, uint8_t *); void InitJSNormLookupTable(void) { int iNum; int iCtr; memset(hex_lookup, INVALID_HEX_VAL, sizeof(hex_lookup)); memset(valid_chars, 0, sizeof(valid_chars)); iNum = 0; for(iCtr = 48; iCtr < 56; iCtr++) { hex_lookup[iCtr] = iNum; valid_chars[iCtr] = (IS_HEX|IS_OCT|IS_DEC); iNum++; } for(iCtr = 56; iCtr < 58; iCtr++) { hex_lookup[iCtr] = iNum; valid_chars[iCtr] = (IS_HEX|IS_DEC); iNum++; } iNum = 10; for(iCtr = 65; iCtr < 71; iCtr++) { valid_chars[iCtr] = IS_HEX; hex_lookup[iCtr] = iNum; iNum++; } iNum = 10; for(iCtr = 97; iCtr < 103; iCtr++) { valid_chars[iCtr] = IS_HEX; hex_lookup[iCtr] = iNum; iNum++; } } static inline int outBounds(const char *start, const char *end, char *ptr) { if((ptr >= start) && (ptr < end)) return 0; else return -1; } static inline void CheckWSExceeded(JSState *js, uint16_t *num_spaces) { if(js->allowed_spaces && (*num_spaces > js->allowed_spaces)) { js->alerts |= ALERT_SPACES_EXCEEDED; } *num_spaces = 0; } static void WriteDecodedPNorm(PNormState *s, int c, JSState *js) { const char *dstart, *dend; char *dptr; dstart = s->output.data; dend = s->output.data + s->output.size; dptr = s->output.data + s->output.len; CheckWSExceeded(js, &(s->num_spaces)); if(dptr < dend) { *dptr = (char)c; dptr++; } s->output.len = dptr - dstart; } static int PNorm_exec (PNormState *s, ActionPNorm a, int c, JSState *js) { char *cur_ptr; int iRet = RET_OK; cur_ptr = s->output.data+ s->output.len; switch(a) { case PNORM_ACT_DQUOTES: if(s->prev_event == '\\') { s->fsm = s->fsm_other; WriteDecodedPNorm(s, c, js); break; } s->d_quotes++; if( s->d_quotes == 2) { s->overwrite = cur_ptr; WriteDecodedPNorm(s, c, js); s->d_quotes = 0; break; } if(s->prev_event == '+') { s->prev_event = 0; if( s->overwrite && (s->overwrite < cur_ptr)) { s->output.len = s->overwrite - s->output.data; } else { WriteDecodedPNorm(s, c, js); } } else { WriteDecodedPNorm(s, c, js); } break; case PNORM_ACT_NOP: s->prev_event = c; s->overwrite = NULL; WriteDecodedPNorm(s, c, js); break; case PNORM_ACT_PLUS: s->prev_event = '+'; WriteDecodedPNorm(s, c, js); break; case PNORM_ACT_SPACE: if( s->num_spaces == 0) { WriteDecodedPNorm(s, c, js); } s->num_spaces++; break; case PNORM_ACT_SQUOTES: if(s->prev_event == '\\') { s->fsm = s->fsm_other; WriteDecodedPNorm(s, c, js); break; } s->s_quotes++; if( s->s_quotes == 2) { s->overwrite = cur_ptr; WriteDecodedPNorm(s, c, js); s->s_quotes = 0; break; } if(s->prev_event == '+') { s->prev_event = 0; if( s->overwrite && (s->overwrite < cur_ptr)) { s->output.len = s->overwrite - s->output.data; } else { WriteDecodedPNorm(s, c, js); } } else { WriteDecodedPNorm(s, c, js); } break; case PNORM_ACT_WITHIN_QUOTES: s->prev_event = c; WriteDecodedPNorm(s, c, js); default: break; } return iRet; } static int PNorm_scan_fsm(PNormState* s, int c, JSState *js) { char uc; JSNorm *m = plus_norm + s->fsm; uc = toupper(c); if(isspace(c)) { c = uc =' '; } do { if ( !m->event || ( m->event == uc)) { s->fsm = m->match; s->fsm_other = m->other; break; } s->fsm = m->other; m = plus_norm + s->fsm; } while ( 1 ); return(PNorm_exec(s, m->action, c, js)); } int PNormDecode(char *src, uint16_t srclen, char *dst, uint16_t dstlen, uint16_t *bytes_copied, JSState *js) { int iRet; const char *end; char *ptr; PNormState s; end = src + srclen; ptr = src; s.fsm = 0; s.prev_event = 0; s.d_quotes = 0; s.s_quotes = 0; s.output.data = dst; s.output.size = dstlen; s.output.len = 0; s.overwrite = NULL; s.num_spaces = 0; s.fsm_other = 0; while(ptr < end) { iRet = PNorm_scan_fsm(&s, *ptr, js); ptr++; } dst = s.output.data; *bytes_copied = s.output.len; return iRet; } int ConvertToChar( uint16_t flags, uint8_t *buf, uint8_t buflen) { int val = 0; char *p = NULL; buf[buflen] = ANY; if(flags & IS_DEC) { val = strtoul( (const char *)buf, &p, 10); } else if(flags & IS_OCT) { val = strtoul( (const char *)buf, &p, 8); } else if (flags & IS_HEX) { val = strtoul( (const char *)buf, &p, 16); } return val; } static void WriteDecodedSFCC(SFCCState *s) { char *start = s->output.data; char *end = s->output.data + s->output.size; uint16_t len = s->output.len; char *ptr = s->output.data + len; int copy_len = 0; if(ptr < end) { if(s->cur_flags) { *ptr = (char)ConvertToChar(s->cur_flags, s->buf, s->buflen); ptr++; } else { if((end - ptr) < s->buflen) copy_len = end - ptr; else copy_len = s->buflen; memcpy(ptr , s->buf , copy_len); ptr = ptr + copy_len; } } s->output.len = (ptr -start); s->cur_flags = 0; s->buflen = 0; } static int SFCC_exec (SFCCState *s, ActionSFCC a, int c) { int iRet = RET_OK; switch(a) { case SFCC_ACT_NOP: break; case SFCC_ACT_QUIT: WriteDecodedSFCC(s); iRet = RET_QUIT; break; case SFCC_ACT_INV: WriteDecodedSFCC(s); iRet = RET_INV; break; case SFCC_ACT_DEC: if( s->buflen < MAX_BUF) { s->buf[s->buflen] = c; s->buflen++; s->cur_flags = IS_DEC; } else { s->cur_flags = 0; WriteDecodedSFCC(s); } break; case SFCC_ACT_OCT: if( s->buflen < MAX_BUF) { s->buf[s->buflen] = c; s->buflen++; s->cur_flags = IS_OCT; } else { s->cur_flags = 0; WriteDecodedSFCC(s); } break; case SFCC_ACT_HEX: if( s->buflen < MAX_BUF) { s->buf[s->buflen] = c; s->buflen++; s->cur_flags = IS_HEX; } else { s->cur_flags = 0; WriteDecodedSFCC(s); } break; case SFCC_ACT_COMMA: case SFCC_ACT_SPACE: WriteDecodedSFCC(s); s->cur_flags = 0; break; default: break; } s->alert_flags |= s->cur_flags; return iRet; } static int SFCC_scan_fsm (SFCCState* s, int c) { int indexed = 0; int value = 0; int uc; JSNorm *m = sfcc_norm + s->fsm; uc = toupper(c); if(isspace(c)) return (SFCC_exec(s, SFCC_ACT_SPACE, c)); value = valid_chars[uc]; if(value) indexed = 1; do { if ( !m->event || ((indexed && ((m->event & value) == m->event)) || ( m->event == uc))) { s->fsm = m->match; break; } s->fsm = m->other; m = sfcc_norm + s->fsm; } while ( 1 ); return(SFCC_exec(s, m->action, c)); } void StringFromCharCodeDecode(char *src, uint16_t srclen, char **ptr, char **dst, uint16_t *bytes_copied, JSState *js, uint8_t *iis_unicode_map) { int iRet; const char *start, *end; SFCCState s; uint16_t alert = 0; start = src; end = src + srclen; s.buflen = 0; s.fsm = 0; s.output.data = decoded_out; s.output.size = sizeof(decoded_out); s.output.len = 0; s.cur_flags = s.alert_flags = 0; while(!outBounds(start, end, *ptr)) { iRet = SFCC_scan_fsm(&s, **ptr); if(iRet != RET_OK) { if( (iRet == RET_INV) && ((*ptr - 1) > start )) (*ptr)--; break; } (*ptr)++; } alert = s.alert_flags; //alert mixed encodings if(alert != ( alert & -alert)) { js->alerts |= ALERT_MIXED_ENCODINGS; } UnescapeDecode(s.output.data, s.output.len, &(s.output.data), &(s.output.data), &(s.output.len), js, iis_unicode_map); *dst = s.output.data; *bytes_copied = s.output.len; } static void WriteDecodedUnescape(UnescapeState *s, int c, JSState *js) { const char *dstart, *dend; char *dptr; dstart = s->output.data; dend = s->output.data + s->output.size; dptr = s->output.data + s->output.len; CheckWSExceeded(js, &(s->num_spaces)); if(dptr < dend) { *dptr = (char)c; dptr++; } s->output.len = dptr - dstart; } static int Unescape_exec (UnescapeState *s, ActionUnsc a, int c, JSState *js) { char *cur_ptr; int iRet = RET_OK; cur_ptr = s->output.data+ s->output.len; switch(a) { case UNESC_ACT_BACKSLASH: s->prev_action = 0; s->alert_flags |= IS_BACKSLASH; s->iNorm <<= 4; s->iNorm = (s->iNorm | (hex_lookup[c])); if( s->overwrite && (s->overwrite < cur_ptr)) { s->output.len = s->overwrite - s->output.data; } s->overwrite = NULL; WriteDecodedUnescape(s, s->iNorm, js); s->iNorm = 0; break; case UNESC_ACT_CONV: s->prev_action = 0; s->iNorm <<= 4; s->iNorm = (s->iNorm | (hex_lookup[c])); WriteDecodedUnescape(s, c, js); break; case UNESC_ACT_NOP: s->prev_action = 0; s->iNorm = 0; s->overwrite = NULL; WriteDecodedUnescape(s, c, js); break; case UNESC_ACT_PAREN: if(s->prev_action == UNESC_ACT_UNESCAPE) { s->prev_action = 0; s->multiple_levels++; } s->iNorm = 0; if(s->paren_count > 0) WriteDecodedUnescape(s, c, js); s->paren_count++; break; case UNESC_ACT_PERCENT: s->prev_action = 0; s->alert_flags |= IS_PERCENT; s->iNorm <<= 4; s->iNorm = (s->iNorm | (hex_lookup[c])); if( s->overwrite && (s->overwrite < cur_ptr)) { s->output.len = s->overwrite - s->output.data; } s->overwrite = NULL; WriteDecodedUnescape(s, s->iNorm, js); s->iNorm = 0; break; case UNESC_ACT_QUIT: s->prev_action = 0; s->iNorm = 0; s->overwrite = NULL; if(s->paren_count) s->paren_count--; if( s->paren_count == 0 ) iRet = RET_QUIT; else WriteDecodedUnescape(s, c, js); break; case UNESC_ACT_SAVE: s->prev_action = 0; s->iNorm = 0; s->overwrite = cur_ptr; WriteDecodedUnescape(s, c, js); break; case UNESC_ACT_SAVE_NOP: s->prev_action = 0; s->iNorm = 0; WriteDecodedUnescape(s, c, js); break; case UNESC_ACT_SPACE: s->iNorm = 0; if(s->prev_event == '\'' || s->prev_event =='"') { WriteDecodedUnescape(s, c, js); return iRet; } if( s->prev_event != ' ') { WriteDecodedUnescape(s, c, js); } s->num_spaces++; break; case UNESC_ACT_UBACKSLASH: s->prev_action = 0; s->alert_flags |= IS_UBACKSLASH; s->iNorm <<= 4; s->iNorm = (s->iNorm | (hex_lookup[c])); if( s->overwrite && (s->overwrite < cur_ptr)) { s->output.len = s->overwrite - s->output.data; } s->overwrite = NULL; if( s->iNorm > 0xff ) { if(s->unicode_map && (s->iNorm <= 0xffff)) { s->iNorm = s->unicode_map[s->iNorm]; if(s->iNorm == -1) s->iNorm = NON_ASCII_CHAR; } else { s->iNorm = NON_ASCII_CHAR; } } WriteDecodedUnescape(s, s->iNorm, js); s->iNorm = 0; break; case UNESC_ACT_UPERCENT: s->prev_action = 0; s->alert_flags |= IS_UPERCENT; s->iNorm <<= 4; s->iNorm = (s->iNorm | (hex_lookup[c])); if( s->overwrite && (s->overwrite < cur_ptr)) { s->output.len = s->overwrite - s->output.data; } s->overwrite = NULL; if( s->iNorm > 0xff ) { if(s->unicode_map && (s->iNorm <= 0xffff)) { s->iNorm = s->unicode_map[s->iNorm]; if(s->iNorm == -1) s->iNorm = NON_ASCII_CHAR; } else { s->iNorm = NON_ASCII_CHAR; } } WriteDecodedUnescape(s, s->iNorm, js); s->iNorm = 0; break; case UNESC_ACT_UNESCAPE: /* Save the action and wait till parenthesis to increment the multiple_levels. * Only space is allowed between this action and parentheses */ s->prev_action = a; s->iNorm = 0; WriteDecodedUnescape(s, c, js); break; default: break; } s->prev_event = c; return iRet; } static int Unescape_scan_fsm (UnescapeState* s, int c, JSState *js) { int indexed = 0; int value = 0; int uc; JSNorm *m = unescape_norm + s->fsm; uc = toupper(c); if(isspace(c)) { c = uc =' '; return(Unescape_exec(s, UNESC_ACT_SPACE, c, js)); } value = valid_chars[uc]; if(value) indexed = 1; do { if ( !m->event || ( ( m->event == uc) || (indexed && ((m->event & value) == m->event)))) { s->fsm = m->match; break; } s->fsm = m->other; m = unescape_norm + s->fsm; } while ( 1 ); return(Unescape_exec(s, m->action, c, js)); } void UnescapeDecode(char *src, uint16_t srclen, char **ptr, char **dst, uint16_t *bytes_copied, JSState *js, uint8_t *iis_unicode_map) { int iRet; const char *start, *end; UnescapeState s; uint16_t alert = 0; start = src; end = src + srclen; s.iNorm = 0; s.fsm = 0; s.output.data = decoded_out; s.output.size = sizeof(decoded_out); s.output.len = 0; s.alert_flags = 0; s.prev_event = 0; s.prev_action = 0; s.overwrite = NULL; s.multiple_levels = 1; s.unicode_map = iis_unicode_map; s.num_spaces = 0; s.paren_count = 0; while(!outBounds(start, end, *ptr)) { iRet = Unescape_scan_fsm(&s, **ptr, js); if(iRet != RET_OK) { /*if( (iRet == RET_INV) && ((*ptr - 1) > start )) (*ptr)--;*/ break; } (*ptr)++; } alert = s.alert_flags; //alert mixed encodings if(alert != ( alert & -alert)) { js->alerts |= ALERT_MIXED_ENCODINGS; } if(s.multiple_levels > js->allowed_levels) { js->alerts |= ALERT_LEVELS_EXCEEDED; } PNormDecode(s.output.data, s.output.len, s.output.data, s.output.len, bytes_copied, js); *dst = s.output.data; //*bytes_copied = s.output.len; } static inline void WriteJSNormChar(JSNormState *s, int c, JSState *js) { const char *dstart, *dend; char *dptr; dstart = s->dest.data; dend = s->dest.data + s->dest.size; dptr = s->dest.data + s->dest.len; CheckWSExceeded(js, &(s->num_spaces)); if(!outBounds(dstart, dend, dptr)) { *dptr = (char)c; dptr++; } s->dest.len = dptr - dstart; } static void WriteJSNorm(JSNormState *s, char *copy_buf, uint16_t copy_len, JSState *js) { const char *end, *dstart, *dend; char *ptr, *dptr; ptr = copy_buf; end = copy_buf + copy_len; dstart = s->dest.data; dend = s->dest.data + s->dest.size; dptr = s->dest.data + s->dest.len; CheckWSExceeded(js, &(s->num_spaces)); if(ptr < end) { if((dend - dptr) < copy_len ) { copy_len = dend - dptr; } memcpy(dptr, ptr, copy_len); dptr = dptr + copy_len; } s->dest.len = dptr - dstart; } static int JSNorm_exec(JSNormState *s, ActionJSNorm a, int c, char *src, uint16_t srclen, char **ptr, JSState *js) { char *cur_ptr; int iRet = RET_OK; uint16_t bcopied = 0; char *dest; cur_ptr = s->dest.data+ s->dest.len; switch(a) { case ACT_NOP: WriteJSNormChar(s, c, js); break; case ACT_SAVE: s->overwrite = cur_ptr; WriteJSNormChar(s, c, js); break; case ACT_SPACE: if( s->prev_event != ' ') { WriteJSNormChar(s, c, js); } s->num_spaces++; break; case ACT_UNESCAPE: if(s->overwrite && (s->overwrite < cur_ptr)) { s->dest.len = s->overwrite - s->dest.data; } UnescapeDecode(src, srclen, ptr, &dest, &bcopied, js, s->unicode_map); WriteJSNorm(s, dest, bcopied, js); break; case ACT_SFCC: if( s->overwrite && (s->overwrite < cur_ptr)) { s->dest.len = s->overwrite - s->dest.data; } StringFromCharCodeDecode(src, srclen, ptr, &dest, &bcopied, js, s->unicode_map); WriteJSNorm(s, dest, bcopied, js); break; case ACT_QUIT: iRet = RET_QUIT; WriteJSNormChar(s, c, js); break; default: break; } s->prev_event = c; return iRet; } static int JSNorm_scan_fsm (JSNormState* s, int c, char *src, uint16_t srclen, char **ptr, JSState *js) { char uc; JSNorm *m = javascript_norm + s->fsm; uc = toupper(c); if(isspace(c)) { c = uc =' '; return(JSNorm_exec(s, ACT_SPACE, c, src, srclen, ptr, js)); } do { if (!m->event || (m->event == uc)) { s->fsm = m->match; break; } s->fsm = m->other; m = javascript_norm + s->fsm; }while ( 1 ); return(JSNorm_exec(s, m->action, c, src, srclen, ptr, js)); } int JSNormalizeDecode(char *src, uint16_t srclen, char *dst, uint16_t destlen, char **ptr, int *bytes_copied, JSState *js, uint8_t *iis_unicode_map) { int iRet; const char *start, *end; JSNormState s; if(js == NULL) { return RET_QUIT; } start = src; end = src + srclen; s.fsm = 0; s.overwrite = NULL; s.dest.data = dst; s.dest.size = destlen; s.dest.len = 0; s.prev_event = 0; s.unicode_map = iis_unicode_map; s.num_spaces = 0; while(!outBounds(start, end, *ptr)) { iRet = JSNorm_scan_fsm(&s, **ptr, src, srclen, ptr, js); if(iRet != RET_OK) { break; } (*ptr)++; } if(!outBounds(start, end, *ptr) && (iRet == RET_QUIT)) { (*ptr)++; } dst = s.dest.data; *bytes_copied = s.dest.len; return RET_OK; } /* int main(int argc, char *argv[]) { FILE *iFile = NULL; FILE *oFile = NULL; char input[65535]; char output[65535]; int bytes_copied = 0; int bytes_read = 0; int ret = 0; char *ptr = input; JSState js; if( argc == 3 ) { iFile = fopen(argv[1], "r"); oFile = fopen(argv[2], "w"); } if(!oFile || !iFile) { fprintf(stderr, "usage: %s \n", argv[0]); return -1; } bytes_read = fread(input, 1, sizeof(input), iFile); js.allowed_spaces = 3; js.allowed_levels = 1; js.alerts = 0; InitJSNormLookupTable(); ret = JSNormalizeDecode(input, bytes_read, output, sizeof(output),&ptr, &bytes_copied, &js, NULL); if( ret == RET_OK) { fwrite( output, 1, bytes_copied, oFile); printf("OUTPUT IS %.*s\n",bytes_copied,output); printf("REMAINING is %s\n",ptr); if( js.alerts & ALERT_MIXED_ENCODINGS ) printf("ALERT MIXED ENCODINGS\n"); if(js.alerts & ALERT_SPACES_EXCEEDED) printf("ALERT SPACES EXCEEDED\n"); if(js.alerts & ALERT_LEVELS_EXCEEDED) printf("ALERT LEVELS EXCEEDED\n"); } fclose(iFile); fclose(oFile); return 0; }*/ snort-2.9.20/src/sfutil/segment_mem.c0000644000175000017500000000664714241077231015650 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** 8/7/2011 - Initial implementation ... Hui Cao */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "segment_mem.h" /*point to the start of the unused memory*/ static MEM_OFFSET unused_ptr = 0; static size_t unused_mem = 0; static void *base_ptr = NULL; size_t segment_unusedmem(void) { return unused_mem; } /*************************************************************************** * Initialize the segment memory * Return values: * 1: success * 0: fail **************************************************************************/ int segment_meminit(uint8_t* buff, size_t mem_cap) { base_ptr = buff; unused_ptr = 0; unused_mem = mem_cap; return 1; } /*************************************************************************** * allocate memory block from segment * todo:currently, we only allocate memory continuously. Need to reuse freed * memory in the future. * return: * 0: fail * other: the offset of the allocated memory block **************************************************************************/ MEM_OFFSET segment_malloc ( size_t size ) { MEM_OFFSET current_ptr = unused_ptr; if (unused_mem < size) return 0; unused_ptr += size; unused_mem -= size; return current_ptr; } /*************************************************************************** * Free memory block from segment * Todo: currently, no action for free. Need to reuse freed memory in the * future. **************************************************************************/ void segment_free ( MEM_OFFSET ptr ) { return; } /*************************************************************************** * allocate memory block from segment and initialize it to zero * It calls segment_malloc() to get memory. * todo:currently, we only allocate memory continuously. Need to reuse freed * memory in the future. * return: * 0: fail * other: the offset of the allocated memory block **************************************************************************/ MEM_OFFSET segment_calloc ( size_t num, size_t size ) { MEM_OFFSET current_ptr; size_t total; if ((0 == size)||(0 == num)) return 0; /*Check possible overflow*/ if (num > SIZE_MAX/size) return 0; total = num * size; current_ptr = segment_malloc(total); if (0 != current_ptr) memset((uint8_t *)base_ptr + current_ptr, 0, total); return current_ptr; } void * segment_basePtr() { return base_ptr; } snort-2.9.20/src/sfutil/md5.c0000644000175000017500000002343214241077224014026 0ustar apoapo/* * This code implements the MD5 message-digest algorithm. * The algorithm is due to Ron Rivest. This code was * written by Colin Plumb in 1993, no copyright is claimed. * This code is in the public domain; do with it what you wish. * * Equivalent code is available from RSA Data Security, Inc. * This code has been tested against that, and is equivalent, * except that you don't need to include two pages of legalese * with every copy. * * To compute the message digest of a chunk of bytes, declare an * MD5Context structure, pass it to MD5Init, call MD5Update as * needed on buffers full of bytes, and then call MD5Final, which * will fill a supplied 16-byte array with the digest. */ /* This code slightly modified to fit into Samba by abartlet@samba.org Jun 2001 and to fit the cifs vfs by Steve French sfrench@us.ibm.com */ #include "config.h" #ifndef HAVE_OPENSSL_MD5 #include #include "md5.h" static void MD5Transform(uint32_t buf[4], uint32_t const in[16]); /* * Note: this code is harmless on little-endian machines. */ static void byteReverse(unsigned char *buf, unsigned longs) { uint32_t t; do { t = (uint32_t) ((unsigned) buf[3] << 8 | buf[2]) << 16 | ((unsigned) buf[1] << 8 | buf[0]); *(uint32_t *) buf = t; buf += 4; } while (--longs); } /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; ctx->buf[2] = 0x98badcfe; ctx->buf[3] = 0x10325476; ctx->bits[0] = 0; ctx->bits[1] = 0; } /* * Update context to reflect the concatenation of another buffer full * of bytes. */ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) { register uint32_t t; /* Update bitcount */ t = ctx->bits[0]; if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) ctx->bits[1]++; /* Carry from low to high */ ctx->bits[1] += len >> 29; t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ /* Handle any leading odd-sized chunks */ if (t) { unsigned char *p = (unsigned char *) ctx->in + t; t = 64 - t; if (len < t) { memmove(p, buf, len); return; } memmove(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); buf += t; len -= t; } /* Process data in 64-byte chunks */ while (len >= 64) { memmove(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); buf += 64; len -= 64; } /* Handle any remaining bytes of data. */ memmove(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned int count; unsigned char *p; /* Compute number of bytes mod 64 */ count = (ctx->bits[0] >> 3) & 0x3F; /* Set the first char of padding to 0x80. This is safe since there is always at least one byte free */ p = ctx->in + count; *p++ = 0x80; /* Bytes of padding needed to make 64 bytes */ count = 64 - 1 - count; /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (uint32_t *) ctx->in); /* Now fill the next block with 56 bytes */ memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ memset(p, 0, count - 8); } byteReverse(ctx->in, 14); /* Append length in bits and transform */ ((uint32_t *) ctx->in)[14] = ctx->bits[0]; ((uint32_t *) ctx->in)[15] = ctx->bits[1]; MD5Transform(ctx->buf, (uint32_t *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memmove(digest, ctx->buf, 16); memset(ctx, 0, sizeof(*ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ #define F1(x, y, z) (z ^ (x & (y ^ z))) #define F2(x, y, z) F1(z, x, y) #define F3(x, y, z) (x ^ y ^ z) #define F4(x, y, z) (y ^ (x | ~z)) /* This is the central step in the MD5 algorithm. */ #define MD5STEP(f, w, x, y, z, data, s) \ ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) /* * The core of the MD5 algorithm, this alters an existing MD5 hash to * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) { register uint32_t a, b, c, d; a = buf[0]; b = buf[1]; c = buf[2]; d = buf[3]; MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); buf[0] += a; buf[1] += b; buf[2] += c; buf[3] += d; } /*********************************************************************** the microsoft version of hmac_md5 initialisation. ***********************************************************************/ void hmac_md5_init_limK_to_64(const unsigned char *key, int key_len, struct HMACMD5Context *ctx) { int i; /* if key is longer than 64 bytes truncate it */ if (key_len > 64) { key_len = 64; } /* start out by storing key in pads */ memset(ctx->k_ipad, 0, sizeof (ctx->k_ipad)); memset(ctx->k_opad, 0, sizeof (ctx->k_opad)); memcpy(ctx->k_ipad, key, key_len); memcpy(ctx->k_opad, key, key_len); /* XOR key with ipad and opad values */ for (i = 0; i < 64; i++) { ctx->k_ipad[i] ^= 0x36; ctx->k_opad[i] ^= 0x5c; } MD5Init(&ctx->ctx); MD5Update(&ctx->ctx, ctx->k_ipad, 64); } /*********************************************************************** update hmac_md5 "inner" buffer ***********************************************************************/ void hmac_md5_update(const unsigned char *text, int text_len, struct HMACMD5Context *ctx) { MD5Update(&ctx->ctx, text, text_len); /* then text of datagram */ } /*********************************************************************** finish off hmac_md5 "inner" buffer and generate outer one. ***********************************************************************/ void hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx) { struct MD5Context ctx_o; MD5Final(digest, &ctx->ctx); MD5Init(&ctx_o); MD5Update(&ctx_o, ctx->k_opad, 64); MD5Update(&ctx_o, digest, 16); MD5Final(digest, &ctx_o); } /*********************************************************** single function to calculate an HMAC MD5 digest from data. use the microsoft hmacmd5 init method because the key is 16 bytes. ************************************************************/ void hmac_md5(unsigned char key[16], unsigned char *data, int data_len, unsigned char *digest) { struct HMACMD5Context ctx; hmac_md5_init_limK_to_64(key, 16, &ctx); if (data_len != 0) { hmac_md5_update(data, data_len, &ctx); } hmac_md5_final(digest, &ctx); } #endif /* !HAVE_OPENSSL_MD5 */ snort-2.9.20/src/sfutil/mpse_methods.h0000644000175000017500000000234114241077230016026 0ustar apoapo/* ** mpse.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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 Gener* */ #ifndef _MPSE_METHODS_H_ #define _MPSE_METHODS_H_ /* * Pattern Matching Methods */ //#define MPSE_MWM 1 #define MPSE_AC 2 //#define MPSE_KTBM 3 #define MPSE_LOWMEM 4 //#define MPSE_AUTO 5 #define MPSE_ACF 6 #define MPSE_ACS 7 #define MPSE_ACB 8 #define MPSE_ACSB 9 #define MPSE_AC_BNFA 10 #define MPSE_AC_BNFA_Q 11 #define MPSE_ACF_Q 12 #define MPSE_LOWMEM_Q 13 #ifdef INTEL_SOFT_CPM #define MPSE_INTEL_CPM 14 #endif /* INTEL_SOFT_CPM */ typedef enum { MPSE_PATTERN_CASE, MPSE_PATTERN_NOCASE } tMpseCaseEnum; #endif snort-2.9.20/src/sfutil/util_math.h0000644000175000017500000000300214241077353015326 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file util_math.h * @author Chris Green * @date Fri Jun 27 10:12:57 2003 * * @brief math related util functions * * Place simple math functions that are useful all over the place * here. */ #ifndef _UTIL_MATH_H #define _UTIL_MATH_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" double calc_percent(double amt, double total); double calc_percent64(uint64_t amt, uint64_t total); #endif /* _UTIL_MATH_H */ snort-2.9.20/src/sfutil/sfrt_flat.h0000644000175000017500000001763614241077326015346 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** 9/7/2011 - Initial implementation ... Hui Cao ** ** This is based on the original sfrt.h, but using the flat segment memory. ** When allocating memory, it uses memory in the segment, and returns ** the offset. ** When accessing memory, it must use the base address and offset to ** correctly refer to it. */ #ifndef _SFRT_FLAT_H_ #define _SFRT_FLAT_H_ #include #include #include "snort_debug.h" #include "ipv6_port.h" #include "segment_mem.h" typedef MEM_OFFSET INFO; /* To be replaced with a pointer to a policy */ typedef MEM_OFFSET FLAT_INDEX; typedef MEM_OFFSET TABLE_PTR; typedef enum { SAVE_TO_NEW, SAVE_TO_CURRENT }SaveDest; typedef int64_t (*updateEntryInfoFunc)(INFO *entryInfo, INFO newInfo, SaveDest saveDest, uint8_t *base); typedef struct { FLAT_INDEX index; int length; } tuple_flat_t; #include "sfrt_flat_dir.h" /*******************************************************************/ /* Master table struct. Abstracts DIR and LC-trie methods */ typedef struct { uint32_t num_ent; /* Number of entries in the policy table */ uint32_t max_size; /* Max size of policies array */ char ip_type; /* Only IPs of this family will be used */ char table_flat_type; char mem_type; uint32_t allocated; INFO data; /* data table. Each IP points to an entry here */ TABLE_PTR rt; /* Actual "routing" table */ TABLE_PTR rt6; /* Actual "routing" table */ TABLE_PTR list_info; /* List file information table (entry information)*/ } table_flat_t; /*******************************************************************/ /* Abstracted routing table API */ table_flat_t * sfrt_flat_new(char table_flat_type, char ip_type, long data_size, uint32_t mem_cap); void sfrt_flat_free(TABLE_PTR table); GENERIC sfrt_flat_lookup(sfaddr_t *ip, table_flat_t *table); int sfrt_flat_insert(sfcidr_t *ip, unsigned char len, INFO ptr, int behavior, table_flat_t *table, updateEntryInfoFunc updateEntry); uint32_t sfrt_flat_usage(table_flat_t *table); uint32_t sfrt_flat_num_entries(table_flat_t *table); /* Perform a lookup on value contained in "ip" * For performance reason, we use this simplified version instead of sfrt_lookup * Note: this only applied to table setting: DIR_8x16 (DIR_16_8_4x2 for IPV4), DIR_8x4*/ static inline GENERIC sfrt_flat_dir8x_lookup(sfaddr_t *ip, table_flat_t* table) { dir_sub_table_flat_t *subtable; Entry_Value *entries_value; Entry_Len *entries_length; uint8_t *base = (uint8_t *) table; int i; dir_table_flat_t *rt = NULL; int index; INFO *data = (INFO *) (&base[table->data]); if (sfaddr_family(ip) == AF_INET) { rt = (dir_table_flat_t *)(&base[table->rt]); subtable = (dir_sub_table_flat_t *)(&base[rt->sub_table]); /* 16 bits */ index = ntohs(ip->ia16[6]); entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( !entries_value[index] || entries_length[index] ) { if (data[entries_value[index]]) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } subtable = (dir_sub_table_flat_t *)(&base[entries_value[index]]); /* 4 bits */ index = ip->ia8[14] >> 4; entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if ( !entries_value[index] || entries_length[index] ) { if ( data[entries_value[index]] ) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } subtable = (dir_sub_table_flat_t *)(&base[entries_value[index]]); /* 4 bits */ index = ip->ia8[14] & 0xF; entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( !entries_value[index] || entries_length[index] ) { if ( data[entries_value[index]] ) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } subtable = (dir_sub_table_flat_t *)(&base[entries_value[index]]); /* 2 bits */ index = ip->ia8[15] >> 6; entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( !entries_value[index] || entries_length[index] ) { if ( data[entries_value[index]] ) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } subtable = (dir_sub_table_flat_t *)(&base[entries_value[index]]); /* 2 bits */ index = (ip->ia8[15] >> 4) & 0x3; entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( !entries_value[index] || entries_length[index] ) { if ( data[entries_value[index]] ) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } subtable = (dir_sub_table_flat_t *)(&base[entries_value[index]]); /* 2 bits */ index = (ip->ia8[15] >> 2) & 0x3; entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( !entries_value[index] || entries_length[index] ) { if ( data[entries_value[index]] ) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } subtable = (dir_sub_table_flat_t *)(&base[entries_value[index]]); /* 2 bits */ index = ip->ia8[15] & 0x3; entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( !entries_value[index] || entries_length[index] ) { if ( data[entries_value[index]] ) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } } else { rt = (dir_table_flat_t *)(&base[table->rt6]); subtable = (dir_sub_table_flat_t *)(&base[rt->sub_table]); for (i = 0; i < 16; i++) { index = ip->ia8[i]; entries_value = (Entry_Value *)(&base[subtable->entries_value]); entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( !entries_value[index] || entries_length[index] ) { if ( data[entries_value[index]] ) return (GENERIC) &base[data[entries_value[index]]]; else return NULL; } subtable = (dir_sub_table_flat_t *)(&base[entries_value[index]]); } } return NULL; } #endif snort-2.9.20/src/sfutil/util_utf.h0000644000175000017500000000420214241077364015200 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2010-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef UTIL_UTF_H #define UTIL_UTF_H /* return codes */ #define DECODE_UTF_SUCCESS 0 #define DECODE_UTF_FAILURE -1 /* character set types */ #define CHARSET_DEFAULT 0 #define CHARSET_UTF7 1 #define CHARSET_UTF16LE 2 #define CHARSET_UTF16BE 3 #define CHARSET_UTF32LE 4 #define CHARSET_UTF32BE 5 #define CHARSET_UNKNOWN 255 /* Since payloads don't have to end on 2/4-byte boundaries, callers to DecodeUTF are responsible for keeping a decode_utf_state_t. This carries state between subsequent calls. */ typedef struct decode_utf_state { int state; int charset; } decode_utf_state_t; /* Init & Terminate functions for decode_utf_state_t. */ int init_decode_utf_state(decode_utf_state_t *new); int term_decode_utf_state(decode_utf_state_t *dead); /* setters & getters */ int set_decode_utf_state_charset(decode_utf_state_t *dstate, int charset); int get_decode_utf_state_charset(decode_utf_state_t *dstate); /* UTF-Decoding function prototypes */ int DecodeUTF(char *src, unsigned int src_len, char *dst, unsigned int dst_len, int *bytes_copied, decode_utf_state_t *dstate); #endif /* UTIL_UTF_H */ snort-2.9.20/src/sfutil/sfeventq.c0000644000175000017500000002263314241077261015177 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file sfeventq.c ** ** @author Daniel Roelker ** ** @brief This provides generic functions for queuing events and ** inserting the events with a provided function. All ** memory management for events is provided here. ** ** ** The sfeventq functions provide a generic way for handling events, ** prioritizing those events, and acting on the highest ranked events ** with a user function. ** ** Example on using sfeventq: ** ** 1. Initialize event queue ** sfeventq_init() ** ** 2. Add events to queue ** sfeventq_event_alloc() allocates the memory for storing the event. ** sfeventq_add() adds the event and prioritizes the event in the queue. ** You should only allocate and add one event at a time. Otherwise, ** event_alloc() will return NULL on memory exhaustion. ** ** 3. Event actions ** sfeventq_action() will call the provided function on the initialized ** number of events to log. */ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sfeventq.h" #include "util.h" /* ** NAME ** sfeventq_new:: */ /** ** Initialize the event queue. Provide the max number of nodes that this ** queue will support, the number of top nodes to log in the queue, and the ** size of the event structure that the user will fill in. ** ** @return integer ** ** @retval -1 failure ** @retval 0 success */ SF_EVENTQ * sfeventq_new(int max_nodes, int log_nodes, int event_size) { SF_EVENTQ *eq; if ((max_nodes <= 0) || (log_nodes <= 0) || (event_size <= 0)) return NULL; eq = (SF_EVENTQ *)SnortAlloc(sizeof(SF_EVENTQ)); /* Initialize the memory for the nodes that we are going to use. */ eq->node_mem = (SF_EVENTQ_NODE *)SnortAlloc(sizeof(SF_EVENTQ_NODE) * max_nodes); eq->event_mem = (char *)SnortAlloc(event_size * (max_nodes + 1)); eq->max_nodes = max_nodes; eq->log_nodes = log_nodes; eq->event_size = event_size; eq->cur_nodes = 0; eq->cur_events = 0; eq->reserve_event = (void *)(&eq->event_mem[max_nodes * eq->event_size]); return eq; } /* ** NAME ** sfeventq_event_alloc:: */ /** ** Allocate the memory for an event to add to the event queue. This ** function is meant to be called first, the event structure filled in, ** and then added to the queue. While you can allocate several times before ** adding to the queue, this is not recommended as you may get a NULL ptr ** if you allocate more than the max node number. ** ** @return void * ** ** @retval NULL unable to allocate memory. ** @retval !NULL ptr to memory. */ void * sfeventq_event_alloc(SF_EVENTQ *eq) { void *event; if (eq->cur_events >= eq->max_nodes) { if (eq->reserve_event == NULL) return NULL; event = (void *)eq->reserve_event; eq->reserve_event = NULL; return event; } event = (void *)(&eq->event_mem[eq->cur_events * eq->event_size]); eq->cur_events++; return event; } /* ** NAME ** sfeventq_reset:: */ /** ** Resets the event queue. We also set the reserve event back ** to the last event in the queue. ** ** @return void */ void sfeventq_reset(SF_EVENTQ *eq) { eq->head = NULL; eq->cur_nodes = 0; eq->cur_events = 0; eq->reserve_event = (void *)(&eq->event_mem[eq->max_nodes * eq->event_size]); } /* ** NAME ** sfeventq_free:: */ /** ** Cleanup the event queue. ** ** @return none ** */ void sfeventq_free(SF_EVENTQ *eq) { if (eq == NULL) return; /* Free the memory for the nodes that were in use. */ if (eq->node_mem != NULL) { free(eq->node_mem); eq->node_mem = NULL; } if (eq->event_mem != NULL) { free(eq->event_mem); eq->event_mem = NULL; } free(eq); } /* ** NAME ** get_eventq_node:: */ /** ** This function returns a ptr to the node to use. We allocate the last ** event node if we have exhausted the event queue. Before we allocate ** the last node, we determine if the incoming event has a higher ** priority than the last node. If it does, we allocate the node, otherwise ** we drop it because it is lower priority. ** ** If the last node is allocated, we have to point the reserve_event to ** the allocated event memory, since the reserved_event memory was used ** for the incoming event. ** ** @return SF_EVENTQ_NODE * ** ** @retval NULL resource exhaustion and event is lower priority than last node ** @retval !NULL ptr to node memory. */ static SF_EVENTQ_NODE * get_eventq_node(SF_EVENTQ *eq, void *event) { if (eq->cur_nodes >= eq->max_nodes) return NULL; /* ** We grab the next node from the node memory. */ return &eq->node_mem[eq->cur_nodes++]; } /* ** NAME ** sfeventq_add: */ /** ** Add this event to the queue using the supplied ordering ** function. If the queue is exhausted, then we compare the ** event to be added with the last event, and decide whether ** it is a higher priority than the last node. ** ** @return integer ** ** @retval -1 add event failed ** @retval 0 add event succeeded */ int sfeventq_add(SF_EVENTQ *eq, void *event) { SF_EVENTQ_NODE *node; if(!event) return -1; /* ** If get_eventq_node() returns NULL, this means that ** we have exhausted the eventq and the incoming event ** is lower in priority then the last ranked event. ** So we just drop it. */ node = get_eventq_node(eq, event); if(!node) return -1; node->event = event; node->next = NULL; node->prev = NULL; /* ** This is the first node */ if(eq->cur_nodes == 1) { eq->head = eq->last = node; return 0; } /* ** This means we are the last node. */ node->prev = eq->last; eq->last->next = node; eq->last = node; return 0; } /* ** NAME ** sfeventq_action:: */ /** ** Call the supplied user action function on the highest priority ** events. ** ** @return integer ** ** @retval -1 action function failed on an event ** @retval 0 no events logged ** @retval 1 events logged */ int sfeventq_action(SF_EVENTQ *eq, int (*action_func)(void *, void *), void *user) { SF_EVENTQ_NODE *node; int logged = 0; if (action_func == NULL) return -1; if (eq->head == NULL) return 0; for (node = eq->head; node != NULL; node = node->next) { if (logged >= eq->log_nodes) return 1; if (action_func(node->event, user)) return -1; logged++; } return 1; } //#define I_WANT_MY_MAIN #ifdef I_WANT_MY_MAIN #include #include int myaction(void *event, void *user) { int *e; if(!event) return 1; e = (int *)event; printf("-- EVENT: %d\n", *e); return 0; } int main(int argc, char **argv) { int max_events; int log_events; int add_events; int *event; int iCtr; SF_EVENTQ *eq; if(argc < 4) { printf("-- Not enough args\n"); return 1; } max_events = atoi(argv[1]); if(max_events <= 0) { printf("-- max_events invalid.\n"); return 1; } log_events = atoi(argv[2]); if(log_events <= 0) { printf("-- log_events invalid.\n"); return 1; } add_events = atoi(argv[3]); if(add_events <= 0) { printf("-- add_events invalid.\n"); return 1; } if(max_events < log_events) { printf("-- log_events greater than max_events\n"); return 1; } srandom(time(NULL)); eq = sfeventq_new(max_events, log_events, sizeof(int)); if (eq == NULL) { printf("Couldn't allocate the event queue\n"); return 1; } do { printf("-- Event Queue Test --\n\n"); for(iCtr = 0; iCtr < add_events; iCtr++) { event = (int *)sfeventq_event_alloc(eq); if(!event) { printf("-- event allocation failed\n"); return 1; } *event = (int)(random()%3); sfeventq_add(eq, event); printf("-- added %d\n", *event); } printf("\n-- Logging\n\n"); if(sfeventq_action(eq, myaction, NULL)) { printf("-- There was a problem.\n"); return 1; } sfeventq_reset(eq); } while(getc(stdin) < 14); return 0; } #endif snort-2.9.20/src/sfutil/sfrt_flat.c0000644000175000017500000002357214241077325015334 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** 9/7/2011 - Initial implementation ... Hui Cao */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sfrt_flat.h" #define MINIMAL_TABLE_MEMORY (1024*512) /*Basic table cost is around 512k*/ /* Create new lookup table * @param table_flat_type Type of table. Uses the types enumeration in route.h * @param ip_type IPv4 or IPv6. Uses the types enumeration in route.h * @param data_size Max number of unique data entries * * Returns the new table. */ table_flat_t *sfrt_flat_new(char table_flat_type, char ip_type, long data_size, uint32_t mem_cap) { table_flat_t *table; MEM_OFFSET table_ptr; uint8_t *base; long data_size_max = 1; table_ptr = segment_malloc(sizeof(table_flat_t)); #if 0 /*The first allocation always return 0*/ if(!table_ptr) { // return NULL; } #endif base = (uint8_t *)segment_basePtr(); table = (table_flat_t *)(&base[table_ptr]); /* If this limit is exceeded, there will be no way to distinguish * between pointers and indeces into the data table. Only * applies to DIR-n-m. */ #if SIZEOF_LONG_INT == 8 if(data_size >= 0x800000000000000) #else if(data_size >= 0x8000000) #endif { segment_free(table_ptr); return NULL; } /* mem_cap is specified in megabytes, but internally uses bytes. Convert */ mem_cap *= 1024*1024; /* Maximum allowable number of stored entries based on memcap */ if (mem_cap > MINIMAL_TABLE_MEMORY) data_size_max = (mem_cap - MINIMAL_TABLE_MEMORY)/sizeof(INFO); /* Maximum allowable number of stored entries */ if (data_size < data_size_max) table->max_size = data_size; else table->max_size = data_size_max; table->data = (INFO)segment_calloc(sizeof(INFO) * table->max_size, 1); if(!table->data) { segment_free(table_ptr); return NULL; } table->allocated = sizeof(table_flat_t) + sizeof(INFO) * table->max_size; table->ip_type = ip_type; table->table_flat_type = table_flat_type; /* This will point to the actual table lookup algorithm */ table->rt = 0; table->rt6 = 0; /* index 0 will be used for failed lookups, so set this to 1 */ table->num_ent = 1; /* Allocate the user-specified DIR-n-m table */ switch(table_flat_type) { case DIR_24_8: table->rt = sfrt_dir_flat_new( mem_cap, 2, 24, 8); break; case DIR_16x2: table->rt = sfrt_dir_flat_new( mem_cap, 2, 16,16); break; case DIR_16_8x2: table->rt = sfrt_dir_flat_new( mem_cap, 3, 16,8,8); break; case DIR_16_4x4: table->rt = sfrt_dir_flat_new( mem_cap, 5, 16,4,4,4,4); break; case DIR_8x4: table->rt = sfrt_dir_flat_new( mem_cap, 4, 8,8,8,8); break; /* There is no reason to use 4x8 except for benchmarking and * comparison purposes. */ case DIR_4x8: table->rt = sfrt_dir_flat_new( mem_cap, 8, 4,4,4,4,4,4,4,4); break; /* There is no reason to use 2x16 except for benchmarking and * comparison purposes. */ case DIR_2x16: table->rt = sfrt_dir_flat_new( mem_cap, 16, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2); break; case DIR_16_4x4_16x5_4x4: table->rt = sfrt_dir_flat_new(mem_cap, 5, 16,4,4,4,4); table->rt6 = sfrt_dir_flat_new(mem_cap, 14, 16,4,4,4,4,16,16,16,16,16,4,4,4,4); break; case DIR_16x7_4x4: table->rt = sfrt_dir_flat_new(mem_cap, 5, 16,4,4,4,4); table->rt6 = sfrt_dir_flat_new(mem_cap, 11, 16,16,16,16,16,16,16,4,4,4,4); break; case DIR_16x8: table->rt = sfrt_dir_flat_new(mem_cap, 2, 16,16); table->rt6 = sfrt_dir_flat_new(mem_cap, 8, 16,16,16,16,16,16,16,16); break; case DIR_8x16: table->rt = sfrt_dir_flat_new(mem_cap, 7, 16,4,4,2,2,2,2); table->rt6 = sfrt_dir_flat_new(mem_cap, 16, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8); break; }; if((!table->rt) || (!table->rt6)) { if (table->rt) sfrt_dir_flat_free( table->rt ); if (table->rt6) sfrt_dir_flat_free( table->rt6 ); segment_free(table->data); segment_free(table_ptr); return NULL; } return table; } /* Free lookup table */ void sfrt_flat_free(TABLE_PTR table_ptr) { table_flat_t *table; uint8_t *base; if(!table_ptr) { /* What are you calling me for? */ return; } base = (uint8_t *)segment_basePtr(); table = (table_flat_t *)(&base[table_ptr]); if(!table->data) { /* This really really should not have happened */ } else { segment_free(table->data); } if(!table->rt) { /* This should not have happened either */ } else { sfrt_dir_flat_free( table->rt ); } if(!table->rt6) { /* This should not have happened either */ } else { sfrt_dir_flat_free( table->rt6 ); } segment_free(table_ptr); } /* Perform a lookup on value contained in "ip" */ GENERIC sfrt_flat_lookup(sfaddr_t *ip, table_flat_t *table) { tuple_flat_t tuple; uint32_t* adr; int numAdrDwords; INFO *data; TABLE_PTR rt; uint8_t *base; if(!ip) { return NULL; } if(!table) { return NULL; } if (sfaddr_family(ip) == AF_INET) { adr = sfaddr_get_ip4_ptr(ip); numAdrDwords = 1; rt = table->rt; } else { adr = sfaddr_get_ip6_ptr(ip); numAdrDwords = 4; rt = table->rt6; } tuple = sfrt_dir_flat_lookup(adr, numAdrDwords, rt); if(tuple.index >= table->num_ent) { return NULL; } base = (uint8_t *)segment_basePtr(); data = (INFO *)(&base[table->data]); if (data[tuple.index]) return (GENERIC) &base[data[tuple.index]]; else return NULL; } /* Insert "ip", of length "len", into "table", and have it point to "ptr" */ /* Insert "ip", of length "len", into "table", and have it point to "ptr" */ int sfrt_flat_insert(sfcidr_t *ip, unsigned char len, INFO ptr, int behavior, table_flat_t* table, updateEntryInfoFunc updateEntry) { int index; int res = RT_SUCCESS; INFO *data; tuple_flat_t tuple; uint32_t* adr; int numAdrDwords; TABLE_PTR rt; uint8_t *base; int64_t bytesAllocated; if(!ip) { return RT_INSERT_FAILURE; } if (len == 0) return RT_INSERT_FAILURE; if(!table || !table->data) { return RT_INSERT_FAILURE; } if(len > 128) { return RT_INSERT_FAILURE; } if (sfaddr_family(&ip->addr) == AF_INET) { if (len < 96) { return RT_INSERT_FAILURE; } len -= 96; adr = sfip_get_ip4_ptr(ip); numAdrDwords = 1; rt = table->rt; } else { adr = sfip_get_ip6_ptr(ip); numAdrDwords = 4; rt = table->rt6; } tuple = sfrt_dir_flat_lookup(adr, numAdrDwords, rt); base = (uint8_t *)segment_basePtr(); data = (INFO *)(&base[table->data]); if(tuple.length != len) { if( table->num_ent >= table->max_size) { return RT_POLICY_TABLE_EXCEEDED; } index = table->num_ent; table->num_ent++; /* Insert value into policy table */ data[index] = 0; } else { index = tuple.index; } bytesAllocated = updateEntry(&data[index], ptr, SAVE_TO_CURRENT, base); if (bytesAllocated < 0) { if(tuple.length != len) table->num_ent--; return MEM_ALLOC_FAILURE; } table->allocated += (uint32_t)bytesAllocated; /* The actual value that is looked-up is an index * into the data table. */ res = sfrt_dir_flat_insert(adr, numAdrDwords, len, index, behavior, rt, updateEntry, data); /* Check if we ran out of memory. If so, need to decrement * table->num_ent */ if(res == MEM_ALLOC_FAILURE) { /* From the control flow above, it's possible table->num_ent was not * incremented. It should be safe to decrement here, because the only * time it will be incremented above is when we are potentially * mallocing one or more new entries (It's not incremented when we * overwrite an existing entry). */ table->num_ent--; } return res; } uint32_t sfrt_flat_num_entries(table_flat_t* table) { if(!table) { return 0; } if( !table->rt || !table->allocated) { return 0; } /* There is always a root node, so subtract 1 for it */ return table->num_ent - 1; } uint32_t sfrt_flat_usage(table_flat_t *table) { uint32_t usage; if(!table || !table->rt || !table->allocated ) { return 0; } usage = table->allocated + sfrt_dir_flat_usage( table->rt ); if (table->rt6) { usage += sfrt_dir_flat_usage( table->rt6 ); } return usage; } snort-2.9.20/src/sfutil/sfhashfcn.c0000644000175000017500000000656314241077266015320 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* sfhashfcn.c Each hash table must allocate it's own SFGHASH struct, this is because sfghash_new uses the number of rows in the hash table to modulo the random values. Updates: 8/31/2006 - man - changed to use sfprimetable.c */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #ifndef MODULUS_HASH # include "snort.h" #endif #include "sfhashfcn.h" #include "sfprimetable.h" SFHASHFCN * sfhashfcn_new( int m ) { SFHASHFCN * p; static int one=1; if( one ) /* one time init */ { srand( (unsigned) time(0) ); one = 0; } // This can make all of the hashing static for testing. //#define rand() 0 p = (SFHASHFCN*) calloc( 1,sizeof(SFHASHFCN) ); if( !p ) return 0; #ifndef MODULUS_HASH #ifndef DYNAMIC_PREPROC_CONTEXT if (ScStaticHash()) { sfhashfcn_static(p); } else #endif #endif { p->seed = sf_nearest_prime( (rand()%m)+3191 ); p->scale = sf_nearest_prime( (rand()%m)+709 ); p->hardener = (rand()*rand()) + 133824503; } p->hash_fcn = &sfhashfcn_hash; p->keycmp_fcn = &memcmp; return p; } void sfhashfcn_free( SFHASHFCN * p ) { if( p ) { free( p); } } void sfhashfcn_static( SFHASHFCN * p ) { p->seed = 3193; p->scale = 719; p->hardener = 133824503; } unsigned sfhashfcn_hash( SFHASHFCN * p, unsigned char *d, int n ) { unsigned hash = p->seed; while( n ) { hash *= p->scale; hash += *d++; n--; } return hash ^ p->hardener; } /** * Make sfhashfcn use a separate set of operators for the backend. * * @param h sfhashfcn ptr * @param hash_fcn user specified hash function * @param keycmp_fcn user specified key comparisoin function */ int sfhashfcn_set_keyops( SFHASHFCN *h, unsigned (*hash_fcn)( SFHASHFCN * p, unsigned char *d, int n), int (*keycmp_fcn)( const void *s1, const void *s2, size_t n)) { if(h && hash_fcn && keycmp_fcn) { h->hash_fcn = hash_fcn; h->keycmp_fcn = keycmp_fcn; return 0; } return -1; } snort-2.9.20/src/sfutil/sfrim.c0000644000175000017500000000653214241077316014465 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * sfrim.c * * Rule Index Map * * author: marc norton * */ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sfrim.h" /* * Return Sid associated with index * author: marc norton */ unsigned RuleIndexMapSid( rule_index_map_t * map, int index ) { if( ! map ) return 0; if( index < map->num_rules ) { return map->map[index].sid; } return 0; } /* * Return Gid associated with index * author: marc norton */ unsigned RuleIndexMapGid(rule_index_map_t * map, int index ) { if( ! map ) { return 0; } if( index < map->num_rules ) { return map->map[index].gid; } return 0; } /* * Create a rule index map table * author: marc norton */ rule_index_map_t * RuleIndexMapCreate( int max_rules ) { rule_index_map_t *p = calloc( 1, sizeof(rule_index_map_t) ); if(!p) { return 0; } p->max_rules=max_rules; p->num_rules=0; p->map = calloc( max_rules, sizeof(rule_number_t)); if(!p->map ) { free(p); return 0; } return p; } /* * Free a rule index map table * author: marc norton */ void RuleIndexMapFree( rule_index_map_t ** p ) { if( !p || !*p ) { return ; } if( (*p)->map ) { free((*p)->map); } free( *p ); *p = 0; } /* * Add a rule to a rule index map table * author: marc norton */ int RuleIndexMapAdd( rule_index_map_t * p, unsigned gid, unsigned sid ) { int index; if( !p ) { return -1; } if( p->num_rules == (p->max_rules - 1) ) { return -1; } index = p->num_rules ; p->map[ index ].gid = gid; p->map[ index ].sid = sid; p->num_rules++; //printf("RuleIndexMapping: index=%d gid=%u sid=%u\n",index,gid,sid); return index; } /* * print a rule index map table to stdout * author: marc norton */ void print_rule_index_map( rule_index_map_t * p ) { int i; printf("***\n*** Rule Index Map (%d entries)\n***\n",p->num_rules); for(i=0;inum_rules;i++) { printf("rule-index-map[%d] { gid:%u sid:%u }\n",i,p->map[i].gid,p->map[i].sid); } printf("***end rule index map ***\n"); } snort-2.9.20/src/sfutil/sf_sechash.h0000644000175000017500000000723514241077250015456 0ustar apoapo /* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __SF_SECHASH_H__ #define __SF_SECHASH_H__ /* D E F I N E S *******************************************************/ #define SHA256_HASH_SIZE 32 #define SHA512_HASH_SIZE 64 #define MD5_HASH_SIZE 16 /* I N C L U D E S *****************************************************/ #include "config.h" #include #ifdef HAVE_OPENSSL_SHA #include #define SHA256CONTEXT SHA256_CTX #define SHA512CONTEXT SHA512_CTX #define SHA256INIT SHA256_Init #define SHA256UPDATE SHA256_Update #define SHA256FINAL SHA256_Final #define SHA256DIGEST SHA256 #define SHA512INIT SHA512_Init #define SHA512UPDATE SHA512_Update #define SHA512FINAL SHA512_Final #define SHA512DIGEST SHA512 #else #include "sha2.h" #define SHA256CONTEXT SHA256_CTX #define SHA512CONTEXT SHA512_CTX #define SHA256INIT SHA256_Init #define SHA256UPDATE SHA256_Update #define SHA256FINAL SHA256_Final #define SHA256DIGEST SHA256 #define SHA512INIT SHA512_Init #define SHA512UPDATE SHA512_Update #define SHA512FINAL SHA512_Final #define SHA512DIGEST SHA512 static inline unsigned char *SHA256(const unsigned char *data, size_t size, unsigned char *digest){ static unsigned char d[SHA256_HASH_SIZE]; SHA256CONTEXT c; if(!digest) digest = d; SHA256INIT(&c); SHA256UPDATE(&c, data, size); SHA256FINAL(digest, &c); return digest; } static inline unsigned char *SHA512(const unsigned char *data, size_t size, unsigned char *digest){ static unsigned char d[SHA512_HASH_SIZE]; SHA512CONTEXT c; if(!digest) digest = d; SHA512INIT(&c); SHA512UPDATE(&c, data, size); SHA512FINAL(digest, &c); return digest; } #endif /* HAVE_OPENSSL_SHA */ #ifdef HAVE_OPENSSL_MD5 #include #define MD5CONTEXT MD5_CTX #define MD5INIT MD5_Init #define MD5UPDATE MD5_Update #define MD5FINAL MD5_Final #define MD5DIGEST MD5 #else #include "md5.h" #define MD5CONTEXT struct MD5Context #define MD5INIT MD5Init #define MD5UPDATE MD5Update #define MD5FINAL MD5Final #define MD5DIGEST MD5 static inline unsigned char *MD5(const unsigned char *data, size_t size, unsigned char *digest){ static unsigned char d[MD5_HASH_SIZE]; MD5CONTEXT c; if(!digest) digest = d; MD5INIT(&c); MD5UPDATE(&c, data, size); MD5FINAL(digest, &c); return digest; } #endif /* HAVE_OPENSSL_MD5 */ /* A set of secure hash types */ typedef enum { SECHASH_NONE = 0, SECHASH_SHA256 = 1, SECHASH_SHA512, SECHASH_MD5 } Secure_Hash_Type; /* P R O T O T Y P E S *************************************************/ unsigned int SecHash_Type2Length( const Secure_Hash_Type type ); Secure_Hash_Type SecHash_Name2Type( const char *name ); #endif /* __SF_SECHASH_H__ */ snort-2.9.20/src/sfutil/mpse.c0000644000175000017500000004572014241077226014313 0ustar apoapo/* * $Id$ * * mpse.c * * An abstracted interface to the Multi-Pattern Matching routines, * thats why we're passing 'void *' objects around. * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2002-2013 Sourcefire, Inc. * Marc A Norton * * Updates: * 3/06 - Added AC_BNFA search ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "bitop.h" #include "bnfa_search.h" #include "acsmx.h" #include "acsmx2.h" #include "sfksearch.h" #include "mpse.h" #include "snort_debug.h" #include "sf_types.h" #include "util.h" #ifdef DYNAMIC_PREPROC_CONTEXT #include "sf_dynamic_preprocessor.h" #endif //DYNAMIC_PREPROC_CONTEXT #ifdef INTEL_SOFT_CPM #include "intel-soft-cpm.h" #endif #include "profiler.h" #include "snort.h" #ifdef PERF_PROFILING PreprocStats mpsePerfStats; #endif static uint64_t s_bcnt=0; typedef struct _mpse_struct { int method; void * obj; int verbose; uint64_t bcnt; char inc_global_counter; } MPSE; void * mpseNew( int method, int use_global_counter_flag, void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)) { MPSE * p; p = (MPSE*)calloc( 1,sizeof(MPSE) ); if( !p ) return NULL; p->method = method; p->verbose = 0; p->obj = NULL; p->bcnt = 0; p->inc_global_counter = (char)use_global_counter_flag; switch( method ) { case MPSE_AC_BNFA: p->obj=bnfaNew(userfree, optiontreefree, neg_list_free); if(p->obj) ((bnfa_struct_t*)(p->obj))->bnfaMethod = 1; break; case MPSE_AC_BNFA_Q: p->obj=bnfaNew(userfree, optiontreefree, neg_list_free); if(p->obj) ((bnfa_struct_t*)(p->obj))->bnfaMethod = 0; break; case MPSE_AC: p->obj = acsmNew(userfree, optiontreefree, neg_list_free); break; case MPSE_ACF: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_FULL ); break; case MPSE_ACF_Q: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_FULLQ ); break; case MPSE_ACS: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_SPARSE ); break; case MPSE_ACB: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_BANDED ); break; case MPSE_ACSB: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_SPARSEBANDS ); break; case MPSE_LOWMEM: p->obj = KTrieNew(0,userfree, optiontreefree, neg_list_free); break; case MPSE_LOWMEM_Q: p->obj = KTrieNew(1,userfree, optiontreefree, neg_list_free); break; default: /* p is free'd below if no case */ break; } if( !p->obj ) { free(p); p = NULL; } return (void *)p; } #ifndef DYNAMIC_PREPROC_CONTEXT void * mpseNewWithSnortConfig( struct _SnortConfig *sc, int method, int use_global_counter_flag, void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)) { MPSE * p; p = (MPSE*)calloc( 1,sizeof(MPSE) ); if( !p ) return NULL; p->method = method; p->verbose = 0; p->obj = NULL; p->bcnt = 0; p->inc_global_counter = (char)use_global_counter_flag; switch( method ) { case MPSE_AC_BNFA: p->obj=bnfaNew(userfree, optiontreefree, neg_list_free); if(p->obj) ((bnfa_struct_t*)(p->obj))->bnfaMethod = 1; break; case MPSE_AC_BNFA_Q: p->obj=bnfaNew(userfree, optiontreefree, neg_list_free); if(p->obj) ((bnfa_struct_t*)(p->obj))->bnfaMethod = 0; break; case MPSE_AC: p->obj = acsmNew(userfree, optiontreefree, neg_list_free); break; case MPSE_ACF: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_FULL ); break; case MPSE_ACF_Q: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_FULLQ ); break; case MPSE_ACS: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_SPARSE ); break; case MPSE_ACB: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_BANDED ); break; case MPSE_ACSB: p->obj = acsmNew2(userfree, optiontreefree, neg_list_free); if(p->obj)acsmSelectFormat2((ACSM_STRUCT2*)p->obj,ACF_SPARSEBANDS ); break; case MPSE_LOWMEM: p->obj = KTrieNew(0,userfree, optiontreefree, neg_list_free); break; case MPSE_LOWMEM_Q: p->obj = KTrieNew(1,userfree, optiontreefree, neg_list_free); break; #ifdef INTEL_SOFT_CPM case MPSE_INTEL_CPM: p->obj=IntelPmNew(sc, userfree, optiontreefree, neg_list_free); break; #endif default: /* p is free'd below if no case */ break; } if( !p->obj ) { free(p); p = NULL; } return (void *)p; } #endif //DYNAMIC_PREPROC_CONTEXT void mpseVerbose( void * pvoid ) { MPSE * p = (MPSE*)pvoid; p->verbose = 1; } void mpseSetOpt( void * pvoid, int flag ) { MPSE * p = (MPSE*)pvoid; if (p == NULL) return; switch( p->method ) { case MPSE_AC_BNFA_Q: case MPSE_AC_BNFA: if (p->obj) bnfaSetOpt((bnfa_struct_t*)p->obj,flag); break; case MPSE_ACF: case MPSE_ACF_Q: if (p->obj) acsmCompressStates((ACSM_STRUCT2*)p->obj, flag); break; default: break; } } void mpseFree( void * pvoid ) { MPSE * p = (MPSE*)pvoid; if (p == NULL) return; switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: if (p->obj) bnfaFree((bnfa_struct_t*)p->obj); free(p); return; case MPSE_AC: if (p->obj) acsmFree((ACSM_STRUCT *)p->obj); free(p); return; case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: if (p->obj) acsmFree2((ACSM_STRUCT2 *)p->obj); free(p); return; case MPSE_LOWMEM: case MPSE_LOWMEM_Q: if (p->obj) KTrieDelete((KTRIE_STRUCT *)p->obj); free(p); return; #ifdef INTEL_SOFT_CPM case MPSE_INTEL_CPM: if (p->obj) IntelPmDelete((IntelPm *)p->obj); free(p); break; #endif default: return; } } int mpseAddPattern ( void * pvoid, void * P, int m, unsigned noCase, unsigned offset, unsigned depth, unsigned negative, void* ID, int IID ) { MPSE * p = (MPSE*)pvoid; switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: return bnfaAddPattern( (bnfa_struct_t*)p->obj, (unsigned char *)P, m, noCase, negative, ID ); case MPSE_AC: return acsmAddPattern( (ACSM_STRUCT*)p->obj, (unsigned char *)P, m, noCase, offset, depth, negative, ID, IID ); case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: return acsmAddPattern2( (ACSM_STRUCT2*)p->obj, (unsigned char *)P, m, noCase, offset, depth, negative, ID, IID ); case MPSE_LOWMEM: case MPSE_LOWMEM_Q: return KTrieAddPattern( (KTRIE_STRUCT *)p->obj, (unsigned char *)P, m, noCase, negative, ID ); default: return -1; } } #ifndef DYNAMIC_PREPROC_CONTEXT int mpseAddPatternWithSnortConfig ( SnortConfig *sc, void * pvoid, void * P, int m, unsigned noCase, unsigned offset, unsigned depth, unsigned negative, void* ID, int IID ) { MPSE * p = (MPSE*)pvoid; switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: return bnfaAddPattern( (bnfa_struct_t*)p->obj, (unsigned char *)P, m, noCase, negative, ID ); case MPSE_AC: return acsmAddPattern( (ACSM_STRUCT*)p->obj, (unsigned char *)P, m, noCase, offset, depth, negative, ID, IID ); case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: return acsmAddPattern2( (ACSM_STRUCT2*)p->obj, (unsigned char *)P, m, noCase, offset, depth, negative, ID, IID ); case MPSE_LOWMEM: case MPSE_LOWMEM_Q: return KTrieAddPattern( (KTRIE_STRUCT *)p->obj, (unsigned char *)P, m, noCase, negative, ID ); #ifdef INTEL_SOFT_CPM case MPSE_INTEL_CPM: return IntelPmAddPattern(sc, (IntelPm *)p->obj, (unsigned char *)P, m, noCase, negative, ID, IID); #endif default: return -1; } } #endif // DYNAMIC_PREPROC_CONTEXT void mpseLargeShifts ( void * pvoid, int flag ) { MPSE * p = (MPSE*)pvoid; switch( p->method ) { default: return ; } } int mpsePrepPatterns ( void * pvoid, int ( *build_tree )(void *id, void **existing_tree), int ( *neg_list_func )(void *id, void **list) ) { int retv; MPSE * p = (MPSE*)pvoid; switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: retv = bnfaCompile( (bnfa_struct_t*) p->obj, build_tree, neg_list_func ); break; case MPSE_AC: retv = acsmCompile( (ACSM_STRUCT*) p->obj, build_tree, neg_list_func ); break; case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: retv = acsmCompile2( (ACSM_STRUCT2*) p->obj, build_tree, neg_list_func ); break; case MPSE_LOWMEM: case MPSE_LOWMEM_Q: return KTrieCompile( (KTRIE_STRUCT *)p->obj, build_tree, neg_list_func ); default: retv = 1; break; } return retv; } #ifndef DYNAMIC_PREPROC_CONTEXT int mpsePrepPatternsWithSnortConf ( struct _SnortConfig *sc, void * pvoid, int ( *build_tree )(struct _SnortConfig *, void *id, void **existing_tree), int ( *neg_list_func )(void *id, void **list) ) { int retv; MPSE * p = (MPSE*)pvoid; switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: retv = bnfaCompileWithSnortConf( sc, (bnfa_struct_t*) p->obj, build_tree, neg_list_func ); break; case MPSE_AC: retv = acsmCompileWithSnortConf( sc, (ACSM_STRUCT*) p->obj, build_tree, neg_list_func ); break; case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: retv = acsmCompile2WithSnortConf( sc, (ACSM_STRUCT2*) p->obj, build_tree, neg_list_func ); break; case MPSE_LOWMEM: case MPSE_LOWMEM_Q: return KTrieCompileWithSnortConf( sc, (KTRIE_STRUCT *)p->obj, build_tree, neg_list_func ); #ifdef INTEL_SOFT_CPM case MPSE_INTEL_CPM: return IntelPmFinishGroup(sc, (IntelPm *)p->obj, build_tree, neg_list_func); #endif default: retv = 1; break; } return retv; } #endif //DYNAMIC_PREPROC_CONTEXT void mpseSetRuleMask ( void *pvoid, BITOP * rm ) { MPSE * p = (MPSE*)pvoid; switch( p->method ) { default: return ; } } int mpsePrintInfo( void *pvoid ) { MPSE * p = (MPSE*)pvoid; fflush(stderr); fflush(stdout); switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: bnfaPrintInfo( (bnfa_struct_t*) p->obj ); break; case MPSE_AC: return acsmPrintDetailInfo( (ACSM_STRUCT*) p->obj ); case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: return acsmPrintDetailInfo2( (ACSM_STRUCT2*) p->obj ); default: return 1; } fflush(stderr); fflush(stdout); return 0; } int mpsePrintSummary(int method) { switch (method) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: bnfaPrintSummary(); break; case MPSE_AC: acsmPrintSummaryInfo(); break; case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: acsmPrintSummaryInfo2(); break; case MPSE_LOWMEM: case MPSE_LOWMEM_Q: if( KTrieMemUsed() ) { double x; x = (double) KTrieMemUsed(); LogMessage("[ LowMem Search-Method Memory Used : %g %s ]\n", (x > 1.e+6) ? x/1.e+6 : x/1.e+3, (x > 1.e+6) ? "MBytes" : "KBytes" ); } break; default: break; } return 0; } #ifndef DYNAMIC_PREPROC_CONTEXT int mpsePrintSummaryWithSnortConfig(SnortConfig *sc, int method) { switch (method) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: bnfaPrintSummary(); break; case MPSE_AC: acsmPrintSummaryInfo(); break; case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: acsmPrintSummaryInfo2(); break; case MPSE_LOWMEM: case MPSE_LOWMEM_Q: if( KTrieMemUsed() ) { double x; x = (double) KTrieMemUsed(); LogMessage("[ LowMem Search-Method Memory Used : %g %s ]\n", (x > 1.e+6) ? x/1.e+6 : x/1.e+3, (x > 1.e+6) ? "MBytes" : "KBytes" ); } break; default: break; } #ifdef INTEL_SOFT_CPM IntelPmPrintSummary(sc); #endif return 0; } #endif //DYNAMIC_PREPROC_CONTEXT void mpseInitSummary(void) { acsm_init_summary(); bnfaInitSummary(); KTrieInitMemUsed(); } int mpseSearch( void *pvoid, const unsigned char * T, int n, int ( *action )(void* id, void * tree, int index, void *data, void *neg_list), void * data, int* current_state ) { MPSE * p = (MPSE*)pvoid; int ret; PROFILE_VARS; PREPROC_PROFILE_START(mpsePerfStats); p->bcnt += n; if(p->inc_global_counter) s_bcnt += n; switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: /* return is actually the state */ ret = bnfaSearch((bnfa_struct_t*) p->obj, (unsigned char *)T, n, action, data, 0 /* start-state */, current_state ); PREPROC_PROFILE_END(mpsePerfStats); return ret; case MPSE_AC: ret = acsmSearch( (ACSM_STRUCT*) p->obj, (unsigned char *)T, n, action, data, current_state ); PREPROC_PROFILE_END(mpsePerfStats); return ret; case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: ret = acsmSearch2( (ACSM_STRUCT2*) p->obj, (unsigned char *)T, n, action, data, current_state ); PREPROC_PROFILE_END(mpsePerfStats); return ret; case MPSE_LOWMEM: case MPSE_LOWMEM_Q: ret = KTrieSearch( (KTRIE_STRUCT *)p->obj, (unsigned char *)T, n, action, data); *current_state = 0; PREPROC_PROFILE_END(mpsePerfStats); return ret; #ifdef INTEL_SOFT_CPM case MPSE_INTEL_CPM: ret = IntelPmSearch((IntelPm *)p->obj, (unsigned char *)T, n, action, data); *current_state = 0; PREPROC_PROFILE_END(mpsePerfStats); return ret; #endif default: PREPROC_PROFILE_END(mpsePerfStats); return 1; } } int mpseSearchAll( void *pvoid, const unsigned char * T, int n, int ( *action )(void* id, void * tree, int index, void *data, void *neg_list), void * data, int* current_state ) { MPSE * p = (MPSE*)pvoid; int ret; PROFILE_VARS; PREPROC_PROFILE_START(mpsePerfStats); p->bcnt += n; if(p->inc_global_counter) s_bcnt += n; switch( p->method ) { case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: ret = acsmSearchAll2( (ACSM_STRUCT2*) p->obj, (unsigned char *)T, n, action, data, current_state ); PREPROC_PROFILE_END(mpsePerfStats); return ret; case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: case MPSE_AC: case MPSE_LOWMEM: case MPSE_LOWMEM_Q: #ifdef INTEL_SOFT_CPM case MPSE_INTEL_CPM: #endif default: //search all not implemented. PREPROC_PROFILE_END(mpsePerfStats); return 1; } } int mpseGetPatternCount(void *pvoid) { MPSE * p = (MPSE*)pvoid; if (p == NULL) return 0; switch( p->method ) { case MPSE_AC_BNFA: case MPSE_AC_BNFA_Q: return bnfaPatternCount((bnfa_struct_t *)p->obj); case MPSE_AC: return acsmPatternCount((ACSM_STRUCT*)p->obj); case MPSE_ACF: case MPSE_ACF_Q: case MPSE_ACS: case MPSE_ACB: case MPSE_ACSB: return acsmPatternCount2((ACSM_STRUCT2*)p->obj); case MPSE_LOWMEM: case MPSE_LOWMEM_Q: return KTriePatternCount((KTRIE_STRUCT*)p->obj); #ifdef INTEL_SOFT_CPM case MPSE_INTEL_CPM: return IntelGetPatternCount((IntelPm *)p->obj); #endif } return 0; } uint64_t mpseGetPatByteCount(void) { return s_bcnt; } void mpseResetByteCount(void) { s_bcnt = 0; } void mpse_print_qinfo(void) { sfksearch_print_qinfo(); bnfa_print_qinfo(); acsmx2_print_qinfo(); } snort-2.9.20/src/sfutil/sflsq.h0000644000175000017500000001076014241077273014502 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * sflsq.h * * Simple LIST, STACK, QUEUE DICTIONARY(LIST BASED)interface * * All of these functions are based on lists, which use * the standard malloc. * * Note that NODE_DATA can be redifined with the * define below. * * Author: Marc Norton */ #ifndef _SFLSQ_ #define _SFLSQ_ /* * */ typedef void * NODE_DATA; /* * Simple list,stack or queue NODE */ typedef struct sf_lnode { struct sf_lnode *next; struct sf_lnode *prev; NODE_DATA ndata; } SF_QNODE,SF_SNODE,SF_LNODE; #define SFLIST_NODE_TO_DATA(node) (node)->ndata /* * Integer Stack - uses an array from the subroutines stack */ typedef struct { unsigned *stack; unsigned nstack; unsigned n; } SF_ISTACK; /* * Pointer Stack - uses an array from the subroutines stack */ typedef struct { void **stack; unsigned nstack; unsigned n; } SF_PSTACK; /* * Simple Structure for Queue's, stacks, lists */ typedef struct sf_list { SF_LNODE *head, *tail; SF_LNODE *cur; /* used for First/Next walking */ unsigned count; } SF_QUEUE,SF_STACK,SF_LIST; /* * Linked List Interface */ SF_LIST * sflist_new ( void ); void sflist_init ( SF_LIST * s); int sflist_add_tail ( SF_LIST* s, NODE_DATA ndata ); int sflist_add_head ( SF_LIST* s, NODE_DATA ndata ); int sflist_add_before ( SF_LIST* s, SF_LNODE * lnode, NODE_DATA ndata ); int sflist_add_after ( SF_LIST* s, SF_LNODE * lnode, NODE_DATA ndata ); NODE_DATA sflist_remove_head ( SF_LIST * s); NODE_DATA sflist_remove_tail ( SF_LIST * s); void sflist_remove_node (SF_LIST * s, SF_LNODE * n); int sflist_count ( SF_LIST* s); NODE_DATA sflist_first( SF_LIST * s); NODE_DATA sflist_next( SF_LIST * s); SF_LNODE * sflist_first_node( SF_LIST * s ); SF_LNODE * sflist_next_node( SF_LIST * s ); NODE_DATA sflist_firstpos( SF_LIST * s, SF_LNODE ** v ); NODE_DATA sflist_nextpos ( SF_LIST * s, SF_LNODE ** v ); void sflist_free ( SF_LIST * s); void sflist_free_all( SF_LIST * s, void (*free)(void*) ); void sflist_static_free_all(SF_LIST *, void(*nfree)(void *)); void sflist_static_free(SF_LIST *); /* * Stack Interface ( LIFO - Last in, First out ) */ SF_STACK *sfstack_new ( void ); int sfstack_add( SF_STACK* s, NODE_DATA ndata ); NODE_DATA sfstack_remove ( SF_STACK * s); int sfstack_count ( SF_STACK * s); void sfstack_free ( SF_STACK * s); void sfstack_free_all( SF_STACK* s, void (*free)(void*) ); void sfstack_static_free_all(SF_STACK *,void (*nfree)(void *)); void sfstack_static_free(SF_STACK *); /* * Queue Interface ( FIFO - First in, First out ) */ SF_QUEUE *sfqueue_new ( void ); int sfqueue_add( SF_QUEUE * s, NODE_DATA ndata ); NODE_DATA sfqueue_remove ( SF_QUEUE * s); int sfqueue_count ( SF_QUEUE * s); void sfqueue_free ( SF_QUEUE * s); void sfqueue_free_all( SF_QUEUE* s, void (*free)(void*) ); void sfqueue_static_free_all(SF_QUEUE *,void (*nfree)(void *)); void sfqueue_static_free(SF_QUEUE *); /* * Performance Stack functions for Integer/Unsigned and Pointers, uses * user provided array storage, perhaps from the program stack or a global. * These are efficient, and use no memory functions. */ int sfistack_init( SF_ISTACK * s, unsigned * a, unsigned n ); int sfistack_push( SF_ISTACK *s, unsigned value); int sfistack_pop( SF_ISTACK *s, unsigned * value); int sfpstack_init( SF_PSTACK * s, void ** a, unsigned n ); int sfpstack_push( SF_PSTACK *s, void * value); int sfpstack_pop( SF_PSTACK *s, void ** value); #endif snort-2.9.20/src/sfutil/sfghash.h0000644000175000017500000000645114241077265015000 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * * sfghash.h * * generic hash table - stores and maps key + data pairs * * Author: Marc Norton * */ #ifndef _SFGHASH_ #define _SFGHASH_ #include #include #include #include "sfhashfcn.h" /* * ERROR DEFINES */ #define SFGHASH_NOMEM -2 #define SFGHASH_ERR -1 #define SFGHASH_OK 0 #define SFGHASH_INTABLE 1 /* * Flags for ghash_new: userkeys */ #define GH_COPYKEYS 0 #define GH_USERKEYS 1 /* * Generic HASH NODE */ typedef struct _sfghash_node { struct _sfghash_node * next, * prev; const void * key; /* Copy of, or Pointer to, the Users key */ void *data; /* The users data, this is never copied! */ } SFGHASH_NODE; /* * Generic HASH table */ typedef struct _sfghash { SFHASHFCN * sfhashfcn; int keysize; /* bytes in key, if < 0 -> keys are strings */ int userkey; /* user owns the key */ SFGHASH_NODE ** table; /* array of node ptr's */ int nrows; /* # rows int the hash table use a prime number 211, 9871 */ unsigned count; /* total # nodes in table */ void (*userfree)( void * ); int crow; /* findfirst/next row in table */ SFGHASH_NODE * cnode; /* findfirst/next node ptr */ int splay; } SFGHASH, SFDICT; /* * HASH PROTOTYPES */ SFGHASH * sfghash_new( int nrows, int keysize, int userkeys, void (*userfree)(void*p) ); void sfghash_delete( SFGHASH * h ); int sfghash_add( SFGHASH * t, const void * const key, void * const data ); int sfghash_remove( SFGHASH * h, const void * const key); int sfghash_count( SFGHASH * h); void * sfghash_find( SFGHASH * h, const void * const key ); SFGHASH_NODE * sfghash_find_node( SFGHASH * t, const void * const key); int sfghash_find2(SFGHASH *, void *, void **); SFGHASH_NODE * sfghash_findfirst( SFGHASH * h ); SFGHASH_NODE * sfghash_findnext ( SFGHASH * h ); int sfghash_set_keyops( SFGHASH *h , unsigned (*hash_fcn)( SFHASHFCN * p, unsigned char *d, int n), int (*keycmp_fcn)( const void *s1, const void *s2, size_t n)); #endif snort-2.9.20/src/sfutil/intel-soft-cpm.h0000644000175000017500000000677514241077220016216 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ***************************************************************************/ #ifndef _INTEL_H_ #define _INTEL_H_ #include "cpa.h" #include "pm/cpa_pm.h" #include "cpa_types.h" #include "snort_debug.h" /* DATA TYPES *****************************************************************/ typedef struct _IntelPmPattern { void *user_data; void *rule_option_tree; void *neg_list; unsigned char *pattern; unsigned int pattern_len; unsigned int no_case; unsigned int negative; int id; /* pattern id passed in from mpse */ Cpa32U patternId; /* actual pattern id */ } IntelPmPattern; struct _SnortConfig; struct _IntelPmHandles; typedef struct _IntelPm { Cpa16U patternGroupId; Cpa32U patternIds; CpaPmSessionCtx sessionCtx; /* Temporary data for building trees */ int (*build_tree)(struct _SnortConfig *, void *id, void **existing_tree); int (*neg_list_func)(void *id, void **list); void *match_queue; /* Temporary data for match callback */ void *data; int (*match)(void *id, void *tree, int index, void *data, void *neg_list); void (*user_free)(void *); void (*option_tree_free)(void **); void (*neg_list_free)(void **); IntelPmPattern *pattern_array; Cpa32U pattern_array_len; /* Every IntelPm has a reference to this */ struct _IntelPmHandles *handles; } IntelPm; /* PROTOTYPES *****************************************************************/ void IntelPmStartInstance(void); void IntelPmStopInstance(void); void * IntelPmNew(struct _SnortConfig *, void (*user_free)(void *p), void (*option_tree_free)(void **p), void (*neg_list_free)(void **p)); void IntelPmDelete(IntelPm *ipm); int IntelPmAddPattern(struct _SnortConfig *sc, IntelPm *ipm, unsigned char *pat, int pat_len, unsigned no_case, unsigned negative, void *pat_data, int pat_id); int IntelPmFinishGroup(struct _SnortConfig *, IntelPm *ipm, int (*build_tree)(struct _SnortConfig *, void *id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); void IntelPmCompile(struct _SnortConfig *); void IntelPmActivate(struct _SnortConfig *); void IntelPmDeactivate(void); int IntelPmSearch(IntelPm *ipm, unsigned char *buffer, int buffer_len, int (*match)(void * id, void *tree, int index, void *data, void *neg_list), void *data); int IntelGetPatternCount(IntelPm *ipm); int IntelPmPrintInfo(IntelPm *ipm); void IntelPmPrintSummary(struct _SnortConfig *); void IntelPmPrintBufferStats(void); int IntelPmRelease(struct _IntelPmHandles *); #endif /* _INTEL_H_ */ snort-2.9.20/src/sfutil/sfxhash.c0000644000175000017500000011040214241077340014776 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*!@file sfxhash.c * * A Customized hash table library for storing and accessing key + data pairs. * * This table incorporates a memory manager (memcap.c) to provide a memory cap, * and an automatic node recovery system for out of memory management. Keys and * Data are copied into the hash table during the add operation. The data may * be allocated and free'd by the user (by setting the datasize to zero ). A * user callback is provided to allow the user to do cleanup whenever a node * is released, by either the ANR system or the relase() function. * * Users can and should delete nodes when they know they are not needed anymore, * but this custom table is designed for the case where nodes are allocated * permanently, we have to limit memory, and we wish to recycle old nodes. * Many problems have a natural node ageing paradigm working in our favor, * so automated node aging makes sense. i.e. thresholding, tcp state. * * This hash table maps keys to data. All keys must be unique. * Uniqueness is enforcedby the code. * * Features: * * 1) Keys must be fixed length (per table) binary byte sequences. * keys are copied during the add function * 2) Data must be fixed length (per table) binary byte sequences. * data is copied during the add function - if datasize > 0 * Data may be managed by the user as well. * 3) Table row sizes can be automatically adjusted to * the nearest prime number size during table initialization/creation. * 4) Memory management includes tracking the size of each allocation, * number of allocations, enforcing a memory cap, and automatic node * recovery - when memory is low the oldest untouched node * is unlinked and recycled for use as a new node. * * Per Node Memory Usage: * ---------------------- * SFXHASH_NODE bytes * KEYSIZE bytes * [DATASIZE bytes] if datasize > 0 during call to sfxhash_new. * * The hash node memory (sfxhash_node,key,and data) is allocated with * one call to s_alloc/memcap_alloc. * * Author: Marc Norton * * 2003-06-03: cmg - added sfxhash_{l,m}ru to return {least,most} * recently used node from the global list * * - added _anrcount function * - changed count function to return unsigned to match structure * * 2003-06-11: cmg added * overhead_bytes + blocks to separate out the * memcap constraints from the hash table itself * find success v fail * * 2003-06-19: cmg added * * ability to set own hash function * ability to set own key cmp function * * 2003-06-30: rdempster * fixed bug in that would anr from the freelist * * 2005-11-15: modified sfxhash_add to check if 'data' is zero before memcpy'ing. * this allows user to pass null for data, and set up the data area * themselves after the call - this is much more flexible. * 8/31/2006: man - changed to use prime table lookup. */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "sfxhash.h" #include "sfprimetable.h" /**@defgroup sfxhash sourcefire.container.sfxhash * Implements SFXHASH as specialized hash container * @{ */ /* * Private Malloc - abstract the memory system */ static inline void * s_alloc( SFXHASH * t, int n ) { return sfmemcap_alloc( &t->mc, n ); } static inline void s_free( SFXHASH * t, void * p ) { sfmemcap_free( &t->mc, p ); } /* * User access to the memory management, do they need it ? WaitAndSee */ void * sfxhash_alloc( SFXHASH * t, unsigned nbytes ) { return s_alloc( t, nbytes ); } void sfxhash_free( SFXHASH * t, void * p ) { s_free( t, p ); } static int sfxhash_nearest_powerof2(int nrows) { unsigned i; nrows -= 1; for(i=1; i> i); nrows += 1; return nrows; } int sfxhash_calcrows(int num) { return sfxhash_nearest_powerof2(num); // return sf_nearest_prime( nrows ); } /*! * * Create a new hash table * * By default, this will "splay" nodes to the top of a free list. * * @param nrows number of rows in hash table * @param keysize key size in bytes, same for all keys * @param datasize datasize in bytes, zero indicates user manages data * @param maxmem maximum memory to use in bytes * @param anr_flag Automatic Node Recovery boolean flag * @param anrfree users Automatic Node Recovery memory release function * @param usrfree users standard memory release function * * @return SFXHASH* * @retval 0 out of memory * @retval !0 Valid SFXHASH pointer * */ /* Notes: if nrows < 0 don't cal the nearest powerof2. datasize must be the same for all entries, unless datasize is zero. maxmem of 0 indicates no memory limits. */ SFXHASH * sfxhash_new( unsigned nrows, int keysize, int datasize, unsigned long maxmem, int anr_flag, SFXHASH_FREE_FCN anrfree, SFXHASH_FREE_FCN usrfree, int recycle_flag ) { unsigned int i; SFXHASH * h; /* If nrows is not a power of two, need to find the * next highest power of two */ nrows = sfxhash_nearest_powerof2(nrows); /* Allocate the table structure from general memory */ h = (SFXHASH*)calloc(1, sizeof(SFXHASH)); if( !h ) { return 0; } /* this has a default hashing function */ h->sfhashfcn = sfhashfcn_new( nrows ); if( !h->sfhashfcn ) { free(h); return 0; } sfmemcap_init( &h->mc, maxmem ); /* Allocate the array of node ptrs */ h->table = (SFXHASH_NODE**) s_alloc( h, sizeof(SFXHASH_NODE*) * nrows ); if( !h->table ) { free(h->sfhashfcn); free(h); return 0; } for( i=0; itable[i] = 0; } h->anrfree = anrfree; h->usrfree = usrfree; h->keysize = keysize; #ifdef WORDS_MUSTALIGN if ((h->keysize) & 7) h->pad = (8 - ((h->keysize) & 7)); #else h->pad = 0; #endif h->datasize = datasize; h->nrows = nrows; h->max_nodes = 0; h->crow = 0; h->cnode = 0; h->count = 0; h->ghead = 0; h->gtail = 0; h->anr_count= 0; h->anr_tries= 0; h->anr_flag = anr_flag; h->splay = 1; h->recycle_nodes = recycle_flag; h->find_success = 0; h->find_fail = 0; /* save off how much we've already allocated from our memcap */ h->overhead_bytes = h->mc.memused; h->overhead_blocks = h->mc.nblocks; return h; } /*! * Set the maximum nodes used in this hash table. * Specifying 0 is unlimited (or otherwise limited by memcap). * * @param h SFXHASH table pointer * @param max_nodes maximum nodes to allow. * */ void sfxhash_set_max_nodes( SFXHASH *h, int max_nodes ) { if (h) { h->max_nodes = max_nodes; } } /*! * Set Splay mode : Splays nodes to front of list on each access * * @param t SFXHASH table pointer * @param n boolean flag toggles splaying of hash nodes * */ void sfxhash_splaymode( SFXHASH * t, int n ) { t->splay = n; } /*! * Free all nodes in the free list * * Removes and frees all of the nodes in the free list * No need to call the user free, since that should've been * done when those nodes were put back in the free list. * * @param h SFXHASH table pointer */ static void sfxhash_delete_free_list(SFXHASH *t) { SFXHASH_NODE *cur = NULL; SFXHASH_NODE *next = NULL; if (t == NULL || t->fhead == NULL) return; cur = t->fhead; while (cur != NULL) { next = cur->gnext; s_free(t, (void *)cur); cur = next; } t->fhead = NULL; t->ftail = NULL; t->anr_count = 0; } /*! * Delete the hash Table * * free key's, free node's, and free the users data. * * @param h SFXHASH table pointer * */ void sfxhash_delete( SFXHASH * h ) { unsigned i; SFXHASH_NODE * node, * onode; if( !h ) return; if( h->sfhashfcn ) sfhashfcn_free( h->sfhashfcn ); if( h->table ) { for(i=0;inrows;i++) { for( node=h->table[i]; node; ) { onode = node; node = node->next; /* Notify user that we are about to free this node function */ if( h->usrfree ) h->usrfree( onode->key, onode->data ); s_free( h,onode ); } } s_free( h, h->table ); h->table = 0; } sfxhash_delete_free_list( h ); free( h ); /* free the table from general memory */ } /*! * Empty out the hash table * * @param h SFXHASH table pointer * * @return -1 on error */ int sfxhash_make_empty(SFXHASH *h) { SFXHASH_NODE *n = NULL; SFXHASH_NODE *tmp = NULL; unsigned i; if (h == NULL) return -1; for (i = 0; i < h->nrows; i++) { for (n = h->table[i]; n != NULL; n = tmp) { tmp = n->next; if (sfxhash_free_node(h, n) != SFXHASH_OK) { return -1; } } } h->max_nodes = 0; h->crow = 0; h->cnode = NULL; h->count = 0; h->ghead = NULL; h->gtail = NULL; h->anr_count = 0; h->anr_tries = 0; h->find_success = 0; h->find_fail = 0; return 0; } /** Save the freed node for later use (recylcing). * Free List - uses the NODE gnext/gprev fields */ static void sfxhash_save_free_node( SFXHASH *t, SFXHASH_NODE * hnode ) { /* Add A Node to the Free Node List */ if( t->fhead ) /* add the node to head of the the existing list */ { hnode->gprev = 0; hnode->gnext = t->fhead; t->fhead->gprev = hnode; t->fhead = hnode; /* tail is not affected */ } else /* 1st node in this list */ { hnode->gprev = 0; hnode->gnext = 0; t->fhead = hnode; t->ftail = hnode; } t->anr_count++; } /**Get a previously freed node for reuse. */ static SFXHASH_NODE * sfxhash_get_free_node( SFXHASH *t ) { SFXHASH_NODE * node = t->fhead; /* Remove A Node from the Free Node List - remove the head node */ if( node ) { t->fhead = node->gnext; if( t->fhead ) t->fhead->gprev = 0; if( t->ftail == node ) /* no more nodes - clear the tail */ t->ftail = 0; t->anr_count--; } return node; } static void sfxhash_glink_node( SFXHASH *t, SFXHASH_NODE * hnode ) { /* Add The Node */ if( t->ghead ) /* add the node to head of the the existing list */ { hnode->gprev = 0; hnode->gnext = t->ghead; t->ghead->gprev = hnode; t->ghead = hnode; /* tail is not affected */ } else /* 1st node in this list */ { hnode->gprev = 0; hnode->gnext = 0; t->ghead = hnode; t->gtail = hnode; } } static void sfxhash_gunlink_node( SFXHASH *t, SFXHASH_NODE * hnode ) { if( t->gnode == hnode ) /* if this was the global next node */ { t->gnode = hnode->gnext; } /* Remove the Head Node */ if( t->ghead == hnode ) /* add the node to head of the the existing list */ { t->ghead = t->ghead->gnext; if( t->ghead ) t->ghead->gprev = 0; } if( hnode->gprev ) hnode->gprev->gnext = hnode->gnext; if( hnode->gnext ) hnode->gnext->gprev = hnode->gprev; if( t->gtail == hnode ) t->gtail = hnode->gprev; } /**Move node to the front of global list. Node movement is application specific. */ void sfxhash_gmovetofront( SFXHASH *t, SFXHASH_NODE * hnode ) { if( hnode != t->ghead ) { sfxhash_gunlink_node( t, hnode ); sfxhash_glink_node( t, hnode ); } } /* * */ static void sfxhash_link_node( SFXHASH * t, SFXHASH_NODE * hnode ) { /* Add The Node to the Hash Table Row List */ if( t->table[hnode->rindex] ) /* add the node to the existing list */ { hnode->prev = 0; // insert node as head node hnode->next=t->table[hnode->rindex]; t->table[hnode->rindex]->prev = hnode; t->table[hnode->rindex] = hnode; } else /* 1st node in this list */ { hnode->prev=0; hnode->next=0; t->table[hnode->rindex] = hnode; } } static void sfxhash_unlink_node( SFXHASH * t, SFXHASH_NODE * hnode ) { if( hnode->prev ) // definitely not the 1st node in the list { hnode->prev->next = hnode->next; if( hnode->next ) hnode->next->prev = hnode->prev; } else if( t->table[hnode->rindex] ) // must be the 1st node in the list { t->table[hnode->rindex] = t->table[hnode->rindex]->next; if( t->table[hnode->rindex] ) t->table[hnode->rindex]->prev = 0; } } /* * move a node to the front of the row list at row = 'index' */ static void movetofront( SFXHASH *t, SFXHASH_NODE * n ) { /* Modify Hash Node Row List */ if( t->table[n->rindex] != n ) // if not at front of list already... { /* Unlink the node */ sfxhash_unlink_node( t, n ); /* Link at front of list */ sfxhash_link_node( t, n ); } /* Move node in the global hash node list to the front */ if (n == t->gnode) t->gnode = n->gnext; sfxhash_gmovetofront( t, n ); } /* * Allocat a new hash node, uses Auto Node Recovery if needed and enabled. * * The oldest node is the one with the longest time since it was last touched, * and does not have any direct indication of how long the node has been around. * We don't monitor the actual time since last being touched, instead we use a * splayed global list of node pointers. As nodes are accessed they are splayed * to the front of the list. The oldest node is just the tail node. * */ static SFXHASH_NODE * sfxhash_newnode( SFXHASH * t ) { SFXHASH_NODE * hnode; /* Recycle Old Nodes - if any */ hnode = sfxhash_get_free_node( t ); /* Allocate memory for a node */ if( ! hnode ) { if ((t->max_nodes == 0) || (t->count < t->max_nodes)) { hnode = (SFXHASH_NODE*)s_alloc( t, sizeof(SFXHASH_NODE) + t->pad + t->keysize + t->datasize ); } } /* If we still haven't found hnode, we're at our memory limit. * * Uses Automatic Node Recovery, to recycle the oldest node-based on access * (Unlink and reuse the tail node) */ if( !hnode && t->anr_flag && t->gtail ) { /* Find the oldes node the users willing to let go. */ for(hnode = t->gtail; hnode; hnode = hnode->gprev ) { if( t->anrfree ) /* User has provided a permission+release callback function */ { t->anr_tries++;/* Count # ANR requests */ /* Ask the user for permission to release this node, but let them say no! */ if( t->anrfree( hnode->key, hnode->data ) ) { /* NO, don't recycle this node, user's not ready to let it go. */ continue; } /* YES, user said we can recycle this node */ } sfxhash_gunlink_node( t, hnode ); /* unlink from the global list */ sfxhash_unlink_node( t, hnode ); /* unlink from the row list */ t->count--; t->anr_count++; /* count # of ANR operations */ break; } } /* either we are returning a node or we're all full and the user * won't let us allocate anymore and we return NULL */ return hnode; } /* * * Find a Node based on the key, return the node and the index. * The index is valid even if the return value is NULL, in which * case the index is the corect row in which the node should be * created. * */ #define hashsize(n) ((uint32_t)1<<(n)) #define hashmask(n) (hashsize(n)-1) static SFXHASH_NODE * sfxhash_find_node_row( SFXHASH * t, const void * key, int * rindex ) { unsigned hashkey; int index; SFXHASH_NODE *hnode; hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*)key, t->keysize ); /* printf("hashkey: %u t->keysize: %d\n", hashkey, t->keysize); */ /* flowkey_fprint(stdout, key); */ /* printf("****\n"); */ // index = hashkey % t->nrows; /* Modulus is slow. Switched to a table size that is a power of 2. */ index = hashkey & (t->nrows - 1); *rindex = index; for( hnode=t->table[index]; hnode; hnode=hnode->next ) { if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) ) { if( t->splay > 0 ) movetofront(t,hnode); t->find_success++; return hnode; } } t->find_fail++; return NULL; } /*! * Add a key + data pair to the hash table * * 2003-06-06: * - unique_tracker.c assumes that this splays * nodes to the top when they are added. * * This is done because of the successful find. * * @param t SFXHASH table pointer * @param key users key pointer * @param data users data pointer * * @return integer * @retval SFXHASH_OK success * @retval SFXHASH_INTABLE already in the table, t->cnode points to the node * @retval SFXHASH_NOMEM not enough memory */ static int sfxhash_add_ex( SFXHASH * t, const void * key, void* data , void **data_ptr) { int index; SFXHASH_NODE * hnode; /* Enforce uniqueness: Check for the key in the table */ hnode = sfxhash_find_node_row( t, key, &index ); if( hnode ) { t->cnode = hnode; if (data_ptr) *data_ptr = hnode->data; return SFXHASH_INTABLE; /* found it - return it. */ } /* * Alloc new hash node - allocate key space and data space at the same time. */ hnode = sfxhash_newnode( t ); if( !hnode ) { return SFXHASH_NOMEM; } /* Set up the new key pointer */ hnode->key = (char*)hnode + sizeof(SFXHASH_NODE); /* Copy the key */ memcpy(hnode->key,key,t->keysize); /* Save our table row index */ hnode->rindex = index; /* Copy the users data - or if datasize is zero set ptr to users data */ if( t->datasize ) { /* Set up the new data pointer */ hnode->data= (char*)hnode + sizeof(SFXHASH_NODE) + t->pad + t->keysize; if(data) { memcpy(hnode->data,data,t->datasize); } if (data_ptr) *data_ptr = hnode->data; } else { hnode->data = data; } /* Link the node into the table row list */ sfxhash_link_node ( t, hnode ); /* Link at the front of the global node list */ sfxhash_glink_node( t, hnode ); /* Track # active nodes */ t->count++; t->cnode = hnode; return SFXHASH_OK; } int sfxhash_add( SFXHASH * t, void * key, void * data) { return sfxhash_add_ex( t, key, data , NULL); } /*! * Add a key to the hash table, return the hash node * * 2003-06-06: * - unique_tracker.c assumes that this splays * nodes to the top when they are added. * * This is done because of the successful find. * * @param t SFXHASH table pointer * @param key users key pointer * * @return integer * @retval SFXHASH_OK success * @retval SFXHASH_INTABLE already in the table, t->cnode points to the node * @retval SFXHASH_NOMEM not enough memory */ SFXHASH_NODE * sfxhash_get_node( SFXHASH * t, const void * key ) { int index; SFXHASH_NODE * hnode; /* Enforce uniqueness: Check for the key in the table */ hnode = sfxhash_find_node_row( t, key, &index ); if( hnode ) { t->cnode = hnode; return hnode; /* found it - return it. */ } /* * Alloc new hash node - allocate key space and data space at the same time. */ hnode = sfxhash_newnode( t ); if( !hnode ) { return NULL; } /* Set up the new key pointer */ hnode->key = (char*)hnode + sizeof(SFXHASH_NODE); /* Copy the key */ memcpy(hnode->key,key,t->keysize); /* Save our table row index */ hnode->rindex = index; /* Copy the users data - or if datasize is zero set ptr to users data */ if( t->datasize ) { /* Set up the new data pointer */ hnode->data = (char*)hnode + sizeof(SFXHASH_NODE) + t->pad + t->keysize; } else { hnode->data = NULL; } /* Link the node into the table row list */ sfxhash_link_node ( t, hnode ); /* Link at the front of the global node list */ sfxhash_glink_node( t, hnode ); /* Track # active nodes */ t->count++; return hnode; } /*! * Find a Node based on the key * * @param t SFXHASH table pointer * @param key users key pointer * * @return SFXHASH_NODE* valid pointer to the hash node * @retval 0 node not found * */ SFXHASH_NODE * sfxhash_find_node( SFXHASH * t, const void * key) { int rindex; return sfxhash_find_node_row( t, key, &rindex ); } /*! * Find the users data based associated with the key * * @param t SFXHASH table pointer * @param key users key pointer * * @return void* valid pointer to the users data * @retval 0 node not found * */ void * sfxhash_find( SFXHASH * t, void * key) { SFXHASH_NODE * hnode; int rindex; hnode = sfxhash_find_node_row( t, key, &rindex ); if( hnode ) return hnode->data; return NULL; } /** * Get the HEAD of the in use list * * @param t table pointer * * @return the head of the list or NULL */ SFXHASH_NODE *sfxhash_ghead( SFXHASH * t ) { if(t) { return t->ghead; } return NULL; } /** * Walk the global list * * @param n current node * * @return the next node in the list or NULL when at the end */ SFXHASH_NODE *sfxhash_gnext( SFXHASH_NODE *n ) { if(n) { return n->gnext; } return NULL; } /** * Walk the global list * * @param n current node * * @return the next node in the list or NULL when at the end */ SFXHASH_NODE *sfxhash_gfindnext( SFXHASH * t ) { SFXHASH_NODE *n; n = t->gnode; if (n) t->gnode = n->gnext; return n; } /** * Get the HEAD of the in use list * * @param t table pointer * * @return the head of the list or NULL */ SFXHASH_NODE *sfxhash_gfindfirst( SFXHASH * t ) { if(t) { if (t->ghead) t->gnode = t->ghead->gnext; else t->gnode = NULL; return t->ghead; } return NULL; } /*! * Return the most recently used data from the global list * * @param t SFXHASH table pointer * * @return void* valid pointer to the users data * @retval 0 node not found * */ void * sfxhash_mru( SFXHASH * t ) { SFXHASH_NODE * hnode; hnode = sfxhash_ghead(t); if( hnode ) return hnode->data; return NULL; } /*! * Return the least recently used data from the global list * * @param t SFXHASH table pointer * * @return void* valid pointer to the users data * @retval 0 node not found * */ void * sfxhash_lru( SFXHASH * t ) { SFXHASH_NODE * hnode; hnode = t->gtail; if( hnode ) return hnode->data; return NULL; } /*! * Return the most recently used node from the global list * * @param t SFXHASH table pointer * * @return SFXHASH_NODE* valid pointer to a node * @retval 0 node not found * */ SFXHASH_NODE * sfxhash_mru_node( SFXHASH * t ) { SFXHASH_NODE * hnode; hnode = sfxhash_ghead(t); if( hnode ) return hnode; return NULL; } /*! * Return the least recently used node from the global list * * @param t SFXHASH table pointer * * @return SFXHASH_NODE* valid pointer to a node * @retval 0 node not found * */ SFXHASH_NODE * sfxhash_lru_node( SFXHASH * t ) { SFXHASH_NODE * hnode; hnode = t->gtail; if( hnode ) return hnode; return NULL; } /*! * Get some hash table statistics. NOT FOR REAL TIME USE. * * * @param t SFXHASH table pointer * @param filled how many * * @return max depth of the table * */ unsigned sfxhash_maxdepth( SFXHASH * t ) { unsigned i; unsigned max_depth = 0; SFXHASH_NODE * hnode; for( i=0; inrows; i++ ) { unsigned cur_depth = 0; for(hnode = t->table[i]; hnode != NULL; hnode = hnode->next) { cur_depth++; } if(cur_depth > max_depth) max_depth = cur_depth; } return max_depth; } /* * Unlink and free the node */ int sfxhash_free_node( SFXHASH * t, SFXHASH_NODE * hnode) { sfxhash_unlink_node( t, hnode ); /* unlink from the hash table row list */ sfxhash_gunlink_node( t, hnode ); /* unlink from global-hash-node list */ t->count--; if( t->usrfree ) { t->usrfree( hnode->key, hnode->data); } if( t->recycle_nodes ) { sfxhash_save_free_node( t, hnode ); } else { s_free( t, hnode ); } return SFXHASH_OK; } /* * Unlink and free an ANR node or the oldest node, if ANR is empty * behavior is undefined if t->usrfree is set */ int sfxhash_free_anr_lru( SFXHASH * t ) { if (!t) return SFXHASH_ERR; if (t->fhead) { SFXHASH_NODE* fn = sfxhash_get_free_node(t); if (fn) { s_free(t, fn); return SFXHASH_OK; } } if (t->gtail) { if (SFXHASH_OK == sfxhash_free_node(t, t->gtail)) { if (t->fhead) { SFXHASH_NODE* fn = sfxhash_get_free_node(t); if (fn) { s_free(t, fn); return SFXHASH_OK; } } //sfxhash_free_node calls s_free for us //when these conditions are met so we should //return SFXHASH_OK else if (!t->recycle_nodes) { return SFXHASH_OK; } } } return SFXHASH_ERR; } /* * Unlink and free an ANR node */ int sfxhash_free_anr( SFXHASH * t ) { if (!t) return SFXHASH_ERR; if (t->fhead) { SFXHASH_NODE* fn = sfxhash_get_free_node(t); if (fn) { s_free(t, fn); return SFXHASH_OK; } } return SFXHASH_ERR; } /*! * Remove a Key + Data Pair from the table. * * @param t SFXHASH table pointer * @param key users key pointer * * @return 0 success * @retval !0 failed * */ int sfxhash_remove( SFXHASH * t, void * key) { SFXHASH_NODE * hnode; unsigned hashkey, index; hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*)key, t->keysize ); // index = hashkey % t->nrows; /* Modulus is slow */ index = hashkey & (t->nrows - 1); hnode = t->table[index]; for( hnode=t->table[index]; hnode; hnode=hnode->next ) { if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) ) { return sfxhash_free_node( t, hnode ); } } return SFXHASH_ERR; } /* Internal use only */ static void sfxhash_next( SFXHASH * t ) { if( !t->cnode ) return ; /* Next node in current node list */ t->cnode = t->cnode->next; if( t->cnode ) { return; } /* Next row */ /* Get 1st node in next non-emtoy row/node list */ for( t->crow++; t->crow < t->nrows; t->crow++ ) { t->cnode = t->table[ t->crow ]; if( t->cnode ) { return; } } } /*! * Find and return the first hash table node * * @param t SFXHASH table pointer * * @return 0 failed * @retval !0 valid SFXHASH_NODE * * */ SFXHASH_NODE * sfxhash_findfirst( SFXHASH * t ) { SFXHASH_NODE * n; if(!t) return NULL; /* Start with 1st row */ for( t->crow=0; t->crow < t->nrows; t->crow++ ) { /* Get 1st Non-Null node in row list */ t->cnode = t->table[ t->crow ]; if( t->cnode ) { n = t->cnode; sfxhash_next( t ); // load t->cnode with the next entry return n; } } return NULL; } /*! * Find and return the next hash table node * * @param t SFXHASH table pointer * * @return 0 failed * @retval !0 valid SFXHASH_NODE * * */ SFXHASH_NODE * sfxhash_findnext( SFXHASH * t ) { SFXHASH_NODE * n; n = t->cnode; if( !n ) /* Done, no more entries */ { return NULL; } /* Preload next node into current node */ sfxhash_next( t ); return n; } /** * Make sfhashfcn use a separate set of operators for the backend. * * @param h sfhashfcn ptr * @param hash_fcn user specified hash function * @param keycmp_fcn user specified key comparisoin function */ int sfxhash_set_keyops( SFXHASH *h , unsigned (*hash_fcn)( SFHASHFCN * p, unsigned char *d, int n), int (*keycmp_fcn)( const void *s1, const void *s2, size_t n)) { if(h && hash_fcn && keycmp_fcn) { return sfhashfcn_set_keyops(h->sfhashfcn, hash_fcn, keycmp_fcn); } return -1; } int sfxhash_add_return_data_ptr( SFXHASH * t, const void * key, void **data ) { if( !t->datasize ) return SFXHASH_ERR; *data = NULL; return sfxhash_add_ex(t, key, NULL, data); } /* * Calculate memcap size based on number of entries and per-entry cost * * @param num_entries number of entries * @param entry_cost cost of a single entry * * @return memcap size */ unsigned sfxhash_calc_maxmem(unsigned num_entries, unsigned entry_cost) { return num_entries * (entry_cost + sizeof(SFXHASH_NODE) + sizeof(long)); } /* * Try to decrease the memcap * First decrease memcap, then delete free list, then kick out lru nodes * Behavior is undefined when t->usrfree is set * * @param t SFXHASH table pointer * @param new_memcap the new desired memcap * @param max_work the maximum amount of work for the function to do (0 = do all available work) * * @return SFXHASH_OK when memcap is successfully decreased * @return SFXHASH_PENDING when more work needs to be done * @return SFXHASH_NOMEM when there isn't enough memory in the hash table * #return SFXHASH_ERR when an error has occurred */ int sfxhash_change_memcap( SFXHASH * t, unsigned long new_memcap, unsigned *max_work ) { unsigned work = 0; if (!t) return SFXHASH_ERR; if (new_memcap == t->mc.memcap) { return SFXHASH_OK; } if (new_memcap > t->mc.memcap) { t->mc.memcap = new_memcap; return SFXHASH_OK; } //memcap decreased if (new_memcap < t->overhead_bytes) return SFXHASH_ERR; while (new_memcap < t->mc.memused && (work < *max_work || *max_work == 0) && sfxhash_free_anr_lru(t) == SFXHASH_OK) work++; if (work == *max_work && new_memcap < t->mc.memused) { *max_work -= work; return SFXHASH_PENDING; } *max_work -= work; //else mem decrased or we ran out of work (no more nodes to free) //we ran out of nodes to free and there still isnt enough memory //or (we have undefined behavior: t->usrfree is set and sfxhash_free_anr_lru is returning SFXHASH_ERR) if (new_memcap < t->mc.memused) return SFXHASH_NOMEM; t->mc.memcap = new_memcap; return SFXHASH_OK; } /* * ----------------------------------------------------------------------------------------- * Test Driver for Hashing * ----------------------------------------------------------------------------------------- */ #ifdef SFXHASH_MAIN /* This is called when the user releases a node or kills the table */ int usrfree( void * key, void * data ) { /* Release any data you need to */ return 0; } /* Auto Node Recovery Callback - optional This is called to ask the user to kill a node, if it reutrns !0 than the hash library does not kill this node. If the user os willing to let the node die, the user must do any free'ing or clean up on the node during this call. */ int anrfree( void * key, void * data ) { static int bx = 0; /* Decide if we can free this node. */ //bx++; if(bx == 4 )bx=0; /* for testing */ /* if we are allowing the node to die, kill it */ if( !bx ) usrfree( key, data ); return bx; /* Allow the caller to kill this nodes data + key */ } /* * Hash test program : use 'sfxhash 1000 50000' to stress the Auto_NodeRecover feature */ int main ( int argc, char ** argv ) { int i; SFXHASH * t; SFXHASH_NODE * n; char strkey[256], strdata[256], * p; int num = 100; int mem = 0; memset(strkey,0,20); memset(strdata,0,20); if( argc > 1 ) { num = atoi(argv[1]); } if( argc > 2 ) { mem = atoi(argv[2]); } /* Create a Hash Table */ t = sfxhash_new( 100, /* one row per element in table, when possible */ 20, /* key size : padded with zeros */ 20, /* data size: padded with zeros */ mem, /* max bytes, 0=no max */ 1, /* enable AutoNodeRecovery */ anrfree, /* provide a function to let user know we want to kill a node */ usrfree, /* provide a function to release user memory */ 1); /* Recycle nodes */ if(!t) { printf("Low Memory!\n"); exit(0); } /* Add Nodes to the Hash Table */ for(i=0;imc); printf("...******\n"); /* Display All Nodes in the Hash Table findfirst/findnext */ printf("\n...FINDFIRST / FINDNEXT TEST\n"); for( n = sfxhash_findfirst(t); n != 0; n = sfxhash_findnext(t) ) { printf("hash-findfirst/next: n=%x, key=%s, data=%s\n", n, n->key, n->data ); /* remove node we are looking at, this is first/next safe. */ if( sfxhash_remove(t,n->key) ) { printf("...ERROR: Could not remove the key node!\n"); } else { printf("...key node removed\n"); } } printf("...Auto-Node-Recovery: %d recycle attempts, %d completions.\n",t->anr_tries,t->anr_count); /* Free the table and it's user data */ printf("...sfxhash_delete\n"); sfxhash_delete( t ); printf("\nnormal pgm finish\n\n"); return 0; } #endif /**@}*/ snort-2.9.20/src/sfutil/sfPolicyData.h0000644000175000017500000000523014241077301015720 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _SF_POLICY_DATA_H_ #define _SF_POLICY_DATA_H_ #include "generators.h" #include "sfPolicy.h" extern tSfPolicyId napRuntimePolicyId; extern tSfPolicyId ipsRuntimePolicyId; extern uint8_t iprep_current_update_counter; static inline tSfPolicyId getNapRuntimePolicy(void) { return napRuntimePolicyId; } static inline tSfPolicyId getIpsRuntimePolicy(void) { return ipsRuntimePolicyId; } static inline tSfPolicyId getApplicableRuntimePolicy(uint32_t gid) { if (gid == GENERATOR_INTERNAL) return getNapRuntimePolicy(); else return getIpsRuntimePolicy(); } static inline void setNapRuntimePolicy(tSfPolicyId id) { napRuntimePolicyId = id; } static inline void setIpsRuntimePolicy(tSfPolicyId id) { ipsRuntimePolicyId = id; } static inline int isNapRuntimePolicyDefault(void) { return ( napRuntimePolicyId == SF_DEFAULT_POLICY_ID ); } static inline int isIpsRuntimePolicyDefault(void) { return ( ipsRuntimePolicyId == SF_DEFAULT_POLICY_ID ); } static inline tSfPolicyId getParserPolicy(SnortConfig *sc) { return sc ? sc->parserPolicyId : snort_conf->parserPolicyId; } static inline void setParserPolicy(SnortConfig *sc, tSfPolicyId id) { if (sc) sc->parserPolicyId = id; else snort_conf->parserPolicyId = id; } static inline int isParserPolicyDefault(SnortConfig *sc) { return ( ( sc ? sc->parserPolicyId : snort_conf->parserPolicyId ) == SF_DEFAULT_POLICY_ID ); } static inline void setIPRepUpdateCount(uint8_t count) { iprep_current_update_counter = count; } static inline uint8_t getIPRepUpdateCount(void) { return iprep_current_update_counter; } #endif snort-2.9.20/src/sfutil/sfksearch.c0000644000175000017500000005526714241077270015326 0ustar apoapo/* * ksearch.c * * Basic Keyword Search Trie - uses linked lists to build the finite automata * * Keyword-Match: Performs the equivalent of a multi-string strcmp() * - use for token testing after parsing the language tokens using lex or the like. * * Keyword-Search: searches the input text for one of multiple keywords, * and supports case sensitivite and case insensitive patterns. * * ** Copyright (C) 2001 Marc Norton ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2003-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. * * */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #ifndef DYNAMIC_PREPROC_CONTEXT #define SFKSEARCH_TRACK_Q #ifdef SFKSEARCH_TRACK_Q # include "snort.h" # include "util.h" #endif #endif //DYNAMIC_PREPROC_CONTEXT #include "snort_debug.h" #include "sfksearch.h" #include "snort_bounds.h" #include "sf_dynamic_preprocessor.h" static void KTrieFree(KTRIENODE *n); static unsigned int mtot = 0; unsigned int KTrieMemUsed(void) { return mtot; } void KTrieInitMemUsed(void) { mtot = 0; } /* * Allocate Memory */ static void * KTRIE_MALLOC(int n) { void *p; if (n < 1) return NULL; p = calloc(1, n); if (p) mtot += n; return p; } /* * Free Memory */ static void KTRIE_FREE(void *p) { if (p == NULL) return; free(p); } /* * Local/Tmp nocase array */ static unsigned char Tnocase[65*1024]; /* ** Case Translation Table */ static unsigned char xlatcase[256]; /* * */ static void init_xlatcase(void) { int i; static int first=1; if( !first ) return; /* thread safe */ for(i=0;i<256;i++) { xlatcase[ i ] = (unsigned char)tolower(i); } first=0; } /* * */ static inline void ConvertCaseEx( unsigned char * d, unsigned char *s, int m ) { int i; for( i=0; i < m; i++ ) { d[i] = xlatcase[ s[i] ]; } } /* * */ KTRIE_STRUCT * KTrieNew(int method, void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)) { KTRIE_STRUCT * ts = (KTRIE_STRUCT*) KTRIE_MALLOC( sizeof(KTRIE_STRUCT) ); if( !ts ) return 0; memset(ts, 0, sizeof(KTRIE_STRUCT)); init_xlatcase(); ts->memory = sizeof(KTRIE_STRUCT); ts->nchars = 0; ts->npats = 0; ts->end_states = 0; ts->method = method; /* - old method, 1 = queue */ ts->userfree = userfree; ts->optiontreefree = optiontreefree; ts->neg_list_free = neg_list_free; return ts; } int KTriePatternCount(KTRIE_STRUCT *k) { return k->npats; } /* * Deletes memory that was used in creating trie * and nodes */ void KTrieDelete(KTRIE_STRUCT *k) { KTRIEPATTERN *p = NULL; KTRIEPATTERN *pnext = NULL; int i; if (k == NULL) return; p = k->patrn; while (p != NULL) { pnext = p->next; if (k->userfree && p->id) k->userfree(p->id); if (k->optiontreefree) { if (p && p->rule_option_tree) k->optiontreefree(&p->rule_option_tree); } if (k->neg_list_free) { if (p && p->neg_list) k->neg_list_free(&p->neg_list); } KTRIE_FREE(p->P); KTRIE_FREE(p->Pcase); KTRIE_FREE(p); p = pnext; } for (i = 0; i < KTRIE_ROOT_NODES; i++) KTrieFree(k->root[i]); KTRIE_FREE(k); } /* * Recursively delete all nodes in trie */ static void KTrieFree(KTRIENODE *n) { if (n == NULL) return; KTrieFree(n->child); KTrieFree(n->sibling); KTRIE_FREE(n); } /* * */ static KTRIEPATTERN * KTrieNewPattern(unsigned char * P, int n) { KTRIEPATTERN *p; int ret; if (n < 1) return NULL; p = (KTRIEPATTERN*) KTRIE_MALLOC( sizeof(KTRIEPATTERN) ); if (p == NULL) return NULL; /* Save as a nocase string */ p->P = (unsigned char*) KTRIE_MALLOC( n ); if( !p->P ) { KTRIE_FREE(p); return NULL; } ConvertCaseEx( p->P, P, n ); /* Save Case specific version */ p->Pcase = (unsigned char*) KTRIE_MALLOC( n ); if( !p->Pcase ) { KTRIE_FREE(p->P); KTRIE_FREE(p); return NULL; } ret = SafeMemcpy(p->Pcase, P, n, p->Pcase, p->Pcase + n); if (ret != SAFEMEM_SUCCESS) { KTRIE_FREE(p->Pcase); KTRIE_FREE(p->P); KTRIE_FREE(p); return NULL; } p->n = n; p->next = NULL; return p; } /* * Add Pattern info to the list of patterns */ int KTrieAddPattern( KTRIE_STRUCT * ts, unsigned char * P, int n, int nocase, int negative, void * id ) { KTRIEPATTERN *pnew; if( !ts->patrn ) { pnew = ts->patrn = KTrieNewPattern( P, n ); if( !pnew ) return -1; } else { pnew = KTrieNewPattern(P, n ); if( !pnew ) return -1; pnew->next = ts->patrn; /* insert at head of list */ ts->patrn = pnew; } pnew->nocase = nocase; pnew->negative = negative; pnew->id = id; pnew->mnext = NULL; ts->npats++; ts->memory += sizeof(KTRIEPATTERN) + 2 * n ; /* Case and nocase */ return 0; } /* * */ static KTRIENODE * KTrieCreateNode(KTRIE_STRUCT * ts) { KTRIENODE * t=(KTRIENODE*)KTRIE_MALLOC( sizeof(KTRIENODE) ); if(!t) return 0; memset(t,0,sizeof(KTRIENODE)); ts->memory += sizeof(KTRIENODE); return t; } /* * Insert a Pattern in the Trie */ static int KTrieInsert( KTRIE_STRUCT *ts, KTRIEPATTERN * px ) { int type = 0; int n = px->n; unsigned char *P = px->P; KTRIENODE *root; /* Make sure we at least have a root character for the tree */ if( !ts->root[*P] ) { ts->root[*P] = root = KTrieCreateNode(ts); if( !root ) return -1; root->edge = *P; }else{ root = ts->root[*P]; } /* Walk existing Patterns */ while( n ) { if( root->edge == *P ) { P++; n--; if( n && root->child ) { root=root->child; } else /* cannot continue */ { type = 0; /* Expand the tree via the child */ break; } } else { if( root->sibling ) { root=root->sibling; } else /* cannot continue */ { type = 1; /* Expand the tree via the sibling */ break; } } } /* * Add the next char of the Keyword, if any */ if( n ) { if( type == 0 ) { /* * Start with a new child to finish this Keyword */ root->child= KTrieCreateNode( ts ); if( ! root->child ) return -1; root=root->child; root->edge = *P; P++; n--; ts->nchars++; } else { /* * Start a new sibling bracnch to finish this Keyword */ root->sibling= KTrieCreateNode( ts ); if( ! root->sibling ) return -1; root=root->sibling; root->edge = *P; P++; n--; ts->nchars++; } } /* * Finish the keyword as child nodes */ while( n ) { root->child = KTrieCreateNode(ts); if( ! root->child ) return -1; root=root->child; root->edge = *P; P++; n--; ts->nchars++; } if( root->pkeyword ) { px->mnext = root->pkeyword; /* insert duplicates at front of list */ root->pkeyword = px; ts->duplicates++; } else { root->pkeyword = px; ts->end_states++; } return 0; } /* * */ static void Build_Bad_Character_Shifts( KTRIE_STRUCT * kt ) { int i,k; KTRIEPATTERN *plist; /* Calc the min pattern size */ kt->bcSize = 32000; for( plist=kt->patrn; plist!=NULL; plist=plist->next ) { if( plist->n < kt->bcSize ) { kt->bcSize = plist->n; /* smallest pattern size */ } } /* * Initialze the Bad Character shift table. */ for (i = 0; i < KTRIE_ROOT_NODES; i++) { kt->bcShift[i] = (unsigned short)kt->bcSize; } /* * Finish the Bad character shift table */ for( plist=kt->patrn; plist!=NULL; plist=plist->next ) { int shift, cindex; for( k=0; kbcSize; k++ ) { shift = kt->bcSize - 1 - k; cindex = plist->P[ k ]; if( shift < kt->bcShift[ cindex ] ) { kt->bcShift[ cindex ] = (unsigned short)shift; } } } } static int KTrieBuildMatchStateNode(KTRIENODE *root, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int cnt = 0; KTRIEPATTERN *p; if (!root) return 0; /* each and every prefix match at this root*/ if (root->pkeyword) { for (p = root->pkeyword; p; p = p->mnext) { if (p->id) { if (p->negative) { neg_list_func(p->id, &root->pkeyword->neg_list); } else { build_tree(p->id, &root->pkeyword->rule_option_tree); } } cnt++; } /* Last call to finalize the tree for this root */ build_tree(NULL, &root->pkeyword->rule_option_tree); } /* for child of this root */ if (root->child) { cnt += KTrieBuildMatchStateNode(root->child, build_tree, neg_list_func); } /* 1st sibling of this root -- other siblings will be processed from * within the processing for root->sibling. */ if (root->sibling) { cnt += KTrieBuildMatchStateNode(root->sibling, build_tree, neg_list_func); } return cnt; } static int KTrieBuildMatchStateNodeWithSnortConf(struct _SnortConfig *sc, KTRIENODE *root, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int cnt = 0; KTRIEPATTERN *p; if (!root) return 0; /* each and every prefix match at this root*/ if (root->pkeyword) { for (p = root->pkeyword; p; p = p->mnext) { if (p->id) { if (p->negative) { neg_list_func(p->id, &root->pkeyword->neg_list); } else { build_tree(sc, p->id, &root->pkeyword->rule_option_tree); } } cnt++; } /* Last call to finalize the tree for this root */ build_tree(sc, NULL, &root->pkeyword->rule_option_tree); } /* for child of this root */ if (root->child) { cnt += KTrieBuildMatchStateNodeWithSnortConf(sc, root->child, build_tree, neg_list_func); } /* 1st sibling of this root -- other siblings will be processed from * within the processing for root->sibling. */ if (root->sibling) { cnt += KTrieBuildMatchStateNodeWithSnortConf(sc, root->sibling, build_tree, neg_list_func); } return cnt; } static int KTrieBuildMatchStateTrees( KTRIE_STRUCT * ts, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int i, cnt = 0; KTRIENODE * root; /* Find the states that have a MatchList */ for (i = 0; i < KTRIE_ROOT_NODES; i++) { root = ts->root[i]; /* each and every prefix match at this root*/ if (root) { cnt += KTrieBuildMatchStateNode(root, build_tree, neg_list_func); } } return cnt; } static int KTrieBuildMatchStateTreesWithSnortConf( struct _SnortConfig *sc, KTRIE_STRUCT * ts, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int i, cnt = 0; KTRIENODE * root; /* Find the states that have a MatchList */ for (i = 0; i < KTRIE_ROOT_NODES; i++) { root = ts->root[i]; /* each and every prefix match at this root*/ if (root) { cnt += KTrieBuildMatchStateNodeWithSnortConf(sc, root, build_tree, neg_list_func); } } return cnt; } /* * Build the Keyword TRIE * */ static inline int _KTrieCompile(KTRIE_STRUCT * ts) { KTRIEPATTERN * p; /* static int tmem=0; */ /* * Build the Keyword TRIE */ for( p=ts->patrn; p; p=p->next ) { if( KTrieInsert( ts, p ) ) return -1; } /* * Build A Setwise Bad Character Shift Table */ Build_Bad_Character_Shifts( ts ); /* tmem += ts->memory; printf(" Compile stats: %d patterns, %d chars, %d duplicate patterns, %d bytes, %d total-bytes\n",ts->npats,ts->nchars,ts->duplicates,ts->memory,tmem); */ return 0; } int KTrieCompile(KTRIE_STRUCT * ts, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int rval; if ((rval = _KTrieCompile(ts))) return rval; if (build_tree && neg_list_func) { KTrieBuildMatchStateTrees(ts, build_tree, neg_list_func); } return 0; } int KTrieCompileWithSnortConf(struct _SnortConfig *sc, KTRIE_STRUCT * ts, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int rval; if ((rval = _KTrieCompile(ts))) return rval; if (build_tree && neg_list_func) { KTrieBuildMatchStateTreesWithSnortConf(sc, ts, build_tree, neg_list_func); } return 0; } void sfksearch_print_qinfo(void) { #ifdef SFKSEARCH_TRACK_Q if( snort_conf->max_inq ) { LogMessage("lowmem: queue size = %u, max = %u\n", snort_conf->max_inq,SFK_MAX_INQ ); LogMessage("lowmem: queue flushes = "STDu64"\n", snort_conf->tot_inq_flush ); LogMessage("lowmem: queue inserts = "STDu64"\n", snort_conf->tot_inq_inserts ); LogMessage("lowmem: queue uinserts = "STDu64"\n", snort_conf->tot_inq_uinserts ); } #endif } static inline void _init_queue( SFK_PMQ * b) { b->inq=0; b->inq_flush=0; } /* uniquely insert into q */ static inline int _add_queue(SFK_PMQ * b, void * p ) { int i; #ifdef SFKSEARCH_TRACK_Q snort_conf->tot_inq_inserts++; #endif for(i=(int)(b->inq)-1;i>=0;i--) if( p == b->q[i] ) return 0; #ifdef SFKSEARCH_TRACK_Q snort_conf->tot_inq_uinserts++; #endif if( b->inq < SFK_MAX_INQ ) { b->q[ b->inq++ ] = p; } if( b->inq == SFK_MAX_INQ ) { #ifdef SFKSEARCH_TRACK_Q b->inq_flush++; #endif return 1; } return 0; } static inline unsigned _process_queue( SFK_PMQ * q, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void *data ) { KTRIEPATTERN * pk; unsigned int i; #ifdef SFKSEARCH_TRACK_Q if( q->inq > snort_conf->max_inq ) snort_conf->max_inq = q->inq; snort_conf->tot_inq_flush += q->inq_flush; #endif for( i=0; iinq; i++ ) { pk = q->q[i]; if (pk) { if (match (pk->id, pk->rule_option_tree, 0, data, pk->neg_list) > 0) { q->inq=0; return 1; } } } q->inq=0; return 0; } static inline int KTriePrefixMatchQ( KTRIE_STRUCT * kt, unsigned char * T, int n, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void * data ) { KTRIENODE * root; //KTRIEPATTERN * pk; //int index ; root = kt->root[ xlatcase[*T] ]; if( !root ) return 0; while( n ) { if( root->edge == xlatcase[*T] ) { T++; n--; if( root->pkeyword ) { if( _add_queue( &kt->q, root->pkeyword ) ) { if( _process_queue( &kt->q,match,data) ) { return 1; } } } if( n && root->child ) { root = root->child; } else /* cannot continue -- match is over */ { break; } } else { if( root->sibling ) { root = root->sibling; } else /* cannot continue */ { break; } } } return 0; } /* * Search - Algorithm * * This routine will log any substring of T that matches a keyword, * and processes all prefix matches. This is used for generic * pattern searching with a set of keywords and a body of text. * * * * kt- Trie Structure * T - nocase text * Tc- case specific text * n - text length * * returns: * # pattern matches */ static inline int KTriePrefixMatch( KTRIE_STRUCT * kt, unsigned char * T, unsigned char * Tc, unsigned char * bT, int n, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void * data ) { KTRIENODE * root = kt->root[ *T ]; int nfound = 0; KTRIEPATTERN * pk; int index ; /* Check if any keywords start with this character */ if( !root ) return 0; while( n ) { if( root->edge == *T ) { T++; n--; pk = root->pkeyword; if (pk) { index = (int)(T - bT - pk->n ); nfound++; if (match (pk->id, pk->rule_option_tree, index, data, pk->neg_list) > 0) { return nfound; } } if( n && root->child ) { root = root->child; } else /* cannot continue -- match is over */ { break; } } else { if( root->sibling ) { root = root->sibling; } else /* cannot continue */ { break; } } } return nfound; } static inline int KTrieSearchQ( KTRIE_STRUCT * ks, unsigned char * T, int n, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void * data ) { _init_queue(&ks->q); while( n > 0 ) { if( KTriePrefixMatchQ( ks, T++, n--, match, data ) ) return 0; } _process_queue(&ks->q,match,data); return 0; } /* * */ static inline int KTrieSearchNoBC( KTRIE_STRUCT * ks, unsigned char * Tx, int n, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void * data ) { int nfound = 0; unsigned char *T, *bT; ConvertCaseEx( Tnocase, Tx, n ); T = Tnocase; bT = T; for( ; n>0 ; n--, T++, Tx++ ) { nfound += KTriePrefixMatch( ks, T, Tx, bT, n, match, data ); } return nfound; } /* * */ static inline int KTrieSearchBC( KTRIE_STRUCT * ks, unsigned char * Tx, int n, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void * data ) { int tshift; unsigned char *Tend; unsigned char *T, *bT; int nfound = 0; short *bcShift = (short*)ks->bcShift; int bcSize = ks->bcSize; ConvertCaseEx( Tnocase, Tx, n ); T = Tnocase; bT = T; Tend = T + n - bcSize; bcSize--; for( ;T <= Tend; n--, T++, Tx++ ) { while( (tshift = bcShift[ *( T + bcSize ) ]) > 0 ) { T += tshift; Tx += tshift; if( T > Tend ) return nfound; } nfound += KTriePrefixMatch( ks, T, Tx, bT, n, match, data ); } return nfound; } /* * */ int KTrieSearch( KTRIE_STRUCT * ks, unsigned char * T, int n, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void * data ) { if( ks->method == KTRIEMETHOD_QUEUE ) { //if( ks->bcSize < 3) return KTrieSearchQ( ks, T, n, match, data ); //else // return KTrieSearchQBC( ks, T, n, match, data ); } else { if( ks->bcSize < 3) return KTrieSearchNoBC( ks, T, n, match, data ); else return KTrieSearchBC( ks, T, n, match, data ); } } /* * * TEST DRIVER FOR KEYWORD TRIE * */ #ifdef KTRIE_MAIN char ** gargv; int trie_nmatches = 0; int match( unsigned id, int index, void * data ) { trie_nmatches++; data = data; printf("id=%d found at index=%d, %s\n",id,index,gargv[id]); return 0; } /* * */ int main( int argc, char ** argv ) { int i; KTRIE_STRUCT * ts; int nocase=1; // don't care about case gargv = argv; ts = KTrieNew(); if( argc < 3 ) { printf("%s text pat1 pat2 ... patn [-c(ase-sensitive)\n",argv[0]); printf("search for keywords-default, or match keywords\n"); exit(0); } for(i=1;i %d characters, %d patterns, %d bytes allocated\n",ts->nchars,ts->npats,ts->memory); printf("Searching...\n"); KTrieSearch( ts, (unsigned char*)argv[1], strlen(argv[1]), match, 0 ); printf("%d matches found\n",trie_nmatches); printf("normal pgm finish.\n"); return 0; } #endif snort-2.9.20/src/sfutil/asn1.h0000644000175000017500000001007314241077207014206 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __ASN1_H__ #define __ASN1_H__ /* ** ASN.1 Identifier Classes */ #define SF_ASN1_CLASS_MASK 0xc0 #define SF_ASN1_CLASS_UNIVERSAL 0x00 #define SF_ASN1_CLASS_APPLICATION 0x40 #define SF_ASN1_CLASS_CONTEXT 0x80 #define SF_ASN1_CLASS_PRIVATE 0xc0 /* ** ASN.1 Identifier Flags */ #define SF_ASN1_FLAG_MASK 0x20 #define SF_ASN1_FLAG_PRIMITIVE 0x00 #define SF_ASN1_FLAG_CONSTRUCT 0x20 /* ** ASN.1 Universal Tags */ #define SF_ASN1_TAG_MASK 0x1f #define SF_ASN1_TAG_RSV_ENC 0 #define SF_ASN1_TAG_BOOL 1 #define SF_ASN1_TAG_INT 2 #define SF_ASN1_TAG_BIT_STR 3 #define SF_ASN1_TAG_OCT_STR 4 #define SF_ASN1_TAG_NULL 5 #define SF_ASN1_TAG_OBJ_IDENT 6 #define SF_ASN1_TAG_OBJ_DESC 7 #define SF_ASN1_TAG_EXT 8 #define SF_ASN1_TAG_REAL 9 #define SF_ASN1_TAG_ENUM 10 #define SF_ASN1_TAG_EMB_PDV 11 #define SF_ASN1_TAG_REL_OBJ 13 #define SF_ASN1_TAG_SEQ 16 #define SF_ASN1_TAG_SET 17 #define SF_ASN1_TAG_UTF8_STR 12 #define SF_ASN1_TAG_NUM_STR 18 #define SF_ASN1_TAG_PRINT_STR 19 #define SF_ASN1_TAG_T61_STR 20 #define SF_ASN1_TAG_VID_STR 21 #define SF_ASN1_TAG_IA5_STR 22 #define SF_ASN1_TAG_GRAPH_STR 25 #define SF_ASN1_TAG_VIS_STR 26 #define SF_ASN1_TAG_GEN_STR 27 #define SF_ASN1_TAG_UNIV_STR 28 #define SF_ASN1_TAG_BMP_STR 30 #define SF_ASN1_TAG_UTC_TIME 23 #define SF_ASN1_TAG_GEN_TIME 24 #define SF_ASN1_TAG_EXTENSION 31 /* ** BER Length Decoding */ #define SF_BER_LEN_MASK 0x80 #define SF_BER_LEN_DEF_SHORT 1 #define SF_BER_LEN_DEF_LONG 2 #define SF_BER_LEN_INDEF 3 typedef struct s_ASN1_LEN { unsigned char type; unsigned int size; } ASN1_LEN; typedef struct s_ASN1_IDENT { unsigned char asn1_class; unsigned char flag; unsigned char tag_type; unsigned int tag; } ASN1_IDENT; typedef struct s_ASN1_TYPE { ASN1_IDENT ident; ASN1_LEN len; const unsigned char *data; unsigned int data_len; unsigned char eoc; struct s_ASN1_TYPE *next; struct s_ASN1_TYPE *cnext; } ASN1_TYPE; typedef struct s_ASN1_DATA { const unsigned char *data; const unsigned char *start; const unsigned char *end; unsigned int len; } ASN1_DATA; typedef struct s_ASN1_CONFIG { ASN1_TYPE *mem; int num_nodes; int node_index; } ASN1_CONFIG; /* ** Error Codes */ #define ASN1_ERR_OOB 1 #define ASN1_ERR_NONFATAL 2 #define ASN1_ERR_OVERLONG_LEN 3 #define ASN1_OK 0 #define ASN1_ERR_NULL_MEM -1 #define ASN1_ERR_INVALID_BER_TAG_LEN -3 #define ASN1_ERR_MEM_ALLOC -4 #define ASN1_ERR_FATAL -5 #define ASN1_ERR_INVALID_INDEF_LEN -6 #define ASN1_ERR_INVALID_ARG -7 #define ASN1_ERR_STACK -8 void asn1_init_mem(int); void asn1_free_mem(void); int asn1_decode(const unsigned char *data, unsigned int len, ASN1_TYPE **asn1_type); int asn1_print_types(ASN1_TYPE *asn1_type, void *user); int asn1_traverse(ASN1_TYPE *asn1, void * user, int (*DetectFunc)(ASN1_TYPE *, void *)); #endif snort-2.9.20/src/sfutil/sfrt_flat_dir.h0000644000175000017500000000540414241077330016165 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** 9/7/2011 - Initial implementation ... Hui Cao */ #ifndef SFRT_DIR_FLAT_H_ #define SFRT_DIR_FLAT_H_ #include "sfrt.h" typedef MEM_OFFSET SUB_TABLE_PTR; typedef MEM_OFFSET ENTRIES_PTR; typedef uint8_t Entry_Len; typedef MEM_OFFSET Entry_Value; /*******************************************************************/ /* DIR-n-m data structures * Each table in the DIR-n-m method is represented by a * dir_sub_table_t. They are managed by a dir_table_t. */ typedef struct { int16_t width; /* width of this table. */ ENTRIES_PTR entries_value; ENTRIES_PTR entries_length; } dir_sub_table_flat_t; /* Master data structure for the DIR-n-m derivative */ typedef struct { int dimensions[20]; /* DIR-n-m will consist of any number of arbitrarily * long tables. This variable keeps track of the * dimensions */ int dim_size; /* And this variable keeps track of 'dimensions''s * dimensions! */ uint32_t mem_cap; /* User-defined maximum memory that can be allocated * for the DIR-n-m derivative */ int cur_num; /* Present number of used nodes */ uint32_t allocated; SUB_TABLE_PTR sub_table; } dir_table_flat_t; /*******************************************************************/ /* DIR-n-m functions, these are not intended to be called directly */ TABLE_PTR sfrt_dir_flat_new(uint32_t mem_cap, int count,...); void sfrt_dir_flat_free(TABLE_PTR); tuple_flat_t sfrt_dir_flat_lookup(uint32_t* adr, int numAdrDwords, TABLE_PTR table); int sfrt_dir_flat_insert(uint32_t* adr, int numAdrDwords, int len, word data_index, int behavior, TABLE_PTR, updateEntryInfoFunc updateEntry, INFO *data); uint32_t sfrt_dir_flat_usage(TABLE_PTR); #endif /* SFRT_DIR_FLAT_H_ */ snort-2.9.20/src/sfutil/Makefile.in0000644000175000017500000005036614242725547015261 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/sfutil ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libsfutil_a_AR = $(AR) $(ARFLAGS) libsfutil_a_LIBADD = am__libsfutil_a_SOURCES_DIST = sfghash.c sfghash.h sfhashfcn.c \ sfhashfcn.h sflsq.c sflsq.h sfmemcap.c sfmemcap.h sfthd.c \ sfthd.h sfxhash.c sfxhash.h ipobj.c ipobj.h getopt_long.c \ getopt.h getopt1.h acsmx.c acsmx.h acsmx2.c acsmx2.h \ sfksearch.c sfksearch.h bnfa_search.c bnfa_search.h mpse.c \ mpse.h bitop.h bitop_funcs.h util_math.c util_math.h \ util_net.c util_net.h util_str.c util_str.h util_utf.c \ util_utf.h util_jsnorm.c util_jsnorm.h util_unfold.c \ util_unfold.h asn1.c asn1.h sfeventq.c sfeventq.h \ sfsnprintfappend.c sfsnprintfappend.h sfrt.c sfrt.h \ sfrt_trie.h sfrt_dir.c sfrt_dir.h sfrt_flat.c sfrt_flat.h \ sfrt_flat_dir.c sfrt_flat_dir.h segment_mem.c segment_mem.h \ sfportobject.c sfportobject.h sfrim.c sfrim.h sfprimetable.c \ sfprimetable.h sf_ip.c sf_ip.h sf_ipvar.c sf_ipvar.h \ sf_vartable.c sf_vartable.h sf_iph.c sf_iph.h sf_textlog.c \ sf_textlog.h sf_sechash.c sf_sechash.h sfPolicy.c sfPolicy.h \ sfPolicyUserData.c sfPolicyUserData.h sfPolicyData.h \ sfActionQueue.c sfActionQueue.h sfrf.c sfrf.h strvec.c \ strvec.h sf_email_attach_decode.c sf_email_attach_decode.h \ sf_base64decode.c sf_base64decode.h Unified2_common.h \ sf_seqnums.h mpse_methods.h sfdebug.h intel-soft-cpm.c \ intel-soft-cpm.h md5.c md5.h sha2.c sha2.h @HAVE_INTEL_SOFT_CPM_TRUE@am__objects_1 = intel-soft-cpm.$(OBJEXT) @BUILD_OPENSSL_MD5_TRUE@am__objects_2 = md5.$(OBJEXT) @BUILD_OPENSSL_SHA_TRUE@am__objects_3 = sha2.$(OBJEXT) am_libsfutil_a_OBJECTS = sfghash.$(OBJEXT) sfhashfcn.$(OBJEXT) \ sflsq.$(OBJEXT) sfmemcap.$(OBJEXT) sfthd.$(OBJEXT) \ sfxhash.$(OBJEXT) ipobj.$(OBJEXT) getopt_long.$(OBJEXT) \ acsmx.$(OBJEXT) acsmx2.$(OBJEXT) sfksearch.$(OBJEXT) \ bnfa_search.$(OBJEXT) mpse.$(OBJEXT) util_math.$(OBJEXT) \ util_net.$(OBJEXT) util_str.$(OBJEXT) util_utf.$(OBJEXT) \ util_jsnorm.$(OBJEXT) util_unfold.$(OBJEXT) asn1.$(OBJEXT) \ sfeventq.$(OBJEXT) sfsnprintfappend.$(OBJEXT) sfrt.$(OBJEXT) \ sfrt_dir.$(OBJEXT) sfrt_flat.$(OBJEXT) sfrt_flat_dir.$(OBJEXT) \ segment_mem.$(OBJEXT) sfportobject.$(OBJEXT) sfrim.$(OBJEXT) \ sfprimetable.$(OBJEXT) sf_ip.$(OBJEXT) sf_ipvar.$(OBJEXT) \ sf_vartable.$(OBJEXT) sf_iph.$(OBJEXT) sf_textlog.$(OBJEXT) \ sf_sechash.$(OBJEXT) sfPolicy.$(OBJEXT) \ sfPolicyUserData.$(OBJEXT) sfActionQueue.$(OBJEXT) \ sfrf.$(OBJEXT) strvec.$(OBJEXT) \ sf_email_attach_decode.$(OBJEXT) sf_base64decode.$(OBJEXT) \ $(am__objects_1) $(am__objects_2) $(am__objects_3) libsfutil_a_OBJECTS = $(am_libsfutil_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libsfutil_a_SOURCES) DIST_SOURCES = $(am__libsfutil_a_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies noinst_LIBRARIES = libsfutil.a @HAVE_INTEL_SOFT_CPM_TRUE@INTEL_SOFT_CPM_SOURCES = intel-soft-cpm.c intel-soft-cpm.h @BUILD_OPENSSL_MD5_TRUE@OPENSSL_MD5 = \ @BUILD_OPENSSL_MD5_TRUE@ md5.c md5.h @BUILD_OPENSSL_SHA_TRUE@OPENSSL_SHA = \ @BUILD_OPENSSL_SHA_TRUE@ sha2.c sha2.h libsfutil_a_SOURCES = \ sfghash.c sfghash.h \ sfhashfcn.c sfhashfcn.h \ sflsq.c sflsq.h \ sfmemcap.c sfmemcap.h \ sfthd.c sfthd.h \ sfxhash.c sfxhash.h \ ipobj.c ipobj.h \ getopt_long.c getopt.h getopt1.h \ acsmx.c acsmx.h \ acsmx2.c acsmx2.h \ sfksearch.c sfksearch.h \ bnfa_search.c bnfa_search.h \ mpse.c mpse.h \ bitop.h bitop_funcs.h \ util_math.c util_math.h \ util_net.c util_net.h \ util_str.c util_str.h \ util_utf.c util_utf.h \ util_jsnorm.c util_jsnorm.h \ util_unfold.c util_unfold.h \ asn1.c asn1.h \ sfeventq.c sfeventq.h \ sfsnprintfappend.c sfsnprintfappend.h \ sfrt.c sfrt.h sfrt_trie.h sfrt_dir.c sfrt_dir.h \ sfrt_flat.c sfrt_flat.h sfrt_flat_dir.c sfrt_flat_dir.h \ segment_mem.c segment_mem.h \ sfportobject.c sfportobject.h \ sfrim.c sfrim.h \ sfprimetable.c sfprimetable.h \ sf_ip.c sf_ip.h \ sf_ipvar.c sf_ipvar.h \ sf_vartable.c sf_vartable.h \ sf_iph.c sf_iph.h \ sf_textlog.c sf_textlog.h \ sf_sechash.c sf_sechash.h \ sfPolicy.c sfPolicy.h \ sfPolicyUserData.c sfPolicyUserData.h \ sfPolicyData.h \ sfActionQueue.c sfActionQueue.h \ sfrf.c sfrf.h \ strvec.c strvec.h \ sf_email_attach_decode.c sf_email_attach_decode.h \ sf_base64decode.c sf_base64decode.h \ Unified2_common.h \ sf_seqnums.h \ mpse_methods.h \ sfdebug.h \ $(INTEL_SOFT_CPM_SOURCES) \ $(OPENSSL_MD5) \ $(OPENSSL_SHA) all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/sfutil/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/sfutil/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libsfutil.a: $(libsfutil_a_OBJECTS) $(libsfutil_a_DEPENDENCIES) $(EXTRA_libsfutil_a_DEPENDENCIES) $(AM_V_at)-rm -f libsfutil.a $(AM_V_AR)$(libsfutil_a_AR) libsfutil.a $(libsfutil_a_OBJECTS) $(libsfutil_a_LIBADD) $(AM_V_at)$(RANLIB) libsfutil.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/sfutil/bnfa_search.c0000644000175000017500000023045014241077212015571 0ustar apoapo/* ** bnfa_search.c ** ** Basic multi-pattern search engine using Aho-Corasick NFA construction. ** ** Version 3.0 (based on acsmx.c and acsmx2.c) ** ** author: marc norton ** date: started 12/21/05 ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** General Design ** Aho-Corasick based NFA state machine. ** Compacted sparse storage mode for better performance. ** Up to 16 Million states + transitions (combined) in compacted sparse mode. ** ** ** Compacted sparse array storage ** ** ** The primary data is held in one array. ** The patterns themselves are stored separately. ** The matching lists of patterns for each state are stored separately as well. ** The compacted sparse format improves caching/performance. ** ** word 1 : state ( only low 24 bits are used ) ** word 2 : control word = cb << 24 | fs ** cb: control byte ** cb = mb | fb | nt ** mb : 8th bit - if set state has matching patterns bit ** fb : 7th bit - if set full storage array bit (256 entries used), else sparse ** nt : 0-63= number of transitions (more than 63 requires full storage) ** fs: 24 bits for failure state transition index. ** word 3+ : transition word = input<<24 | next-state-index ** input : 8 bit character, input to state machine from search text ** next-state-index: 24 bits for index of next state ** (if we reallly need 16M states, we can add a state->index lookup array) ** ...repeat for each state ... ** ** * if a state is empty it has words 1 and 2, but no transition words. ** ** Construction: ** ** Patterns are added to a list based trie. ** The list based trie is compiled into a list based NFA with failure states. ** The list based NFA is converted to full or sparse format NFA. ** The Zero'th state sparse transitions may be stored in full format for ** performance. ** Sparse transition arrays are searched using linear and binary search ** strategies depending on the number of entries to search through in ** each state. ** The state machine in sparse mode is compacted into a single vector for ** better performance. ** ** Notes: ** ** The NFA can require twice the state transitions that a DFA uses. However, ** the construction of a DFA generates many additional transitions in each ** state which consumes significant additional memory. This particular ** implementation is best suited to environments where the very large memory ** requirements of a full state table implementation is not possible and/or ** the speed trade off is warranted to maintain a small memory footprint. ** ** Each state of an NFA usually has very few transitions but can have up to ** 256. It is important to not degenerate into a linear search so we utilize ** a binary search if there are more than 5 elements in the state to test for ** a match. This allows us to use a simple sparse memory design with an ** acceptable worst case search scenario. The binary search over 256 elements ** is limtied to a max of 8 tests. The zero'th state may use a full 256 state ** array, so a quick index lookup provides the next state transition. The ** zero'th state is generally visited much more than other states. ** ** Compiling : gcc, Intel C/C++, Microsoft C/C++, each optimize differently. ** My studies have shown Intel C/C++ 9,8,7 to be the fastest, Microsoft 8,7,6 ** is next fastest, and gcc 4.x,3.x,2.x is the slowest of the three. My ** testing has been mainly on x86. In general gcc does a poor job with ** optimizing this state machine for performance, compared to other less cache ** and prefetch sensitive algorithms. I've documented this behavior in a ** paper 'Optimizing Pattern Matching for IDS' (www.sourcefire.com, ** www.idsresearch.org). ** ** The code is sensitive to cache optimization and prefetching, as well as ** instruction pipelining. Aren't we all. To this end, the number of ** patterns, length of search text, and cpu cache L1,L2,L3 all affect ** performance. The relative performance of the sparse and full format NFA and ** DFA varies as you vary the pattern charactersitics,and search text length, ** but strong performance trends are present and stable. ** ** ** BNFA API SUMMARY ** ** bnfa=bnfaNew(); create a state machine ** bnfaAddPattern(bnfa,..); add a pattern to the state machine ** bnfaCompile (bnfa,..) compile the state machine ** bnfaPrintInfo(bnfa); print memory usage and state info ** bnfaPrint(bnfa); print the state machine in total ** state=bnfaSearch(bnfa, ...,state); search a data buffer for a pattern match ** bnfaFree (bnfa); free the bnfa ** ** ** Reference - Efficient String matching: An Aid to Bibliographic Search ** Alfred V Aho and Margaret J Corasick ** Bell Labratories ** Copyright(C) 1975 Association for Computing Machinery,Inc ** ** 12/4/06 - man - modified summary ** 6/26/07 - man - Added last_match tracking, and accounted for nocase/case by ** preseting the last match state, and reverting if we fail the ** case memcmp test for any rule in the states matching rule ** list. The states in the defaul matcher represent either ** case or nocase states, so they are dual mode, that makes ** this a bit tricky. When we sue the pure exact match, or ** pure don't care matching routines, we just track the last ** state, and never need to revert. This only tracks the ** single repeated states and repeated data. ** 01/2008 - man - added 2 phase pattern matcher using a pattern match queue. ** Text is scanned and matching states are queued, duplicate ** matches are dropped, and after the complete buffer scan the ** queued matches are processed. This improves cacheing ** performance, and reduces duplicate rule processing. The ** queue is limited in size and is flushed if it becomes full ** during the scan. This allows simple insertions. Tracking ** queue ops is optional, as this can impose a modest ** performance hit of a few percent. ** ** LICENSE (GPL) ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #ifndef DYNAMIC_PREPROC_CONTEXT #define BNFA_TRACK_Q #ifdef BNFA_TRACK_Q # include "snort.h" #endif #endif //DYNAMIC_PREPROC_CONTEXT #include "bnfa_search.h" #include "snort_debug.h" #include "util.h" #include "sf_dynamic_preprocessor.h" /* * Used to initialize last state, states are limited to 0-16M * so this will not conflict. */ #define LAST_STATE_INIT 0xffffffff #define printf LogMessage /* * Case Translation Table - his guarantees we use * indexed lookups for case conversion */ static unsigned char xlatcase[BNFA_MAX_ALPHABET_SIZE]; static void init_xlatcase(void) { int i; static int first=1; if( !first ) return; for(i=0; ihead = s->tail = 0; s->count= 0; s->maxcnt=0; } /* * Add items to tail of queue (fifo) */ static int queue_add (QUEUE * s, int state) { QNODE * q; if (!s->head) { q = s->tail = s->head = (QNODE *) BNFA_MALLOC (sizeof(QNODE),queue_memory); if(!q) return -1; q->state = state; q->next = 0; } else { q = (QNODE *) BNFA_MALLOC (sizeof(QNODE),queue_memory); if(!q) return -1; q->state = state; q->next = 0; s->tail->next = q; s->tail = q; } s->count++; if( s->count > s->maxcnt ) s->maxcnt = s->count; return 0; } /* * Remove items from head of queue (fifo) */ static int queue_remove (QUEUE * s) { int state = 0; QNODE * q; if (s->head) { q = s->head; state = q->state; s->head = s->head->next; s->count--; if( !s->head ) { s->tail = 0; s->count = 0; } BNFA_FREE (q,sizeof(QNODE),queue_memory); } return state; } /* * Return count of items in the queue */ static int queue_count (QUEUE * s) { return s->count; } /* * Free the queue */ static void queue_free (QUEUE * s) { while (queue_count (s)) { queue_remove (s); } } /* * Get next state from transition list */ static int _bnfa_list_get_next_state( bnfa_struct_t * bnfa, int state, int input ) { if ( state == 0 ) /* Full set of states always */ { bnfa_state_t * p = (bnfa_state_t*)bnfa->bnfaTransTable[0]; if(!p) { return 0; } return p[input]; } else { bnfa_trans_node_t * t = bnfa->bnfaTransTable[state]; while( t ) { if( t->key == (unsigned)input ) { return t->next_state; } t=t->next; } return BNFA_FAIL_STATE; /* Fail state */ } } /* * Put next state - head insertion, and transition updates */ static int _bnfa_list_put_next_state( bnfa_struct_t * bnfa, int state, int input, int next_state ) { if( state >= bnfa->bnfaMaxStates ) { return -1; } if( input >= bnfa->bnfaAlphabetSize ) { return -1; } if( state == 0 ) { bnfa_state_t * p; p = (bnfa_state_t*)bnfa->bnfaTransTable[0]; if( !p ) { p = (bnfa_state_t*)BNFA_MALLOC(sizeof(bnfa_state_t)*bnfa->bnfaAlphabetSize,bnfa->list_memory); if( !p ) { return -1; } bnfa->bnfaTransTable[0] = (bnfa_trans_node_t*)p; } if( p[input] ) { p[input] = next_state; return 0; } p[input] = next_state; } else { bnfa_trans_node_t * p; bnfa_trans_node_t * tnew; /* Check if the transition already exists, if so just update the next_state */ p = bnfa->bnfaTransTable[state]; while( p ) { if( p->key == (unsigned)input ) /* transition already exists- reset the next state */ { p->next_state = next_state; return 0; } p=p->next; } /* Definitely not an existing transition - add it */ tnew = (bnfa_trans_node_t*)BNFA_MALLOC(sizeof(bnfa_trans_node_t),bnfa->list_memory); if( !tnew ) { return -1; } tnew->key = input; tnew->next_state = next_state; tnew->next = bnfa->bnfaTransTable[state]; bnfa->bnfaTransTable[state] = tnew; } bnfa->bnfaNumTrans++; return 0; } /* * Free the entire transition list table */ static int _bnfa_list_free_table( bnfa_struct_t * bnfa ) { int i; bnfa_trans_node_t * t, *p; if( !bnfa->bnfaTransTable ) return 0; if( bnfa->bnfaTransTable[0] ) { BNFA_FREE(bnfa->bnfaTransTable[0],sizeof(bnfa_state_t)*bnfa->bnfaAlphabetSize,bnfa->list_memory); } for(i=1; ibnfaMaxStates; i++) { t = bnfa->bnfaTransTable[i]; while( t ) { p = t; t = t->next; BNFA_FREE(p,sizeof(bnfa_trans_node_t),bnfa->list_memory); } } if( bnfa->bnfaTransTable ) { BNFA_FREE(bnfa->bnfaTransTable,sizeof(bnfa_trans_node_t*)*bnfa->bnfaMaxStates,bnfa->list_memory); bnfa->bnfaTransTable = 0; } return 0; } static int bnfaBuildMatchStateTrees(bnfa_struct_t *bnfa, int (*build_tree)(void *id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int i,cnt = 0; bnfa_match_node_t * mn; bnfa_match_node_t ** MatchList = bnfa->bnfaMatchList; bnfa_pattern_t * patrn; for (i=0;ibnfaNumStates;i++) { for(mn = MatchList[i]; mn!= NULL; mn = mn->next ) { patrn = (bnfa_pattern_t *)mn->data; if (patrn->userdata) { if (patrn->negative) { neg_list_func(patrn->userdata, &MatchList[i]->neg_list); } else { build_tree(patrn->userdata, &MatchList[i]->rule_option_tree); } } cnt++; } /* Last call to finalize the tree */ if (MatchList[i]) { build_tree(NULL, &MatchList[i]->rule_option_tree); } } return cnt; } #ifndef DYNAMIC_PREPROCESSOR_CONTEXT static int bnfaBuildMatchStateTreesWithSnortConf(struct _SnortConfig *sc, bnfa_struct_t *bnfa, int (*build_tree)(struct _SnortConfig *, void *id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int i,cnt = 0; bnfa_match_node_t * mn; bnfa_match_node_t ** MatchList = bnfa->bnfaMatchList; bnfa_pattern_t * patrn; for (i=0;ibnfaNumStates;i++) { for(mn = MatchList[i]; mn!= NULL; mn = mn->next ) { patrn = (bnfa_pattern_t *)mn->data; if (patrn->userdata) { if (patrn->negative) { neg_list_func(patrn->userdata, &MatchList[i]->neg_list); } else { build_tree(sc, patrn->userdata, &MatchList[i]->rule_option_tree); } } cnt++; } /* Last call to finalize the tree */ if (MatchList[i]) { build_tree(sc, NULL, &MatchList[i]->rule_option_tree); } } return cnt; } #endif //DYNAMIC_PREPROCESSOR_CONTEXT #ifdef ALLOW_LIST_PRINT /* * Print the transition list table to stdout */ static int _bnfa_list_print_table( bnfa_struct_t * bnfa ) { int i; bnfa_trans_node_t * t; bnfa_match_node_t * mn; bnfa_pattern_t * patrn; if( !bnfa->bnfaTransTable ) { return 0; } printf("Print Transition Table- %d active states\n",bnfa->bnfaNumStates); for(i=0;i< bnfa->bnfaNumStates;i++) { printf("state %3d: ",i); if( i == 0 ) { int k; bnfa_state_t * p = (bnfa_state_t*)bnfa->bnfaTransTable[0]; if(!p) continue; for(k=0;kbnfaAlphabetSize;k++) { if( p[k] == 0 ) continue; if( isascii((int)p[k]) && isprint((int)p[k]) ) printf("%3c->%-5d\t",k,p[k]); else printf("%3d->%-5d\t",k,p[k]); } } else { t = bnfa->bnfaTransTable[i]; while( t ) { if( isascii((int)t->key) && isprint((int)t->key) ) printf("%3c->%-5d\t",t->key,t->next_state); else printf("%3d->%-5d\t",t->key,t->next_state); t = t->next; } } mn =bnfa->bnfaMatchList[i]; while( mn ) { patrn =(bnfa_pattern_t *)mn->data; printf("%.*s ",patrn->n,patrn->casepatrn); mn = mn->next; } printf("\n"); } return 0; } #endif /* * Converts a single row of states from list format to a full format */ static int _bnfa_list_conv_row_to_full(bnfa_struct_t * bnfa, bnfa_state_t state, bnfa_state_t * full ) { if( (int)state >= bnfa->bnfaMaxStates ) /* protects 'full' against overflow */ { return -1; } if( state == 0 ) { if( bnfa->bnfaTransTable[0] ) memcpy(full,bnfa->bnfaTransTable[0],sizeof(bnfa_state_t)*bnfa->bnfaAlphabetSize); else memset(full,0,sizeof(bnfa_state_t)*bnfa->bnfaAlphabetSize); return bnfa->bnfaAlphabetSize; } else { int tcnt = 0; bnfa_trans_node_t * t = bnfa->bnfaTransTable[ state ]; memset(full,0,sizeof(bnfa_state_t)*bnfa->bnfaAlphabetSize); if( !t ) { return 0; } while(t && (t->key < BNFA_MAX_ALPHABET_SIZE ) ) { full[ t->key ] = t->next_state; tcnt++; t = t->next; } return tcnt; } } /* * Add pattern characters to the initial upper case trie * unless Exact has been specified, in which case all patterns * are assumed to be case specific. */ static int _bnfa_add_pattern_states (bnfa_struct_t * bnfa, bnfa_pattern_t * p) { int state, next, n; unsigned char * pattern; bnfa_match_node_t * pmn; n = p->n; pattern = p->casepatrn; state = 0; /* * Match up pattern with existing states */ for (; n > 0; pattern++, n--) { if( bnfa->bnfaCaseMode == BNFA_CASE ) next = _bnfa_list_get_next_state(bnfa,state,*pattern); else next = _bnfa_list_get_next_state(bnfa,state,xlatcase[*pattern]); if( next == (int)BNFA_FAIL_STATE || next == 0 ) { break; } state = next; } /* * Add new states for the rest of the pattern bytes, 1 state per byte, uppercase */ for (; n > 0; pattern++, n--) { bnfa->bnfaNumStates++; if( bnfa->bnfaCaseMode == BNFA_CASE ) { if( _bnfa_list_put_next_state(bnfa,state,*pattern,bnfa->bnfaNumStates) < 0 ) return -1; } else { if( _bnfa_list_put_next_state(bnfa,state,xlatcase[*pattern],bnfa->bnfaNumStates) < 0 ) return -1; } state = bnfa->bnfaNumStates; if ( bnfa->bnfaNumStates >= bnfa->bnfaMaxStates ) { return -1; } } /* Add a pattern to the list of patterns terminated at this state */ pmn = (bnfa_match_node_t*)BNFA_MALLOC(sizeof(bnfa_match_node_t),bnfa->matchlist_memory); if( !pmn ) { return -1; } pmn->data = p; pmn->next = bnfa->bnfaMatchList[state]; bnfa->bnfaMatchList[state] = pmn; return 0; } #ifdef XXXXX int _bnfa_list_get_next_state( bnfa_struct_t * bnfa, int state, int input ) { if ( state == 0 ) /* Full set of states always */ { bnfa_state_t * p = (bnfa_state_t*)bnfa->bnfaTransTable[0]; if(!p) { return 0; } return p[input]; } else { bnfa_trans_node_t * t = bnfa->bnfaTransTable[state]; while( t ) { if( t->key == (unsigned)input ) { return t->next_state; } t=t->next; } return BNFA_FAIL_STATE; /* Fail state */ } } #endif static /* used only by KcontainsJ() */ int _bnfa_conv_node_to_full(bnfa_trans_node_t *t, bnfa_state_t * full ) { int tcnt = 0; memset(full,0,sizeof(bnfa_state_t)*BNFA_MAX_ALPHABET_SIZE); if( !t ) { return 0; } while(t && (t->key < BNFA_MAX_ALPHABET_SIZE ) ) { full[ t->key ] = t->next_state; tcnt++; t = t->next; } return tcnt; } /* * containment test - * test if all of tj transitions are in tk */ #ifdef XXXX static int KcontainsJx(bnfa_trans_node_t * tk, bnfa_trans_node_t *tj ) { bnfa_trans_node_t *t; int found; while( tj ) { found=0; for( t=tk;t;t=t->next ) { if( tj->key == t->key ) { found=1; break; } } if( !found ) return 0; tj=tj->next; /* get next tj key */ } return 1; } #endif static int KcontainsJ(bnfa_trans_node_t * tk, bnfa_trans_node_t *tj ) { bnfa_state_t full[BNFA_MAX_ALPHABET_SIZE]; if( !_bnfa_conv_node_to_full(tk,full) ) return 1; /* emtpy state */ while( tj ) { if( !full[tj->key] ) return 0; tj=tj->next; /* get next tj key */ } return 1; } /* * 1st optimization - eliminate duplicate fail states * * check if a fail state is a subset of the current state, * if so recurse to the next fail state, and so on. */ static int _bnfa_opt_nfa (bnfa_struct_t * bnfa) { int cnt=0; int k, fs, fr; bnfa_state_t * FailState = bnfa->bnfaFailState; for(k=2;kbnfaNumStates;k++) { fr = fs = FailState[k]; while( fs && KcontainsJ(bnfa->bnfaTransTable[k],bnfa->bnfaTransTable[fs]) ) { fs = FailState[fs]; } if( fr != fs ) { cnt++; FailState[ k ] = fs; } } #ifdef DEBUG if( cnt)LogMessage("ac-bnfa: %d nfa optimizations found in %d states\n",cnt,bnfa->bnfaNumStates); #endif return 0; } /* * Build a non-deterministic finite automata using Aho-Corasick construction * The keyword trie must already be built via _bnfa_add_pattern_states() */ static int _bnfa_build_nfa (bnfa_struct_t * bnfa) { int r, s, i; QUEUE q, *queue = &q; bnfa_state_t * FailState = bnfa->bnfaFailState; bnfa_match_node_t ** MatchList = bnfa->bnfaMatchList; bnfa_match_node_t * mlist; bnfa_match_node_t * px; /* Init a Queue */ queue_init (queue); /* Add the state 0 transitions 1st, * the states at depth 1, fail to state 0 */ for (i = 0; i < bnfa->bnfaAlphabetSize; i++) { /* note that state zero deos not fail, * it just returns 0..nstates-1 */ s = _bnfa_list_get_next_state(bnfa,0,i); if( s ) /* don't bother adding state zero */ { if( queue_add (queue, s) ) { return -1; } FailState[s] = 0; } } /* Build the fail state successive layer of transitions */ while (queue_count (queue) > 0) { r = queue_remove (queue); /* Find Final States for any Failure */ for(i = 0; ibnfaAlphabetSize; i++) { int fs, next; s = _bnfa_list_get_next_state(bnfa,r,i); if( s == (int)BNFA_FAIL_STATE ) continue; if( queue_add (queue, s) ) { return -1; } fs = FailState[r]; /* * Locate the next valid state for 'i' starting at fs */ while( (next=_bnfa_list_get_next_state(bnfa,fs,i)) == (int)BNFA_FAIL_STATE ) { fs = FailState[fs]; } /* * Update 's' state failure state to point to the next valid state */ FailState[s] = next; /* * Copy 'next'states MatchList into 's' states MatchList, * we just create a new list nodes, the patterns are not copied. */ for( mlist = MatchList[next];mlist;mlist = mlist->next) { /* Dup the node, don't copy the data */ px = (bnfa_match_node_t*)BNFA_MALLOC(sizeof(bnfa_match_node_t),bnfa->matchlist_memory); if( !px ) { return 0; } px->data = mlist->data; px->next = MatchList[s]; /* insert at head */ MatchList[s] = px; } } } /* Clean up the queue */ queue_free (queue); /* optimize the failure states */ if( bnfa->bnfaOpt ) _bnfa_opt_nfa(bnfa); return 0; } #ifdef ALLOW_NFA_FULL /* * Conver state machine to full format */ static int _bnfa_conv_list_to_full(bnfa_struct_t * bnfa) { int k; bnfa_state_t * p; bnfa_state_t ** NextState = bnfa->bnfaNextState; for(k=0;kbnfaNumStates;k++) { p = BNFA_MALLOC(sizeof(bnfa_state_t)*bnfa->bnfaAlphabetSize,bnfa->nextstate_memory); if(!p) { return -1; } _bnfa_list_conv_row_to_full( bnfa, (bnfa_state_t)k, p ); NextState[k] = p; /* now we have a full format row vector */ } return 0; } #endif /* * Convert state machine to csparse format * * Merges state/transition/failure arrays into one. * * For each state we use a state-word followed by the transition list for * the state sw(state 0 )...tl(state 0) sw(state 1)...tl(state1) sw(state2)... * tl(state2) .... * * The transition and failure states are replaced with the start index of * transition state, this eliminates the NextState[] lookup.... * * The compaction of multiple arays into a single array reduces the total * number of states that can be handled since the max index is 2^24-1, * whereas without compaction we had 2^24-1 states. */ static int _bnfa_conv_list_to_csparse_array(bnfa_struct_t * bnfa) { int m, k, i, nc; bnfa_state_t state; bnfa_state_t * FailState = (bnfa_state_t *)bnfa->bnfaFailState; bnfa_state_t * ps; /* transition list */ bnfa_state_t * pi; /* state indexes into ps */ bnfa_state_t ps_index=0; unsigned nps; bnfa_state_t full[BNFA_MAX_ALPHABET_SIZE]; /* count total state transitions, account for state and control words */ nps = 0; for(k=0;kbnfaNumStates;k++) { nps++; /* state word */ nps++; /* control word */ /* count transitions */ nc = 0; _bnfa_list_conv_row_to_full(bnfa, (bnfa_state_t)k, full ); for( i=0; ibnfaAlphabetSize; i++ ) { state = full[i] & BNFA_SPARSE_MAX_STATE; if( state != 0 ) { nc++; } } /* add in transition count */ if( (k == 0 && bnfa->bnfaForceFullZeroState) || nc > BNFA_SPARSE_MAX_ROW_TRANSITIONS ) { nps += BNFA_MAX_ALPHABET_SIZE; } else { for( i=0; ibnfaAlphabetSize; i++ ) { state = full[i] & BNFA_SPARSE_MAX_STATE; if( state != 0 ) { nps++; } } } } /* check if we have too many states + transitions */ if( nps > BNFA_SPARSE_MAX_STATE ) { /* Fatal */ return -1; } /* Alloc The Transition List - we need an array of bnfa_state_t items of size 'nps' */ ps = BNFA_MALLOC( nps*sizeof(bnfa_state_t),bnfa->nextstate_memory); if( !ps ) { /* Fatal */ return -1; } bnfa->bnfaTransList = ps; /* State Index list for pi - we need an array of bnfa_state_t items of size 'NumStates' */ pi = BNFA_MALLOC( bnfa->bnfaNumStates*sizeof(bnfa_state_t),bnfa->nextstate_memory); if( !pi ) { /* Fatal */ return -1; } /* Build the Transition List Array */ for(k=0;kbnfaNumStates;k++) { pi[k] = ps_index; /* save index of start of state 'k' */ ps[ ps_index ] = k; /* save the state were in as the 1st word */ ps_index++; /* skip past state word */ /* conver state 'k' to full format */ _bnfa_list_conv_row_to_full(bnfa, (bnfa_state_t)k, full ); /* count transitions */ nc = 0; for( i=0; ibnfaAlphabetSize; i++ ) { state = full[i] & BNFA_SPARSE_MAX_STATE; if( state != 0 ) { nc++; } } /* add a full state or a sparse state */ if( (k == 0 && bnfa->bnfaForceFullZeroState) || nc > BNFA_SPARSE_MAX_ROW_TRANSITIONS ) { /* set the control word */ ps[ps_index] = BNFA_SPARSE_FULL_BIT; ps[ps_index] |= FailState[k] & BNFA_SPARSE_MAX_STATE; if( bnfa->bnfaMatchList[k] ) { ps[ps_index] |= BNFA_SPARSE_MATCH_BIT; } ps_index++; /* copy the transitions */ _bnfa_list_conv_row_to_full(bnfa, (bnfa_state_t)k, &ps[ps_index] ); ps_index += BNFA_MAX_ALPHABET_SIZE; /* add in 256 transitions */ } else { /* set the control word */ ps[ps_index] = nc<bnfaMatchList[k] ) { ps[ps_index] |= BNFA_SPARSE_MATCH_BIT; } ps_index++; /* add in the transitions */ for( m=0, i=0; ibnfaAlphabetSize && m nps ) { /* Fatal */ return -1; } /* Replace Transition states with Transition Indices. This allows us to skip using NextState[] to locate the next state This limits us to <16M transitions due to 24 bit state sizes, and the fact we have now converted next-state fields to next-index fields in this array, and we have merged the next-state and state arrays. */ ps_index=0; for(k=0; k< bnfa->bnfaNumStates; k++ ) { if( pi[k] >= nps ) { /* Fatal */ return -1; } //ps_index = pi[k]; /* get index of next state */ ps_index++; /* skip state id */ /* Full Format */ if( ps[ps_index] & BNFA_SPARSE_FULL_BIT ) { /* Do the fail-state */ ps[ps_index] = ( ps[ps_index] & 0xff000000 ) | ( pi[ ps[ps_index] & BNFA_SPARSE_MAX_STATE ] ) ; ps_index++; /* Do the transition-states */ for(i=0;i>BNFA_SPARSE_COUNT_SHIFT; /* Do the cw = [cb | fail-state] */ ps[ps_index] = ( ps[ps_index] & 0xff000000 ) | ( pi[ ps[ps_index] & BNFA_SPARSE_MAX_STATE ] ); ps_index++; /* Do the transition-states */ for(i=0;i nps ) { /* Fatal */ return -1; } } BNFA_FREE(pi,bnfa->bnfaNumStates*sizeof(bnfa_state_t),bnfa->nextstate_memory); return 0; } /* * Print the state machine - rather verbose */ void bnfaPrint(bnfa_struct_t * bnfa) { int k; bnfa_match_node_t ** MatchList; bnfa_match_node_t * mlist; int ps_index=0; bnfa_state_t * ps=0; if( !bnfa ) return; MatchList = bnfa->bnfaMatchList; if( !bnfa->bnfaNumStates ) return; if( bnfa->bnfaFormat ==BNFA_SPARSE ) { printf("Print NFA-SPARSE state machine : %d active states\n", bnfa->bnfaNumStates); ps = bnfa->bnfaTransList; if( !ps ) return; } #ifdef ALLOW_NFA_FULL else if( bnfa->bnfaFormat ==BNFA_FULL ) { printf("Print NFA-FULL state machine : %d active states\n", bnfa->bnfaNumStates); } #endif for(k=0;kbnfaNumStates;k++) { printf(" state %-4d fmt=%d ",k,bnfa->bnfaFormat); if( bnfa->bnfaFormat == BNFA_SPARSE ) { unsigned i,cw,fs,nt,fb,mb; ps_index++; /* skip state number */ cw = ps[ps_index]; /* control word */ fb = (cw & BNFA_SPARSE_FULL_BIT)>>BNFA_SPARSE_VALUE_SHIFT; /* full storage bit */ mb = (cw & BNFA_SPARSE_MATCH_BIT)>>BNFA_SPARSE_VALUE_SHIFT; /* matching state bit */ nt = (cw & BNFA_SPARSE_COUNT_BITS)>>BNFA_SPARSE_VALUE_SHIFT;/* number of transitions 0-63 */ fs = (cw & BNFA_SPARSE_MAX_STATE)>>BNFA_SPARSE_VALUE_SHIFT; /* fail state */ ps_index++; /* skip control word */ printf("mb=%3u fb=%3u fs=%-4u ",mb,fb,fs); if( fb ) { printf(" nt=%-3d : ",bnfa->bnfaAlphabetSize); for( i=0; i<(unsigned)bnfa->bnfaAlphabetSize; i++, ps_index++ ) { if( ps[ps_index] == 0 ) continue; if( isascii((int)i) && isprint((int)i) ) printf("%3c->%-6d\t",i,ps[ps_index]); else printf("%3d->%-6d\t",i,ps[ps_index]); } } else { printf(" nt=%-3d : ",nt); for( i=0; i>BNFA_SPARSE_VALUE_SHIFT) && isprint(ps[ps_index]>>BNFA_SPARSE_VALUE_SHIFT) ) printf("%3c->%-6d\t",ps[ps_index]>>BNFA_SPARSE_VALUE_SHIFT,ps[ps_index] & BNFA_SPARSE_MAX_STATE); else printf("%3d->%-6d\t",ps[ps_index]>>BNFA_SPARSE_VALUE_SHIFT,ps[ps_index] & BNFA_SPARSE_MAX_STATE); } } } #ifdef ALLOW_NFA_FULL else if( bnfa->bnfaFormat == BNFA_FULL ) { int i; bnfa_state_t state; bnfa_state_t * p; bnfa_state_t ** NextState; NextState = (bnfa_state_t **)bnfa->bnfaNextState; if( !NextState ) continue; p = NextState[k]; printf("fs=%-4d nc=256 ",bnfa->bnfaFailState[k]); for( i=0; ibnfaAlphabetSize; i++ ) { state = p[i]; if( state != 0 && state != BNFA_FAIL_STATE ) { if( isascii(i) && isprint(i) ) printf("%3c->%-5d\t",i,state); else printf("%3d->%-5d\t",i,state); } } } #endif printf("\n"); if( MatchList[k] ) printf("---MatchList For State %d\n",k); for( mlist = MatchList[k]; mlist!= NULL; mlist = mlist->next ) { bnfa_pattern_t * pat; pat = (bnfa_pattern_t*)mlist->data; printf("---pattern : %.*s\n",pat->n,pat->casepatrn); } } } /* * Create a new AC state machine */ bnfa_struct_t * bnfaNew(void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)) { bnfa_struct_t * p; int bnfa_memory=0; init_xlatcase (); p = (bnfa_struct_t *) BNFA_MALLOC(sizeof(bnfa_struct_t),bnfa_memory); if(!p) return 0; if( p ) { p->bnfaOpt = 0; p->bnfaCaseMode = BNFA_PER_PAT_CASE; p->bnfaFormat = BNFA_SPARSE; p->bnfaAlphabetSize = BNFA_MAX_ALPHABET_SIZE; p->bnfaForceFullZeroState = 1; p->bnfa_memory = sizeof(bnfa_struct_t); p->userfree = userfree; p->optiontreefree = optiontreefree; p->neg_list_free = neg_list_free; } queue_memory = 0; return p; } void bnfaSetOpt(bnfa_struct_t * p, int flag) { p->bnfaOpt=flag; } void bnfaSetCase(bnfa_struct_t * p, int flag) { if( flag == BNFA_PER_PAT_CASE ) p->bnfaCaseMode = flag; if( flag == BNFA_CASE ) p->bnfaCaseMode = flag; if( flag == BNFA_NOCASE ) p->bnfaCaseMode = flag; } /* * Fee all memory */ void bnfaFree (bnfa_struct_t * bnfa) { int i; bnfa_pattern_t * patrn, *ipatrn; bnfa_match_node_t * mlist, *ilist; for(i = 0; i < bnfa->bnfaNumStates; i++) { /* free match list entries */ mlist = bnfa->bnfaMatchList[i]; while (mlist) { ilist = mlist; mlist = mlist->next; if (ilist->rule_option_tree && bnfa->optiontreefree) { bnfa->optiontreefree(&(ilist->rule_option_tree)); } if (ilist->neg_list && bnfa->neg_list_free) { bnfa->neg_list_free(&(ilist->neg_list)); } BNFA_FREE(ilist,sizeof(bnfa_match_node_t),bnfa->matchlist_memory); } bnfa->bnfaMatchList[i] = 0; #ifdef ALLOW_NFA_FULL /* free next state entries */ if( bnfa->bnfaFormat==BNFA_FULL )/* Full format */ { if( bnfa->bnfaNextState[i] ) { BNFA_FREE(bnfa->bnfaNextState[i],bnfa->bnfaAlphabetSize*sizeof(bnfa_state_t),bnfa->nextstate_memory); } } #endif } /* Free patterns */ patrn = bnfa->bnfaPatterns; while(patrn) { ipatrn=patrn; patrn=patrn->next; BNFA_FREE(ipatrn->casepatrn,ipatrn->n,bnfa->pat_memory); if(bnfa->userfree && ipatrn->userdata) bnfa->userfree(ipatrn->userdata); BNFA_FREE(ipatrn,sizeof(bnfa_pattern_t),bnfa->pat_memory); } /* Free arrays */ BNFA_FREE(bnfa->bnfaFailState,bnfa->bnfaNumStates*sizeof(bnfa_state_t),bnfa->failstate_memory); BNFA_FREE(bnfa->bnfaMatchList,bnfa->bnfaNumStates*sizeof(bnfa_pattern_t*),bnfa->matchlist_memory); BNFA_FREE(bnfa->bnfaNextState,bnfa->bnfaNumStates*sizeof(bnfa_state_t*),bnfa->nextstate_memory); BNFA_FREE(bnfa->bnfaTransList,(2*bnfa->bnfaNumStates+bnfa->bnfaNumTrans)*sizeof(bnfa_state_t*),bnfa->nextstate_memory); free( bnfa ); /* cannot update memory tracker when deleting bnfa so just 'free' it !*/ } /* * Add a pattern to the pattern list */ int bnfaAddPattern (bnfa_struct_t * p, unsigned char *pat, int n, int nocase, int negative, void * userdata ) { bnfa_pattern_t * plist; plist = (bnfa_pattern_t *)BNFA_MALLOC(sizeof(bnfa_pattern_t),p->pat_memory); if(!plist) return -1; plist->casepatrn = (unsigned char *)BNFA_MALLOC(n,p->pat_memory ); if(!plist->casepatrn) { BNFA_FREE(plist,sizeof(bnfa_pattern_t),p->pat_memory); return -1; } memcpy (plist->casepatrn, pat, n); plist->n = n; plist->nocase = nocase; plist->negative = negative; plist->userdata = userdata; plist->next = p->bnfaPatterns; /* insert at front of list */ p->bnfaPatterns = plist; p->bnfaPatternCnt++; return 0; } /* * Compile the patterns into an nfa state machine */ static inline int _bnfaCompile (bnfa_struct_t * bnfa) { bnfa_pattern_t * plist; bnfa_match_node_t ** tmpMatchList; unsigned cntMatchStates; int i; queue_memory =0; /* Count number of states */ for(plist = bnfa->bnfaPatterns; plist != NULL; plist = plist->next) { bnfa->bnfaMaxStates += plist->n; } bnfa->bnfaMaxStates++; /* one extra */ /* Alloc a List based State Transition table */ bnfa->bnfaTransTable =(bnfa_trans_node_t**) BNFA_MALLOC(sizeof(bnfa_trans_node_t*) * bnfa->bnfaMaxStates,bnfa->list_memory ); if(!bnfa->bnfaTransTable) { return -1; } /* Alloc a MatchList table - this has a list of pattern matches for each state */ bnfa->bnfaMatchList=(bnfa_match_node_t**) BNFA_MALLOC(sizeof(void*)*bnfa->bnfaMaxStates,bnfa->matchlist_memory ); if(!bnfa->bnfaMatchList) { return -1; } /* Add each Pattern to the State Table - This forms a keyword trie using lists */ bnfa->bnfaNumStates = 0; for (plist = bnfa->bnfaPatterns; plist != NULL; plist = plist->next) { _bnfa_add_pattern_states (bnfa, plist); } bnfa->bnfaNumStates++; if( bnfa->bnfaNumStates > BNFA_SPARSE_MAX_STATE ) { return -1; /* Call bnfaFree to clean up */ } /* ReAlloc a smaller MatchList table - only need NumStates */ tmpMatchList=bnfa->bnfaMatchList; bnfa->bnfaMatchList=(bnfa_match_node_t**)BNFA_MALLOC(sizeof(void*) * bnfa->bnfaNumStates,bnfa->matchlist_memory); if(!bnfa->bnfaMatchList) { return -1; } memcpy(bnfa->bnfaMatchList,tmpMatchList,sizeof(void*) * bnfa->bnfaNumStates); BNFA_FREE(tmpMatchList,sizeof(void*) * bnfa->bnfaMaxStates,bnfa->matchlist_memory); #ifdef MATCH_LIST_CNT bnfa->bnfaMatchListCnt=(unsigned*)calloc(sizeof(unsigned) * bnfa->bnfaNumStates); if(!bnfa->bnfaMatchListCnt) { return -1; } #endif /* Alloc a failure state table - only need NumStates */ bnfa->bnfaFailState =(bnfa_state_t*)BNFA_MALLOC(sizeof(bnfa_state_t) * bnfa->bnfaNumStates,bnfa->failstate_memory); if(!bnfa->bnfaFailState) { return -1; } #ifdef ALLOW_NFA_FULL if( bnfa->bnfaFormat == BNFA_FULL ) { /* Alloc a state transition table - only need NumStates */ bnfa->bnfaNextState=(bnfa_state_t**)BNFA_MALLOC(sizeof(bnfa_state_t*) * bnfa->bnfaNumStates,bnfa->nextstate_memory); if(!bnfa->bnfaNextState) { return -1; } } #endif /* Build the nfa w/failure states - time the nfa construction */ if( _bnfa_build_nfa (bnfa) ) { return -1; } /* Convert nfa storage format from list to full or sparse */ if( bnfa->bnfaFormat == BNFA_SPARSE ) { if( _bnfa_conv_list_to_csparse_array(bnfa) ) { return -1; } BNFA_FREE(bnfa->bnfaFailState,sizeof(bnfa_state_t)*bnfa->bnfaNumStates,bnfa->failstate_memory); bnfa->bnfaFailState=0; } #ifdef ALLOW_NFA_FULL else if( bnfa->bnfaFormat == BNFA_FULL ) { if( _bnfa_conv_list_to_full( bnfa ) ) { return -1; } } #endif else { return -1; } /* Free up the Table Of Transition Lists */ _bnfa_list_free_table( bnfa ); /* Count states with Pattern Matches */ cntMatchStates=0; for(i=0;ibnfaNumStates;i++) { if( bnfa->bnfaMatchList[i] ) cntMatchStates++; } bnfa->bnfaMatchStates = cntMatchStates; bnfa->queue_memory = queue_memory; bnfaAccumInfo( bnfa ); return 0; } int bnfaCompile (bnfa_struct_t * bnfa, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func )(void *id, void **list)) { int rval; if ((rval = _bnfaCompile (bnfa))) return rval; if (build_tree && neg_list_func) { bnfaBuildMatchStateTrees( bnfa, build_tree, neg_list_func ); } return 0; } #ifndef DYNAMIC_PREPROCESSOR_CONTEXT int bnfaCompileWithSnortConf (struct _SnortConfig *sc, bnfa_struct_t * bnfa, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func )(void *id, void **list)) { int rval; if ((rval = _bnfaCompile (bnfa))) return rval; if (build_tree && neg_list_func) { bnfaBuildMatchStateTreesWithSnortConf( sc, bnfa, build_tree, neg_list_func ); } return 0; } #endif //DYNAMIC_PREPROCESSOR_CONTEXT #ifdef ALLOW_NFA_FULL /* * Full Matrix Format Search */ static inline unsigned _bnfa_search_full_nfa( bnfa_struct_t * bnfa, unsigned char *Tx, int n, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data, bnfa_state_t state, int *current_state ) { unsigned char * Tend; unsigned char * T; unsigned char Tchar; unsigned index; bnfa_state_t ** NextState= bnfa->bnfaNextState; bnfa_state_t * FailState= bnfa->bnfaFailState; bnfa_match_node_t ** MatchList= bnfa->bnfaMatchList; bnfa_state_t * pcs; bnfa_match_node_t * mlist; bnfa_pattern_t * patrn; unsigned nfound = 0; int res; unsigned last_match=LAST_STATE_INIT; unsigned last_match_saved=LAST_STATE_INIT; T = Tx; Tend = T + n; for( ; T < Tend; T++ ) { Tchar = xlatcase[ *T ]; for(;;) { pcs = NextState[state]; if( pcs[Tchar] == 0 && state > 0 ) { state = FailState[state]; } else { state = pcs[Tchar]; break; } } if( state ) { if( state == last_match ) continue; last_match_saved=last_match; last_match = state; { mlist = MatchList[state]; if (!mlist) { continue; } patrn = (bnfa_pattern_t*)mlist->data; if( ( T - Tx) < patrn->n ) index = 0; else index = T - Tx - patrn->n + 1; nfound++; /* Don't do anything specific for case sensitive patterns and not, * since that will be covered by the rule tree itself. Each tree * might have both case sensitive & case insensitive patterns. */ res = Match (patrn->userdata, mlist->rule_option_tree, index, data, mlist->neg_list); if ( res > 0 ) { *current_state = state; return nfound; } else if( res < 0 ) { last_match = last_match_saved; } } } } *current_state = state; return nfound; } /* * Full Matrix Format Search - Exact matching patterns only */ static inline unsigned _bnfa_search_full_nfa_case( bnfa_struct_t * bnfa, unsigned char *Tx, int n, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data, bnfa_state_t state, int *current_state ) { unsigned char * Tend; unsigned char * T; unsigned char Tchar; unsigned index; bnfa_state_t ** NextState= bnfa->bnfaNextState; bnfa_state_t * FailState= bnfa->bnfaFailState; bnfa_match_node_t ** MatchList= bnfa->bnfaMatchList; bnfa_state_t * pcs; bnfa_match_node_t * mlist; bnfa_pattern_t * patrn; unsigned nfound = 0; unsigned last_match=LAST_STATE_INIT; unsigned last_match_saved=LAST_STATE_INIT; int res; T = Tx; Tend = T + n; for( ; T < Tend; T++ ) { Tchar = *T ; for(;;) { pcs = NextState[state]; if( pcs[Tchar] == 0 && state > 0 ) { state = FailState[state]; } else { state = pcs[Tchar]; break; } } if( state ) { if( state == last_match ) continue; last_match_saved=last_match; last_match = state; { mlist = MatchList[state]; if (!mlist) { continue; } patrn = (bnfa_pattern_t*)mlist->data; if( ( T - Tx) < patrn->n ) index = 0; else index = T - Tx - patrn->n + 1; nfound++; /* Don't do anything specific for case (in)sensitive patterns * since that will be covered by the rule tree itself. Each * tree might have both case sensitive & case insensitive patterns. */ res = Match (patrn->userdata, mlist->rule_option_tree, index, data, mlist->neg_list); if ( res > 0 ) { *current_state = state; return nfound; } else if( res < 0 ) { last_match = last_match_saved; } } } } *current_state = state; return nfound; } /* * Full Matrix Format Search - no case */ static inline unsigned _bnfa_search_full_nfa_nocase( bnfa_struct_t * bnfa, unsigned char *Tx, int n, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data, bnfa_state_t state, int *current_state ) { unsigned char * Tend; unsigned char * T; unsigned char Tchar; unsigned index; bnfa_state_t ** NextState= bnfa->bnfaNextState; bnfa_state_t * FailState= bnfa->bnfaFailState; bnfa_match_node_t ** MatchList= bnfa->bnfaMatchList; bnfa_state_t * pcs; bnfa_match_node_t * mlist; bnfa_pattern_t * patrn; unsigned nfound = 0; unsigned last_match=LAST_STATE_INIT; unsigned last_match_saved=LAST_STATE_INIT; int res; T = Tx; Tend = T + n; for( ; T < Tend; T++ ) { Tchar = xlatcase[ *T ]; for(;;) { pcs = NextState[state]; if( pcs[Tchar] == 0 && state > 0 ) { state = FailState[state]; } else { state = pcs[Tchar]; break; } } if( state ) { if( state == last_match ) continue; last_match_saved=last_match; last_match = state; { mlist = MatchList[state]; if (!mlist) { continue; } patrn = (bnfa_pattern_t*)mlist->data; if( ( T - Tx) < patrn->n ) index = 0; else index = T - Tx - patrn->n + 1; /* Don't do anything specific for case sensitive patterns and not, * since that will be covered by the rule tree itself. Each tree * might have both case sensitive & case insensitive patterns. */ res = Match (patrn->userdata, mlist->rule_option_tree, index, data, mlist->neg_list); if ( res > 0 ) { *current_state = state; return nfound; } else if( res < 0 ) { last_match = last_match_saved; } } } } *current_state = state; return nfound; } #endif /* binary array search on sparse transition array O(logN) search times..same as a binary tree. data must be in sorted order in the array. return: = -1 => not found >= 0 => index of element 'val' notes: val is tested against the high 8 bits of the 'a' array entry, this is particular to the storage format we are using. */ static inline int _bnfa_binearch( bnfa_state_t * a, int a_len, int val ) { int m, l, r; int c; l = 0; r = a_len - 1; while( r >= l ) { m = ( r + l ) >> 1; c = a[m] >> BNFA_SPARSE_VALUE_SHIFT; if( val == c ) { return m; } else if( val < c ) { r = m - 1; } else /* val > c */ { l = m + 1; } } return -1; } /* * Sparse format for state table using single array storage * * word 1: state * word 2: control-word = cb<<24| fs * cb : control-byte * : mb | fb | nt * mb : bit 8 set if match state, zero otherwise * fb : bit 7 set if using full format, zero otherwise * nt : number of transitions 0..63 (more than 63 requires full format) * fs: failure-transition-state * word 3+: byte-value(0-255) << 24 | transition-state */ static inline unsigned _bnfa_get_next_state_csparse_nfa_qx(bnfa_state_t * pcx, unsigned sindex, unsigned input) { int k; int nc; int index; register bnfa_state_t * pcs; for(;;) { pcs = pcx + sindex + 1; /* skip state-id == 1st word */ if( pcs[0] & BNFA_SPARSE_FULL_BIT ) { if( sindex == 0 ) { return pcs[1+input] & BNFA_SPARSE_MAX_STATE; } else { if( pcs[1+input] & BNFA_SPARSE_MAX_STATE ) return pcs[1+input] & BNFA_SPARSE_MAX_STATE; } } else { nc = (pcs[0]>>BNFA_SPARSE_COUNT_SHIFT) & BNFA_SPARSE_MAX_ROW_TRANSITIONS; if( nc > BNFA_SPARSE_LINEAR_SEARCH_LIMIT ) { /* binary search... */ index = _bnfa_binearch( pcs+1, nc, input ); if( index >= 0 ) { return pcs[index+1] & BNFA_SPARSE_MAX_STATE; } } else { /* linear search... */ for( k=0; k>BNFA_SPARSE_VALUE_SHIFT) == input ) { return pcs[k+1] & BNFA_SPARSE_MAX_STATE; } } } } return 0; /* no transition keyword match failed */ } } /* * Sparse format for state table using single array storage * * word 1: state * word 2: control-word = cb<<24| fs * cb : control-byte * : mb | fb | nt * mb : bit 8 set if match state, zero otherwise * fb : bit 7 set if using full format, zero otherwise * nt : number of transitions 0..63 (more than 63 requires full format) * fs: failure-transition-state * word 3+: byte-value(0-255) << 24 | transition-state */ static inline unsigned _bnfa_get_next_state_csparse_nfa(bnfa_state_t * pcx, unsigned sindex, unsigned input) { int k; int nc; int index; register bnfa_state_t * pcs; for(;;) { pcs = pcx + sindex + 1; /* skip state-id == 1st word */ if( pcs[0] & BNFA_SPARSE_FULL_BIT ) { if( sindex == 0 ) { return pcs[1+input] & BNFA_SPARSE_MAX_STATE; } else { if( pcs[1+input] & BNFA_SPARSE_MAX_STATE ) return pcs[1+input] & BNFA_SPARSE_MAX_STATE; } } else { nc = (pcs[0]>>BNFA_SPARSE_COUNT_SHIFT) & BNFA_SPARSE_MAX_ROW_TRANSITIONS; if( nc > BNFA_SPARSE_LINEAR_SEARCH_LIMIT ) { /* binary search... */ index = _bnfa_binearch( pcs+1, nc, input ); if( index >= 0 ) { return pcs[index+1] & BNFA_SPARSE_MAX_STATE; } } else { /* linear search... */ for( k=0; k>BNFA_SPARSE_VALUE_SHIFT) == input ) { return pcs[k+1] & BNFA_SPARSE_MAX_STATE; } } } } /* no transition found ... get the failure state and try again */ sindex = pcs[0] & BNFA_SPARSE_MAX_STATE; } } /* * Per Pattern case search, case is on per pattern basis * standard snort search * note: index is not used by snort, so it's commented * TRACK_Q can impose a modest couple % performance difference in the * pattern matching rate. */ /* Queue whole pattern groups at end states in AC */ void bnfa_print_qinfo(void) { #ifdef BNFA_TRACK_Q if( snort_conf->max_inq ) { LogMessage("ac-bnfa: queue size = %d, max = %d\n",snort_conf->max_inq, MAX_INQ ); LogMessage("ac-bnfa: queue flushes = "STDu64"\n", snort_conf->tot_inq_flush ); LogMessage("ac-bnfa: queue inserts = "STDu64"\n", snort_conf->tot_inq_inserts ); LogMessage("ac-bnfa: queue uinserts = "STDu64"\n", snort_conf->tot_inq_uinserts ); } #endif } static inline void _init_queue(bnfa_struct_t * b) { b->inq=0; b->inq_flush=0; } /* uniquely insert into q, should splay elements for performance */ static inline int _add_queue(bnfa_struct_t* b, bnfa_match_node_t * p ) { int i; #ifdef BNFA_TRACK_Q snort_conf->tot_inq_inserts++; #endif for(i=(int)(b->inq)-1;i>=0;i--) if( p == b->q[i] ) return 0; #ifdef BNFA_TRACK_Q snort_conf->tot_inq_uinserts++; #endif if( b->inq < MAX_INQ ) { b->q[ b->inq++ ] = p; } if( b->inq == MAX_INQ ) { #ifdef BNFA_TRACK_Q b->inq_flush++; #endif return 1; } return 0; } static inline unsigned _process_queue( bnfa_struct_t * bnfa, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data ) { bnfa_match_node_t * mlist; bnfa_pattern_t * patrn; int res; unsigned int i; #ifdef BNFA_TRACK_Q if( bnfa->inq > snort_conf->max_inq ) snort_conf->max_inq = bnfa->inq; snort_conf->tot_inq_flush += bnfa->inq_flush; #endif for( i=0; iinq; i++ ) { mlist = bnfa->q[i]; if (mlist) { patrn = (bnfa_pattern_t*)mlist->data; /*process a pattern - case is handled by otn processing */ res = Match (patrn->userdata, mlist->rule_option_tree, 0, data, mlist->neg_list); if ( res > 0 ) { /* terminate matching */ bnfa->inq=0;/* clear the q */ return 1; } } } bnfa->inq=0;/* clear the q */ return 0; } static inline unsigned _bnfa_search_csparse_nfa_qx(bnfa_struct_t * bnfa, unsigned char *T, int n, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data ) { bnfa_match_node_t * mlist; unsigned char * Tend; bnfa_match_node_t ** MatchList = bnfa->bnfaMatchList; bnfa_state_t * transList = bnfa->bnfaTransList; unsigned sindex=0; Tend = T + n; for(; TbnfaMatchList; bnfa_state_t * transList = bnfa->bnfaTransList; unsigned last_sindex; Tend = T + n; _init_queue(bnfa); for(; TbnfaMatchList; bnfa_pattern_t * patrn; bnfa_state_t * transList = bnfa->bnfaTransList; unsigned nfound = 0; unsigned last_match=LAST_STATE_INIT; unsigned last_match_saved=LAST_STATE_INIT; int res; #ifdef MATCH_LIST_CNT unsigned * MatchTestCnt = bnfa->bnfaMatchTestCnt; #endif T = Tx; Tend = T + n; for(; Tdata; if( ( T - Tx) < patrn->n ) index = 0; else index = T - Tx - patrn->n + 1; nfound++; /* Don't do anything specific for case sensitive patterns and not, * since that will be covered by the rule tree itself. Each tree * might have both case sensitive & case insensitive patterns. */ res = Match (patrn->userdata, mlist->rule_option_tree, index, data, mlist->neg_list); if ( res > 0 ) { *current_state = sindex; return nfound; } else if( res < 0 ) { last_match = last_match_saved; } } } } *current_state = sindex; return nfound; } /* * Case specific search, global to all patterns * * note: index is not used by snort, so it's commented */ static inline unsigned _bnfa_search_csparse_nfa_case( bnfa_struct_t * bnfa, unsigned char *Tx, int n, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data, unsigned sindex, int *current_state ) { bnfa_match_node_t * mlist; unsigned char * Tend; unsigned char * T; unsigned index; bnfa_match_node_t ** MatchList = bnfa->bnfaMatchList; bnfa_pattern_t * patrn; bnfa_state_t * transList = bnfa->bnfaTransList; unsigned nfound = 0; unsigned last_match=LAST_STATE_INIT; unsigned last_match_saved=LAST_STATE_INIT; int res; T = Tx; Tend = T + n; for(; Tdata; if( ( T - Tx) < patrn->n ) index = 0; else index = T - Tx - patrn->n + 1; nfound++; /* Don't do anything specific for case sensitive patterns and not, * since that will be covered by the rule tree itself. Each tree * might have both case sensitive & case insensitive patterns. */ res = Match (patrn->userdata, mlist->rule_option_tree, index, data, mlist->neg_list); if ( res > 0 ) { *current_state = sindex; return nfound; } else if( res < 0 ) { last_match = last_match_saved; } } } } *current_state = sindex; return nfound; } /* * NoCase search - global to all patterns * * note: index is not used by snort, so it's commented */ static inline unsigned _bnfa_search_csparse_nfa_nocase( bnfa_struct_t * bnfa, unsigned char *Tx, int n, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data, unsigned sindex, int *current_state ) { bnfa_match_node_t * mlist; unsigned char * Tend; unsigned char * T; unsigned char Tchar; unsigned index; bnfa_match_node_t ** MatchList = bnfa->bnfaMatchList; bnfa_pattern_t * patrn; bnfa_state_t * transList = bnfa->bnfaTransList; unsigned nfound = 0; unsigned last_match=LAST_STATE_INIT; unsigned last_match_saved=LAST_STATE_INIT; int res; T = Tx; Tend = T + n; for(; Tdata; if( ( T - Tx) < patrn->n ) index = 0; else index = T - Tx - patrn->n + 1; nfound++; /* Don't do anything specific for case sensitive patterns and not, * since that will be covered by the rule tree itself. Each tree * might have both case sensitive & case insensitive patterns. */ res = Match (patrn->userdata, mlist->rule_option_tree, index, data, mlist->neg_list); if ( res > 0 ) { *current_state = sindex; return nfound; } else if( res < 0 ) { last_match = last_match_saved; } } } } *current_state = sindex; return nfound; } /* * BNFA Search Function * * bnfa - state machine * Tx - text buffer to search * n - number of bytes in Tx * Match - function to call when a match is found * data - user supplied data that is passed to the Match function * sindex - state tracker, set value to zero to reset the state machine, * zero should be the value passed in on the 1st buffer or each buffer * that is to be analyzed on its own, the state machine updates this * during searches. This allows for sequential buffer searchs without * reseting the state machine. Save this value as returned from the * previous search for the next search. * * returns * The state or sindex of the state machine. This can than be passed back * in on the next search, if desired. */ unsigned bnfaSearchX( bnfa_struct_t * bnfa, unsigned char *T, int n, int (*Match)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list), void *data, unsigned sindex, int* current_state ) { int ret; _init_queue(bnfa); while( n > 0) { ret = _bnfa_search_csparse_nfa_qx( bnfa, T++, n--, Match, data ); if( ret ) return 0; } return _process_queue( bnfa, Match, data ); } unsigned bnfaSearch( bnfa_struct_t * bnfa, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, unsigned sindex, int* current_state ) { int ret = 0; if (current_state) { sindex = (unsigned)*current_state; } #ifdef ALLOW_NFA_FULL if( bnfa->bnfaFormat == BNFA_SPARSE ) { if( bnfa->bnfaCaseMode == BNFA_PER_PAT_CASE ) { if (bnfa->bnfaMethod) { ret = _bnfa_search_csparse_nfa( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } else { ret = _bnfa_search_csparse_nfa_q( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } } else if( bnfa->bnfaCaseMode == BNFA_CASE ) { ret = _bnfa_search_csparse_nfa_case( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } else /* NOCASE */ { ret = _bnfa_search_csparse_nfa_nocase( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } } else if( bnfa->bnfaFormat == BNFA_FULL ) { if( bnfa->bnfaCaseMode == BNFA_PER_PAT_CASE ) { ret = _bnfa_search_full_nfa( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, (bnfa_state_t) sindex, current_state ); } else if( bnfa->bnfaCaseMode == BNFA_CASE ) { ret = _bnfa_search_full_nfa_case( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, (bnfa_state_t) sindex, current_state ); } else { ret = _bnfa_search_full_nfa_nocase( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, (bnfa_state_t) sindex, current_state ); } } #else if( bnfa->bnfaCaseMode == BNFA_PER_PAT_CASE ) { if (bnfa->bnfaMethod) { ret = _bnfa_search_csparse_nfa( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } else { ret = _bnfa_search_csparse_nfa_q( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } } else if( bnfa->bnfaCaseMode == BNFA_CASE ) { ret = _bnfa_search_csparse_nfa_case( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } else/* NOCASE */ { ret = _bnfa_search_csparse_nfa_nocase( bnfa, Tx, n, (int (*)(bnfa_pattern_t * id, void *tree, int index, void *data, void *neg_list)) Match, data, sindex, current_state ); } #endif return ret; } int bnfaPatternCount( bnfa_struct_t * p) { return p->bnfaPatternCnt; } /* * Summary Info Data */ static bnfa_struct_t summary; static int summary_cnt=0; /* * Info: Print info a particular state machine. */ void bnfaPrintInfoEx( bnfa_struct_t * p, char * text ) { unsigned max_memory; if( !p->bnfaNumStates ) { return; } max_memory = p->bnfa_memory + p->pat_memory + p->list_memory + p->matchlist_memory + p->failstate_memory + p->nextstate_memory; if( text && summary_cnt ) { LogMessage("+-[AC-BNFA Search Info%s]------------------------------\n",text); LogMessage("| Instances : %d\n",summary_cnt); } else { LogMessage("+-[AC-BNFA Search Info]------------------------------\n"); } LogMessage("| Patterns : %d\n",p->bnfaPatternCnt); LogMessage("| Pattern Chars : %d\n",p->bnfaMaxStates); LogMessage("| Num States : %d\n",p->bnfaNumStates); LogMessage("| Num Match States : %d\n",p->bnfaMatchStates); if( max_memory < 1024*1024 ) { LogMessage("| Memory : %.2fKbytes\n", (double)max_memory/1024 ); LogMessage("| Patterns : %.2fK\n",(double)p->pat_memory/1024 ); LogMessage("| Match Lists : %.2fK\n",(double)p->matchlist_memory/1024 ); LogMessage("| Transitions : %.2fK\n",(double)p->nextstate_memory/1024 ); } else { LogMessage("| Memory : %.2fMbytes\n", (double)max_memory/(1024*1024) ); LogMessage("| Patterns : %.2fM\n",(double)p->pat_memory/(1024*1024) ); LogMessage("| Match Lists : %.2fM\n",(double)p->matchlist_memory/(1024*1024) ); LogMessage("| Transitions : %.2fM\n",(double)p->nextstate_memory/(1024*1024) ); } LogMessage("+-------------------------------------------------\n"); } void bnfaPrintInfo( bnfa_struct_t * p ) { bnfaPrintInfoEx( p, 0 ); } void bnfaPrintSummary( void ) { bnfaPrintInfoEx( &summary, " Summary" ); } void bnfaInitSummary( void ) { summary_cnt=0; memset(&summary,0,sizeof(bnfa_struct_t)); } void bnfaAccumInfo( bnfa_struct_t * p ) { bnfa_struct_t * px = &summary; summary_cnt++; px->bnfaAlphabetSize = p->bnfaAlphabetSize; px->bnfaPatternCnt += p->bnfaPatternCnt; px->bnfaMaxStates += p->bnfaMaxStates; px->bnfaNumStates += p->bnfaNumStates; px->bnfaNumTrans += p->bnfaNumTrans; px->bnfaMatchStates += p->bnfaMatchStates; px->bnfa_memory += p->bnfa_memory; px->pat_memory += p->pat_memory; px->list_memory += p->list_memory; px->matchlist_memory += p->matchlist_memory; px->nextstate_memory += p->nextstate_memory; px->failstate_memory += p->failstate_memory; } #ifdef MATCH_LIST_CNT void bnfaPrintMatchListCnt( bnfa_struct_t * p ) { unsigned * cnt = p->bnfaMatchListCnt; int i; bnfa_match_node_t * mn; bnfa_pattern_t * patrn; printf("[ MatchListCnt for ac-bnfa state machine\n ]"); for(i=0;ibnfaNumStates;i++) { if( cnt[i] ) { printf("state[%d] cnt=%d",i,cnt[i]); mn = bnfa->MatchList[i] ; if( mn ) { patrn =(bnfa_pattern_t *)mn->data; //xprintOTNSidGid(cnt,patrn->userdata); } printf("\n"); fflush(stdout); } } } #endif #ifdef BNFA_MAIN #include /* * Text Data Buffer */ unsigned char text[512]; unsigned char text2[512]; static int s_verbose=0; /* * A Match is found */ int MatchFound (void* id, void *tree, int index, void *data, void *neglist) { fprintf (stdout, "%s\n", (char *) data); return 0; } void objfree(void **obj) { return; } int buildtree(void *id, void **existing) { return 1; } int neglist(void *id, void **list) { return 1; } void LogMessage(const char *format,...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } SnortConfig sc; SnortConfig *snort_conf; /* * */ int main (int argc, char **argv) { int i, nc, nocase = 0; bnfa_struct_t * bnfa; int current_state = 0; bool split_search = false; char * p; if (argc < 3) { fprintf (stderr,"Usage: %s search-text pattern +pattern... [flags]\n",argv[0]); fprintf (stderr," flags: -q -nocase -splitsearch -v\n"); exit (0); } memset(&sc, 0, sizeof(SnortConfig)); snort_conf = ≻ bnfa = bnfaNew(free, objfree, objfree); if( !bnfa ) { printf("bnfa-no memory\n"); exit(0); } strncpy (text, argv[1], sizeof(text) - 1); text[sizeof(text) - 1] = '\0'; bnfa->bnfaMethod = 1; for (i = 1; i < argc; i++) { if (strcmp (argv[i], "-nocase") == 0){ nocase = 1; } if (strcmp (argv[i], "-v") == 0){ s_verbose=1; } if (strcmp (argv[i], "-splitsearch") == 0){ int len2 = strlen(text)/2; split_search =true; strncpy(text2, &text[len2], sizeof(text2) -1 ); text[len2] = '\0'; text2[len2] = '\0'; } if (strcmp (argv[i], "-q") == 0){ bnfa->bnfaMethod = 0; } } for (i = 2; i < argc; i++) { if (argv[i][0] == '-') continue; p = argv[i]; if ( *p == '+') { nc=1; p++; } else { nc = nocase; } bnfaAddPattern (bnfa, p, strlen(p), nc, 0, (void*)NULL); } if(s_verbose)printf("Patterns added\n"); //Print_DFA (acsm); bnfaCompile (bnfa, buildtree, neglist); //Write_DFA(acsm, "bnfa-snort.dfa") ; if(s_verbose) printf("Patterns compiled--written to file.\n"); bnfaPrintInfo ( bnfa ); bnfaPrintSummary ( ); bnfaSearch (bnfa, text, strlen (text), MatchFound, NULL, current_state, ¤t_state); if (split_search) bnfaSearch (bnfa, text2, strlen (text2), MatchFound, NULL, current_state, ¤t_state); bnfaFree (bnfa); printf ("normal pgm end\n"); return (0); } #endif /* */ snort-2.9.20/src/sfutil/sfportobject.h0000644000175000017500000002713714241077310016054 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* sfportobject.h Port List Object Management author: marc norton Hierarchy: PortTable -> PortObject's PortVar -> PortObject PortObject -> PortObjectItems (port or port range) */ #ifndef SFPORTOBJECT_H #define SFPORTOBJECT_H #include "sflsq.h" #include "sfghash.h" #ifdef HAVE_CONFIG_H #include "config.h" #endif #define SFPO_MAX_LPORTS 500 #define SFPO_MAX_PORTS 65536 typedef SFGHASH PortVarTable; /* * PortObjectItem Flags */ #define PORT_OBJECT_NOT_FLAG 1 #define PORT_OBJECT_PORT 1 #define PORT_OBJECT_RANGE 2 #define PORT_OBJECT_ANY 3 /* * Port Object Item supports * port, lowport:highport, portlist */ typedef struct _PortObjectItem_s { int type; /* ANY, RANGE, PORT */ int flags; /* NOT */ uint16_t hport; /* hi port */ uint16_t lport; /* lo port */ uint16_t cur_port; /* internal - first/next */ uint16_t tmp; }PortObjectItem; /* * PortObject supports a set of PortObjectItems * * A Port Set may include one or more of the following * Port * Port Range * List of Ports mixed with Port Ranges */ #include "bitop_funcs.h" typedef struct { /* not used yet */ char * name; /* user name - always use strdup or malloc for this*/ SF_LIST * item_list; /* list of port and port-range items */ }PortList_x; typedef struct { char * name; /* user name - always use strdup or malloc for this*/ int id; /* internal tracking - compiling sets this value */ SF_LIST * item_list; /* list of port and port-range items */ SF_LIST * rule_list; /* list of rules */ void * data; /* user data, PORT_GROUP based on rule_list - only used by any-any ports */ void (*data_free)(void *); }PortObject; typedef struct { char * name; /* user name - always use strdup or malloc for this*/ int id; /* internal tracking - compiling sets this value */ SF_LIST * item_list; /* list of port and port-range items */ SFGHASH * rule_hash; /* hash of rule (rule-indexes) in use */ int port_cnt; /* count of ports using this object */ BITOP * bitop; /* for collecting ports that use this object */ void * data; /* user data, PORT_GROUP based on rule_hash */ void (*data_free)(void *); }PortObject2; /* Port Table */ typedef struct _PortTable_s { /* turns on group optimization, better speed-but more memory * otherwise a single merged rule group is used. */ int pt_optimize; /* save the users input port objects in this list * rules may be added after creation of a port object * but the ports are not modified. */ SF_LIST * pt_polist; int pt_poid; /* * Array of lists of PortObject pointers to unique PortObjects, * the associated rule lists are stored in Data elements in rh, * the keys are the address of the PortObjects */ SF_LIST * pt_port_lists[SFPO_MAX_PORTS]; /* Compiled / merged port object hash table */ SFGHASH * pt_mpo_hash; SFGHASH * pt_mpxo_hash; SF_LIST * pt_plx_list; /* a single rule list with all rules merged together */ SF_LIST * pt_merged_rule_list; /* * Final Port/Rule Groupings, one port object per port, or null */ PortObject2 * pt_port_object[SFPO_MAX_PORTS]; int pt_lrc; /* large rule count, this many rules is a large group */ /* Stats */ int single_merges; /* single PortObject on a port */ int small_merges; /* small port objects merged into a bigger object */ int large_single_merges; /* 1 large + some small objects */ int large_multi_merges; /* >1 large object merged + some small objects */ int non_opt_merges; }PortTable; typedef struct { PortTable * tcp_src, * tcp_dst; PortTable * udp_src, * udp_dst; PortTable * icmp_src,* icmp_dst; PortTable * ip_src, * ip_dst; #ifdef TARGET_BASED PortTable * ns_tcp_src, * ns_tcp_dst; PortTable * ns_udp_src, * ns_udp_dst; PortTable * ns_icmp_src,* ns_icmp_dst; PortTable * ns_ip_src, * ns_ip_dst; #endif PortObject * tcp_anyany; PortObject * udp_anyany; PortObject * icmp_anyany; PortObject * ip_anyany; PortObject * tcp_nocontent; PortObject * udp_nocontent; PortObject * icmp_nocontent; PortObject * ip_nocontent; }rule_port_tables_t; #define POPERR_NO_NAME 1 #define POPERR_NO_ENDLIST_BRACKET 2 #define POPERR_NOT_A_NUMBER 3 #define POPERR_EXTRA_BRACKET 4 #define POPERR_NO_DATA 5 #define POPERR_ADDITEM_FAILED 6 #define POPERR_MALLOC_FAILED 7 #define POPERR_INVALID_RANGE 8 #define POPERR_DUPLICATE_ENTRY 9 #define POPERR_BOUNDS 10 #define POPERR_BAD_VARIABLE 11 #define POP_MAX_BUFFER_SIZE 256 typedef struct { char * s; /* current string pointer */ int slen; /* bytes left in string */ int pos; /* position in string of last GetChar() */ char token[POP_MAX_BUFFER_SIZE+4]; /* single number, or range, or not flag */ int errflag; /* for handling PortObject references when parsing */ PortObject * po_ref; SF_LNODE * poi_pos; PortVarTable * pvTable; }POParser; /* Prototypes */ /* * Port List Table * * The PortTable provides support to analyze the Port List objects defined by * the user as either PortVar entries or simply as inline rule port * list declarations. */ PortTable * PortTableNew (void); void PortTableFree( PortTable *p ); int PortTableAddObject( PortTable *p, PortObject * po ); int PortTableAddObjectRaw( PortTable *p, PortObject * po ); int PortTableAddRule ( PortTable * p, int port, int rule ); int PortTableCompile ( PortTable * P); void PortTablePrintInputEx( PortTable * p, void (*rule_index_map_print)(int index, char *buf, int bufsize) ); int PortTablePrintCompiledEx( PortTable * p, void (*rule_index_map_print)(int index, char *buf, int bufsize) ); PortObject * PortTableFindPortObjectByPort( PortTable * pt , int port ); PortObject * PortTableFindInputPortObjectName(PortTable * pt, char * po_name); PortObject * PortTableFindInputPortObjectPorts( PortTable * pt , PortObject * po ); /* Port List Object */ PortObject * PortObjectNew ( void ); PortObject2 * PortObject2New (int nrules/*guess at this */); void PortObjectFree ( void * p ); void PortObject2Free ( void * p ); int PortObjectSetName ( PortObject * po, char * name ); PortObjectItem * PortObjectItemNew ( void ); PortObjectItem * PortObjectItemDup ( PortObjectItem * poi ); void PortObjectItemFree ( PortObjectItem * poi ); int PortObjectAddItem ( PortObject * po, PortObjectItem * poi, int *errflag); int PortObjectAddPortObject ( PortObject * podst, PortObject * posrc, int *errflag); int PortObjectAddPort ( PortObject * po, int port, int not_flag ); int PortObjectAddRange ( PortObject * po, int lport, int hport, int not_flag ); int PortObjectAddRule ( PortObject * po, int rule ); int PortObjectAddPortAny ( PortObject * po ); PortObject * PortObjectDup ( PortObject * po ); PortObject2 * PortObject2Dup ( PortObject * po ); PortObject * PortObjectDupPorts ( PortObject * po ); int * PortObjectExtractRuleArray( PortObject * po, int * nrules ); int * PortObject2ExtractRuleArray( PortObject2 * po, int * nrules ); int PortObjectNormalize ( PortObject * po ); int PortObjectNegate ( PortObject * po ); int PortObjectEqual ( PortObject * poa, PortObject * bob ); void PortObjectSetAny ( PortObject * po ); int PortObjectPortCount ( PortObject * po ); int PortObjectHasPort ( PortObject * po, int port ); int PortObjectHasNot ( PortObject * po ); int PortObjectIsPureNot ( PortObject * po ); int PortObjectHasAny ( PortObject * po ); int PortObjectIncludesPort(PortObject * po, int port ); char * PortObjectCharPortArray ( char * parray, PortObject * po, int * nports ); int PortObjectRemovePorts( PortObject * a, PortObject * b ); PortObject * PortObjectAppend(PortObject * poa, PortObject * pob ); PortObject * PortObjectAppendPortObject(PortObject * poa, PortObject * pob ); PortObject2 * PortObject2AppendPortObject(PortObject2 * poa, PortObject * pob ); PortObject2 * PortObject2AppendPortObject2(PortObject2 * poa, PortObject2 * pob ); PortObject * PortObjectAppendEx(PortObject * poa, PortObject * pob ); PortObject2 * PortObjectAppendEx2(PortObject2 * poa, PortObject * pob ); int PortTableNormalizeInputPortObjects( PortTable *p ); int PortTableCompileMergePortObjects( PortTable * p ); int PortTableConsistencyCheck( PortTable *p ); void PortTablePrintInput( PortTable * p ); void PortTablePrintUserRules( PortTable * p ); void PortTablePrintPortGroups( PortTable * p ); void PortTablePrintPortPortObjects( PortTable * p ); int PortVarTableFree(PortVarTable * pvt); void PortObjectPrint ( PortObject * po ); void PortObjectPrintPorts ( PortObject * po ); void PortObjectPrintPortsRaw(PortObject * po ); void PortObject2PrintPorts ( PortObject2 * po ); void PortObject2Print ( PortObject2 * po ); int PortObjectPrintDetails( PortObject * po ); void PortObjectPrintEx(PortObject * po, void (*print_index_map)(int index, char *buf, int bufsize) ); void PortObject2PrintEx(PortObject2 * po, void (*print_index_map)(int index, char *buf, int bufsize) ); void PortTableSortUniqRules( PortTable * p ); void RuleListSortUniq( SF_LIST * rl ); /* PortVarTable port lists may be defined as 'name port-list' */ PortVarTable * PortVarTableCreate (void); int PortVarTableAdd ( PortVarTable * pvt, PortObject * po ); PortObject * PortVarTableFind ( PortVarTable * pvt, char * name ); /* PortVars are internally stored in PortObjects This function parses PortVar strings into PortObjects */ PortObject * PortObjectParseString ( PortVarTable * pvTable, POParser * pop,char * name, char * s,int nameflag ); char * PortObjectParseError( POParser * p ); #endif snort-2.9.20/src/sfutil/intel-soft-cpm.c0000644000175000017500000005105614241077217016207 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ***************************************************************************/ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "intel-soft-cpm.h" #include "pm/cpa_pm_compile.h" #include "util.h" #include "snort.h" #include "snort_debug.h" #include "fpcreate.h" /* MACROS *********************************************************************/ #define GROUP_ARRAY_ALLOC_SIZE 50 #define PATTERN_ARRAY_ALLOC_SIZE 10 #define MAX_INQ 32 #define DIM(x) (sizeof(x)/sizeof(x[0])) /* DATA TYPES *****************************************************************/ typedef struct _IntelPmMatchQueue { unsigned int inq; unsigned int inq_flush; void * q[MAX_INQ]; } IntelPmMatchQueue; typedef struct _IntelPmMatchState { void *user_data; void *rule_option_tree; void *neg_list; void (*user_free)(void *); void (*option_tree_free)(void **); void (*neg_list_free)(void **); } IntelPmMatchState; typedef struct _IntelPmHandles { CpaPmPdbPatternSetHandle psh; /* pattern set handle */ CpaPmPdbHandle pdbh; /* pattern database handle */ Cpa16U pgids; /* pattern group ids */ Cpa32U pids; /* pattern ids */ Cpa32U pcs; /* pattern characters */ IntelPm **ipms; /* pattern matchers */ int ipms_len; IntelPmMatchState *pm_mtchs; int pm_mtchs_len; int refs; } IntelPmHandles; typedef int (*MatchFunc)(void * id, void *tree, int index, void *data, void *neg_list); /* GLOBALS ********************************************************************/ static CpaInstanceHandle ipm_instance = NULL; /* instance handle */ /* XXX Temporary stat for Intel */ //static uint64_t intel_pm_search_buf_sizes[65536]; //static uint64_t intel_pm_matches = 0; /* PROTOTYPES *****************************************************************/ static inline const char * GetCpaStatusStr(CpaStatus); static void IntelPmSearchCallback(const CpaInstanceHandle, CpaPmMatchCtx *); static inline void IntelPmInitQueue(IntelPmMatchQueue *); static inline int IntelPmAddQueue(IntelPmMatchQueue *, void *); static inline unsigned int IntelPmProcessQueue(IntelPmMatchQueue *, MatchFunc, void *); /* FUNCTIONS ******************************************************************/ static inline const char * GetCpaStatusStr(CpaStatus status) { switch (status) { case CPA_STATUS_SUCCESS: return CPA_STATUS_STR_SUCCESS; case CPA_STATUS_FAIL: return CPA_STATUS_STR_FAIL; case CPA_STATUS_RETRY: return CPA_STATUS_STR_RETRY; case CPA_STATUS_RESOURCE: return CPA_STATUS_STR_RESOURCE; case CPA_STATUS_INVALID_PARAM: return CPA_STATUS_STR_INVALID_PARAM; case CPA_STATUS_FATAL: return CPA_STATUS_STR_FATAL; default: break; } return "Unknown Cpa error"; } static inline void IntelPmInitQueue(IntelPmMatchQueue *q) { q->inq = 0; q->inq_flush = 0; } static inline int IntelPmAddQueue(IntelPmMatchQueue *q, void *p) { int i; for (i = (int)q->inq - 1; i >= 0; i--) { if(p == q->q[i]) return 0; } if (q->inq < MAX_INQ) q->q[q->inq++] = p; if (q->inq == MAX_INQ) return 1; return 0; } static inline unsigned int IntelPmProcessQueue(IntelPmMatchQueue *q, MatchFunc match, void *data) { unsigned int i; for (i = 0; i < q->inq; i++) { IntelPmMatchState *mstate = (IntelPmMatchState *)q->q[i]; if (mstate != NULL) { if (match(mstate->user_data, mstate->rule_option_tree, 0, data, mstate->neg_list) > 0) { q->inq = 0; return 1; } } } q->inq = 0; return 0; } void IntelPmStartInstance(void) { Cpa16U nInstances; CpaInstanceHandle instanceHandle; CpaInstanceHandle *pHandles; CpaStatus status; if (ipm_instance != NULL) return; status = cpaPmGetNumInstances(&nInstances); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmGetNumInstances() failed: %s\n", GetCpaStatusStr(status)); pHandles = (CpaInstanceHandle *)SnortAlloc(nInstances * sizeof(CpaInstanceHandle)); status = cpaPmGetInstances(nInstances, pHandles); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmGetInstances() failed: %s\n", GetCpaStatusStr(status)); instanceHandle = pHandles[0]; status = cpaPmStartInstance(instanceHandle); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmStartInstance() failed: %s\n", GetCpaStatusStr(status)); /* Not sure if this frees everything except the first handle - taken * from intel code */ free(pHandles); ipm_instance = instanceHandle; } void * IntelPmNew(SnortConfig *sc, void (*user_free)(void *p), void (*option_tree_free)(void **p), void (*neg_list_free)(void **p)) { CpaStatus status; IntelPm *ipm = (IntelPm *)SnortAlloc(sizeof(IntelPm)); if (sc->ipm_handles == NULL) { CpaPmPdbPatternSetHandle patternSetHandle; status = cpaPmPdbCreatePatternSet(ipm_instance, 0, &patternSetHandle); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmPdbCreatePatternSet() failed: %s\n", GetCpaStatusStr(status)); sc->ipm_handles = (IntelPmHandles *)SnortAlloc(sizeof(IntelPmHandles)); sc->ipm_handles->psh = patternSetHandle; sc->ipm_handles->pdbh = NULL; sc->ipm_handles->pgids = 1; sc->ipm_handles->pids = 0; sc->ipm_handles->refs = 1; //sc has a reference //memset(intel_pm_search_buf_sizes, 0, sizeof(intel_pm_search_buf_sizes)); } ipm->user_free = user_free; ipm->option_tree_free = option_tree_free; ipm->neg_list_free = neg_list_free; ipm->match_queue = SnortAlloc(sizeof(IntelPmMatchQueue)); ipm->handles = sc->ipm_handles; sc->ipm_handles->refs++; return (void *)ipm; } void IntelPmDelete(IntelPm *ipm) { int i; if (ipm == NULL) return; if (ipm->sessionCtx != NULL) { cpaPmReleaseSessionCtx(ipm_instance, ipm->sessionCtx); free(ipm->sessionCtx); ipm->sessionCtx = NULL; } if (ipm->match_queue != NULL) free(ipm->match_queue); for (i = 0; i < ipm->pattern_array_len; i++) { IntelPmPattern *pat = &ipm->pattern_array[i]; if (ipm->user_free && pat->user_data) ipm->user_free(pat->user_data); } free(ipm->pattern_array); IntelPmRelease(ipm->handles); free(ipm); } int IntelPmRelease(IntelPmHandles *handles) { CpaStatus status; int i; if (handles == NULL) return -1; handles->refs--; if (handles->refs != 0) return handles->refs; for (i = 0; i < handles->pm_mtchs_len; i++) { IntelPmMatchState *ms = &handles->pm_mtchs[i]; if (ms->rule_option_tree && ms->option_tree_free) ms->option_tree_free(&ms->rule_option_tree); if (ms->neg_list && ms->neg_list_free) ms->neg_list_free(&ms->neg_list); } if (handles->psh != NULL) { status = cpaPmPdbReleasePatternSet(ipm_instance, handles->psh); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmPdbReleasePatternSet() failed: %s\n", GetCpaStatusStr(status)); } if (handles->pdbh != NULL) { status = cpaPmPdbRelease(ipm_instance, handles->pdbh); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmPdbRelease() failed: %s\n", GetCpaStatusStr(status)); } free(handles->ipms); free(handles->pm_mtchs); free(handles); return 0; } int IntelPmAddPattern(SnortConfig *sc, IntelPm *ipm, unsigned char *pat, int pat_len, unsigned no_case, unsigned negative, void *pat_data, int pat_id) { Cpa32U patternOptions = CPA_PM_PDB_OPTIONS_CASELESS | CPA_PM_PDB_OPTIONS_LITERAL; CpaStatus status; IntelPmPattern *ipp; if ((ipm == NULL) || (sc->ipm_handles == NULL)) return -1; if (!ipm->patternGroupId) { ipm->patternGroupId = sc->ipm_handles->pgids++; ipm->patternIds = 1; } status = cpaPmPdbAddPattern( ipm_instance, sc->ipm_handles->psh, ipm->patternIds, patternOptions, pat_len, pat, ipm->patternGroupId); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmPdbAddPattern() failed: %s\n", GetCpaStatusStr(status)); if (ipm->pattern_array == NULL) { ipm->pattern_array = (IntelPmPattern *)SnortAlloc( sizeof(IntelPmPattern) * PATTERN_ARRAY_ALLOC_SIZE); ipm->pattern_array_len = PATTERN_ARRAY_ALLOC_SIZE; } else if (ipm->patternIds >= ipm->pattern_array_len) { IntelPmPattern *tmp = (IntelPmPattern *)SnortAlloc( sizeof(IntelPmPattern) * (ipm->patternIds + PATTERN_ARRAY_ALLOC_SIZE)); memcpy((void *)tmp, ipm->pattern_array, ipm->patternIds * sizeof(IntelPmPattern)); free(ipm->pattern_array); ipm->pattern_array = tmp; ipm->pattern_array_len = ipm->patternIds + PATTERN_ARRAY_ALLOC_SIZE; } ipp = &ipm->pattern_array[ipm->patternIds]; ipp->user_data = pat_data; ipp->rule_option_tree = NULL; ipp->neg_list = NULL; //ipp->pattern = (unsigned char *)SnortAlloc(pat_len); //memcpy(ipp->pattern, pat, pat_len); ipp->pattern = NULL; ipp->pattern_len = pat_len; ipp->no_case = no_case; ipp->negative = negative; ipp->id = pat_id; ipp->patternId = ipm->patternIds++; sc->ipm_handles->pids++; sc->ipm_handles->pcs += pat_len; return 0; } int IntelPmFinishGroup(SnortConfig *sc, IntelPm *ipm, int (*build_tree)(SnortConfig *, void *id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { Cpa32U sessionCtxSize; CpaPmSessionProperty sessionProperty; Cpa8U *pMemory; CpaStatus status; if (ipm == NULL) return -1; ipm->build_tree = build_tree; ipm->neg_list_func = neg_list_func; sessionProperty.numPatternGroupIds = 1; sessionProperty.patternGroups[0].id.pdb = 0; sessionProperty.patternGroups[0].id.group = ipm->patternGroupId; sessionProperty.stateless = CPA_TRUE; status = cpaPmSessionCtxGetSize(ipm_instance, &sessionProperty, &sessionCtxSize); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmSessionCtxGetSize() failed: %s\n", GetCpaStatusStr(status)); pMemory = (Cpa8U *)SnortAlloc(sessionCtxSize); status = cpaPmCreateSessionCtx(ipm_instance, &sessionProperty, pMemory, &ipm->sessionCtx); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmCreateSessionCtx() failed: %s\n", GetCpaStatusStr(status)); if (sc->ipm_handles->ipms == NULL) { sc->ipm_handles->ipms = (IntelPm **)SnortAlloc( sizeof(IntelPm *) * GROUP_ARRAY_ALLOC_SIZE); sc->ipm_handles->ipms_len = GROUP_ARRAY_ALLOC_SIZE; } else if (ipm->patternGroupId >= sc->ipm_handles->ipms_len) { IntelPm **tmp = (IntelPm **)SnortAlloc( sizeof(IntelPm *) * (ipm->patternGroupId + GROUP_ARRAY_ALLOC_SIZE)); memcpy((void *)tmp, sc->ipm_handles->ipms, sc->ipm_handles->ipms_len * sizeof(IntelPm *)); free(sc->ipm_handles->ipms); sc->ipm_handles->ipms = tmp; sc->ipm_handles->ipms_len = ipm->patternGroupId + GROUP_ARRAY_ALLOC_SIZE; } sc->ipm_handles->ipms[ipm->patternGroupId] = ipm; return 0; } void IntelPmCompile(SnortConfig *sc) { if ((ipm_instance == NULL) || (sc->ipm_handles == NULL) || (sc->ipm_handles->psh == NULL)) { return; } if (sc->ipm_handles->pdbh == NULL) { CpaStatus status; Cpa16U patternGroup; Cpa32U numMatchStates; status = cpaPmPdbCompile(ipm_instance, sc->ipm_handles->psh, CPA_PM_COMPILE_OPTION_CONSOLIDATE, NULL, &sc->ipm_handles->pdbh); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmPdbCompile() failed: %s\n", GetCpaStatusStr(status)); status = cpaPmMsoGetNumMatchStates(ipm_instance, sc->ipm_handles->pdbh, &numMatchStates); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmMsoGetNumMatchStates() failed: %s\n", GetCpaStatusStr(status)); /* Hack because the last match state is returned instead of the * number of match states */ numMatchStates += 1; sc->ipm_handles->pm_mtchs = (IntelPmMatchState *)SnortAlloc(numMatchStates * sizeof(IntelPmMatchState)); sc->ipm_handles->pm_mtchs_len = numMatchStates; for (patternGroup = 1; patternGroup < sc->ipm_handles->pgids; patternGroup++) { CpaPmMsoMatchStateIter matchStateIter = NULL; Cpa32U matchStateId; IntelPm *ipm = sc->ipm_handles->ipms[patternGroup]; if (ipm == NULL) continue; status = cpaPmMsoGetFirstMatchState(ipm_instance, sc->ipm_handles->pdbh, patternGroup, &matchStateIter, &matchStateId); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmMsoGetFirstMatchState() failed: %s\n", GetCpaStatusStr(status)); while (matchStateIter != NULL) { CpaPmMsoPatternIdIter patternIdIter = NULL; Cpa32U patternID; IntelPmPattern *ipp = NULL; void *rule_option_tree = NULL; void *neg_list = NULL; void *user_data = NULL; status = cpaPmMsoGetFirstPatternId(ipm_instance, sc->ipm_handles->pdbh, matchStateIter, &patternIdIter, &patternID); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmMsoGetFirstPatternId() failed: %s\n", GetCpaStatusStr(status)); while (patternIdIter != NULL) { ipp = &ipm->pattern_array[patternID]; if (user_data == NULL) user_data = ipp->user_data; if (ipp->negative) ipm->neg_list_func(ipp->user_data, &neg_list); else ipm->build_tree(sc, ipp->user_data, &rule_option_tree); status = cpaPmMsoGetNextPatternId(ipm_instance, sc->ipm_handles->pdbh, &patternIdIter, &patternID); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmMsoGetNextPatternId() failed: %s\n", GetCpaStatusStr(status)); } if (ipp != NULL) { ipm->build_tree(sc, NULL, &rule_option_tree); sc->ipm_handles->pm_mtchs[matchStateId].user_data = user_data; sc->ipm_handles->pm_mtchs[matchStateId].neg_list = neg_list; sc->ipm_handles->pm_mtchs[matchStateId].rule_option_tree = rule_option_tree; sc->ipm_handles->pm_mtchs[matchStateId].user_free = ipm->user_free; sc->ipm_handles->pm_mtchs[matchStateId].option_tree_free = ipm->option_tree_free; sc->ipm_handles->pm_mtchs[matchStateId].neg_list_free = ipm->neg_list_free; } status = cpaPmMsoGetNextMatchState(ipm_instance, sc->ipm_handles->pdbh, patternGroup, &matchStateIter, &matchStateId); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmMsoGetNextMatchState() failed: %s\n", GetCpaStatusStr(status)); } } } } void IntelPmActivate(SnortConfig *sc) { CpaStatus status; IntelPmHandles *handles = sc->ipm_handles; sc->ipm_handles = NULL; if (IntelPmRelease(handles) <= 0) return; if (ipm_instance == NULL) return; status = cpaPmActivatePdb(ipm_instance, handles->pdbh, NULL); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmPdbActivate() failed: %s\n", GetCpaStatusStr(status)); } static void IntelPmSearchCallback(const CpaInstanceHandle instanceHandle, CpaPmMatchCtx *pMatchCtxList) { Cpa32U i; IntelPm *ipm = (IntelPm *)pMatchCtxList->userData; IntelPmHandles *handles = (IntelPmHandles *)ipm->handles; for (i = 0; i < pMatchCtxList->numMatchResults; i++) { CpaPmMatchResult *result = &pMatchCtxList->pMatchResult[i]; //intel_pm_matches++; if (result->matchLength == 0) continue; if (result->patternGroupId.id.group != ipm->patternGroupId) continue; if (IntelPmAddQueue((IntelPmMatchQueue *)ipm->match_queue, (void *)&handles->pm_mtchs[result->patternId])) { IntelPmProcessQueue((IntelPmMatchQueue *)ipm->match_queue, ipm->match, ipm->data); } } } int IntelPmSearch(IntelPm *ipm, unsigned char *buffer, int buffer_len, MatchFunc match, void *data) { CpaFlatBuffer flat_buffer = {buffer_len, buffer}; CpaBufferList buffer_list = {1, &flat_buffer, NULL, NULL}; CpaPmMatchResult matchResults[100]; /* XXX Can this be unlimited? */ CpaPmMatchCtx matchCtxList; CpaStatus status; //intel_pm_search_buf_sizes[buffer_len]++; ipm->data = data; ipm->match = match; /* Note: Search options CPA_PM_MATCH_OPTION_RESET_STREAM | CPA_PM_MATCH_OPTION_END_OF_STREAM specify a stateless search. */ matchCtxList.pNext = NULL; matchCtxList.pBufferList = &buffer_list; matchCtxList.sessionCtx = ipm->sessionCtx; matchCtxList.matchOptions = CPA_PM_MATCH_OPTION_RESET_SESSION | CPA_PM_MATCH_OPTION_END_OF_SESSION | CPA_PM_MATCH_OPTION_EOB_NOCALLBACK | CPA_PM_MATCH_OPTION_FIND_FIRST_MATCH; matchCtxList.matchCallback = IntelPmSearchCallback; matchCtxList.userData = ipm; matchCtxList.sizeMatchResults = DIM(matchResults); matchCtxList.pMatchResult = matchResults; status = cpaPmSearchExec(ipm_instance, &matchCtxList, NULL); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmSearchExec() failed: %s\n", GetCpaStatusStr(status)); IntelPmProcessQueue((IntelPmMatchQueue *)ipm->match_queue, ipm->match, ipm->data); return 0; } int IntelGetPatternCount(IntelPm *ipm) { if (ipm == NULL) return 0; return (int)ipm->patternIds; } int IntelPmPrintInfo(IntelPm *ipm) { return 0; } void IntelPmPrintSummary(SnortConfig *sc) { if (sc->ipm_handles == NULL) return; LogMessage("+-[Intel PM Search Info Summary]------------------\n"); LogMessage("| Instances : %u\n", sc->ipm_handles->pgids - 1); /* pattern groups start at 1 */ LogMessage("| Patterns : %u\n", sc->ipm_handles->pids); LogMessage("| Pattern Chars : %u\n", sc->ipm_handles->pcs); LogMessage("+-------------------------------------------------\n"); } #if 0 /* XXX Temporary because Intel wants some stats on buffer sizes */ void IntelPmPrintBufferStats(void) { int i; LogMessage("===============================================================================\n"); LogMessage("Intel stats\n\n"); LogMessage("Number of buffers per size of buffer\n"); for (i = 0; i < 65535; i++) { if (intel_pm_search_buf_sizes[i] > 0) LogMessage("%5u bytes : %7llu buffers\n", i, intel_pm_search_buf_sizes[i]); } LogMessage("\nNumber of matches: %llu\n", intel_pm_matches); LogMessage("===============================================================================\n"); } #endif void IntelPmStopInstance(void) { if (ipm_instance != NULL) { CpaStatus status = cpaPmStopInstance(ipm_instance); if (status != CPA_STATUS_SUCCESS) FatalError("cpaPmStopInstance() failed: %s\n", GetCpaStatusStr(status)); ipm_instance = NULL; } } snort-2.9.20/src/sfutil/util_utf.c0000644000175000017500000002213614241077363015200 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2010-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* Some UTF-{16,32}{le,be} normalization functions */ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "util_utf.h" #define DSTATE_FIRST 0 #define DSTATE_SECOND 1 #define DSTATE_THIRD 2 #define DSTATE_FOURTH 3 /* init a new decode_utf_state_t */ int init_decode_utf_state(decode_utf_state_t *new) { if (new == NULL) return DECODE_UTF_FAILURE; new->state = DSTATE_FIRST; new->charset = CHARSET_DEFAULT; return DECODE_UTF_SUCCESS; } /* terminate a decode_utf_state_t. returns DECODE_UTF_FAILURE if we're not at the base state. */ int term_decode_utf_state(decode_utf_state_t *dead) { if (dead == NULL) return DECODE_UTF_FAILURE; if (dead->state != DSTATE_FIRST) return DECODE_UTF_FAILURE; return DECODE_UTF_SUCCESS; } /* setters & getters */ int set_decode_utf_state_charset(decode_utf_state_t *dstate, int charset) { if (dstate == NULL) return DECODE_UTF_FAILURE; dstate->state = DSTATE_FIRST; dstate->charset = charset; return DECODE_UTF_SUCCESS; } int get_decode_utf_state_charset(decode_utf_state_t *dstate) { if (dstate == NULL) return DECODE_UTF_FAILURE; return dstate->charset; } /* Decode UTF-16le from src to dst. * * src => buffer containing utf-16le text * src_len => length of src * dst => buffer to write translated text * dst_len => length allocated for dst * bytes_copied => store the # of bytes copied to dst * dstate => saved state from last call * * returns: DECODE_UTF_SUCCESS or DECODE_UTF_FAILURE */ static int DecodeUTF16LE(char *src, unsigned int src_len, char *dst, unsigned int dst_len, int *bytes_copied, decode_utf_state_t *dstate) { char *src_index = src; char *dst_index = dst; int result = DECODE_UTF_SUCCESS; if (src == NULL || dst == NULL || bytes_copied == NULL || dstate == NULL || src_len == 0 || dst_len == 0) return DECODE_UTF_FAILURE; while ((src_index < (char *)(src + src_len)) && (dst_index < (char *)(dst + dst_len))) { /* Copy first byte, skip second, failing if second byte != 0 */ switch (dstate->state) { case DSTATE_FIRST: *dst_index++ = *src_index++; dstate->state = DSTATE_SECOND; break; case DSTATE_SECOND: if (*src_index++ > 0) result = DECODE_UTF_FAILURE; dstate->state = DSTATE_FIRST; break; default: return DECODE_UTF_FAILURE; } } *bytes_copied = (int) (dst_index - dst); return result; } /* Decode UTF-16be from src to dst. * * src => buffer containing utf-16le text * src_len => length of src * dst => buffer to write translated text * dst_len => length allocated for dst * bytes_copied => store the # of bytes copied to dst * dstate => saved state from last call * * returns: DECODE_UTF_SUCCESS or DECODE_UTF_FAILURE */ static int DecodeUTF16BE(char *src, unsigned int src_len, char *dst, unsigned int dst_len, int *bytes_copied, decode_utf_state_t *dstate) { char *src_index = src; char *dst_index = dst; int result = DECODE_UTF_SUCCESS; if (src == NULL || dst == NULL || bytes_copied == NULL || dstate == NULL || src_len == 0 || dst_len == 0) return DECODE_UTF_FAILURE; while ((src_index < (char *)(src + src_len)) && (dst_index < (char *)(dst + dst_len))) { /* Skip first byte, copy second. */ switch (dstate->state) { case DSTATE_FIRST: if (*src_index++ > 0) result = DECODE_UTF_FAILURE; dstate->state = DSTATE_SECOND; break; case DSTATE_SECOND: *dst_index++ = *src_index++; dstate->state = DSTATE_FIRST; break; default: return DECODE_UTF_FAILURE; } } *bytes_copied = (int) (dst_index - dst); return result; } /* Decode UTF-32le from src to dst. * * src => buffer containing utf-16le text * src_len => length of src * dst => buffer to write translated text * dst_len => length allocated for dst * bytes_copied => store the # of bytes copied to dst * dstate => saved state from last call * * returns: DECODE_UTF_SUCCESS or DECODE_UTF_FAILURE */ static int DecodeUTF32LE(char *src, unsigned int src_len, char *dst, unsigned int dst_len, int *bytes_copied, decode_utf_state_t *dstate) { char *src_index = src; char *dst_index = dst; int result = DECODE_UTF_SUCCESS; if (src == NULL || dst == NULL || bytes_copied == NULL || dstate == NULL || src_len == 0 || dst_len == 0) return DECODE_UTF_FAILURE; while ((src_index < (char *)(src + src_len)) && (dst_index < (char *)(dst + dst_len))) { /* Copy the first byte, then skip three. */ switch (dstate->state) { case DSTATE_FIRST: *dst_index++ = *src_index++; dstate->state++; break; case DSTATE_SECOND: case DSTATE_THIRD: case DSTATE_FOURTH: if (*src_index++ > 0) result = DECODE_UTF_FAILURE; if (dstate->state == DSTATE_FOURTH) dstate->state = DSTATE_FIRST; else dstate->state++; break; default: return DECODE_UTF_FAILURE; } } *bytes_copied = (int) (dst_index - dst); return result; } /* Decode UTF-32be from src to dst. * * src => buffer containing utf-16le text * src_len => length of src * dst => buffer to write translated text * dst_len => length allocated for dst * bytes_copied => store the # of bytes copied to dst * dstate => saved state from last call * * returns: DECODE_UTF_SUCCESS or DECODE_UTF_FAILURE */ static int DecodeUTF32BE(char *src, unsigned int src_len, char *dst, unsigned int dst_len, int *bytes_copied, decode_utf_state_t *dstate) { char *src_index = src; char *dst_index = dst; int result = DECODE_UTF_SUCCESS; if (src == NULL || dst == NULL || bytes_copied == NULL || dstate == NULL || src_len == 0 || dst_len == 0) return DECODE_UTF_FAILURE; while ((src_index < (char *)(src + src_len)) && (dst_index < (char *)(dst + dst_len))) { /* Skip 3 bytes, copy the fourth. */ switch (dstate->state) { case DSTATE_FIRST: case DSTATE_SECOND: case DSTATE_THIRD: if (*src_index++ > 0) result = DECODE_UTF_FAILURE; dstate->state++; break; case DSTATE_FOURTH: *dst_index++ = *src_index++; dstate->state = DSTATE_FIRST; break; default: return DECODE_UTF_FAILURE; } } *bytes_copied = (int) (dst_index - dst); return result; } /* Wrapper function for DecodeUTF{16,32}{LE,BE} */ int DecodeUTF(char *src, unsigned int src_len, char *dst, unsigned int dst_len, int *bytes_copied, decode_utf_state_t *dstate) { if (!bytes_copied) return DECODE_UTF_FAILURE; *bytes_copied = 0; // FIXIT-L Should make this an assert since it should never happen if (src == NULL || dst == NULL || dstate == NULL || src_len == 0 || dst_len == 0) return DECODE_UTF_FAILURE; switch (dstate->charset) { case CHARSET_UTF16LE: return DecodeUTF16LE(src, src_len, dst, dst_len, bytes_copied, dstate); case CHARSET_UTF16BE: return DecodeUTF16BE(src, src_len, dst, dst_len, bytes_copied, dstate); case CHARSET_UTF32LE: return DecodeUTF32LE(src, src_len, dst, dst_len, bytes_copied, dstate); case CHARSET_UTF32BE: return DecodeUTF32BE(src, src_len, dst, dst_len, bytes_copied, dstate); } /* In case the function is called with a bad charset. */ return DECODE_UTF_FAILURE; } snort-2.9.20/src/sfutil/sfPolicyUserData.c0000644000175000017500000001312614241077302016556 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "stdlib.h" #include "string.h" #include "sf_types.h" #include "sfPolicy.h" #include "sfPolicyUserData.h" /** @defgroup sfPolicyConfig Sourcefire policy configuration module * * Create a user policy configuration context. A context provides facility for creating * policy specific data instances. User can create as many policy instances as memory * resources will allow. User can create/delete context, set/clear/get user date for a * specific policy, default policy or current policy. User can also iterate over all instances * user data. * * In current design, preprocessor use this module directly to manage policy specific data * instances. A future enhancement can be to extract policy management code from each processor * and put it in a new policy management module. Policy management module will set a single * pointer to user data before calling appropriate callback function in a preprocessor. As * an example, policy module will iterate over all policies and call CleanExit functions in every * preprocessor for each policy. This will make policy management module will hide policies from * preprocessors and make them policy agnostic. * @{ */ /**Create a user context. * Allocates a new context and return it to user. All transactions within a context are independent from * any other transactions in a different context. * * @returns tSfPolicyUserContextId */ tSfPolicyUserContextId sfPolicyConfigCreate(void) { tSfPolicyUserContext *pTmp = NULL; pTmp = calloc(1, sizeof(tSfPolicyUserContext)); return pTmp; } /**Delete a user policy data context. * @param pContext */ void sfPolicyConfigDelete( tSfPolicyUserContextId pContext ) { if (pContext == NULL) return; if (pContext->userConfig != NULL) free(pContext->userConfig); free(pContext); } /**Store a pointer to user data. * @param pContext * @param policyId is 0 based. * @param config - pointer to user configuration. */ int sfPolicyUserDataSet ( tSfPolicyUserContextId pContext, tSfPolicyId policyId, void *config ) { void **ppTmp; if (policyId >= pContext->numAllocatedPolicies) { //expand the array ppTmp = (void **)calloc(policyId+POLICY_ALLOCATION_CHUNK, sizeof(void *)); if (!(ppTmp)) { return -1; } if (pContext->numAllocatedPolicies) { memcpy(ppTmp, pContext->userConfig, sizeof(void*)*(pContext->numAllocatedPolicies)); free(pContext->userConfig); } pContext->userConfig = ppTmp; pContext->numAllocatedPolicies = policyId + POLICY_ALLOCATION_CHUNK; } if (pContext->userConfig[policyId]) { //dont overwrite existing configuration return -1; } pContext->userConfig[policyId] = config; pContext->numActivePolicies++; return 0; } /**user is responsible for freeing any memory. */ void * sfPolicyUserDataClear ( tSfPolicyUserContextId pContext, tSfPolicyId policyId ) { void *pTmp = NULL; if (policyId < pContext->numAllocatedPolicies) { pTmp = pContext->userConfig[policyId]; pContext->userConfig[policyId] = NULL; pContext->numActivePolicies--; } return pTmp; } int sfPolicyUserDataIterate ( struct _SnortConfig *sc, tSfPolicyUserContextId pContext, int (*callback)(struct _SnortConfig *sc, tSfPolicyUserContextId pContext, tSfPolicyId policyId, void* config) ) { tSfPolicyId policyId; int ret = 0; //must not use numActivePolicies because the callback may delete a policy for (policyId = 0; policyId < pContext->numAllocatedPolicies; policyId++) { if (pContext->userConfig[policyId]) { ret = callback(sc, pContext, policyId, pContext->userConfig[policyId]); if (ret != 0) break; } } return ret; } int sfPolicyUserDataFreeIterate ( tSfPolicyUserContextId pContext, int (*callback)(tSfPolicyUserContextId pContext, tSfPolicyId policyId, void* config) ) { tSfPolicyId policyId; int ret = 0; //must not use numActivePolicies because the callback may delete a policy for (policyId = 0; policyId < pContext->numAllocatedPolicies; policyId++) { if (pContext->userConfig[policyId]) { ret = callback(pContext, policyId, pContext->userConfig[policyId]); if (ret != 0) break; } } return ret; } /** @} */ // snort-2.9.20/src/sfutil/sf_iph.h0000644000175000017500000000521014241077244014612 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2007-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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 nto, write to the Free Software ** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, ** USA */ #ifndef SFIPH_H #define SFIPH_H struct _Packet; typedef struct _IPH_API { sfaddr_t * (*iph_ret_src)(const struct _Packet *); sfaddr_t * (*iph_ret_dst)(const struct _Packet *); uint16_t (*iph_ret_tos)(const struct _Packet *); uint8_t (*iph_ret_ttl)(const struct _Packet *); uint16_t (*iph_ret_len)(const struct _Packet *); uint32_t (*iph_ret_id)(const struct _Packet *); uint8_t (*iph_ret_proto)(const struct _Packet *); uint16_t (*iph_ret_off)(const struct _Packet *); uint8_t (*iph_ret_ver)(const struct _Packet *); uint8_t (*iph_ret_hlen)(const struct _Packet *); sfaddr_t * (*orig_iph_ret_src)(const struct _Packet *); sfaddr_t * (*orig_iph_ret_dst)(const struct _Packet *); uint16_t (*orig_iph_ret_tos)(const struct _Packet *); uint8_t (*orig_iph_ret_ttl)(const struct _Packet *); uint16_t (*orig_iph_ret_len)(const struct _Packet *); uint32_t (*orig_iph_ret_id)(const struct _Packet *); uint8_t (*orig_iph_ret_proto)(const struct _Packet *); uint16_t (*orig_iph_ret_off)(const struct _Packet *); uint8_t (*orig_iph_ret_ver)(const struct _Packet *); uint8_t (*orig_iph_ret_hlen)(const struct _Packet *); char ver; } IPH_API; extern IPH_API ip4; extern IPH_API ip6; #define IPH_API_V4 4 #define IPH_API_V6 6 #define iph_is_valid(p) ((p)->family != NO_IP) #define NO_IP 0 void sfiph_build(struct _Packet *p, const void *hdr, int family); void sfiph_orig_build(struct _Packet *p, const void *hdr, int family); /* Sets the callbacks to point at the family selected by * * "family". "family" is either AF_INET or AF_INET6 */ #define CALLBACK_IP 0 #define CALLBACK_ICMP_ORIG 1 void set_callbacks(struct _Packet *p, int family, char orig); #endif snort-2.9.20/src/sfutil/md5.h0000644000175000017500000000226014241077225014030 0ustar apoapo#include "config.h" #ifndef HAVE_OPENSSL_MD5 #ifndef MD5_H #define MD5_H #ifndef HEADER_MD5_H /* Try to avoid clashes with OpenSSL */ #define HEADER_MD5_H #endif #include struct MD5Context { uint32_t buf[4]; uint32_t bits[2]; unsigned char in[64]; }; #endif /* !MD5_H */ #ifndef _HMAC_MD5_H struct HMACMD5Context { struct MD5Context ctx; unsigned char k_ipad[65]; unsigned char k_opad[65]; }; #endif /* _HMAC_MD5_H */ void MD5Init(struct MD5Context *context); void MD5Update(struct MD5Context *context, unsigned char const *buf, unsigned len); void MD5Final(unsigned char digest[16], struct MD5Context *context); /* The following definitions come from lib/hmacmd5.c */ /* void hmac_md5_init_rfc2104(unsigned char *key, int key_len, struct HMACMD5Context *ctx);*/ void hmac_md5_init_limK_to_64(const unsigned char *key, int key_len, struct HMACMD5Context *ctx); void hmac_md5_update(const unsigned char *text, int text_len, struct HMACMD5Context *ctx); void hmac_md5_final(unsigned char *digest, struct HMACMD5Context *ctx); void hmac_md5(unsigned char key[16], unsigned char *data, int data_len, unsigned char *digest); #endif /* !HAVE_OPENSSL_MD5 */ snort-2.9.20/src/sfutil/asn1.c0000644000175000017500000006616614241077206014216 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file asn1.c ** ** @author Daniel Roelker ** ** @brief ASN.1 Decoding API for BER and DER encodings. ** ** Author: Daniel Roelker ** ** ASN.1 decoding functions that incorporate an internal stack for ** processing. That way we don't have to worry about attackers trying ** to overload the machine stack. ** ** Handles both DER and BER encodings, and also the indefinite encoding ** that BER supports. Lots of functionality can be added on top of ** this library. SNMP will probably be the first. ** ** NOTES: ** - Stop using global variables so we can have multiple instances, ** but we don't need that functionality right now. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #ifdef WIN32 #include #endif #include "asn1.h" #include "util.h" /* ** Macros */ #define SF_ASN1_CLASS(c) (((u_char)c) & SF_ASN1_CLASS_MASK) #define SF_ASN1_FLAG(c) (((u_char)c) & SF_ASN1_FLAG_MASK) #define SF_ASN1_TAG(c) (((u_char)c) & SF_ASN1_TAG_MASK) #define SF_ASN1_LEN_EXT(c) (((u_char)c) & SF_BER_LEN_MASK) #define ASN1_OOB(s,e,d) (!(((s) <= (d)) && ((d) < (e)))) #define ASN1_FATAL_ERR(e) ((e) < 0) #define ASN1_NONFATAL_ERR(e) ((e) > 0) #define ASN1_MAX_STACK 128 ASN1_CONFIG asn1_config; /* ** NAME ** asn1_init_node_index:: */ /** ** This function should get called whenever we decode a new ASN.1 ** string to initialize the memory. ** ** @return void */ static void asn1_init_node_index(void) { asn1_config.node_index = 0; } /* ** NAME ** asn1_node_alloc:: */ /** ** Allocate an ASN1_NODE. ** ** @return ASN1_TYPE * ** ** @retval NULL memory allocation failed ** @retval !NULL function successful */ static ASN1_TYPE * asn1_node_alloc(void) { if ((asn1_config.mem == NULL) || (asn1_config.num_nodes <= asn1_config.node_index)) return NULL; return &asn1_config.mem[asn1_config.node_index++]; } /* ** NAME ** asn1_init_mem:: */ /** ** This function initializes the number of nodes that we want to track in ** an ASN.1 decode. Pass in the max number of nodes for an ASN.1 decode and ** we will track that many. ** ** @return integer ** ** @retval ASN1_OK function successful ** @retval ASN1_ERR_MEM_ALLOC memory allocation failed ** @retval ASN1_ERR_INVALID_ARG invalid argument */ void asn1_init_mem(int num_nodes) { if (num_nodes <= 0) return; asn1_config.mem = (ASN1_TYPE *)SnortAlloc(sizeof(ASN1_TYPE) * num_nodes); asn1_config.num_nodes = num_nodes; asn1_config.node_index = 0; } /* ** NAME ** asn1_free_mem:: */ /** ** This function frees the number of nodes that we were tracking in ** an ASN.1 decode. ** ** @return none ** */ void asn1_free_mem(void) { if (asn1_config.mem != NULL) { free(asn1_config.mem); asn1_config.mem = NULL; } } /* ** NAME ** asn1_decode_tag_num_ext:: */ /** ** This routine decodes extended tag numbers and checks for overlong ** tag numbers, etc. ** ** @param ASN1_DATA ptr to data ** @param u_int ptr to tag num ** ** @return integer ** ** @retval ASN1_OK function successful ** @retval ASN1_ERR_OVERLONG_LEN tag number too large ** @retval ASN1_ERR_OOB encoding goes out of bounds ** @retval ASN1_ERR_NULL_MEM function arguments are NULL */ static int asn1_decode_tag_num_ext(ASN1_DATA *asn1_data, u_int *tag_num) { int iExtension = 0; u_int new_tag_num; if(!asn1_data || !tag_num) return ASN1_ERR_NULL_MEM; *tag_num = 0; /* ** Loop through the tag type while extension bit is set */ do { /* ** Is this an extension byte? */ iExtension = SF_ASN1_LEN_EXT(*asn1_data->data); new_tag_num = ((*tag_num << 7) | (*asn1_data->data & 0x7f)); if(*tag_num != 0 && new_tag_num <= *tag_num) { return ASN1_ERR_OVERLONG_LEN; } *tag_num = new_tag_num; asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { return ASN1_ERR_OOB; } } while(iExtension); return ASN1_OK; } /* ** NAME ** asn1_decode_ident:: */ /** ** This function decodes the identifier byte(s) of an ASN.1 structure. ** We handle long tag numbers and check for overflows in the extended ** tag numbers. ** ** @return integer ** ** @retval ASN1_ERR_NULL_MEM function arguments are NULL ** @retval ASN1_ERR_OOB buffer out of bounds ** @retval ASN1_ERR_INVALID_BER_TAG_LEN tag num too large or bad encoding ** @retval ASN1_OK function ok */ static int asn1_decode_ident(ASN1_TYPE *asn1_type, ASN1_DATA *asn1_data) { ASN1_IDENT *ident; int iRet; if(!asn1_type || !asn1_data) return ASN1_ERR_NULL_MEM; ident = &asn1_type->ident; ident->asn1_class = SF_ASN1_CLASS(*asn1_data->data); ident->flag = SF_ASN1_FLAG(*asn1_data->data); ident->tag = SF_ASN1_TAG(*asn1_data->data); asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { //printf("** decode_ident: oob\n"); return ASN1_ERR_OOB; } /* ** Is tag extended? */ if(ident->tag == SF_ASN1_TAG_EXTENSION) { ident->tag_type = SF_ASN1_TAG_EXTENSION; iRet = asn1_decode_tag_num_ext(asn1_data, &ident->tag); if(iRet) { //printf("** decode_ident: ext_len error\n"); return ASN1_ERR_INVALID_BER_TAG_LEN; } } return ASN1_OK; } /* ** NAME ** asn1_decode_len_type:: */ /** ** Determine the type of len encoding. Could be short, long or ** indeterminate. ** ** @return integer ** ** @retval SF_BER_LEN_DEF_LONG extended length ** @retval SF_BER_LEN_DEF_SHORT one byte length < 127 ** @retval SF_BER_LEN_INDEF indeterminate length */ static int asn1_decode_len_type(const u_char *data) { int iExt; iExt = SF_ASN1_LEN_EXT(*data); if(iExt) { if(*data & 0x7f) { return SF_BER_LEN_DEF_LONG; } else { return SF_BER_LEN_INDEF; } } return SF_BER_LEN_DEF_SHORT; } /* ** NAME ** asn1_decode_len_ext:: */ /** ** Decode the extended length version. Basically we read the first ** byte for the number of bytes in the extended length. We then read ** that number of bytes to determine the length. If the number of bytes ** in the length is greater than our variable, then we return ** ASN1_ERR_OVERLONG_LEN, and exit decoding. ** ** @return integer ** ** @retval ASN1_ERR_NULL_MEM function arguments NULL ** @retval ASN1_ERR_OVERLONG_LEN length to long for us to decode ** @retval ASN1_ERR_OOB out of bounds condition ** @retval ASN1_OK function successful */ static int asn1_decode_len_ext(ASN1_DATA *asn1_data, u_int *size) { int iBytes; int iCtr; u_int new_size; if(!asn1_data || !size) return ASN1_ERR_NULL_MEM; *size = 0; iBytes = (*asn1_data->data & 0x7f); asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { return ASN1_ERR_OOB; } for(iCtr = 0; iCtr < iBytes; iCtr++) { new_size = ((*size << 8) | (*asn1_data->data)); /* ** If we've just added some data to the size, and ** we are still the same or less than the previous ** size, we've just overflowed our variable */ if(*size != 0 && new_size <= *size) { return ASN1_ERR_OVERLONG_LEN; } *size = new_size; asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { /* ** Check to see if this was just an extended length that was zero at ** the end of the buffer. If it was, then return normal. */ if(*size == 0 && (iCtr+1) == iBytes) break; return ASN1_ERR_OOB; } } return ASN1_OK; } /* ** NAME ** asn1_decode_len:: */ /** ** This function decodes the ASN.1 type length. Determines what type of ** BER encoding is used for the length and decodes that length. ** ** @return integer ** ** @retval ASN1_ERR_NULL_MEM function arguments NULL ** @retval ASN1_ERR_FATAL should never get this ** @retval ASN1_ERR_OOB out of bounds condition ** @retval ASN1_OK function successful */ static int asn1_decode_len(ASN1_TYPE *asn1_type, ASN1_DATA *asn1_data) { ASN1_LEN *len; int iRet; if(!asn1_type || !asn1_data) return ASN1_ERR_NULL_MEM; len = &asn1_type->len; len->type = (unsigned char)asn1_decode_len_type(asn1_data->data); switch(len->type) { case SF_BER_LEN_DEF_SHORT: len->size = *asn1_data->data; (asn1_data->data)++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) { /* ** Only return OOB if the short length wasn't zero. Otherwise, ** it's a valid encoding. */ if(len->size != 0) return ASN1_ERR_OOB; } break; case SF_BER_LEN_DEF_LONG: iRet = asn1_decode_len_ext(asn1_data, &len->size); if(iRet) return iRet; break; case SF_BER_LEN_INDEF: /* ** Not sure what to do here, so we'll just set the length ** to 0 and proceed for now. */ len->size = 0; asn1_data->data++; if(ASN1_OOB(asn1_data->start, asn1_data->end, asn1_data->data)) return ASN1_ERR_OOB; break; default: /* ** This should be one of the three values. So we are in ** error condition. */ return ASN1_ERR_FATAL; } return ASN1_OK; } /* ** NAME ** asn1_is_eoc:: */ /** ** This function checks and ASN1_TYPE for end-of-content encoding. This ** doesn't determine that this is what it is, but what it could be. ** ** @return int ** ** @retval 0 not EOC ** @retval 1 is EOC */ static int asn1_is_eoc(ASN1_TYPE *asn1) { if(!asn1) return 0; if(asn1->ident.asn1_class == 0x00 && asn1->ident.flag == 0x00 && asn1->ident.tag == 0x00 && asn1->len.type == SF_BER_LEN_DEF_SHORT && asn1->len.size == 0) { return 1; } return 0; } /* ** NAME ** asn1_decode_type:: */ /** ** This function decodes an ASN1_TYPE structure. It processes the type in ** three parts. ** ** 1) Identifier ** 2) Length ** 3) Data ** ** The data processing skips over primitive data (if it can) and processes ** construct data (if it can). ** ** This function also updates the data and len ptrs so we continue moving ** through the data. ** ** @return integer ** ** @retval ASN1_OK function successful ** @retval ASN1_ERR_MEM_ALLOC memory allocation failed ** @retval ASN1_ERR_INVALID_INDEF_LEN invalid indefinite encoding ** @retval ASN1_ERR_INVALID_ARG invalid argument ** @retval ASN1_ERR_OOB out of bounds */ static int asn1_decode_type(const u_char **data, u_int *len, ASN1_TYPE **asn1_type) { ASN1_DATA asn1data; u_int uiRawLen; int iRet; if(!*data) return ASN1_ERR_INVALID_ARG; *asn1_type = NULL; /* ** Check len first, because if it's 0, then we already decoded a valid ** construct. We let the caller know this, by returning OK, but setting ** the asn1_type ptr to NULL. */ if(*len == 0) return ASN1_OK; if(ASN1_OOB(*data, (*data) + *len, *data)) return ASN1_ERR_OOB; *asn1_type = asn1_node_alloc(); if(*asn1_type == NULL) { return ASN1_ERR_MEM_ALLOC; } memset(*asn1_type, 0x00, sizeof(ASN1_TYPE)); asn1data.start = *data; asn1data.end = (*data) + *len; asn1data.data = *data; iRet = asn1_decode_ident(*asn1_type, &asn1data); if(iRet) { return iRet; } iRet = asn1_decode_len(*asn1_type, &asn1data); if(iRet) { return iRet; } /* ** Set this varible here, so we can set the data_len for ** indeterminate constructs. */ uiRawLen = asn1data.end - asn1data.data; /* ** This is an important check. If the length is zero, it means that ** we've either hit a zero length type or we've hit a BER indefinite ** encoding (hate those). ** ** Standard says that only constructs can have the indefinite length ** encoding, but we still need to "prove" that. Thanks M$. */ if(!(*asn1_type)->len.size) { if((*asn1_type)->len.type != SF_BER_LEN_INDEF || (*asn1_type)->ident.flag == SF_ASN1_FLAG_CONSTRUCT) { (*asn1_type)->data = asn1data.data; if((*asn1_type)->len.type == SF_BER_LEN_INDEF) { (*asn1_type)->data_len = uiRawLen; } else { /* ** If we're not an indefinite type, then we check to ** see if we are an eoc, so we don't have to check again. */ (*asn1_type)->data_len = 0; if(asn1_is_eoc(*asn1_type)) (*asn1_type)->eoc = 1; } goto valid; } return ASN1_ERR_INVALID_INDEF_LEN; } /* ** Set data ptr for asn1 types that have data. */ (*asn1_type)->data = asn1data.data; /* ** Check for the ASN.1 type being larger than we have room for. */ if(uiRawLen < (*asn1_type)->len.size) { (*asn1_type)->data_len = uiRawLen; /* ** If we're a construct, then don't skip over the data because ** we have to process it. */ if((*asn1_type)->ident.flag == SF_ASN1_FLAG_CONSTRUCT) goto valid; return ASN1_ERR_OOB; } /* ** We got enough data in the buffer for the true identifier size, so ** we set it. */ (*asn1_type)->data_len = (*asn1_type)->len.size; /* ** Only jump data that's not going to be decoded. That means jump ** over primitive data and decode construct data. */ if(!((*asn1_type)->ident.flag == SF_ASN1_FLAG_CONSTRUCT)) { asn1data.data += (*asn1_type)->len.size; } valid: /* ** Update data buffer, before we return. Depending on if we just decoded ** a zero length identifier and are on the last data byte, we could be at ** the end of our buffer. Otherwise, we're still in the buffer. */ *len = asn1data.end - asn1data.data; *data = asn1data.data; return ASN1_OK; } /* ** NAME ** asn1_decode:: */ /** ** This function decodes an ASN.1 string and returns the decoded ** structures. We BER encoding, which means we handle both ** definite and indefinite length encodings (that was a B). ** ** @return integer ** ** @retval ASN1_OK function successful ** @retval !ASN1_OK lots of error conditions, figure it out */ int asn1_decode(const u_char *data, u_int len, ASN1_TYPE **asn1_type) { ASN1_TYPE *cur; ASN1_TYPE *child = NULL; ASN1_TYPE *indef; ASN1_TYPE *asnstack[ASN1_MAX_STACK]; const u_char *end; u_int con_len; int index = 0; int iRet; if(!data || !len) return ASN1_ERR_NULL_MEM; asn1_init_node_index(); /* ** Keep track of where the end of the data buffer is so we can continue ** processing if there is a construct. */ end = data + len; iRet = asn1_decode_type(&data,&len,asn1_type); if(iRet || !(*asn1_type)) { //printf("** initial bad decode\n"); return iRet; } cur = *asn1_type; while(cur) { /* ** This is where we decode the ASN.1 constructs. We do while() ** because we may have back to back constructs. We bail on the ** first indentifier that isn't a construct. */ while(cur && cur->ident.flag == SF_ASN1_FLAG_CONSTRUCT) { if(index < ASN1_MAX_STACK) asnstack[index++] = cur; else return ASN1_ERR_STACK; /* ** We now set the current len for this constructs true length, ** or raw length if true length is past buffer. */ if(cur->len.type != SF_BER_LEN_INDEF) { if(len < cur->data_len) return ASN1_ERR_OVERLONG_LEN; len = cur->data_len; } iRet = asn1_decode_type(&data, &len, &cur->cnext); if(iRet) { return iRet; } /* ** Check next child for ending of indefinite encodings. */ if(cur->cnext && cur->cnext->eoc) { if(index && (indef = asnstack[--index]) != NULL) { if(indef->len.type == SF_BER_LEN_INDEF) { indef->len.size = data - indef->data - 2; indef->data_len = indef->len.size; cur->cnext = NULL; cur = indef; break; } else { /* ** Not an EOC type, so it's just a strange child ** encoding. Put the construct back on the stack. */ asnstack[index++] = indef; } } } cur = cur->cnext; } /* ** If there is a node, then process any peers that this node has. */ if(cur) { iRet = asn1_decode_type(&data, &len, &cur->next); if(iRet) return iRet; /* ** Cycle through any eoc that might be back to back */ while(cur->next && cur->next->eoc) { if(index && (indef = asnstack[--index]) != NULL) { if(indef->len.type == SF_BER_LEN_INDEF) { indef->len.size = data - indef->data - 2; indef->data_len = indef->len.size; cur->next = NULL; cur = indef; iRet = asn1_decode_type(&data, &len, &cur->next); if(iRet) { return iRet; } continue; } asnstack[index++] = indef; } break; } cur = cur->next; if(cur) continue; } /* ** We only get here if the peer decode fails. ** ** Traverse the stack and close off any constructs that we ** are done with. This gets a little trickier, because we have to ** check for additional peers for each construct, depending on the ** length of the parent construct. */ while(index && (cur = asnstack[--index]) != NULL) { /* ** Get the construct length and set the length appropriately ** if there is more data in this construct. */ con_len = data - cur->data; if(cur->data_len > con_len) { len = cur->data_len - con_len; } /* ** If this construct has no more data left, then save it off as ** the last child of the previous construct. */ if(len == 0) { child = cur; } else if(child) { /* ** Means this construct has more data left, so if the child is set ** then we set it's next ptr. Otherwise, this means we are in ** an indeterminate construct, and need to check for eoc before we ** continue processing. */ asnstack[index++] = cur; cur = child; child = NULL; } iRet = asn1_decode_type(&data, &len, &cur->next); if(iRet) { return iRet; } if(cur->next && cur->next->eoc) { if(index && (indef = asnstack[--index]) != NULL) { if(indef->len.type == SF_BER_LEN_INDEF) { indef->len.size = data - indef->data - 2; indef->data_len = indef->len.size; cur->next = NULL; cur = indef; } else { asnstack[index++] = indef; } } } /* ** This logic tell us that we are on the root construct, but there ** are additional peers because there is more data. We recalculate ** the length and continue on. ** ** NOTE: ** We may not want this because research may only be able to point ** us at the first sequence and it's anyone's guess after that. */ if(!index && !(cur->next) && (data < end)) { len = (end - data); iRet = asn1_decode_type(&data, &len, &cur->next); if(iRet) return iRet; } cur = cur->next; if(cur) break; } /* ** The loop logic bails us out if there is no cur. */ } return ASN1_OK; } /* ** NAME ** asn1_traverse:: */ /** ** This function traverses a decoded ASN1 structure, applying a detection ** function for the different types. This is just to make this user stack ** generic AND easy. ** ** @return integer ** ** @retval 1 detection function successful ** @retval 0 detection function unsuccessful */ int asn1_traverse(ASN1_TYPE *asn1, void *user, int (*DetectFunc)(ASN1_TYPE *, void *)) { ASN1_TYPE *asnstack[ASN1_MAX_STACK]; int index = 0; ASN1_TYPE *cur; int iRet; if(!asn1) return 0; cur = asn1; while(cur) { while(cur && cur->ident.flag == SF_ASN1_FLAG_CONSTRUCT) { if(index < ASN1_MAX_STACK) asnstack[index++] = cur; else return 0; iRet = DetectFunc(cur, user); if(iRet) return 1; cur = cur->cnext; } if(cur) { iRet = DetectFunc(cur, user); if(iRet) return 1; cur = cur->next; if(cur) continue; } while(index && (cur = asnstack[--index]) != NULL) { cur = cur->next; if(cur) break; } } return 0; } /* ** NAME ** asn1_print_types:: */ /** ** Print out the ASN.1 type. ** ** @return integer ** ** @retval 0 printed */ int asn1_print_types(ASN1_TYPE *asn1_type, void *user) { unsigned int iTabs = 0; unsigned int iCtr; if(user) iTabs = *((int *)user); for(iCtr = 0; iCtr < iTabs; iCtr++) printf(" "); printf("## PRINT ASN1_TYPE STRUCTURE ##\n"); for(iCtr = 0; iCtr < iTabs; iCtr++) printf(" "); printf("IDENT - asn1_class: %.2x | flag: %.2x | tag_type: %.2x | " "tag_num: %d\n", asn1_type->ident.asn1_class, asn1_type->ident.flag, asn1_type->ident.tag_type, asn1_type->ident.tag); for(iCtr = 0; iCtr < iTabs; iCtr++) printf(" "); printf("LEN - type: %d | size: %u\n", asn1_type->len.type, asn1_type->len.size); for(iCtr = 0; iCtr < iTabs; iCtr++) printf(" "); printf("DATA | data_len: %d | ", asn1_type->data_len); if(asn1_type->data) { for(iCtr = 0; iCtr < asn1_type->data_len; iCtr++) printf(" %.2x", asn1_type->data[iCtr]); } else { printf(" NULL"); } printf("\n\n"); /* printf("\n"); //if(BitStringOverflow(asn1_type)) //{ // printf("!! BITSTRING OVERFLOW\n"); //} printf("\n"); if(asn1_type->cnext) asn1_print_types(asn1_type->cnext, iTabs+1); if(asn1_type->next) asn1_print_types(asn1_type->next, iTabs); */ return 0; } #ifdef I_WANT_MAIN_DAMMIT static int BitStringOverflow(ASN1_TYPE *asn1_type) { if(!asn1_type) return 0; if(asn1_type->ident.tag == SF_ASN1_TAG_BIT_STR && !asn1_type->ident.flag) { if(((asn1_type->len.size - 1)*8) < (u_int)asn1_type->data[0]) { return 1; } } return 0; } /* ** Program reads from stdin and decodes the hexadecimal ASN.1 stream ** into identifier,len,data. */ int main(int argc, char **argv) { ASN1_TYPE *asn1_type; char line[10000]; u_int ctmp; char *buf; int buf_size; int iCtr; int iRet; fgets(line, sizeof(line), stdin); buf_size = strlen(line); while(buf_size && line[buf_size-1] <= 0x20) { buf_size--; line[buf_size] = 0x00; } if(!buf_size) { printf("** No valid characters in data string.\n"); return 1; } if(buf_size % 2) { printf("** Data must be represent in hex, meaning that there is an " "odd number of characters in the data string.\n"); return 1; } buf_size >>= 1; buf = calloc(1,buf_size + 1); if(!buf) { return 1; } for(iCtr = 0; iCtr < buf_size; iCtr++) { if(!(isxdigit(line[iCtr*2]) && isxdigit(line[(iCtr*2)+1]))) { printf("** Data stream is not all hex digits.\n"); return 1; } sscanf(&line[iCtr*2], "%2x", &ctmp); buf[iCtr] = (char)ctmp; } buf[iCtr] = 0x00; asn1_init_mem(256); iRet = asn1_decode(buf, buf_size, &asn1_type); if(iRet && !asn1_type) { printf("** FAILED\n"); return 1; } printf("** iRet = %d\n", iRet); asn1_print_types(asn1_type, 0); free(buf); return 0; } #endif snort-2.9.20/src/sfutil/sfActionQueue.h0000644000175000017500000000331414241077257016124 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _SF_ACTION_QUEUE_ #define _SF_ACTION_QUEUE_ #include "mempool.h" typedef struct { MemPool mempool; } tSfActionQueue; typedef tSfActionQueue* tSfActionQueueId; typedef struct _sfActionNode { void (*callback)(void *); void *data; } tSfActionNode; tSfActionQueueId sfActionQueueInit( int queueLength ); int sfActionQueueAdd( tSfActionQueueId actionQ, void (*callback)(void *), void *data ); void sfActionQueueExecAll( tSfActionQueueId actionQ ); void sfActionQueueExec( tSfActionQueueId actionQ ); void sfActionQueueDestroy( tSfActionQueueId actionQ ); #endif snort-2.9.20/src/sfutil/sfprimetable.c0000644000175000017500000026120714241077311016017 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * sfprimetable.c * * Prime number calculation via Table lookups. * * This was implemented for use with the hasing functions * in sfghash, and sfxhash. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sfprimetable.h" /* 0-8K, increments=8 */ static unsigned prime_table0[1024]={ 3, /* 1 */ 7, /* 9 */ 17, /* 17 */ 23, /* 25 */ 31, /* 33 */ 41, /* 41 */ 47, /* 49 */ 53, /* 57 */ 61, /* 65 */ 73, /* 73 */ 79, /* 81 */ 89, /* 89 */ 97, /* 97 */ 103, /* 105 */ 113, /* 113 */ 113, /* 121 */ 127, /* 129 */ 137, /* 137 */ 139, /* 145 */ 151, /* 153 */ 157, /* 161 */ 167, /* 169 */ 173, /* 177 */ 181, /* 185 */ 193, /* 193 */ 199, /* 201 */ 199, /* 209 */ 211, /* 217 */ 223, /* 225 */ 233, /* 233 */ 241, /* 241 */ 241, /* 249 */ 257, /* 257 */ 263, /* 265 */ 271, /* 273 */ 281, /* 281 */ 283, /* 289 */ 293, /* 297 */ 293, /* 305 */ 313, /* 313 */ 317, /* 321 */ 317, /* 329 */ 337, /* 337 */ 337, /* 345 */ 353, /* 353 */ 359, /* 361 */ 367, /* 369 */ 373, /* 377 */ 383, /* 385 */ 389, /* 393 */ 401, /* 401 */ 409, /* 409 */ 409, /* 417 */ 421, /* 425 */ 433, /* 433 */ 439, /* 441 */ 449, /* 449 */ 457, /* 457 */ 463, /* 465 */ 467, /* 473 */ 479, /* 481 */ 487, /* 489 */ 491, /* 497 */ 503, /* 505 */ 509, /* 513 */ 521, /* 521 */ 523, /* 529 */ 523, /* 537 */ 541, /* 545 */ 547, /* 553 */ 557, /* 561 */ 569, /* 569 */ 577, /* 577 */ 577, /* 585 */ 593, /* 593 */ 601, /* 601 */ 607, /* 609 */ 617, /* 617 */ 619, /* 625 */ 631, /* 633 */ 641, /* 641 */ 647, /* 649 */ 653, /* 657 */ 661, /* 665 */ 673, /* 673 */ 677, /* 681 */ 683, /* 689 */ 691, /* 697 */ 701, /* 705 */ 709, /* 713 */ 719, /* 721 */ 727, /* 729 */ 733, /* 737 */ 743, /* 745 */ 751, /* 753 */ 761, /* 761 */ 769, /* 769 */ 773, /* 777 */ 773, /* 785 */ 787, /* 793 */ 797, /* 801 */ 809, /* 809 */ 811, /* 817 */ 823, /* 825 */ 829, /* 833 */ 839, /* 841 */ 839, /* 849 */ 857, /* 857 */ 863, /* 865 */ 863, /* 873 */ 881, /* 881 */ 887, /* 889 */ 887, /* 897 */ 887, /* 905 */ 911, /* 913 */ 919, /* 921 */ 929, /* 929 */ 937, /* 937 */ 941, /* 945 */ 953, /* 953 */ 953, /* 961 */ 967, /* 969 */ 977, /* 977 */ 983, /* 985 */ 991, /* 993 */ 997, /* 1001 */ 1009, /* 1009 */ 1013, /* 1017 */ 1021, /* 1025 */ 1033, /* 1033 */ 1039, /* 1041 */ 1049, /* 1049 */ 1051, /* 1057 */ 1063, /* 1065 */ 1069, /* 1073 */ 1069, /* 1081 */ 1087, /* 1089 */ 1097, /* 1097 */ 1103, /* 1105 */ 1109, /* 1113 */ 1117, /* 1121 */ 1129, /* 1129 */ 1129, /* 1137 */ 1129, /* 1145 */ 1153, /* 1153 */ 1153, /* 1161 */ 1163, /* 1169 */ 1171, /* 1177 */ 1181, /* 1185 */ 1193, /* 1193 */ 1201, /* 1201 */ 1201, /* 1209 */ 1217, /* 1217 */ 1223, /* 1225 */ 1231, /* 1233 */ 1237, /* 1241 */ 1249, /* 1249 */ 1249, /* 1257 */ 1259, /* 1265 */ 1259, /* 1273 */ 1279, /* 1281 */ 1289, /* 1289 */ 1297, /* 1297 */ 1303, /* 1305 */ 1307, /* 1313 */ 1321, /* 1321 */ 1327, /* 1329 */ 1327, /* 1337 */ 1327, /* 1345 */ 1327, /* 1353 */ 1361, /* 1361 */ 1367, /* 1369 */ 1373, /* 1377 */ 1381, /* 1385 */ 1381, /* 1393 */ 1399, /* 1401 */ 1409, /* 1409 */ 1409, /* 1417 */ 1423, /* 1425 */ 1433, /* 1433 */ 1439, /* 1441 */ 1447, /* 1449 */ 1453, /* 1457 */ 1459, /* 1465 */ 1471, /* 1473 */ 1481, /* 1481 */ 1489, /* 1489 */ 1493, /* 1497 */ 1499, /* 1505 */ 1511, /* 1513 */ 1511, /* 1521 */ 1523, /* 1529 */ 1531, /* 1537 */ 1543, /* 1545 */ 1553, /* 1553 */ 1559, /* 1561 */ 1567, /* 1569 */ 1571, /* 1577 */ 1583, /* 1585 */ 1583, /* 1593 */ 1601, /* 1601 */ 1609, /* 1609 */ 1613, /* 1617 */ 1621, /* 1625 */ 1627, /* 1633 */ 1637, /* 1641 */ 1637, /* 1649 */ 1657, /* 1657 */ 1663, /* 1665 */ 1669, /* 1673 */ 1669, /* 1681 */ 1669, /* 1689 */ 1697, /* 1697 */ 1699, /* 1705 */ 1709, /* 1713 */ 1721, /* 1721 */ 1723, /* 1729 */ 1733, /* 1737 */ 1741, /* 1745 */ 1753, /* 1753 */ 1759, /* 1761 */ 1759, /* 1769 */ 1777, /* 1777 */ 1783, /* 1785 */ 1789, /* 1793 */ 1801, /* 1801 */ 1801, /* 1809 */ 1811, /* 1817 */ 1823, /* 1825 */ 1831, /* 1833 */ 1831, /* 1841 */ 1847, /* 1849 */ 1847, /* 1857 */ 1861, /* 1865 */ 1873, /* 1873 */ 1879, /* 1881 */ 1889, /* 1889 */ 1889, /* 1897 */ 1901, /* 1905 */ 1913, /* 1913 */ 1913, /* 1921 */ 1913, /* 1929 */ 1933, /* 1937 */ 1933, /* 1945 */ 1951, /* 1953 */ 1951, /* 1961 */ 1951, /* 1969 */ 1973, /* 1977 */ 1979, /* 1985 */ 1993, /* 1993 */ 1999, /* 2001 */ 2003, /* 2009 */ 2017, /* 2017 */ 2017, /* 2025 */ 2029, /* 2033 */ 2039, /* 2041 */ 2039, /* 2049 */ 2053, /* 2057 */ 2063, /* 2065 */ 2069, /* 2073 */ 2081, /* 2081 */ 2089, /* 2089 */ 2089, /* 2097 */ 2099, /* 2105 */ 2113, /* 2113 */ 2113, /* 2121 */ 2129, /* 2129 */ 2137, /* 2137 */ 2143, /* 2145 */ 2153, /* 2153 */ 2161, /* 2161 */ 2161, /* 2169 */ 2161, /* 2177 */ 2179, /* 2185 */ 2179, /* 2193 */ 2179, /* 2201 */ 2207, /* 2209 */ 2213, /* 2217 */ 2221, /* 2225 */ 2221, /* 2233 */ 2239, /* 2241 */ 2243, /* 2249 */ 2251, /* 2257 */ 2251, /* 2265 */ 2273, /* 2273 */ 2281, /* 2281 */ 2287, /* 2289 */ 2297, /* 2297 */ 2297, /* 2305 */ 2311, /* 2313 */ 2311, /* 2321 */ 2311, /* 2329 */ 2333, /* 2337 */ 2341, /* 2345 */ 2351, /* 2353 */ 2357, /* 2361 */ 2357, /* 2369 */ 2377, /* 2377 */ 2383, /* 2385 */ 2393, /* 2393 */ 2399, /* 2401 */ 2399, /* 2409 */ 2417, /* 2417 */ 2423, /* 2425 */ 2423, /* 2433 */ 2441, /* 2441 */ 2447, /* 2449 */ 2447, /* 2457 */ 2459, /* 2465 */ 2473, /* 2473 */ 2477, /* 2481 */ 2477, /* 2489 */ 2477, /* 2497 */ 2503, /* 2505 */ 2503, /* 2513 */ 2521, /* 2521 */ 2521, /* 2529 */ 2531, /* 2537 */ 2543, /* 2545 */ 2551, /* 2553 */ 2557, /* 2561 */ 2557, /* 2569 */ 2557, /* 2577 */ 2579, /* 2585 */ 2593, /* 2593 */ 2593, /* 2601 */ 2609, /* 2609 */ 2617, /* 2617 */ 2621, /* 2625 */ 2633, /* 2633 */ 2633, /* 2641 */ 2647, /* 2649 */ 2657, /* 2657 */ 2663, /* 2665 */ 2671, /* 2673 */ 2677, /* 2681 */ 2689, /* 2689 */ 2693, /* 2697 */ 2699, /* 2705 */ 2713, /* 2713 */ 2719, /* 2721 */ 2729, /* 2729 */ 2731, /* 2737 */ 2741, /* 2745 */ 2753, /* 2753 */ 2753, /* 2761 */ 2767, /* 2769 */ 2777, /* 2777 */ 2777, /* 2785 */ 2791, /* 2793 */ 2801, /* 2801 */ 2803, /* 2809 */ 2803, /* 2817 */ 2819, /* 2825 */ 2833, /* 2833 */ 2837, /* 2841 */ 2843, /* 2849 */ 2857, /* 2857 */ 2861, /* 2865 */ 2861, /* 2873 */ 2879, /* 2881 */ 2887, /* 2889 */ 2897, /* 2897 */ 2903, /* 2905 */ 2909, /* 2913 */ 2917, /* 2921 */ 2927, /* 2929 */ 2927, /* 2937 */ 2939, /* 2945 */ 2953, /* 2953 */ 2957, /* 2961 */ 2969, /* 2969 */ 2971, /* 2977 */ 2971, /* 2985 */ 2971, /* 2993 */ 3001, /* 3001 */ 3001, /* 3009 */ 3011, /* 3017 */ 3023, /* 3025 */ 3023, /* 3033 */ 3041, /* 3041 */ 3049, /* 3049 */ 3049, /* 3057 */ 3061, /* 3065 */ 3067, /* 3073 */ 3079, /* 3081 */ 3089, /* 3089 */ 3089, /* 3097 */ 3089, /* 3105 */ 3109, /* 3113 */ 3121, /* 3121 */ 3121, /* 3129 */ 3137, /* 3137 */ 3137, /* 3145 */ 3137, /* 3153 */ 3137, /* 3161 */ 3169, /* 3169 */ 3169, /* 3177 */ 3181, /* 3185 */ 3191, /* 3193 */ 3191, /* 3201 */ 3209, /* 3209 */ 3217, /* 3217 */ 3221, /* 3225 */ 3229, /* 3233 */ 3229, /* 3241 */ 3229, /* 3249 */ 3257, /* 3257 */ 3259, /* 3265 */ 3271, /* 3273 */ 3271, /* 3281 */ 3271, /* 3289 */ 3271, /* 3297 */ 3301, /* 3305 */ 3313, /* 3313 */ 3319, /* 3321 */ 3329, /* 3329 */ 3331, /* 3337 */ 3343, /* 3345 */ 3347, /* 3353 */ 3361, /* 3361 */ 3361, /* 3369 */ 3373, /* 3377 */ 3373, /* 3385 */ 3391, /* 3393 */ 3391, /* 3401 */ 3407, /* 3409 */ 3413, /* 3417 */ 3413, /* 3425 */ 3433, /* 3433 */ 3433, /* 3441 */ 3449, /* 3449 */ 3457, /* 3457 */ 3463, /* 3465 */ 3469, /* 3473 */ 3469, /* 3481 */ 3469, /* 3489 */ 3491, /* 3497 */ 3499, /* 3505 */ 3511, /* 3513 */ 3517, /* 3521 */ 3529, /* 3529 */ 3533, /* 3537 */ 3541, /* 3545 */ 3547, /* 3553 */ 3559, /* 3561 */ 3559, /* 3569 */ 3571, /* 3577 */ 3583, /* 3585 */ 3593, /* 3593 */ 3593, /* 3601 */ 3607, /* 3609 */ 3617, /* 3617 */ 3623, /* 3625 */ 3631, /* 3633 */ 3637, /* 3641 */ 3643, /* 3649 */ 3643, /* 3657 */ 3659, /* 3665 */ 3673, /* 3673 */ 3677, /* 3681 */ 3677, /* 3689 */ 3697, /* 3697 */ 3701, /* 3705 */ 3709, /* 3713 */ 3719, /* 3721 */ 3727, /* 3729 */ 3733, /* 3737 */ 3739, /* 3745 */ 3739, /* 3753 */ 3761, /* 3761 */ 3769, /* 3769 */ 3769, /* 3777 */ 3779, /* 3785 */ 3793, /* 3793 */ 3797, /* 3801 */ 3803, /* 3809 */ 3803, /* 3817 */ 3823, /* 3825 */ 3833, /* 3833 */ 3833, /* 3841 */ 3847, /* 3849 */ 3853, /* 3857 */ 3863, /* 3865 */ 3863, /* 3873 */ 3881, /* 3881 */ 3889, /* 3889 */ 3889, /* 3897 */ 3889, /* 3905 */ 3911, /* 3913 */ 3919, /* 3921 */ 3929, /* 3929 */ 3931, /* 3937 */ 3943, /* 3945 */ 3947, /* 3953 */ 3947, /* 3961 */ 3967, /* 3969 */ 3967, /* 3977 */ 3967, /* 3985 */ 3989, /* 3993 */ 4001, /* 4001 */ 4007, /* 4009 */ 4013, /* 4017 */ 4021, /* 4025 */ 4027, /* 4033 */ 4027, /* 4041 */ 4049, /* 4049 */ 4057, /* 4057 */ 4057, /* 4065 */ 4073, /* 4073 */ 4079, /* 4081 */ 4079, /* 4089 */ 4093, /* 4097 */ 4099, /* 4105 */ 4111, /* 4113 */ 4111, /* 4121 */ 4129, /* 4129 */ 4133, /* 4137 */ 4139, /* 4145 */ 4153, /* 4153 */ 4159, /* 4161 */ 4159, /* 4169 */ 4177, /* 4177 */ 4177, /* 4185 */ 4177, /* 4193 */ 4201, /* 4201 */ 4201, /* 4209 */ 4217, /* 4217 */ 4219, /* 4225 */ 4231, /* 4233 */ 4241, /* 4241 */ 4243, /* 4249 */ 4253, /* 4257 */ 4261, /* 4265 */ 4273, /* 4273 */ 4273, /* 4281 */ 4289, /* 4289 */ 4297, /* 4297 */ 4297, /* 4305 */ 4297, /* 4313 */ 4297, /* 4321 */ 4327, /* 4329 */ 4337, /* 4337 */ 4339, /* 4345 */ 4349, /* 4353 */ 4357, /* 4361 */ 4363, /* 4369 */ 4373, /* 4377 */ 4373, /* 4385 */ 4391, /* 4393 */ 4397, /* 4401 */ 4409, /* 4409 */ 4409, /* 4417 */ 4423, /* 4425 */ 4423, /* 4433 */ 4441, /* 4441 */ 4447, /* 4449 */ 4457, /* 4457 */ 4463, /* 4465 */ 4463, /* 4473 */ 4481, /* 4481 */ 4483, /* 4489 */ 4493, /* 4497 */ 4493, /* 4505 */ 4513, /* 4513 */ 4519, /* 4521 */ 4523, /* 4529 */ 4523, /* 4537 */ 4523, /* 4545 */ 4549, /* 4553 */ 4561, /* 4561 */ 4567, /* 4569 */ 4567, /* 4577 */ 4583, /* 4585 */ 4591, /* 4593 */ 4597, /* 4601 */ 4603, /* 4609 */ 4603, /* 4617 */ 4621, /* 4625 */ 4621, /* 4633 */ 4639, /* 4641 */ 4649, /* 4649 */ 4657, /* 4657 */ 4663, /* 4665 */ 4673, /* 4673 */ 4679, /* 4681 */ 4679, /* 4689 */ 4691, /* 4697 */ 4703, /* 4705 */ 4703, /* 4713 */ 4721, /* 4721 */ 4729, /* 4729 */ 4733, /* 4737 */ 4733, /* 4745 */ 4751, /* 4753 */ 4759, /* 4761 */ 4759, /* 4769 */ 4759, /* 4777 */ 4783, /* 4785 */ 4793, /* 4793 */ 4801, /* 4801 */ 4801, /* 4809 */ 4817, /* 4817 */ 4817, /* 4825 */ 4831, /* 4833 */ 4831, /* 4841 */ 4831, /* 4849 */ 4831, /* 4857 */ 4861, /* 4865 */ 4871, /* 4873 */ 4877, /* 4881 */ 4889, /* 4889 */ 4889, /* 4897 */ 4903, /* 4905 */ 4909, /* 4913 */ 4919, /* 4921 */ 4919, /* 4929 */ 4937, /* 4937 */ 4943, /* 4945 */ 4951, /* 4953 */ 4957, /* 4961 */ 4969, /* 4969 */ 4973, /* 4977 */ 4973, /* 4985 */ 4993, /* 4993 */ 4999, /* 5001 */ 5009, /* 5009 */ 5011, /* 5017 */ 5023, /* 5025 */ 5023, /* 5033 */ 5039, /* 5041 */ 5039, /* 5049 */ 5051, /* 5057 */ 5059, /* 5065 */ 5059, /* 5073 */ 5081, /* 5081 */ 5087, /* 5089 */ 5087, /* 5097 */ 5101, /* 5105 */ 5113, /* 5113 */ 5119, /* 5121 */ 5119, /* 5129 */ 5119, /* 5137 */ 5119, /* 5145 */ 5153, /* 5153 */ 5153, /* 5161 */ 5167, /* 5169 */ 5171, /* 5177 */ 5179, /* 5185 */ 5189, /* 5193 */ 5197, /* 5201 */ 5209, /* 5209 */ 5209, /* 5217 */ 5209, /* 5225 */ 5233, /* 5233 */ 5237, /* 5241 */ 5237, /* 5249 */ 5237, /* 5257 */ 5261, /* 5265 */ 5273, /* 5273 */ 5281, /* 5281 */ 5281, /* 5289 */ 5297, /* 5297 */ 5303, /* 5305 */ 5309, /* 5313 */ 5309, /* 5321 */ 5323, /* 5329 */ 5333, /* 5337 */ 5333, /* 5345 */ 5351, /* 5353 */ 5351, /* 5361 */ 5351, /* 5369 */ 5351, /* 5377 */ 5381, /* 5385 */ 5393, /* 5393 */ 5399, /* 5401 */ 5407, /* 5409 */ 5417, /* 5417 */ 5419, /* 5425 */ 5431, /* 5433 */ 5441, /* 5441 */ 5449, /* 5449 */ 5449, /* 5457 */ 5449, /* 5465 */ 5471, /* 5473 */ 5479, /* 5481 */ 5483, /* 5489 */ 5483, /* 5497 */ 5503, /* 5505 */ 5507, /* 5513 */ 5521, /* 5521 */ 5527, /* 5529 */ 5531, /* 5537 */ 5531, /* 5545 */ 5531, /* 5553 */ 5557, /* 5561 */ 5569, /* 5569 */ 5573, /* 5577 */ 5581, /* 5585 */ 5591, /* 5593 */ 5591, /* 5601 */ 5591, /* 5609 */ 5591, /* 5617 */ 5623, /* 5625 */ 5623, /* 5633 */ 5641, /* 5641 */ 5647, /* 5649 */ 5657, /* 5657 */ 5659, /* 5665 */ 5669, /* 5673 */ 5669, /* 5681 */ 5689, /* 5689 */ 5693, /* 5697 */ 5701, /* 5705 */ 5711, /* 5713 */ 5717, /* 5721 */ 5717, /* 5729 */ 5737, /* 5737 */ 5743, /* 5745 */ 5749, /* 5753 */ 5749, /* 5761 */ 5749, /* 5769 */ 5749, /* 5777 */ 5783, /* 5785 */ 5791, /* 5793 */ 5801, /* 5801 */ 5807, /* 5809 */ 5813, /* 5817 */ 5821, /* 5825 */ 5827, /* 5833 */ 5839, /* 5841 */ 5849, /* 5849 */ 5857, /* 5857 */ 5861, /* 5865 */ 5869, /* 5873 */ 5881, /* 5881 */ 5881, /* 5889 */ 5897, /* 5897 */ 5903, /* 5905 */ 5903, /* 5913 */ 5903, /* 5921 */ 5927, /* 5929 */ 5927, /* 5937 */ 5939, /* 5945 */ 5953, /* 5953 */ 5953, /* 5961 */ 5953, /* 5969 */ 5953, /* 5977 */ 5981, /* 5985 */ 5987, /* 5993 */ 5987, /* 6001 */ 6007, /* 6009 */ 6011, /* 6017 */ 6011, /* 6025 */ 6029, /* 6033 */ 6037, /* 6041 */ 6047, /* 6049 */ 6053, /* 6057 */ 6053, /* 6065 */ 6073, /* 6073 */ 6079, /* 6081 */ 6089, /* 6089 */ 6091, /* 6097 */ 6101, /* 6105 */ 6113, /* 6113 */ 6121, /* 6121 */ 6121, /* 6129 */ 6133, /* 6137 */ 6143, /* 6145 */ 6151, /* 6153 */ 6151, /* 6161 */ 6163, /* 6169 */ 6173, /* 6177 */ 6173, /* 6185 */ 6173, /* 6193 */ 6199, /* 6201 */ 6203, /* 6209 */ 6217, /* 6217 */ 6221, /* 6225 */ 6229, /* 6233 */ 6229, /* 6241 */ 6247, /* 6249 */ 6257, /* 6257 */ 6263, /* 6265 */ 6271, /* 6273 */ 6277, /* 6281 */ 6287, /* 6289 */ 6287, /* 6297 */ 6301, /* 6305 */ 6311, /* 6313 */ 6317, /* 6321 */ 6329, /* 6329 */ 6337, /* 6337 */ 6343, /* 6345 */ 6353, /* 6353 */ 6361, /* 6361 */ 6367, /* 6369 */ 6373, /* 6377 */ 6379, /* 6385 */ 6389, /* 6393 */ 6397, /* 6401 */ 6397, /* 6409 */ 6397, /* 6417 */ 6421, /* 6425 */ 6427, /* 6433 */ 6427, /* 6441 */ 6449, /* 6449 */ 6451, /* 6457 */ 6451, /* 6465 */ 6473, /* 6473 */ 6481, /* 6481 */ 6481, /* 6489 */ 6491, /* 6497 */ 6491, /* 6505 */ 6491, /* 6513 */ 6521, /* 6521 */ 6529, /* 6529 */ 6529, /* 6537 */ 6529, /* 6545 */ 6553, /* 6553 */ 6553, /* 6561 */ 6569, /* 6569 */ 6577, /* 6577 */ 6581, /* 6585 */ 6581, /* 6593 */ 6599, /* 6601 */ 6607, /* 6609 */ 6607, /* 6617 */ 6619, /* 6625 */ 6619, /* 6633 */ 6637, /* 6641 */ 6637, /* 6649 */ 6653, /* 6657 */ 6661, /* 6665 */ 6673, /* 6673 */ 6679, /* 6681 */ 6689, /* 6689 */ 6691, /* 6697 */ 6703, /* 6705 */ 6709, /* 6713 */ 6719, /* 6721 */ 6719, /* 6729 */ 6737, /* 6737 */ 6737, /* 6745 */ 6737, /* 6753 */ 6761, /* 6761 */ 6763, /* 6769 */ 6763, /* 6777 */ 6781, /* 6785 */ 6793, /* 6793 */ 6793, /* 6801 */ 6803, /* 6809 */ 6803, /* 6817 */ 6823, /* 6825 */ 6833, /* 6833 */ 6841, /* 6841 */ 6841, /* 6849 */ 6857, /* 6857 */ 6863, /* 6865 */ 6871, /* 6873 */ 6871, /* 6881 */ 6883, /* 6889 */ 6883, /* 6897 */ 6899, /* 6905 */ 6911, /* 6913 */ 6917, /* 6921 */ 6917, /* 6929 */ 6917, /* 6937 */ 6917, /* 6945 */ 6949, /* 6953 */ 6961, /* 6961 */ 6967, /* 6969 */ 6977, /* 6977 */ 6983, /* 6985 */ 6991, /* 6993 */ 7001, /* 7001 */ 7001, /* 7009 */ 7013, /* 7017 */ 7019, /* 7025 */ 7027, /* 7033 */ 7039, /* 7041 */ 7043, /* 7049 */ 7057, /* 7057 */ 7057, /* 7065 */ 7069, /* 7073 */ 7079, /* 7081 */ 7079, /* 7089 */ 7079, /* 7097 */ 7103, /* 7105 */ 7109, /* 7113 */ 7121, /* 7121 */ 7129, /* 7129 */ 7129, /* 7137 */ 7129, /* 7145 */ 7151, /* 7153 */ 7159, /* 7161 */ 7159, /* 7169 */ 7177, /* 7177 */ 7177, /* 7185 */ 7193, /* 7193 */ 7193, /* 7201 */ 7207, /* 7209 */ 7213, /* 7217 */ 7219, /* 7225 */ 7229, /* 7233 */ 7237, /* 7241 */ 7247, /* 7249 */ 7253, /* 7257 */ 7253, /* 7265 */ 7253, /* 7273 */ 7253, /* 7281 */ 7283, /* 7289 */ 7297, /* 7297 */ 7297, /* 7305 */ 7309, /* 7313 */ 7321, /* 7321 */ 7321, /* 7329 */ 7333, /* 7337 */ 7333, /* 7345 */ 7351, /* 7353 */ 7351, /* 7361 */ 7369, /* 7369 */ 7369, /* 7377 */ 7369, /* 7385 */ 7393, /* 7393 */ 7393, /* 7401 */ 7393, /* 7409 */ 7417, /* 7417 */ 7417, /* 7425 */ 7433, /* 7433 */ 7433, /* 7441 */ 7433, /* 7449 */ 7457, /* 7457 */ 7459, /* 7465 */ 7459, /* 7473 */ 7481, /* 7481 */ 7489, /* 7489 */ 7489, /* 7497 */ 7499, /* 7505 */ 7507, /* 7513 */ 7517, /* 7521 */ 7529, /* 7529 */ 7537, /* 7537 */ 7541, /* 7545 */ 7549, /* 7553 */ 7561, /* 7561 */ 7561, /* 7569 */ 7577, /* 7577 */ 7583, /* 7585 */ 7591, /* 7593 */ 7591, /* 7601 */ 7607, /* 7609 */ 7607, /* 7617 */ 7621, /* 7625 */ 7621, /* 7633 */ 7639, /* 7641 */ 7649, /* 7649 */ 7649, /* 7657 */ 7649, /* 7665 */ 7673, /* 7673 */ 7681, /* 7681 */ 7687, /* 7689 */ 7691, /* 7697 */ 7703, /* 7705 */ 7703, /* 7713 */ 7717, /* 7721 */ 7727, /* 7729 */ 7727, /* 7737 */ 7741, /* 7745 */ 7753, /* 7753 */ 7759, /* 7761 */ 7759, /* 7769 */ 7759, /* 7777 */ 7759, /* 7785 */ 7793, /* 7793 */ 7793, /* 7801 */ 7793, /* 7809 */ 7817, /* 7817 */ 7823, /* 7825 */ 7829, /* 7833 */ 7841, /* 7841 */ 7841, /* 7849 */ 7853, /* 7857 */ 7853, /* 7865 */ 7873, /* 7873 */ 7879, /* 7881 */ 7883, /* 7889 */ 7883, /* 7897 */ 7901, /* 7905 */ 7907, /* 7913 */ 7919, /* 7921 */ 7927, /* 7929 */ 7937, /* 7937 */ 7937, /* 7945 */ 7951, /* 7953 */ 7951, /* 7961 */ 7963, /* 7969 */ 7963, /* 7977 */ 7963, /* 7985 */ 7993, /* 7993 */ 7993, /* 8001 */ 8009, /* 8009 */ 8017, /* 8017 */ 8017, /* 8025 */ 8017, /* 8033 */ 8039, /* 8041 */ 8039, /* 8049 */ 8053, /* 8057 */ 8059, /* 8065 */ 8069, /* 8073 */ 8081, /* 8081 */ 8089, /* 8089 */ 8093, /* 8097 */ 8101, /* 8105 */ 8111, /* 8113 */ 8117, /* 8121 */ 8123, /* 8129 */ 8123, /* 8137 */ 8123, /* 8145 */ 8147, /* 8153 */ 8161, /* 8161 */ 8167, /* 8169 */ 8171, /* 8177 */ 8179, /* 8185 */ }; /* 0-64K, increments=64 */ static unsigned prime_table1[]={ 1, /* 1 */ 61, /* 65 */ 127, /* 129 */ 193, /* 193 */ 257, /* 257 */ 317, /* 321 */ 383, /* 385 */ 449, /* 449 */ 509, /* 513 */ 577, /* 577 */ 641, /* 641 */ 701, /* 705 */ 769, /* 769 */ 829, /* 833 */ 887, /* 897 */ 953, /* 961 */ 1021, /* 1025 */ 1087, /* 1089 */ 1153, /* 1153 */ 1217, /* 1217 */ 1279, /* 1281 */ 1327, /* 1345 */ 1409, /* 1409 */ 1471, /* 1473 */ 1531, /* 1537 */ 1601, /* 1601 */ 1663, /* 1665 */ 1723, /* 1729 */ 1789, /* 1793 */ 1847, /* 1857 */ 1913, /* 1921 */ 1979, /* 1985 */ 2039, /* 2049 */ 2113, /* 2113 */ 2161, /* 2177 */ 2239, /* 2241 */ 2297, /* 2305 */ 2357, /* 2369 */ 2423, /* 2433 */ 2477, /* 2497 */ 2557, /* 2561 */ 2621, /* 2625 */ 2689, /* 2689 */ 2753, /* 2753 */ 2803, /* 2817 */ 2879, /* 2881 */ 2939, /* 2945 */ 3001, /* 3009 */ 3067, /* 3073 */ 3137, /* 3137 */ 3191, /* 3201 */ 3259, /* 3265 */ 3329, /* 3329 */ 3391, /* 3393 */ 3457, /* 3457 */ 3517, /* 3521 */ 3583, /* 3585 */ 3643, /* 3649 */ 3709, /* 3713 */ 3769, /* 3777 */ 3833, /* 3841 */ 3889, /* 3905 */ 3967, /* 3969 */ 4027, /* 4033 */ 4093, /* 4097 */ 4159, /* 4161 */ 4219, /* 4225 */ 4289, /* 4289 */ 4349, /* 4353 */ 4409, /* 4417 */ 4481, /* 4481 */ 4523, /* 4545 */ 4603, /* 4609 */ 4673, /* 4673 */ 4733, /* 4737 */ 4801, /* 4801 */ 4861, /* 4865 */ 4919, /* 4929 */ 4993, /* 4993 */ 5051, /* 5057 */ 5119, /* 5121 */ 5179, /* 5185 */ 5237, /* 5249 */ 5309, /* 5313 */ 5351, /* 5377 */ 5441, /* 5441 */ 5503, /* 5505 */ 5569, /* 5569 */ 5623, /* 5633 */ 5693, /* 5697 */ 5749, /* 5761 */ 5821, /* 5825 */ 5881, /* 5889 */ 5953, /* 5953 */ 6011, /* 6017 */ 6079, /* 6081 */ 6143, /* 6145 */ 6203, /* 6209 */ 6271, /* 6273 */ 6337, /* 6337 */ 6397, /* 6401 */ 6451, /* 6465 */ 6529, /* 6529 */ 6581, /* 6593 */ 6653, /* 6657 */ 6719, /* 6721 */ 6781, /* 6785 */ 6841, /* 6849 */ 6911, /* 6913 */ 6977, /* 6977 */ 7039, /* 7041 */ 7103, /* 7105 */ 7159, /* 7169 */ 7229, /* 7233 */ 7297, /* 7297 */ 7351, /* 7361 */ 7417, /* 7425 */ 7489, /* 7489 */ 7549, /* 7553 */ 7607, /* 7617 */ 7681, /* 7681 */ 7741, /* 7745 */ 7793, /* 7809 */ 7873, /* 7873 */ 7937, /* 7937 */ 7993, /* 8001 */ 8059, /* 8065 */ 8123, /* 8129 */ 8191, /* 8193 */ 8243, /* 8257 */ 8317, /* 8321 */ 8377, /* 8385 */ 8447, /* 8449 */ 8513, /* 8513 */ 8573, /* 8577 */ 8641, /* 8641 */ 8699, /* 8705 */ 8761, /* 8769 */ 8831, /* 8833 */ 8893, /* 8897 */ 8951, /* 8961 */ 9013, /* 9025 */ 9067, /* 9089 */ 9151, /* 9153 */ 9209, /* 9217 */ 9281, /* 9281 */ 9343, /* 9345 */ 9403, /* 9409 */ 9473, /* 9473 */ 9533, /* 9537 */ 9601, /* 9601 */ 9661, /* 9665 */ 9721, /* 9729 */ 9791, /* 9793 */ 9857, /* 9857 */ 9907, /* 9921 */ 9973, /* 9985 */ 10039, /* 10049 */ 10111, /* 10113 */ 10177, /* 10177 */ 10223, /* 10241 */ 10303, /* 10305 */ 10369, /* 10369 */ 10433, /* 10433 */ 10487, /* 10497 */ 10559, /* 10561 */ 10613, /* 10625 */ 10687, /* 10689 */ 10753, /* 10753 */ 10799, /* 10817 */ 10867, /* 10881 */ 10939, /* 10945 */ 11003, /* 11009 */ 11071, /* 11073 */ 11131, /* 11137 */ 11197, /* 11201 */ 11261, /* 11265 */ 11329, /* 11329 */ 11393, /* 11393 */ 11447, /* 11457 */ 11519, /* 11521 */ 11579, /* 11585 */ 11633, /* 11649 */ 11701, /* 11713 */ 11777, /* 11777 */ 11839, /* 11841 */ 11903, /* 11905 */ 11969, /* 11969 */ 12011, /* 12033 */ 12097, /* 12097 */ 12161, /* 12161 */ 12211, /* 12225 */ 12289, /* 12289 */ 12347, /* 12353 */ 12413, /* 12417 */ 12479, /* 12481 */ 12541, /* 12545 */ 12601, /* 12609 */ 12671, /* 12673 */ 12721, /* 12737 */ 12799, /* 12801 */ 12853, /* 12865 */ 12923, /* 12929 */ 12983, /* 12993 */ 13049, /* 13057 */ 13121, /* 13121 */ 13183, /* 13185 */ 13249, /* 13249 */ 13313, /* 13313 */ 13367, /* 13377 */ 13441, /* 13441 */ 13499, /* 13505 */ 13567, /* 13569 */ 13633, /* 13633 */ 13697, /* 13697 */ 13759, /* 13761 */ 13807, /* 13825 */ 13883, /* 13889 */ 13933, /* 13953 */ 14011, /* 14017 */ 14081, /* 14081 */ 14143, /* 14145 */ 14207, /* 14209 */ 14251, /* 14273 */ 14327, /* 14337 */ 14401, /* 14401 */ 14461, /* 14465 */ 14519, /* 14529 */ 14593, /* 14593 */ 14657, /* 14657 */ 14717, /* 14721 */ 14783, /* 14785 */ 14843, /* 14849 */ 14897, /* 14913 */ 14969, /* 14977 */ 15031, /* 15041 */ 15101, /* 15105 */ 15161, /* 15169 */ 15233, /* 15233 */ 15289, /* 15297 */ 15361, /* 15361 */ 15413, /* 15425 */ 15473, /* 15489 */ 15551, /* 15553 */ 15607, /* 15617 */ 15679, /* 15681 */ 15739, /* 15745 */ 15809, /* 15809 */ 15859, /* 15873 */ 15937, /* 15937 */ 16001, /* 16001 */ 16063, /* 16065 */ 16127, /* 16129 */ 16193, /* 16193 */ 16253, /* 16257 */ 16319, /* 16321 */ 16381, /* 16385 */ 16447, /* 16449 */ 16493, /* 16513 */ 16573, /* 16577 */ 16633, /* 16641 */ 16703, /* 16705 */ 16763, /* 16769 */ 16831, /* 16833 */ 16889, /* 16897 */ 16943, /* 16961 */ 17021, /* 17025 */ 17077, /* 17089 */ 17137, /* 17153 */ 17209, /* 17217 */ 17257, /* 17281 */ 17341, /* 17345 */ 17401, /* 17409 */ 17471, /* 17473 */ 17519, /* 17537 */ 17599, /* 17601 */ 17659, /* 17665 */ 17729, /* 17729 */ 17791, /* 17793 */ 17851, /* 17857 */ 17921, /* 17921 */ 17981, /* 17985 */ 18049, /* 18049 */ 18097, /* 18113 */ 18169, /* 18177 */ 18233, /* 18241 */ 18301, /* 18305 */ 18367, /* 18369 */ 18433, /* 18433 */ 18493, /* 18497 */ 18553, /* 18561 */ 18617, /* 18625 */ 18679, /* 18689 */ 18749, /* 18753 */ 18803, /* 18817 */ 18869, /* 18881 */ 18919, /* 18945 */ 19009, /* 19009 */ 19073, /* 19073 */ 19121, /* 19137 */ 19183, /* 19201 */ 19259, /* 19265 */ 19319, /* 19329 */ 19391, /* 19393 */ 19457, /* 19457 */ 19507, /* 19521 */ 19583, /* 19585 */ 19609, /* 19649 */ 19709, /* 19713 */ 19777, /* 19777 */ 19841, /* 19841 */ 19891, /* 19905 */ 19963, /* 19969 */ 20029, /* 20033 */ 20089, /* 20097 */ 20161, /* 20161 */ 20219, /* 20225 */ 20287, /* 20289 */ 20353, /* 20353 */ 20411, /* 20417 */ 20479, /* 20481 */ 20543, /* 20545 */ 20599, /* 20609 */ 20663, /* 20673 */ 20731, /* 20737 */ 20789, /* 20801 */ 20857, /* 20865 */ 20929, /* 20929 */ 20983, /* 20993 */ 21031, /* 21057 */ 21121, /* 21121 */ 21179, /* 21185 */ 21247, /* 21249 */ 21313, /* 21313 */ 21377, /* 21377 */ 21433, /* 21441 */ 21503, /* 21505 */ 21569, /* 21569 */ 21617, /* 21633 */ 21683, /* 21697 */ 21757, /* 21761 */ 21821, /* 21825 */ 21881, /* 21889 */ 21943, /* 21953 */ 22013, /* 22017 */ 22079, /* 22081 */ 22133, /* 22145 */ 22193, /* 22209 */ 22273, /* 22273 */ 22307, /* 22337 */ 22397, /* 22401 */ 22453, /* 22465 */ 22511, /* 22529 */ 22573, /* 22593 */ 22651, /* 22657 */ 22721, /* 22721 */ 22783, /* 22785 */ 22817, /* 22849 */ 22907, /* 22913 */ 22973, /* 22977 */ 23041, /* 23041 */ 23099, /* 23105 */ 23167, /* 23169 */ 23227, /* 23233 */ 23297, /* 23297 */ 23357, /* 23361 */ 23417, /* 23425 */ 23473, /* 23489 */ 23549, /* 23553 */ 23609, /* 23617 */ 23677, /* 23681 */ 23743, /* 23745 */ 23801, /* 23809 */ 23873, /* 23873 */ 23929, /* 23937 */ 24001, /* 24001 */ 24061, /* 24065 */ 24121, /* 24129 */ 24181, /* 24193 */ 24251, /* 24257 */ 24317, /* 24321 */ 24379, /* 24385 */ 24443, /* 24449 */ 24509, /* 24513 */ 24571, /* 24577 */ 24631, /* 24641 */ 24697, /* 24705 */ 24767, /* 24769 */ 24821, /* 24833 */ 24889, /* 24897 */ 24953, /* 24961 */ 25013, /* 25025 */ 25087, /* 25089 */ 25153, /* 25153 */ 25189, /* 25217 */ 25261, /* 25281 */ 25343, /* 25345 */ 25409, /* 25409 */ 25471, /* 25473 */ 25537, /* 25537 */ 25601, /* 25601 */ 25657, /* 25665 */ 25717, /* 25729 */ 25793, /* 25793 */ 25849, /* 25857 */ 25919, /* 25921 */ 25981, /* 25985 */ 26041, /* 26049 */ 26113, /* 26113 */ 26177, /* 26177 */ 26237, /* 26241 */ 26297, /* 26305 */ 26357, /* 26369 */ 26431, /* 26433 */ 26497, /* 26497 */ 26561, /* 26561 */ 26597, /* 26625 */ 26687, /* 26689 */ 26737, /* 26753 */ 26813, /* 26817 */ 26881, /* 26881 */ 26927, /* 26945 */ 26993, /* 27009 */ 27073, /* 27073 */ 27127, /* 27137 */ 27197, /* 27201 */ 27259, /* 27265 */ 27329, /* 27329 */ 27367, /* 27393 */ 27457, /* 27457 */ 27509, /* 27521 */ 27583, /* 27585 */ 27647, /* 27649 */ 27701, /* 27713 */ 27773, /* 27777 */ 27827, /* 27841 */ 27901, /* 27905 */ 27967, /* 27969 */ 28031, /* 28033 */ 28097, /* 28097 */ 28151, /* 28161 */ 28219, /* 28225 */ 28289, /* 28289 */ 28351, /* 28353 */ 28411, /* 28417 */ 28477, /* 28481 */ 28541, /* 28545 */ 28607, /* 28609 */ 28669, /* 28673 */ 28729, /* 28737 */ 28793, /* 28801 */ 28859, /* 28865 */ 28927, /* 28929 */ 28979, /* 28993 */ 29033, /* 29057 */ 29101, /* 29121 */ 29179, /* 29185 */ 29243, /* 29249 */ 29311, /* 29313 */ 29363, /* 29377 */ 29437, /* 29441 */ 29501, /* 29505 */ 29569, /* 29569 */ 29633, /* 29633 */ 29683, /* 29697 */ 29761, /* 29761 */ 29819, /* 29825 */ 29881, /* 29889 */ 29947, /* 29953 */ 30013, /* 30017 */ 30071, /* 30081 */ 30139, /* 30145 */ 30203, /* 30209 */ 30271, /* 30273 */ 30323, /* 30337 */ 30391, /* 30401 */ 30449, /* 30465 */ 30529, /* 30529 */ 30593, /* 30593 */ 30649, /* 30657 */ 30713, /* 30721 */ 30781, /* 30785 */ 30841, /* 30849 */ 30911, /* 30913 */ 30977, /* 30977 */ 31039, /* 31041 */ 31091, /* 31105 */ 31159, /* 31169 */ 31231, /* 31233 */ 31277, /* 31297 */ 31357, /* 31361 */ 31397, /* 31425 */ 31489, /* 31489 */ 31547, /* 31553 */ 31607, /* 31617 */ 31667, /* 31681 */ 31741, /* 31745 */ 31799, /* 31809 */ 31873, /* 31873 */ 31907, /* 31937 */ 31991, /* 32001 */ 32063, /* 32065 */ 32119, /* 32129 */ 32191, /* 32193 */ 32257, /* 32257 */ 32321, /* 32321 */ 32381, /* 32385 */ 32443, /* 32449 */ 32507, /* 32513 */ 32573, /* 32577 */ 32633, /* 32641 */ 32693, /* 32705 */ 32749, /* 32769 */ 32833, /* 32833 */ 32887, /* 32897 */ 32957, /* 32961 */ 33023, /* 33025 */ 33083, /* 33089 */ 33151, /* 33153 */ 33211, /* 33217 */ 33247, /* 33281 */ 33343, /* 33345 */ 33409, /* 33409 */ 33469, /* 33473 */ 33533, /* 33537 */ 33601, /* 33601 */ 33647, /* 33665 */ 33721, /* 33729 */ 33791, /* 33793 */ 33857, /* 33857 */ 33911, /* 33921 */ 33967, /* 33985 */ 34039, /* 34049 */ 34061, /* 34113 */ 34171, /* 34177 */ 34231, /* 34241 */ 34303, /* 34305 */ 34369, /* 34369 */ 34429, /* 34433 */ 34487, /* 34497 */ 34549, /* 34561 */ 34613, /* 34625 */ 34687, /* 34689 */ 34747, /* 34753 */ 34807, /* 34817 */ 34877, /* 34881 */ 34939, /* 34945 */ 34981, /* 35009 */ 35069, /* 35073 */ 35129, /* 35137 */ 35201, /* 35201 */ 35257, /* 35265 */ 35327, /* 35329 */ 35393, /* 35393 */ 35449, /* 35457 */ 35521, /* 35521 */ 35573, /* 35585 */ 35617, /* 35649 */ 35677, /* 35713 */ 35771, /* 35777 */ 35839, /* 35841 */ 35899, /* 35905 */ 35969, /* 35969 */ 36017, /* 36033 */ 36097, /* 36097 */ 36161, /* 36161 */ 36217, /* 36225 */ 36277, /* 36289 */ 36353, /* 36353 */ 36389, /* 36417 */ 36479, /* 36481 */ 36541, /* 36545 */ 36607, /* 36609 */ 36671, /* 36673 */ 36721, /* 36737 */ 36793, /* 36801 */ 36857, /* 36865 */ 36929, /* 36929 */ 36979, /* 36993 */ 37057, /* 37057 */ 37117, /* 37121 */ 37181, /* 37185 */ 37243, /* 37249 */ 37313, /* 37313 */ 37369, /* 37377 */ 37441, /* 37441 */ 37501, /* 37505 */ 37567, /* 37569 */ 37633, /* 37633 */ 37693, /* 37697 */ 37747, /* 37761 */ 37813, /* 37825 */ 37889, /* 37889 */ 37951, /* 37953 */ 38011, /* 38017 */ 38069, /* 38081 */ 38119, /* 38145 */ 38201, /* 38209 */ 38273, /* 38273 */ 38333, /* 38337 */ 38393, /* 38401 */ 38461, /* 38465 */ 38501, /* 38529 */ 38593, /* 38593 */ 38653, /* 38657 */ 38713, /* 38721 */ 38783, /* 38785 */ 38839, /* 38849 */ 38903, /* 38913 */ 38977, /* 38977 */ 39041, /* 39041 */ 39103, /* 39105 */ 39163, /* 39169 */ 39233, /* 39233 */ 39293, /* 39297 */ 39359, /* 39361 */ 39419, /* 39425 */ 39461, /* 39489 */ 39551, /* 39553 */ 39607, /* 39617 */ 39679, /* 39681 */ 39733, /* 39745 */ 39799, /* 39809 */ 39869, /* 39873 */ 39937, /* 39937 */ 39989, /* 40001 */ 40063, /* 40065 */ 40129, /* 40129 */ 40193, /* 40193 */ 40253, /* 40257 */ 40289, /* 40321 */ 40361, /* 40385 */ 40433, /* 40449 */ 40507, /* 40513 */ 40577, /* 40577 */ 40639, /* 40641 */ 40699, /* 40705 */ 40763, /* 40769 */ 40829, /* 40833 */ 40897, /* 40897 */ 40961, /* 40961 */ 41023, /* 41025 */ 41081, /* 41089 */ 41149, /* 41153 */ 41213, /* 41217 */ 41281, /* 41281 */ 41341, /* 41345 */ 41399, /* 41409 */ 41467, /* 41473 */ 41521, /* 41537 */ 41597, /* 41601 */ 41659, /* 41665 */ 41729, /* 41729 */ 41777, /* 41793 */ 41851, /* 41857 */ 41911, /* 41921 */ 41983, /* 41985 */ 42043, /* 42049 */ 42101, /* 42113 */ 42169, /* 42177 */ 42239, /* 42241 */ 42299, /* 42305 */ 42359, /* 42369 */ 42433, /* 42433 */ 42491, /* 42497 */ 42557, /* 42561 */ 42611, /* 42625 */ 42689, /* 42689 */ 42751, /* 42753 */ 42797, /* 42817 */ 42863, /* 42881 */ 42943, /* 42945 */ 43003, /* 43009 */ 43067, /* 43073 */ 43133, /* 43137 */ 43201, /* 43201 */ 43261, /* 43265 */ 43321, /* 43329 */ 43391, /* 43393 */ 43457, /* 43457 */ 43517, /* 43521 */ 43579, /* 43585 */ 43649, /* 43649 */ 43711, /* 43713 */ 43777, /* 43777 */ 43801, /* 43841 */ 43891, /* 43905 */ 43969, /* 43969 */ 44029, /* 44033 */ 44089, /* 44097 */ 44159, /* 44161 */ 44221, /* 44225 */ 44281, /* 44289 */ 44351, /* 44353 */ 44417, /* 44417 */ 44453, /* 44481 */ 44543, /* 44545 */ 44587, /* 44609 */ 44657, /* 44673 */ 44729, /* 44737 */ 44797, /* 44801 */ 44851, /* 44865 */ 44927, /* 44929 */ 44987, /* 44993 */ 45053, /* 45057 */ 45121, /* 45121 */ 45181, /* 45185 */ 45247, /* 45249 */ 45307, /* 45313 */ 45377, /* 45377 */ 45439, /* 45441 */ 45503, /* 45505 */ 45569, /* 45569 */ 45631, /* 45633 */ 45697, /* 45697 */ 45757, /* 45761 */ 45823, /* 45825 */ 45887, /* 45889 */ 45953, /* 45953 */ 45989, /* 46017 */ 46073, /* 46081 */ 46141, /* 46145 */ 46199, /* 46209 */ 46273, /* 46273 */ 46337, /* 46337 */ 46399, /* 46401 */ 46457, /* 46465 */ 46523, /* 46529 */ 46591, /* 46593 */ 46649, /* 46657 */ 46703, /* 46721 */ 46771, /* 46785 */ 46831, /* 46849 */ 46901, /* 46913 */ 46957, /* 46977 */ 47041, /* 47041 */ 47093, /* 47105 */ 47161, /* 47169 */ 47221, /* 47233 */ 47297, /* 47297 */ 47353, /* 47361 */ 47419, /* 47425 */ 47459, /* 47489 */ 47543, /* 47553 */ 47609, /* 47617 */ 47681, /* 47681 */ 47743, /* 47745 */ 47809, /* 47809 */ 47869, /* 47873 */ 47933, /* 47937 */ 47981, /* 48001 */ 48049, /* 48065 */ 48121, /* 48129 */ 48193, /* 48193 */ 48247, /* 48257 */ 48313, /* 48321 */ 48383, /* 48385 */ 48449, /* 48449 */ 48497, /* 48513 */ 48571, /* 48577 */ 48623, /* 48641 */ 48679, /* 48705 */ 48767, /* 48769 */ 48823, /* 48833 */ 48889, /* 48897 */ 48953, /* 48961 */ 49019, /* 49025 */ 49081, /* 49089 */ 49139, /* 49153 */ 49211, /* 49217 */ 49279, /* 49281 */ 49339, /* 49345 */ 49409, /* 49409 */ 49463, /* 49473 */ 49537, /* 49537 */ 49597, /* 49601 */ 49663, /* 49665 */ 49727, /* 49729 */ 49789, /* 49793 */ 49853, /* 49857 */ 49921, /* 49921 */ 49957, /* 49985 */ 50047, /* 50049 */ 50111, /* 50113 */ 50177, /* 50177 */ 50231, /* 50241 */ 50291, /* 50305 */ 50363, /* 50369 */ 50423, /* 50433 */ 50497, /* 50497 */ 50551, /* 50561 */ 50599, /* 50625 */ 50683, /* 50689 */ 50753, /* 50753 */ 50789, /* 50817 */ 50873, /* 50881 */ 50929, /* 50945 */ 51001, /* 51009 */ 51071, /* 51073 */ 51137, /* 51137 */ 51199, /* 51201 */ 51263, /* 51265 */ 51329, /* 51329 */ 51383, /* 51393 */ 51449, /* 51457 */ 51521, /* 51521 */ 51581, /* 51585 */ 51647, /* 51649 */ 51713, /* 51713 */ 51769, /* 51777 */ 51839, /* 51841 */ 51899, /* 51905 */ 51949, /* 51969 */ 52027, /* 52033 */ 52081, /* 52097 */ 52153, /* 52161 */ 52223, /* 52225 */ 52289, /* 52289 */ 52321, /* 52353 */ 52391, /* 52417 */ 52457, /* 52481 */ 52543, /* 52545 */ 52609, /* 52609 */ 52673, /* 52673 */ 52733, /* 52737 */ 52783, /* 52801 */ 52861, /* 52865 */ 52919, /* 52929 */ 52981, /* 52993 */ 53051, /* 53057 */ 53117, /* 53121 */ 53173, /* 53185 */ 53239, /* 53249 */ 53309, /* 53313 */ 53377, /* 53377 */ 53441, /* 53441 */ 53503, /* 53505 */ 53569, /* 53569 */ 53633, /* 53633 */ 53693, /* 53697 */ 53759, /* 53761 */ 53819, /* 53825 */ 53887, /* 53889 */ 53951, /* 53953 */ 54013, /* 54017 */ 54059, /* 54081 */ 54139, /* 54145 */ 54193, /* 54209 */ 54269, /* 54273 */ 54331, /* 54337 */ 54401, /* 54401 */ 54449, /* 54465 */ 54521, /* 54529 */ 54583, /* 54593 */ 54647, /* 54657 */ 54721, /* 54721 */ 54779, /* 54785 */ 54833, /* 54849 */ 54907, /* 54913 */ 54973, /* 54977 */ 55021, /* 55041 */ 55103, /* 55105 */ 55163, /* 55169 */ 55229, /* 55233 */ 55291, /* 55297 */ 55351, /* 55361 */ 55411, /* 55425 */ 55487, /* 55489 */ 55547, /* 55553 */ 55609, /* 55617 */ 55681, /* 55681 */ 55733, /* 55745 */ 55807, /* 55809 */ 55871, /* 55873 */ 55933, /* 55937 */ 55997, /* 56001 */ 56053, /* 56065 */ 56123, /* 56129 */ 56179, /* 56193 */ 56249, /* 56257 */ 56311, /* 56321 */ 56383, /* 56385 */ 56443, /* 56449 */ 56509, /* 56513 */ 56569, /* 56577 */ 56633, /* 56641 */ 56701, /* 56705 */ 56767, /* 56769 */ 56827, /* 56833 */ 56897, /* 56897 */ 56957, /* 56961 */ 56999, /* 57025 */ 57089, /* 57089 */ 57149, /* 57153 */ 57203, /* 57217 */ 57271, /* 57281 */ 57331, /* 57345 */ 57397, /* 57409 */ 57467, /* 57473 */ 57529, /* 57537 */ 57601, /* 57601 */ 57653, /* 57665 */ 57727, /* 57729 */ 57793, /* 57793 */ 57853, /* 57857 */ 57917, /* 57921 */ 57977, /* 57985 */ 58049, /* 58049 */ 58111, /* 58113 */ 58171, /* 58177 */ 58237, /* 58241 */ 58271, /* 58305 */ 58369, /* 58369 */ 58427, /* 58433 */ 58481, /* 58497 */ 58549, /* 58561 */ 58613, /* 58625 */ 58687, /* 58689 */ 58741, /* 58753 */ 58789, /* 58817 */ 58831, /* 58881 */ 58943, /* 58945 */ 59009, /* 59009 */ 59069, /* 59073 */ 59123, /* 59137 */ 59197, /* 59201 */ 59263, /* 59265 */ 59281, /* 59329 */ 59393, /* 59393 */ 59453, /* 59457 */ 59513, /* 59521 */ 59581, /* 59585 */ 59629, /* 59649 */ 59707, /* 59713 */ 59771, /* 59777 */ 59833, /* 59841 */ 59887, /* 59905 */ 59957, /* 59969 */ 60029, /* 60033 */ 60091, /* 60097 */ 60161, /* 60161 */ 60223, /* 60225 */ 60289, /* 60289 */ 60353, /* 60353 */ 60413, /* 60417 */ 60457, /* 60481 */ 60539, /* 60545 */ 60607, /* 60609 */ 60661, /* 60673 */ 60737, /* 60737 */ 60793, /* 60801 */ 60859, /* 60865 */ 60923, /* 60929 */ 60961, /* 60993 */ 61057, /* 61057 */ 61121, /* 61121 */ 61169, /* 61185 */ 61231, /* 61249 */ 61297, /* 61313 */ 61363, /* 61377 */ 61441, /* 61441 */ 61493, /* 61505 */ 61561, /* 61569 */ 61631, /* 61633 */ 61687, /* 61697 */ 61757, /* 61761 */ 61819, /* 61825 */ 61879, /* 61889 */ 61949, /* 61953 */ 62017, /* 62017 */ 62081, /* 62081 */ 62143, /* 62145 */ 62207, /* 62209 */ 62273, /* 62273 */ 62327, /* 62337 */ 62401, /* 62401 */ 62459, /* 62465 */ 62507, /* 62529 */ 62591, /* 62593 */ 62653, /* 62657 */ 62701, /* 62721 */ 62773, /* 62785 */ 62827, /* 62849 */ 62903, /* 62913 */ 62971, /* 62977 */ 63031, /* 63041 */ 63103, /* 63105 */ 63149, /* 63169 */ 63211, /* 63233 */ 63281, /* 63297 */ 63361, /* 63361 */ 63421, /* 63425 */ 63487, /* 63489 */ 63541, /* 63553 */ 63617, /* 63617 */ 63671, /* 63681 */ 63743, /* 63745 */ 63809, /* 63809 */ 63863, /* 63873 */ 63929, /* 63937 */ 63997, /* 64001 */ 64063, /* 64065 */ 64123, /* 64129 */ 64189, /* 64193 */ 64237, /* 64257 */ 64319, /* 64321 */ 64381, /* 64385 */ 64439, /* 64449 */ 64513, /* 64513 */ 64577, /* 64577 */ 64633, /* 64641 */ 64693, /* 64705 */ 64763, /* 64769 */ 64817, /* 64833 */ 64891, /* 64897 */ 64951, /* 64961 */ 65011, /* 65025 */ 65089, /* 65089 */ 65147, /* 65153 */ 65213, /* 65217 */ 65269, /* 65281 */ 65327, /* 65345 */ 65407, /* 65409 */ 65449, /* 65473 */ }; /* 0-1M, increments=1024 */ static unsigned prime_table2[1024]={ 1021, /* 1024 */ 2039, /* 2048 */ 3067, /* 3072 */ 4093, /* 4096 */ 5119, /* 5120 */ 6143, /* 6144 */ 7159, /* 7168 */ 8191, /* 8192 */ 9209, /* 9216 */ 10223, /* 10240 */ 11261, /* 11264 */ 12281, /* 12288 */ 13309, /* 13312 */ 14327, /* 14336 */ 15359, /* 15360 */ 16381, /* 16384 */ 17401, /* 17408 */ 18427, /* 18432 */ 19447, /* 19456 */ 20479, /* 20480 */ 21503, /* 21504 */ 22511, /* 22528 */ 23549, /* 23552 */ 24571, /* 24576 */ 25589, /* 25600 */ 26597, /* 26624 */ 27647, /* 27648 */ 28669, /* 28672 */ 29683, /* 29696 */ 30713, /* 30720 */ 31741, /* 31744 */ 32749, /* 32768 */ 33791, /* 33792 */ 34807, /* 34816 */ 35839, /* 35840 */ 36857, /* 36864 */ 37879, /* 37888 */ 38903, /* 38912 */ 39929, /* 39936 */ 40949, /* 40960 */ 41983, /* 41984 */ 43003, /* 43008 */ 44029, /* 44032 */ 45053, /* 45056 */ 46073, /* 46080 */ 47093, /* 47104 */ 48121, /* 48128 */ 49139, /* 49152 */ 50159, /* 50176 */ 51199, /* 51200 */ 52223, /* 52224 */ 53239, /* 53248 */ 54269, /* 54272 */ 55291, /* 55296 */ 56311, /* 56320 */ 57331, /* 57344 */ 58367, /* 58368 */ 59387, /* 59392 */ 60413, /* 60416 */ 61417, /* 61440 */ 62459, /* 62464 */ 63487, /* 63488 */ 64499, /* 64512 */ 65521, /* 65536 */ 66553, /* 66560 */ 67579, /* 67584 */ 68597, /* 68608 */ 69623, /* 69632 */ 70639, /* 70656 */ 71671, /* 71680 */ 72701, /* 72704 */ 73727, /* 73728 */ 74747, /* 74752 */ 75773, /* 75776 */ 76781, /* 76800 */ 77813, /* 77824 */ 78839, /* 78848 */ 79867, /* 79872 */ 80863, /* 80896 */ 81919, /* 81920 */ 82939, /* 82944 */ 83939, /* 83968 */ 84991, /* 84992 */ 86011, /* 86016 */ 87037, /* 87040 */ 88037, /* 88064 */ 89087, /* 89088 */ 90107, /* 90112 */ 91129, /* 91136 */ 92153, /* 92160 */ 93179, /* 93184 */ 94207, /* 94208 */ 95231, /* 95232 */ 96233, /* 96256 */ 97259, /* 97280 */ 98299, /* 98304 */ 99317, /* 99328 */ 100343, /* 100352 */ 101363, /* 101376 */ 102397, /* 102400 */ 103423, /* 103424 */ 104417, /* 104448 */ 105467, /* 105472 */ 106487, /* 106496 */ 107509, /* 107520 */ 108541, /* 108544 */ 109567, /* 109568 */ 110587, /* 110592 */ 111611, /* 111616 */ 112621, /* 112640 */ 113657, /* 113664 */ 114679, /* 114688 */ 115693, /* 115712 */ 116731, /* 116736 */ 117757, /* 117760 */ 118757, /* 118784 */ 119797, /* 119808 */ 120829, /* 120832 */ 121853, /* 121856 */ 122869, /* 122880 */ 123887, /* 123904 */ 124919, /* 124928 */ 125941, /* 125952 */ 126967, /* 126976 */ 127997, /* 128000 */ 129023, /* 129024 */ 130043, /* 130048 */ 131071, /* 131072 */ 132071, /* 132096 */ 133117, /* 133120 */ 134129, /* 134144 */ 135151, /* 135168 */ 136189, /* 136192 */ 137209, /* 137216 */ 138239, /* 138240 */ 139241, /* 139264 */ 140281, /* 140288 */ 141311, /* 141312 */ 142327, /* 142336 */ 143357, /* 143360 */ 144383, /* 144384 */ 145399, /* 145408 */ 146423, /* 146432 */ 147451, /* 147456 */ 148471, /* 148480 */ 149503, /* 149504 */ 150523, /* 150528 */ 151549, /* 151552 */ 152567, /* 152576 */ 153589, /* 153600 */ 154621, /* 154624 */ 155627, /* 155648 */ 156671, /* 156672 */ 157679, /* 157696 */ 158699, /* 158720 */ 159739, /* 159744 */ 160757, /* 160768 */ 161783, /* 161792 */ 162791, /* 162816 */ 163819, /* 163840 */ 164839, /* 164864 */ 165887, /* 165888 */ 166909, /* 166912 */ 167917, /* 167936 */ 168943, /* 168960 */ 169957, /* 169984 */ 171007, /* 171008 */ 172031, /* 172032 */ 173053, /* 173056 */ 174079, /* 174080 */ 175103, /* 175104 */ 176123, /* 176128 */ 177131, /* 177152 */ 178169, /* 178176 */ 179173, /* 179200 */ 180221, /* 180224 */ 181243, /* 181248 */ 182261, /* 182272 */ 183289, /* 183296 */ 184309, /* 184320 */ 185327, /* 185344 */ 186343, /* 186368 */ 187387, /* 187392 */ 188407, /* 188416 */ 189439, /* 189440 */ 190409, /* 190464 */ 191473, /* 191488 */ 192499, /* 192512 */ 193513, /* 193536 */ 194543, /* 194560 */ 195581, /* 195584 */ 196597, /* 196608 */ 197621, /* 197632 */ 198647, /* 198656 */ 199679, /* 199680 */ 200699, /* 200704 */ 201709, /* 201728 */ 202751, /* 202752 */ 203773, /* 203776 */ 204797, /* 204800 */ 205823, /* 205824 */ 206827, /* 206848 */ 207869, /* 207872 */ 208891, /* 208896 */ 209917, /* 209920 */ 210943, /* 210944 */ 211949, /* 211968 */ 212987, /* 212992 */ 214009, /* 214016 */ 214993, /* 215040 */ 216061, /* 216064 */ 217081, /* 217088 */ 218111, /* 218112 */ 219133, /* 219136 */ 220151, /* 220160 */ 221173, /* 221184 */ 222199, /* 222208 */ 223229, /* 223232 */ 224251, /* 224256 */ 225263, /* 225280 */ 226283, /* 226304 */ 227303, /* 227328 */ 228341, /* 228352 */ 229373, /* 229376 */ 230393, /* 230400 */ 231419, /* 231424 */ 232439, /* 232448 */ 233437, /* 233472 */ 234473, /* 234496 */ 235519, /* 235520 */ 236527, /* 236544 */ 237563, /* 237568 */ 238591, /* 238592 */ 239611, /* 239616 */ 240631, /* 240640 */ 241663, /* 241664 */ 242681, /* 242688 */ 243709, /* 243712 */ 244733, /* 244736 */ 245759, /* 245760 */ 246781, /* 246784 */ 247799, /* 247808 */ 248827, /* 248832 */ 249853, /* 249856 */ 250871, /* 250880 */ 251903, /* 251904 */ 252919, /* 252928 */ 253951, /* 253952 */ 254971, /* 254976 */ 255989, /* 256000 */ 257017, /* 257024 */ 258031, /* 258048 */ 259033, /* 259072 */ 260089, /* 260096 */ 261101, /* 261120 */ 262139, /* 262144 */ 263167, /* 263168 */ 264179, /* 264192 */ 265207, /* 265216 */ 266239, /* 266240 */ 267259, /* 267264 */ 268283, /* 268288 */ 269281, /* 269312 */ 270329, /* 270336 */ 271357, /* 271360 */ 272383, /* 272384 */ 273367, /* 273408 */ 274423, /* 274432 */ 275453, /* 275456 */ 276467, /* 276480 */ 277499, /* 277504 */ 278503, /* 278528 */ 279551, /* 279552 */ 280561, /* 280576 */ 281581, /* 281600 */ 282617, /* 282624 */ 283639, /* 283648 */ 284659, /* 284672 */ 285673, /* 285696 */ 286711, /* 286720 */ 287731, /* 287744 */ 288767, /* 288768 */ 289789, /* 289792 */ 290803, /* 290816 */ 291833, /* 291840 */ 292849, /* 292864 */ 293863, /* 293888 */ 294911, /* 294912 */ 295909, /* 295936 */ 296941, /* 296960 */ 297971, /* 297984 */ 298999, /* 299008 */ 300023, /* 300032 */ 301051, /* 301056 */ 302053, /* 302080 */ 303097, /* 303104 */ 304127, /* 304128 */ 305147, /* 305152 */ 306169, /* 306176 */ 307189, /* 307200 */ 308219, /* 308224 */ 309241, /* 309248 */ 310243, /* 310272 */ 311293, /* 311296 */ 312313, /* 312320 */ 313343, /* 313344 */ 314359, /* 314368 */ 315389, /* 315392 */ 316403, /* 316416 */ 317437, /* 317440 */ 318457, /* 318464 */ 319483, /* 319488 */ 320483, /* 320512 */ 321509, /* 321536 */ 322559, /* 322560 */ 323581, /* 323584 */ 324593, /* 324608 */ 325631, /* 325632 */ 326633, /* 326656 */ 327673, /* 327680 */ 328687, /* 328704 */ 329723, /* 329728 */ 330749, /* 330752 */ 331769, /* 331776 */ 332791, /* 332800 */ 333821, /* 333824 */ 334843, /* 334848 */ 335857, /* 335872 */ 336887, /* 336896 */ 337919, /* 337920 */ 338927, /* 338944 */ 339959, /* 339968 */ 340979, /* 340992 */ 341993, /* 342016 */ 343037, /* 343040 */ 344053, /* 344064 */ 345067, /* 345088 */ 346111, /* 346112 */ 347131, /* 347136 */ 348149, /* 348160 */ 349183, /* 349184 */ 350191, /* 350208 */ 351229, /* 351232 */ 352249, /* 352256 */ 353263, /* 353280 */ 354301, /* 354304 */ 355321, /* 355328 */ 356351, /* 356352 */ 357359, /* 357376 */ 358373, /* 358400 */ 359419, /* 359424 */ 360439, /* 360448 */ 361469, /* 361472 */ 362473, /* 362496 */ 363497, /* 363520 */ 364543, /* 364544 */ 365567, /* 365568 */ 366547, /* 366592 */ 367613, /* 367616 */ 368633, /* 368640 */ 369661, /* 369664 */ 370687, /* 370688 */ 371699, /* 371712 */ 372733, /* 372736 */ 373757, /* 373760 */ 374783, /* 374784 */ 375799, /* 375808 */ 376823, /* 376832 */ 377851, /* 377856 */ 378869, /* 378880 */ 379903, /* 379904 */ 380917, /* 380928 */ 381949, /* 381952 */ 382961, /* 382976 */ 383987, /* 384000 */ 385013, /* 385024 */ 386047, /* 386048 */ 387071, /* 387072 */ 388081, /* 388096 */ 389117, /* 389120 */ 390119, /* 390144 */ 391163, /* 391168 */ 392177, /* 392192 */ 393209, /* 393216 */ 394223, /* 394240 */ 395261, /* 395264 */ 396269, /* 396288 */ 397303, /* 397312 */ 398323, /* 398336 */ 399353, /* 399360 */ 400381, /* 400384 */ 401407, /* 401408 */ 402419, /* 402432 */ 403439, /* 403456 */ 404461, /* 404480 */ 405499, /* 405504 */ 406517, /* 406528 */ 407527, /* 407552 */ 408563, /* 408576 */ 409597, /* 409600 */ 410623, /* 410624 */ 411641, /* 411648 */ 412667, /* 412672 */ 413689, /* 413696 */ 414709, /* 414720 */ 415729, /* 415744 */ 416761, /* 416768 */ 417773, /* 417792 */ 418813, /* 418816 */ 419831, /* 419840 */ 420859, /* 420864 */ 421847, /* 421888 */ 422911, /* 422912 */ 423931, /* 423936 */ 424939, /* 424960 */ 425977, /* 425984 */ 427001, /* 427008 */ 428027, /* 428032 */ 429043, /* 429056 */ 430061, /* 430080 */ 431099, /* 431104 */ 432121, /* 432128 */ 433151, /* 433152 */ 434167, /* 434176 */ 435191, /* 435200 */ 436217, /* 436224 */ 437243, /* 437248 */ 438271, /* 438272 */ 439289, /* 439296 */ 440311, /* 440320 */ 441319, /* 441344 */ 442367, /* 442368 */ 443389, /* 443392 */ 444403, /* 444416 */ 445433, /* 445440 */ 446461, /* 446464 */ 447481, /* 447488 */ 448451, /* 448512 */ 449473, /* 449536 */ 450557, /* 450560 */ 451579, /* 451584 */ 452597, /* 452608 */ 453631, /* 453632 */ 454637, /* 454656 */ 455659, /* 455680 */ 456697, /* 456704 */ 457711, /* 457728 */ 458747, /* 458752 */ 459763, /* 459776 */ 460793, /* 460800 */ 461819, /* 461824 */ 462841, /* 462848 */ 463867, /* 463872 */ 464879, /* 464896 */ 465917, /* 465920 */ 466919, /* 466944 */ 467963, /* 467968 */ 468983, /* 468992 */ 469993, /* 470016 */ 471007, /* 471040 */ 472063, /* 472064 */ 473027, /* 473088 */ 474101, /* 474112 */ 475109, /* 475136 */ 476143, /* 476160 */ 477163, /* 477184 */ 478207, /* 478208 */ 479231, /* 479232 */ 480209, /* 480256 */ 481249, /* 481280 */ 482281, /* 482304 */ 483323, /* 483328 */ 484339, /* 484352 */ 485371, /* 485376 */ 486397, /* 486400 */ 487423, /* 487424 */ 488441, /* 488448 */ 489457, /* 489472 */ 490493, /* 490496 */ 491503, /* 491520 */ 492523, /* 492544 */ 493567, /* 493568 */ 494591, /* 494592 */ 495613, /* 495616 */ 496631, /* 496640 */ 497663, /* 497664 */ 498679, /* 498688 */ 499711, /* 499712 */ 500729, /* 500736 */ 501731, /* 501760 */ 502781, /* 502784 */ 503803, /* 503808 */ 504821, /* 504832 */ 505823, /* 505856 */ 506873, /* 506880 */ 507901, /* 507904 */ 508919, /* 508928 */ 509947, /* 509952 */ 510943, /* 510976 */ 511997, /* 512000 */ 513017, /* 513024 */ 514021, /* 514048 */ 515041, /* 515072 */ 516091, /* 516096 */ 517091, /* 517120 */ 518137, /* 518144 */ 519161, /* 519168 */ 520151, /* 520192 */ 521201, /* 521216 */ 522239, /* 522240 */ 523261, /* 523264 */ 524287, /* 524288 */ 525299, /* 525312 */ 526307, /* 526336 */ 527353, /* 527360 */ 528383, /* 528384 */ 529393, /* 529408 */ 530429, /* 530432 */ 531383, /* 531456 */ 532453, /* 532480 */ 533459, /* 533504 */ 534511, /* 534528 */ 535547, /* 535552 */ 536563, /* 536576 */ 537599, /* 537600 */ 538621, /* 538624 */ 539641, /* 539648 */ 540629, /* 540672 */ 541693, /* 541696 */ 542719, /* 542720 */ 543713, /* 543744 */ 544759, /* 544768 */ 545791, /* 545792 */ 546781, /* 546816 */ 547831, /* 547840 */ 548861, /* 548864 */ 549883, /* 549888 */ 550909, /* 550912 */ 551933, /* 551936 */ 552917, /* 552960 */ 553981, /* 553984 */ 554977, /* 555008 */ 556027, /* 556032 */ 557041, /* 557056 */ 558067, /* 558080 */ 559099, /* 559104 */ 560123, /* 560128 */ 561109, /* 561152 */ 562169, /* 562176 */ 563197, /* 563200 */ 564197, /* 564224 */ 565247, /* 565248 */ 566233, /* 566272 */ 567277, /* 567296 */ 568303, /* 568320 */ 569323, /* 569344 */ 570359, /* 570368 */ 571381, /* 571392 */ 572399, /* 572416 */ 573437, /* 573440 */ 574439, /* 574464 */ 575479, /* 575488 */ 576509, /* 576512 */ 577531, /* 577536 */ 578537, /* 578560 */ 579583, /* 579584 */ 580607, /* 580608 */ 581617, /* 581632 */ 582649, /* 582656 */ 583673, /* 583680 */ 584699, /* 584704 */ 585727, /* 585728 */ 586741, /* 586752 */ 587773, /* 587776 */ 588779, /* 588800 */ 589811, /* 589824 */ 590839, /* 590848 */ 591863, /* 591872 */ 592877, /* 592896 */ 593903, /* 593920 */ 594931, /* 594944 */ 595967, /* 595968 */ 596987, /* 596992 */ 598007, /* 598016 */ 599023, /* 599040 */ 600053, /* 600064 */ 601079, /* 601088 */ 602111, /* 602112 */ 603133, /* 603136 */ 604073, /* 604160 */ 605177, /* 605184 */ 606181, /* 606208 */ 607219, /* 607232 */ 608213, /* 608256 */ 609277, /* 609280 */ 610301, /* 610304 */ 611323, /* 611328 */ 612349, /* 612352 */ 613367, /* 613376 */ 614387, /* 614400 */ 615413, /* 615424 */ 616439, /* 616448 */ 617471, /* 617472 */ 618463, /* 618496 */ 619511, /* 619520 */ 620531, /* 620544 */ 621541, /* 621568 */ 622577, /* 622592 */ 623591, /* 623616 */ 624607, /* 624640 */ 625663, /* 625664 */ 626687, /* 626688 */ 627709, /* 627712 */ 628721, /* 628736 */ 629747, /* 629760 */ 630737, /* 630784 */ 631789, /* 631808 */ 632813, /* 632832 */ 633833, /* 633856 */ 634871, /* 634880 */ 635893, /* 635904 */ 636919, /* 636928 */ 637939, /* 637952 */ 638971, /* 638976 */ 639997, /* 640000 */ 640993, /* 641024 */ 642013, /* 642048 */ 643061, /* 643072 */ 644089, /* 644096 */ 645097, /* 645120 */ 646103, /* 646144 */ 647161, /* 647168 */ 648191, /* 648192 */ 649183, /* 649216 */ 650227, /* 650240 */ 651257, /* 651264 */ 652283, /* 652288 */ 653311, /* 653312 */ 654323, /* 654336 */ 655357, /* 655360 */ 656377, /* 656384 */ 657403, /* 657408 */ 658417, /* 658432 */ 659453, /* 659456 */ 660449, /* 660480 */ 661483, /* 661504 */ 662527, /* 662528 */ 663547, /* 663552 */ 664571, /* 664576 */ 665591, /* 665600 */ 666607, /* 666624 */ 667643, /* 667648 */ 668671, /* 668672 */ 669689, /* 669696 */ 670711, /* 670720 */ 671743, /* 671744 */ 672767, /* 672768 */ 673787, /* 673792 */ 674813, /* 674816 */ 675839, /* 675840 */ 676861, /* 676864 */ 677857, /* 677888 */ 678907, /* 678912 */ 679933, /* 679936 */ 680959, /* 680960 */ 681983, /* 681984 */ 683003, /* 683008 */ 684017, /* 684032 */ 685051, /* 685056 */ 686057, /* 686080 */ 687101, /* 687104 */ 688111, /* 688128 */ 689141, /* 689152 */ 690163, /* 690176 */ 691199, /* 691200 */ 692221, /* 692224 */ 693223, /* 693248 */ 694271, /* 694272 */ 695293, /* 695296 */ 696317, /* 696320 */ 697327, /* 697344 */ 698359, /* 698368 */ 699383, /* 699392 */ 700393, /* 700416 */ 701419, /* 701440 */ 702451, /* 702464 */ 703471, /* 703488 */ 704507, /* 704512 */ 705533, /* 705536 */ 706547, /* 706560 */ 707573, /* 707584 */ 708601, /* 708608 */ 709609, /* 709632 */ 710641, /* 710656 */ 711679, /* 711680 */ 712697, /* 712704 */ 713681, /* 713728 */ 714751, /* 714752 */ 715753, /* 715776 */ 716789, /* 716800 */ 717817, /* 717824 */ 718847, /* 718848 */ 719839, /* 719872 */ 720887, /* 720896 */ 721909, /* 721920 */ 722933, /* 722944 */ 723967, /* 723968 */ 724991, /* 724992 */ 726013, /* 726016 */ 727021, /* 727040 */ 728047, /* 728064 */ 729073, /* 729088 */ 730111, /* 730112 */ 731117, /* 731136 */ 732157, /* 732160 */ 733177, /* 733184 */ 734207, /* 734208 */ 735211, /* 735232 */ 736249, /* 736256 */ 737279, /* 737280 */ 738301, /* 738304 */ 739327, /* 739328 */ 740351, /* 740352 */ 741373, /* 741376 */ 742393, /* 742400 */ 743423, /* 743424 */ 744431, /* 744448 */ 745471, /* 745472 */ 746483, /* 746496 */ 747499, /* 747520 */ 748541, /* 748544 */ 749557, /* 749568 */ 750571, /* 750592 */ 751613, /* 751616 */ 752639, /* 752640 */ 753659, /* 753664 */ 754651, /* 754688 */ 755707, /* 755712 */ 756727, /* 756736 */ 757753, /* 757760 */ 758783, /* 758784 */ 759799, /* 759808 */ 760813, /* 760832 */ 761833, /* 761856 */ 762877, /* 762880 */ 763901, /* 763904 */ 764903, /* 764928 */ 765949, /* 765952 */ 766967, /* 766976 */ 767957, /* 768000 */ 769019, /* 769024 */ 770047, /* 770048 */ 771049, /* 771072 */ 772091, /* 772096 */ 773117, /* 773120 */ 774143, /* 774144 */ 775163, /* 775168 */ 776183, /* 776192 */ 777209, /* 777216 */ 778237, /* 778240 */ 779249, /* 779264 */ 780287, /* 780288 */ 781309, /* 781312 */ 782329, /* 782336 */ 783359, /* 783360 */ 784379, /* 784384 */ 785377, /* 785408 */ 786431, /* 786432 */ 787447, /* 787456 */ 788479, /* 788480 */ 789493, /* 789504 */ 790523, /* 790528 */ 791543, /* 791552 */ 792563, /* 792576 */ 793591, /* 793600 */ 794593, /* 794624 */ 795647, /* 795648 */ 796657, /* 796672 */ 797689, /* 797696 */ 798713, /* 798720 */ 799741, /* 799744 */ 800759, /* 800768 */ 801791, /* 801792 */ 802811, /* 802816 */ 803819, /* 803840 */ 804857, /* 804864 */ 805877, /* 805888 */ 806903, /* 806912 */ 807931, /* 807936 */ 808957, /* 808960 */ 809983, /* 809984 */ 810989, /* 811008 */ 812011, /* 812032 */ 813049, /* 813056 */ 814069, /* 814080 */ 815063, /* 815104 */ 816121, /* 816128 */ 817151, /* 817152 */ 818173, /* 818176 */ 819187, /* 819200 */ 820223, /* 820224 */ 821209, /* 821248 */ 822259, /* 822272 */ 823283, /* 823296 */ 824287, /* 824320 */ 825343, /* 825344 */ 826363, /* 826368 */ 827389, /* 827392 */ 828409, /* 828416 */ 829399, /* 829440 */ 830449, /* 830464 */ 831461, /* 831488 */ 832499, /* 832512 */ 833509, /* 833536 */ 834527, /* 834560 */ 835559, /* 835584 */ 836573, /* 836608 */ 837631, /* 837632 */ 838633, /* 838656 */ 839669, /* 839680 */ 840703, /* 840704 */ 841727, /* 841728 */ 842747, /* 842752 */ 843763, /* 843776 */ 844777, /* 844800 */ 845809, /* 845824 */ 846841, /* 846848 */ 847871, /* 847872 */ 848893, /* 848896 */ 849917, /* 849920 */ 850943, /* 850944 */ 851957, /* 851968 */ 852989, /* 852992 */ 853999, /* 854016 */ 855031, /* 855040 */ 856061, /* 856064 */ 857083, /* 857088 */ 858103, /* 858112 */ 859121, /* 859136 */ 860143, /* 860160 */ 861167, /* 861184 */ 862207, /* 862208 */ 863231, /* 863232 */ 864251, /* 864256 */ 865261, /* 865280 */ 866293, /* 866304 */ 867319, /* 867328 */ 868349, /* 868352 */ 869371, /* 869376 */ 870391, /* 870400 */ 871393, /* 871424 */ 872441, /* 872448 */ 873469, /* 873472 */ 874487, /* 874496 */ 875519, /* 875520 */ 876529, /* 876544 */ 877567, /* 877568 */ 878573, /* 878592 */ 879607, /* 879616 */ 880603, /* 880640 */ 881663, /* 881664 */ 882659, /* 882688 */ 883703, /* 883712 */ 884717, /* 884736 */ 885737, /* 885760 */ 886777, /* 886784 */ 887759, /* 887808 */ 888827, /* 888832 */ 889829, /* 889856 */ 890867, /* 890880 */ 891899, /* 891904 */ 892919, /* 892928 */ 893939, /* 893952 */ 894973, /* 894976 */ 895987, /* 896000 */ 897019, /* 897024 */ 898033, /* 898048 */ 899069, /* 899072 */ 900091, /* 900096 */ 901111, /* 901120 */ 902141, /* 902144 */ 903163, /* 903168 */ 904181, /* 904192 */ 905213, /* 905216 */ 906233, /* 906240 */ 907259, /* 907264 */ 908287, /* 908288 */ 909301, /* 909312 */ 910307, /* 910336 */ 911359, /* 911360 */ 912367, /* 912384 */ 913397, /* 913408 */ 914429, /* 914432 */ 915451, /* 915456 */ 916477, /* 916480 */ 917503, /* 917504 */ 918497, /* 918528 */ 919531, /* 919552 */ 920561, /* 920576 */ 921589, /* 921600 */ 922619, /* 922624 */ 923641, /* 923648 */ 924661, /* 924672 */ 925679, /* 925696 */ 926707, /* 926720 */ 927743, /* 927744 */ 928703, /* 928768 */ 929791, /* 929792 */ 930779, /* 930816 */ 931837, /* 931840 */ 932863, /* 932864 */ 933883, /* 933888 */ 934909, /* 934912 */ 935903, /* 935936 */ 936953, /* 936960 */ 937969, /* 937984 */ 939007, /* 939008 */ 940031, /* 940032 */ 941041, /* 941056 */ 942079, /* 942080 */ 943097, /* 943104 */ 944123, /* 944128 */ 945151, /* 945152 */ 946163, /* 946176 */ 947197, /* 947200 */ 948187, /* 948224 */ 949243, /* 949248 */ 950269, /* 950272 */ 951283, /* 951296 */ 952313, /* 952320 */ 953341, /* 953344 */ 954367, /* 954368 */ 955391, /* 955392 */ 956401, /* 956416 */ 957433, /* 957440 */ 958459, /* 958464 */ 959479, /* 959488 */ 960499, /* 960512 */ 961531, /* 961536 */ 962543, /* 962560 */ 963581, /* 963584 */ 964589, /* 964608 */ 965623, /* 965632 */ 966653, /* 966656 */ 967667, /* 967680 */ 968699, /* 968704 */ 969721, /* 969728 */ 970747, /* 970752 */ 971767, /* 971776 */ 972799, /* 972800 */ 973823, /* 973824 */ 974837, /* 974848 */ 975869, /* 975872 */ 976883, /* 976896 */ 977897, /* 977920 */ 978931, /* 978944 */ 979949, /* 979968 */ 980963, /* 980992 */ 981983, /* 982016 */ 982981, /* 983040 */ 984059, /* 984064 */ 985079, /* 985088 */ 986101, /* 986112 */ 987127, /* 987136 */ 988157, /* 988160 */ 989173, /* 989184 */ 990181, /* 990208 */ 991229, /* 991232 */ 992249, /* 992256 */ 993269, /* 993280 */ 994303, /* 994304 */ 995327, /* 995328 */ 996329, /* 996352 */ 997369, /* 997376 */ 998399, /* 998400 */ 999389, /* 999424 */ 1000429, /* 1000448 */ 1001467, /* 1001472 */ 1002493, /* 1002496 */ 1003517, /* 1003520 */ 1004537, /* 1004544 */ 1005553, /* 1005568 */ 1006589, /* 1006592 */ 1007609, /* 1007616 */ 1008617, /* 1008640 */ 1009651, /* 1009664 */ 1010687, /* 1010688 */ 1011697, /* 1011712 */ 1012733, /* 1012736 */ 1013741, /* 1013760 */ 1014779, /* 1014784 */ 1015769, /* 1015808 */ 1016789, /* 1016832 */ 1017851, /* 1017856 */ 1018879, /* 1018880 */ 1019903, /* 1019904 */ 1020913, /* 1020928 */ 1021919, /* 1021952 */ 1022963, /* 1022976 */ 1023991, /* 1024000 */ 1025021, /* 1025024 */ 1026043, /* 1026048 */ 1027067, /* 1027072 */ 1028089, /* 1028096 */ 1029113, /* 1029120 */ 1030121, /* 1030144 */ 1031161, /* 1031168 */ 1032191, /* 1032192 */ 1033189, /* 1033216 */ 1034239, /* 1034240 */ 1035263, /* 1035264 */ 1036271, /* 1036288 */ 1037303, /* 1037312 */ 1038329, /* 1038336 */ 1039351, /* 1039360 */ 1040381, /* 1040384 */ 1041373, /* 1041408 */ 1042427, /* 1042432 */ 1043453, /* 1043456 */ 1044479, /* 1044480 */ 1045493, /* 1045504 */ 1046527, /* 1046528 */ 1047551, /* 1047552 */ }; /* 0-128M, increments=102400 */ static unsigned prime_table3[1024]={ 131071, /* 131072 */ 262139, /* 262144 */ 393209, /* 393216 */ 524287, /* 524288 */ 655357, /* 655360 */ 786431, /* 786432 */ 917503, /* 917504 */ 1048573, /* 1048576 */ 1179641, /* 1179648 */ 1310719, /* 1310720 */ 1441771, /* 1441792 */ 1572853, /* 1572864 */ 1703903, /* 1703936 */ 1835003, /* 1835008 */ 1966079, /* 1966080 */ 2097143, /* 2097152 */ 2228221, /* 2228224 */ 2359267, /* 2359296 */ 2490337, /* 2490368 */ 2621431, /* 2621440 */ 2752499, /* 2752512 */ 2883577, /* 2883584 */ 3014653, /* 3014656 */ 3145721, /* 3145728 */ 3276799, /* 3276800 */ 3407857, /* 3407872 */ 3538933, /* 3538944 */ 3670013, /* 3670016 */ 3801073, /* 3801088 */ 3932153, /* 3932160 */ 4063217, /* 4063232 */ 4194301, /* 4194304 */ 4325359, /* 4325376 */ 4456433, /* 4456448 */ 4587503, /* 4587520 */ 4718579, /* 4718592 */ 4849651, /* 4849664 */ 4980727, /* 4980736 */ 5111791, /* 5111808 */ 5242877, /* 5242880 */ 5373931, /* 5373952 */ 5505023, /* 5505024 */ 5636077, /* 5636096 */ 5767129, /* 5767168 */ 5898209, /* 5898240 */ 6029299, /* 6029312 */ 6160381, /* 6160384 */ 6291449, /* 6291456 */ 6422519, /* 6422528 */ 6553577, /* 6553600 */ 6684659, /* 6684672 */ 6815741, /* 6815744 */ 6946813, /* 6946816 */ 7077883, /* 7077888 */ 7208951, /* 7208960 */ 7340009, /* 7340032 */ 7471099, /* 7471104 */ 7602151, /* 7602176 */ 7733233, /* 7733248 */ 7864301, /* 7864320 */ 7995391, /* 7995392 */ 8126453, /* 8126464 */ 8257531, /* 8257536 */ 8388593, /* 8388608 */ 8519647, /* 8519680 */ 8650727, /* 8650752 */ 8781797, /* 8781824 */ 8912887, /* 8912896 */ 9043967, /* 9043968 */ 9175037, /* 9175040 */ 9306097, /* 9306112 */ 9437179, /* 9437184 */ 9568219, /* 9568256 */ 9699323, /* 9699328 */ 9830393, /* 9830400 */ 9961463, /* 9961472 */ 10092539, /* 10092544 */ 10223593, /* 10223616 */ 10354667, /* 10354688 */ 10485751, /* 10485760 */ 10616831, /* 10616832 */ 10747903, /* 10747904 */ 10878961, /* 10878976 */ 11010037, /* 11010048 */ 11141113, /* 11141120 */ 11272181, /* 11272192 */ 11403247, /* 11403264 */ 11534329, /* 11534336 */ 11665403, /* 11665408 */ 11796469, /* 11796480 */ 11927551, /* 11927552 */ 12058621, /* 12058624 */ 12189677, /* 12189696 */ 12320753, /* 12320768 */ 12451807, /* 12451840 */ 12582893, /* 12582912 */ 12713959, /* 12713984 */ 12845033, /* 12845056 */ 12976121, /* 12976128 */ 13107197, /* 13107200 */ 13238263, /* 13238272 */ 13369333, /* 13369344 */ 13500373, /* 13500416 */ 13631477, /* 13631488 */ 13762549, /* 13762560 */ 13893613, /* 13893632 */ 14024671, /* 14024704 */ 14155763, /* 14155776 */ 14286809, /* 14286848 */ 14417881, /* 14417920 */ 14548979, /* 14548992 */ 14680063, /* 14680064 */ 14811133, /* 14811136 */ 14942197, /* 14942208 */ 15073277, /* 15073280 */ 15204349, /* 15204352 */ 15335407, /* 15335424 */ 15466463, /* 15466496 */ 15597559, /* 15597568 */ 15728611, /* 15728640 */ 15859687, /* 15859712 */ 15990781, /* 15990784 */ 16121849, /* 16121856 */ 16252919, /* 16252928 */ 16383977, /* 16384000 */ 16515067, /* 16515072 */ 16646099, /* 16646144 */ 16777213, /* 16777216 */ 16908263, /* 16908288 */ 17039339, /* 17039360 */ 17170429, /* 17170432 */ 17301463, /* 17301504 */ 17432561, /* 17432576 */ 17563633, /* 17563648 */ 17694709, /* 17694720 */ 17825791, /* 17825792 */ 17956849, /* 17956864 */ 18087899, /* 18087936 */ 18219001, /* 18219008 */ 18350063, /* 18350080 */ 18481097, /* 18481152 */ 18612211, /* 18612224 */ 18743281, /* 18743296 */ 18874367, /* 18874368 */ 19005433, /* 19005440 */ 19136503, /* 19136512 */ 19267561, /* 19267584 */ 19398647, /* 19398656 */ 19529717, /* 19529728 */ 19660799, /* 19660800 */ 19791869, /* 19791872 */ 19922923, /* 19922944 */ 20054011, /* 20054016 */ 20185051, /* 20185088 */ 20316151, /* 20316160 */ 20447191, /* 20447232 */ 20578297, /* 20578304 */ 20709347, /* 20709376 */ 20840429, /* 20840448 */ 20971507, /* 20971520 */ 21102583, /* 21102592 */ 21233651, /* 21233664 */ 21364727, /* 21364736 */ 21495797, /* 21495808 */ 21626819, /* 21626880 */ 21757951, /* 21757952 */ 21889019, /* 21889024 */ 22020091, /* 22020096 */ 22151167, /* 22151168 */ 22282199, /* 22282240 */ 22413289, /* 22413312 */ 22544351, /* 22544384 */ 22675403, /* 22675456 */ 22806521, /* 22806528 */ 22937591, /* 22937600 */ 23068667, /* 23068672 */ 23199731, /* 23199744 */ 23330773, /* 23330816 */ 23461877, /* 23461888 */ 23592937, /* 23592960 */ 23724031, /* 23724032 */ 23855101, /* 23855104 */ 23986159, /* 23986176 */ 24117217, /* 24117248 */ 24248299, /* 24248320 */ 24379391, /* 24379392 */ 24510463, /* 24510464 */ 24641479, /* 24641536 */ 24772603, /* 24772608 */ 24903667, /* 24903680 */ 25034731, /* 25034752 */ 25165813, /* 25165824 */ 25296893, /* 25296896 */ 25427957, /* 25427968 */ 25559033, /* 25559040 */ 25690097, /* 25690112 */ 25821179, /* 25821184 */ 25952243, /* 25952256 */ 26083273, /* 26083328 */ 26214379, /* 26214400 */ 26345471, /* 26345472 */ 26476543, /* 26476544 */ 26607611, /* 26607616 */ 26738687, /* 26738688 */ 26869753, /* 26869760 */ 27000817, /* 27000832 */ 27131903, /* 27131904 */ 27262931, /* 27262976 */ 27394019, /* 27394048 */ 27525109, /* 27525120 */ 27656149, /* 27656192 */ 27787213, /* 27787264 */ 27918323, /* 27918336 */ 28049407, /* 28049408 */ 28180459, /* 28180480 */ 28311541, /* 28311552 */ 28442551, /* 28442624 */ 28573673, /* 28573696 */ 28704749, /* 28704768 */ 28835819, /* 28835840 */ 28966909, /* 28966912 */ 29097977, /* 29097984 */ 29229047, /* 29229056 */ 29360087, /* 29360128 */ 29491193, /* 29491200 */ 29622269, /* 29622272 */ 29753341, /* 29753344 */ 29884411, /* 29884416 */ 30015481, /* 30015488 */ 30146531, /* 30146560 */ 30277627, /* 30277632 */ 30408701, /* 30408704 */ 30539749, /* 30539776 */ 30670847, /* 30670848 */ 30801917, /* 30801920 */ 30932987, /* 30932992 */ 31064063, /* 31064064 */ 31195117, /* 31195136 */ 31326181, /* 31326208 */ 31457269, /* 31457280 */ 31588351, /* 31588352 */ 31719409, /* 31719424 */ 31850491, /* 31850496 */ 31981567, /* 31981568 */ 32112607, /* 32112640 */ 32243707, /* 32243712 */ 32374781, /* 32374784 */ 32505829, /* 32505856 */ 32636921, /* 32636928 */ 32767997, /* 32768000 */ 32899037, /* 32899072 */ 33030121, /* 33030144 */ 33161201, /* 33161216 */ 33292283, /* 33292288 */ 33423319, /* 33423360 */ 33554393, /* 33554432 */ 33685493, /* 33685504 */ 33816571, /* 33816576 */ 33947621, /* 33947648 */ 34078699, /* 34078720 */ 34209787, /* 34209792 */ 34340861, /* 34340864 */ 34471933, /* 34471936 */ 34602991, /* 34603008 */ 34734079, /* 34734080 */ 34865141, /* 34865152 */ 34996223, /* 34996224 */ 35127263, /* 35127296 */ 35258347, /* 35258368 */ 35389423, /* 35389440 */ 35520467, /* 35520512 */ 35651579, /* 35651584 */ 35782613, /* 35782656 */ 35913727, /* 35913728 */ 36044797, /* 36044800 */ 36175871, /* 36175872 */ 36306937, /* 36306944 */ 36438013, /* 36438016 */ 36569083, /* 36569088 */ 36700159, /* 36700160 */ 36831227, /* 36831232 */ 36962291, /* 36962304 */ 37093373, /* 37093376 */ 37224437, /* 37224448 */ 37355503, /* 37355520 */ 37486591, /* 37486592 */ 37617653, /* 37617664 */ 37748717, /* 37748736 */ 37879783, /* 37879808 */ 38010871, /* 38010880 */ 38141951, /* 38141952 */ 38273023, /* 38273024 */ 38404081, /* 38404096 */ 38535151, /* 38535168 */ 38666219, /* 38666240 */ 38797303, /* 38797312 */ 38928371, /* 38928384 */ 39059431, /* 39059456 */ 39190519, /* 39190528 */ 39321599, /* 39321600 */ 39452671, /* 39452672 */ 39583727, /* 39583744 */ 39714799, /* 39714816 */ 39845887, /* 39845888 */ 39976939, /* 39976960 */ 40108027, /* 40108032 */ 40239103, /* 40239104 */ 40370173, /* 40370176 */ 40501231, /* 40501248 */ 40632313, /* 40632320 */ 40763369, /* 40763392 */ 40894457, /* 40894464 */ 41025499, /* 41025536 */ 41156569, /* 41156608 */ 41287651, /* 41287680 */ 41418739, /* 41418752 */ 41549803, /* 41549824 */ 41680871, /* 41680896 */ 41811949, /* 41811968 */ 41943023, /* 41943040 */ 42074101, /* 42074112 */ 42205183, /* 42205184 */ 42336253, /* 42336256 */ 42467317, /* 42467328 */ 42598397, /* 42598400 */ 42729437, /* 42729472 */ 42860537, /* 42860544 */ 42991609, /* 42991616 */ 43122683, /* 43122688 */ 43253759, /* 43253760 */ 43384813, /* 43384832 */ 43515881, /* 43515904 */ 43646963, /* 43646976 */ 43778011, /* 43778048 */ 43909111, /* 43909120 */ 44040187, /* 44040192 */ 44171261, /* 44171264 */ 44302303, /* 44302336 */ 44433391, /* 44433408 */ 44564461, /* 44564480 */ 44695549, /* 44695552 */ 44826611, /* 44826624 */ 44957687, /* 44957696 */ 45088739, /* 45088768 */ 45219827, /* 45219840 */ 45350869, /* 45350912 */ 45481973, /* 45481984 */ 45613039, /* 45613056 */ 45744121, /* 45744128 */ 45875191, /* 45875200 */ 46006249, /* 46006272 */ 46137319, /* 46137344 */ 46268381, /* 46268416 */ 46399471, /* 46399488 */ 46530557, /* 46530560 */ 46661627, /* 46661632 */ 46792699, /* 46792704 */ 46923761, /* 46923776 */ 47054809, /* 47054848 */ 47185907, /* 47185920 */ 47316991, /* 47316992 */ 47448061, /* 47448064 */ 47579131, /* 47579136 */ 47710207, /* 47710208 */ 47841257, /* 47841280 */ 47972341, /* 47972352 */ 48103417, /* 48103424 */ 48234451, /* 48234496 */ 48365563, /* 48365568 */ 48496639, /* 48496640 */ 48627697, /* 48627712 */ 48758783, /* 48758784 */ 48889837, /* 48889856 */ 49020913, /* 49020928 */ 49151987, /* 49152000 */ 49283063, /* 49283072 */ 49414111, /* 49414144 */ 49545193, /* 49545216 */ 49676267, /* 49676288 */ 49807327, /* 49807360 */ 49938431, /* 49938432 */ 50069497, /* 50069504 */ 50200573, /* 50200576 */ 50331599, /* 50331648 */ 50462683, /* 50462720 */ 50593783, /* 50593792 */ 50724859, /* 50724864 */ 50855899, /* 50855936 */ 50987003, /* 50987008 */ 51118069, /* 51118080 */ 51249131, /* 51249152 */ 51380179, /* 51380224 */ 51511277, /* 51511296 */ 51642341, /* 51642368 */ 51773431, /* 51773440 */ 51904511, /* 51904512 */ 52035569, /* 52035584 */ 52166641, /* 52166656 */ 52297717, /* 52297728 */ 52428767, /* 52428800 */ 52559867, /* 52559872 */ 52690919, /* 52690944 */ 52821983, /* 52822016 */ 52953077, /* 52953088 */ 53084147, /* 53084160 */ 53215229, /* 53215232 */ 53346301, /* 53346304 */ 53477357, /* 53477376 */ 53608441, /* 53608448 */ 53739493, /* 53739520 */ 53870573, /* 53870592 */ 54001663, /* 54001664 */ 54132721, /* 54132736 */ 54263789, /* 54263808 */ 54394877, /* 54394880 */ 54525917, /* 54525952 */ 54656983, /* 54657024 */ 54788089, /* 54788096 */ 54919159, /* 54919168 */ 55050217, /* 55050240 */ 55181311, /* 55181312 */ 55312351, /* 55312384 */ 55443433, /* 55443456 */ 55574507, /* 55574528 */ 55705589, /* 55705600 */ 55836659, /* 55836672 */ 55967701, /* 55967744 */ 56098813, /* 56098816 */ 56229881, /* 56229888 */ 56360911, /* 56360960 */ 56491993, /* 56492032 */ 56623093, /* 56623104 */ 56754167, /* 56754176 */ 56885219, /* 56885248 */ 57016319, /* 57016320 */ 57147379, /* 57147392 */ 57278461, /* 57278464 */ 57409529, /* 57409536 */ 57540599, /* 57540608 */ 57671671, /* 57671680 */ 57802739, /* 57802752 */ 57933817, /* 57933824 */ 58064861, /* 58064896 */ 58195939, /* 58195968 */ 58327039, /* 58327040 */ 58458091, /* 58458112 */ 58589161, /* 58589184 */ 58720253, /* 58720256 */ 58851307, /* 58851328 */ 58982389, /* 58982400 */ 59113469, /* 59113472 */ 59244539, /* 59244544 */ 59375587, /* 59375616 */ 59506679, /* 59506688 */ 59637733, /* 59637760 */ 59768831, /* 59768832 */ 59899901, /* 59899904 */ 60030953, /* 60030976 */ 60162029, /* 60162048 */ 60293119, /* 60293120 */ 60424183, /* 60424192 */ 60555227, /* 60555264 */ 60686321, /* 60686336 */ 60817397, /* 60817408 */ 60948479, /* 60948480 */ 61079531, /* 61079552 */ 61210603, /* 61210624 */ 61341659, /* 61341696 */ 61472753, /* 61472768 */ 61603811, /* 61603840 */ 61734899, /* 61734912 */ 61865971, /* 61865984 */ 61997053, /* 61997056 */ 62128127, /* 62128128 */ 62259193, /* 62259200 */ 62390261, /* 62390272 */ 62521331, /* 62521344 */ 62652407, /* 62652416 */ 62783477, /* 62783488 */ 62914549, /* 62914560 */ 63045613, /* 63045632 */ 63176693, /* 63176704 */ 63307763, /* 63307776 */ 63438839, /* 63438848 */ 63569917, /* 63569920 */ 63700991, /* 63700992 */ 63832057, /* 63832064 */ 63963131, /* 63963136 */ 64094207, /* 64094208 */ 64225267, /* 64225280 */ 64356349, /* 64356352 */ 64487417, /* 64487424 */ 64618493, /* 64618496 */ 64749563, /* 64749568 */ 64880587, /* 64880640 */ 65011703, /* 65011712 */ 65142769, /* 65142784 */ 65273851, /* 65273856 */ 65404909, /* 65404928 */ 65535989, /* 65536000 */ 65667067, /* 65667072 */ 65798137, /* 65798144 */ 65929211, /* 65929216 */ 66060277, /* 66060288 */ 66191351, /* 66191360 */ 66322427, /* 66322432 */ 66453479, /* 66453504 */ 66584561, /* 66584576 */ 66715643, /* 66715648 */ 66846709, /* 66846720 */ 66977767, /* 66977792 */ 67108859, /* 67108864 */ 67239883, /* 67239936 */ 67370999, /* 67371008 */ 67502063, /* 67502080 */ 67633127, /* 67633152 */ 67764223, /* 67764224 */ 67895251, /* 67895296 */ 68026363, /* 68026368 */ 68157433, /* 68157440 */ 68288503, /* 68288512 */ 68419567, /* 68419584 */ 68550631, /* 68550656 */ 68681719, /* 68681728 */ 68812769, /* 68812800 */ 68943851, /* 68943872 */ 69074933, /* 69074944 */ 69205987, /* 69206016 */ 69337087, /* 69337088 */ 69468151, /* 69468160 */ 69599221, /* 69599232 */ 69730303, /* 69730304 */ 69861331, /* 69861376 */ 69992443, /* 69992448 */ 70123513, /* 70123520 */ 70254563, /* 70254592 */ 70385641, /* 70385664 */ 70516729, /* 70516736 */ 70647793, /* 70647808 */ 70778861, /* 70778880 */ 70909933, /* 70909952 */ 71041021, /* 71041024 */ 71172091, /* 71172096 */ 71303153, /* 71303168 */ 71434229, /* 71434240 */ 71565283, /* 71565312 */ 71696363, /* 71696384 */ 71827423, /* 71827456 */ 71958521, /* 71958528 */ 72089573, /* 72089600 */ 72220663, /* 72220672 */ 72351733, /* 72351744 */ 72482807, /* 72482816 */ 72613861, /* 72613888 */ 72744937, /* 72744960 */ 72876031, /* 72876032 */ 73007089, /* 73007104 */ 73138171, /* 73138176 */ 73269247, /* 73269248 */ 73400311, /* 73400320 */ 73531379, /* 73531392 */ 73662461, /* 73662464 */ 73793521, /* 73793536 */ 73924583, /* 73924608 */ 74055637, /* 74055680 */ 74186747, /* 74186752 */ 74317801, /* 74317824 */ 74448877, /* 74448896 */ 74579951, /* 74579968 */ 74711027, /* 74711040 */ 74842099, /* 74842112 */ 74973181, /* 74973184 */ 75104243, /* 75104256 */ 75235327, /* 75235328 */ 75366397, /* 75366400 */ 75497467, /* 75497472 */ 75628513, /* 75628544 */ 75759613, /* 75759616 */ 75890653, /* 75890688 */ 76021661, /* 76021760 */ 76152821, /* 76152832 */ 76283897, /* 76283904 */ 76414973, /* 76414976 */ 76546039, /* 76546048 */ 76677113, /* 76677120 */ 76808119, /* 76808192 */ 76939253, /* 76939264 */ 77070317, /* 77070336 */ 77201347, /* 77201408 */ 77332471, /* 77332480 */ 77463541, /* 77463552 */ 77594599, /* 77594624 */ 77725691, /* 77725696 */ 77856767, /* 77856768 */ 77987821, /* 77987840 */ 78118903, /* 78118912 */ 78249973, /* 78249984 */ 78381047, /* 78381056 */ 78512101, /* 78512128 */ 78643199, /* 78643200 */ 78774259, /* 78774272 */ 78905303, /* 78905344 */ 79036411, /* 79036416 */ 79167479, /* 79167488 */ 79298543, /* 79298560 */ 79429619, /* 79429632 */ 79560673, /* 79560704 */ 79691761, /* 79691776 */ 79822829, /* 79822848 */ 79953901, /* 79953920 */ 80084969, /* 80084992 */ 80216063, /* 80216064 */ 80347103, /* 80347136 */ 80478199, /* 80478208 */ 80609279, /* 80609280 */ 80740339, /* 80740352 */ 80871419, /* 80871424 */ 81002489, /* 81002496 */ 81133567, /* 81133568 */ 81264587, /* 81264640 */ 81395683, /* 81395712 */ 81526763, /* 81526784 */ 81657841, /* 81657856 */ 81788923, /* 81788928 */ 81919993, /* 81920000 */ 82051043, /* 82051072 */ 82182137, /* 82182144 */ 82313213, /* 82313216 */ 82444279, /* 82444288 */ 82575331, /* 82575360 */ 82706431, /* 82706432 */ 82837501, /* 82837504 */ 82968563, /* 82968576 */ 83099641, /* 83099648 */ 83230717, /* 83230720 */ 83361781, /* 83361792 */ 83492863, /* 83492864 */ 83623931, /* 83623936 */ 83754997, /* 83755008 */ 83886053, /* 83886080 */ 84017117, /* 84017152 */ 84148213, /* 84148224 */ 84279277, /* 84279296 */ 84410353, /* 84410368 */ 84541421, /* 84541440 */ 84672487, /* 84672512 */ 84803581, /* 84803584 */ 84934621, /* 84934656 */ 85065719, /* 85065728 */ 85196789, /* 85196800 */ 85327849, /* 85327872 */ 85458929, /* 85458944 */ 85589989, /* 85590016 */ 85721081, /* 85721088 */ 85852147, /* 85852160 */ 85983217, /* 85983232 */ 86114279, /* 86114304 */ 86245343, /* 86245376 */ 86376443, /* 86376448 */ 86507507, /* 86507520 */ 86638577, /* 86638592 */ 86769647, /* 86769664 */ 86900731, /* 86900736 */ 87031759, /* 87031808 */ 87162857, /* 87162880 */ 87293939, /* 87293952 */ 87425021, /* 87425024 */ 87556087, /* 87556096 */ 87687167, /* 87687168 */ 87818239, /* 87818240 */ 87949307, /* 87949312 */ 88080359, /* 88080384 */ 88211449, /* 88211456 */ 88342519, /* 88342528 */ 88473569, /* 88473600 */ 88604653, /* 88604672 */ 88735721, /* 88735744 */ 88866797, /* 88866816 */ 88997827, /* 88997888 */ 89128939, /* 89128960 */ 89260027, /* 89260032 */ 89391103, /* 89391104 */ 89522171, /* 89522176 */ 89653217, /* 89653248 */ 89784313, /* 89784320 */ 89915383, /* 89915392 */ 90046441, /* 90046464 */ 90177533, /* 90177536 */ 90308599, /* 90308608 */ 90439667, /* 90439680 */ 90570751, /* 90570752 */ 90701797, /* 90701824 */ 90832871, /* 90832896 */ 90963967, /* 90963968 */ 91095013, /* 91095040 */ 91226101, /* 91226112 */ 91357177, /* 91357184 */ 91488251, /* 91488256 */ 91619321, /* 91619328 */ 91750391, /* 91750400 */ 91881443, /* 91881472 */ 92012537, /* 92012544 */ 92143609, /* 92143616 */ 92274671, /* 92274688 */ 92405723, /* 92405760 */ 92536823, /* 92536832 */ 92667863, /* 92667904 */ 92798969, /* 92798976 */ 92930039, /* 92930048 */ 93061117, /* 93061120 */ 93192191, /* 93192192 */ 93323249, /* 93323264 */ 93454307, /* 93454336 */ 93585379, /* 93585408 */ 93716471, /* 93716480 */ 93847549, /* 93847552 */ 93978559, /* 93978624 */ 94109681, /* 94109696 */ 94240733, /* 94240768 */ 94371833, /* 94371840 */ 94502899, /* 94502912 */ 94633963, /* 94633984 */ 94765039, /* 94765056 */ 94896119, /* 94896128 */ 95027197, /* 95027200 */ 95158249, /* 95158272 */ 95289329, /* 95289344 */ 95420401, /* 95420416 */ 95551487, /* 95551488 */ 95682541, /* 95682560 */ 95813621, /* 95813632 */ 95944691, /* 95944704 */ 96075739, /* 96075776 */ 96206839, /* 96206848 */ 96337919, /* 96337920 */ 96468979, /* 96468992 */ 96600041, /* 96600064 */ 96731101, /* 96731136 */ 96862169, /* 96862208 */ 96993269, /* 96993280 */ 97124347, /* 97124352 */ 97255409, /* 97255424 */ 97386467, /* 97386496 */ 97517543, /* 97517568 */ 97648637, /* 97648640 */ 97779701, /* 97779712 */ 97910759, /* 97910784 */ 98041831, /* 98041856 */ 98172887, /* 98172928 */ 98303999, /* 98304000 */ 98435063, /* 98435072 */ 98566121, /* 98566144 */ 98697187, /* 98697216 */ 98828281, /* 98828288 */ 98959337, /* 98959360 */ 99090427, /* 99090432 */ 99221489, /* 99221504 */ 99352567, /* 99352576 */ 99483647, /* 99483648 */ 99614689, /* 99614720 */ 99745787, /* 99745792 */ 99876851, /* 99876864 */ 100007927, /* 100007936 */ 100138979, /* 100139008 */ 100270069, /* 100270080 */ 100401139, /* 100401152 */ 100532207, /* 100532224 */ 100663291, /* 100663296 */ 100794319, /* 100794368 */ 100925431, /* 100925440 */ 101056507, /* 101056512 */ 101187577, /* 101187584 */ 101318647, /* 101318656 */ 101449717, /* 101449728 */ 101580793, /* 101580800 */ 101711839, /* 101711872 */ 101842931, /* 101842944 */ 101974009, /* 101974016 */ 102105049, /* 102105088 */ 102236149, /* 102236160 */ 102367189, /* 102367232 */ 102498301, /* 102498304 */ 102629369, /* 102629376 */ 102760387, /* 102760448 */ 102891499, /* 102891520 */ 103022537, /* 103022592 */ 103153649, /* 103153664 */ 103284733, /* 103284736 */ 103415791, /* 103415808 */ 103546879, /* 103546880 */ 103677949, /* 103677952 */ 103809011, /* 103809024 */ 103940093, /* 103940096 */ 104071157, /* 104071168 */ 104202233, /* 104202240 */ 104333311, /* 104333312 */ 104464369, /* 104464384 */ 104595397, /* 104595456 */ 104726527, /* 104726528 */ 104857589, /* 104857600 */ 104988641, /* 104988672 */ 105119741, /* 105119744 */ 105250811, /* 105250816 */ 105381841, /* 105381888 */ 105512951, /* 105512960 */ 105644029, /* 105644032 */ 105775079, /* 105775104 */ 105906167, /* 105906176 */ 106037237, /* 106037248 */ 106168319, /* 106168320 */ 106299379, /* 106299392 */ 106430449, /* 106430464 */ 106561523, /* 106561536 */ 106692601, /* 106692608 */ 106823677, /* 106823680 */ 106954747, /* 106954752 */ 107085799, /* 107085824 */ 107216891, /* 107216896 */ 107347943, /* 107347968 */ 107479033, /* 107479040 */ 107610079, /* 107610112 */ 107741167, /* 107741184 */ 107872249, /* 107872256 */ 108003323, /* 108003328 */ 108134393, /* 108134400 */ 108265459, /* 108265472 */ 108396521, /* 108396544 */ 108527603, /* 108527616 */ 108658681, /* 108658688 */ 108789727, /* 108789760 */ 108920831, /* 108920832 */ 109051903, /* 109051904 */ 109182947, /* 109182976 */ 109314043, /* 109314048 */ 109445107, /* 109445120 */ 109576189, /* 109576192 */ 109707253, /* 109707264 */ 109838293, /* 109838336 */ 109969403, /* 109969408 */ 110100409, /* 110100480 */ 110231531, /* 110231552 */ 110362559, /* 110362624 */ 110493661, /* 110493696 */ 110624753, /* 110624768 */ 110755793, /* 110755840 */ 110886883, /* 110886912 */ 111017983, /* 111017984 */ 111148963, /* 111149056 */ 111280121, /* 111280128 */ 111411173, /* 111411200 */ 111542261, /* 111542272 */ 111673343, /* 111673344 */ 111804389, /* 111804416 */ 111935459, /* 111935488 */ 112066553, /* 112066560 */ 112197629, /* 112197632 */ 112328683, /* 112328704 */ 112459751, /* 112459776 */ 112590839, /* 112590848 */ 112721893, /* 112721920 */ 112852981, /* 112852992 */ 112984061, /* 112984064 */ 113115133, /* 113115136 */ 113246183, /* 113246208 */ 113377279, /* 113377280 */ 113508319, /* 113508352 */ 113639419, /* 113639424 */ 113770457, /* 113770496 */ 113901553, /* 113901568 */ 114032599, /* 114032640 */ 114163703, /* 114163712 */ 114294721, /* 114294784 */ 114425807, /* 114425856 */ 114556913, /* 114556928 */ 114687977, /* 114688000 */ 114819031, /* 114819072 */ 114950131, /* 114950144 */ 115081189, /* 115081216 */ 115212287, /* 115212288 */ 115343341, /* 115343360 */ 115474417, /* 115474432 */ 115605467, /* 115605504 */ 115736539, /* 115736576 */ 115867627, /* 115867648 */ 115998719, /* 115998720 */ 116129789, /* 116129792 */ 116260849, /* 116260864 */ 116391917, /* 116391936 */ 116523007, /* 116523008 */ 116654077, /* 116654080 */ 116785133, /* 116785152 */ 116916223, /* 116916224 */ 117047291, /* 117047296 */ 117178367, /* 117178368 */ 117309421, /* 117309440 */ 117440509, /* 117440512 */ 117571523, /* 117571584 */ 117702649, /* 117702656 */ 117833711, /* 117833728 */ 117964793, /* 117964800 */ 118095853, /* 118095872 */ 118226893, /* 118226944 */ 118358003, /* 118358016 */ 118489081, /* 118489088 */ 118620143, /* 118620160 */ 118751207, /* 118751232 */ 118882279, /* 118882304 */ 119013347, /* 119013376 */ 119144447, /* 119144448 */ 119275511, /* 119275520 */ 119406587, /* 119406592 */ 119537653, /* 119537664 */ 119668723, /* 119668736 */ 119799803, /* 119799808 */ 119930873, /* 119930880 */ 120061951, /* 120061952 */ 120193019, /* 120193024 */ 120324077, /* 120324096 */ 120455147, /* 120455168 */ 120586231, /* 120586240 */ 120717307, /* 120717312 */ 120848353, /* 120848384 */ 120979447, /* 120979456 */ 121110523, /* 121110528 */ 121241597, /* 121241600 */ 121372649, /* 121372672 */ 121503737, /* 121503744 */ 121634801, /* 121634816 */ 121765871, /* 121765888 */ 121896949, /* 121896960 */ 122028019, /* 122028032 */ 122159101, /* 122159104 */ 122290171, /* 122290176 */ 122421241, /* 122421248 */ 122552317, /* 122552320 */ 122683391, /* 122683392 */ 122814463, /* 122814464 */ 122945527, /* 122945536 */ 123076601, /* 123076608 */ 123207677, /* 123207680 */ 123338737, /* 123338752 */ 123469783, /* 123469824 */ 123600857, /* 123600896 */ 123731963, /* 123731968 */ 123863023, /* 123863040 */ 123994099, /* 123994112 */ 124125161, /* 124125184 */ 124256243, /* 124256256 */ 124387321, /* 124387328 */ 124518397, /* 124518400 */ 124649449, /* 124649472 */ 124780531, /* 124780544 */ 124911601, /* 124911616 */ 125042663, /* 125042688 */ 125173759, /* 125173760 */ 125304787, /* 125304832 */ 125435897, /* 125435904 */ 125566963, /* 125566976 */ 125698021, /* 125698048 */ 125829103, /* 125829120 */ 125960189, /* 125960192 */ 126091241, /* 126091264 */ 126222293, /* 126222336 */ 126353407, /* 126353408 */ 126484469, /* 126484480 */ 126615551, /* 126615552 */ 126746623, /* 126746624 */ 126877693, /* 126877696 */ 127008733, /* 127008768 */ 127139833, /* 127139840 */ 127270849, /* 127270912 */ 127401947, /* 127401984 */ 127533047, /* 127533056 */ 127664113, /* 127664128 */ 127795181, /* 127795200 */ 127926263, /* 127926272 */ 128057327, /* 128057344 */ 128188409, /* 128188416 */ 128319469, /* 128319488 */ 128450533, /* 128450560 */ 128581631, /* 128581632 */ 128712691, /* 128712704 */ 128843761, /* 128843776 */ 128974841, /* 128974848 */ 129105901, /* 129105920 */ 129236959, /* 129236992 */ 129368051, /* 129368064 */ 129499129, /* 129499136 */ 129630199, /* 129630208 */ 129761273, /* 129761280 */ 129892333, /* 129892352 */ 130023407, /* 130023424 */ 130154483, /* 130154496 */ 130285567, /* 130285568 */ 130416631, /* 130416640 */ 130547621, /* 130547712 */ 130678781, /* 130678784 */ 130809853, /* 130809856 */ 130940911, /* 130940928 */ 131071987, /* 131072000 */ 131203069, /* 131203072 */ 131334131, /* 131334144 */ 131465177, /* 131465216 */ 131596279, /* 131596288 */ 131727359, /* 131727360 */ 131858413, /* 131858432 */ 131989477, /* 131989504 */ 132120557, /* 132120576 */ 132251621, /* 132251648 */ 132382717, /* 132382720 */ 132513781, /* 132513792 */ 132644851, /* 132644864 */ 132775931, /* 132775936 */ 132907007, /* 132907008 */ 133038053, /* 133038080 */ 133169137, /* 133169152 */ 133300207, /* 133300224 */ 133431293, /* 133431296 */ 133562329, /* 133562368 */ 133693433, /* 133693440 */ 133824503, /* 133824512 */ 133955581, /* 133955584 */ 134086639, /* 134086656 */ }; int sf_nearest_prime( int n ) { if( n < 0 ) n = -n; if( n < 8192 ) { return prime_table0[(n>>3)& 1023]; } else if( n < 64*1024 ) { return prime_table1[(n>>6)&1023]; } else if( n < 1024*1024 ) { return prime_table2[(n>>10)&1023]; } else if( n < 128*1024*1024 ) { return prime_table3[(n>>17)&1023]; } else if( n < 1024*1024*1024 ) { return prime_table3[(n>>20)&1023]; } return 134086639; /* too big for table, just use a big prime */ } snort-2.9.20/src/sfutil/sf_sechash.c0000644000175000017500000000466314241077247015461 0ustar apoapo /* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /*************************************************************************** * * File: sf_sechash.C * * Purpose: Provide a set of secure hash utilities * * History: * * Date: Author: Notes: * ---------- ------- ---------------------------------------------- * 12/05/13 ECB Initial coding begun * **************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "sf_types.h" #include "sf_sechash.h" #include "snort_debug.h" static struct { Secure_Hash_Type type; char *name; unsigned int length; } Secure_Hash_Map[] = { { SECHASH_SHA512, "SHA512", 64 }, { SECHASH_SHA256, "SHA256", 32 }, { SECHASH_MD5, "MD5", 16 }, { SECHASH_NONE, "", 0 } }; unsigned int SecHash_Type2Length( const Secure_Hash_Type type ) { unsigned int index; index = 0; while(Secure_Hash_Map[index].type != SECHASH_NONE) { if( Secure_Hash_Map[index].type == type ) { return( Secure_Hash_Map[index].length ); } index += 1; } return( 0 ); } Secure_Hash_Type SecHash_Name2Type( const char *name ) { unsigned int index; index = 0; while(Secure_Hash_Map[index].type != SECHASH_NONE) { if( strcasecmp(name,Secure_Hash_Map[index].name) == 0 ) { return( Secure_Hash_Map[index].type ); } index += 1; } return( SECHASH_NONE ); } snort-2.9.20/src/sfutil/sfrt.h0000644000175000017500000002360714241077321014326 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * @file sfrt.h * @author Adam Keeton * @date Thu July 20 10:16:26 EDT 2006 * * SFRT implements two different routing table lookup methods that have been * adapted to return a void pointers. Any generic information may be * associated with a given IP or CIDR block. * * As of this writing, the two methods used are Stefan Nilsson and Gunnar * Karlsson's LC-trie, and a multibit-trie method similar to Gupta et-al.'s * DIR-n-m. Presently, the LC-trie is used for testing purposes as the * current implementation does not allow for fast, dynamic inserts. * * The intended use is to associate large IP blocks with specific information; * such as what may be written into the table by RNA. * * NOTE: information should only move from less specific to more specific, ie: * * First insert: 1.1.0.0/16 -> some data * Second insert: 1.1.2.3 -> some other data * * As opposed to: * * First insert: 1.1.2.3 -> some other data * Second insert: 1.1.0.0/16 -> some data * * If more general information is to overwrite existing entries, the table * should be free'ed and rebuilt. This is due to the difficulty of cleaning * out stale entries with the current implementation. At runtime, this won't * be a significant issue since inserts should apply to specific IP addresses * and not entire blocks of IPs. * * * Implementation: * * The routing tables associate an index into a "data" table with each CIDR. * Each entry in the data table stores a pointer to actual data. This * implementation was chosen so each routing entry only needs one word to * either index the data array, or point to another table. * * Inserts are performed by specifying a CIDR and a pointer to its associated * data. Since a new routing table entry may overwrite previous entries, * a flag selects whether the insert favors the most recent or favors the most * specific. Favoring most specific should be the default behvior. If * the user wishes to overwrite routing entries with more general data, the * table should be flushed, rather than using favor-most-recent. * * Before modifying the routing or data tables, the insert function performs a * lookup on the CIDR-to-be-insertted. If no entry or an entry *of differing * bit length* is found, the data is insertted into the data table, and its * index is used for the new routing table entry. If an entry is found that * is as specific as the new CIDR, the index stored points to where the new * data is written into the data table. * * If more specific CIDR blocks overwrote the data table, then the more * general routing table entries that were not overwritten will be referencing * the wrong data. Alternatively, less specific entries can only overwrite * existing routing table entries if favor-most-recent inserts are used. * * Because there is no quick way to clean the data-table if a user wishes to * use a favor-most-recent insert for more general data, the user should flush * the table with sfrt_free and create one anew. Alternatively, a small * memory leak occurs with the data table, as it will be storing pointers that * no routing table entry cares about. * * * The API calls that should be used are: * sfrt_new - create new table * sfrt_insert - insert entry * sfrt_lookup - lookup entry * sfrt_free - free table */ #ifndef _SFRT_H_ #define _SFRT_H_ #include #include #include "sfrt_trie.h" #include "snort_debug.h" #include "ipv6_port.h" typedef sfcidr_t *IP; typedef void* GENERIC; /* To be replaced with a pointer to a policy */ typedef struct { word index; word length; } tuple_t; #include "sfrt_dir.h" /*#define SUPPORT_LCTRIE */ #ifdef SUPPORT_LCTRIE #include "sfrt_lctrie.h" #endif enum types { #ifdef SUPPORT_LCTRIE LCT, #endif DIR_24_8, DIR_16x2, DIR_16_8x2, DIR_16_4x4, DIR_8x4, DIR_4x8, DIR_2x16, DIR_16_4x4_16x5_4x4, DIR_16x7_4x4, DIR_16x8, DIR_8x16, IPv4, IPv6 }; enum return_codes { RT_SUCCESS=0, RT_INSERT_FAILURE, RT_POLICY_TABLE_EXCEEDED, DIR_INSERT_FAILURE, DIR_LOOKUP_FAILURE, MEM_ALLOC_FAILURE, #ifdef SUPPORT_LCTRIE LCT_COMPILE_FAILURE, LCT_INSERT_FAILURE, LCT_LOOKUP_FAILURE, #endif RT_REMOVE_FAILURE }; /* Defined in sfrt.c */ extern char *rt_error_messages[]; enum { RT_FAVOR_TIME, RT_FAVOR_SPECIFIC, RT_FAVOR_ALL }; /*******************************************************************/ /* Master table struct. Abstracts DIR and LC-trie methods */ typedef struct { GENERIC *data; /* data table. Each IP points to an entry here */ uint32_t num_ent; /* Number of entries in the policy table */ uint32_t max_size; /* Max size of policies array */ uint32_t lastAllocatedIndex; /* Index allocated last. Search for unused index starts from this value and then wraps around at max_size.*/ char ip_type; /* Only IPs of this family will be used */ char table_type; uint32_t allocated; void *rt; /* Actual "routing" table */ void *rt6; /* Actual "routing" table */ tuple_t (*lookup)(uint32_t* adr, int numAdrDwords, GENERIC tbl); int (*insert)(uint32_t* adr, int numAdrDwords, int len, word index, int behavior, GENERIC tbl); void (*free)(GENERIC tbl); uint32_t (*usage)(GENERIC tbl); void (*print)(GENERIC tbl); word (*remove)(uint32_t* adr, int numAdrDwords, int len, int behavior, GENERIC tbl); } table_t; /*******************************************************************/ /* Abstracted routing table API */ table_t * sfrt_new(char type, char ip_type, long data_size, uint32_t mem_cap); void sfrt_free(table_t *table); GENERIC sfrt_lookup(sfaddr_t* ip, table_t* table); GENERIC sfrt_search(sfaddr_t* ip, table_t *table); typedef void (*sfrt_iterator_callback)(void *); struct _SnortConfig; typedef void (*sfrt_sc_iterator_callback)(struct _SnortConfig *, void *); typedef int (*sfrt_sc_iterator_callback3)(struct _SnortConfig *, void *); typedef void (*sfrt_iterator_callback2)(void *, void *); typedef int (*sfrt_iterator_callback3)(void *); void sfrt_iterate(table_t* table, sfrt_iterator_callback userfunc); void sfrt_iterate_with_snort_config(struct _SnortConfig *sc, table_t* table, sfrt_sc_iterator_callback userfunc); int sfrt_iterate2(table_t* table, sfrt_iterator_callback3 userfunc); int sfrt_iterate2_with_snort_config(struct _SnortConfig *sc, table_t* table, sfrt_sc_iterator_callback3 userfunc); void sfrt_cleanup(table_t* table, sfrt_iterator_callback userfunc); void sfrt_cleanup2(table_t*, sfrt_iterator_callback2, void *); int sfrt_insert(sfcidr_t* ip, unsigned char len, GENERIC ptr, int behavior, table_t *table); int sfrt_remove(sfcidr_t* ip, unsigned char len, GENERIC *ptr, int behavior, table_t *table); uint32_t sfrt_usage(table_t *table); void sfrt_print(table_t *table); uint32_t sfrt_num_entries(table_t *table); /* Perform a lookup on value contained in "ip" * For performance reason, we use this simplified version instead of sfrt_lookup * Note: this only applied to table setting: DIR_8x16 (DIR_16_8_4x2 for IPV4), DIR_8x4*/ static inline GENERIC sfrt_dir8x_lookup(sfaddr_t *ip, table_t* table) { dir_sub_table_t *subtable; int i; void *rt = NULL; int index; if (sfaddr_family(ip) == AF_INET) { rt = table->rt; subtable = ((dir_table_t *)rt)->sub_table; /* 16 bits*/ index = ntohs(ip->ia16[6]); if( !subtable->entries[index] || subtable->lengths[index] ) { return table->data[subtable->entries[index]]; } subtable = (dir_sub_table_t *) subtable->entries[index]; /* 8 bits*/ index = ip->ia8[14]; if( !subtable->entries[index] || subtable->lengths[index] ) { return table->data[subtable->entries[index]]; } subtable = (dir_sub_table_t *) subtable->entries[index]; /* 4 bits */ index = ip->ia8[15] >> 4; if( !subtable->entries[index] || subtable->lengths[index] ) { return table->data[subtable->entries[index]]; } subtable = (dir_sub_table_t *) subtable->entries[index]; /* 4 bits */ index = ip->ia8[15] & 0xF; if( !subtable->entries[index] || subtable->lengths[index] ) { return table->data[subtable->entries[index]]; } } else { rt = table->rt6; subtable = ((dir_table_t *)rt)->sub_table; for (i = 0; i < 16; i++) { index = ip->ia8[i]; if( !subtable->entries[index] || subtable->lengths[index] ) { return table->data[subtable->entries[index]]; } subtable = (dir_sub_table_t *) subtable->entries[index]; } } return NULL; } #endif snort-2.9.20/src/sfutil/sfghash.c0000644000175000017500000004233014241077264014766 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * * sfghash.c * * Generic hash table library. * * This hash table maps unique keys to void data pointers. * * Features: * 1) Keys may be ascii strings of variable size, or * fixed length (per table) binary byte sequences. This * allows use as a Mapping for String+Data pairs, or a * generic hashing. * 2) User can allocate keys, or pass copies and we can * allocate space and save keys. * 3) User can pass a free function to free up user data * when the table is deleted. * 4) Table rows sizes can be automatically adjusted to * the nearest prime number size. * * 6/10/03 - man - Upgraded the hash function to a Hardened hash function, * it has no predictable cycles, and each hash table gets a different * randomized hashing function. So even with the source code, you cannot predict * anything with this function. If an attacker can setup a feedback * loop he might gain some knowledge of how to muck with us, but even in that case * his odds are astronomically skinny. This is actually the same problem as solved * early on with hashing functions where degenerate data with close keys could * produce very long bucket chains. * * 8/31/06 - man - Added prime tables to speed up prime number lookup. * * Author: Marc Norton * */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sfghash.h" #include "sfprimetable.h" /* * Private Malloc */ static void * s_alloc( int n ) { return calloc( 1,n ); } /* * Private Free */ static void s_free( void * p ) { if( p )free( p ); } /* * * Create a new hash table * * nrows : number of rows in hash table, primes are best. * > 0 => we use the nearest prime internally * < 0 => we use the magnitude as nrows. * keysize : > 0 => bytes in each key, keys are binary bytes, * all keys are the same size. * ==0 => keys are strings and are null terminated, * allowing random key lengths. * userkeys : > 0 => indicates user owns the key data * and we should not allocate or free space for it, * nor should we attempt to free the user key. We just * save the pointer to the key. * ==0 => we should copy the keys and manage them internally * userfree : routine to free users data, null if we should not * free user data in sfghash_delete(). The routine * should be of the form 'void userfree(void * userdata)', * 'free' works for simple allocations. */ SFGHASH * sfghash_new( int nrows, int keysize, int userkeys, void (*userfree)(void*p) ) { int i; SFGHASH * h; if( nrows > 0 ) /* make sure we have a prime number */ { nrows = sf_nearest_prime( nrows ); } else /* use the magnitude or nrows as is */ { nrows = -nrows; } h = (SFGHASH*)s_alloc( sizeof(SFGHASH) ); if( !h ) return 0; memset( h, 0, sizeof(SFGHASH) ); h->sfhashfcn = sfhashfcn_new( nrows ); if( !h->sfhashfcn ) { free(h); return 0; } h->table = (SFGHASH_NODE**) s_alloc( sizeof(SFGHASH_NODE*) * nrows ); if( !h->table ) { free(h->sfhashfcn); free(h); return 0; } for( i=0; itable[i] = 0; } h->userkey = userkeys; h->keysize = keysize; h->nrows = nrows; h->count = 0; h->userfree = userfree; h->crow = 0; // findfirst/next current row h->cnode = 0; // findfirst/next current node ptr return h; } /* * Set Splay mode : Splays nodes to front of list on each access */ void sfghash_splaymode( SFGHASH * t, int n ) { if ( t ) t->splay = n; } /* * Delete the hash Table * * free key's, free node's, and free the users data, if they * supply a free function */ void sfghash_delete( SFGHASH * h ) { int i; SFGHASH_NODE * node, * onode; if( !h ) return; sfhashfcn_free( h->sfhashfcn ); if( h->table ) { for(i=0;inrows;i++) { for( node=h->table[i]; node; ) { onode = node; node = node->next; if( !h->userkey && onode->key ) s_free( (void *)onode->key ); if( h->userfree && onode->data ) h->userfree( onode->data ); /* free users data, with users function */ s_free( onode ); } } s_free( h->table ); h->table = 0; } s_free( h ); } /* * Get the # of Nodes in HASH the table */ int sfghash_count( SFGHASH * t ) { if( !t ) return 0; return t->count; } /* * Add a key + data pair * --------------------- * * key + data should both be non-zero, although data can be zero * * t - hash table * key - users key data (should be unique in this table) * may be ascii strings or fixed size binary keys * data - users data pointer * * returns SF_HASH_NOMEM: alloc error * SF_HASH_INTABLE : key already in table (t->cnode points to the node) * SF_OK: added a node for this key + data pair * * Notes: * If the key node already exists, then t->cnode points to it on return, * this allows you to do something with the node - like add the data to a * linked list of data items held by the node, or track a counter, or whatever. * */ int sfghash_add( SFGHASH * t, const void * const key, void * const data ) { unsigned hashkey; int klen; int index; SFGHASH_NODE *hnode; if (t == NULL) return SFGHASH_ERR; /* * Get proper Key Size */ if( t->keysize > 0 ) { klen = t->keysize; } else { /* need the null byte for strcmp() in sfghash_find() */ klen = strlen( (char*)key ) + 1; } hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen ); index = hashkey % t->nrows; /* * Uniqueness: * Check 1st to see if the key is already in the table * Just bail if it is. */ for( hnode=t->table[index]; hnode; hnode=hnode->next ) { if( t->keysize > 0 ) { if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,klen) ) { t->cnode = hnode; /* save pointer to the node */ return SFGHASH_INTABLE; /* found it */ } } else { if( !strcmp((const char *)hnode->key,(const char*)key) ) { t->cnode = hnode; /* save pointer to the node */ return SFGHASH_INTABLE; /* found it */ } } } /* * Create new node */ hnode = (SFGHASH_NODE*)s_alloc(sizeof(SFGHASH_NODE)); if( !hnode ) return SFGHASH_NOMEM; /* Add the Key */ if( t->userkey ) { /* Use the Users key */ hnode->key = key; } else { /* Create new key */ hnode->key = s_alloc( klen ); if( !hnode->key ) { free(hnode); return SFGHASH_NOMEM; } /* Copy key */ memcpy((void*)hnode->key,key,klen); } /* Add The Node */ if( t->table[index] ) /* add the node to the existing list */ { hnode->prev = 0; // insert node as head node hnode->next=t->table[index]; hnode->data=data; t->table[index]->prev = hnode; t->table[index] = hnode; } else /* 1st node in this list */ { hnode->prev=0; hnode->next=0; hnode->data=data; t->table[index] = hnode; } t->count++; return SFGHASH_OK; } /* * move a node to the front of the list */ static void movetofront( SFGHASH *t , int index, SFGHASH_NODE * n ) { if( t->table[index] != n ) // if not at front of list already... { /* Unlink the node */ if( n->prev ) n->prev->next = n->next; if( n->next ) n->next->prev = n->prev; /* Link at front of list */ n->prev=0; n->next=t->table[index]; t->table[index]->prev=n; } } /* * Find a Node based on the key, return users data. */ SFGHASH_NODE * sfghash_find_node( SFGHASH * t, const void * const key) { unsigned hashkey; int index, klen; SFGHASH_NODE *hnode; if ( !t ) return NULL; if( t->keysize ) { klen = t->keysize; } else { klen = strlen( (char*) key ) + 1; } hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen ); index = hashkey % t->nrows; for( hnode=t->table[index]; hnode; hnode=hnode->next ) { if( t->keysize == 0 ) { if( !strcmp((char*)hnode->key,(char*)key) ) { if( t->splay > 0 ) movetofront(t,index,hnode); return hnode; } } else { if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,t->keysize) ) { if( t->splay > 0 ) movetofront(t,index,hnode); return hnode; } } } return NULL; } /* * Find a Node based on the key, return users data. */ void * sfghash_find( SFGHASH * t, const void * const key) { SFGHASH_NODE * hnode; if ( !t ) return NULL; hnode = sfghash_find_node( t, key ); if( hnode ) return hnode->data; return NULL; } /* Returns whether or not the there is an entry in the table with key * Sets argument data to data in hash node which could be NULL. * This function is used to both make sure there is an entry in the * table and get potential data associated with entry */ int sfghash_find2(SFGHASH *t, void *key, void **data) { SFGHASH_NODE * hnode; if (t == NULL) return 0; hnode = sfghash_find_node(t, key); if (hnode != NULL) { *data = hnode->data; return 1; } return 0; } /* * Unlink and free the node */ static int sfghash_free_node( SFGHASH * t, unsigned index, SFGHASH_NODE * hnode ) { if( !t->userkey && hnode->key ) s_free( (void *)hnode->key ); hnode->key = 0; if( t->userfree) t->userfree( hnode->data); /* free users data, with users function */ if( hnode->prev ) // not the 1st node { hnode->prev->next = hnode->next; if( hnode->next ) hnode->next->prev = hnode->prev; } else if( t->table[index] ) // 1st node { t->table[index] = t->table[index]->next; if( t->table[index] )t->table[index]->prev = 0; } s_free( hnode ); t->count--; return SFGHASH_OK; } /* * Remove a Key/Data Pair from the table - find it, unlink it, and free the memory for it. * * returns : 0 - OK * -1 - node not found */ int sfghash_remove( SFGHASH * t, const void * const key) { SFGHASH_NODE * hnode; int klen; unsigned hashkey, index; if ( !t ) return 0; if( t->keysize > 0 ) { klen = t->keysize; } else { klen = strlen((char*)key) + 1; } hashkey = t->sfhashfcn->hash_fcn( t->sfhashfcn, (unsigned char*) key, klen ); index = hashkey % t->nrows; for( hnode=t->table[index]; hnode; hnode=hnode->next ) { if( t->keysize > 0 ) { if( !t->sfhashfcn->keycmp_fcn(hnode->key,key,klen) ) { return sfghash_free_node( t, index, hnode ); } } else { if( !strcmp((const char *)hnode->key,(const char*)key) ) { return sfghash_free_node( t, index, hnode ); } } } return SFGHASH_ERR; } /* * Get First Hash Table Node */ SFGHASH_NODE * sfghash_findfirst1( SFGHASH * t ) { if ( !t ) return NULL; /* Start with 1st row */ for( t->crow=0; t->crow < t->nrows; t->crow++ ) { /* Get 1st Non-Null node in row list */ t->cnode = t->table[t->crow]; if( t->cnode ) return t->cnode; } return NULL; } /* * Get Next Hash Table Node */ SFGHASH_NODE * sfghash_findnext1( SFGHASH * t ) { if ( !t ) return NULL; if( t->cnode ) /* get next in this list */ { /* Next node in current node list */ t->cnode = t->cnode->next; if( t->cnode ) { return t->cnode; } } /* Get 1st node in next non-empty row/node list */ for( t->crow++; t->crow < t->nrows; t->crow++ ) { t->cnode = t->table[ t->crow ]; if( t->cnode ) { return t->cnode; } } return NULL; } /* Internal use only */ static void sfghash_next( SFGHASH * t ) { if ( !t ) return; if( !t->cnode ) return ; /* Next node in current node list */ t->cnode = t->cnode->next; if( t->cnode ) { return; } /* Next row */ /* Get 1st node in next non-emtoy row/node list */ for( t->crow++; t->crow < t->nrows; t->crow++ ) { t->cnode = t->table[ t->crow ]; if( t->cnode ) { return; } } } /* * Get First Hash Table Node */ SFGHASH_NODE * sfghash_findfirst( SFGHASH * t ) { SFGHASH_NODE * n; if ( !t ) return NULL; /* Start with 1st row */ for( t->crow=0; t->crow < t->nrows; t->crow++ ) { /* Get 1st Non-Null node in row list */ t->cnode = t->table[ t->crow ]; if( t->cnode ) { n = t->cnode; sfghash_next( t ); // load t->cnode with the next entry return n; } } return NULL; } /* * Get Next Hash Table Node */ SFGHASH_NODE * sfghash_findnext( SFGHASH * t ) { SFGHASH_NODE * n; if ( !t ) return NULL; n = t->cnode; if( !n ) /* Done, no more entries */ { return NULL; } /* Preload next node into current node */ sfghash_next( t ); return n; } /** * Make sfhashfcn use a separate set of operators for the backend. * * @param h sfhashfcn ptr * @param hash_fcn user specified hash function * @param keycmp_fcn user specified key comparisoin function */ int sfghash_set_keyops( SFGHASH *h , unsigned (*hash_fcn)( SFHASHFCN * p, unsigned char *d, int n), int (*keycmp_fcn)( const void *s1, const void *s2, size_t n)) { if(h && hash_fcn && keycmp_fcn) { return sfhashfcn_set_keyops(h->sfhashfcn, hash_fcn, keycmp_fcn); } return -1; } /* * * Test Driver for Hashing * */ #ifdef SFGHASH_MAIN void myfree ( void * p ) { printf("freeing '%s'\n",p); free(p); } /* * Hash test program */ int main ( int argc, char ** argv ) { int i; SFGHASH * t; SFGHASH_NODE * n, *m; char str[256],*p; int num=100; if( argc > 1 ) num = atoi(argv[1]); sfatom_init(); /* Create a Hash Table */ t = sfghash_new( 1000, 0 , GH_COPYKEYS , myfree ); /* Add Nodes to the Hash Table */ for(i=0;ikey, n->data ); // hashing code frees user data using 'myfree' above .... if( sfghash_remove(t,n->key) ) printf("Could not remove the key node\n"); else printf("key node removed\n"); } for( n = sfatom_findfirst(); n; n = sfatom_findnext() ) { printf("atom-findfirst/next: key=%s, data=%s\n", n->key, n->data ); free( n->data ); //since atom data is not freed automatically } /* Free the table and it's user data */ printf("****sfghash_delete\n"); sfghash_delete( t ); printf("****sfatom_reset\n"); sfatom_reset(); printf("\nnormal pgm finish\n\n"); return 0; } #endif snort-2.9.20/src/sfutil/sfPolicyUserData.h0000644000175000017500000001071414241077303016564 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _SF_POLICY_USER_DATA_H_ #define _SF_POLICY_USER_DATA_H_ #include "sf_ip.h" #include "ipv6_port.h" #include "sfPolicy.h" /*SharedObjectAddStarts #include "sf_dynamic_preprocessor.h" SharedObjectAddEnds */ typedef struct { /**policy id of configuration file or packet being processed. */ tSfPolicyId currentPolicyId; /**Number of policies currently allocated. */ unsigned int numAllocatedPolicies; /**Number of policies active. Since we use an array of policy pointers, * number of allocated policies may be more than active policies. */ unsigned int numActivePolicies; /**user configuration for a policy. This is a pointer to an array of pointers * to user configuration. */ void **userConfig; } tSfPolicyUserContext; typedef tSfPolicyUserContext * tSfPolicyUserContextId; //SharedObjectDeleteBegins // index for default nap & ips polices is 0 // so single function to return the default idx static inline tSfPolicyId getDefaultPolicy(void) { return SF_DEFAULT_POLICY_ID; } //SharedObjectDeleteEnds tSfPolicyUserContextId sfPolicyConfigCreate( void ); void sfPolicyConfigDelete( tSfPolicyUserContextId pContext ); //Functions for setting, getting and clearing policy ids static inline void sfPolicyUserPolicySet ( tSfPolicyUserContextId pContext, tSfPolicyId policyId ) { pContext->currentPolicyId = policyId; } static inline tSfPolicyId sfPolicyUserPolicyGet ( tSfPolicyUserContextId pContext ) { return pContext->currentPolicyId; } static inline unsigned int sfPolicyUserPolicyGetActive ( tSfPolicyUserContextId pContext ) { return (pContext->numActivePolicies); } //Functions for setting, getting and clearing user data specific to policies. int sfPolicyUserDataSet ( tSfPolicyUserContextId pContext, tSfPolicyId policyId, void *config ); static inline void * sfPolicyUserDataGet ( tSfPolicyUserContextId pContext, tSfPolicyId policyId ) { if (pContext && policyId < pContext->numAllocatedPolicies) return pContext->userConfig[policyId]; return NULL; } static inline int sfPolicyUserDataSetDefault ( tSfPolicyUserContextId pContext, void *config ) { return sfPolicyUserDataSet (pContext, getDefaultPolicy(), config); } static inline void * sfPolicyUserDataGetDefault ( tSfPolicyUserContextId pContext ) { return sfPolicyUserDataGet (pContext, getDefaultPolicy()); } static inline int sfPolicyUserDataSetCurrent ( tSfPolicyUserContextId pContext, void *config ) { return sfPolicyUserDataSet (pContext, pContext->currentPolicyId, config); } static inline void * sfPolicyUserDataGetCurrent ( tSfPolicyUserContextId pContext ) { return sfPolicyUserDataGet (pContext, pContext->currentPolicyId); } void *sfPolicyUserDataClear( tSfPolicyUserContextId pContext, tSfPolicyId policyId ); int sfPolicyUserDataIterate( struct _SnortConfig *sc, tSfPolicyUserContextId pContext, int ( *callback )( struct _SnortConfig *sc, tSfPolicyUserContextId pContext, tSfPolicyId policyId, void *config ) ); int sfPolicyUserDataFreeIterate( tSfPolicyUserContextId pContext, int ( *callback )( tSfPolicyUserContextId pContext, tSfPolicyId policyId, void *config ) ); #endif snort-2.9.20/src/sfutil/sfrt_dir.c0000644000175000017500000005464414241077322015165 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * @file sfdir.c * @author Adam Keeton * @date Thu July 20 10:16:26 EDT 2006 * * The implementation uses an multibit-trie that is similar to Gupta et-al's * DIR-n-m. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* For variadic */ #include #include /* For memset */ #include "sf_types.h" #include "sfrt.h" #include "sfrt_dir.h" typedef struct { uint32_t* adr; int bits; } IPLOOKUP; /* Create new "sub" table of 2^width entries */ static dir_sub_table_t *_sub_table_new(dir_table_t *root, uint32_t dimension, uint32_t prefill, uint32_t bit_length) { int width = root->dimensions[dimension]; int len = 1 << width; int index; dir_sub_table_t *sub; /* Check if creating this node will exceed the memory cap. * The symbols in the conditional (other than cap), come from the * allocs below. */ if( root->mem_cap < ( root->allocated + sizeof(dir_sub_table_t) + sizeof(word) * len + len ) || bit_length > 128) { return NULL; } /* Set up the initial prefilled "sub table" */ sub = (dir_sub_table_t*)malloc(sizeof(dir_sub_table_t)); if(!sub) { return NULL; } /* This keeps the width readily available rather than recalculating it * from the number of entries during an insert or lookup */ sub->width = width; /* need 2^sub->width entries */ sub->num_entries = len; sub->entries = (word*)malloc(sizeof(word) * sub->num_entries); if(!sub->entries) { free(sub); return NULL; } /* A "length" needs to be stored with each entry above. The length refers * to how specific the insertion that set the entry was. It is necessary * so that the entry is not overwritten by less general routing * information if "RT_FAVOR_SPECIFIC" insertions are being performed. */ sub->lengths = (uint8_t*)malloc(sub->num_entries); if(!sub->lengths) { free(sub->entries); free(sub); return NULL; } /* Can't use memset here since prefill is multibyte */ for(index = 0; index < sub->num_entries; index++) { sub->entries[index] = prefill; sub->lengths[index] = (uint8_t)bit_length; } sub->cur_num = 0; if (prefill) sub->filledEntries = sub->num_entries; else sub->filledEntries = 0; root->allocated += sizeof(dir_sub_table_t) + sizeof(word) * sub->num_entries + sub->num_entries; root->cur_num++; return sub; } /* Create new dir-n-m root table with 'count' depth */ dir_table_t *sfrt_dir_new(uint32_t mem_cap, int count,...) { va_list ap; uint32_t val; int index; dir_table_t* table = (dir_table_t*)malloc(sizeof(dir_table_t)); if(!table) { return NULL; } table->allocated = 0; table->dimensions = (int*)malloc(sizeof(int)*count); if(!table->dimensions) { free(table); return NULL; } table->dim_size = count; va_start(ap, count); for(index=0; index < count; index++) { val = va_arg(ap, int); table->dimensions[index] = val; } va_end(ap); table->mem_cap = mem_cap; table->cur_num = 0; table->sub_table = _sub_table_new(table, 0, 0, 0); if(!table->sub_table) { free(table->dimensions); free(table); return NULL; } table->allocated += sizeof(dir_table_t) + sizeof(int)*count; return table; } /* Traverse "sub" tables, freeing each */ static void _sub_table_free(uint32_t *allocated, dir_sub_table_t *sub) { int index; sub->cur_num--; for(index=0; index < sub->num_entries; index++) { /* The following condition will only be true if * this entry is a pointer */ if( !sub->lengths[index] && sub->entries[index] ) { _sub_table_free( allocated, (dir_sub_table_t*) sub->entries[index]); } } if(sub->entries) { /* This probably does not need to be checked * since if it was not allocated, we would have errored out * in _sub_table_new */ free(sub->entries); *allocated -= sizeof(word) * sub->num_entries; } if(sub->lengths) { /* This probably does not need to be checked * since if it was not allocated, we would have errored out * in _sub_table_new */ free(sub->lengths); *allocated -= sub->num_entries; } free(sub); *allocated -= sizeof(dir_sub_table_t); } /* Free the DIR-n-m structure */ void sfrt_dir_free(void *tbl) { dir_table_t *table = (dir_table_t*)tbl; if(!table) { return; } if(table->sub_table) { _sub_table_free(&table->allocated, table->sub_table); } if(table->dimensions) { free(table->dimensions); } free(table); } static inline void _dir_fill_all(uint32_t *allocated, uint32_t index, uint32_t fill, word length, uint32_t val, dir_sub_table_t *table) { /* Fill entries */ for(; index < fill; index++) { /* Before overwriting this entry, verify there's not an existing * pointer ... otherwise free it to avoid a huge memory leak. */ if(table->entries[index]) { if (!table->lengths[index]) { _sub_table_free(allocated, (dir_sub_table_t*)table->entries[index]); } } else { table->filledEntries++; } table->entries[index] = val; table->lengths[index] = (uint8_t)length; } } static inline void _dir_fill_less_specific(int index, int fill, word length, uint32_t val, dir_sub_table_t *table) { /* Fill entries */ for(; index < fill; index++) { /* If we encounter a pointer, and we're inserting at this level, we * automatically know that this entry refers to more specific * information. However, there might only be one more specific entry * in the entire block, meaning the rest must be filled. * * For instance, imagine a 24-8 with 1.2.3/24 -> A and 1.2.3.4/32 -> B * There will be a pointer at 1.2.3 in the first table. The second * table needs to have 255 entries pointing A, and 1 entry pointing to * B. * * Therefore, recurse to this next level. */ if( !table->lengths[index] && table->entries[index]) { dir_sub_table_t *next = (dir_sub_table_t*)table->entries[index]; _dir_fill_less_specific(0, 1 << next->width, length, val, next); } else if(length >= (word)table->lengths[index]) { if (!table->entries[index]) { table->filledEntries++; } table->entries[index] = val; table->lengths[index] = (char)length; } } } /*Remove entries all this level and discard any more specific entries. * * @note RT_FAVOR_TIME behavior can cause hung or crosslinked entries if part of a subnet * (which was added) are deleted. Same issue is there when a more general subnet overwrites * a specific subnet. table->data[] entry for more specific subnet is not cleared. * * @note RT_FAVOR_TIME can cause orphaned table->data[] entries if the entire subnet * is replaced by more specific sudnets. */ static inline uint32_t _dir_remove_all(uint32_t *allocated, uint32_t index, uint32_t fill, word length, dir_sub_table_t *table) { uint32_t valueIndex = 0; /* Fill entries */ for(; index < fill; index++) { /* Before overwriting this entry, verify there's not an existing * pointer ... otherwise free it to avoid a huge memory leak. */ if (table->entries[index]) { if (!table->lengths[index]) { _sub_table_free(allocated, (dir_sub_table_t*)table->entries[index]); } if(length == (word)table->lengths[index]) { valueIndex = table->entries[index]; } table->filledEntries--; //zero value here works since sfrt uses 0 for failed entries. table->entries[index] = 0; table->lengths[index] = 0; } } return valueIndex; } /**Remove entries which match in address/length in all subtables. * @note RT_FAVOR_SPECIFIC can cause orphaned table->data[] entries if the entire subnet * is replaced by more specific subnets. */ static inline uint32_t _dir_remove_less_specific(uint32_t *allocated, int index, int fill, word length, dir_sub_table_t *table) { uint32_t valueIndexRet = 0; uint32_t valueIndex = 0; for(; index < fill; index++) { if( !table->lengths[index] && table->entries[index]) { dir_sub_table_t *next = (dir_sub_table_t*)table->entries[index]; valueIndex = _dir_remove_less_specific(allocated, 0, 1 << next->width, length, next); if (valueIndex) { valueIndexRet = valueIndex; } if (!next->filledEntries) //table can be collapsed. { _sub_table_free(allocated, next); table->entries[index] = 0; table->lengths[index] = 0; table->filledEntries--; } } else if(length == (word)table->lengths[index]) { if (table->entries[index]) { table->filledEntries--; valueIndexRet = table->entries[index]; } table->entries[index] = 0; table->lengths[index] = 0; } } return valueIndexRet; } /* Sub table insertion * This is called by dir_insert and recursively to find the the sub table * that should house the value "ptr" * @param ip IP address structure * @param cur_len Number of bits of the IP left at this depth * @param length Number of bits of the IP used to specify this CIDR * @param ptr Information to be associated with this IP range * @param master_table The table that describes all, returned by dir_new */ static int _dir_sub_insert(IPLOOKUP *ip, int length, int cur_len, GENERIC ptr, int current_depth, int behavior, dir_sub_table_t *sub_table, dir_table_t *root_table) { word index; uint32_t fill; { uint32_t local_index, i; /* need to handle bits usage across multiple 32bit vals within IPv6. */ if (ip->bits < 32 ) { i=0; } else if (ip->bits < 64) { i=1; } else if (ip->bits < 96) { i=2; } else { i=3; } local_index = ip->adr[i] << (ip->bits %32); index = local_index >> (sizeof(local_index)*8 - sub_table->width); } /* Check if this is the last table to traverse to */ if(sub_table->width >= cur_len) { /* Calculate how many entries need to be filled * in this table. If the table is 24 bits wide, and the entry * is 20 bytes long, 2^4 entries need to be filled. */ fill = 1 << (sub_table->width - cur_len); index = (index >> (sub_table->width - cur_len)) << (sub_table->width - cur_len); fill += index; /* Favor most recent CIDR */ if(behavior == RT_FAVOR_TIME) { _dir_fill_all(&root_table->allocated, index, fill, length, (word)ptr, sub_table); } /* Fill over less specific CIDR */ else { _dir_fill_less_specific(index, fill, length, (word)ptr, sub_table); } } /* Need to traverse to a sub-table */ else { dir_sub_table_t *next_sub = (dir_sub_table_t *)sub_table->entries[index]; /* Check if we need to alloc a new sub table. * If next_sub was 0/NULL, there's no entry at this index * If the length is non-zero, there is an entry */ if(!next_sub || sub_table->lengths[index]) { if( root_table->dim_size <= current_depth ) { return RT_INSERT_FAILURE; } sub_table->entries[index] = (word) _sub_table_new(root_table, current_depth+1, (word) next_sub, sub_table->lengths[index]); if (!next_sub) { sub_table->filledEntries++; } sub_table->cur_num++; sub_table->lengths[index] = 0; next_sub = (dir_sub_table_t *)sub_table->entries[index]; if(!next_sub) { return MEM_ALLOC_FAILURE; } } /* Recurse to next level. Rightshift off appropriate number of * bits and update the length accordingly. */ ip->bits += sub_table->width; return (_dir_sub_insert(ip, length, cur_len - sub_table->width, ptr, current_depth+1, behavior, next_sub, root_table)); } return RT_SUCCESS; } /* Insert entry into DIR-n-m tables * @param ip IP address structure * @param len Number of bits of the IP used for lookup * @param ptr Information to be associated with this IP range * @param master_table The table that describes all, returned by dir_new */ int sfrt_dir_insert(uint32_t* adr, int numAdrDwords, int len, word data_index, int behavior, void *table) { dir_table_t *root = (dir_table_t*)table; uint32_t h_adr[4]; IPLOOKUP iplu; iplu.adr = h_adr; iplu.bits = 0; /* Validate arguments */ if(!root || !root->sub_table) { return DIR_INSERT_FAILURE; } h_adr[0] = ntohl(adr[0]); if (len > 96) { h_adr[1] = ntohl(adr[1]); h_adr[2] = ntohl(adr[2]); h_adr[3] = ntohl(adr[3]); } else if (len > 64) { h_adr[1] = ntohl(adr[1]); h_adr[2] = ntohl(adr[2]); } else if (len > 32) { h_adr[1] = ntohl(adr[1]); } /* Find the sub table in which to insert */ return _dir_sub_insert(&iplu, len, len, (GENERIC)data_index, 0, behavior, root->sub_table, root); } /* Traverse sub tables looking for match */ /* Called by dir_lookup and recursively */ static tuple_t _dir_sub_lookup(IPLOOKUP *ip, dir_sub_table_t *table) { word index; { uint32_t local_index, i; /* need to handle bits usage across multiple 32bit vals within IPv6. */ if (ip->bits < 32 ) { i=0; } else if (ip->bits < 64) { i=1; } else if (ip->bits < 96) { i=2; } else { i=3; } local_index = ip->adr[i] << (ip->bits %32); index = local_index >> (sizeof(local_index)*8 - table->width); } if( !table->entries[index] || table->lengths[index] ) { tuple_t ret; ret.index = table->entries[index]; ret.length = (word)table->lengths[index]; return ret; } ip->bits += table->width; return _dir_sub_lookup( ip, (dir_sub_table_t *)table->entries[index]); } /* Lookup information associated with the value "ip" */ tuple_t sfrt_dir_lookup(uint32_t* adr, int numAdrDwords, void *tbl) { dir_table_t *root = (dir_table_t*)tbl; uint32_t h_adr[4]; int i; IPLOOKUP iplu; iplu.adr = h_adr; iplu.bits = 0; if(!root || !root->sub_table) { tuple_t ret = { 0, 0 }; return ret; } for (i = 0; i < numAdrDwords; i++) { h_adr[i] = ntohl(adr[i]); } return _dir_sub_lookup(&iplu, root->sub_table); } uint32_t sfrt_dir_usage(void *table) { if(!table) { return 0; } return ((dir_table_t*)(table))->allocated; } static void _sub_table_print(dir_sub_table_t *sub, uint32_t level, dir_table_t *table) { int index; char label[100]; memset(label, ' ', sizeof(label)); label[level*5] = '\0'; printf("%sCurrent Nodes: %d, Filled Entries: %d, table Width: %d\n", label, sub->cur_num, sub->filledEntries, sub->width); for(index=0; index < sub->num_entries; index++) { if (sub->lengths[index] || sub->entries[index]) printf("%sIndex: %d, Length: %d, dataIndex: %d\n", label, index, sub->lengths[index], (uint32_t)sub->entries[index]); if( !sub->lengths[index] && sub->entries[index] ) { _sub_table_print((dir_sub_table_t*) sub->entries[index], level+1, table); } } } /* Print a table. * Prints a table and its subtable. This is used for debugging purpose only. * @param table The table that describes all, returned by dir_new */ void sfrt_dir_print(void *tbl) { dir_table_t *table = (dir_table_t*)tbl; if(!table) { return; } printf ("Nodes in use: %d\n", table->cur_num); if(table->sub_table) { _sub_table_print(table->sub_table, 1, table); } } /* Sub table removal * Recursive function to drill down to subnet table and remove entries. * @param ip IP address structure * @param length Number of bits of the IP used to specify this CIDR * @param cur_len Number of bits of the IP left at this depth * @param current_depth Number of levels down from root_table. * @param behavior RT_FAVOR_SPECIFIC or RT_FAVOR_TIME * @param root_table The table that describes all, returned by dir_new * @returns index of entry removed. Returns 0, which is a valid index, as failure code. * Calling function should treat 0 index as failure case.*/ static int _dir_sub_remove(IPLOOKUP *ip, int length, int cur_len, int current_depth, int behavior, dir_sub_table_t *sub_table, dir_table_t *root_table) { word index; uint32_t fill; uint32_t valueIndex = 0; { uint32_t local_index, i; /* need to handle bits usage across multiple 32bit vals within IPv6. */ if (ip->bits < 32 ) { i=0; } else if (ip->bits < 64) { i=1; } else if (ip->bits < 96) { i=2; } else { i=3; } local_index = ip->adr[i] << (ip->bits %32); index = local_index >> (sizeof(local_index)*8 - sub_table->width); } /* Check if this is the last table to traverse to */ if(sub_table->width >= cur_len) { /* Calculate how many entries need to be removed (filled with 0) * in this table. If the table is 24 bits wide, and the entry * is 20 bytes long, 2^4 entries need to be filled. */ fill = 1 << (sub_table->width - cur_len); index = (index >> (sub_table->width - cur_len)) << (sub_table->width - cur_len); fill += index; /* Remove and overwrite without consedering CIDR specificity*/ if(behavior == RT_FAVOR_TIME) { valueIndex = _dir_remove_all(&root_table->allocated, index, fill, length, sub_table); } /* Remove and overwrite only less specific CIDR */ else { valueIndex = _dir_remove_less_specific(&root_table->allocated, index, fill, length, sub_table); } } else { /* traverse to a next sub-table down*/ dir_sub_table_t *next_sub = (dir_sub_table_t *)sub_table->entries[index]; /*subtable was never added. */ if(!next_sub || sub_table->lengths[index]) { return 0; } /* Recurse to next level. Rightshift off appropriate number of * bits and update the length accordingly. */ ip->bits += sub_table->width; valueIndex = _dir_sub_remove(ip, length, cur_len - sub_table->width, current_depth+1, behavior, next_sub, root_table); if (!next_sub->filledEntries) { _sub_table_free(&root_table->allocated, next_sub); sub_table->entries[index] = 0; sub_table->lengths[index] = 0; sub_table->filledEntries--; root_table->cur_num--; } } return valueIndex; } /* Remove entry into DIR-n-m tables * @param ip IP address structure * @param len Number of bits of the IP used for lookup * @param behavior RT_FAVOR_SPECIFIC or RT_FAVOR_TIME * @param table The table that describes all, returned by dir_new * @return index to data or 0 on failure. Calling function should check for 0 since * this is valid index for failed operation. */ word sfrt_dir_remove(uint32_t* adr, int numAdrDwords, int len, int behavior, void *table) { dir_table_t *root = (dir_table_t*)table; uint32_t h_adr[4]; IPLOOKUP iplu; iplu.adr = h_adr; iplu.bits = 0; /* Validate arguments */ if(!root || !root->sub_table) { return 0; } h_adr[0] = ntohl(adr[0]); if (len > 96) { h_adr[1] = ntohl(adr[1]); h_adr[2] = ntohl(adr[2]); h_adr[3] = ntohl(adr[3]); } else if (len > 64) { h_adr[1] = ntohl(adr[1]); h_adr[2] = ntohl(adr[2]); } else if (len > 32) { h_adr[1] = ntohl(adr[1]); } /* Find the sub table in which to remove */ return _dir_sub_remove(&iplu, len, len, 0, behavior, root->sub_table, root); } snort-2.9.20/src/sfutil/sf_ipvar.c0000644000175000017500000007310114241077245015153 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Adam Keeton * sf_ipvar.c * 11/17/06 * * Library for IP variables. */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "util.h" #include "sf_ipvar.h" #include "sf_vartable.h" #define LIST_OPEN '[' #define LIST_CLOSE ']' static SFIP_RET sfvar_list_compare(sfip_node_t *, sfip_node_t *); static inline void sfip_node_free ( sfip_node_t * ); static inline void sfip_node_freelist ( sfip_node_t * ); static inline sfip_var_t *_alloc_var(void) { return (sfip_var_t*)calloc(1, sizeof(sfip_var_t)); } void sfvar_free(sfip_var_t *var) { if(!var) return; if(var->name) free(var->name); if(var->value) free(var->value); if(var->mode == SFIP_LIST) { sfip_node_freelist(var->head); sfip_node_freelist(var->neg_head); } else if(var->mode == SFIP_TABLE) { // XXX } free(var); } sfip_node_t *sfipnode_alloc(char *str, SFIP_RET *status) { sfip_node_t *ret; if(!str) { if(status) *status = SFIP_ARG_ERR; return NULL; } if( (ret = (sfip_node_t*)calloc(1, sizeof(sfip_node_t))) == NULL ) { if(status) *status = SFIP_ALLOC_ERR; return NULL; } /* Check if this string starts with a '!', if so, * then the node needs to be negated */ if(*str == '!') { str++; ret->flags |= SFIP_NEGATED; } /* Check if this is an "any" */ if(!strncasecmp(str, "any", 3)) { /* Make sure they're not doing !any, which is meaningless */ if(ret->flags & SFIP_NEGATED) { if(status) *status = SFIP_ARG_ERR; free(ret); return NULL; } ret->flags |= SFIP_ANY; if( (ret->ip = sfip_alloc("0.0.0.0", status)) == NULL ) { /* Failed to parse this string, so free and return */ if(status) *status = SFIP_ALLOC_ERR; free(ret); return NULL; } if(status) *status = SFIP_SUCCESS; #if 0 if( (ret->ip = sfip_alloc("0.0.0.0", NULL)) == NULL) { if(status) *status = SFIP_FAILURE; free(ret); return NULL; } #endif } else if( (ret->ip = sfip_alloc(str, status)) == NULL ) { /* Failed to parse this string, so free and return */ if(status) *status = SFIP_INET_PARSE_ERR; free(ret); return NULL; } /* Check if this is a negated, zero'ed IP (equivalent of a "!any") */ if(!sfip_is_set(ret->ip) && (ret->flags & SFIP_NEGATED)) { if(status) *status = SFIP_NOT_ANY; free(ret->ip); free(ret); return NULL; } return ret; } static inline void sfip_node_free ( sfip_node_t *node ) { if ( !node ) return; if ( node->ip ) sfip_free(node->ip); free(node); } static inline void sfip_node_freelist ( sfip_node_t *root ) { sfip_node_t *node; if ( !root ) return; for ( node = root; node; node = root ) { root = root->next; sfip_node_free(node); } } sfip_node_t* MergeLists(sfip_node_t *list1, sfip_node_t* list2, uint16_t list1_len, uint16_t list2_len, uint16_t *merge_len) { SFIP_RET ret = SFIP_SUCCESS; sfip_node_t* listHead = NULL, *merge_list = NULL, *tmp = NULL, *node = NULL; uint16_t num_nodes = 0; if(!list1 && !list2) { *merge_len = 0; return NULL; } if (list1 == NULL) { *merge_len = list2_len; return list2; } if (list2 == NULL) { *merge_len = list1_len; return list1; } /*Both lists are sorted and not NULL. If list1 or list2 contains "any", free the other list*/ if(list1->flags & SFIP_ANY) { *merge_len = list1_len; while(list2) { tmp = list2->next; sfip_node_free(list2); list2 = tmp; } return list1; } if(list2->flags & SFIP_ANY) { *merge_len = list2_len; while(list1) { tmp = list1->next; sfip_node_free(list1); list1 = tmp; } return list2; } /*Iterate till one of the list is NULL. Append each node to merge_list*/ while (list1 && list2) { ret = sfip_cidr_compare(list1->ip, list2->ip); if(ret == SFIP_LESSER) { node = list1; list1 = list1->next; list1_len--; } else if(ret == SFIP_GREATER) { node = list2; list2 = list2->next; list2_len--; } else if(ret == SFIP_EQUAL) { node = list1; list1 = list1->next; /*Free the duplicate node*/ tmp = list2->next; sfip_node_free(list2); list2 = tmp; list1_len--; list2_len--; } if(!merge_list) { merge_list = node; listHead = node; } else { merge_list->next = node; merge_list = merge_list->next; } num_nodes++; } /*list2 is NULL. Append list1*/ if(list1 != NULL) { merge_list->next = list1; num_nodes += list1_len; } /*list1 is NULL. Append list2*/ if(list2 != NULL) { merge_list->next = list2; num_nodes += list2_len; } *merge_len = num_nodes; return listHead; } /* Deep copy of src added to dst */ /*src is sorted. Iterate over every node in dst and do sfvar_add_node*/ SFIP_RET sfvar_add(sfip_var_t *dst, sfip_var_t *src) { sfip_var_t *copiedvar; if(!dst || !src) return SFIP_ARG_ERR; if((copiedvar = sfvar_deep_copy(src)) == NULL) { return SFIP_ALLOC_ERR; } dst->head = MergeLists(dst->head, copiedvar->head, dst->head_count, copiedvar->head_count, &dst->head_count); dst->neg_head = MergeLists(dst->neg_head, copiedvar->neg_head, dst->neg_head_count, copiedvar->neg_head_count, &dst->neg_head_count); free(copiedvar); return SFIP_SUCCESS; } SFIP_RET sfvar_add_node(sfip_var_t *var, sfip_node_t *node, int negated) { sfip_node_t *p, *swp, *tmp; sfip_node_t **head; uint16_t *count; if(!var || !node) return SFIP_ARG_ERR; /* XXX */ /* As of this writing, 11/20/06, nodes are always added to * the list, regardless of the mode (list or table). */ if(negated) { head = &var->neg_head; count = &var->neg_head_count; } else { head = &var->head; count = &var->head_count; } if(!(*head)) { *head = node; ++*count; return SFIP_SUCCESS; } /*If head node is any, do not add anything else*/ if((*head)->flags & SFIP_ANY) { sfip_node_free(node); return SFIP_SUCCESS; } /* "Anys" should always be inserted first */ /* Otherwise, check if this IP is less than the head's IP */ SFIP_RET node_cmp_ret = SFIP_SUCCESS; if(node->flags & SFIP_ANY) { if((*head)->flags & SFIP_ANY) { sfip_node_free(node); return SFIP_SUCCESS; } else { /*Free the list when adding any*/ while(*head) { tmp = (*head)->next; sfip_node_free(*head); *head = tmp; } *head = node; *count = 1; return SFIP_SUCCESS; } } else { node_cmp_ret = sfip_cidr_compare(node->ip, (*head)->ip); if(node_cmp_ret == SFIP_EQUAL) { sfip_node_free(node); return SFIP_SUCCESS; } else if(node_cmp_ret == SFIP_LESSER) { node->next = *head; *head = node; ++*count; return SFIP_SUCCESS; } } /* If we're here, the head node was lesser than the new node */ /* Before searching the list, verify there is atleast two nodes. * (This saves an extra check during the loop below) */ if(!(*head)->next) { (*head)->next = node; ++*count; return SFIP_SUCCESS; } /* Insertion sort */ for(p = *head; p->next; p=p->next) { node_cmp_ret = sfip_cidr_compare(node->ip, p->next->ip); if(node_cmp_ret == SFIP_EQUAL) { sfip_node_free(node); return SFIP_SUCCESS; } else if(node_cmp_ret == SFIP_LESSER) { swp = p->next; p->next = node; node->next = swp; ++*count; return SFIP_SUCCESS; } } p->next = node; ++*count; return SFIP_SUCCESS; /* XXX Insert new node into routing table */ // sfrt_add(node->ip, } static SFIP_RET sfvar_list_compare(sfip_node_t *list1, sfip_node_t *list2) { sfip_node_t *tmp, *tmp2; if ((list1 == NULL) && (list2 == NULL)) return SFIP_EQUAL; /* Lists are ordered and of equal size */ for (tmp = list1, tmp2 = list2; (tmp != NULL) && (tmp2 != NULL); tmp = tmp->next, tmp2 = tmp2->next) { if ((sfip_cidr_compare(tmp->ip, tmp2->ip) != SFIP_EQUAL)) { return SFIP_FAILURE; } } return SFIP_EQUAL; } /* Check's if two variables have the same nodes */ SFIP_RET sfvar_compare(const sfip_var_t *one, const sfip_var_t *two) { /* If both NULL, consider equal */ if(!one && !two) return SFIP_EQUAL; /* If one NULL and not the other, consider unequal */ if((one && !two) || (!one && two)) return SFIP_FAILURE; if (sfvar_is_alias(one, two)) return SFIP_EQUAL; if(one->head_count != two->head_count) return SFIP_FAILURE; if (sfvar_list_compare(one->head, two->head) == SFIP_FAILURE) return SFIP_FAILURE; if(one->neg_head_count != two->neg_head_count) return SFIP_FAILURE; if (sfvar_list_compare(one->neg_head, two->neg_head) == SFIP_FAILURE) return SFIP_FAILURE; return SFIP_EQUAL; } /* Support function for sfvar_parse_iplist. Used to * correctly match up end brackets. * (Can't just do strchr(str, ']') because of the * [a, [b], c] case, and can't do strrchr because * of the [a, [b], [c]] case) */ static char *_find_end_token(char *str) { int stack = 0; for(; *str; str++) { if(*str == LIST_OPEN) stack++; else if(*str == LIST_CLOSE) stack--; if(!stack) { return str; } } return NULL; } /* Support function for sfvar_parse_iplist. * Negates a node */ static void _negate_node(sfip_node_t *node) { if(node->addr_flags & SFIP_NEGATED) { node->addr_flags &= ~SFIP_NEGATED; node->flags &= ~SFIP_NEGATED; } else { node->addr_flags |= SFIP_NEGATED; node->flags |= SFIP_NEGATED; } } /* Support function for sfvar_parse_iplist. * Negates a variable */ static void _negate_lists(sfip_var_t *var) { sfip_node_t *node; sfip_node_t *temp; uint16_t temp_count; for(node = var->head; node; node=node->next) _negate_node(node); for(node = var->neg_head; node; node=node->next) _negate_node(node); /* Swap lists */ temp = var->head; var->head = var->neg_head; var->neg_head = temp; /*Swap the counts*/ temp_count = var->neg_head_count; var->neg_head_count = var->head_count; var->head_count = temp_count; } SFIP_RET sfvar_parse_iplist(vartable_t *table, sfip_var_t *var, char *str, int negation) { char *end, *tok; SFIP_RET ret; int neg_ip; if(!var || !table || !str) return SFIP_ARG_ERR; while(*str) { /* Skip whitespace and leading commas */ if(isspace((int)*str) || *str == ',') { str++; continue; } neg_ip = 0; /* Handle multiple negations */ for(; *str == '!'; str++) neg_ip = !neg_ip; /* Find end of this token */ for(end = str+1; *end && !isspace((int)*end) && *end != LIST_CLOSE && *end != ','; end++) ; tok = SnortStrndup(str, end - str); if(*str == LIST_OPEN) { char *list_tok; /* Find end of this list */ if((end = _find_end_token(str)) == NULL) { /* No trailing bracket found */ free(tok); return SFIP_UNMATCHED_BRACKET; } str++; list_tok = SnortStrndup(str, end - str); if((ret = sfvar_parse_iplist(table, var, list_tok, negation ^ neg_ip)) != SFIP_SUCCESS) { free(list_tok); free(tok); return ret; } free(list_tok); } else if(*str == '$') { sfip_var_t *tmp_var; sfip_var_t *copy_var; if((tmp_var = sfvt_lookup_var(table, tok)) == NULL) { free(tok); return SFIP_LOOKUP_FAILURE; } copy_var = sfvar_deep_copy(tmp_var); /* Apply the negation */ if(negation ^ neg_ip) { /* Check for a negated "any" */ if(copy_var->head && copy_var->head->flags & SFIP_ANY) { free(tok); sfvar_free(copy_var); return SFIP_NOT_ANY; } /* Check if this is a negated, zero'ed IP (equivalent of a "!any") */ if(copy_var->head && !sfip_is_set(copy_var->head->ip)) { free(tok); sfvar_free(copy_var); return SFIP_NOT_ANY; } _negate_lists(copy_var); } sfvar_add(var, copy_var); sfvar_free(copy_var); } else if(*str == LIST_CLOSE) { /* This should be the last character, if not, then this is an * invalid extra closing bracket */ if(!(*(str+1))) { free(tok); return SFIP_SUCCESS; } free(tok); return SFIP_UNMATCHED_BRACKET; } else { sfip_node_t *node; /* Skip leading commas */ for(; *str == ','; str++) ; /* Check for a negated "any" */ if(negation ^ neg_ip && !strcasecmp(tok, "any")) { free(tok); return SFIP_NOT_ANY; } /* This should be an IP address! */ /* Allocate new node for this string and add it to "ret" */ if((node = sfipnode_alloc(tok, &ret)) == NULL) { free(tok); return ret; } if(negation ^ neg_ip) { _negate_node(node); } /* Check if this is a negated, zero'ed IP (equivalent of a "!any") */ if(!sfip_is_set(node->ip) && (node->flags & SFIP_NEGATED)) { sfip_node_free(node); free(tok); return SFIP_NOT_ANY; } ret = sfvar_add_node(var, node, negation ^ neg_ip); if(ret != SFIP_SUCCESS ) { free(tok); return ret; } } free(tok); if(*end) str = end + 1; else break; } return SFIP_SUCCESS; } SFIP_RET sfvar_validate(sfip_var_t *var) { sfip_node_t *idx, *neg_idx; if(!var->head || !var->neg_head) return SFIP_SUCCESS; for(idx = var->head; idx; idx = idx->next) { for(neg_idx = var->neg_head; neg_idx; neg_idx = neg_idx->next) { /* A smaller netmask means "less specific" */ if((sfip_bits(neg_idx->ip) <= sfip_bits(idx->ip)) && /* Verify they overlap */ (sfip_contains(neg_idx->ip, &idx->ip->addr) == SFIP_CONTAINS)) { return SFIP_CONFLICT; } } } return SFIP_SUCCESS; } sfip_var_t * sfvar_create_alias(const sfip_var_t *alias_from, const char *alias_to) { sfip_var_t *ret; if ((alias_from == NULL) || (alias_to == NULL)) return NULL; ret = sfvar_deep_copy(alias_from); if (ret == NULL) return NULL; ret->name = SnortStrdup(alias_to); ret->id = alias_from->id; return ret; } int sfvar_is_alias(const sfip_var_t *one, const sfip_var_t *two) { if ((one == NULL) || (two == NULL)) return 0; if ((one->id != 0) && (one->id == two->id)) return 1; return 0; } /* Allocates and returns a new variable, described by "variable". */ sfip_var_t *sfvar_alloc(vartable_t *table, char *variable, SFIP_RET *status) { sfip_var_t *ret, *tmpvar; char *str, *end, *tmp; SFIP_RET stat; bool invalidvar = true; if(!variable || !(*variable)) { if(status) *status = SFIP_ARG_ERR; return NULL; } if( (ret = _alloc_var()) == NULL ) { if(status) *status = SFIP_ALLOC_ERR; return NULL; } /* Extract and save the variable's name */ /* Start by skipping leading whitespace or line continuations: '\' */ for(str = variable ; *str && (isspace((int)*str) || *str == '\\'); str++) ; if (*str == 0) /* Didn't get anything */ { if (status) *status = SFIP_ARG_ERR; sfvar_free(ret); return NULL; } /* Find the end of the name */ for(end = str; *end && !isspace((int)*end) && *end != '\\'; end++) { if(isalpha((int)*end)) invalidvar = false; } if(invalidvar) { if(status) *status = SFIP_INVALID_VAR; sfvar_free(ret); return NULL; } if(!isalnum((int)*str) && *str != '$' && *str != '!' && *str != '_') { if(status) *status = SFIP_ARG_ERR; sfvar_free(ret); return NULL; } /* Set the new variable's name/key */ if((ret->name = SnortStrndup(str, end - str)) == NULL) { if(status) *status = SFIP_ALLOC_ERR; sfvar_free(ret); return NULL; } /* End points to the end of the name. Skip past it and any whitespace * or potential line continuations */ str = end; for (; (*str != 0) && (isspace((int)*str) || (*str == '\\')); str++); if (*str == 0) /* Didn't get anything */ { if (status) *status = SFIP_ARG_ERR; sfvar_free(ret); return NULL; } /* Trim off whitespace and line continuations from the end of the string */ end = (str + strlen(str)) - 1; for (; (end > str) && (isspace((int)*end) || (*end == '\\')); end--); end++; /* See if this is just an alias */ tmp = SnortStrndup(str, end - str); tmpvar = sfvt_lookup_var(table, tmp); free(tmp); if (tmpvar != NULL) { sfip_var_t *aliased = sfvar_create_alias(tmpvar, ret->name); if (aliased != NULL) { if (status != NULL) *status = SFIP_SUCCESS; sfvar_free(ret); return aliased; } } /* Everything is treated as a list, even if it's one element that's not * surrounded by brackets */ stat = sfvar_parse_iplist(table, ret, str, 0); if (status != NULL) *status = stat; if (stat != SFIP_SUCCESS) { sfvar_free(ret); return NULL; } if(ret->head && (ret->head->flags & SFIP_ANY && ret->head->flags & SFIP_NEGATED)) { if(status) *status = SFIP_NOT_ANY; sfvar_free(ret); return NULL; } if(sfvar_validate(ret) == SFIP_CONFLICT) { if(status) *status = SFIP_CONFLICT; sfvar_free(ret); return NULL; } return ret; } static inline sfip_node_t *_sfvar_deep_copy_list(const sfip_node_t *idx) { sfip_node_t *ret, *temp, *prev; ret = temp = NULL; for( ; idx; idx = idx->next) { prev = temp; if( (temp = (sfip_node_t*)calloc(1, sizeof(sfip_node_t))) == NULL ) { sfip_node_freelist(ret); return NULL; } if( (temp->ip = (sfcidr_t*)calloc(1, sizeof(sfcidr_t))) == NULL ) { sfip_node_freelist(ret); free(temp); return NULL; } temp->flags = idx->flags; temp->addr_flags = idx->addr_flags; /* If it's an "any", there may be no IP object */ if(idx->ip) memcpy(temp->ip, idx->ip, sizeof(sfcidr_t)); if(prev) prev->next = temp; else ret = temp; } return ret; } sfip_var_t *sfvar_deep_copy(const sfip_var_t *var) { sfip_var_t *ret; if(!var) return NULL; ret = (sfip_var_t*)SnortAlloc(sizeof(sfip_var_t)); ret->mode = var->mode; ret->head = _sfvar_deep_copy_list(var->head); ret->neg_head = _sfvar_deep_copy_list(var->neg_head); ret->head_count = var->head_count; ret->neg_head_count = var->neg_head_count; return ret; } /* Support function for sfvar_ip_in */ static inline int _sfvar_ip_in4(sfip_var_t *var, sfaddr_t *ip) { int match; sfip_node_t *pos_idx, *neg_idx; match = 0; pos_idx = var->head; neg_idx = var->neg_head; if(!pos_idx) { for( ; neg_idx; neg_idx = neg_idx->next) { if(sfaddr_family(&neg_idx->ip->addr) != AF_INET) continue; if(sfip_fast_cont4(neg_idx->ip, ip)) { return 0; } } return 1; } while(pos_idx) { if(neg_idx) { if(sfaddr_family(&neg_idx->ip->addr) == AF_INET && sfip_fast_cont4(neg_idx->ip, ip)) { return 0; } neg_idx = neg_idx->next; } /* No more potential negations. Check if we've already matched. */ else if(match) { return 1; } if(!match) { if(sfip_is_set(pos_idx->ip)) { if(sfaddr_family(&pos_idx->ip->addr) == AF_INET && sfip_fast_cont4(pos_idx->ip, ip)) { match = 1; } else { pos_idx = pos_idx->next; } } else { match = 1; } } } return 0; } /* Support function for sfvar_ip_in */ static inline int _sfvar_ip_in6(sfip_var_t *var, sfaddr_t *ip) { int match; sfip_node_t *pos_idx, *neg_idx; match = 0; pos_idx = var->head; neg_idx = var->neg_head; if(!pos_idx) { for( ; neg_idx; neg_idx = neg_idx->next) { if(sfaddr_family(&neg_idx->ip->addr) != AF_INET6) continue; if(sfip_fast_cont6(neg_idx->ip, ip)) { return 0; } } return 1; } while(pos_idx) { if(neg_idx) { if(sfaddr_family(&neg_idx->ip->addr) == AF_INET6 && sfip_fast_cont6(neg_idx->ip, ip)) { return 0; } neg_idx = neg_idx->next; } /* No more potential negations. Check if we've already matched. */ else if(match) { return 1; } if(!match) { if(sfip_is_set(pos_idx->ip)) { if(sfaddr_family(&pos_idx->ip->addr) == AF_INET6 && sfip_fast_cont6(pos_idx->ip, ip)) { match = 1; } else { pos_idx = pos_idx->next; } } else { match = 1; } } } return 0; } /* Returns SFIP_SUCCESS if ip is contained in 'var', SFIP_FAILURE otherwise */ /* If either argument is NULL, SFIP_ARG_ERR is returned. */ int sfvar_ip_in(sfip_var_t *var, sfaddr_t *ip) { if(!var || !ip) return 0; #if 0 if(var->mode == SFIP_TABLE) { // XXX } else { #endif /* Since this is a performance-critical function it uses different * codepaths for IPv6 and IPv4 traffic, rather than the dual-stack * functions. */ if(sfaddr_family(ip) == AF_INET) { return _sfvar_ip_in4(var, ip); } else { return _sfvar_ip_in6(var, ip); } #if 0 } #endif } static char buffer[1024]; void sfip_set_print(const char *prefix, sfip_node_t *p) { int ret; for(; p; p = p->next) { buffer[0] = '\0'; if(!p->ip) continue; if(p->flags & SFIP_NEGATED) { if (p->ip->bits != 128) { if (sfaddr_family(&p->ip->addr) == AF_INET6) { ret = SnortSnprintfAppend(buffer, sizeof(buffer), "!%s/%d", sfip_to_str(&p->ip->addr), p->ip->bits); } else { ret = SnortSnprintfAppend(buffer, sizeof(buffer), "!%s/%d", sfip_to_str(&p->ip->addr), p->ip->bits < 96 ? -1 : p->ip->bits - 96); } } else { ret = SnortSnprintfAppend(buffer, sizeof(buffer), "!%s", sfip_to_str(&p->ip->addr)); } if (ret != SNORT_SNPRINTF_SUCCESS) return; } else { if (p->ip->bits != 128) { if (sfaddr_family(&p->ip->addr) == AF_INET6) { ret = SnortSnprintfAppend(buffer, sizeof(buffer), "%s/%d", sfip_to_str(&p->ip->addr), p->ip->bits); } else { ret = SnortSnprintfAppend(buffer, sizeof(buffer), "%s/%d", sfip_to_str(&p->ip->addr), p->ip->bits < 96 ? -1 : p->ip->bits - 96); } } else { ret = SnortSnprintfAppend(buffer, sizeof(buffer), "%s", sfip_to_str(&p->ip->addr)); } if (ret != SNORT_SNPRINTF_SUCCESS) return; } if (prefix) LogMessage("%s%s\n", prefix, buffer); else LogMessage("%s\n", buffer); } } void sfvar_print(const char *prefix, sfip_var_t *var) { if (!var || !var->head) { return; } if(var->mode == SFIP_LIST) { if(var->head->flags & SFIP_ANY) { if (prefix) LogMessage("%sany\n", prefix); else LogMessage("any\n"); } else { sfip_set_print(prefix, var->head); } } else if(var->mode == SFIP_TABLE) { // XXX } } void sfip_set_print_to_file(FILE *f, sfip_node_t *p) { for(; p; p = p->next) { if(!p->ip) continue; if(p->flags & SFIP_NEGATED) fprintf(f, "\t!%s\n", sfip_to_str(&p->ip->addr)); else fprintf(f, "\t %s\n", sfip_to_str(&p->ip->addr)); } } /* Prints the variable "var" to the file descriptor 'f' */ void sfvar_print_to_file(FILE *f, sfip_var_t *var) { if(!f) return; if(!var || !var->head) { fprintf(f, "[no variable]\n"); return; } fprintf(f, "Name: %s\n", var->name); if(var->mode == SFIP_LIST) { if(var->head->flags & SFIP_ANY) fprintf(f, "\t%p: \n", (void*)var->head); else { sfip_set_print_to_file(f, var->head); } } else if(var->mode == SFIP_TABLE) { // XXX } } int sfvar_flags(sfip_node_t *node) { if(node) return node->flags; return -1; } /* XXX The unit tests for this code are performed within sf_vartable.c */ #if 0 int main() { sfip_vtable *table; sfip_var_t *var; sfcidr_t *ip; /* Test parsing */ /* Allowable arguments: * { [, , ... , } * Where an IP can be in CIDR notation, or be specified with a netmask. * IPs may also be negated with '!' */ puts("********************************************************************"); puts("Testing parsing:"); var = sfvar_str(" { 1.2.3.4/8, 5.5.5.5 255.255.255.0, any} "); sfip_print_var(stdout, var); sfvar_free(var); puts(""); var = sfvar_str(" { 1.2.3.4, ffff::3/127, 0.0.0.1} "); sfip_print_var(stdout, var); ip = sfip_alloc("1.2.3.5"); printf("(need more of these) 'in': %d\n", sfip_in(var, ip)); puts("also, use 'sfip_in' for the unit tests"); puts(""); return 0; } #endif snort-2.9.20/src/sfutil/util_net.h0000644000175000017500000000265514241077355015202 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file util_net.h * @author Chris Green * @date Fri Jun 27 10:20:31 2003 * * @brief simple network related functions * * Put your simple network related functions here */ #ifndef _UTIL_NET_H #define _UTIL_NET_H #include "sf_types.h" #include "ipv6_port.h" char *inet_ntoax(const sfaddr_t *); char * mktcpflag_str(int flags); #endif /* _UTIL_NET_H */ snort-2.9.20/src/sfutil/mpse.h0000644000175000017500000000717414241077227014322 0ustar apoapo/* ** $Id$ ** ** mpse.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Marc Norton ** ** Multi-Pattern Search Engine ** ** Supports: ** ** Modified Wu-Manber mwm.c/.h ** Aho-Corasick - Deterministic Finite Automatum ** Keyword Trie with Boyer Moore Bad Character Shifts ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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 Gener* ** ** ** Updates: ** ** man - 7/25/2002 - modified #defines for WIN32, and added uint64 ** */ #ifndef _MPSE_H #define _MPSE_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "bitop.h" #include "mpse_methods.h" /* * Move these defines to a generic Win32/Unix compatability file, * there must be one somewhere... */ #ifndef CDECL #define CDECL #endif #define MPSE_INCREMENT_GLOBAL_CNT 1 #define MPSE_DONT_INCREMENT_GLOBAL_COUNT 0 /* ** PROTOTYPES */ struct _SnortConfig; void * mpseNew( int method, int use_global_counter_flag, void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)); void * mpseNewWithSnortConfig( struct _SnortConfig *sc, int method, int use_global_counter_flag, void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)); void mpseFree( void * pv ); int mpseAddPattern ( void * pv, void * P, int m, unsigned noCase, unsigned offset, unsigned depth, unsigned negative, void* ID, int IID ); int mpseAddPatternWithSnortConfig ( struct _SnortConfig *sc, void * pvoid, void * P, int m, unsigned noCase, unsigned offset, unsigned depth, unsigned negative, void* ID, int IID ); void mpseLargeShifts ( void * pvoid, int flag ); int mpsePrepPatterns ( void * pvoid, int ( *build_tree )(void *id, void **existing_tree), int ( *neg_list_func )(void *id, void **list) ); struct _SnortConfig; int mpsePrepPatternsWithSnortConf ( struct _SnortConfig *, void * pvoid, int ( *build_tree )(struct _SnortConfig *, void *id, void **existing_tree), int ( *neg_list_func )(void *id, void **list) ); void mpseSetRuleMask ( void *pv, BITOP * rm ); int mpseSearch( void *pv, const unsigned char * T, int n, int ( *action )(void* id, void * tree, int index, void *data, void *neg_list), void * data, int* current_state ); int mpseSearchAll( void *pv, const unsigned char * T, int n, int ( *action )(void* id, void * tree, int index, void *data, void *neg_list), void * data, int* current_state ); int mpseGetPatternCount(void *pv); uint64_t mpseGetPatByteCount(void); void mpseResetByteCount(void); int mpsePrintInfo( void * obj ); int mpsePrintSummary(int); int mpsePrintSummaryWithSnortConfig(struct _SnortConfig *, int); void mpseVerbose( void * pvoid ); void mpseSetOpt( void * pvoid,int flag); void mpse_print_qinfo(void); void mpseInitSummary(void); #endif snort-2.9.20/src/sfutil/util_net.c0000644000175000017500000000605514241077354015172 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #ifndef WIN32 /* for inet_ntoa */ #include #include #include #include #endif /* WIN32 */ #include #include "util_net.h" #include "util.h" /** * give a textual representation of tcp flags * * @param flags tcph->flags * * @return ptr to a static buffer w/ the string represented */ char * mktcpflag_str(int flags) { static char buf[9]; const int fin = 0x01; const int syn = 0x02; const int rst = 0x04; const int psh = 0x08; const int ack = 0x10; const int urg = 0x20; const int cwr = 0x40; const int ecn_echo = 0x80; memset(buf, '-', 9); if(flags & fin) buf[0] = 'F'; if(flags & syn) buf[1] = 'S'; if(flags & rst) buf[2] = 'R'; if(flags & psh) buf[3] = 'P'; if(flags & ack) buf[4] = 'A'; if(flags & urg) buf[5] = 'U'; if(flags & cwr) buf[6] = 'C'; if(flags & ecn_echo) buf[7] = 'E'; buf[8] = '\0'; return buf; } /** * A inet_ntoa that has 2 static buffers that are changed between * subsequent calls * * @param ip ip in NETWORK BYTE ORDER */ char *inet_ntoax(const sfaddr_t *ip) { static char ip_buf1[INET6_ADDRSTRLEN]; static char ip_buf2[INET6_ADDRSTRLEN]; static int buf_num = 0; int buf_size = INET6_ADDRSTRLEN; char *ip_buf; if (buf_num) ip_buf = ip_buf2; else ip_buf = ip_buf1; buf_num ^= 1; ip_buf[0] = 0; SnortSnprintf(ip_buf, buf_size, "%s", inet_ntoa(ip)); return ip_buf; } #ifdef TEST_UTIL_NET int main(void) { uint32_t ip1 = htonl(0xFF00FF00); uint32_t ip2 = htonl(0xFFAAFFAA); printf("%s -> %s\n", inet_ntoax(ip1), inet_ntoax(ip2)); /* the following one is invalid and will break the first one*/ printf("%s -> %s -> %s\n", inet_ntoax(ip1), inet_ntoax(ip2), inet_ntoax(ip2)); return 0; } #endif /* TEST_UTIL_NET */ snort-2.9.20/src/sfutil/sfrt_dir.h0000644000175000017500000000656614241077323015173 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * @file sfdir.h * @author Adam Keeton * @date Thu July 20 10:16:26 EDT 2006 * * The implementation uses an multibit-trie that is similar to Gupta et-al's * DIR-n-m. */ #ifndef SFRT_DIR_H_ #define SFRT_DIR_H_ /*******************************************************************/ /* DIR-n-m data structures * Each table in the DIR-n-m method is represented by a * dir_sub_table_t. They are managed by a dir_table_t. */ typedef struct { word *entries; uint8_t *lengths; int num_entries; /* Number of entries in this table */ int width; /* width of this table. */ /* While one determines the other, this way fewer * calculations are needed at runtime, since both * are used. */ int cur_num; /* Present number of used nodes */ /** number of entries filled including chidren sub_tables. This is used * for freeing sub_tables when all entried are freed by delete operation. */ int filledEntries; } dir_sub_table_t; /* Master data structure for the DIR-n-m derivative */ typedef struct { int *dimensions; /* DIR-n-m will consist of any number of arbitrarily * long tables. This variable keeps track of the * dimensions */ int dim_size; /* And this variable keeps track of 'dimensions''s * dimensions! */ uint32_t mem_cap; /* User-defined maximum memory that can be allocated * for the DIR-n-m derivative */ int cur_num; /* Present number of used nodes */ uint32_t allocated; dir_sub_table_t *sub_table; } dir_table_t; /*******************************************************************/ /* DIR-n-m functions, these are not intended to be called directly */ dir_table_t * sfrt_dir_new(uint32_t mem_cap, int count,...); void sfrt_dir_free(void *); tuple_t sfrt_dir_lookup(uint32_t* adr, int numAdrDwords, void *table); int sfrt_dir_insert(uint32_t* adr, int numAdrDwords, int len, word data_index, int behavior, void *table); uint32_t sfrt_dir_usage(void *table); void sfrt_dir_print(void *table); word sfrt_dir_remove(uint32_t* adr, int numAdrDwords, int len, int behavior, void *table); #endif /* SFRT_DIR_H_ */ snort-2.9.20/src/sfutil/sfprimetable.h0000644000175000017500000000217114241077312016016 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef SF_PRIME_TABLE #define SF_PRIME_TABLE int sf_nearest_prime( int n ); #endif snort-2.9.20/src/sfutil/util_math.c0000644000175000017500000000345514241077352015334 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file util_math.c * @author Chris Green * @date Fri Jun 27 10:12:57 2003 * * @brief math related util functions * * Place simple math functions that are useful all over the place * here. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "util_math.h" #include "sf_types.h" /** * Calculate the percentage of something. * * If the total is <= 0, we return 0. * * @param amt amount to that you have * @param total amount there is * * @return a/b * 100 */ double calc_percent(double amt, double total) { if(total <= 0.0) return 0.0; return (amt/total) * 100.0; } double calc_percent64(uint64_t amt, uint64_t total) { if (total <= 0) return 0.0; return ((double)amt/total) * 100.0; } snort-2.9.20/src/sfutil/util_unfold.c0000644000175000017500000001257714241077360015676 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Writen by Bhagyashree Bantwal ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "util_unfold.h" /* Given a string, removes header folding (\r\n followed by linear whitespace) * and exits when the end of a header is found, defined as \n followed by a * non-whitespace. This is especially helpful for HTML. */ int sf_unfold_header(const uint8_t *inbuf, uint32_t inbuf_size, uint8_t *outbuf, uint32_t outbuf_size, uint32_t *output_bytes, int trim_spaces, int *folded) { int num_spaces = 0; const uint8_t *cursor, *endofinbuf; uint8_t *outbuf_ptr; uint32_t n = 0; int httpheaderfolding = 0; int folding_present = 0; cursor = inbuf; endofinbuf = inbuf + inbuf_size; outbuf_ptr = outbuf; /* Keep adding chars until we get to the end of the line. If we get to the * end of the line and the next line starts with a tab or space, add the space * to the buffer and keep reading. If the next line does not start with a * tab or space, stop reading because that's the end of the header. */ while((cursor < endofinbuf) && (n < outbuf_size)) { if(((*cursor == ' ') || (*cursor == '\t'))) { if(folding_present) num_spaces++; if(httpheaderfolding) { num_spaces++; folding_present = 1; httpheaderfolding = 0; } else if(!trim_spaces) { /* Spaces are valid except after CRs */ *outbuf_ptr++ = *cursor; } } else if((*cursor == '\n') && (httpheaderfolding != 1)) { /* Can't have multiple LFs in a row, but if we get one it * needs to be followed by at least one space */ httpheaderfolding = 1; } else if((*cursor == '\r') && !httpheaderfolding) { /* CR needs to be followed by LF and can't start a line */ httpheaderfolding = 2; } else if(!httpheaderfolding) { *outbuf_ptr++ = *cursor; n++; } else { /* We have reached the end of the header */ /* Unless we get multiple CRs, which is suspicious, but not for us to decide */ break; } cursor++; } if(n < outbuf_size) *outbuf_ptr = '\0'; else outbuf[outbuf_size - 1] = '\0'; *output_bytes = outbuf_ptr - outbuf; if(folded) *folded = num_spaces; return 0; } /* Strips the CRLF from the input buffer */ int sf_strip_CRLF(const uint8_t *inbuf, uint32_t inbuf_size, uint8_t *outbuf, uint32_t outbuf_size, uint32_t *output_bytes) { const uint8_t *cursor, *endofinbuf; uint8_t *outbuf_ptr; uint32_t n = 0; if( !inbuf || !outbuf) return -1; cursor = inbuf; endofinbuf = inbuf + inbuf_size; outbuf_ptr = outbuf; while((cursor < endofinbuf) && (n < outbuf_size)) { if((*cursor != '\n') && (*cursor != '\r')) { *outbuf_ptr++ = *cursor; n++; } cursor++; } if(output_bytes) *output_bytes = outbuf_ptr - outbuf; return(0); } /* Strips the LWS at the end of line. * Only strips the LWS before LF or CRLF */ int sf_strip_LWS(const uint8_t *inbuf, uint32_t inbuf_size, uint8_t *outbuf, uint32_t outbuf_size, uint32_t *output_bytes) { const uint8_t *cursor, *endofinbuf; uint8_t *outbuf_ptr; uint32_t n = 0; uint8_t lws = 0; if( !inbuf || !outbuf) return -1; cursor = inbuf; endofinbuf = inbuf + inbuf_size; outbuf_ptr = outbuf; while((cursor < endofinbuf) && (n < outbuf_size)) { if((*cursor != '\n') && (*cursor != '\r')) { if((*cursor != ' ') && (*cursor != '\t')) lws = 0; else lws = 1; *outbuf_ptr++ = *cursor; n++; } else { if(lws) { lws = 0; while( n > 0 ) { if((*(outbuf_ptr-1) != ' ') && (*(outbuf_ptr-1) !='\t')) break; n--; outbuf_ptr--; } } *outbuf_ptr++ = *cursor; n++; } cursor++; } if(output_bytes) *output_bytes = outbuf_ptr - outbuf; return(0); } snort-2.9.20/src/sfutil/sha2.c0000644000175000017500000010455714241077342014207 0ustar apoapo/* * FILE: sha2.c * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ * * Copyright (c) 2000-2001, Aaron D. Gifford * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include "config.h" #ifndef HAVE_OPENSSL_SHA #include /* memcpy()/memset() or bcopy()/bzero() */ #include /* assert() */ #include "sha2.h" /* * ASSERT NOTE: * Some sanity checking code is included using assert(). On my FreeBSD * system, this additional code can be removed by compiling with NDEBUG * defined. Check your own systems manpage on assert() to see how to * compile WITHOUT the sanity checking code on your system. * * UNROLLED TRANSFORM LOOP NOTE: * You can define SHA2_UNROLL_TRANSFORM to use the unrolled transform * loop version for the hash transform rounds (defined using macros * later in this file). Either define on the command line, for example: * * cc -DSHA2_UNROLL_TRANSFORM -o sha2 sha2.c sha2prog.c * * or define below: * * #define SHA2_UNROLL_TRANSFORM * */ /*** SHA-256/384/512 Machine Architecture Definitions *****************/ /* * BYTE_ORDER NOTE: * * Please make sure that your system defines BYTE_ORDER. If your * architecture is little-endian, make sure it also defines * LITTLE_ENDIAN and that the two (BYTE_ORDER and LITTLE_ENDIAN) are * equivilent. * * If your system does not define the above, then you can do so by * hand like this: * * #define LITTLE_ENDIAN 1234 * #define BIG_ENDIAN 4321 * * And for little-endian machines, add: * * #define BYTE_ORDER LITTLE_ENDIAN * * Or for big-endian machines: * * #define BYTE_ORDER BIG_ENDIAN * * The FreeBSD machine this was written on defines BYTE_ORDER * appropriately by including (which in turn includes * where the appropriate definitions are actually * made). */ #if !defined(BYTE_ORDER) || (BYTE_ORDER != LITTLE_ENDIAN && BYTE_ORDER != BIG_ENDIAN) #error Define BYTE_ORDER to be equal to either LITTLE_ENDIAN or BIG_ENDIAN #endif /* * Define the followingsha2_* types to types of the correct length on * the native archtecture. Most BSD systems and Linux define u_intXX_t * types. Machines with very recent ANSI C headers, can use the * uintXX_t definintions from inttypes.h by defining SHA2_USE_INTTYPES_H * during compile or in the sha.h header file. * * Machines that support neither u_intXX_t nor inttypes.h's uintXX_t * will need to define these three typedefs below (and the appropriate * ones in sha.h too) by hand according to their system architecture. * * Thank you, Jun-ichiro itojun Hagino, for suggesting using u_intXX_t * types and pointing out recent ANSI C support for uintXX_t in inttypes.h. */ #ifdef SHA2_USE_INTTYPES_H typedef uint8_t sha2_byte; /* Exactly 1 byte */ typedef uint32_t sha2_word32; /* Exactly 4 bytes */ typedef uint64_t sha2_word64; /* Exactly 8 bytes */ #else /* SHA2_USE_INTTYPES_H */ typedef u_int8_t sha2_byte; /* Exactly 1 byte */ typedef u_int32_t sha2_word32; /* Exactly 4 bytes */ typedef u_int64_t sha2_word64; /* Exactly 8 bytes */ #endif /* SHA2_USE_INTTYPES_H */ /*** SHA-256/384/512 Various Length Definitions ***********************/ /* NOTE: Most of these are in sha2.h */ #define SHA256_SHORT_BLOCK_LENGTH (SHA256_BLOCK_LENGTH - 8) #define SHA384_SHORT_BLOCK_LENGTH (SHA384_BLOCK_LENGTH - 16) #define SHA512_SHORT_BLOCK_LENGTH (SHA512_BLOCK_LENGTH - 16) /*** ENDIAN REVERSAL MACROS *******************************************/ #if BYTE_ORDER == LITTLE_ENDIAN #define REVERSE32(w,x) { \ sha2_word32 tmp = (w); \ tmp = (tmp >> 16) | (tmp << 16); \ (x) = ((tmp & 0xff00ff00UL) >> 8) | ((tmp & 0x00ff00ffUL) << 8); \ } #if defined(WIN32) # define REVERSE64(w,x) { \ sha2_word64 tmp = (w); \ tmp = (tmp >> 32) | (tmp << 32); \ tmp = ((tmp & 0xff00ff00ff00ff00ui64) >> 8) | \ ((tmp & 0x00ff00ff00ff00ffui64) << 8); \ (x) = ((tmp & 0xffff0000ffff0000ui64) >> 16) | \ ((tmp & 0x0000ffff0000ffffui64) << 16); \ } #else /* defined(WIN32) */ #define REVERSE64(w,x) { \ sha2_word64 tmp = (w); \ tmp = (tmp >> 32) | (tmp << 32); \ tmp = ((tmp & 0xff00ff00ff00ff00ULL) >> 8) | \ ((tmp & 0x00ff00ff00ff00ffULL) << 8); \ (x) = ((tmp & 0xffff0000ffff0000ULL) >> 16) | \ ((tmp & 0x0000ffff0000ffffULL) << 16); \ } #endif /* defined(WIN32) */ #endif /* BYTE_ORDER == LITTLE_ENDIAN */ /* * Macro for incrementally adding the unsigned 64-bit integer n to the * unsigned 128-bit integer (represented using a two-element array of * 64-bit words): */ #define ADDINC128(w,n) { \ (w)[0] += (sha2_word64)(n); \ if ((w)[0] < (n)) { \ (w)[1]++; \ } \ } /* * Macros for copying blocks of memory and for zeroing out ranges * of memory. Using these macros makes it easy to switch from * using memset()/memcpy() and using bzero()/bcopy(). * * Please define either SHA2_USE_MEMSET_MEMCPY or define * SHA2_USE_BZERO_BCOPY depending on which function set you * choose to use: */ #if !defined(SHA2_USE_MEMSET_MEMCPY) && !defined(SHA2_USE_BZERO_BCOPY) /* Default to memset()/memcpy() if no option is specified */ #define SHA2_USE_MEMSET_MEMCPY 1 #endif #if defined(SHA2_USE_MEMSET_MEMCPY) && defined(SHA2_USE_BZERO_BCOPY) /* Abort with an error if BOTH options are defined */ #error Define either SHA2_USE_MEMSET_MEMCPY or SHA2_USE_BZERO_BCOPY, not both! #endif #ifdef SHA2_USE_MEMSET_MEMCPY #define MEMSET_BZERO(p,l) memset((p), 0, (l)) #define MEMCPY_BCOPY(d,s,l) memcpy((d), (s), (l)) #endif #ifdef SHA2_USE_BZERO_BCOPY #define MEMSET_BZERO(p,l) bzero((p), (l)) #define MEMCPY_BCOPY(d,s,l) bcopy((s), (d), (l)) #endif /*** THE SIX LOGICAL FUNCTIONS ****************************************/ /* * Bit shifting and rotation (used by the six SHA-XYZ logical functions: * * NOTE: The naming of R and S appears backwards here (R is a SHIFT and * S is a ROTATION) because the SHA-256/384/512 description document * (see http://csrc.nist.gov/cryptval/shs/sha256-384-512.pdf) uses this * same "backwards" definition. */ /* Shift-right (used in SHA-256, SHA-384, and SHA-512): */ #define R(b,x) ((x) >> (b)) /* 32-bit Rotate-right (used in SHA-256): */ #define S32(b,x) (((x) >> (b)) | ((x) << (32 - (b)))) /* 64-bit Rotate-right (used in SHA-384 and SHA-512): */ #define S64(b,x) (((x) >> (b)) | ((x) << (64 - (b)))) /* Two of six logical functions used in SHA-256, SHA-384, and SHA-512: */ #define Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z))) #define Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z))) /* Four of six logical functions used in SHA-256: */ #define Sigma0_256(x) (S32(2, (x)) ^ S32(13, (x)) ^ S32(22, (x))) #define Sigma1_256(x) (S32(6, (x)) ^ S32(11, (x)) ^ S32(25, (x))) #define sigma0_256(x) (S32(7, (x)) ^ S32(18, (x)) ^ R(3 , (x))) #define sigma1_256(x) (S32(17, (x)) ^ S32(19, (x)) ^ R(10, (x))) /* Four of six logical functions used in SHA-384 and SHA-512: */ #define Sigma0_512(x) (S64(28, (x)) ^ S64(34, (x)) ^ S64(39, (x))) #define Sigma1_512(x) (S64(14, (x)) ^ S64(18, (x)) ^ S64(41, (x))) #define sigma0_512(x) (S64( 1, (x)) ^ S64( 8, (x)) ^ R( 7, (x))) #define sigma1_512(x) (S64(19, (x)) ^ S64(61, (x)) ^ R( 6, (x))) /*** INTERNAL FUNCTION PROTOTYPES *************************************/ /* NOTE: These should not be accessed directly from outside this * library -- they are intended for private internal visibility/use * only. */ void SHA512_Last(SHA512_CTX*); void SHA256_Transform(SHA256_CTX*, const sha2_word32*); void SHA512_Transform(SHA512_CTX*, const sha2_word64*); /*** SHA-XYZ INITIAL HASH VALUES AND CONSTANTS ************************/ /* Hash constant words K for SHA-256: */ static const sha2_word32 K256[64] = { 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL }; /* Initial hash value H for SHA-256: */ static const sha2_word32 sha256_initial_hash_value[8] = { 0x6a09e667UL, 0xbb67ae85UL, 0x3c6ef372UL, 0xa54ff53aUL, 0x510e527fUL, 0x9b05688cUL, 0x1f83d9abUL, 0x5be0cd19UL }; #if defined(WIN32) /* Hash constant words K for SHA-384 and SHA-512: */ const static sha2_word64 K512[80] = { 0x428a2f98d728ae22ui64, 0x7137449123ef65cdui64, 0xb5c0fbcfec4d3b2fui64, 0xe9b5dba58189dbbcui64, 0x3956c25bf348b538ui64, 0x59f111f1b605d019ui64, 0x923f82a4af194f9bui64, 0xab1c5ed5da6d8118ui64, 0xd807aa98a3030242ui64, 0x12835b0145706fbeui64, 0x243185be4ee4b28cui64, 0x550c7dc3d5ffb4e2ui64, 0x72be5d74f27b896fui64, 0x80deb1fe3b1696b1ui64, 0x9bdc06a725c71235ui64, 0xc19bf174cf692694ui64, 0xe49b69c19ef14ad2ui64, 0xefbe4786384f25e3ui64, 0x0fc19dc68b8cd5b5ui64, 0x240ca1cc77ac9c65ui64, 0x2de92c6f592b0275ui64, 0x4a7484aa6ea6e483ui64, 0x5cb0a9dcbd41fbd4ui64, 0x76f988da831153b5ui64, 0x983e5152ee66dfabui64, 0xa831c66d2db43210ui64, 0xb00327c898fb213fui64, 0xbf597fc7beef0ee4ui64, 0xc6e00bf33da88fc2ui64, 0xd5a79147930aa725ui64, 0x06ca6351e003826fui64, 0x142929670a0e6e70ui64, 0x27b70a8546d22ffcui64, 0x2e1b21385c26c926ui64, 0x4d2c6dfc5ac42aedui64, 0x53380d139d95b3dfui64, 0x650a73548baf63deui64, 0x766a0abb3c77b2a8ui64, 0x81c2c92e47edaee6ui64, 0x92722c851482353bui64, 0xa2bfe8a14cf10364ui64, 0xa81a664bbc423001ui64, 0xc24b8b70d0f89791ui64, 0xc76c51a30654be30ui64, 0xd192e819d6ef5218ui64, 0xd69906245565a910ui64, 0xf40e35855771202aui64, 0x106aa07032bbd1b8ui64, 0x19a4c116b8d2d0c8ui64, 0x1e376c085141ab53ui64, 0x2748774cdf8eeb99ui64, 0x34b0bcb5e19b48a8ui64, 0x391c0cb3c5c95a63ui64, 0x4ed8aa4ae3418acbui64, 0x5b9cca4f7763e373ui64, 0x682e6ff3d6b2b8a3ui64, 0x748f82ee5defb2fcui64, 0x78a5636f43172f60ui64, 0x84c87814a1f0ab72ui64, 0x8cc702081a6439ecui64, 0x90befffa23631e28ui64, 0xa4506cebde82bde9ui64, 0xbef9a3f7b2c67915ui64, 0xc67178f2e372532bui64, 0xca273eceea26619cui64, 0xd186b8c721c0c207ui64, 0xeada7dd6cde0eb1eui64, 0xf57d4f7fee6ed178ui64, 0x06f067aa72176fbaui64, 0x0a637dc5a2c898a6ui64, 0x113f9804bef90daeui64, 0x1b710b35131c471bui64, 0x28db77f523047d84ui64, 0x32caab7b40c72493ui64, 0x3c9ebe0a15c9bebcui64, 0x431d67c49c100d4cui64, 0x4cc5d4becb3e42b6ui64, 0x597f299cfc657e2aui64, 0x5fcb6fab3ad6faecui64, 0x6c44198c4a475817ui64 }; /* Initial hash value H for SHA-384 */ const static sha2_word64 sha384_initial_hash_value[8] = { 0xcbbb9d5dc1059ed8ui64, 0x629a292a367cd507ui64, 0x9159015a3070dd17ui64, 0x152fecd8f70e5939ui64, 0x67332667ffc00b31ui64, 0x8eb44a8768581511ui64, 0xdb0c2e0d64f98fa7ui64, 0x47b5481dbefa4fa4ui64 }; /* Initial hash value H for SHA-512 */ const static sha2_word64 sha512_initial_hash_value[8] = { 0x6a09e667f3bcc908ui64, 0xbb67ae8584caa73bui64, 0x3c6ef372fe94f82bui64, 0xa54ff53a5f1d36f1ui64, 0x510e527fade682d1ui64, 0x9b05688c2b3e6c1fui64, 0x1f83d9abfb41bd6bui64, 0x5be0cd19137e2179ui64 }; #else /* defined(WIN32) */ /* Hash constant words K for SHA-384 and SHA-512: */ static const sha2_word64 K512[80] = { 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL }; /* Initial hash value H for SHA-384 */ static const sha2_word64 sha384_initial_hash_value[8] = { 0xcbbb9d5dc1059ed8ULL, 0x629a292a367cd507ULL, 0x9159015a3070dd17ULL, 0x152fecd8f70e5939ULL, 0x67332667ffc00b31ULL, 0x8eb44a8768581511ULL, 0xdb0c2e0d64f98fa7ULL, 0x47b5481dbefa4fa4ULL }; /* Initial hash value H for SHA-512 */ static const sha2_word64 sha512_initial_hash_value[8] = { 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL }; #endif /* defined(WIN32) */ /* * Constant used by SHA256/384/512_End() functions for converting the * digest to a readable hexadecimal character string: */ static const char *sha2_hex_digits = "0123456789abcdef"; /*** SHA-256: *********************************************************/ void SHA256_Init(SHA256_CTX* context) { if (context == (SHA256_CTX*)0) { return; } MEMCPY_BCOPY(context->state, sha256_initial_hash_value, SHA256_DIGEST_LENGTH); MEMSET_BZERO(context->buffer, SHA256_BLOCK_LENGTH); context->bitcount = 0; } #ifdef SHA2_UNROLL_TRANSFORM /* Unrolled SHA-256 round macros: */ #if BYTE_ORDER == LITTLE_ENDIAN #define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ REVERSE32(*data++, W256[j]); \ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ K256[j] + W256[j]; \ (d) += T1; \ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ j++ #else /* BYTE_ORDER == LITTLE_ENDIAN */ #define ROUND256_0_TO_15(a,b,c,d,e,f,g,h) \ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + \ K256[j] + (W256[j] = *data++); \ (d) += T1; \ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ j++ #endif /* BYTE_ORDER == LITTLE_ENDIAN */ #define ROUND256(a,b,c,d,e,f,g,h) \ s0 = W256[(j+1)&0x0f]; \ s0 = sigma0_256(s0); \ s1 = W256[(j+14)&0x0f]; \ s1 = sigma1_256(s1); \ T1 = (h) + Sigma1_256(e) + Ch((e), (f), (g)) + K256[j] + \ (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); \ (d) += T1; \ (h) = T1 + Sigma0_256(a) + Maj((a), (b), (c)); \ j++ void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { sha2_word32 a, b, c, d, e, f, g, h, s0, s1; sha2_word32 T1, *W256; int j; W256 = (sha2_word32*)context->buffer; /* Initialize registers with the prev. intermediate value */ a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7]; j = 0; do { /* Rounds 0 to 15 (unrolled): */ ROUND256_0_TO_15(a,b,c,d,e,f,g,h); ROUND256_0_TO_15(h,a,b,c,d,e,f,g); ROUND256_0_TO_15(g,h,a,b,c,d,e,f); ROUND256_0_TO_15(f,g,h,a,b,c,d,e); ROUND256_0_TO_15(e,f,g,h,a,b,c,d); ROUND256_0_TO_15(d,e,f,g,h,a,b,c); ROUND256_0_TO_15(c,d,e,f,g,h,a,b); ROUND256_0_TO_15(b,c,d,e,f,g,h,a); } while (j < 16); /* Now for the remaining rounds to 64: */ do { ROUND256(a,b,c,d,e,f,g,h); ROUND256(h,a,b,c,d,e,f,g); ROUND256(g,h,a,b,c,d,e,f); ROUND256(f,g,h,a,b,c,d,e); ROUND256(e,f,g,h,a,b,c,d); ROUND256(d,e,f,g,h,a,b,c); ROUND256(c,d,e,f,g,h,a,b); ROUND256(b,c,d,e,f,g,h,a); } while (j < 64); /* Compute the current intermediate hash value */ context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h; /* Clean up */ a = b = c = d = e = f = g = h = T1 = 0; } #else /* SHA2_UNROLL_TRANSFORM */ void SHA256_Transform(SHA256_CTX* context, const sha2_word32* data) { sha2_word32 a, b, c, d, e, f, g, h, s0, s1; sha2_word32 T1, T2, *W256; int j; W256 = (sha2_word32*)context->buffer; /* Initialize registers with the prev. intermediate value */ a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7]; j = 0; do { #if BYTE_ORDER == LITTLE_ENDIAN /* Copy data while converting to host byte order */ REVERSE32(*data++,W256[j]); /* Apply the SHA-256 compression function to update a..h */ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + W256[j]; #else /* BYTE_ORDER == LITTLE_ENDIAN */ /* Apply the SHA-256 compression function to update a..h with copy */ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j] = *data++); #endif /* BYTE_ORDER == LITTLE_ENDIAN */ T2 = Sigma0_256(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; j++; } while (j < 16); do { /* Part of the message block expansion: */ s0 = W256[(j+1)&0x0f]; s0 = sigma0_256(s0); s1 = W256[(j+14)&0x0f]; s1 = sigma1_256(s1); /* Apply the SHA-256 compression function to update a..h */ T1 = h + Sigma1_256(e) + Ch(e, f, g) + K256[j] + (W256[j&0x0f] += s1 + W256[(j+9)&0x0f] + s0); T2 = Sigma0_256(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; j++; } while (j < 64); /* Compute the current intermediate hash value */ context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h; /* Clean up */ a = b = c = d = e = f = g = h = T1 = T2 = 0; } #endif /* SHA2_UNROLL_TRANSFORM */ void SHA256_Update(SHA256_CTX* context, const sha2_byte *data, size_t len) { unsigned int freespace, usedspace; if (len == 0) { /* Calling with no data is valid - we do nothing */ return; } /* Sanity check: */ assert(context != (SHA256_CTX*)0 && data != (sha2_byte*)0); usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; if (usedspace > 0) { /* Calculate how much free space is available in the buffer */ freespace = SHA256_BLOCK_LENGTH - usedspace; if (len >= freespace) { /* Fill the buffer completely and process it */ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace); context->bitcount += freespace << 3; len -= freespace; data += freespace; SHA256_Transform(context, (sha2_word32*)context->buffer); } else { /* The buffer is not yet full */ MEMCPY_BCOPY(&context->buffer[usedspace], data, len); context->bitcount += len << 3; /* Clean up: */ usedspace = freespace = 0; return; } } while (len >= SHA256_BLOCK_LENGTH) { /* Process as many complete blocks as we can */ SHA256_Transform(context, (sha2_word32*)data); context->bitcount += SHA256_BLOCK_LENGTH << 3; len -= SHA256_BLOCK_LENGTH; data += SHA256_BLOCK_LENGTH; } if (len > 0) { /* There's left-overs, so save 'em */ MEMCPY_BCOPY(context->buffer, data, len); context->bitcount += len << 3; } /* Clean up: */ usedspace = freespace = 0; } void SHA256_Final(sha2_byte digest[], SHA256_CTX* context) { sha2_word32 *d = (sha2_word32*)digest; unsigned int usedspace; /* Sanity check: */ assert(context != (SHA256_CTX*)0); /* If no digest buffer is passed, we don't bother doing this: */ if (digest != (sha2_byte*)0) { usedspace = (context->bitcount >> 3) % SHA256_BLOCK_LENGTH; #if BYTE_ORDER == LITTLE_ENDIAN /* Convert FROM host byte order */ REVERSE64(context->bitcount,context->bitcount); #endif if (usedspace > 0) { /* Begin padding with a 1 bit: */ context->buffer[usedspace++] = 0x80; if (usedspace <= SHA256_SHORT_BLOCK_LENGTH) { /* Set-up for the last transform: */ MEMSET_BZERO(&context->buffer[usedspace], SHA256_SHORT_BLOCK_LENGTH - usedspace); } else { if (usedspace < SHA256_BLOCK_LENGTH) { MEMSET_BZERO(&context->buffer[usedspace], SHA256_BLOCK_LENGTH - usedspace); } /* Do second-to-last transform: */ SHA256_Transform(context, (sha2_word32*)context->buffer); /* And set-up for the last transform: */ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); } } else { /* Set-up for the last transform: */ MEMSET_BZERO(context->buffer, SHA256_SHORT_BLOCK_LENGTH); /* Begin padding with a 1 bit: */ *context->buffer = 0x80; } /* Set the bit count: */ *(sha2_word64*)&context->buffer[SHA256_SHORT_BLOCK_LENGTH] = context->bitcount; /* Final transform: */ SHA256_Transform(context, (sha2_word32*)context->buffer); #if BYTE_ORDER == LITTLE_ENDIAN { /* Convert TO host byte order */ int j; for (j = 0; j < 8; j++) { REVERSE32(context->state[j],context->state[j]); *d++ = context->state[j]; } } #else MEMCPY_BCOPY(d, context->state, SHA256_DIGEST_LENGTH); #endif } /* Clean up state data: */ MEMSET_BZERO(context, sizeof(SHA256_CTX)); usedspace = 0; } char *SHA256_End(SHA256_CTX* context, char buffer[]) { sha2_byte digest[SHA256_DIGEST_LENGTH], *d = digest; int i; /* Sanity check: */ assert(context != (SHA256_CTX*)0); if (buffer != (char*)0) { SHA256_Final(digest, context); for (i = 0; i < SHA256_DIGEST_LENGTH; i++) { *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; *buffer++ = sha2_hex_digits[*d & 0x0f]; d++; } *buffer = (char)0; } else { MEMSET_BZERO(context, sizeof(SHA256_CTX)); } MEMSET_BZERO(digest, SHA256_DIGEST_LENGTH); return buffer; } char* SHA256_Data(const sha2_byte* data, size_t len, char digest[SHA256_DIGEST_STRING_LENGTH]) { SHA256_CTX context; SHA256_Init(&context); SHA256_Update(&context, data, len); return SHA256_End(&context, digest); } /*** SHA-512: *********************************************************/ void SHA512_Init(SHA512_CTX* context) { if (context == (SHA512_CTX*)0) { return; } MEMCPY_BCOPY(context->state, sha512_initial_hash_value, SHA512_DIGEST_LENGTH); MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH); context->bitcount[0] = context->bitcount[1] = 0; } #ifdef SHA2_UNROLL_TRANSFORM /* Unrolled SHA-512 round macros: */ #if BYTE_ORDER == LITTLE_ENDIAN #define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ REVERSE64(*data++, W512[j]); \ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ K512[j] + W512[j]; \ (d) += T1, \ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)), \ j++ #else /* BYTE_ORDER == LITTLE_ENDIAN */ #define ROUND512_0_TO_15(a,b,c,d,e,f,g,h) \ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + \ K512[j] + (W512[j] = *data++); \ (d) += T1; \ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ j++ #endif /* BYTE_ORDER == LITTLE_ENDIAN */ #define ROUND512(a,b,c,d,e,f,g,h) \ s0 = W512[(j+1)&0x0f]; \ s0 = sigma0_512(s0); \ s1 = W512[(j+14)&0x0f]; \ s1 = sigma1_512(s1); \ T1 = (h) + Sigma1_512(e) + Ch((e), (f), (g)) + K512[j] + \ (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); \ (d) += T1; \ (h) = T1 + Sigma0_512(a) + Maj((a), (b), (c)); \ j++ void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) { sha2_word64 a, b, c, d, e, f, g, h, s0, s1; sha2_word64 T1, *W512 = (sha2_word64*)context->buffer; int j; /* Initialize registers with the prev. intermediate value */ a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7]; j = 0; do { ROUND512_0_TO_15(a,b,c,d,e,f,g,h); ROUND512_0_TO_15(h,a,b,c,d,e,f,g); ROUND512_0_TO_15(g,h,a,b,c,d,e,f); ROUND512_0_TO_15(f,g,h,a,b,c,d,e); ROUND512_0_TO_15(e,f,g,h,a,b,c,d); ROUND512_0_TO_15(d,e,f,g,h,a,b,c); ROUND512_0_TO_15(c,d,e,f,g,h,a,b); ROUND512_0_TO_15(b,c,d,e,f,g,h,a); } while (j < 16); /* Now for the remaining rounds up to 79: */ do { ROUND512(a,b,c,d,e,f,g,h); ROUND512(h,a,b,c,d,e,f,g); ROUND512(g,h,a,b,c,d,e,f); ROUND512(f,g,h,a,b,c,d,e); ROUND512(e,f,g,h,a,b,c,d); ROUND512(d,e,f,g,h,a,b,c); ROUND512(c,d,e,f,g,h,a,b); ROUND512(b,c,d,e,f,g,h,a); } while (j < 80); /* Compute the current intermediate hash value */ context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h; /* Clean up */ a = b = c = d = e = f = g = h = T1 = 0; } #else /* SHA2_UNROLL_TRANSFORM */ void SHA512_Transform(SHA512_CTX* context, const sha2_word64* data) { sha2_word64 a, b, c, d, e, f, g, h, s0, s1; sha2_word64 T1, T2, *W512 = (sha2_word64*)context->buffer; int j; /* Initialize registers with the prev. intermediate value */ a = context->state[0]; b = context->state[1]; c = context->state[2]; d = context->state[3]; e = context->state[4]; f = context->state[5]; g = context->state[6]; h = context->state[7]; j = 0; do { #if BYTE_ORDER == LITTLE_ENDIAN /* Convert TO host byte order */ REVERSE64(*data++, W512[j]); /* Apply the SHA-512 compression function to update a..h */ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + W512[j]; #else /* BYTE_ORDER == LITTLE_ENDIAN */ /* Apply the SHA-512 compression function to update a..h with copy */ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j] = *data++); #endif /* BYTE_ORDER == LITTLE_ENDIAN */ T2 = Sigma0_512(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; j++; } while (j < 16); do { /* Part of the message block expansion: */ s0 = W512[(j+1)&0x0f]; s0 = sigma0_512(s0); s1 = W512[(j+14)&0x0f]; s1 = sigma1_512(s1); /* Apply the SHA-512 compression function to update a..h */ T1 = h + Sigma1_512(e) + Ch(e, f, g) + K512[j] + (W512[j&0x0f] += s1 + W512[(j+9)&0x0f] + s0); T2 = Sigma0_512(a) + Maj(a, b, c); h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a; a = T1 + T2; j++; } while (j < 80); /* Compute the current intermediate hash value */ context->state[0] += a; context->state[1] += b; context->state[2] += c; context->state[3] += d; context->state[4] += e; context->state[5] += f; context->state[6] += g; context->state[7] += h; /* Clean up */ a = b = c = d = e = f = g = h = T1 = T2 = 0; } #endif /* SHA2_UNROLL_TRANSFORM */ void SHA512_Update(SHA512_CTX* context, const sha2_byte *data, size_t len) { unsigned int freespace, usedspace; if (len == 0) { /* Calling with no data is valid - we do nothing */ return; } /* Sanity check: */ assert(context != (SHA512_CTX*)0 && data != (sha2_byte*)0); usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; if (usedspace > 0) { /* Calculate how much free space is available in the buffer */ freespace = SHA512_BLOCK_LENGTH - usedspace; if (len >= freespace) { /* Fill the buffer completely and process it */ MEMCPY_BCOPY(&context->buffer[usedspace], data, freespace); ADDINC128(context->bitcount, freespace << 3); len -= freespace; data += freespace; SHA512_Transform(context, (sha2_word64*)context->buffer); } else { /* The buffer is not yet full */ MEMCPY_BCOPY(&context->buffer[usedspace], data, len); ADDINC128(context->bitcount, len << 3); /* Clean up: */ usedspace = freespace = 0; return; } } while (len >= SHA512_BLOCK_LENGTH) { /* Process as many complete blocks as we can */ SHA512_Transform(context, (sha2_word64*)data); ADDINC128(context->bitcount, SHA512_BLOCK_LENGTH << 3); len -= SHA512_BLOCK_LENGTH; data += SHA512_BLOCK_LENGTH; } if (len > 0) { /* There's left-overs, so save 'em */ MEMCPY_BCOPY(context->buffer, data, len); ADDINC128(context->bitcount, len << 3); } /* Clean up: */ usedspace = freespace = 0; } void SHA512_Last(SHA512_CTX* context) { unsigned int usedspace; usedspace = (context->bitcount[0] >> 3) % SHA512_BLOCK_LENGTH; #if BYTE_ORDER == LITTLE_ENDIAN /* Convert FROM host byte order */ REVERSE64(context->bitcount[0],context->bitcount[0]); REVERSE64(context->bitcount[1],context->bitcount[1]); #endif if (usedspace > 0) { /* Begin padding with a 1 bit: */ context->buffer[usedspace++] = 0x80; if (usedspace <= SHA512_SHORT_BLOCK_LENGTH) { /* Set-up for the last transform: */ MEMSET_BZERO(&context->buffer[usedspace], SHA512_SHORT_BLOCK_LENGTH - usedspace); } else { if (usedspace < SHA512_BLOCK_LENGTH) { MEMSET_BZERO(&context->buffer[usedspace], SHA512_BLOCK_LENGTH - usedspace); } /* Do second-to-last transform: */ SHA512_Transform(context, (sha2_word64*)context->buffer); /* And set-up for the last transform: */ MEMSET_BZERO(context->buffer, SHA512_BLOCK_LENGTH - 2); } } else { /* Prepare for final transform: */ MEMSET_BZERO(context->buffer, SHA512_SHORT_BLOCK_LENGTH); /* Begin padding with a 1 bit: */ *context->buffer = 0x80; } /* Store the length of input data (in bits): */ *(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH] = context->bitcount[1]; *(sha2_word64*)&context->buffer[SHA512_SHORT_BLOCK_LENGTH+8] = context->bitcount[0]; /* Final transform: */ SHA512_Transform(context, (sha2_word64*)context->buffer); } void SHA512_Final(sha2_byte digest[], SHA512_CTX* context) { sha2_word64 *d = (sha2_word64*)digest; /* Sanity check: */ assert(context != (SHA512_CTX*)0); /* If no digest buffer is passed, we don't bother doing this: */ if (digest != (sha2_byte*)0) { SHA512_Last(context); /* Save the hash data for output: */ #if BYTE_ORDER == LITTLE_ENDIAN { /* Convert TO host byte order */ int j; for (j = 0; j < 8; j++) { REVERSE64(context->state[j],context->state[j]); *d++ = context->state[j]; } } #else MEMCPY_BCOPY(d, context->state, SHA512_DIGEST_LENGTH); #endif } /* Zero out state data */ MEMSET_BZERO(context, sizeof(SHA512_CTX)); } char *SHA512_End(SHA512_CTX* context, char buffer[]) { sha2_byte digest[SHA512_DIGEST_LENGTH], *d = digest; int i; /* Sanity check: */ assert(context != (SHA512_CTX*)0); if (buffer != (char*)0) { SHA512_Final(digest, context); for (i = 0; i < SHA512_DIGEST_LENGTH; i++) { *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; *buffer++ = sha2_hex_digits[*d & 0x0f]; d++; } *buffer = (char)0; } else { MEMSET_BZERO(context, sizeof(SHA512_CTX)); } MEMSET_BZERO(digest, SHA512_DIGEST_LENGTH); return buffer; } char* SHA512_Data(const sha2_byte* data, size_t len, char digest[SHA512_DIGEST_STRING_LENGTH]) { SHA512_CTX context; SHA512_Init(&context); SHA512_Update(&context, data, len); return SHA512_End(&context, digest); } /*** SHA-384: *********************************************************/ void SHA384_Init(SHA384_CTX* context) { if (context == (SHA384_CTX*)0) { return; } MEMCPY_BCOPY(context->state, sha384_initial_hash_value, SHA512_DIGEST_LENGTH); MEMSET_BZERO(context->buffer, SHA384_BLOCK_LENGTH); context->bitcount[0] = context->bitcount[1] = 0; } void SHA384_Update(SHA384_CTX* context, const sha2_byte* data, size_t len) { SHA512_Update((SHA512_CTX*)context, data, len); } void SHA384_Final(sha2_byte digest[], SHA384_CTX* context) { sha2_word64 *d = (sha2_word64*)digest; /* Sanity check: */ assert(context != (SHA384_CTX*)0); /* If no digest buffer is passed, we don't bother doing this: */ if (digest != (sha2_byte*)0) { SHA512_Last((SHA512_CTX*)context); /* Save the hash data for output: */ #if BYTE_ORDER == LITTLE_ENDIAN { /* Convert TO host byte order */ int j; for (j = 0; j < 6; j++) { REVERSE64(context->state[j],context->state[j]); *d++ = context->state[j]; } } #else MEMCPY_BCOPY(d, context->state, SHA384_DIGEST_LENGTH); #endif } /* Zero out state data */ MEMSET_BZERO(context, sizeof(SHA384_CTX)); } char *SHA384_End(SHA384_CTX* context, char buffer[]) { sha2_byte digest[SHA384_DIGEST_LENGTH], *d = digest; int i; /* Sanity check: */ assert(context != (SHA384_CTX*)0); if (buffer != (char*)0) { SHA384_Final(digest, context); for (i = 0; i < SHA384_DIGEST_LENGTH; i++) { *buffer++ = sha2_hex_digits[(*d & 0xf0) >> 4]; *buffer++ = sha2_hex_digits[*d & 0x0f]; d++; } *buffer = (char)0; } else { MEMSET_BZERO(context, sizeof(SHA384_CTX)); } MEMSET_BZERO(digest, SHA384_DIGEST_LENGTH); return buffer; } char* SHA384_Data(const sha2_byte* data, size_t len, char digest[SHA384_DIGEST_STRING_LENGTH]) { SHA384_CTX context; SHA384_Init(&context); SHA384_Update(&context, data, len); return SHA384_End(&context, digest); } #endif /* !HAVE_OPENSSL_SHA */ snort-2.9.20/src/sfutil/sf_vartable.c0000644000175000017500000002701514241077254015635 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Adam Keeton * sf_vartable.c * 11/17/06 * * Library for managing IP variables. */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sf_vartable.h" #include "util.h" vartable_t * sfvt_alloc_table(void) { vartable_t *table = (vartable_t *)SnortAlloc(sizeof(vartable_t)); /* ID for recognition of variables with different name, but same content * Start at 1, so a value of zero indicates not set. * This value should be incremented for each variable that hasn't been * identified as an alias of another variable */ table->id = 1; return table; } static char * sfvt_expand_value(vartable_t *table, char *value) { char *ptr, *end, *tmp, *ret = NULL; int retlen = 0, retsize = 0; int escaped = 0; if ((table == NULL) || (value == NULL)) return NULL; if (strlen(value) == 0) return NULL; ptr = value; end = value + strlen(value); while ((ptr < end) && isspace((int)*ptr)) ptr++; while ((end > ptr) && isspace((int)*(end-1))) end--; if (ptr == end) return NULL; tmp = SnortStrndup(ptr, end-ptr); if (tmp == NULL) return NULL; /* Start by allocating the length of the value */ retsize = strlen(value) + 1; ret = (char *)SnortAlloc(retsize); ptr = tmp; end = tmp + strlen(tmp); while (ptr < end) { if (!escaped && (*ptr == '$')) { char *varstart, *vartmp; int parens = 0; sfip_var_t *ipvar; ptr++; if (ptr >= end) goto sfvt_expand_value_error; if (*ptr == '(') { ptr++; parens = 1; } varstart = ptr; while (ptr < end) { if (parens) { if (*ptr == ')') { break; } } else if (!isalnum((int)*ptr) && (*ptr != '_')) { break; } ptr++; } if (varstart == ptr) goto sfvt_expand_value_error; vartmp = SnortStrndup(varstart, ptr - varstart); if (vartmp == NULL) goto sfvt_expand_value_error; ipvar = sfvt_lookup_var(table, vartmp); free(vartmp); if (ipvar == NULL) goto sfvt_expand_value_error; if (ipvar->value != NULL) { if ((int)(retlen + strlen(ipvar->value)) >= retsize) { char *tmpalloc; retsize = retlen + strlen(ipvar->value) + (end - ptr) + 1; tmpalloc = (char *)SnortAlloc(retsize); memcpy(tmpalloc, ret, retlen); memcpy(tmpalloc + retlen, ipvar->value, strlen(ipvar->value)); free(ret); retlen += strlen(ipvar->value); ret = tmpalloc; } } if (parens) ptr++; continue; } if (*ptr == '\\') escaped = 1; else escaped = 0; ret[retlen++] = *ptr; ptr++; } free(tmp); if ((retlen + 1) < retsize) { char *tmpalloc = (char *)SnortAlloc(retlen + 1); memcpy(tmpalloc, ret, retlen); free(ret); ret = tmpalloc; } ret[retlen] = 0; return ret; sfvt_expand_value_error: free(ret); free(tmp); return NULL; } // XXX this implementation is just used to support // Snort's underlying implementation better SFIP_RET sfvt_define(vartable_t *table, char *name, char *value) { char *buf; int len; sfip_var_t *ipret = NULL; SFIP_RET ret; if(!name || !value) return SFIP_ARG_ERR; len = strlen(name) + strlen(value) + 2; if((buf = (char*)malloc(len)) == NULL) { return SFIP_FAILURE; } SnortSnprintf(buf, len, "%s %s", name, value); ret = sfvt_add_str(table, buf, &ipret); if ((ret == SFIP_SUCCESS) || (ret == SFIP_DUPLICATE)) ipret->value = sfvt_expand_value(table, value); free(buf); return ret; } /* Adds the variable described by "str" to the table "table" */ SFIP_RET sfvt_add_str(vartable_t *table, char *str, sfip_var_t **ipret) { sfip_var_t *var; sfip_var_t *swp; sfip_var_t *p; int ret; SFIP_RET status; if(!table || !str || !ipret) return SFIP_FAILURE; /* Creates the variable */ if( (var = sfvar_alloc(table, str, &status)) == NULL ) { return status; } /* If this is an alias of another var, id will be set */ if (var->id == 0) var->id = table->id++; *ipret = var; /* Insertion sort */ if(!table->head) { table->head = var; return SFIP_SUCCESS; } if((ret = strcmp(var->name, table->head->name)) < 0) { var->next = table->head; table->head = var; return SFIP_SUCCESS; } /* Redefinition */ else if(ret == 0) { var->next = table->head->next; sfvar_free(table->head); table->head = var; return SFIP_DUPLICATE; } /* The loop below checks table->head->next->name in the first iteration. * Make sure there is a table->head->next first */ if(!table->head->next) { table->head->next = var; return SFIP_SUCCESS; } else if(!strcmp(var->name, table->head->next->name)) { var->next = table->head->next->next; sfvar_free(table->head->next); table->head->next = var; return SFIP_DUPLICATE; } for(p = table->head; p->next; p=p->next) { if((ret = strcmp(var->name, p->next->name)) < 0) { swp = p->next; p->next = var; var->next = swp; return SFIP_SUCCESS; } /* Redefinition */ else if(ret == 0) { var->next = p->next->next; sfvar_free(p->next); p->next = var; return SFIP_DUPLICATE; } } p->next = var; return SFIP_SUCCESS; } /* Adds the variable described by "src" to the variable "dst", * using the vartable for looking variables used within "src" */ SFIP_RET sfvt_add_to_var(vartable_t *table, sfip_var_t *dst, char *src) { SFIP_RET ret; if(!table || !dst || !src) return SFIP_ARG_ERR; if((ret = sfvar_parse_iplist(table, dst, src, 0)) == SFIP_SUCCESS) return sfvar_validate(dst); return ret; } /* Looks up a variable from the table by the variable's name */ sfip_var_t *sfvt_lookup_var(vartable_t *table, char *name) { sfip_var_t *p; int len; char *end; if(!table || !name) return NULL; if(*name == '$') name++; /* XXX should I assume there will be trailing garbage or * should I automatically find where the variable ends? */ for(end=name; *end && !isspace((int)*end) && *end != '\\' && *end != ']'; end++) ; len = end - name; for(p=table->head; len && p; p=p->next) { int name_len = strlen(p->name); if((len == name_len) && !strncmp(p->name, name, len)) return p; } return NULL; } void sfvt_free_table(vartable_t *table) { sfip_var_t *p, *tmp; if (!table) return; p = table->head; while (p) { tmp = p->next; sfvar_free(p); p = tmp; } free(table); } /* Prints a table's contents */ void sfip_print_table(FILE *f, vartable_t *table) { sfip_var_t *p; if(!f || !table) return; fprintf(f, "(Table %p)\n", (void*)table); for(p=table->head; p; p=p->next) { sfvar_print_to_file(f, p); puts(""); } } //#define TESTER #ifdef TESTER int failures = 0; #define TEST(x) if(x) printf("\tSuccess: line %d\n", __LINE__);\ else { printf("\tFAILURE: line %d\n", __LINE__); failures++; } int main() { vartable_t *table; sfip_var_t *var; sfcidr_t *ip; puts("********************************************************************"); puts("Testing variable table parsing:"); table = sfvt_alloc_table(); /* These are all valid */ TEST(sfvt_add_str(table, "foo [ 1.2.0.0/16, ffff:dead:beef::0 ] ", &var) == SFIP_SUCCESS); TEST(sfvt_add_str(table, " goo [ ffff:dead:beef::0 ] ", &var) == SFIP_SUCCESS); TEST(sfvt_add_str(table, " moo [ any ] ", &var) == SFIP_SUCCESS); /* Test variable redefine */ TEST(sfvt_add_str(table, " goo [ 192.168.0.1, 192.168.0.2, 192.168.255.0 255.255.248.0 ] ", &var) == SFIP_DUPLICATE); /* These should fail since it's a variable name with bogus arguments */ TEST(sfvt_add_str(table, " phlegm ", &var) == SFIP_FAILURE); TEST(sfvt_add_str(table, " phlegm [", &var) == SFIP_FAILURE); TEST(sfvt_add_str(table, " phlegm [ ", &var) == SFIP_FAILURE); TEST(sfvt_add_str(table, " phlegm [sdfg ", &var) == SFIP_FAILURE); TEST(sfvt_add_str(table, " phlegm [ sdfg, 12.123.1.4.5 }", &var) == SFIP_FAILURE); TEST(sfvt_add_str(table, " [ 12.123.1.4.5 ]", &var) == SFIP_FAILURE); TEST(sfvt_add_str(table, NULL, &var) == SFIP_FAILURE); TEST(sfvt_add_str(table, "", &var) == SFIP_FAILURE); puts(""); puts("********************************************************************"); puts("Expansions:"); /* Note: used this way leaks memory */ printf("\t%s\n", sfvt_alloc_expanded(table, "$foo")); printf("\t%s\n", sfvt_alloc_expanded(table, "goo $goo sf sfasdfasdf $moo")); printf("\t%s\n", sfvt_alloc_expanded(table, " ssdf $moo $moo asdf $fooadff $foo ")); printf("\t%s\n", sfvt_alloc_expanded(table, " ssdf $moo $moo\\sdf $foo adff")); puts(""); puts("********************************************************************"); puts("Containment checks:"); var = sfvt_lookup(table, "goo"); ip = sfip_alloc("192.168.248.255"); TEST(sfvar_ip_in(var, ip) == SFIP_SUCCESS); /* Check against the 'any' variable */ var = sfvt_lookup_var(table, "moo"); TEST(sfvar_ip_in(var, ip) == SFIP_SUCCESS); /* Verify it's not in this variable */ var = sfvt_lookup_var(table, "foo"); TEST(sfvar_ip_in(var, ip) == SFIP_FAILURE); /* Check boundary cases */ var = sfvt_lookup_var(table, "goo"); free_ip(ip); ip = sfip_alloc_str("192.168.0.3"); TEST(sfvar_ip_in(var, ip) == SFIP_FAILURE); free_ip(ip); ip = sfip_alloc_str("192.168.0.2"); TEST(sfvar_ip_in(var, ip) == SFIP_SUCCESS); puts(""); puts("********************************************************************"); printf("\n\tTotal Failures: %d\n", failures); return 0; } #endif snort-2.9.20/src/sfutil/getopt_long.c0000644000175000017500000006104214241077216015662 0ustar apoapo/* Getopt for GNU. NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu before changing it! Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it it under the terms of the GNU General Public License Version 2 as published by the Free Software Foundation. You may not use, modify or distribute this program under any other version of the GNU General Public License. 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. */ /* This tells Alpha OSF/1 not to define a getopt prototype in . Ditto for AIX 3.2 and . */ #ifndef _NO_PROTO #define _NO_PROTO #endif #include #ifdef HAVE_CONFIG_H #if defined (emacs) || defined (CONFIG_BROKETS) /* We use instead of "config.h" so that a compilation using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h (which it would do because it found this file in $srcdir). */ #include #else #include "config.h" #endif #endif #ifndef __STDC__ /* This is a separate conditional since some stdc systems reject `defined (const)'. */ #ifndef const #define const #endif #endif #ifdef HAVE_STRING_H #include #endif /* Comment out all this code if we are using the GNU C Library, and are not actually compiling the library itself. This code is part of the GNU C Library, but also included in many other GNU distributions. Compiling and linking in this code is a waste when using the GNU C library (especially if it is a shared library). Rather than having every GNU program understand `configure --with-gnu-libc' and omit the object files, it is simpler to just do this in the source for each such file. */ #if defined (_LIBC) || !defined (__GNU_LIBRARY__) /* This needs to come after some library #include to get __GNU_LIBRARY__ defined. */ #ifdef __GNU_LIBRARY__ /* Don't include stdlib.h for non-GNU C libraries because some of them contain conflicting prototypes for getopt. */ #include #endif /* GNU C library. */ /* This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves differently for the user, since it allows the user to intersperse the options with the other arguments. As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options precede everything else. Thus all application programs are extended to handle flexible argument order. Setting the environment variable POSIXLY_CORRECT disables permutation. Then the behavior is completely standard. GNU application programs can use a third alternative mode in which they can distinguish the relative order of options and other arguments. */ #include "getopt1.h" /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ char *optarg = NULL; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ /* XXX 1003.2 says this must be 1 before any call. */ int optind = 0; /* The next char to be scanned in the option-element in which the last option character we returned was found. This allows us to pick up the scan where we left off. If this is zero, or a null string, it means resume the scan by advancing to the next ARGV-element. */ static char *nextchar; /* Callers store zero here to inhibit the error message for unrecognized options. */ int opterr = 1; /* Set to an option character which was unrecognized. This must be initialized on some systems to avoid linking in the system's own getopt implementation. */ int optopt = '?'; /* Describe how to deal with options that follow non-option ARGV-elements. If the caller did not specify anything, the default is REQUIRE_ORDER if the environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. REQUIRE_ORDER means don't recognize them as options; stop option processing when the first non-option is seen. This is what Unix does. This mode of operation is selected by either setting the environment variable POSIXLY_CORRECT, or using `+' as the first character of the list of option characters. PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all the non-options are at the end. This allows options to be given in any order, even with programs that were not written to expect this. RETURN_IN_ORDER is an option available to programs that were written to expect options and other ARGV-elements in any order and that care about the ordering of the two. We describe each non-option ARGV-element as if it were the argument of an option with character code 1. Using `-' as the first character of the list of option characters selects this mode of operation. The special argument `--' forces an end of option-scanning regardless of the value of `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with `optind' != ARGC. */ static enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering; /* Value of POSIXLY_CORRECT environment variable. */ static char *posixly_correct; #ifdef __GNU_LIBRARY__ /* We want to avoid inclusion of string.h with non-GNU libraries because there are many ways it can cause trouble. On some systems, it contains special magic macros that don't work in GCC. */ #include #define my_index strchr #else /* Avoid depending on library functions or files whose names are inconsistent. */ #if !defined(_WIN32) char *getenv (); #endif static char * my_index (const char *str, int chr) { while (*str) { if (*str == chr) return (char *) str; str++; } return 0; } /* If using GCC, we can safely declare strlen this way. If not using GCC, it is ok not to declare it. */ #ifdef __GNUC__ /* Note that Motorola Delta 68k R3V7 comes with GCC but not stddef.h. That was relevant to code that was here before. */ #ifndef __STDC__ /* gcc with -traditional declares the built-in strlen to return int, and has done so at least since version 2.4.5. -- rms. */ extern int strlen (const char *); #endif /* not __STDC__ */ #endif /* __GNUC__ */ #endif /* not __GNU_LIBRARY__ */ /* Handle permutation of arguments. */ /* Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. */ static int first_nonopt; static int last_nonopt; /* Exchange two adjacent subsequences of ARGV. One subsequence is elements [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The other is elements [last_nonopt,optind), which contains all the options processed since those non-options were skipped. `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the non-options in ARGV after they are moved. */ static void exchange (char **argv) { int bottom = first_nonopt; int middle = last_nonopt; int top = optind; char *tem; /* Exchange the shorter segment with the far end of the longer segment. That puts the shorter segment into the right place. It leaves the longer segment in the right place overall, but it consists of two parts that need to be swapped next. */ while (top > middle && middle > bottom) { if (top - middle > middle - bottom) { /* Bottom segment is the short one. */ int len = middle - bottom; register int i; /* Swap it with the top part of the top segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[top - (middle - bottom) + i]; argv[top - (middle - bottom) + i] = tem; } /* Exclude the moved bottom segment from further swapping. */ top -= len; } else { /* Top segment is the short one. */ int len = top - middle; register int i; /* Swap it with the bottom part of the bottom segment. */ for (i = 0; i < len; i++) { tem = argv[bottom + i]; argv[bottom + i] = argv[middle + i]; argv[middle + i] = tem; } /* Exclude the moved top segment from further swapping. */ bottom += len; } } /* Update records for the slots the non-options now occupy. */ first_nonopt += (optind - last_nonopt); last_nonopt = optind; } /* Initialize the internal data when the first call is made. */ static const char * _getopt_initialize (const char *optstring) { /* Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the sequence of previously skipped non-option ARGV-elements is empty. */ first_nonopt = last_nonopt = optind = 1; nextchar = NULL; posixly_correct = getenv ("POSIXLY_CORRECT"); /* Determine how to handle the ordering of options and nonoptions. */ if (optstring[0] == '-') { ordering = RETURN_IN_ORDER; ++optstring; } else if (optstring[0] == '+') { ordering = REQUIRE_ORDER; ++optstring; } else if (posixly_correct != NULL) ordering = REQUIRE_ORDER; else ordering = PERMUTE; return optstring; } /* Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option element. The characters of this element (aside from the initial '-') are option characters. If `getopt' is called repeatedly, it returns successively each of the option characters from each of the option elements. If `getopt' finds another option character, it returns that character, updating `optind' and `nextchar' so that the next call to `getopt' can resume the scan with the following option character or ARGV-element. If there are no more option characters, `getopt' returns `EOF'. Then `optind' is the index in ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so that those that are not options now come last.) OPTSTRING is a string containing the legitimate option characters. If an option character is seen that is not listed in OPTSTRING, return '?' after printing an error message. If you set `opterr' to zero, the error message is suppressed but we still return '?'. If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. Two colons mean an option that wants an optional arg; if there is text in the current ARGV-element, it is returned in `optarg', otherwise `optarg' is set to zero. If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the abbreviation is unique or is an exact match for some defined option. If they have an argument, it follows the option name in the same ARGV-element, separated from the option name by a `=', or else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is zero. The elements of ARGV aren't really const, because we permute them. But we pretend they're const in the prototype to be compatible with other systems. LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a long-named option has been found by the most recent call. If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. */ int _getopt_internal (int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) { optarg = NULL; if (optind == 0) optstring = _getopt_initialize (optstring); if (nextchar == NULL || *nextchar == '\0') { /* Advance to the next ARGV-element. */ if (ordering == PERMUTE) { /* If we have just processed some options following some non-options, exchange them so that the options come first. */ if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (last_nonopt != optind) first_nonopt = optind; /* Skip any additional non-options and extend the range of non-options previously skipped. */ while (optind < argc && (argv[optind][0] != '-' || argv[optind][1] == '\0')) optind++; last_nonopt = optind; } /* The special ARGV-element `--' means premature end of options. Skip it like a null option, then exchange with previous non-options as if it were an option, then skip everything else like a non-option. */ if (optind != argc && !strcmp (argv[optind], "--")) { optind++; if (first_nonopt != last_nonopt && last_nonopt != optind) exchange ((char **) argv); else if (first_nonopt == last_nonopt) first_nonopt = optind; last_nonopt = argc; optind = argc; } /* If we have done all the ARGV-elements, stop the scan and back over any non-options that we skipped and permuted. */ if (optind == argc) { /* Set the next-arg-index to point at the non-options that we previously skipped, so the caller will digest them. */ if (first_nonopt != last_nonopt) optind = first_nonopt; return EOF; } /* If we have come to a non-option and did not permute it, either stop the scan or describe it to the caller and pass it by. */ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')) { if (ordering == REQUIRE_ORDER) return EOF; optarg = argv[optind++]; return 1; } /* We have found another option-ARGV-element. Skip the initial punctuation. */ nextchar = (argv[optind] + 1 + (longopts != NULL && argv[optind][1] == '-')); } /* Decode the current option-ARGV-element. */ /* Check whether the ARGV-element is a long option. If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't consider it an abbreviated form of a long option that starts with f. Otherwise there would be no way to give the -f short option. On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg "u". This distinction seems to be the most useful approach. */ if (longopts != NULL && (argv[optind][1] == '-' || (long_only && (argv[optind][2] || !my_index (optstring, argv[optind][1]))))) { char *nameend; const struct option *p; const struct option *pfound = NULL; int exact = 0; int ambig = 0; int indfound = 0; int option_index; for (nameend = nextchar; *nameend && *nameend != '='; nameend++) /* Do nothing. */ ; /* Test all long options for either exact match or abbreviated matches. */ for (p = longopts, option_index = 0; p->name; p++, option_index++) if (!strncmp (p->name, nextchar, nameend - nextchar)) { if (nameend - nextchar == (int) strlen (p->name)) { /* Exact match found. */ pfound = p; indfound = option_index; exact = 1; break; } else if (pfound == NULL) { /* First nonexact match found. */ pfound = p; indfound = option_index; } else /* Second or later nonexact match found. */ ambig = 1; } if (ambig && !exact) { if (opterr) fprintf (stderr, "%s: option `%s' is ambiguous\n", argv[0], argv[optind]); nextchar += strlen (nextchar); optind++; return '?'; } if (pfound != NULL) { option_index = indfound; optind++; if (*nameend) { /* Don't test has_arg with >, because some C compilers don't allow it to be used on enums. */ if (pfound->has_arg) optarg = nameend + 1; else { if (opterr) { if (argv[optind - 1][1] == '-') /* --option */ fprintf (stderr, "%s: option `--%s' doesn't allow an argument\n", argv[0], pfound->name); else /* +option or -option */ fprintf (stderr, "%s: option `%c%s' doesn't allow an argument\n", argv[0], argv[optind - 1][0], pfound->name); } nextchar += strlen (nextchar); return '?'; } } else if (pfound->has_arg == 1) { if (optind < argc) optarg = argv[optind++]; else { if (opterr) fprintf (stderr, "%s: option `%s' requires an argument\n", argv[0], argv[optind - 1]); nextchar += strlen (nextchar); return optstring[0] == ':' ? ':' : '?'; } } nextchar += strlen (nextchar); if (longind != NULL) *longind = option_index; if (pfound->flag) { *(pfound->flag) = pfound->val; return 0; } return pfound->val; } /* Can't find it as a long option. If this is not getopt_long_only, or the option starts with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a short option. */ if (!long_only || argv[optind][1] == '-' || my_index (optstring, *nextchar) == NULL) { if (opterr) { if (argv[optind][1] == '-') /* --option */ fprintf (stderr, "%s: unrecognized option `--%s'\n", argv[0], nextchar); else /* +option or -option */ fprintf (stderr, "%s: unrecognized option `%c%s'\n", argv[0], argv[optind][0], nextchar); } nextchar = (char *) ""; optind++; return '?'; } } /* Look at and handle the next short option-character. */ { char c = *nextchar++; char *temp = my_index (optstring, c); /* Increment `optind' when we start to process its last character. */ if (*nextchar == '\0') ++optind; if (temp == NULL || c == ':') { if (opterr) { if (posixly_correct) /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c); else fprintf (stderr, "%s: invalid option -- %c\n", argv[0], c); } optopt = c; return '?'; } if (temp[1] == ':') { if (temp[2] == ':') { /* This is an option that accepts an argument optionally. */ if (*nextchar != '\0') { optarg = nextchar; optind++; } else optarg = NULL; nextchar = NULL; } else { /* This is an option that requires an argument. */ if (*nextchar != '\0') { optarg = nextchar; /* If we end this ARGV-element by taking the rest as an arg, we must advance to the next element now. */ optind++; } else if (optind == argc) { if (opterr) { /* 1003.2 specifies the format of this message. */ fprintf (stderr, "%s: option requires an argument -- %c\n", argv[0], c); } optopt = c; if (optstring[0] == ':') c = ':'; else c = '?'; } else /* We already incremented `optind' once; increment it again when taking next ARGV-elt as argument. */ optarg = argv[optind++]; nextchar = NULL; } } return c; } } int getopt (int argc, char *const *argv, const char *optstring) { return _getopt_internal (argc, argv, optstring, (const struct option *) 0, (int *) 0, 0); } /* getopt_long and getopt_long_only entry points for GNU getopt. Copyright (C) 1987, 88, 89, 90, 91, 92, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it it under the terms of the GNU General Public License Version 2 as published by the Free Software Foundation. You may not use, modify or distribute this program under any other version of the GNU General Public License. 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. */ int getopt_long (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, argv, options, long_options, opt_index, 0); } /* Like getopt_long, but '-' as well as '--' can indicate a long option. If an option that starts with '-' (not '--') doesn't match a long option, but does match a short option, it is parsed as a short option instead. */ int getopt_long_only (int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) { return _getopt_internal (argc, argv, options, long_options, opt_index, 1); } #endif /* _LIBC or not __GNU_LIBRARY__. */ snort-2.9.20/src/sfutil/sha2.h0000644000175000017500000001416214241077343014205 0ustar apoapo/* * FILE: sha2.h * AUTHOR: Aaron D. Gifford - http://www.aarongifford.com/ * * Copyright (c) 2000-2001, Aaron D. Gifford * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the copyright holder nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTOR(S) ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTOR(S) BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id$ */ #include "config.h" #ifndef HAVE_OPENSSL_SHA #ifndef __SHA2_H__ #define __SHA2_H__ #ifdef __cplusplus extern "C" { #endif /* * Import u_intXX_t size_t type definitions from system headers. You * may need to change this, or define these things yourself in this * file. */ #include #ifdef SHA2_USE_INTTYPES_H #include #endif /* SHA2_USE_INTTYPES_H */ /*** SHA-256/384/512 Various Length Definitions ***********************/ #define SHA256_BLOCK_LENGTH 64 #define SHA256_DIGEST_LENGTH 32 #define SHA256_DIGEST_STRING_LENGTH (SHA256_DIGEST_LENGTH * 2 + 1) #define SHA384_BLOCK_LENGTH 128 #define SHA384_DIGEST_LENGTH 48 #define SHA384_DIGEST_STRING_LENGTH (SHA384_DIGEST_LENGTH * 2 + 1) #define SHA512_BLOCK_LENGTH 128 #define SHA512_DIGEST_LENGTH 64 #define SHA512_DIGEST_STRING_LENGTH (SHA512_DIGEST_LENGTH * 2 + 1) /*** SHA-256/384/512 Context Structures *******************************/ /* NOTE: If your architecture does not define either u_intXX_t types or * uintXX_t (from inttypes.h), you may need to define things by hand * for your system: */ /* * Most BSD systems already define u_intXX_t types, as does Linux. * Some systems, however, like Compaq's Tru64 Unix instead can use * uintXX_t types defined by very recent ANSI C standards and included * in the file: * * #include * * If you choose to use then please define: * * #define SHA2_USE_INTTYPES_H * * Or on the command line during compile: * * cc -DSHA2_USE_INTTYPES_H ... */ #ifdef SHA2_USE_INTTYPES_H typedef struct _SHA256_CTX { uint32_t state[8]; uint64_t bitcount; uint8_t buffer[SHA256_BLOCK_LENGTH]; } SHA256_CTX; typedef struct _SHA512_CTX { uint64_t state[8]; uint64_t bitcount[2]; uint8_t buffer[SHA512_BLOCK_LENGTH]; } SHA512_CTX; #else /* SHA2_USE_INTTYPES_H */ typedef struct _SHA256_CTX { u_int32_t state[8]; u_int64_t bitcount; u_int8_t buffer[SHA256_BLOCK_LENGTH]; } SHA256_CTX; typedef struct _SHA512_CTX { u_int64_t state[8]; u_int64_t bitcount[2]; u_int8_t buffer[SHA512_BLOCK_LENGTH]; } SHA512_CTX; #endif /* SHA2_USE_INTTYPES_H */ typedef SHA512_CTX SHA384_CTX; /*** SHA-256/384/512 Function Prototypes ******************************/ #ifndef NOPROTO #ifdef SHA2_USE_INTTYPES_H void SHA256_Init(SHA256_CTX *); void SHA256_Update(SHA256_CTX*, const uint8_t*, size_t); void SHA256_Final(uint8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); char* SHA256_Data(const uint8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); void SHA384_Init(SHA384_CTX*); void SHA384_Update(SHA384_CTX*, const uint8_t*, size_t); void SHA384_Final(uint8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]); char* SHA384_Data(const uint8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]); void SHA512_Init(SHA512_CTX*); void SHA512_Update(SHA512_CTX*, const uint8_t*, size_t); void SHA512_Final(uint8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); char* SHA512_Data(const uint8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); #else /* SHA2_USE_INTTYPES_H */ void SHA256_Init(SHA256_CTX *); void SHA256_Update(SHA256_CTX*, const u_int8_t*, size_t); void SHA256_Final(u_int8_t[SHA256_DIGEST_LENGTH], SHA256_CTX*); char* SHA256_End(SHA256_CTX*, char[SHA256_DIGEST_STRING_LENGTH]); char* SHA256_Data(const u_int8_t*, size_t, char[SHA256_DIGEST_STRING_LENGTH]); void SHA384_Init(SHA384_CTX*); void SHA384_Update(SHA384_CTX*, const u_int8_t*, size_t); void SHA384_Final(u_int8_t[SHA384_DIGEST_LENGTH], SHA384_CTX*); char* SHA384_End(SHA384_CTX*, char[SHA384_DIGEST_STRING_LENGTH]); char* SHA384_Data(const u_int8_t*, size_t, char[SHA384_DIGEST_STRING_LENGTH]); void SHA512_Init(SHA512_CTX*); void SHA512_Update(SHA512_CTX*, const u_int8_t*, size_t); void SHA512_Final(u_int8_t[SHA512_DIGEST_LENGTH], SHA512_CTX*); char* SHA512_End(SHA512_CTX*, char[SHA512_DIGEST_STRING_LENGTH]); char* SHA512_Data(const u_int8_t*, size_t, char[SHA512_DIGEST_STRING_LENGTH]); #endif /* SHA2_USE_INTTYPES_H */ #else /* NOPROTO */ void SHA256_Init(); void SHA256_Update(); void SHA256_Final(); char* SHA256_End(); char* SHA256_Data(); void SHA384_Init(); void SHA384_Update(); void SHA384_Final(); char* SHA384_End(); char* SHA384_Data(); void SHA512_Init(); void SHA512_Update(); void SHA512_Final(); char* SHA512_End(); char* SHA512_Data(); #endif /* NOPROTO */ #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* __SHA2_H__ */ #endif /* !HAVE_OPENSSL_SHA */ snort-2.9.20/src/sfutil/util_str.h0000644000175000017500000000262614241077357015224 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file util_str.h * @author Chris Green * @date Fri Jun 27 10:34:37 2003 * * @brief string utility functions * * some string handling wrappers */ #ifndef _UTIL_STR_H #define _UTIL_STR_H int str2int(char *str, int *ret, int allow_negative); int toggle_option(char *name, char *value, int *opt_value); #endif /* _UTIL_STR_H */ snort-2.9.20/src/sfutil/acsmx.c0000644000175000017500000005064214241077201014452 0ustar apoapo/* ** ** $Id$ ** ** Multi-Pattern Search Engine ** ** Aho-Corasick State Machine - uses a Deterministic Finite Automata - DFA ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Marc Norton ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** ** Reference - Efficient String matching: An Aid to Bibliographic Search ** Alfred V Aho and Margaret J Corasick ** Bell Labratories ** Copyright(C) 1975 Association for Computing Machinery,Inc ** ** Implemented from the 4 algorithms in the paper by Aho & Corasick ** and some implementation ideas from 'Practical Algorithms in C' ** ** Notes: ** 1) This version uses about 1024 bytes per pattern character - heavy on the memory. ** 2) This algorithm finds all occurrences of all patterns within a ** body of text. ** 3) Support is included to handle upper and lower case matching. ** 4) Some comopilers optimize the search routine well, others don't, this makes all the difference. ** 5) Aho inspects all bytes of the search text, but only once so it's very efficient, ** if the patterns are all large than the Modified Wu-Manbar method is often faster. ** 6) I don't subscribe to any one method is best for all searching needs, ** the data decides which method is best, ** and we don't know until after the search method has been tested on the specific data sets. ** ** ** May 2002 : Marc Norton 1st Version ** June 2002 : Modified interface for SNORT, added case support ** Aug 2002 : Cleaned up comments, and removed dead code. ** Nov 2,2002: Fixed queue_init() , added count=0 ** ** */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "acsmx.h" #include "util.h" #include "snort_debug.h" #ifdef DYNAMIC_PREPROC_CONTEXT #include "sf_dynamic_preprocessor.h" #endif //DYNAMIC_PREPROC_CONTEXT #define MEMASSERT(p,s) if(!p){fprintf(stderr,"ACSM-No Memory: %s!\n",s);exit(0);} #ifdef DEBUG_AC static int max_memory = 0; #endif /*static void Print_DFA( ACSM_STRUCT * acsm );*/ /* * */ static void * AC_MALLOC (int n) { void *p; p = calloc (1,n); #ifdef DEBUG_AC if (p) max_memory += n; #endif return p; } /* * */ static void AC_FREE (void *p) { if (p) free (p); } /* * Simple QUEUE NODE */ typedef struct _qnode { int state; struct _qnode *next; } QNODE; /* * Simple QUEUE Structure */ typedef struct _queue { QNODE * head, *tail; int count; } QUEUE; /* * */ static void queue_init (QUEUE * s) { s->head = s->tail = 0; s->count = 0; } /* * Add Tail Item to queue */ static void queue_add (QUEUE * s, int state) { QNODE * q; if (!s->head) { q = s->tail = s->head = (QNODE *) AC_MALLOC (sizeof (QNODE)); MEMASSERT (q, "queue_add"); q->state = state; q->next = 0; } else { q = (QNODE *) AC_MALLOC (sizeof (QNODE)); MEMASSERT (q, "queue_add"); q->state = state; q->next = 0; s->tail->next = q; s->tail = q; } s->count++; } /* * Remove Head Item from queue */ static int queue_remove (QUEUE * s) { int state = 0; QNODE * q; if (s->head) { q = s->head; state = q->state; s->head = s->head->next; s->count--; if (!s->head) { s->tail = 0; s->count = 0; } AC_FREE (q); } return state; } /* * */ static int queue_count (QUEUE * s) { return s->count; } /* * */ static void queue_free (QUEUE * s) { while (queue_count (s)) { queue_remove (s); } } /* ** Case Translation Table */ static unsigned char xlatcase[256]; /* * */ static void init_xlatcase () { int i; for (i = 0; i < 256; i++) { xlatcase[i] = (unsigned char)toupper (i); } } /* * */ static inline void ConvertCaseEx (unsigned char *d, unsigned char *s, int m) { int i; for (i = 0; i < m; i++) { d[i] = xlatcase[s[i]]; } } /* * */ static ACSM_PATTERN * CopyMatchListEntry (ACSM_PATTERN * px) { ACSM_PATTERN * p; p = (ACSM_PATTERN *) AC_MALLOC (sizeof (ACSM_PATTERN)); MEMASSERT (p, "CopyMatchListEntry"); memcpy (p, px, sizeof (ACSM_PATTERN)); px->udata->ref_count++; p->next = 0; return p; } /* * Add a pattern to the list of patterns terminated at this state. * Insert at front of list. */ static void AddMatchListEntry (ACSM_STRUCT * acsm, int state, ACSM_PATTERN * px) { ACSM_PATTERN * p; p = (ACSM_PATTERN *) AC_MALLOC (sizeof (ACSM_PATTERN)); MEMASSERT (p, "AddMatchListEntry"); memcpy (p, px, sizeof (ACSM_PATTERN)); p->next = acsm->acsmStateTable[state].MatchList; acsm->acsmStateTable[state].MatchList = p; } /* Add Pattern States */ static void AddPatternStates (ACSM_STRUCT * acsm, ACSM_PATTERN * p) { unsigned char *pattern; int state=0, next, n; n = p->n; pattern = p->patrn; /* * Match up pattern with existing states */ for (; n > 0; pattern++, n--) { next = acsm->acsmStateTable[state].NextState[*pattern]; if (next == ACSM_FAIL_STATE) break; state = next; } /* * Add new states for the rest of the pattern bytes, 1 state per byte */ for (; n > 0; pattern++, n--) { acsm->acsmNumStates++; acsm->acsmStateTable[state].NextState[*pattern] = acsm->acsmNumStates; state = acsm->acsmNumStates; } AddMatchListEntry (acsm, state, p); } /* * Build Non-Deterministic Finite Automata */ static void Build_NFA (ACSM_STRUCT * acsm) { int r, s; int i; QUEUE q, *queue = &q; ACSM_PATTERN * mlist=0; ACSM_PATTERN * px=0; /* Init a Queue */ queue_init (queue); /* Add the state 0 transitions 1st */ for (i = 0; i < ALPHABET_SIZE; i++) { s = acsm->acsmStateTable[0].NextState[i]; if (s) { queue_add (queue, s); acsm->acsmStateTable[s].FailState = 0; } } /* Build the fail state transitions for each valid state */ while (queue_count (queue) > 0) { r = queue_remove (queue); /* Find Final States for any Failure */ for (i = 0; i < ALPHABET_SIZE; i++) { int fs, next; if ((s = acsm->acsmStateTable[r].NextState[i]) != ACSM_FAIL_STATE) { queue_add (queue, s); fs = acsm->acsmStateTable[r].FailState; /* * Locate the next valid state for 'i' starting at s */ while ((next=acsm->acsmStateTable[fs].NextState[i]) == ACSM_FAIL_STATE) { fs = acsm->acsmStateTable[fs].FailState; } /* * Update 's' state failure state to point to the next valid state */ acsm->acsmStateTable[s].FailState = next; /* * Copy 'next'states MatchList to 's' states MatchList, * we copy them so each list can be AC_FREE'd later, * else we could just manipulate pointers to fake the copy. */ for (mlist = acsm->acsmStateTable[next].MatchList; mlist != NULL ; mlist = mlist->next) { px = CopyMatchListEntry (mlist); if( !px ) { FatalError("*** Out of memory Initializing Aho Corasick in acsmx.c ****"); } /* Insert at front of MatchList */ px->next = acsm->acsmStateTable[s].MatchList; acsm->acsmStateTable[s].MatchList = px; } } } } /* Clean up the queue */ queue_free (queue); } /* * Build Deterministic Finite Automata from NFA */ static void Convert_NFA_To_DFA (ACSM_STRUCT * acsm) { int r, s; int i; QUEUE q, *queue = &q; /* Init a Queue */ queue_init (queue); /* Add the state 0 transitions 1st */ for (i = 0; i < ALPHABET_SIZE; i++) { s = acsm->acsmStateTable[0].NextState[i]; if (s) { queue_add (queue, s); } } /* Start building the next layer of transitions */ while (queue_count (queue) > 0) { r = queue_remove (queue); /* State is a branch state */ for (i = 0; i < ALPHABET_SIZE; i++) { if ((s = acsm->acsmStateTable[r].NextState[i]) != ACSM_FAIL_STATE) { queue_add (queue, s); } else { acsm->acsmStateTable[r].NextState[i] = acsm->acsmStateTable[acsm->acsmStateTable[r].FailState]. NextState[i]; } } } /* Clean up the queue */ queue_free (queue); } /* * */ ACSM_STRUCT * acsmNew (void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)) { ACSM_STRUCT * p; init_xlatcase (); p = (ACSM_STRUCT *) AC_MALLOC (sizeof (ACSM_STRUCT)); MEMASSERT (p, "acsmNew"); if (p) { memset (p, 0, sizeof (ACSM_STRUCT)); p->userfree = userfree; p->optiontreefree = optiontreefree; p->neg_list_free = neg_list_free; } return p; } /* * Add a pattern to the list of patterns for this state machine */ int acsmAddPattern (ACSM_STRUCT * p, unsigned char *pat, int n, int nocase, int offset, int depth, int negative, void * id, int iid) { ACSM_PATTERN * plist; plist = (ACSM_PATTERN *) AC_MALLOC (sizeof (ACSM_PATTERN)); MEMASSERT (plist, "acsmAddPattern"); plist->patrn = (unsigned char *) AC_MALLOC (n); ConvertCaseEx (plist->patrn, pat, n); plist->casepatrn = (unsigned char *) AC_MALLOC (n); memcpy (plist->casepatrn, pat, n); plist->udata = (ACSM_USERDATA *)AC_MALLOC(sizeof(ACSM_USERDATA)); MEMASSERT (plist->udata, "acsmAddPattern"); plist->udata->ref_count = 1; plist->udata->id = id; plist->n = n; plist->nocase = nocase; plist->negative = negative; plist->offset = offset; plist->depth = depth; plist->iid = iid; plist->next = p->acsmPatterns; p->acsmPatterns = plist; p->numPatterns++; return 0; } static int acsmBuildMatchStateTrees( ACSM_STRUCT * acsm, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list) ) { int i, cnt = 0; ACSM_PATTERN * mlist; /* Find the states that have a MatchList */ for (i = 0; i < acsm->acsmMaxStates; i++) { for ( mlist=acsm->acsmStateTable[i].MatchList; mlist!=NULL; mlist=mlist->next ) { if (mlist->udata->id) { if (mlist->negative) { neg_list_func(mlist->udata->id, &acsm->acsmStateTable[i].MatchList->neg_list); } else { build_tree(mlist->udata->id, &acsm->acsmStateTable[i].MatchList->rule_option_tree); } } cnt++; } if (acsm->acsmStateTable[i].MatchList) { /* Last call to finalize the tree */ build_tree(NULL, &acsm->acsmStateTable[i].MatchList->rule_option_tree); } } return cnt; } static int acsmBuildMatchStateTreesWithSnortConf( struct _SnortConfig *sc, ACSM_STRUCT * acsm, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list) ) { int i, cnt = 0; ACSM_PATTERN * mlist; /* Find the states that have a MatchList */ for (i = 0; i < acsm->acsmMaxStates; i++) { for ( mlist=acsm->acsmStateTable[i].MatchList; mlist!=NULL; mlist=mlist->next ) { if (mlist->udata->id) { if (mlist->negative) { neg_list_func(mlist->udata->id, &acsm->acsmStateTable[i].MatchList->neg_list); } else { build_tree(sc, mlist->udata->id, &acsm->acsmStateTable[i].MatchList->rule_option_tree); } } cnt++; } if (acsm->acsmStateTable[i].MatchList) { /* Last call to finalize the tree */ build_tree(sc, NULL, &acsm->acsmStateTable[i].MatchList->rule_option_tree); } } return cnt; } /* * Compile State Machine */ static inline int _acsmCompile (ACSM_STRUCT * acsm) { int i, k; ACSM_PATTERN * plist; /* Count number of states */ acsm->acsmMaxStates = 1; for (plist = acsm->acsmPatterns; plist != NULL; plist = plist->next) { acsm->acsmMaxStates += plist->n; } acsm->acsmStateTable = (ACSM_STATETABLE *) AC_MALLOC (sizeof (ACSM_STATETABLE) * acsm->acsmMaxStates); MEMASSERT (acsm->acsmStateTable, "acsmCompile"); memset (acsm->acsmStateTable, 0, sizeof (ACSM_STATETABLE) * acsm->acsmMaxStates); /* Initialize state zero as a branch */ acsm->acsmNumStates = 0; /* Initialize all States NextStates to FAILED */ for (k = 0; k < acsm->acsmMaxStates; k++) { for (i = 0; i < ALPHABET_SIZE; i++) { acsm->acsmStateTable[k].NextState[i] = ACSM_FAIL_STATE; } } /* Add each Pattern to the State Table */ for (plist = acsm->acsmPatterns; plist != NULL; plist = plist->next) { AddPatternStates (acsm, plist); } /* Set all failed state transitions to return to the 0'th state */ for (i = 0; i < ALPHABET_SIZE; i++) { if (acsm->acsmStateTable[0].NextState[i] == ACSM_FAIL_STATE) { acsm->acsmStateTable[0].NextState[i] = 0; } } /* Build the NFA */ Build_NFA (acsm); /* Convert the NFA to a DFA */ Convert_NFA_To_DFA (acsm); /* printf ("ACSMX-Max Memory: %d bytes, %d states\n", max_memory, acsm->acsmMaxStates); */ //Print_DFA( acsm ); return 0; } int acsmCompile (ACSM_STRUCT * acsm, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int rval; if ((rval = _acsmCompile (acsm))) return rval; if (build_tree && neg_list_func) { acsmBuildMatchStateTrees(acsm, build_tree, neg_list_func); } return 0; } int acsmCompileWithSnortConf (struct _SnortConfig *sc, ACSM_STRUCT * acsm, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)) { int rval; if ((rval = _acsmCompile (acsm))) return rval; if (build_tree && neg_list_func) { acsmBuildMatchStateTreesWithSnortConf(sc, acsm, build_tree, neg_list_func); } return 0; } static unsigned char Tc[64*1024]; /* * Search Text or Binary Data for Pattern matches */ int acsmSearch (ACSM_STRUCT * acsm, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int* current_state ) { int state = 0; ACSM_PATTERN * mlist; unsigned char *Tend; ACSM_STATETABLE * StateTable = acsm->acsmStateTable; int nfound = 0; unsigned char *T; int index; /* Case conversion */ ConvertCaseEx (Tc, Tx, n); T = Tc; Tend = T + n; if ( !current_state ) { return 0; } state = *current_state; for (; T < Tend; T++) { state = StateTable[state].NextState[*T]; if( StateTable[state].MatchList != NULL ) { mlist = StateTable[state].MatchList; index = T - mlist->n + 1 - Tc; nfound++; if (Match (mlist->udata->id, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) { *current_state = state; return nfound; } } } *current_state = state; return nfound; } /* * Free all memory */ void acsmFree (ACSM_STRUCT * acsm) { int i; ACSM_PATTERN * mlist, *ilist; for (i = 0; i < acsm->acsmMaxStates; i++) { mlist = acsm->acsmStateTable[i].MatchList; while (mlist) { ilist = mlist; mlist = mlist->next; ilist->udata->ref_count--; if (ilist->udata->ref_count == 0) { if (acsm->userfree && ilist->udata->id) acsm->userfree(ilist->udata->id); AC_FREE(ilist->udata); } if (ilist->rule_option_tree && acsm->optiontreefree) { acsm->optiontreefree(&(ilist->rule_option_tree)); } if (ilist->neg_list && acsm->neg_list_free) { acsm->neg_list_free(&(ilist->neg_list)); } AC_FREE (ilist); } } AC_FREE (acsm->acsmStateTable); mlist = acsm->acsmPatterns; while(mlist) { ilist = mlist; mlist = mlist->next; AC_FREE(ilist->patrn); AC_FREE(ilist->casepatrn); AC_FREE(ilist); } AC_FREE (acsm); } int acsmPatternCount ( ACSM_STRUCT * acsm ) { return acsm->numPatterns; } /* * */ /* static void Print_DFA( ACSM_STRUCT * acsm ) { int k; int i; int next; for (k = 0; k < acsm->acsmMaxStates; k++) { for (i = 0; i < ALPHABET_SIZE; i++) { next = acsm->acsmStateTable[k].NextState[i]; if( next == 0 || next == ACSM_FAIL_STATE ) { if( isprint(i) ) printf("%3c->%-5d\t",i,next); else printf("%3d->%-5d\t",i,next); } } printf("\n"); } } */ int acsmPrintDetailInfo(ACSM_STRUCT * p) { #ifdef WIN32 if(p) p = p; #endif return 0; } int acsmPrintSummaryInfo(void) { #ifdef XXXXX char * fsa[]={ "TRIE", "NFA", "DFA", }; ACSM_STRUCT2 * p = &summary.acsm; if( !summary.num_states ) return; LogMessage("+--[Pattern Matcher:Aho-Corasick Summary]----------------------\n"); LogMessage("| Alphabet Size : %d Chars\n",p->acsmAlphabetSize); LogMessage("| Sizeof State : %d bytes\n",sizeof(acstate_t)); LogMessage("| Storage Format : %s \n",sf[ p->acsmFormat ]); LogMessage("| Num States : %d\n",summary.num_states); LogMessage("| Num Transitions : %d\n",summary.num_transitions); LogMessage("| State Density : %.1f%%\n",100.0*(double)summary.num_transitions/(summary.num_states*p->acsmAlphabetSize)); LogMessage("| Finite Automatum : %s\n", fsa[p->acsmFSA]); if( max_memory < 1024*1024 ) LogMessage("| Memory : %.2fKbytes\n", (float)max_memory/1024 ); else LogMessage("| Memory : %.2fMbytes\n", (float)max_memory/(1024*1024) ); LogMessage("+-------------------------------------------------------------\n"); #endif return 0; } #ifdef ACSMX_MAIN /* * Text Data Buffer */ unsigned char text[512]; /* * A Match is found */ int MatchFound (unsigned id, int index, void *data) { fprintf (stdout, "%s\n", (char *) id); return 0; } /* * */ int main (int argc, char **argv) { int i, nocase = 0; ACSM_STRUCT * acsm; if (argc < 3) { fprintf (stderr, "Usage: acsmx pattern word-1 word-2 ... word-n -nocase\n"); exit (0); } acsm = acsmNew (); strncpy (text, argv[1], sizeof(text) - 1); text[sizeof(text) - 1] = '\0'; for (i = 1; i < argc; i++) if (strcmp (argv[i], "-nocase") == 0) nocase = 1; for (i = 2; i < argc; i++) { if (argv[i][0] == '-') continue; acsmAddPattern (acsm, argv[i], strlen (argv[i]), nocase, 0, 0, argv[i], i - 2); } acsmCompile (acsm); acsmSearch (acsm, text, strlen (text), MatchFound, (void *) 0); acsmFree (acsm); printf ("normal pgm end\n"); return (0); } #endif /* */ snort-2.9.20/src/sfutil/bitop_funcs.h0000644000175000017500000001574414241077211015664 0ustar apoapo/* ** $Id$ ** ** bitopt_funcs.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Dan Roelker ** Marc Norton ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** NOTES ** 5.15.02 - Initial Source Code. Norton/Roelker ** 5.23.02 - Moved bitop functions to bitop.h to inline. Norton/Roelker ** 1.21.04 - Added static initialization. Roelker ** 9.13.05 - Separated type and inline func definitions. Sturges ** */ #ifndef _BITOP_FUNCS_H #define _BITOP_FUNCS_H #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "snort_debug.h" #include "bitop.h" /* ** NAME ** boInitStaticBITOP:: */ /** ** This function is for use if you handle the bitop buffer allocation ** yourself. Just pass in the char array and the number of bytes the array ** is and this function sets up the structure for you. ** ** @retval int ** ** @return 0 successful ** @return !0 failed */ static inline int boInitStaticBITOP(BITOP *BitOp,int iBytes,unsigned char *buf) { if(iBytes < 1 || !buf || !BitOp) return 1; BitOp->pucBitBuffer = buf; BitOp->uiBitBufferSize = (unsigned int)iBytes; BitOp->uiMaxBits = (unsigned int)(iBytes << 3); memset(buf, 0x00, iBytes); return 0; } /* ** ** NAME ** boInitBITOP ** ** DESCRIPTION ** Initializes the BITOP structure for use. ** ** NOTE: ** BITOP structure must be zeroed to avoid misinterpretation ** of initialization. ** ** FORMAL INPUTS ** BITOP * - the structure to initialize ** int - the number of bit positions to hold. ** ** FORMAL OUTPUTS ** int - 0 if successful, 1 if failed. ** */ static inline int boInitBITOP(BITOP *BitOp, int iBytes) { int iSize; /* ** Sanity check for size */ if((iBytes < 1) || (BitOp == NULL)) { return 1; } /* ** Check for already initialized buffer, and ** if it is already initialized then we return that it ** is initialized. */ if(BitOp->pucBitBuffer) { return 0; } iSize = iBytes << 3; BitOp->pucBitBuffer = (unsigned char *)calloc(1, iBytes); if(BitOp->pucBitBuffer == NULL) { return 1; } BitOp->uiBitBufferSize = (unsigned int)iBytes; BitOp->uiMaxBits = (unsigned int)iSize; return 0; } /* ** ** NAME ** boResetBITOP ** ** DESCRIPTION ** This resets the bit buffer so that it can be used again. ** ** FORMAL INPUTS ** BITOP * - structure to reset ** ** FORMAL OUTPUT ** int - 0 if successful, 1 if failed. ** */ static inline int boResetBITOP(BITOP *BitOp) { if (BitOp == NULL) return 1; memset(BitOp->pucBitBuffer, 0x00, BitOp->uiBitBufferSize); return 0; } /* ** ** NAME ** boSetAllBits ** ** DESCRIPTION ** This resets the bit buffer to all 1's so that it can be used again. ** ** FORMAL INPUTS ** BITOP * - structure to reset ** ** FORMAL OUTPUT ** int - 0 if successful, 1 if failed. ** */ static inline int boSetAllBits(BITOP *BitOp) { if (BitOp == NULL) return 1; memset(BitOp->pucBitBuffer, 0xff, BitOp->uiBitBufferSize); return 0; } /* ** ** NAME ** boSetBit ** ** DESCRIPTION ** Set the bit in the specified position within the bit buffer. ** ** FORMAL INPUTS ** BITOP * - the structure with the bit buffer ** int - the position to set within the bit buffer ** ** FORMAL OUTPUTS ** int - 0 if the bit was set, 1 if there was an error. ** */ static inline int boSetBit(BITOP *BitOp, unsigned int uiPos) { unsigned char mask; /* ** Sanity Check while setting bits */ if((BitOp == NULL) || (BitOp->uiMaxBits <= uiPos)) return 1; mask = (unsigned char)( 0x80 >> (uiPos & 7)); BitOp->pucBitBuffer[uiPos >> 3] |= mask; return 0; } /* ** ** NAME ** boIsBitSet ** ** DESCRIPTION ** Checks for the bit set in iPos of bit buffer. ** ** FORMAL INPUTS ** BITOP * - structure that holds the bit buffer ** int - the position number in the bit buffer ** ** FORMAL OUTPUTS ** int - 0 if bit not set, 1 if bit is set. ** */ static inline int boIsBitSet(BITOP *BitOp, unsigned int uiPos) { unsigned char mask; /* ** Sanity Check while setting bits */ if((BitOp == NULL) || (BitOp->uiMaxBits <= uiPos)) return 0; mask = (unsigned char)(0x80 >> (uiPos & 7)); return (mask & BitOp->pucBitBuffer[uiPos >> 3]); } /* ** ** NAME ** boClearBit ** ** DESCRIPTION ** Clear the bit in the specified position within the bit buffer. ** ** FORMAL INPUTS ** BITOP * - the structure with the bit buffer ** int - the position to clear within the bit buffer ** ** FORMAL OUTPUTS ** int - 0 if the bit was cleared, 1 if there was an error. ** */ static inline void boClearBit(BITOP *BitOp, unsigned int uiPos) { unsigned char mask; /* ** Sanity Check while clearing bits */ if((BitOp == NULL) || (BitOp->uiMaxBits <= uiPos)) return; mask = (unsigned char)(0x80 >> (uiPos & 7)); BitOp->pucBitBuffer[uiPos >> 3] &= ~mask; } /* ** ** NAME ** boClearByte ** ** DESCRIPTION ** Clear the byte in the specified position within the bit buffer. ** ** FORMAL INPUTS ** BITOP * - the structure with the bit buffer ** int - the position to clear within the bit buffer ** ** FORMAL OUTPUTS ** int - 0 if the byte was cleared, 1 if there was an error. ** */ static inline void boClearByte(BITOP *BitOp, unsigned int uiPos) { /* ** Sanity Check while clearing bytes */ if((BitOp == NULL) || (BitOp->uiMaxBits <= uiPos)) return; BitOp->pucBitBuffer[uiPos >> 3] = 0; } /* ** ** NAME ** boFreeBITOP ** ** DESCRIPTION ** Frees memory created by boInitBITOP - specifically ** BitOp->pucBitBuffer ** ** NOTE: ** !!! ONLY USE THIS FUNCTION IF YOU USED boInitBITOP !!! ** ** FORMAL INPUTS ** BITOP * - the structure initially passed to boInitBITOP ** ** FORMAL OUTPUTS ** void function ** **/ static inline void boFreeBITOP(BITOP *BitOp) { if((BitOp == NULL) || (BitOp->pucBitBuffer == NULL)) return; free(BitOp->pucBitBuffer); BitOp->pucBitBuffer = NULL; } #endif /* _BITOPT_FUNCS_H_ */ snort-2.9.20/src/sfutil/sf_iph.c0000644000175000017500000003264114241077242014613 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2007-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "decode.h" #include "reg_test.h" #define FAILURE -1 #define SUCCESS 0 #define IP6_HEADER_LEN 40 /* Version is the first four bits of the uint32_t passed in */ #define IP6_VER(x) \ (ntohl(x) >> 28) /* The 'Packet' structure is almost always allocated on the stack. * Likewise, return buffers will almost always be aswell. * So, for performance reasons, argument validation can be disabled * and removed from the code at compile time to prevent unecessary extra * conditionals from being checked at run-time. */ #define ERR_CHK_LVL 0 #if ERR_CHK_LVL == 2 #define VALIDATE(x,y) if(!x || !y) return FAILURE; #elif ERR_CHK_LVL == 1 #define VALIDATE(x,y) if(!y) return FAILURE; #else #define VALIDATE(x,y) #endif sfaddr_t *ip6_ret_src(const Packet *p) { VALIDATE(p, 1); return &p->ip6h->ip_addrs->ip_src; } sfaddr_t *orig_ip6_ret_src(const Packet *p) { VALIDATE(p, 1); return &p->orig_ip6h->ip_addrs->ip_src; } sfaddr_t *ip6_ret_dst(const Packet *p) { VALIDATE(p, 1); return &p->ip6h->ip_addrs->ip_dst; } sfaddr_t *orig_ip6_ret_dst(const Packet *p) { VALIDATE(p, 1); return &p->orig_ip6h->ip_addrs->ip_dst; } uint16_t ip6_ret_toc(const Packet *p) { uint16_t toc; VALIDATE(p,1); toc = (uint16_t)((ntohl(p->ip6h->vcl) & 0x0FF00000) >> 20); return toc; } uint16_t orig_ip6_ret_toc(const Packet *p) { uint16_t toc; VALIDATE(p,1); toc = (uint16_t)((ntohl(p->orig_ip6h->vcl) & 0x0FF00000) >> 20); return toc; } uint8_t ip6_ret_hops(const Packet *p) { // VALIDATE(p,1); return p->ip6h->hop_lmt; } uint8_t orig_ip6_ret_hops(const Packet *p) { // VALIDATE(p,1); return p->orig_ip6h->hop_lmt; } uint16_t ip6_ret_len(const Packet *p) { VALIDATE(p,1); /* The length field does not include the header in IPv6, but does in IPv4. * To make this analogous to IPv4, for Snort's purposes, we need to tack * on the difference. */ return p->ip6h->len; } uint16_t orig_ip6_ret_len(const Packet *p) { VALIDATE(p,1); return p->orig_ip6h->len; } uint32_t ip6_ret_id(const Packet *p) { IP6Frag *frag_hdr; if (p->ip6_extension_count == 0) return 0; frag_hdr = (IP6Frag*)p->ip6_extensions[p->ip6_frag_index].data; return frag_hdr->ip6f_ident; } uint32_t orig_ip6_ret_id(const Packet *p) { // XXX-IPv6 "NOT YET IMPLEMENTED - IP6 identification" return 0; } uint8_t ip6_ret_next(const Packet *p) { VALIDATE(p,1); return p->ip6h->next; } uint8_t orig_ip6_ret_next(const Packet *p) { VALIDATE(p,1); return p->orig_ip6h->next; } uint16_t ip6_ret_off(const Packet *p) { IP6Frag *frag_hdr; if (p->ip6_extension_count == 0) return 0; frag_hdr = (IP6Frag *)p->ip6_extensions[p->ip6_frag_index].data; return frag_hdr->ip6f_offlg; } uint16_t orig_ip6_ret_off(const Packet *p) { // XXX-IPv6 "NOT YET IMPLEMENTED - IP6 frag offset" return 0; } uint8_t ip6_ret_ver(const Packet *p) { return (uint8_t)IP6_VER(p->ip6h->vcl); } uint8_t orig_ip6_ret_ver(const Packet *p) { return (uint8_t)IP6_VER(p->orig_ip6h->vcl); } sfaddr_t *ip4_ret_dst(const Packet *p) { VALIDATE(p,1); return &p->ip4h->ip_addrs->ip_dst; } sfaddr_t *orig_ip4_ret_dst(const Packet *p) { VALIDATE(p,1); return &p->orig_ip4h->ip_addrs->ip_dst; } sfaddr_t *ip4_ret_src(const Packet *p) { VALIDATE(p,1); return &p->ip4h->ip_addrs->ip_src; } sfaddr_t *orig_ip4_ret_src(const Packet *p) { VALIDATE(p,1); return &p->orig_ip4h->ip_addrs->ip_src; } uint16_t ip4_ret_tos(const Packet *p) { VALIDATE(p,1); return p->ip4h->ip_tos; } uint16_t orig_ip4_ret_tos(const Packet *p) { VALIDATE(p,1); return p->orig_ip4h->ip_tos; } uint8_t ip4_ret_ttl(const Packet *p) { VALIDATE(p,1); return p->ip4h->ip_ttl; } uint8_t orig_ip4_ret_ttl(const Packet *p) { VALIDATE(p,1); return p->orig_ip4h->ip_ttl; } uint16_t ip4_ret_len(const Packet *p) { VALIDATE(p,1); return p->ip4h->ip_len; } uint16_t orig_ip4_ret_len(const Packet *p) { VALIDATE(p,1); return p->orig_ip4h->ip_len; } uint32_t ip4_ret_id(const Packet *p) { VALIDATE(p,1); return (uint32_t)p->ip4h->ip_id; } uint32_t orig_ip4_ret_id(const Packet *p) { VALIDATE(p,1); return (uint32_t)p->orig_ip4h->ip_id; } uint8_t ip4_ret_proto(const Packet *p) { // VALIDATION() return p->ip4h->ip_proto; } uint8_t orig_ip4_ret_proto(const Packet *p) { // VALIDATION() return p->orig_ip4h->ip_proto; } uint16_t ip4_ret_off(const Packet *p) { return p->ip4h->ip_off; } uint16_t orig_ip4_ret_off(const Packet *p) { return p->orig_ip4h->ip_off; } uint8_t ip4_ret_ver(const Packet *p) { return IP_VER(p->iph); } uint8_t orig_ip4_ret_ver(const Packet *p) { return IP_VER(p->orig_iph); } uint8_t ip4_ret_hlen(const Packet *p) { return IP_HLEN(p->iph); } uint8_t orig_ip4_ret_hlen(const Packet *p) { return IP_HLEN(p->orig_iph); } uint8_t ip6_ret_hlen(const Packet *p) { /* Snort is expecting this number to be in terms of 32 bit words */ return IP6_HDR_LEN / 4 ; } uint8_t orig_ip6_ret_hlen(const Packet *p) { return IP6_HDR_LEN / 4; } IPH_API ip4 = { ip4_ret_src, ip4_ret_dst, ip4_ret_tos, ip4_ret_ttl, ip4_ret_len, ip4_ret_id, ip4_ret_proto, ip4_ret_off, ip4_ret_ver, ip4_ret_hlen, orig_ip4_ret_src, orig_ip4_ret_dst, orig_ip4_ret_tos, orig_ip4_ret_ttl, orig_ip4_ret_len, orig_ip4_ret_id, orig_ip4_ret_proto, orig_ip4_ret_off, orig_ip4_ret_ver, orig_ip4_ret_hlen, IPH_API_V4 }; IPH_API ip6 = { ip6_ret_src, ip6_ret_dst, ip6_ret_toc, ip6_ret_hops, ip6_ret_len, ip6_ret_id, ip6_ret_next, ip6_ret_off, ip6_ret_ver, ip6_ret_hlen, orig_ip6_ret_src, orig_ip6_ret_dst, orig_ip6_ret_toc, orig_ip6_ret_hops, orig_ip6_ret_len, orig_ip6_ret_id, orig_ip6_ret_next, orig_ip6_ret_off, orig_ip6_ret_ver, orig_ip6_ret_hlen, IPH_API_V6 }; static inline void _set_callbacks(struct _Packet *p, int family, char orig) { if ( !orig ) { if(family == AF_INET) p->iph_api = &ip4; else p->iph_api = &ip6; p->family = family; } else { if(family == AF_INET) p->orig_iph_api = &ip4; else p->orig_iph_api = &ip6; p->orig_family = family; } } void set_callbacks(struct _Packet *p, int family, char orig) { _set_callbacks(p, family, orig); } void sfiph_build(Packet *p, const void *hdr, int family) { IP6RawHdr *hdr6; IPHdr *hdr4; if(!p || !hdr) return; /* If family is already set, we've been here before. * That means this is a nested IP. */ if (p->family != NO_IP) { memcpy(&p->outer_ips, &p->inner_ips, sizeof(p->outer_ips)); if (p->iph_api->ver == IPH_API_V4) { memcpy(&p->outer_ip4h, &p->inner_ip4h, sizeof(p->outer_ip4h) - sizeof(p->outer_ip4h.ip_addrs)); p->outer_ip4h.ip_addrs = &p->outer_ips; } else if (p->iph_api->ver == IPH_API_V6) { memcpy(&p->outer_ip6h, &p->inner_ip6h, sizeof(p->outer_ip6h) - sizeof(p->outer_ip6h.ip_addrs)); p->outer_ip6h.ip_addrs = &p->outer_ips; } p->outer_iph_api = p->iph_api; p->outer_family = p->family; } _set_callbacks(p, family, CALLBACK_IP); if(family == AF_INET) { hdr4 = (IPHdr*)hdr; /* The struct Snort uses is identical to the actual IP4 struct, * with the exception of the IP addresses. Copy over everything but * the IPs */ memcpy(&p->inner_ip4h, hdr4, sizeof(IPHdr) - 8); #ifdef HAVE_DAQ_REAL_ADDRESSES if (p->outer_family != NO_IP || !(p->pkth->flags & DAQ_PKT_FLAG_REAL_ADDRESSES)) { #endif sfip_set_raw(&p->inner_ips.ip_src, &hdr4->ip_src, AF_INET); sfip_set_raw(&p->inner_ips.ip_dst, &hdr4->ip_dst, AF_INET); #ifdef HAVE_DAQ_REAL_ADDRESSES } else { sfip_set_raw(&p->inner_ips.ip_src, p->pkth->real_sIP.s6_addr, (p->pkth->flags & DAQ_PKT_FLAG_REAL_SIP_V6) ? AF_INET6 : AF_INET); sfip_set_raw(&p->inner_ips.ip_dst, p->pkth->real_dIP.s6_addr, (p->pkth->flags & DAQ_PKT_FLAG_REAL_DIP_V6) ? AF_INET6 : AF_INET); } #endif p->inner_ip4h.ip_addrs = &p->inner_ips; p->actual_ip_len = ntohs(p->inner_ip4h.ip_len); p->ip4h = &p->inner_ip4h; } else { hdr6 = (IP6RawHdr*)hdr; /* The struct Snort uses is identical to the actual IP6 struct, * with the exception of the IP addresses. Copy over everything but * the IPs*/ memcpy(&p->inner_ip6h, hdr6, sizeof(IP6RawHdr) - 32); #ifdef HAVE_DAQ_REAL_ADDRESSES if (p->outer_family != NO_IP || !(p->pkth->flags & DAQ_PKT_FLAG_REAL_ADDRESSES)) { #endif sfip_set_raw(&p->inner_ips.ip_src, &hdr6->ip6_src, family); sfip_set_raw(&p->inner_ips.ip_dst, &hdr6->ip6_dst, family); #ifdef HAVE_DAQ_REAL_ADDRESSES } else { sfip_set_raw(&p->inner_ips.ip_src, p->pkth->real_sIP.s6_addr, (p->pkth->flags & DAQ_PKT_FLAG_REAL_SIP_V6) ? AF_INET6 : AF_INET); sfip_set_raw(&p->inner_ips.ip_dst, p->pkth->real_dIP.s6_addr, (p->pkth->flags & DAQ_PKT_FLAG_REAL_DIP_V6) ? AF_INET6 : AF_INET); } #endif p->inner_ip6h.ip_addrs = &p->inner_ips; p->actual_ip_len = ntohs(p->inner_ip6h.len) + IP6_HDR_LEN; p->ip6h = &p->inner_ip6h; } #ifdef REG_TEST if (rt_ip_increment) { uint32_t* addr; addr = sfaddr_get_ip4_ptr(&p->inner_ips.ip_src); *addr = htonl(ntohl(*addr) + rt_ip_increment); addr = sfaddr_get_ip4_ptr(&p->inner_ips.ip_dst); *addr = htonl(ntohl(*addr) + rt_ip_increment); } #endif } void sfiph_orig_build(Packet *p, const void *hdr, int family) { IP6RawHdr *hdr6; IPHdr *hdr4; if(!p || !hdr) return; /* If iph_api is already set, we've been here before. * That means this is a nested IP. */ if (p->orig_iph_api) { memcpy(&p->outer_orig_ips, &p->inner_orig_ips, sizeof(p->outer_orig_ips)); if (p->orig_iph_api->ver == IPH_API_V4) { memcpy(&p->outer_orig_ip4h, &p->inner_orig_ip4h, sizeof(p->outer_orig_ip4h) - sizeof(p->outer_orig_ip4h.ip_addrs)); p->outer_orig_ip4h.ip_addrs = &p->outer_orig_ips; p->outer_orig_iph_api = p->orig_iph_api; } else if (p->orig_iph_api->ver == IPH_API_V6) { memcpy(&p->outer_orig_ip6h, &p->inner_orig_ip6h, sizeof(p->outer_orig_ip6h) - sizeof(p->outer_orig_ip6h.ip_addrs)); p->outer_orig_ip6h.ip_addrs = &p->outer_orig_ips; p->outer_orig_iph_api = p->orig_iph_api; } } _set_callbacks(p, family, CALLBACK_ICMP_ORIG); if(family == AF_INET) { hdr4 = (IPHdr*)hdr; /* The struct Snort uses is identical to the actual IP6 struct, * with the exception of the IP addresses. Copy over everything but * the IPs */ memcpy(&p->inner_orig_ip4h, hdr4, sizeof(IPHdr) - 8); sfip_set_raw(&p->inner_orig_ips.ip_src, &hdr4->ip_src, family); sfip_set_raw(&p->inner_orig_ips.ip_dst, &hdr4->ip_dst, family); p->inner_orig_ip4h.ip_addrs = &p->inner_orig_ips; p->actual_ip_len = ntohs(p->inner_orig_ip4h.ip_len); p->orig_ip4h = &p->inner_orig_ip4h; } else { hdr6 = (IP6RawHdr*)hdr; /* The struct Snort uses is identical to the actual IP6 struct, * with the exception of the IP addresses. Copy over everything but * the IPs*/ memcpy(&p->inner_orig_ip6h, hdr6, sizeof(IP6RawHdr) - 32); sfip_set_raw(&p->inner_orig_ips.ip_src, &hdr6->ip6_src, family); sfip_set_raw(&p->inner_orig_ips.ip_dst, &hdr6->ip6_dst, family); p->inner_orig_ip6h.ip_addrs = &p->inner_orig_ips; p->actual_ip_len = ntohs(p->inner_orig_ip6h.len) + IP6_HDR_LEN; p->orig_ip6h = &p->inner_orig_ip6h; } } #ifdef TESTER int main() { Packet p; IP4Hdr i4; IP6Hdr i6; /* This test assumes we get an IPv4 packet and verifies * that the correct callbacks are setup, and they return * the correct values. */ _set_callbacks(&p, AF_INET, CALLBACK_IP); /* Same test as above, but with IPv6 */ _set_callbacks(&p, AF_INET6, CALLBACK_IP); return 0; } #endif snort-2.9.20/src/sfutil/sfportobject.c0000644000175000017500000030271614241077304016051 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* sfportobject.c author: marc norton date: 11/05/2005 description: Port objects provides support for generic ports lists comprised of individual ports, port ranges, and negation of ports and port ranges. Port lists require a somewhat more complex scheme to determine the proper grouping of rules for each port while minimizing the number of rule groups created. We can use a single group of rules in the multi-pattern detection phase, however that can have a huge impact on performance. Instead we try to create a smaller grouping of rules that might be applicable to each port. As rules are defined using port ranges, and port lists there will be port overlapps between rules. This requires us to determine whether we should create one larger rule group to apply to all relevant ports, or to create multiple rule groups and apply the smallest applicable one to each port. In practice snort has some rules which span almost all 64K ports which might cause all rules in all port-rule groups to be merged into one set unless we apply a more complex logic than simply merging rule-port groups with common ports. This is the problem addressed by the sfportobject module. port list examples of acceptable usage: - var has been overloaded, if it includes _port we add as a port-object also. var http_ports 80 var http_range_ports 80:81 var http_list_ports [80,8080,8138] - portvar has been added to indicate portvariables, this form does not require _port portvar http 80 portvar http_range 80:81 portvar http_list [80,8080,8138] 80 $http !90 80:81 $http_range !90:91 [80,8080,8138] $http_list [$http,$http_list] [2001,2008,20022,8100:8150,!8121,!8123] [!any] - uhhh, why do people ask about this ? Rules are defined using a port, a port-range or a list of these, we call these port objects. As rules are loaded we generate some large rule counts on some ports, and small rule counts on most ports. If for each port you build a list of rules on that port, we may end up with some ports with a large rule set that differs by the addition of a few rules on each port (relative to the group sizes) we don't want to generate compeletely different rule groups for these as that would than generate multiple large state machines for the multi-pattern matching phase of the detection engine which in turn could use a lot of memory. It turns out that one scheme, the one used herein, provides some blending of rule groups to minimize memory, and tries to minimize large group sizes to keep performance more optimal - although this is at the expense of memory. --- Port variables Var - has been overloaded. If it's name includes _port as part of the var name it is added to the PortVarTable. PortVar - has been added. These are always added to the PortVarTable. --- Loading Port lists and rules PortTables - we support src and dst tables for tcp/udp/icmp/ip/arp rules. PortVar References - we dup the PortVar entries as needed into each table if referenced, so HTTP_PORTS for tcp and udp contain different rules. If a rule references a PortVar we look it up in the table, if its not present we dup it from the PortVarTable, otherwise we just add the rule index to the PortVar HTTP_PORTS in the proper table. If a PortVar is not used to specify a Port entry in a rule we create a temp port-object, and check if it's port-list is already in the table. If it's not we make the temp port-object the permanent entry in the table. If it is, we just add the rule index to the existing entry, and delete the temp port-object. When the rules are done loading we should have a set of port-objects with port-lists that differ by at least one port. The next step handles the cases where we have multiple port-objects with at least one common port. --- Merging Ports and Rules We maintain for each port a list of port objects and their rules that apply to it. This allows us to view combining the rules associated with each port object using a few heuristics. A list of port objects applicable to each port presents rules in one of four catagories: 1) a single port object, and all rules associated with it. 2) multiple port objects each with a small set of rules associated with it. 3) one port object with a large rule set, and one or more port objects with a small set of rules associated with each. 4) multiple port objects with large rule sets, and zero or more port objects each with a small set of rules associated with it. We process these four categories as follows: 1) -a single port object (large or small) do nothing, each port referencing this port object is complete. 2) -multiple small port objects merge the rules for all port objects into one virtual object, for each port in this category lookup it's combined port object to see if it's already defined, if not create one. This way all ports that have the same port groups point to the same virtual port object. 3) -one large port object, and one or more small port objects add the small rule groups into the large rule set, using the existing port object. 4) -multiple large port objects and zero or more small port objects merge the large port objects into a virtual port object and add all rules from both large and small sets into it's rule set. we use the combined large group ports to form a key, so any ports referencing just these large rule groups, and some small ones will be recognized as the same. This handles cases where we have 2,3,4.etc large rule groups combined. Any time we see a 'n' grouping of the same large rule sets we'll look it up and point to it for that port. To determine when a port object has a large rule set or a small one we use a simple threshold value. In theory the larger this value is the more merging of rules in category 2 and 3 will occur. When this value is small category 4 should become a more prevalent situation. However, the behavior of groupings for a given threshold can change as more rules are added to port groups. Therefore generous statistics are printed after the rules and port objects are compiled into their final groupings. Procedure for using PortLists 1) Process Var's as PortVar's and standard Var's (for now). This allows existing snort features to work, with the Var's. Add in the PortVar support to parse the Var input into PortObjects, and add these to the PortVartable. 2) Read Rules a) Read port numbers and lists 1) Dereference PortVar/Var Names if any are referenced. b) Create a Port Object c) Test if this Port object exists already, 1) If so, add the sid to it. 2) If not add it .... Notes: All any-any port rules are managed separately, and added in to the final rules lists of each port group after this analysis. Rules defined with ranges are no longer considered any-any rules for the purpose of organizing port-rule groupings. This should help prevent some cross fertilization of rule groups with rules that are unneccessary, this causes rule group sizes to bloat and performance to slow. Hierarchy: PortTable -> PortObject's PortVar -> PortObject ( These are pure, and are dup'ed for use in the PortTables ) PortObject -> PortObjectItems (port or port range) */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort.h" #include "snort_bounds.h" #include "snort_debug.h" #include "sfportobject.h" #include "sfrim.h" #include "util.h" #define PO_EXTRA_RULE_CNT 25 #define PTBL_LRC_DEFAULT 10 #define PO_INIT_ID 1000000 #define PO_HASH_TBL_ROWS 10000 /* Hash Key Comparisons for treating PortObjects as Keys return values memcmp style */ static int PortObject_keycmp( const void *a , const void *b, size_t n ) { #ifdef WIN32 n = n; #endif return !PortObjectEqual( *(PortObject**)a, *(PortObject**)b ); } /* * plx_t is a variable sized array of pointers */ typedef struct { int n; void ** p; }plx_t; static plx_t * plx_new( void * pv_array[], int n ) { plx_t * p; int i; if(!pv_array || n < 0) return NULL; p = SnortAlloc(sizeof(plx_t)); p->p = SnortAlloc(n * sizeof(void*)); p->n = n; for(i=0;ip[i] = pv_array[i]; } return p; } static void plx_free(void * p ) { plx_t * plx=(plx_t*)p; if( !plx ) return; if( plx->p ) free(plx->p); free( p ); } #ifdef DEBUG_MSGS static void plx_print(plx_t * p) { DEBUG_WRAP( int i; DebugMessage(DEBUG_PORTLISTS, "plx-n=%d\n", p->n); for(i=0;in;i++) DebugMessage(DEBUG_PORTLISTS, "plx[%d]=%lu\n", i, p->p[i]); ); } #endif /* * hash function for plx_t types */ static unsigned plx_hash( SFHASHFCN * p, unsigned char *d, int n ) { unsigned k, hash = p->seed; int i; plx_t* plx; #ifdef WIN32 n = n; /* To silence a Win32 warning */ #endif plx = *(plx_t**)d; for(i=0;in;i++) { unsigned char * pc_ptr = (unsigned char*)&plx->p[i]; for(k=0;kscale; hash += pc_ptr[k]; } } return hash ^ p->hardener; } /* for sorting an array of pointers */ static inline int p_keycmp( const void *a , const void *b ) { if( *(unsigned long**)a < *(unsigned long**)b ) return -1; if( *(unsigned long**)a > *(unsigned long**)b ) return 1; return 0; /* they are equal */ } /* Hash Key Comparisons for treating plx_t types as Keys return values memcmp style this only needs to produce 0 => exact match, otherwise not. -1, and +1 are not strictly needed, they could both return a non zero value for the purposes of hashing and searching. */ static int plx_keycmp( const void *a , const void *b, size_t n ) { int i, cmp; plx_t * pla = *(plx_t**)a; plx_t * plb = *(plx_t**)b; #ifdef WIN32 n = n; /* To silence a Win32 warning */ #endif if( pla->n < plb->n ) return -1; if( pla->n > plb->n ) return 1; for(i=0;in;i++) { if((cmp = p_keycmp(&pla->p[i], &plb->p[i])) != 0) return cmp; } return 0; /* they are equal */ } /* global for printing so we don't put so many bytes * on the stack */ static char po_print_buf[MAXPORTS]; /* PORT OBJECT FUNCTIONS */ /* Create a new PortObject */ PortObject * PortObjectNew(void) { PortObject *po = (PortObject *)SnortAlloc(sizeof(PortObject)); po->item_list =(SF_LIST*) sflist_new(); if( !po->item_list ) { free( po ); return 0; } po->rule_list =(SF_LIST*) sflist_new(); if( !po->rule_list ) { sflist_free_all( po->item_list, free ); free( po ); return 0; } return po; } /* This is the opposite of ntohl/htonl defines, and does the * swap on big endian hardware */ #ifdef WORDS_BIGENDIAN #define SWAP_BYTES(a) \ ((((uint32_t)(a) & 0xFF000000) >> 24) | \ (((uint32_t)(a) & 0x00FF0000) >> 8) | \ (((uint32_t)(a) & 0x0000FF00) << 8) | \ (((uint32_t)(a) & 0x000000FF) << 24)) #else #define SWAP_BYTES(a) (a) #endif static unsigned po_rule_hash_func(SFHASHFCN *p, unsigned char *k, int n) { unsigned char *key; int ikey = *(int*)k; /* Since the input is really an int, put the bytes into a normalized * order so that the hash function returns consistent results across * on BE & LE hardware. */ ikey = SWAP_BYTES(ikey); /* Set a pointer to the key to pass to the hashing function */ key = (unsigned char *)&ikey; return sfhashfcn_hash(p, key, n); } /* Create a new PortObject2 */ PortObject2 * PortObject2New(int nrules) { PortObject2 *po = (PortObject2 *)SnortAlloc(sizeof(PortObject2)); po->item_list =(SF_LIST*) sflist_new(); if( !po->item_list ) { free( po ); return 0; } po->rule_hash =(SFGHASH*) sfghash_new(nrules,sizeof(int),0,free /* frees data - should be rule id ptrs == (int*) */); if( !po->rule_hash ) { sflist_free_all( po->item_list, free ); free( po ); return 0; } /* Use hash function defined above for hashing the key as an int. */ sfghash_set_keyops(po->rule_hash, po_rule_hash_func, memcmp); //sfhashfcn_static( po->rule_hash->sfhashfcn ); /* TODO: Leave this in, else we get different events */ return po; } /* * Set the name of the Port Object */ int PortObjectSetName(PortObject * po, char * name) { if( !po ) return -1; if( !name ) return -1; /* free the old name */ if(po->name) free(po->name); /* alloc a new name */ po->name = SnortStrdup(name); if( !po->name ) return -1; return 0; } /* * Free a PortObjectItem */ void PortObjectItemFree (PortObjectItem * poi) { if(poi) free(poi); } /* * Free the PortObject */ void PortObjectFree( void * pvoid ) { PortObject * po = (PortObject *)pvoid; DEBUG_WRAP(static int pof_cnt = 0; pof_cnt++;); DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"PortObjectFree-Cnt: %d ptr=%p\n",pof_cnt,pvoid);); if( !po ) return ; if( po->name ) free (po->name ); if( po->item_list) sflist_free_all( po->item_list, free ); if( po->rule_list) sflist_free_all( po->rule_list, free ); if (po->data && po->data_free) { po->data_free(po->data); } free( po ); } /* * Free the PortObject2 */ void PortObject2Free( void * pvoid ) { PortObject2 * po = (PortObject2 *)pvoid; DEBUG_WRAP(static int pof2_cnt = 0; pof2_cnt++;); DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"PortObjectFree2-Cnt: %d ptr=%p\n",pof2_cnt,pvoid);); if( !po ) return; if( po->name ) free (po->name ); if( po->item_list) sflist_free_all( po->item_list, free ); if( po->rule_hash) sfghash_delete( po->rule_hash ); if (po->bitop) { boFreeBITOP(po->bitop); free(po->bitop); } if (po->data && po->data_free) { po->data_free(po->data); } free( po ); } /* * Create a new PortObjectItem */ PortObjectItem * PortObjectItemNew(void) { PortObjectItem *poi = (PortObjectItem *)SnortAlloc(sizeof(PortObjectItem)); return poi; } /* * Add a PortObjectItem to a PortObject */ int PortObjectAddItem( PortObject * po, PortObjectItem * poi, int *errflag) { PortObjectItem *p; SF_LNODE *pos = NULL; if(!po || !poi) return 0; if(errflag) *errflag = 0; /* Make sure this is not a duplicate */ for(p=(PortObjectItem*)sflist_firstpos(po->item_list,&pos); p != 0; p=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { if((p->lport == poi->lport) && (p->hport == poi->hport)) { if(errflag) *errflag = POPERR_DUPLICATE_ENTRY; return -1; /* -1 chosen for consistency with sflist_add_tail */ } } return sflist_add_tail( po->item_list, poi ); } /* * Add a PortObjectItem to a PortObject */ int PortObjectAddPortObject(PortObject * podst, PortObject * posrc, int *errflag) { PortObjectItem *po; SF_LNODE *pos = NULL; int ret = 0; if(errflag) *errflag = 0; for(po=(PortObjectItem*)sflist_firstpos(posrc->item_list, &pos); po != 0; po=(PortObjectItem*)sflist_nextpos(posrc->item_list, &pos) ) { PortObjectItem *poi = PortObjectItemDup(po); if((ret = PortObjectAddItem(podst, poi, errflag)) != 0) return ret; } return ret; } /* Dup a PortObjectItem */ PortObjectItem * PortObjectItemDup( PortObjectItem * poi) { PortObjectItem * poinew; if( !poi ) return 0; poinew = PortObjectItemNew(); if( !poinew ) return 0; memcpy(poinew,poi,sizeof(PortObjectItem)); return poinew; } /* * Dup the PortObjects Item List, RuleList, and Name */ PortObject * PortObjectDup( PortObject * po ) { PortObject * ponew = NULL; PortObjectItem * poi = NULL; PortObjectItem * poinew = NULL; SF_LNODE * lpos = NULL; int * prid = NULL; int * prule = NULL; ponew = PortObjectNew(); if( !ponew ) return 0; /* Dup the Name */ if( po->name ) ponew->name = strdup(po->name); else ponew->name = strdup("dup"); if( !ponew->name ) { free( ponew ); return NULL; } /* Dup the Item List */ if( po->item_list ) { for(poi =(PortObjectItem*)sflist_firstpos(po->item_list,&lpos); poi != NULL; poi =(PortObjectItem*)sflist_nextpos(po->item_list,&lpos) ) { poinew = PortObjectItemDup( poi ); if(!poinew) { free( ponew->name ); free( ponew ); return 0; } PortObjectAddItem( ponew, poinew, NULL ); } } /* Dup the input rule list */ if( po->rule_list ) { for(prid = (int*)sflist_firstpos(po->rule_list,&lpos); prid != 0; prid = (int*)sflist_nextpos(po->rule_list,&lpos) ) { prule = calloc(1,sizeof(int)); if(!prule) { free( poinew ); free( ponew->name ); free( ponew ); return NULL; } *prule = *prid; sflist_add_tail(ponew->rule_list,prule); } } return ponew; } /* * Dup the PortObjects Item List, and Name */ PortObject * PortObjectDupPorts( PortObject * po ) { PortObject * ponew = NULL; PortObjectItem * poi = NULL; PortObjectItem * poinew = NULL; SF_LNODE * lpos = NULL; ponew = PortObjectNew(); if( !ponew ) return 0; /* Dup the Name */ if( po->name ) ponew->name = strdup(po->name); else ponew->name = strdup("dup"); if( !ponew->name ) { free( ponew ); return NULL; } /* Dup the Item List */ if( po->item_list ) { for(poi =(PortObjectItem*)sflist_firstpos(po->item_list,&lpos); poi != NULL; poi =(PortObjectItem*)sflist_nextpos(po->item_list,&lpos) ) { poinew = PortObjectItemDup( poi ); if(!poinew) { free (ponew->name); free (ponew); return NULL; } PortObjectAddItem( ponew, poinew, NULL ); } } return ponew; } /* * Dup the PortObjects Item List, Name, and RuleList->RuleHash */ PortObject2 * PortObject2Dup( PortObject * po ) { PortObject2 * ponew = NULL; PortObjectItem * poi = NULL; PortObjectItem * poinew = NULL; SF_LNODE * lpos = NULL; int * prid = NULL; int * prule = NULL; if( !po ) return NULL; if( !po->rule_list ) return NULL; ponew = PortObject2New(po->rule_list->count + PO_EXTRA_RULE_CNT); if( !ponew ) return NULL; /* Dup the Name */ if( po->name ) ponew->name = strdup(po->name); else ponew->name = strdup("dup"); if( !ponew->name ) { PortObject2Free(ponew); return NULL; } /* Dup the Item List */ if( po->item_list ) { for(poi =(PortObjectItem*)sflist_firstpos(po->item_list,&lpos); poi != NULL; poi =(PortObjectItem*)sflist_nextpos(po->item_list,&lpos) ) { poinew = PortObjectItemDup( poi ); if(!poinew) { PortObject2Free(ponew); return 0; } PortObjectAddItem( (PortObject*)ponew, poinew, NULL ); } } /* Dup the input rule list */ if( po->rule_list ) { for(prid = (int*)sflist_firstpos(po->rule_list,&lpos); prid != 0; prid = (int*)sflist_nextpos(po->rule_list,&lpos) ) { prule = calloc(1,sizeof(int)); if(!prule) { PortObject2Free(ponew); return NULL; } *prule = *prid; if( sfghash_add( ponew->rule_hash, prule, prule ) != SFGHASH_OK ) { free( prule ); } } } return ponew; } /* Add a Port to a PortObject */ int PortObjectAddPort( PortObject * po, int port, int not_flag ) { PortObjectItem * poi; poi = PortObjectItemNew(); if( !poi ) return -1; poi->type = PORT_OBJECT_PORT; if( not_flag ) poi->flags = PORT_OBJECT_NOT_FLAG; poi->lport = (unsigned short)port; poi->hport = (unsigned short)port; return sflist_add_tail( po->item_list, poi ); } /* Add a Port Range to a PortObject */ int PortObjectAddRange( PortObject * po, int lport, int hport, int not_flag ) { PortObjectItem * poi; poi = PortObjectItemNew(); if( !poi ) return -1; poi->type = PORT_OBJECT_RANGE; if( not_flag ) poi->flags = PORT_OBJECT_NOT_FLAG; poi->lport = (unsigned short)lport; poi->hport = (unsigned short)hport; return sflist_add_tail( po->item_list, poi ); } /* Add ANY port */ int PortObjectAddPortAny( PortObject * po ) { PortObjectItem * poi; if(!po) return -1 ; poi = PortObjectItemNew(); if( !poi ) return -1; poi->type = PORT_OBJECT_ANY; poi->lport = 0; poi->hport = MAXPORTS-1; if(!po->name) po->name = strdup("any"); if(!po->name) { free(poi); return -1; } return sflist_add_tail( po->item_list, poi ); } /* * Check if we have any ANY ports */ int PortObjectHasAny (PortObject * po ) { PortObjectItem *poi; if( !po ) return 0; for(poi=(PortObjectItem*)sflist_first(po->item_list); poi != 0; poi=(PortObjectItem*)sflist_next(po->item_list) ) { if( poi->type == PORT_OBJECT_ANY ) return 1; } return 0; } int PortObjectHasNot (PortObject * po ) { PortObjectItem *poi; if( !po ) return 0; for(poi=(PortObjectItem*)sflist_first(po->item_list); poi != 0; poi=(PortObjectItem*)sflist_next(po->item_list) ) { if ( poi->flags== PORT_OBJECT_NOT_FLAG) return 1; } return 0; } int PortObjectIsPureNot (PortObject * po ) { PortObjectItem *poi; int cnt=0; if( !po ) return 0; for(poi=(PortObjectItem*)sflist_first(po->item_list); poi != 0; poi=(PortObjectItem*)sflist_next(po->item_list) ) { cnt++; if ( poi->flags != PORT_OBJECT_NOT_FLAG) return 0; } if( cnt == 0 ) return 0; return 1; } /* * This does NOT return true if the object is an ANY port */ int PortObjectHasPort (PortObject * po, int port ) { PortObjectItem *poi; if( !po ) return 0; for(poi=(PortObjectItem*)sflist_first(po->item_list); poi != 0; poi=(PortObjectItem*)sflist_next(po->item_list) ) { switch( poi->type ) { case PORT_OBJECT_ANY: return 0; case PORT_OBJECT_PORT: if( poi->lport == (uint16_t)(port&0xffff) ) return 1; if( poi->flags & PORT_OBJECT_NOT_FLAG ) return 1; break; case PORT_OBJECT_RANGE: if( (uint16_t)port >= poi->lport && (uint16_t)port <= poi->hport ) return 1; if( poi->flags & PORT_OBJECT_NOT_FLAG ) return 1; break; } } return 0; } /* * This returns true if the object is an ANY port */ int PortObjectIncludesPort (PortObject * po, int port ) { PortObjectItem *poi; if( !po ) return 0; for(poi=(PortObjectItem*)sflist_first(po->item_list); poi != 0; poi=(PortObjectItem*)sflist_next(po->item_list) ) { switch( poi->type ) { case PORT_OBJECT_ANY: return 1; case PORT_OBJECT_PORT: if( poi->lport == (uint16_t)port ) return 1; if( poi->flags & PORT_OBJECT_NOT_FLAG ) return 1; break; case PORT_OBJECT_RANGE: if( (uint16_t)port >= poi->lport && (uint16_t)port <= poi->hport ) return 1; if( poi->flags & PORT_OBJECT_NOT_FLAG ) return 1; break; } } return 0; } /* * Locate a PortObject by Port number , this only locates the 1st one * This was a hack for testing.... */ PortObject * PortTableFindPortObjectByPort( PortTable * p , int port ) { PortObject * po; SF_LNODE * pos; for(po =(PortObject*)sflist_firstpos(p->pt_polist,&pos); po != NULL; po =(PortObject*)sflist_nextpos(p->pt_polist,&pos) ) { if( PortObjectHasPort ( po, port ) ) { return po; } } return 0; } /* * Calcs number of ports in this object, * object do not have to be normalized, * but if the same ports are referenced * twice, the count will be off. * * returns: * any = -1 * 0 = none/empty * >0 = number of ports */ int PortObjectPortCount (PortObject * po ) { PortObjectItem *poi; int cnt=0; int nports; if( !po ) return 0; for(poi=(PortObjectItem*)sflist_first(po->item_list); poi != 0; poi=(PortObjectItem*)sflist_next(po->item_list) ) { switch( poi->type ) { case PORT_OBJECT_ANY: return -1; case PORT_OBJECT_PORT: if( poi->flags & PORT_OBJECT_NOT_FLAG ) { cnt--; } else { cnt++; } break; case PORT_OBJECT_RANGE: nports = poi->hport - poi->lport + 1; if( poi->flags & PORT_OBJECT_NOT_FLAG ) { cnt-=nports; } else { cnt+=nports; } } } if( cnt < 0 ) { /* we have a pure not port or port range * * !80 = -1, add 64K (65535 -1 = 65534) * !80:81 = -2, (65535 - 2 = 65533) * * [:1023,!80] = 1024 - 1 = 1023 ports * */ cnt += SFPO_MAX_PORTS; /* add back in the acceptable ports */ } return cnt; } /* * Build a PortMap Char Array * returns: 0 if an ANY port. * n number of unique ports. */ char * PortObjectCharPortArray ( char * parray, PortObject * po, int * nports ) { int cnt = 0; unsigned not_cnt=0; PortObjectItem * poi; SF_LNODE * pos; if( !po || PortObjectHasAny ( po ) ) { return 0; /* ANY =64K */ } if( !parray ) { parray = (char*) calloc(1,SFPO_MAX_PORTS); if( !parray ) return 0; } for(poi=(PortObjectItem*)sflist_firstpos(po->item_list,&pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { /* Add ports that are not NOT'd */ if( poi->flags & PORT_OBJECT_NOT_FLAG ) { not_cnt++; continue; } if( poi->type == PORT_OBJECT_PORT ) { if( !parray[poi->lport] ) cnt++; parray[poi->lport] = 1; } else if( poi->type == PORT_OBJECT_RANGE ) { int i; for(i=poi->lport;i<=poi->hport;i++) { if( !parray[i] ) cnt++; parray[i] = 1; } } } /* Remove any NOT'd ports that may have been added above */ for(poi=(PortObjectItem*)sflist_firstpos(po->item_list,&pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { if( !( poi->flags & PORT_OBJECT_NOT_FLAG) ) continue; if( poi->type == PORT_OBJECT_PORT ) { if( parray[poi->lport] ) cnt--; parray[poi->lport] =0; } else if( poi->type == PORT_OBJECT_RANGE ) { int i; for(i=poi->lport;i<=poi->hport;i++) { if( parray[i] ) cnt--; parray[i] = 0; } } } /* A pure Not list */ if( po->item_list->count == not_cnt ) { int i; /* enable all of the ports */ for(i=0;iitem_list,&pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { if( !( poi->flags & PORT_OBJECT_NOT_FLAG) ) continue; /* should not happen */ if( poi->type == PORT_OBJECT_PORT ) { if( parray[poi->lport] ) cnt--; parray[poi->lport] =0; } else if( poi->type == PORT_OBJECT_RANGE ) { int k; for(k=poi->lport;k<=poi->hport;k++) { if( parray[k] ) cnt--; parray[k] = 0; } } } } *nports = cnt; return parray; } /* * Make a list of ports form the char array, each char is either * on or off. */ static SF_LIST * PortObjectItemListFromCharPortArray( char * parray, int n, int nports ) { int i, lport ,hport; SF_LIST * plist; PortObjectItem * poi; plist = sflist_new(); if( !plist ) return 0; for(i=0; (i 0); i++) { if( parray[i] == 0 ) continue; /* Either a port or the start of a range */ lport = hport = i; nports--; for(i++;itype = PORT_OBJECT_PORT; poi->lport = (unsigned short)lport; } else { poi->type = PORT_OBJECT_RANGE; poi->lport =(unsigned short)lport; poi->hport =(unsigned short)hport; } if( sflist_add_tail( plist, poi ) ) { sflist_free_all( plist, free ); return 0; } } return plist; } /* * Removes Ports in B from A ... A = A - B */ int PortObjectRemovePorts( PortObject * a, PortObject * b ) { int i; int nportsa; int nportsb; SF_LIST * plist; static char pA[SFPO_MAX_PORTS]; static char pB[SFPO_MAX_PORTS]; memset(pA,0,SFPO_MAX_PORTS); memset(pB,0,SFPO_MAX_PORTS); /* Create a char array of ports */ PortObjectCharPortArray ( pA, a, &nportsa ); /* Create a char array of ports */ PortObjectCharPortArray ( pB, b, &nportsb ); for(i=0;iitem_list, free ); /* Replace the old PortObject list */ a->item_list = plist; return 0; } /* * Normalize a port object * * The reduces multiple references to a given port to a single unique reference * This function should be used on each PortObject, once it's completed. After * the normalized PortObject is created, the input PortObject may be deleted. */ int PortObjectNormalize (PortObject * po ) { SF_LIST * plist; int nports = 0; static char parray[SFPO_MAX_PORTS]; if( PortObjectHasAny ( po ) ) { return 0; /* ANY =65K */ } memset(parray,0,SFPO_MAX_PORTS); /* Create a char array of ports */ PortObjectCharPortArray ( parray, po, &nports ); /* Convert the array into a Port Object list */ plist = PortObjectItemListFromCharPortArray( parray, SFPO_MAX_PORTS, nports ); if( !plist ) return -1; /* Release the old port list */ sflist_free_all( po->item_list, free ); /* Replace the old PortObject list */ po->item_list = plist; return nports; } /* * Negate an entire PortObject */ int PortObjectNegate (PortObject * po ) { int i; SF_LIST * plist; int nports = 0; static char parray[SFPO_MAX_PORTS]; if( PortObjectHasAny ( po ) ) { return 0; /* ANY =65K */ } memset(parray,0,SFPO_MAX_PORTS); /* Create a char array of ports */ PortObjectCharPortArray ( parray, po, &nports ); for(i=0;iitem_list, free ); /* Replace the old PortObject list */ po->item_list = plist; return nports; } /* PortObjects should be normalized, prior to testing */ static int PortObjectItemsEqual(PortObjectItem * a, PortObjectItem * b ) { if( a->type != b->type ) return 0; switch( a->type ) { case PORT_OBJECT_ANY: return 1; case PORT_OBJECT_PORT: if( a->lport == b->lport ) return 1; break; case PORT_OBJECT_RANGE: if( a->lport == b->lport && a->hport == b->hport ) return 1; break; } return 0; } /* PortObjects should be normalized, prior to testing */ int PortObjectEqual( PortObject * a, PortObject *b ) { PortObjectItem *pa; PortObjectItem *pb; SF_LNODE * posa; SF_LNODE * posb; if( a->item_list->count != b->item_list->count ) return 0; pa = (PortObjectItem*)sflist_firstpos(a->item_list,&posa); pb = (PortObjectItem*)sflist_firstpos(b->item_list,&posb); while( pa && pb ) { if( !PortObjectItemsEqual( pa, pb) ) return 0; pa = (PortObjectItem*)sflist_nextpos(a->item_list,&posa); pb = (PortObjectItem*)sflist_nextpos(b->item_list,&posb); } if( pa || pb ) /* both are not done - cannot match */ return 0; return 1; /* match */ } /* Dup and Append PortObjectItems from pob to poa */ PortObject * PortObjectAppend(PortObject * poa, PortObject * pob ) { PortObjectItem * poia; PortObjectItem * poib; for( poib = (PortObjectItem*) sflist_first(pob->item_list); poib!= 0; poib = (PortObjectItem*)sflist_next(pob->item_list) ) { poia = PortObjectItemNew(); if(!poia) return 0; memcpy(poia,poib,sizeof(PortObjectItem)); sflist_add_tail(poa->item_list,poia); } return poa; } /* Dup and append rule list numbers from pob to poa */ PortObject * PortObjectAppendPortObject(PortObject * poa, PortObject * pob ) { int * prid; int * prid2; SF_LNODE * lpos; for( prid = (int*) sflist_firstpos(pob->rule_list,&lpos); prid!= 0; prid = (int*)sflist_nextpos(pob->rule_list,&lpos) ) { prid2 = calloc( 1, sizeof(int)); if( !prid2 ) return 0; *prid2 = *prid; sflist_add_tail(poa->rule_list,prid2); } return poa; } /* Dup and append rule list numbers from pob to poa */ PortObject2 * PortObject2AppendPortObject(PortObject2 * poa, PortObject * pob ) { int * prid; int * prid2; SF_LNODE * lpos; for( prid = (int*) sflist_firstpos(pob->rule_list,&lpos); prid!= 0; prid = (int*)sflist_nextpos(pob->rule_list,&lpos) ) { prid2 = calloc( 1, sizeof(int)); if( !prid2 ) return 0; *prid2 = *prid; if( sfghash_add(poa->rule_hash,prid2,prid2) != SFGHASH_OK ) { free(prid2); } } return poa; } /* Dup and append rule list numbers from pob to poa */ PortObject2 * PortObject2AppendPortObject2(PortObject2 * poa, PortObject2 * pob ) { int * prid; int * prid2; SFGHASH_NODE * node; for( node = sfghash_findfirst(pob->rule_hash); node!= NULL; node = sfghash_findnext(pob->rule_hash) ) { prid = node->data; if( !prid ) continue; prid2 = calloc( 1, sizeof(int)); if( !prid2 ) return 0; *prid2 = *prid; if( sfghash_add(poa->rule_hash,prid2,prid2) != SFGHASH_OK ) { free( prid2 ); } } return poa; } /* * Append Ports and Rules from pob to poa */ PortObject * PortObjectAppendEx(PortObject * poa, PortObject * pob ) { // LogMessage("PortObjectAppendEx: appending ports\n"); if( !PortObjectAppend( poa, pob ) ) return 0; //LogMessage("PortObjectAppendEx: appending rules\n"); if( !PortObjectAppendPortObject( poa, pob ) ) return 0; return poa; } /* * Append Ports and Rules from pob to poa */ PortObject2 * PortObjectAppendEx2(PortObject2 * poa, PortObject * pob ) { // LogMessage("PortObjectAppendEx: appending ports\n"); if( !PortObjectAppend((PortObject*) poa, pob ) ) return 0; // LogMessage("PortObjectAppendEx: appending rules\n"); if( !PortObject2AppendPortObject( poa, pob ) ) return 0; return poa; } /* PORT TABLE FUNCTIONS */ /* Create a new table */ PortTable * PortTableNew(void) { PortTable * p; p = (PortTable*) calloc(1,sizeof(PortTable)); if(!p) return 0; p->pt_polist = sflist_new(); if(!p->pt_polist ) { free(p); return 0; } p->pt_lrc = PTBL_LRC_DEFAULT; /* 10 rules, user should really control these */ p->pt_optimize = 1; /* if disabled, only one merged rule group is used */ return p; } void PortTableFree(PortTable *p) { int i; SFGHASH_NODE *node; if (!p) return; if (p->pt_polist) { sflist_free_all(p->pt_polist, PortObjectFree ); } if (p->pt_mpo_hash) { PortObject2 *po; for (node = sfghash_findfirst(p->pt_mpo_hash); node; node = sfghash_findnext(p->pt_mpo_hash)) { po = node->data; /* Free the data from this entry */ PortObject2Free(po); } sfghash_delete(p->pt_mpo_hash); } if (p->pt_plx_list) { sflist_free_all(p->pt_plx_list, plx_free); } if (p->pt_mpxo_hash) { #if 0 PortObject2 *po; for (node = sfghash_findfirst(p->pt_mpxo_hash); node; node = sfghash_findnext(p->pt_mpxo_hash)) { po = node->data; /* Free the data from this entry */ //PortObject2Free(po); } #endif sfghash_delete(p->pt_mpxo_hash); } for (i=0;ipt_port_object[i]) { PortObject2Free(p->pt_port_object[i]); } #endif } free(p); } PortObject * PortTableFindInputPortObjectName(PortTable * pt, char * po_name) { SF_LNODE * lpos; PortObject * po; if( !pt ) return NULL; if( !po_name ) return NULL; /* Normalize each of the input port objects */ for(po =(PortObject*)sflist_firstpos(pt->pt_polist,&lpos); po!=0; po =(PortObject*)sflist_nextpos(pt->pt_polist,&lpos) ) { if( po->name ) { if( strcmp(po->name,po_name)==0 ) { return po; } } } return NULL; } /* * Find PortObject by PortItem Info */ PortObject * PortTableFindInputPortObjectPorts( PortTable * pt, PortObject * pox ) { SF_LNODE * lpos; PortObject * po; if( !pt ) return NULL; if( !pox ) return NULL; for(po =(PortObject*)sflist_firstpos(pt->pt_polist,&lpos); po!=0; po =(PortObject*)sflist_nextpos(pt->pt_polist,&lpos) ) { if( PortObjectEqual( po, pox ) ) { return po; } } return NULL; } int PortTableNormalizeInputPortObjects( PortTable *p ) { SF_LNODE * lpos; PortObject * po; /* Normalize each of the input port objects */ for(po =(PortObject*)sflist_firstpos(p->pt_polist,&lpos); po!=0; po =(PortObject*)sflist_nextpos(p->pt_polist,&lpos) ) { PortObjectNormalize(po); } return 0; } int PortObjectAddRule( PortObject * po , int rule ) { int * pruleid; //LogMessage("Adding Rule %d to Port Object '%s'\n",rule,po->name); if( !po ) return -1; if( !po->rule_list ) return -1; /* Add rule index to rule list */ pruleid = calloc(1,sizeof(int)); if( !pruleid ) { return -1; } *pruleid = rule; sflist_add_tail( po->rule_list, pruleid ); return 0; } /* Add Users PortObjects to the Table We save the users port object, so it's no longer the users. */ int PortTableAddObject( PortTable *p, PortObject * po ) { SF_LNODE * lpos; PortObject * pox; /* Search for the Port Object in the input list, by address */ for(pox =(PortObject*)sflist_firstpos(p->pt_polist,&lpos); pox!=0; pox =(PortObject*)sflist_nextpos(p->pt_polist,&lpos) ) { if( pox == po ) { /* already in list - just return */ return 0; } } /* Save the users port object, if not already in the list */ if( sflist_add_tail(p->pt_polist,po) ) return -1; return 0; } /* Hash routine for hashing PortObjects as Keys p - SFHASHFCN * d - PortObject * n = 4 bytes (sizeof*) - not used Don't use this for type=ANY port objects */ static unsigned PortObject_hash( SFHASHFCN * p, unsigned char *d, int n ) { unsigned hash = p->seed; PortObjectItem * poi; PortObject * po; SF_LNODE * pos; #ifdef WIN32 n = n; /* This quiets a Win32 warning */ #endif po = *(PortObject**) d; /* hash up each item */ for(poi =(PortObjectItem*)sflist_firstpos(po->item_list,&pos); poi != NULL; poi =(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { switch(poi->type) { case PORT_OBJECT_PORT: hash *= p->scale; hash += poi->lport & 0xff; hash *= p->scale; hash += (poi->lport >> 8) & 0xff; break; case PORT_OBJECT_RANGE: hash *= p->scale; hash += poi->lport & 0xff; hash *= p->scale; hash += (poi->lport >> 8) & 0xff; hash *= p->scale; hash += poi->hport & 0xff; hash *= p->scale; hash += (poi->hport >> 8) & 0xff; break; } } return hash ^ p->hardener; } /* * Merge multiple PortObjects into a final PortObject2, * this merges ports and rules. * * merge po's in pol, find a previous instance add it. * * This is done as follows: * 1) check if it's in the plx table-mhashx, this uses the list of * addresses of the Input PortObjects as it's key, not the ports. * This is quick and does not require assembling/merging the port * objects intoa PortObject2 1st. * 2) if found were done, otherwise * 3) make a merged PortObject2 * 4) Try adding the PortObject2 to it's table - mhash * a) if it adds go on, else * b) if it's already in the table * 1) get the one in the table * 2) add any ports in the just created one * 3) free the one just created * 5) Create a plx object * 6) Add the plx object to the plx Table * 1) if it's already in the object - fail this contradicts 1) * 7) return the create PortObject2, or the one retrived from the * PortObject table. * * pol - list of input PortObject pointers * pol_cnt- count in 'pol' * mhash - stores the merged ports, using the merged port objects port list as a key. * mhashx - stores plx keys, and PortObject2 *'s as data for the final merged port objects, * the plx keys provide a quicker way to compare port lists to ensure if two ports * are using the same set of rules (port objects). * mhash and mhashx reference the same port objects as data, but use different keys for lookup * purposes. Once we perform a merge we store the results, using the 'plx' as the key for future lookup. * plx - key to use to lookup and store the merged port object * * */ static PortObject2 * _merge_N_pol( SFGHASH * mhash, SFGHASH * mhashx, SF_LIST * plx_list, void ** pol, int pol_cnt, plx_t * plx ) { PortObject2 * ponew; PortObject2 * pox; plx_t * plx_tmp; int stat; int i; /* * Check for the merged port object in the plx table */ DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS, "++++n=%d sfghash_find-mhashx\n",pol_cnt);); ponew = sfghash_find( mhashx, &plx ); if( ponew ) { DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS, "n=%d ponew found in mhashx\n",pol_cnt);); return ponew; } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS, "n=%d posnew not found in mhashx\n",pol_cnt);); /* * Merge the port objects together - ports and rules */ /* Dup the 1st port objects rules and ports */ ponew = PortObject2Dup( (PortObject *)pol[0] ); if( !ponew ) { FatalError("Could not Dup2\n"); } /* Merge in all the other port object rules and ports */ if( pol_cnt > 1 ) { for(i=1;irule_list->count,i);); PortObjectAppendEx2( ponew, (PortObject *)pol[i] ); DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS, "*** merged port-object[%d], %d rules\n", i,ponew->rule_hash->count);); } PortObjectNormalize( (PortObject*)ponew ); } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS, "*** merged %d port objects, %d rules\n", pol_cnt,ponew->rule_hash->count);); DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"*** merged ponew - follows: \n");); // PortObjectPrint2(ponew); /* * Add the Merged PortObject2 to the PortObject2 hash table * keyed by ports. */ DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"n=%d sfghash_add-mhash\n",pol_cnt);); stat =sfghash_add( mhash, &ponew, ponew ); if( stat != SFGHASH_OK ) { /* This is possible since PLX hash on a different key */ if( stat == SFGHASH_INTABLE ) { DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"n=%d sfghash_add-mhash ponew in table\n",pol_cnt);); DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"n=%d sfghash_find-mhash ponew\n",pol_cnt);); pox = sfghash_find(mhash,&ponew); if( pox ) { PortObject2AppendPortObject2(pox,ponew); DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"sfportobject.c: merge_N_pol() line=%d SFGHASH_INTABLE\n",__LINE__);); PortObject2Free( ponew ); ponew = pox; DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"n=%d sfghash_find-mhash ponew found, new rules merged\n",pol_cnt);); } else { FatalError("mhash add/find error n=%d\n", pol_cnt); } } else { FatalError("Could not add ponew to hash table- error\n"); } } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"***%d ports merged object added to mhash table\n",pol_cnt);); /* * Create a plx node and add it to plx table * as the key with the merged port object as the data */ plx_tmp = plx_new( pol, pol_cnt); if(!plx_tmp) { FatalError("plx_new: memory alloc error\n"); } sflist_add_head(plx_list, (void *)plx_tmp); /* * Add the plx node to the PLX hash table */ DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"n=%d sfghash_add-mhashx\n",pol_cnt);); stat = sfghash_add( mhashx, &plx_tmp, ponew ); if( stat != SFGHASH_OK ) { if( stat == SFGHASH_INTABLE ) { FatalError("Could not add merged plx to PLX HASH table-INTABLE\n"); } else { FatalError("Could not add merged plx to PLX HASH table\n"); } } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"Added-%d Merged Rule Groups to PLX HASH\n",pol_cnt);); /* * Validate hash table entry */ if( sfghash_find( mhashx, &plx_tmp ) != ponew ) { FatalError("Find after add failed on PLX HASH table key\n"); } return ponew; } /* * Merge Input Port Objects into rule collections that are particular to * each port. We store the results as objects and point to these in the * pt_port_object[MAX_PORTS] array. * * We use plx_t types to manage tracking and testing for merged large * rule groups, and merged small port groups. * * mhash - table of merged port objects ( built and used here ) * mhashx - table of plx_t objects ( built and used here ) * pol - list of input port objects touching the current port * pol_cnt - number of port objects in port list * lcnt - large rule count * */ static PortObject2 * PortTableCompileMergePortObjectList2(SFGHASH * mhash, SFGHASH * mhashx, SF_LIST * plx_list, PortObject * pol[], int pol_cnt, unsigned int lcnt ) { PortObject2 * ponew = NULL; PortObject2 * posnew = NULL; static void * polarge[SFPO_MAX_LPORTS]; static void * posmall[SFPO_MAX_LPORTS]; int nlarge = 0; int nsmall = 0; plx_t plx_small; plx_t plx_large; unsigned largest; int i; /* * Find the largest rule count of all of the port objects */ largest = 0; for(i=0;irule_list->count >= (unsigned)lcnt ) { if( pol[i]->rule_list->count > largest ) largest = pol[i]->rule_list->count; } } /* * Classify PortObjects as large or small based on rule set size * and copy them into separate lists */ for(i=0;irule_list->count >= (unsigned)lcnt ) { if( nlarge < SFPO_MAX_LPORTS ) polarge[ nlarge++ ] = (void *)pol[i]; } else { if( nsmall < SFPO_MAX_LPORTS ) posmall[ nsmall++ ] = (void *)pol[i]; } } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"*** %d small rule groups, %d large rule groups\n",nsmall,nlarge);); /* * Sort the pointers to the input port objects so * we always get them in the same order for key comparisons */ if( nlarge > 1 ) qsort( polarge, nlarge, sizeof(void*), p_keycmp ); if( nsmall > 1 ) qsort( posmall, nsmall, sizeof(void*), p_keycmp ); DEBUG_WRAP( for(i=0;itail && (parray[port]->tail->ndata == po)) return; sflist_add_tail( parray [port], po ); } // Update port object lists static inline void update_port_lists(SF_LIST **parray, PortObject *po) { PortObjectItem *poi; int port; bool not_flag_set = FALSE; for(poi=(PortObjectItem*)sflist_first(po->item_list); poi != 0; poi=(PortObjectItem*)sflist_next(po->item_list) ) { if( poi->type == PORT_OBJECT_ANY) return; else if( poi->type == PORT_OBJECT_PORT) { if (poi->flags & PORT_OBJECT_NOT_FLAG) { not_flag_set = TRUE; break; } add_port_object(parray, poi->lport, po); } else if( poi->type == PORT_OBJECT_RANGE) { if (poi->flags & PORT_OBJECT_NOT_FLAG) { not_flag_set = TRUE; break; } for( port = poi->lport; port <= poi->hport; port++ ) { add_port_object(parray, port, po); } } } if (not_flag_set) { for( port = 0; port < SFPO_MAX_PORTS; port++ ) { add_port_object(parray, port, po); } } } // Create optimized port lists per port static inline SF_LIST **create_port_lists(PortTable * p) { PortObject *po; SF_LNODE *lpos; SF_LIST **parray = calloc(sizeof(SF_LIST *),SFPO_MAX_PORTS); if(!parray) return NULL; /* Build a list of port objects touching port 'i' */ for(po=sflist_firstpos(p->pt_polist,&lpos); po; po=sflist_nextpos(p->pt_polist,&lpos) ) { update_port_lists(parray, po); } return parray; } static inline void delete_port_lists(SF_LIST **parray) { int port; for( port = 0; port < SFPO_MAX_PORTS; port++ ) { SF_LIST *list = (SF_LIST *) parray[port]; if (list) sflist_free(list); } } /* * * * mhash * mhashx data structure used: p->pt_polist */ int PortTableCompileMergePortObjects( PortTable * p ) { SF_LNODE * lpos; SFGHASH * mhash; SFGHASH * mhashx; SFGHASH_NODE * node; SF_LIST * plx_list; int id = PO_INIT_ID; static PortObject * pol[SFPO_MAX_LPORTS]; // TODO: dynamically allocate int pol_cnt; char * parray = NULL; int i; SF_LIST **optimized_pl; DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"***\n***Merging PortObjects->PortObjects2\n***\n");); /* Create a Merged Port Object Table - hash by ports */ mhash = sfghash_new(PO_HASH_TBL_ROWS, sizeof(PortObject *), 0 /*userkeys-no*/, 0 /*free data-don't*/); if( !mhash ) return -1; /* Setup hashing function and key comparison function */ sfhashfcn_set_keyops( mhash->sfhashfcn, PortObject_hash, PortObject_keycmp ); /* remove randomness */ if (ScStaticHash()) sfhashfcn_static( mhash->sfhashfcn ); p->pt_mpo_hash = mhash; /* Create a Merged Port Object Table - hash by ports */ mhashx = sfghash_new(PO_HASH_TBL_ROWS, sizeof(plx_t *), 0/*userkeys-no*/, 0/*freedata()-don't*/); if( !mhashx ) return -1; /* Setup hashing function and key comparison function */ sfhashfcn_set_keyops( mhashx->sfhashfcn,plx_hash,plx_keycmp ); /* remove randomness */ if (ScStaticHash()) sfhashfcn_static( mhashx->sfhashfcn ); p->pt_mpxo_hash = mhashx; DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"***\n*** PortList-Merging, Large Rule groups must have %d rules\n",p->pt_lrc);); plx_list = sflist_new(); sflist_init(plx_list); p->pt_plx_list = plx_list; optimized_pl = create_port_lists(p); if(!optimized_pl) { FatalError("Memory error in PortTableCompile()\n"); } /* * For each port, merge rules from all port objects that touch the port * into an optimal object, that may be shared with other ports. */ for(i=0;ipt_port_object[i] = 0; if( !pol_cnt ) { //port not contained in any PortObject continue; } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"*** merging list for port[%d] \n",i);fflush(stdout);); /* merge the rules into an optimal port object */ p->pt_port_object[i] = PortTableCompileMergePortObjectList2( mhash, mhashx, plx_list, pol, pol_cnt, p->pt_lrc ); if( !p->pt_port_object[i] ) { FatalError(" Could not merge PorObjectList on port %d\n",i); } /* give the new compiled port object an id of its own */ p->pt_port_object[i]->id = id++; DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"\n");fflush(stdout);); } delete_port_lists(optimized_pl); free(optimized_pl); /* * Normalize the Ports so they indicate only the ports that * reference the composite port object */ /* 1st- Setup bitmasks for collecting ports */ for(node=sfghash_findfirst(mhashx); node; node=sfghash_findnext(mhashx) ) { unsigned char * buf; PortObject2 * poa; poa = (PortObject2*)node->data; if( !poa ) continue; if (!poa->bitop) { poa->bitop = calloc(1,sizeof(BITOP)); if( !poa->bitop) { FatalError("Memory error in PortTableCompile\n"); } buf = calloc(1,8192); if( !buf ) { FatalError("Memory alloc error in PortObjectCompile()\n"); } if( boInitStaticBITOP(poa->bitop,8192,buf) ) { FatalError("BitOp error in PortObjectCompile()\n"); } } } /* Count how many ports each final port-object is used on */ for(i=0;ipt_port_object[i]; if(poa) { poa->port_cnt++; if( poa->bitop ) { if( boSetBit(poa->bitop, (unsigned int) i ) ) { FatalError("BitOp-Set error\n"); } } else { FatalError("NULL po->bitop in po on port %d\n",i); } } } /* get a port array 64K bytes */ parray = calloc(1,SFPO_MAX_PORTS); if(!parray) { FatalError("Memory error in PortTableCompile()\n"); } /* Process Port-Bitop map and print final port-object usage stats */ for(node=sfghash_findfirst(mhashx); node; node=sfghash_findnext(mhashx) ) { SF_LIST * plist; PortObject2 * po; int nports; po = (PortObject2*)node->data; if( !po ) { FatalError("MergePortOBject-NormalizePorts -NULL po\n"); } if( !po->port_cnt )/* port object is not used ignore it */ continue; if( !po->bitop ) { //FatalError("MergePortOBject-NormalizePorts -NULL po->bitop\n"); continue; } /* Convert the bitop bits to a char array */ memset(parray,0,SFPO_MAX_PORTS); nports = 0; for(i=0;ibitop, i ) ) { parray[ i ] = 1; nports++; } } /* Release bit buffer for each port object */ if( po->bitop ) { //if( po->bitop->pucBitBuffer ) //{ // free( po->bitop->pucBitBuffer ); // po->bitop->pucBitBuffer = NULL; //} boFreeBITOP(po->bitop); free( po->bitop ); po->bitop=NULL; } /* Build a PortObjectItem list from the char array */ plist = PortObjectItemListFromCharPortArray( parray, SFPO_MAX_PORTS, nports); if( !plist ) { FatalError("MergePortObjects: No PortObjectItems in portobject\n"); } /* free the original list */ sflist_free_all( po->item_list, free ); /* set the new list - this is a list of port itmes for this port object */ po->item_list = plist; DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"port-object id = %d, port cnt = %d\n",po->id,po->port_cnt);); } if(parray) free(parray); return 0; } /* * * Verify all rules in 'po' list are in 'po2' hash * * return 0 - OK * !0 - a rule in po is not in po2 */ static int _po2_include_po_rules( PortObject2 * po2, PortObject * po ) { //SFGHASH_NODE * node; int * pid; int * id; SF_LNODE * rpos; /* get each rule in po */ for(pid=sflist_firstpos(po->rule_list,&rpos); pid; pid=sflist_nextpos(po->rule_list,&rpos) ) { /* find it in po2 */ id =(int*) sfghash_find(po2->rule_hash,pid); /* make sure it's in po2 */ if(!id ) { return 1; /* error */ } } return 0; } /* * Perform a consistency check on the final port+rule objects * * Walk the rules */ int PortTableConsistencyCheck( PortTable *p ) { char * parray = 0; SFGHASH_NODE * node; int i; SF_LNODE * pos; SF_LNODE * ipos; PortObject * ipo; PortObject2 * lastpo = NULL; PortObjectItem * poi; parray = calloc(1,SFPO_MAX_PORTS); if(!parray) { FatalError("Memory eror in PortTableComopile\n"); } /* Make sure each port is only in one composite port object */ for(node=sfghash_findfirst(p->pt_mpo_hash); node; node=sfghash_findnext(p->pt_mpo_hash) ) { PortObject2 * po; po = (PortObject2*)node->data; if( !po ) { FatalError("PortObject consistency Check failed, hash table problem\n"); } if( !po->port_cnt )/* port object is not used ignore it */ continue; for(i=0;ipt_polist,&pos); ipo; ipo=sflist_nextpos(p->pt_polist,&pos) ) { /* * for each port in this object get the composite port object * assigned to that port and verify all of the input objects rules * are in the composite object. This verifies all rules are applied * to the originally intended port. */ for(poi=sflist_firstpos(ipo->item_list,&ipos); poi; poi=sflist_nextpos(ipo->item_list,&ipos) ) { switch(poi->type) { case PORT_OBJECT_ANY: /* do nothing */ break; case PORT_OBJECT_PORT: if( _po2_include_po_rules( p->pt_port_object[ poi->lport ], ipo ) ) { FatalError("InputPortObject<->CompositePortObject consistency Check II failed!\n"); } break; case PORT_OBJECT_RANGE: { for(i=poi->lport;i<=poi->hport;i++) { /* small optimization*/ if( lastpo != p->pt_port_object[ i ] ) { if( _po2_include_po_rules( p->pt_port_object[ i ], ipo ) ) { FatalError("InputPortObject<->CompositePortObject consistency Check II failed!\n"); } lastpo = p->pt_port_object[ i ]; } } } break; } } } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS, "***\n***Port Table Compiler Consistency Check Phase-II Passed !!! - Good to go Houston\n****\n");); return 0; } /* * Compile the PortTable * * This builds a set of Port+Rule objects that are in some way an optimal * set of objects to indicate which rules to apply to which ports. Since * these groups are calculated consistency checking is done with the finished * objects. */ int PortTableCompile( PortTable * p ) { /* * If not using an optimized Table use the rule_index_map in parser.c */ if( !p->pt_optimize ) { return 0; } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"#PortTableCompile: Compiling Port Array Lists\n");); if( PortTableCompileMergePortObjects( p ) ) { FatalError("Could not create PortArryayLists\n"); } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"Done\n");fflush(stdout);); if(ScTestMode()) PortTableConsistencyCheck(p); return 0; } static int integer_compare( const void *arg1, const void *arg2 ) { if( *(int*)arg1 < *(int*)arg2 ) return -1; if( *(int*)arg1 > *(int*)arg2 ) return 1; return 0; } static int * RuleListToSortedArray( SF_LIST * rl ) { SF_LNODE * pos = NULL; int * prid; int * ra; int k=0; if( !rl ) return 0; if(!rl->count) return NULL; ra = (int *)SnortAlloc(rl->count * sizeof(int)); for( prid = sflist_firstpos(rl,&pos); prid!= 0 && k < (int)rl->count; prid = sflist_nextpos(rl,&pos) ) { ra[k++] = *prid; } /* sort the array */ qsort(ra,rl->count,sizeof(int),integer_compare); return ra; } /**sort and uniq rule list. */ void RuleListSortUniq( SF_LIST * rl ) { unsigned i; int lastRuleIndex = -1; SF_LNODE *pos = NULL; int *currNode = NULL; unsigned uniqElements = 0; int *node = 0; int * rlist = NULL; rlist = RuleListToSortedArray(rl); if(!rlist ) { return ; } currNode = sflist_firstpos(rl,&pos); if (currNode == NULL) { free(rlist); return; } for(i=0; i < rl->count; i++) { if (rlist[i] > lastRuleIndex) { *currNode = lastRuleIndex = rlist[i]; //replace the next element in place currNode = sflist_nextpos(rl,&pos); uniqElements++; } } //free the remaining list nodes while (uniqElements != rl->count) { node = sflist_remove_tail (rl); free(node); } free(rlist); } /**Sort and make rule index in all port objects unique. Multiple policies may add * the same rule which can lead to duplication. */ void PortTableSortUniqRules( PortTable * p ) { PortObject * po; SF_LNODE *pos = NULL; for(po =(PortObject*)sflist_firstpos(p->pt_polist,&pos); po != NULL; po =(PortObject*)sflist_nextpos(p->pt_polist,&pos) ) { RuleListSortUniq(po->rule_list); } } static int * RuleHashToSortedArray( SFGHASH * rh ) { int * prid; int * ra; int k = 0; SFGHASH_NODE * node; if( !rh ) return 0; if(!rh->count) return NULL; ra = (int *)SnortAlloc(rh->count * sizeof(int)); for( node = sfghash_findfirst(rh); node != 0 && k < (int)rh->count; node = sfghash_findnext(rh) ) { prid = node->data; if( prid ) { ra[k++] = *prid; } } /* sort the array */ qsort(ra,rh->count,sizeof(int),integer_compare); return ra; } /* * Print Input Port List */ void PortTablePrintInput( PortTable * p ) { PortObject * po; SF_LNODE * pos; LogMessage("*** %d PortObjects in Table\n",p->pt_polist->count); for(po =(PortObject*)sflist_firstpos(p->pt_polist,&pos); po!=0; po =(PortObject*)sflist_nextpos(p->pt_polist,&pos) ) { PortObjectPrint( po ); } } void PortTablePrintInputEx( PortTable * p, void (*print_index_map)(int index, char *buf, int bufsize) ) { PortObject * po; SF_LNODE * pos; for(po =(PortObject*)sflist_firstpos(p->pt_polist,&pos); po != NULL; po =(PortObject*)sflist_nextpos(p->pt_polist,&pos) ) { PortObjectPrintEx( po, print_index_map ); } } /* Prints Compiled Ports/Rules Objects */ int PortTablePrintCompiledEx( PortTable * p , void (*print_index_map)(int index, char *buf, int bufsize) ) { PortObject2 * po = NULL; SFGHASH_NODE * node = NULL; LogMessage(" *** PortTableCompiled [ %d compiled port groups ] \n\n", p->pt_mpo_hash->count); for(node = sfghash_findfirst(p->pt_mpo_hash); node!= 0; node = sfghash_findnext(p->pt_mpo_hash) ) { po = node->data; PortObject2PrintEx( po, print_index_map ); } return 0; } /* Print port items. Used internally by sfportobject.c. Buffer assumed trusted. */ static void PortObjectItemPrint ( PortObjectItem * poi, char *dstbuf, int bufsize ) { SnortSnprintfAppend(dstbuf, bufsize, " "); if( poi->flags & PORT_OBJECT_NOT_FLAG ) SnortSnprintfAppend(dstbuf, bufsize, "!"); switch( poi->type ) { case PORT_OBJECT_PORT : SnortSnprintfAppend(dstbuf, bufsize, "%u", poi->lport); break; case PORT_OBJECT_RANGE : SnortSnprintfAppend(dstbuf, bufsize, "%u:%u",poi->lport,poi->hport); break; case PORT_OBJECT_ANY: SnortSnprintfAppend(dstbuf, bufsize, "any"); break; default: SnortSnprintfAppend(dstbuf, bufsize, " unknown port type @ %p", (void*)poi); break; } } void PortObjectPrintPortsRaw(PortObject * po ) { PortObjectItem * poi = NULL; SF_LNODE * pos = NULL; char * buf; int bufsize; /* Need to buffer the string so we only do one LogMessage, * due to syslog output. The largest string needed to represent * each portobject is the length required to represent: * " unknown port type @ 0x<8 max bytes>" (See PortObjectItemPrint), or: * 30 bytes. For the entire list, need room for spaces and brackets and * potential negations. Or: * list_size * (30 + 1space_for_each_element, + * 1potential_negation) + surrounding_whitespace + brackets + NULL */ bufsize = po->item_list->count * (30 + 1 + 1) + 5; buf = (char*)SnortAlloc(bufsize); SnortSnprintfAppend(buf, bufsize, " ["); for(poi=(PortObjectItem*)sflist_firstpos(po->item_list, &pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list, &pos) ) { PortObjectItemPrint(poi, buf, bufsize); } SnortSnprintfAppend(buf, bufsize, " ]"); LogMessage("%s", buf); free(buf); } void PortObject2PrintPorts(PortObject2 * po ) { PortObjectItem * poi = NULL; SF_LNODE * pos = NULL; int bufsize = sizeof(po_print_buf); po_print_buf[0] = '\0'; SnortSnprintfAppend(po_print_buf, bufsize, " PortObject "); if( po->name ) { SnortSnprintfAppend(po_print_buf, bufsize, "%s ", po->name); } SnortSnprintfAppend(po_print_buf, bufsize, " Id:%d Ports:%d Rules:%d\n {\n Ports [", po->id, po->item_list->count, po->rule_hash->count); if( PortObjectHasAny( (PortObject*)po ) ) { SnortSnprintfAppend(po_print_buf, bufsize, "any"); } else { for(poi=(PortObjectItem*)sflist_firstpos(po->item_list,&pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { PortObjectItemPrint(poi, po_print_buf, bufsize); } } SnortSnprintfAppend(po_print_buf, bufsize, " ]\n }\n"); LogMessage("%s", po_print_buf); } /* Print Port Object - Prints input ports and rules (uncompiled) ports rules (input by user) */ void PortObjectPrintEx(PortObject * po, void (*print_index_map)(int index, char *buf, int bufsize) ) { PortObjectItem * poi = NULL; SF_LNODE * pos = NULL; int k=0; int * rlist = NULL; unsigned i; int bufsize = sizeof(po_print_buf); po_print_buf[0] = '\0'; if( !po ) return ; if( !po->rule_list ) return ; if( !po->rule_list->count ) return ; SnortSnprintfAppend(po_print_buf, bufsize, " PortObject "); if( po->name ) { SnortSnprintfAppend(po_print_buf, bufsize, "%s ", po->name); } SnortSnprintfAppend(po_print_buf, bufsize, " Id:%d Ports:%d Rules:%d\n {\n", po->id, po->item_list->count,po->rule_list->count ); SnortSnprintfAppend(po_print_buf, bufsize, " Ports [\n "); if( PortObjectHasAny( po ) ) { SnortSnprintfAppend(po_print_buf, bufsize, "any"); } else { for(poi=(PortObjectItem*)sflist_firstpos(po->item_list,&pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { PortObjectItemPrint(poi, po_print_buf, bufsize); } } SnortSnprintfAppend(po_print_buf, bufsize, " ]\n"); rlist = RuleListToSortedArray( po->rule_list ); if(!rlist ) { return ; } SnortSnprintfAppend(po_print_buf, bufsize, " Rules [ \n "); for(i=0;irule_list->count;i++) { if( print_index_map ) { print_index_map( rlist[i], po_print_buf, bufsize ); } else { SnortSnprintfAppend(po_print_buf, bufsize, " %d",rlist[i]); } k++; if( k == 25 ) { k=0; SnortSnprintfAppend(po_print_buf, bufsize, " \n "); } } SnortSnprintfAppend(po_print_buf, bufsize, " ]\n }\n"); LogMessage("%s", po_print_buf); free(rlist); } // extern void rule_index_map_print_index( int index ); void PortObjectPrint (PortObject * po ) { PortObjectPrintEx( po, rule_index_map_print_index ); } void PortObject2PrintEx(PortObject2 * po, void (*print_index_map)(int index, char *buf, int bufsize) ) { PortObjectItem * poi = NULL; SF_LNODE * pos = NULL; int k=0; int * rlist = NULL; unsigned int i; int bufsize = sizeof(po_print_buf); po_print_buf[0] = '\0'; SnortSnprintfAppend(po_print_buf, bufsize, " PortObject2 "); if( po->name ) SnortSnprintfAppend(po_print_buf, bufsize, "%s ",po->name); SnortSnprintfAppend(po_print_buf, bufsize, " Id:%d Ports:%d Rules:%d PortUsageCnt=%d\n {\n", po->id, po->item_list->count, po->rule_hash->count, po->port_cnt ); SnortSnprintfAppend(po_print_buf, bufsize, " Ports [\n "); if( PortObjectHasAny( (PortObject*)po ) ) { SnortSnprintfAppend(po_print_buf, bufsize, "any"); } else { for(poi=(PortObjectItem*)sflist_firstpos(po->item_list,&pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { PortObjectItemPrint(poi, po_print_buf, bufsize); } } SnortSnprintfAppend(po_print_buf, bufsize, " ]\n"); rlist = RuleHashToSortedArray( po->rule_hash ); if(!rlist ) return ; SnortSnprintfAppend(po_print_buf, bufsize, " Rules [ \n "); for(i=0;irule_hash->count;i++) { if( print_index_map ) { print_index_map( rlist[i], po_print_buf, bufsize ); } else { SnortSnprintfAppend(po_print_buf, bufsize, " %d", rlist[i]); } k++; if( k == 25 ) { k=0; SnortSnprintfAppend(po_print_buf, bufsize, " \n "); } } SnortSnprintfAppend(po_print_buf, bufsize, " ]\n }\n"); LogMessage("%s", po_print_buf); free(rlist); } void PortObject2Print (PortObject2 * po ) { // void rule_index_map_print_index( int index ); PortObject2PrintEx( po, rule_index_map_print_index ); } /* Prints the original (normalized) PortGroups and as sepcified by the user */ void PortTablePrintUserRules( PortTable * p ) { PortObject * po; /* normalized user PortObjects and rule ids */ LogMessage(">>>PortTable - Rules\n"); for(po = (PortObject*)sflist_first(p->pt_polist); po!= 0; po = (PortObject*)sflist_next(p->pt_polist) ) { PortObjectPrint( po ); } /* port array of rule ids */ } /* Prints the Unique Port Groups and rules that reference them */ void PortTablePrintPortGroups( PortTable * p ) { PortObject2 * po; SFGHASH_NODE * ponode; /* normalized user PortObjects and rule ids */ LogMessage(">>>PortTable - Compiled Port Groups\n"); LogMessage(" [ %d port groups ] \n\n",p->pt_mpo_hash->count); for(ponode = sfghash_findfirst(p->pt_mpo_hash); ponode!= 0; ponode = sfghash_findnext(p->pt_mpo_hash) ) { po = ponode->data; PortObject2Print(po); } /* port array of rule ids */ } /* Print */ void PortTablePrintPortPortObjects( PortTable * p ) { int i; PortObject * po; SF_LIST * last = NULL; int bufsize = sizeof(po_print_buf); po_print_buf[0] = '\0'; LogMessage(">>>Port PortObjects\n"); for(i=0;ipt_port_lists[i] ) continue; if( p->pt_port_lists[i] == last ) continue; SnortSnprintfAppend(po_print_buf, bufsize, "---Port[%d] PortObjects [ ",i); for(po=(PortObject*)sflist_first(p->pt_port_lists[i]); po != 0; po=(PortObject*)sflist_next(p->pt_port_lists[i]) ) { SnortSnprintfAppend(po_print_buf, bufsize, "%d ",po->id); } SnortSnprintfAppend(po_print_buf, bufsize, "]\n"); last = p->pt_port_lists[i] ; } LogMessage("%s", po_print_buf); } /* * * Port Object Parser * */ static int POParserInit( POParser * pop, char * s, PortVarTable * pvTable ) { memset(pop,0,sizeof(POParser)); pop->pos = 0; pop->s = s; pop->slen = strlen(s); pop->errflag = 0; pop->pvTable = pvTable; return 0; } /* Get a Char */ static int POPGetChar( POParser * pop ) { int c; if( pop->slen > 0 ) { c = pop->s[0]; pop->slen--; pop->s++; pop->pos++; DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"GetChar: %c, %d bytes left\n",c, pop->slen);); return c; } return 0; } /* Skip whitespace till we find a non-whitespace char */ static int POPGetChar2( POParser * pop ) { int c; for(;;) { c=POPGetChar( pop ) ; if( !c ) return 0; if( isspace(c) || c==',' ) continue; break; } return c; } /* Restore last char */ static void POPUnGetChar( POParser * pop ) { if( pop->pos > 0 ) { pop->slen++; pop->s--; pop->pos--; } } /* Peek at next char */ static int POPPeekChar( POParser * pop ) { if( pop->slen > 0) { return pop->s[0]; } return 0; } #ifdef XXXX /* copy a simple alpha string */ static void POPeekString(POParser * p, char * s, int smax) { int c; int cnt = 0; int k = p->slen; smax--; s[0] = 0; while( k > 0 && cnt < smax ) { c = p->s[ cnt ]; if( c == 0 ) break; if( !isalpha(c) ) break; s[ cnt++ ] = c; s[ cnt ] = 0; k--; } } static void POGetString(POParser * p, char * s, int smax) { int c; int cnt = 0; smax--; s[0] = 0; while( p->slen > 0 && cnt < smax ) { c = p->s[ 0 ]; if( c == 0 ) break; if( !isalpha(c) ) break; s[ cnt++ ] = c; s[ cnt ] = 0; p->slen--; p->s++; } } #endif /* Skip whitespace : ' ', '\t', '\n' */ static int POPSkipSpace( POParser * p ) { int c; for( c = POPPeekChar(p); c != 0 ; c = POPPeekChar(p) ) { if( !isspace(c) && c != ',' ) return c; POPGetChar(p); } return 0; } /* Get the Port Object Name */ static char * POParserName( POParser * pop ) { int k = 0; int c; /* check if were done */ if( !pop || !pop->s || !*(pop->s) ) return 0; /* Start the name - skip space */ c = POPGetChar2(pop) ; if( !c ) return 0; if( c== '$' )/* skip leading '$' - old Var indicator */ { c = POPGetChar2(pop) ; if( !c ) return 0; } if( isalnum(c) ) { pop->token[k++] = (char)c; pop->token[k] = (char)0; } else { POPUnGetChar( pop ); return 0; /* not a name */ } for( c = POPGetChar(pop); c != 0 && k < POP_MAX_BUFFER_SIZE; c = POPGetChar(pop) ) { if( isalnum(c) || c== '_' || c=='-' || c=='.' ) { pop->token[k++] = (char)c; pop->token[k] = (char)0; } else { POPUnGetChar( pop ); break; } } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,">>> POParserName : %s\n",pop->token);); return strdup(pop->token); } /* * Read an unsigned short (a port) */ static uint16_t POParserGetShort(POParser * pop) { int c; int k = 0; char buffer[32]; char * pend; POPSkipSpace(pop); buffer[0] = 0; while( (c = POPGetChar(pop)) != 0 ) { if( isdigit(c) ) { buffer[k++]=(char)c; buffer[k] =0; if( k == sizeof(buffer)-1 ) break; /* thats all that fits */ } else { if( c && ( c!= ':' && c != ' ' && c != ']' && c != ',' && c != '\t' && c != '\n' ) ) { pop->errflag = POPERR_NOT_A_NUMBER; return 0; } POPUnGetChar(pop); break; } } c = (int)strtoul(buffer,&pend,10); if(c > 65535 || c < 0) { pop->errflag = POPERR_BOUNDS; return 0; } DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"GetUNumber: %d\n",c);); return c; } static PortObject *_POParseVar(POParser *pop) { PortObject *pox; char *name; name = POParserName(pop); if(!name) { pop->pos++; pop->errflag = POPERR_NO_NAME; return NULL; } pox = PortVarTableFind(pop->pvTable, name); free(name); if(!pox) { pop->errflag = POPERR_BAD_VARIABLE; return NULL; } pox = PortObjectDup(pox); if(!pox) { pop->errflag = POPERR_MALLOC_FAILED; return NULL; } return pox; } /* * Sets the PORT_OBJECT_NOT_FLAG flag on each port object item in the list */ static void _PONegateList(PortObject *po) { PortObjectItem *poi; SF_LNODE * pos; if(!po) return; /* disable the NOT'd ports */ for(poi=(PortObjectItem*)sflist_firstpos(po->item_list,&pos); poi != 0; poi=(PortObjectItem*)sflist_nextpos(po->item_list,&pos) ) { poi->flags ^= PORT_OBJECT_NOT_FLAG; } } static PortObject *_POParsePort(POParser *pop) { uint16_t hport, lport; char c; PortObject *po = PortObjectNew(); if(!po) { pop->errflag = POPERR_MALLOC_FAILED; return NULL; } hport = MAXPORTS-1; pop->token[0]=0; /* The string in pop should only be of the form or : */ lport = POParserGetShort(pop); if(pop->errflag) { PortObjectFree(po); return NULL; } c = POPPeekChar(pop); if( c == ':' ) /* half open range */ { POPGetChar(pop); c = POPPeekChar(pop); if (((c == 0) && (pop->slen == 0)) || (c == ',')) { /* Open ended range, highport is 65k */ hport = MAXPORTS-1; PortObjectAddRange(po, lport, hport, 0); return po; } if( !isdigit((int)c) ) /* not a number */ { pop->errflag = POPERR_NOT_A_NUMBER; PortObjectFree(po); return NULL; } hport = POParserGetShort(pop); if( pop->errflag ) { PortObjectFree(po); return NULL; } if(lport > hport) { pop->errflag = POPERR_INVALID_RANGE; PortObjectFree(po); return NULL; } PortObjectAddRange(po, lport, hport, 0); } else { PortObjectAddPort(po, lport, 0); } return po; } static PortObject* _POParseString(POParser *pop) { PortObject * po; PortObject * potmp = NULL; int local_neg = 0; char c; int list_count = 0; po = PortObjectNew(); if(!po) { pop->errflag = POPERR_MALLOC_FAILED; return NULL; } while( (c = POPGetChar2(pop)) != 0 ) { if(c == '!') { local_neg = 1; continue; } if(c == '$') { /* Don't dup this again - the returned PortObject has already * been dup'ed */ potmp = _POParseVar(pop); } /* Start of a list. Tokenize list and recurse on it */ else if(c == '[') { POParser local_pop; char *tok; char *end; list_count++; if( (end = strrchr(pop->s, (int)']')) == NULL ) { pop->errflag = POPERR_NO_ENDLIST_BRACKET; PortObjectFree(po); return NULL; } if( (tok = SnortStrndup(pop->s, end - pop->s)) == NULL) { pop->errflag = POPERR_MALLOC_FAILED; PortObjectFree(po); return NULL; } POParserInit(&local_pop, tok, pop->pvTable); /* Recurse */ potmp = _POParseString(&local_pop); free(tok); if(!potmp) { pop->errflag = local_pop.errflag; PortObjectFree(po); return NULL; } /* Advance "cursor" to end of this list */ for(; c && pop->s != end; c = POPGetChar2(pop)) ; } else if(c == ']') { list_count--; if(list_count < 0) { pop->errflag = POPERR_EXTRA_BRACKET; PortObjectFree(po); return NULL; } continue; } else { POPUnGetChar(pop); potmp = _POParsePort(pop); } if(!potmp) { PortObjectFree(po); return NULL; } if(local_neg) { /* Note: this intentionally only sets the negation flag! */ /* The actual negation will take place when normalization is called */ _PONegateList(potmp); local_neg = 0; } if(PortObjectAddPortObject(po, potmp, &pop->errflag)) { PortObjectFree(po); PortObjectFree(potmp); return NULL; } if (potmp) { PortObjectFree(potmp); potmp = NULL; } } /* Check for mis-matched brackets */ if(list_count) { if(list_count > 0) pop->errflag = POPERR_NO_ENDLIST_BRACKET; else pop->errflag = POPERR_EXTRA_BRACKET; PortObjectFree(po); return NULL; } return po; } /* * PortObject : name value * PortObject : name [!][ value value value ... ] * * value : [!]port * [!]low-port[:high-port] * * inputs: * pvTable - PortVarTable to search for PortVar references in the current PortVar * pop - parsing structure * s - string with port object text * * nameflag - indicates a name must be present, this allows usage for * embedded rule or portvar declarations of portlists * returns: * (PortObject *) - a normalized version */ PortObject * PortObjectParseString ( PortVarTable * pvTable, POParser * pop, char * name, char * s , int nameflag ) { PortObject *po, *potmp; DEBUG_WRAP(DebugMessage(DEBUG_PORTLISTS,"PortObjectParseString: %s\n",s);); POParserInit( pop, s, pvTable ); po = PortObjectNew(); if(!po) { pop->errflag=POPERR_MALLOC_FAILED; return 0; } if( nameflag ) /* parse a name */ { po->name = POParserName( pop ); if(!po->name ) { pop->errflag=POPERR_NO_NAME; PortObjectFree(po); return 0; } } else { if( name ) po->name = SnortStrdup(name); else po->name = SnortStrdup("noname"); } // LogMessage("PortObjectParseString: po->name=%s\n",po->name); potmp = _POParseString(pop); if(!potmp) { PortObjectFree(po); return NULL; } PortObjectNormalize(potmp); if(PortObjectAddPortObject(po, potmp, &pop->errflag)) { PortObjectFree(po); PortObjectFree(potmp); return NULL; } PortObjectFree(potmp); return po; } char * PortObjectParseError( POParser * pop ) { switch( pop->errflag ) { case POPERR_NO_NAME: return "no name"; case POPERR_NO_ENDLIST_BRACKET: return "no end of list bracket." " Elements must be comma separated," " and no spaces may appear between" " brackets."; case POPERR_NOT_A_NUMBER: return "not a number"; case POPERR_EXTRA_BRACKET: return "extra list bracket"; case POPERR_NO_DATA: return "no data"; case POPERR_ADDITEM_FAILED: return "add item failed"; case POPERR_MALLOC_FAILED: return "mem alloc failed"; case POPERR_INVALID_RANGE: return "invalid port range"; case POPERR_DUPLICATE_ENTRY: return "duplicate ports in list"; case POPERR_BOUNDS: return "value out of bounds for a port"; case POPERR_BAD_VARIABLE: return "unrecognized variable"; default: break; } return "unknown POParse error"; } /* * * PORT VAR TABLE FUNCTIONS * */ /* * Create a PortVar Table * * The PortVar table used to store and lookup Named PortObjects */ PortVarTable * PortVarTableCreate(void) { PortObject * po; SFGHASH * h; /* * This is used during parsing of config, * so 1000 entries is ok, worst that happens is somewhat slower * config/rule processing. */ h = sfghash_new(1000,0,0,PortObjectFree); if( !h ) return 0; /* Create default port objects */ po = PortObjectNew(); if( !po ) { sfghash_delete(h); return 0; } /* Default has an ANY port */ PortObjectAddPortAny( po ); /* Add ANY to the table */ PortVarTableAdd( h, po ); return h; } /* * PortVarTableAdd() * * returns * -1 : error, no memory... * 0 : added * 1 : in table */ int PortVarTableAdd( PortVarTable * h, PortObject * po ) { int stat; stat = sfghash_add(h,po->name,po); if( stat == SFGHASH_INTABLE ) return 1; if( stat == SFGHASH_OK ) return 0; return -1; } PortObject * PortVarTableFind( PortVarTable * h, char * name ) { if (!h || !name) return NULL; return sfghash_find(h,name); } /* This deletes the table, the PortObjects and PortObjectItems, and rule list. */ int PortVarTableFree(PortVarTable * pvt) { if( pvt ) { sfghash_delete( pvt ); } return 0; } /* TEST DRIVER PorObjects use the follow creation strategy po = PortObjectNew(); PortObjectAddPort( po, 80, 0 ); PortObjectAddPort( po, 8080, 0 ); PortObjectAddPort( po, 8138, 0 ); PortTableAddObject( p, po, k++ ); PortVarTable just stores PorObjects by Name */ //#define MAIN_PORTOBJECT //char * sample1="http [ 80 8100:8200 !8150 ]"; //char * sample2="httpx [ !http 8120 ]"; #ifdef MAIN_PORTOBJECT int main( int argc, char ** argv ) { PortVarTable * pvTable; PortTable * p; PortObject * po; POParser pop; int i; int k=1; int debug=0; int names=1; int lrc =100; int lrp =20; char * portlist; for(i=1;ipt_lrc=lrc; // large rule count - primary for(i=1;i>Bogus PortObject Definition (pos=%d,errflag=%d)\n>>%s\n>>%*s\n", pop.pos,pop.errflag,PORTLISTS,pop.pos,"^"); continue; /* invalid parse - no port object to add */ } PortObjectPrint ( po ); if( PortVarTableAdd( pvTable, po ) ) /* requires a name : portlist http [ portlist ]*/ { LogMessage("error: named port var '%s' already in table \n",po->name); } // Lets test the lookup ... if( !PortVarTableFind(pvTable,po->name) ) { LogMessage("Could not find PortVar: %s\n",po->name); exit(0); } /* Assume each PortVar object has one rule and add it to the PortTable PortObjects that are defined in rules have no names and are not added to the PortVar table */ PortTableAddObject(p,po,k++/*rule id*/); } PortTableCompile( p ); //PortTablePrintRules( p ); //if(debug) PortTablePrintPortGroups( p ); //PortTablePrintPortPortObjects( p ); PortTableDumpPortRules( p ); LogMessage("\n"); LogMessage("#rule and port groups compiled successfully\n"); return 0; } #endif snort-2.9.20/src/sfutil/util_jsnorm.h0000644000175000017500000000273514241077351015717 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Writen by Bhagyashree Bantwal ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #define ALERT_SPACES_EXCEEDED 0x1 #define ALERT_LEVELS_EXCEEDED 0x2 #define ALERT_MIXED_ENCODINGS 0x4 #define MAX_ALLOWED_OBFUSCATION 1 typedef struct { int allowed_spaces; int allowed_levels; uint16_t alerts; }JSState; int JSNormalizeDecode(char *, uint16_t , char *, uint16_t destlen, char **, int *, JSState *, uint8_t *); void InitJSNormLookupTable(void); snort-2.9.20/src/sfutil/Makefile.am0000444000175000017500000000343114230012554015215 0ustar apoapoAUTOMAKE_OPTIONS=foreign no-dependencies noinst_LIBRARIES = libsfutil.a if HAVE_INTEL_SOFT_CPM INTEL_SOFT_CPM_SOURCES = intel-soft-cpm.c intel-soft-cpm.h endif if BUILD_OPENSSL_MD5 OPENSSL_MD5 = \ md5.c md5.h endif if BUILD_OPENSSL_SHA OPENSSL_SHA = \ sha2.c sha2.h endif libsfutil_a_SOURCES = \ sfghash.c sfghash.h \ sfhashfcn.c sfhashfcn.h \ sflsq.c sflsq.h \ sfmemcap.c sfmemcap.h \ sfthd.c sfthd.h \ sfxhash.c sfxhash.h \ ipobj.c ipobj.h \ getopt_long.c getopt.h getopt1.h \ acsmx.c acsmx.h \ acsmx2.c acsmx2.h \ sfksearch.c sfksearch.h \ bnfa_search.c bnfa_search.h \ mpse.c mpse.h \ bitop.h bitop_funcs.h \ util_math.c util_math.h \ util_net.c util_net.h \ util_str.c util_str.h \ util_utf.c util_utf.h \ util_jsnorm.c util_jsnorm.h \ util_unfold.c util_unfold.h \ asn1.c asn1.h \ sfeventq.c sfeventq.h \ sfsnprintfappend.c sfsnprintfappend.h \ sfrt.c sfrt.h sfrt_trie.h sfrt_dir.c sfrt_dir.h \ sfrt_flat.c sfrt_flat.h sfrt_flat_dir.c sfrt_flat_dir.h \ segment_mem.c segment_mem.h \ sfportobject.c sfportobject.h \ sfrim.c sfrim.h \ sfprimetable.c sfprimetable.h \ sf_ip.c sf_ip.h \ sf_ipvar.c sf_ipvar.h \ sf_vartable.c sf_vartable.h \ sf_iph.c sf_iph.h \ sf_textlog.c sf_textlog.h \ sf_sechash.c sf_sechash.h \ sfPolicy.c sfPolicy.h \ sfPolicyUserData.c sfPolicyUserData.h \ sfPolicyData.h \ sfActionQueue.c sfActionQueue.h \ sfrf.c sfrf.h \ strvec.c strvec.h \ sf_email_attach_decode.c sf_email_attach_decode.h \ sf_base64decode.c sf_base64decode.h \ Unified2_common.h \ sf_seqnums.h \ mpse_methods.h \ sfdebug.h \ $(INTEL_SOFT_CPM_SOURCES) \ $(OPENSSL_MD5) \ $(OPENSSL_SHA) INCLUDES = @INCLUDES@ snort-2.9.20/src/sfutil/sfrf.c0000644000175000017500000006204414241077313014302 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* @file sfrf.c * @brief rate filter implementation for Snort * @ingroup rate_filter * @author Dilbagh Chahal */ /* @ingroup rate_filter * @{ */ #include #include #include #ifndef WIN32 #include #include #endif /* !WIN32 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "snort.h" #include "parser/IpAddrSet.h" #include "generators.h" #include "rules.h" #include "treenodes.h" #include "sfrf.h" #include "util.h" #include "sfPolicyData.h" #include "sfPolicyUserData.h" #include "session_common.h" // Number of hash rows for gid 1 (rules) #define SFRF_GEN_ID_1_ROWS 4096 // Number of hash rows for non-zero gid #define SFRF_GEN_ID_ROWS 512 // maximum number of norevert rate_filter configuration allowed. #define SFRF_NO_REVERT_LIMIT 1000 // private data ... /* Key to find tracking nodes in trackingHash. */ typedef struct { ///policy identifier. tSfPolicyId policyId; /* Internally generated threshold identity for a configured threshold. */ int tid; /* Stores either source or destination IP address on a matching packet, depending on * whether dos threshold is tracking by source or destination IP address. For tracking * by rule, it is cleared out (all 0s). */ struct in6_addr ip; } tSFRFTrackingNodeKey ; /* Tracking node for rate_filter. One node is created on fly, in tracking * hash for each threshold configure (identified by Tid) and source or * destination IP address. For rule based tracking, IP is cleared in the * created node. Nodes are deleted when hash performs ANR on hash. */ typedef struct { // automatically initialized to FS_NEW when allocated FilterState filterState; #ifdef SFRF_OVER_RATE int overRate; // 0 = count not exceeded in prior seconds time_t tlast; // time of most recent event #endif /* number of packets counted against a specific IP address or threshold. */ unsigned count; /* time when this sampling period started. */ time_t tstart; /* time when new action was activated due to rate limit exceeding. */ time_t revertTime; } tSFRFTrackingNode; SFXHASH *rf_hash = NULL; static int internal_syn_rate_limit_act; // private methods ... static int _checkThreshold( tSFRFConfigNode*, tSFRFTrackingNode*, time_t curTime ); static int _checkSamplingPeriod( tSFRFConfigNode*, tSFRFTrackingNode*, time_t curTime ); static tSFRFTrackingNode *_getSFRFTrackingNode( sfaddr_t*, unsigned tid, time_t curTime ); static void _updateDependentThresholds( RateFilterConfig *config, unsigned gid, unsigned sid, sfaddr_t* sip, sfaddr_t* dip, time_t curTime ); // public methods ... /* Create a new threshold global context * * Create a threshold table, initialize the threshold system, and optionally * limit it's memory usage. * * @param nbytes maximum memory to use for thresholding objects, in bytes. * @return pointer to newly created tSFRFContext */ #define SFRF_BYTES (sizeof(tSFRFTrackingNodeKey) + sizeof(tSFRFTrackingNode)) static void SFRF_New( unsigned nbytes ) { int nrows; /* Calc max ip nodes for this memory */ if ( nbytes < SFRF_BYTES ) { nbytes = SFRF_BYTES; } nrows = nbytes / (SFRF_BYTES); /* Create global hash table for all of the IP Nodes */ rf_hash = sfxhash_new( nrows, /* try one node per row - for speed */ sizeof(tSFRFTrackingNodeKey), /* keys size */ sizeof(tSFRFTrackingNode), /* data size */ nbytes, /* memcap **/ 1, /* ANR flag - true ?- Automatic Node Recovery=ANR */ 0, /* ANR callback - none */ 0, /* user freemem callback - none */ 1) ; /* Recycle nodes ?*/ } void SFRF_Delete (void) { if ( !rf_hash ) return; sfxhash_delete(rf_hash); rf_hash = NULL; } void SFRF_Flush (void) { if ( rf_hash ) sfxhash_make_empty(rf_hash); } static void SFRF_ConfigNodeFree(void *item) { tSFRFConfigNode *node = (tSFRFConfigNode *)item; if (node == NULL) return; if (node->applyTo != NULL) { IpAddrSetDestroy(node->applyTo); } free(node); } /* free tSFRFSidNode and related buffers. * * @param item - pointer to tSFRFSidNode to be freed. * @returns void */ static void SFRF_SidNodeFree(void* item) { tSFRFSidNode* pSidnode = (tSFRFSidNode*)item; sflist_free_all(pSidnode->configNodeList, SFRF_ConfigNodeFree); free(pSidnode); } /* Add a permanent threshold object to the threshold table. Multiple * objects may be defined for each gid and sid pair. Internally * a unique threshold id is generated for each pair. * * Threshold objects track the number of events seen during the time * interval specified by seconds. Depending on the type of threshold * object and the count value, the thresholding object determines if * the current event should be logged or dropped. * * @param pContext Threshold object from SFRF_ContextNew() * @param cfgNode Permanent Thresholding Object * * @return @retval 0 successfully added the thresholding object, !0 otherwise */ int SFRF_ConfigAdd(SnortConfig *sc, RateFilterConfig *rf_config, tSFRFConfigNode *cfgNode) { SFGHASH* genHash; int nrows; int hstatus; tSFRFSidNode* pSidNode; tSFRFConfigNode* pNewConfigNode; tSFRFGenHashKey key = {0,0}; tSfPolicyId policy_id = getParserPolicy(sc); // Auto init - memcap must be set 1st, which is not really a problem if ( rf_hash == NULL ) { SFRF_New(rf_config->memcap); if ( rf_hash == NULL ) return -1; } if ((rf_config == NULL) || (cfgNode == NULL)) return -1; if ( (cfgNode->sid == 0 ) || (cfgNode->gid == 0) ) return -1; if ( cfgNode->gid >= SFRF_MAX_GENID ) return -1; if ( cfgNode->count < 1 ) return -1; if ( cfgNode->timeout == 0 ) { if ( rf_config->noRevertCount >= SFRF_NO_REVERT_LIMIT ) return -1; rf_config->noRevertCount++; } /* Check for an existing 'gid' entry, if none found then create one. */ /* Get the hash table for this gid */ genHash = rf_config->genHash[cfgNode->gid]; if ( !genHash ) { if ( cfgNode->gid == 1 )/* patmatch rules gid, many rules */ { nrows= SFRF_GEN_ID_1_ROWS; } else /* other gid's */ { nrows= SFRF_GEN_ID_ROWS; } /* Create the hash table for this gid */ genHash = sfghash_new( nrows, sizeof(tSFRFGenHashKey), 0, SFRF_SidNodeFree ); if ( !genHash ) return -2; rf_config->genHash[cfgNode->gid] = genHash; } key.sid = cfgNode->sid; key.policyId = policy_id; /* Check if sid is already in the table - if not allocate and add it */ pSidNode = (tSFRFSidNode*)sfghash_find( genHash, (void*)&key ); if ( !pSidNode ) { /* Create the pSidNode hash node data */ pSidNode = (tSFRFSidNode*)calloc(1,sizeof(tSFRFSidNode)); if ( !pSidNode ) return -3; pSidNode->gid = cfgNode->gid; pSidNode->sid = cfgNode->sid; pSidNode->configNodeList = sflist_new(); if ( !pSidNode->configNodeList ) { free(pSidNode); return -4; } /* Add the pSidNode to the hash table */ hstatus = sfghash_add( genHash, (void*)&key, pSidNode ); if ( hstatus ) { sflist_free(pSidNode->configNodeList); free(pSidNode); return -5; } } /* Create a tSFRFConfigNode for this tSFRFSidNode (Object) */ pNewConfigNode = (tSFRFConfigNode*)calloc(1,sizeof(tSFRFConfigNode)); if ( !pNewConfigNode ) { sflist_free(pSidNode->configNodeList); free(pSidNode); return -6; } *pNewConfigNode = *cfgNode; rf_config->count++; /* Copy the node parameters, with unique internally assigned tid */ pNewConfigNode->tid = rf_config->count; if ( pNewConfigNode->tid == 0 ) { // tid overflow. rare but possible free(pNewConfigNode); sflist_free(pSidNode->configNodeList); free(pSidNode); return -6; } #ifdef SFRF_DEBUG printf("--%d-%d-%d: Threshold node added to tail of list\n", pNewConfigNode->tid, pNewConfigNode->gid, pNewConfigNode->sid); fflush(stdout); #endif sflist_add_tail(pSidNode->configNodeList,pNewConfigNode); return 0; } #ifdef SFRF_DEBUG static char* get_netip(sfaddr_t* ip) { return sfip_ntoa(ip); } #endif // SFRF_DEBUG /* * * Find/Test/Add an event against a single threshold object. * Events without thresholding objects are automatically loggable. * * @param pContext Threshold table pointer * @param cfgNode Permanent Thresholding Object * @param ip Event/Packet Src IP address- should be host ordered for comparison * @param curTime Current Event/Packet time in seconds * @param op operation of type SFRF_COUNT_OPERATION * * @return integer * @retval !0 : rate limit is reached. Return value contains new action. * @retval 0 : Otherwise */ static int SFRF_TestObject( tSFRFConfigNode* cfgNode, sfaddr_t* ip, time_t curTime, SFRF_COUNT_OPERATION op ) { tSFRFTrackingNode* dynNode; int retValue = -1; dynNode = _getSFRFTrackingNode(ip, cfgNode->tid, curTime); if ( dynNode == NULL ) return retValue; if ( _checkSamplingPeriod(cfgNode, dynNode, curTime) != 0 ) { #ifdef SFRF_DEBUG printf("...Sampling period reset\n"); fflush(stdout); #endif } switch (op) { case SFRF_COUNT_INCREMENT: if ( (dynNode->count+1) != 0 ) { dynNode->count++; } break; case SFRF_COUNT_DECREMENT: if ( cfgNode->seconds == 0 ) { // count can be decremented only for total count, and not for rate if ( dynNode->count != 0 ) { dynNode->count--; } } break; case SFRF_COUNT_RESET: dynNode->count = 0; break; default: break; } retValue = _checkThreshold(cfgNode, dynNode, curTime); // we drop after the session count has been incremented // but the decrement will never come so we "fix" it here // if the count were not incremented in such cases, the // threshold would never be exceeded. if ( !cfgNode->seconds && dynNode->count > cfgNode->count ) if ( cfgNode->newAction == RULE_TYPE__DROP ) dynNode->count--; #ifdef SFRF_DEBUG printf("--SFRF_DEBUG: %d-%d-%d: %d Packet IP %s, op: %d, count %d, action %d\n", cfgNode->tid, cfgNode->gid, cfgNode->sid, (unsigned) curTime, get_netip(ip), op, dynNode->count, retValue); fflush(stdout); #endif return retValue; } static inline int SFRF_AppliesTo(tSFRFConfigNode* pCfg, sfaddr_t* ip) { return ( !pCfg->applyTo || IpAddrSetContains(pCfg->applyTo, ip) ); } /* Test a an event against the threshold database. Events without thresholding * objects are automatically loggable. * * @param pContext Threshold table pointer * @param gid Generator Id from the event * @param sid Signature Id from the event * @param sip Event/Packet Src IP address * @param dip Event/Packet Dst IP address * @param curTime Current Event/Packet time * @param op operation of type SFRF_COUNT_OPERATION * * @return -1 if packet is within dos_threshold and therefore action is allowed. * >=0 if packet violates a dos_threshold and therefore new_action should * replace rule action. new_action value is returned. */ int SFRF_TestThreshold( RateFilterConfig *config, unsigned gid, unsigned sid, sfaddr_t* sip, sfaddr_t* dip, time_t curTime, SFRF_COUNT_OPERATION op ) { SFGHASH *genHash; tSFRFSidNode* pSidNode; tSFRFConfigNode* cfgNode; int newStatus = -1; int status = -1; tSFRFGenHashKey key; #ifdef SFRF_DEBUG printf("--%d-%d-%d: %s() entering\n", 0, gid, sid, __func__); fflush(stdout); #endif if ( gid >= SFRF_MAX_GENID ) return status; /* bogus gid */ if(EventIsInternal(gid) && sid == INTERNAL_EVENT_SYN_RECEIVED ) { if( internal_syn_rate_limit_act >= -1 ) return internal_syn_rate_limit_act; } // Some events (like 'TCP connection closed' raised by preprocessor may // not have any configured threshold but may impact thresholds for other // events (like 'TCP connection opened' _updateDependentThresholds(config, gid, sid, sip, dip, curTime); /* * Get the hash table for this gid */ genHash = config->genHash [ gid ]; if ( !genHash ) { #ifdef SFRF_DEBUG printf("--SFRF_DEBUG: %d-%d-%d: no hash table entry for gid\n", 0, gid, sid); fflush(stdout); #endif return status; } /* * Check for any Permanent sid objects for this gid */ key.sid = sid; key.policyId = getApplicableRuntimePolicy(gid); pSidNode = (tSFRFSidNode*)sfghash_find( genHash, (void*)&key ); if ( !pSidNode ) { #ifdef SFRF_DEBUG printf("--SFRF_DEBUG: %d-%d-%d: no DOS THD object\n", 0, gid, sid); fflush(stdout); #endif return status; } /* No List of Threshold objects - bail and log it */ if ( !pSidNode->configNodeList ) { #ifdef SFRF_DEBUG printf("--SFRF_DEBUG: %d-%d-%d: No user configuration\n", 0, gid, sid); fflush(stdout); #endif return status; } /* For each permanent thresholding object, test/add/update the config object */ /* We maintain a list of thd objects for each gid+sid */ /* each object has it's own unique thd_id */ for ( cfgNode = (tSFRFConfigNode*)sflist_first(pSidNode->configNodeList); cfgNode != 0; cfgNode = (tSFRFConfigNode*)sflist_next(pSidNode->configNodeList) ) { switch (cfgNode->tracking) { case SFRF_TRACK_BY_SRC: if ( SFRF_AppliesTo(cfgNode, sip) ) { newStatus = SFRF_TestObject(cfgNode, sip, curTime, op); } break; case SFRF_TRACK_BY_DST: if ( SFRF_AppliesTo(cfgNode, dip) ) { newStatus = SFRF_TestObject(cfgNode, dip, curTime, op); } break; case SFRF_TRACK_BY_RULE: { sfaddr_t cleared; IP_CLEAR(cleared); newStatus = SFRF_TestObject(cfgNode, IP_ARG(cleared), curTime, op); } break; default: // error case break; } #ifdef SFRF_DEBUG printf("--SFRF_DEBUG: %d-%d-%d: Time %d, rate limit blocked: %d\n", cfgNode->tid, gid, sid, (unsigned)curTime, newStatus); fflush(stdout); #endif // rate limit is reached if ( newStatus >= 0 && (status == -1) ) { status = newStatus; } } // rate limit not reached return status; } /* A function to print the thresholding objects to stdout. * * @param pContext pointer to global threshold context * @return */ void SFRF_ShowObjects(RateFilterConfig *config) { SFGHASH* genHash; tSFRFSidNode* pSidnode; tSFRFConfigNode* cfgNode; int gid; SFGHASH_NODE* sidHashNode; for ( gid=0;gid < SFRF_MAX_GENID ; gid++ ) { genHash = config->genHash [ gid ]; if ( !genHash ) { continue; } printf("...GEN_ID = %u\n",gid); for ( sidHashNode = sfghash_findfirst( genHash ); sidHashNode != 0; sidHashNode = sfghash_findnext( genHash ) ) { /* Check for any Permanent sid objects for this gid */ pSidnode = (tSFRFSidNode*)sidHashNode->data; printf(".....GEN_ID = %u, SIG_ID = %u, PolicyId = %u\n",gid, pSidnode->sid, pSidnode->policyId); /* For each permanent thresholding object, test/add/update the thd object */ /* We maintain a list of thd objects for each gid+sid */ /* each object has it's own unique thd_id */ for ( cfgNode = (tSFRFConfigNode*)sflist_first(pSidnode->configNodeList); cfgNode != 0; cfgNode = (tSFRFConfigNode*)sflist_next(pSidnode->configNodeList) ) { printf(".........SFRF_ID =%d\n",cfgNode->tid ); printf(".........tracking =%d\n",cfgNode->tracking); printf(".........count =%u\n",cfgNode->count); printf(".........seconds =%u\n",cfgNode->seconds); } } } } /* Set sampling period rate limit * * @param cfgNode threshold configuration node * @param dynNode tracking node for a configured node * @param curTime for packet timestamp * * @returns 0 if continuing with old sampling period. * 1 if new sampling period is started. */ static int _checkSamplingPeriod( tSFRFConfigNode* cfgNode, tSFRFTrackingNode* dynNode, time_t curTime ) { unsigned dt; if ( cfgNode->seconds ) { dt = (unsigned)(curTime - dynNode->tstart); if ( dt >= cfgNode->seconds ) { // observation period is over, start a new one dynNode->tstart = curTime; #ifdef SFRF_OVER_RATE dt = (unsigned)(curTime - dynNode->tlast); if ( dt > cfgNode->seconds ) dynNode->overRate = 0; else dynNode->overRate = (dynNode->count > cfgNode->count); dynNode->tlast = curTime; #endif dynNode->count = 0; return 1; } } #ifdef SFRF_OVER_RATE else { dynNode->overRate = (dynNode->count > cfgNode->count); } dynNode->tlast = curTime; #endif return 0; } /* Checks if rate limit is reached for a configured threshold. * * DOS Threshold monitoring is done is discrete time intervals specified by * 'cfgNode->seconds'. Once threshold action is activated, it stays active * for the revert timeout. Counters and seconds is maintained current at all * times. This may cause threshold action to be reactivated immediately if counter * is above threshold. * Threshold is tracked using a hash with ANR. This could cause some tracking nodes * to disappear when memory is low. Node deletion and subsequent creation will cause * rate limiting to start afresh for a specific stream. * * @param cfgNode threshold configuration node * @param dynNode tracking node for a configured node * @param curTime for packet timestamp * * @returns 0 if threshold is not reached * 1 otherwise */ static int _checkThreshold( tSFRFConfigNode* cfgNode, tSFRFTrackingNode* dynNode, time_t curTime ) { /* Once newAction is activated, it stays active for the revert timeout, unless ANR * causes the node itself to disappear. * Also note that we want to maintain the counters and rates update so that we reblock * offending traffic again quickly if it has not subsided. */ if ( dynNode->filterState == FS_ON ) { if ( (cfgNode->timeout != 0 ) && ((unsigned)(curTime - dynNode->revertTime) >= cfgNode->timeout)) { #ifdef SFRF_OVER_RATE if ( dynNode->count > cfgNode->count || dynNode->overRate ) { #ifdef SFRF_DEBUG printf("...dos action continued, count %u\n", dynnode->count); fflush(stdout); #endif dynNode->revertTime = curTime; return cfgNode->newAction; } #endif #ifdef SFRF_DEBUG printf("...dos action stopped, count %u\n", dynnode->count); fflush(stdout); #endif dynNode->filterState = FS_OFF; } else { #ifdef SFRF_DEBUG printf("...DOS action continued, count %u\n", dynNode->count); fflush(stdout); #endif return cfgNode->newAction; } } #ifdef SFRF_OVER_RATE if ( dynNode->count <= cfgNode->count && !dynNode->overRate ) #else if ( dynNode->count <= cfgNode->count ) #endif { // rate limit not reached. #ifdef SFRF_DEBUG printf("...DOS action nop, count %u\n", dynNode->count); fflush(stdout); #endif return -1; } // rate limit reached. dynNode->revertTime = curTime; dynNode->filterState = FS_ON; #ifdef SFRF_OVER_RATE dynNode->overRate = 1; #endif #ifdef SFRF_DEBUG printf("...DOS action started, count %u\n", dynNode->count); fflush(stdout); #endif return RULE_TYPE__MAX + cfgNode->newAction; } static void _updateDependentThresholds( RateFilterConfig *config, unsigned gid, unsigned sid, sfaddr_t* sip, sfaddr_t* dip, time_t curTime ) { if ( gid == GENERATOR_INTERNAL && sid == INTERNAL_EVENT_SESSION_DEL ) { // decrementing counters - this results in the following sequence: // 1. sfdos_thd_test_threshold(gid internal, sid DEL) // 2. _updateDependentThresholds(gid internal, sid DEL) // 3. | sfdos_thd_test_threshold(gid internal, sid ADD) // 4. | _updateDependentThresholds(gid internal, sid ADD) // 5. continue with regularly scheduled programming (ie step 1) SFRF_TestThreshold(config, gid, INTERNAL_EVENT_SESSION_ADD, sip, dip, curTime, SFRF_COUNT_DECREMENT); return; } } static tSFRFTrackingNode* _getSFRFTrackingNode( sfaddr_t* ip, unsigned tid, time_t curTime ) { tSFRFTrackingNode* dynNode = NULL; tSFRFTrackingNodeKey key; SFXHASH_NODE * hnode = NULL; /* Setup key */ sfaddr_copy_to_raw(&key.ip, ip); key.tid = tid; key.policyId = getNapRuntimePolicy(); // TBD-EDM should this be NAP or IPS? /* * Check for any Permanent sid objects for this gid or add this one ... */ hnode = sfxhash_get_node(rf_hash, (void*)&key); if ( hnode && hnode->data ) { dynNode = (tSFRFTrackingNode*)hnode->data; if ( dynNode->filterState == FS_NEW ) { // first time initialization dynNode->tstart = curTime; #ifdef SFRF_OVER_RATE dynNode->tlast = curTime; #endif dynNode->filterState = FS_OFF; } } return dynNode; } int SFRF_InternalSynRecdEvent(Packet* p) { sfaddr_t* sip = NULL, *dip = NULL; internal_syn_rate_limit_act = -2; if ( IPH_IS_VALID(p) ) { sip = GET_SRC_IP(p); dip = GET_DST_IP(p); getSessionPlugins()->select_session_nap( p, true ); internal_syn_rate_limit_act = SFRF_TestThreshold( snort_conf->rate_filter_config, GENERATOR_INTERNAL, INTERNAL_EVENT_SYN_RECEIVED, sip, dip, p->pkth->ts.tv_sec, SFRF_COUNT_INCREMENT); if(internal_syn_rate_limit_act > 0) { SnortEventqAdd( GENERATOR_INTERNAL, /* GID */ INTERNAL_EVENT_SYN_RECEIVED, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ "", /* event msg*/ NULL /* rule info ptr */ ); pc.syn_rate_limit_events++; } if( ScNapInlineMode() && ( DAQ_GetInterfaceMode(p->pkth) == DAQ_MODE_INLINE )) { int action = internal_syn_rate_limit_act; if (internal_syn_rate_limit_act >= RULE_TYPE__MAX) action = internal_syn_rate_limit_act - RULE_TYPE__MAX; if( (action == RULE_TYPE__DROP) || (action == RULE_TYPE__SDROP) || (action == RULE_TYPE__REJECT) ) { p->error_flags |= PKT_ERR_SYN_RL_DROP; } } } return 1; } /*@}*/ snort-2.9.20/src/sfutil/sf_seqnums.h0000644000175000017500000000262314241077251015530 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2012-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _SF_SEQNUMS_H_ #define _SF_SEQNUMS_H_ /* Macros to deal with sequence numbers - p810 TCP Illustrated vol 2 */ #define SEQ_LT(a,b) ((int)((a) - (b)) < 0) #define SEQ_LEQ(a,b) ((int)((a) - (b)) <= 0) #define SEQ_GT(a,b) ((int)((a) - (b)) > 0) #define SEQ_GEQ(a,b) ((int)((a) - (b)) >= 0) #define SEQ_EQ(a,b) ((int)((a) - (b)) == 0) #endif // _SF_SEQNUMS_H_ snort-2.9.20/src/sfutil/getopt1.h0000644000175000017500000001000414241077215014720 0ustar apoapo/* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it it under the terms of the GNU General Public License Version 2 as published by the Free Software Foundation. You may not use, modify or distribute this program under any other version of the GNU General Public License. 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. */ #ifndef _GETOPT1_H #define _GETOPT1_H 1 #ifndef HAVE_GETOPT_LONG #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if __STDC__ #ifndef HAVE_GETOPT extern int getopt (int argc, char *const *argv, const char *optstring); #endif extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ #ifndef HAVE_GETOPT extern int getopt (); #endif extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); #endif /* not __STDC__ */ #ifdef __cplusplus } #endif #endif /* HAVE_GETOPT_LONG */ #endif /* _GETOPT_H */ snort-2.9.20/src/sfutil/ipobj.h0000644000175000017500000000505114241077222014444 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* ipobj.h IP address encapsulation interface This module provides encapsulation of single IP ADDRESSes as objects, and collections of IP ADDRESSes as objects Interaction with this library should be done in HOST byte order. */ #ifndef IPOBJ_SNORT #define IPOBJ_SNORT #include #include #include #include "sflsq.h" #include "ipv6_port.h" #ifdef WIN32 #define snprintf _snprintf #endif typedef struct { unsigned port_lo; unsigned port_hi; }PORTRANGE; typedef struct { SF_LIST port_list; }PORTSET; typedef struct { sfcidr_t ip; PORTSET portset; char notflag; } IP_PORT; typedef struct { SF_LIST ip_list; } IPSET; /* IP ADDRESS SET OBJECTS Snort Accepts: IP-Address 192.168.1.1 IP-Address/MaskBits 192.168.1.0/24 IP-Address/Mask 192.168.1.0/255.255.255.0 These can all be handled via the CIDR block notation : IP/MaskBits We use collections (lists) of cidr blocks to represent address blocks and indivdual addresses. For a single IPAddress the implied Mask is 32 bits,or 255.255.255.255, or 0xffffffff, or -1. */ IPSET * ipset_new (void); int ipset_add ( IPSET * ipset, sfcidr_t *ip, void * port, int notflag); int ipset_contains( IPSET * ipset, sfaddr_t *ip, void * port); IPSET * ipset_copy ( IPSET * ipset ); void ipset_free ( IPSET * ipset ); int ipset_print ( IPSET * ipset ); /* helper functions -- all the sets work in host order */ int ipset_parse(IPSET * ipset, char *ipstr); #endif snort-2.9.20/src/sfutil/sfmemcap.h0000644000175000017500000000310214241077275015137 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* ** sfmemcap.h */ #ifndef __SF_MEMCAP_H__ #define __SF_MEMCAP_H__ typedef struct { unsigned long memused; unsigned long memcap; int nblocks; }MEMCAP; void sfmemcap_init(MEMCAP * mc, unsigned long nbytes); MEMCAP * sfmemcap_new( unsigned nbytes ); void sfmemcap_delete( MEMCAP * mc ); void * sfmemcap_alloc(MEMCAP * mc, unsigned long nbytes); void sfmemcap_showmem(MEMCAP * mc ); void sfmemcap_free( MEMCAP * mc, void * memory); void * sfmemcap_dupmem(MEMCAP * mc, void * src, unsigned long n ); #endif snort-2.9.20/src/sfutil/sfrf.h0000644000175000017500000001112314241077315014301 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _SFRF_H_ #define _SFRF_H_ /* @file sfrf.h * @brief rate filter implementation for Snort * @ingroup rate_filter * @author Dilbagh Chahal */ /* @defgroup rate_filter sourcefire.rate_filter * Implements rate_filter feature for snort * @{ */ #include "ipv6_port.h" #include "parser/IpAddrSet.h" #include "sflsq.h" #include "sfghash.h" #include "sfxhash.h" #include "sfPolicy.h" // define to use over rate threshold #define SFRF_OVER_RATE // used for the dimensions of the gid lookup array. #define SFRF_MAX_GENID 8129 // rate_filter tracking by src, by dst, or by rule typedef enum { SFRF_TRACK_BY_SRC = 1, SFRF_TRACK_BY_DST, SFRF_TRACK_BY_RULE, SFRF_TRACK_BY_MAX } SFRF_TRACK; /* Type of operation for threshold tracking nodes. */ typedef enum { SFRF_COUNT_NOP, SFRF_COUNT_RESET, SFRF_COUNT_INCREMENT, SFRF_COUNT_DECREMENT, SFRF_COUNT_MAX } SFRF_COUNT_OPERATION; typedef enum { FS_NEW = 0, FS_OFF, FS_ON, FS_MAX } FilterState; /* A threshold configuration object, created for each configured rate_filter. * These are created at initialization, and remain static. */ typedef struct { // Internally generated unique threshold identity int tid; // Generator id from configured threshold unsigned gid; // Signature id from configured threshold unsigned sid; // Signature id from configured threshold tSfPolicyId policyId; // Threshold tracking by src, dst or rule SFRF_TRACK tracking; // Number of rule matching before rate limit is reached. unsigned count; // Duration in seconds for determining rate of rule matching unsigned seconds; // Action that replaces original rule action on reaching threshold RuleType newAction; // Threshold action duration in seconds before reverting to original rule action unsigned timeout; // ip set to restrict rate_filter IpAddrSet* applyTo; } tSFRFConfigNode; /* tSFRFSidNode acts as a container of gid+sid based threshold objects, * this allows multiple threshold objects to be applied to a single * gid+sid pair. This is static data elements, built at initialization. */ typedef struct { // List of threshold configuration nodes of type tSFRFConfigNode tSfPolicyId policyId; // Generator id from configured threshold unsigned gid; // Signature id from configured threshold unsigned sid; // List of threshold configuration nodes of type tSFRFConfigNode SF_LIST* configNodeList; } tSFRFSidNode; typedef struct { ///policy identifier tSfPolicyId policyId; // Signature id from configured threshold unsigned sid; } tSFRFGenHashKey; /* Single global context containing rate_filter configuration nodes. */ typedef struct _RateFilterConfig { /* Array of hash, indexed by gid. Each array element is a hash, which * is keyed on sid/policyId and data is a tSFRFSidNode node. */ SFGHASH* genHash [SFRF_MAX_GENID]; // Number of DOS thresholds added. int count; // count of no revert DOS thresholds unsigned noRevertCount; int memcap; int internal_event_mask; } RateFilterConfig; /* * Prototypes */ void SFRF_Delete(void); void SFRF_Flush(void); struct _SnortConfig; int SFRF_ConfigAdd(struct _SnortConfig *, RateFilterConfig *, tSFRFConfigNode* ); int SFRF_TestThreshold( RateFilterConfig *config, unsigned gid, unsigned sid, sfaddr_t* sip, sfaddr_t* dip, time_t curTime, SFRF_COUNT_OPERATION ); void SFRF_ShowObjects(RateFilterConfig *); int SFRF_InternalSynRecdEvent(Packet* p); /*@}*/ #endif // _SFRF_H_ snort-2.9.20/src/sfutil/acsmx2.h0000644000175000017500000001312214241077205014535 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* ** ACSMX2.H ** ** Version 2.0 ** ** Author: Marc Norton */ #include #include #include #ifndef ACSMX2S_H #define ACSMX2S_H /* * DEFINES and Typedef's */ #define MAX_ALPHABET_SIZE 256 /* FAIL STATE for 1,2,or 4 bytes for state transitions Uncomment this define to use 32 bit state values #define AC32 */ #define AC32 #ifdef AC32 typedef unsigned int acstate_t; #define ACSM_FAIL_STATE2 0xffffffff #else typedef unsigned short acstate_t; #define ACSM_FAIL_STATE2 0xffff #endif /* * */ typedef struct _acsm_pattern2 { struct _acsm_pattern2 *next; unsigned char *patrn; unsigned char *casepatrn; int n; int nocase; int offset; int depth; int negative; void *udata; int iid; void * rule_option_tree; void * neg_list; } ACSM_PATTERN2; /* * transition nodes - either 8 or 12 bytes */ typedef struct trans_node_s { acstate_t key; /* The character that got us here - sized to keep structure aligned on 4 bytes */ /* to better the caching opportunities. A value that crosses the cache line */ /* forces an expensive reconstruction, typing this as acstate_t stops that. */ acstate_t next_state; /* */ struct trans_node_s * next; /* next transition for this state */ } trans_node_t; /* * User specified final storage type for the state transitions */ enum { ACF_FULL, ACF_SPARSE, ACF_BANDED, ACF_SPARSEBANDS, ACF_FULLQ }; /* * User specified machine types * * TRIE : Keyword trie * NFA : * DFA : */ enum { FSA_TRIE, FSA_NFA, FSA_DFA }; #define AC_MAX_INQ 32 typedef struct { unsigned inq; unsigned inq_flush; void * q[AC_MAX_INQ]; } PMQ; /* * Aho-Corasick State Machine Struct - one per group of pattterns */ typedef struct { int acsmMaxStates; int acsmNumStates; ACSM_PATTERN2 * acsmPatterns; acstate_t * acsmFailState; ACSM_PATTERN2 ** acsmMatchList; /* list of transitions in each state, this is used to build the nfa & dfa */ /* after construction we convert to sparse or full format matrix and free */ /* the transition lists */ trans_node_t ** acsmTransTable; acstate_t ** acsmNextState; int acsmFormat; int acsmSparseMaxRowNodes; int acsmSparseMaxZcnt; int acsmNumTrans; int acsmAlphabetSize; int acsmFSA; int numPatterns; void (*userfree)(void *p); void (*optiontreefree)(void **p); void (*neg_list_free)(void **p); PMQ q; int sizeofstate; int compress_states; }ACSM_STRUCT2; /* * Prototypes */ ACSM_STRUCT2 * acsmNew2 (void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)); int acsmAddPattern2( ACSM_STRUCT2 * p, unsigned char * pat, int n, int nocase, int offset, int depth, int negative, void * id, int iid ); int acsmCompile2 ( ACSM_STRUCT2 * acsm, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); struct _SnortConfig; int acsmCompile2WithSnortConf ( struct _SnortConfig *, ACSM_STRUCT2 * acsm, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); int acsmSearch2 ( ACSM_STRUCT2 * acsm,unsigned char * T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void * data, int* current_state ); int acsmSearchAll2 ( ACSM_STRUCT2 * acsm,unsigned char * T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void * data, int* current_state ); void acsmFree2 ( ACSM_STRUCT2 * acsm ); int acsmPatternCount2 ( ACSM_STRUCT2 * acsm ); void acsmCompressStates(ACSM_STRUCT2 *, int); int acsmSelectFormat2( ACSM_STRUCT2 * acsm, int format ); int acsmSelectFSA2( ACSM_STRUCT2 * acsm, int fsa ); void acsmSetMaxSparseBandZeros2( ACSM_STRUCT2 * acsm, int n ); void acsmSetMaxSparseElements2( ACSM_STRUCT2 * acsm, int n ); int acsmSetAlphabetSize2( ACSM_STRUCT2 * acsm, int n ); void acsmSetVerbose2(void); void acsmPrintInfo2( ACSM_STRUCT2 * p); int acsmPrintDetailInfo2(ACSM_STRUCT2*); int acsmPrintSummaryInfo2(void); void acsmx2_print_qinfo(void); void acsm_init_summary(void); #endif snort-2.9.20/src/sfutil/acsmx.h0000644000175000017500000000663314241077202014461 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* ** ACSMX.H ** ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include #include #include #ifndef ACSMX_H #define ACSMX_H /* * Prototypes */ #define ALPHABET_SIZE 256 #define ACSM_FAIL_STATE -1 typedef struct _acsm_userdata { uint32_t ref_count; void *id; } ACSM_USERDATA; typedef struct _acsm_pattern { struct _acsm_pattern *next; unsigned char *patrn; unsigned char *casepatrn; int n; int nocase; int offset; int depth; int negative; ACSM_USERDATA *udata; int iid; void * rule_option_tree; void * neg_list; } ACSM_PATTERN; typedef struct { /* Next state - based on input character */ int NextState[ ALPHABET_SIZE ]; /* Failure state - used while building NFA & DFA */ int FailState; /* List of patterns that end here, if any */ ACSM_PATTERN *MatchList; }ACSM_STATETABLE; /* * State machine Struct */ typedef struct { int acsmMaxStates; int acsmNumStates; ACSM_PATTERN * acsmPatterns; ACSM_STATETABLE * acsmStateTable; int bcSize; short bcShift[256]; int numPatterns; void (*userfree)(void *p); void (*optiontreefree)(void **p); void (*neg_list_free)(void **p); }ACSM_STRUCT; /* * Prototypes */ ACSM_STRUCT * acsmNew (void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)); int acsmAddPattern( ACSM_STRUCT * p, unsigned char * pat, int n, int nocase, int offset, int depth, int negative, void * id, int iid ); int acsmCompile ( ACSM_STRUCT * acsm, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); struct _SnortConfig; int acsmCompileWithSnortConf ( struct _SnortConfig *, ACSM_STRUCT * acsm, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); int acsmSearch ( ACSM_STRUCT * acsm,unsigned char * T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void * data, int* current_state ); void acsmFree ( ACSM_STRUCT * acsm ); int acsmPatternCount ( ACSM_STRUCT * acsm ); int acsmPrintDetailInfo(ACSM_STRUCT *); int acsmPrintSummaryInfo(void); #endif snort-2.9.20/src/sfutil/sflsq.c0000644000175000017500000002410114241077272014466 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * sflsq.c * * Simple list, stack, queue, and dictionary implementations * ( most of these implementations are list based - not performance monsters, * and they all use alloc via s_alloc/s_free ) * Stack based Ineteger and Pointer Stacks, these are for performance.(inline would be better) * * 11/05/2005 - man - Added sflist_firstx() and sflist_nextx() with user * provided SF_NODE inputs for tracking the list position. This allows * multiple readers to traverse a list. The built in 'cur' field does not * wrok for multiple readers. * * */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sflsq.h" /* * private alloc */ static void * s_alloc (size_t n) { void *p = (void*) calloc( 1,n ); return p; } /* * private free */ static void s_free (void *p) { if( p ) free( p ); } /* * INIT - called by the NEW functions */ void sflist_init ( SF_LIST * s) { s->count=0; s->head = s->tail = s->cur = 0; } /* * NEW */ SF_LIST * sflist_new(void) { SF_LIST * s; s = (SF_LIST*)s_alloc( sizeof(SF_LIST) ); if( s )sflist_init( s ); return s; } SF_STACK * sfstack_new(void) { return (SF_STACK*)sflist_new(); } SF_QUEUE * sfqueue_new(void) { return (SF_QUEUE*)sflist_new(); } /* * Add-before Item */ int sflist_add_before ( SF_LIST* s, SF_LNODE * lnode, NODE_DATA ndata ) { SF_LNODE * q; if( !lnode ) return 0; /* Add to head of list */ if( s->head == lnode ) { return sflist_add_head ( s, ndata ); } else { q = (SF_LNODE *) s_alloc ( sizeof (SF_LNODE) ); if( !q ) { return -1; } q->ndata = (NODE_DATA)ndata; q->next = lnode; q->prev = lnode->prev; lnode->prev->next = q; lnode->prev = q; } s->count++; return 0; } /* * ADD to List/Stack/Queue/Dictionary */ /* * Add-Head Item */ int sflist_add_head ( SF_LIST* s, NODE_DATA ndata ) { SF_LNODE * q; if (!s->head) { q = s->tail = s->head = (SF_LNODE *) s_alloc (sizeof (SF_LNODE)); if(!q)return -1; q->ndata = (NODE_DATA)ndata; q->next = 0; q->prev = 0; } else { q = (SF_LNODE *) s_alloc (sizeof (SF_LNODE)); if(!q)return -1; q->ndata = ndata; q->next = s->head; q->prev = 0; s->head->prev = q; s->head = q; } s->count++; return 0; } /* * Add-Tail Item */ int sflist_add_tail ( SF_LIST* s, NODE_DATA ndata ) { SF_LNODE * q; if (!s->head) { q = s->tail = s->head = (SF_LNODE *) s_alloc (sizeof (SF_LNODE)); if(!q)return -1; q->ndata = (NODE_DATA)ndata; q->next = 0; q->prev = 0; } else { q = (SF_LNODE *) s_alloc (sizeof (SF_LNODE)); if(!q)return -1; q->ndata = ndata; q->next = 0; q->prev = s->tail; s->tail->next = q; s->tail = q; } s->count++; return 0; } int sfqueue_add(SF_QUEUE * s, NODE_DATA ndata ) { return sflist_add_tail ( s, ndata ); } int sfstack_add( SF_STACK* s, NODE_DATA ndata ) { return sflist_add_tail ( s, ndata ); } /* * List walk - First/Next - return the node data or NULL */ NODE_DATA sflist_first( SF_LIST * s ) { if(!s) return 0; s->cur = s->head; if( s->cur ) return s->cur->ndata; return 0; } NODE_DATA sflist_next( SF_LIST * s ) { if(!s) return 0; if( s->cur ) { s->cur = s->cur->next; if( s->cur ) return s->cur->ndata; } return 0; } NODE_DATA sflist_firstpos( SF_LIST * s, SF_LNODE ** v ) { if(!s) return 0; *v = s->head; if( *v ) return (*v)->ndata; return 0; } NODE_DATA sflist_nextpos( SF_LIST * s, SF_LNODE ** v ) { if(!s) return 0; if(v) { if(*v) { *v = (*v)->next; if( *v ) return (*v)->ndata; } } return 0; } /* * List walk - First/Next - return the node data or NULL */ SF_LNODE * sflist_first_node( SF_LIST * s ) { if(!s) return 0; s->cur = s->head; return s->cur; } SF_LNODE * sflist_next_node( SF_LIST * s ) { if(!s) return 0; if( s->cur ) { s->cur = s->cur->next; if( s->cur ) return s->cur; } return 0; } /* * Remove Head Item from list */ NODE_DATA sflist_remove_head (SF_LIST * s) { NODE_DATA ndata = 0; SF_QNODE * q; if ( s && s->head ) { q = s->head; ndata = q->ndata; s->head = s->head->next; s->count--; if( !s->head ) { s->tail = 0; } else { s->head->prev = NULL; } s_free( q ); } return (NODE_DATA)ndata; } /* * Remove tail Item from list */ NODE_DATA sflist_remove_tail (SF_LIST * s) { NODE_DATA ndata = 0; SF_QNODE * q; if (s && s->tail) { q = s->tail; ndata = q->ndata; s->count--; s->tail = q->prev; if (!s->tail) { s->head = NULL; } else { s->tail->next = NULL; } s_free (q); } return (NODE_DATA)ndata; } void sflist_remove_node (SF_LIST * s, SF_LNODE * n) { // NODE_DATA ndata = 0; SF_LNODE * cur; if( n == s->head ) { s->head = s->head->next; s->count--; if (!s->head) { s->tail = 0; } else { s->head->prev = NULL; } s_free( n ); return ; } else if( n == s->tail ) { s->tail = s->tail->prev; s->count--; if (!s->tail ) { s->head = 0; } else { s->tail->next = NULL; } s_free( n ); return ; } for(cur = s->head; cur!= NULL; cur = cur->next ) { if( n == cur ) { /* unlink a middle node */ n->next->prev = n->prev; n->prev->next = n->next; s->count--; s_free(n); return ; } } } /* * Remove Head Item from queue */ NODE_DATA sfqueue_remove (SF_QUEUE * s) { return (NODE_DATA)sflist_remove_head( s ); } /* * Remove Tail Item from stack */ NODE_DATA sfstack_remove (SF_QUEUE * s) { return (NODE_DATA)sflist_remove_tail( s ); } /* * COUNT */ int sfqueue_count (SF_QUEUE * s) { if(!s)return 0; return s->count; } int sflist_count ( SF_LIST* s) { if(!s)return 0; return s->count; } int sfstack_count ( SF_STACK * s) { if(!s)return 0; return s->count; } /* * Free List + Free it's data nodes using 'nfree' */ void sflist_free_all( SF_LIST * s, void (*nfree)(void*) ) { void * p; if(!s) return; while( s->count > 0 ) { p = sflist_remove_head (s); if( p && nfree ) nfree( p ); } s_free(s); } void sfqueue_free_all(SF_QUEUE * s,void (*nfree)(void*) ) { sflist_free_all( s, nfree ); } void sfstack_free_all(SF_STACK * s,void (*nfree)(void*) ) { sflist_free_all( s, nfree ); } void sflist_static_free_all( SF_LIST * s, void (*nfree)(void*) ) { void * p; if(!s) return; while( s->count > 0 ) { p = sflist_remove_head (s); if( p && nfree ) nfree( p ); } } void sfqueue_static_free_all(SF_QUEUE * s,void (*nfree)(void*) ) { sflist_static_free_all( s, nfree ); } void sfstack_static_free_all(SF_STACK * s,void (*nfree)(void*) ) { sflist_static_free_all( s, nfree ); } /* * FREE List/Queue/Stack/Dictionary * * This does not free a nodes data */ void sflist_free (SF_LIST * s) { while( sflist_count(s) ) { sflist_remove_head(s); } s_free(s); } void sfqueue_free (SF_QUEUE * s) { sflist_free ( s ); } void sfstack_free (SF_STACK * s) { sflist_free ( s ); } /* Use these if the SF_LIST was not dynamically allocated via * sflist_new() */ void sflist_static_free(SF_LIST *s) { while (sflist_count(s)) sflist_remove_head(s); } void sfqueue_static_free(SF_QUEUE *s) { sflist_static_free(s); } void sfstack_static_free(SF_STACK *s) { sflist_static_free(s); } /* * Integer stack functions - for performance scenarios */ int sfistack_init( SF_ISTACK * s, unsigned * a, unsigned n ) { if( a ) s->stack = a; else { s->stack = (unsigned*) calloc( n, sizeof(unsigned) ); } if( !s->stack ) return -1; s->nstack= n; s->n =0; return 0; } int sfistack_push( SF_ISTACK *s, unsigned value) { if( s->n < s->nstack ) { s->stack[s->n++] = value; return 0; } return -1; } int sfistack_pop( SF_ISTACK *s, unsigned * value) { if( s->n > 0 ) { s->n--; *value = s->stack[s->n]; return 0; } return -1; } /* * Pointer Stack Functions - for performance scenarios */ int sfpstack_init( SF_PSTACK * s, void ** a, unsigned n ) { if( a ) s->stack = a; else { s->stack = (void**) calloc( n , sizeof(void*) ); } if( !s->stack ) return -1; s->nstack= n; s->n =0; return 0; } int sfpstack_push( SF_PSTACK *s, void * value) { if( s->n < s->nstack ) { s->stack[s->n++] = value; return 0; } return -1; } int sfpstack_pop( SF_PSTACK *s, void ** value) { if( s->n > 0 ) { s->n--; *value = s->stack[s->n]; return 0; } return -1; } snort-2.9.20/src/sfutil/sf_ip.c0000644000175000017500000004127514241077240014444 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** Adam Keeton ** Kevin Liu ** ** $Id$ ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Adam Keeton * sf_ip.c * 11/17/06 * * Library for managing IP addresses of either v6 or v4 families. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include /* For ceil */ #include "sf_types.h" /* For bool */ #include "sf_ip.h" /* For inet_pton */ #ifndef WIN32 #include #include #include #include #endif /* WIN32 */ /* Support function */ // note that an ip6 address may have a trailing dotted quad form // but that it always has at least 2 ':'s; furthermore there is // no valid ip4 format (including mask) with 2 ':'s // we don't have to figure out if the format is entirely legal // we just have to be able to tell correct formats apart static inline int sfip_str_to_fam(const char *str) { const char* s; ARG_CHECK1(str, 0); s = strchr(str, (int)':'); if ( s && strchr(s+1, (int)':') ) return AF_INET6; if ( strchr(str, (int)'.') ) return AF_INET; return AF_UNSPEC; } /* Place-holder allocation incase we want to do something more indepth later */ static inline sfcidr_t *_sfip_alloc() { /* Note: using calloc here instead of SnortAlloc since the dynamic libs * can't presently resolve SnortAlloc */ return (sfcidr_t*)calloc(sizeof(sfcidr_t), 1); } /* Support function for _netmask_str_to_bit_count */ static inline int _count_bits(unsigned int val) { unsigned int count; for (count = 0; val; count++) { val &= val - 1; } return count; } /* Support function for sfip_pton. Used for converting a netmask string * into a number of bits to mask off */ static inline int _netmask_str_to_bit_count(char *mask, int family) { uint32_t buf[4]; int bits, i, nBits, nBytes; uint8_t* bytes = (uint8_t*)buf; /* XXX * Mask not validated. * Only sfip_pton should be using this function, and using it safely. * XXX */ if(inet_pton(family, mask, buf) < 1) return -1; bits = _count_bits(buf[0]); if(family == AF_INET6) { bits += _count_bits(buf[1]); bits += _count_bits(buf[2]); bits += _count_bits(buf[3]); nBytes = 16; } else { nBytes = 4; } // now make sure that only the most significant bits are set nBits = bits; for ( i = 0; i < nBytes; i++ ) { if ( nBits >= 8 ) { if ( bytes[i] != 0xff ) return -1; nBits -= 8; } else if ( nBits == 0 ) { if ( bytes[i] != 0x00 ) return -1; } else { if ( bytes[i] != ((0xff00 >> nBits) & 0xff) ) return -1; nBits = 0; } } return bits; } /* Masks off 'val' bits from the IP contained within 'ip' */ static inline int sfip_cidr_mask(sfaddr_t *ip, int val) { uint32_t *p; int index = (int)ceil(val / 32.0) - 1; int bits; ARG_CHECK1(ip, SFIP_ARG_ERR); p = sfaddr_get_ip6_ptr(ip); if( val < 0 || val > 128) return SFIP_ARG_ERR; if (val == 128) return SFIP_SUCCESS; /* Build the netmask by converting "val" into * the corresponding number of bits that are set */ bits = 32 - (val - (index * 32)); if (bits) { unsigned int mask; mask = ~0; mask >>= bits; mask <<= bits; p[index] &= htonl(mask); } index++; /* 0 off the rest of the IP */ for( ; index<4; index++) p[index] = 0; return SFIP_SUCCESS; } /* Parses "src" and stores results in "dst" */ static SFIP_RET _sfip_pton(const char *src, sfaddr_t *dst, uint16_t *srcBits) { char *mask; char *sfip_buf; char *ip; int bits; int family; if(!dst || !src) return SFIP_ARG_ERR; if((sfip_buf = strdup(src)) == NULL) return SFIP_ALLOC_ERR; ip = sfip_buf; family = sfip_str_to_fam(src); /* skip whitespace or opening bracket */ while(isspace((int)*ip) || (*ip == '[')) ip++; /* check for and extract a mask in CIDR form */ if( (mask = strchr(ip, (int)'/')) != NULL ) { /* NULL out this character so inet_pton will see the * correct ending to the IP string */ char* end = mask++; while ( (end > ip) && isspace((int)end[-1]) ) end--; *end = 0; while(isspace((int)*mask)) mask++; /* verify a leading digit */ if(((family == AF_INET6) && !isxdigit((int)*mask)) || ((family == AF_INET) && !isdigit((int)*mask))) { free(sfip_buf); return SFIP_CIDR_ERR; } /* Check if there's a netmask here instead of the number of bits */ if(strchr(mask, (int)'.') || strchr(mask, (int)':')) bits = _netmask_str_to_bit_count(mask, sfip_str_to_fam(mask)); else bits = atoi(mask); } else if( /* If this is IPv4, ia ':' may used specified to indicate a netmask */ ((family == AF_INET) && (mask = strchr(ip, (int)':')) != NULL) || /* We've already skipped the leading whitespace, if there is more * whitespace, then there's probably a netmask specified after it. */ (mask = strchr(ip, (int)' ')) != NULL ) { char* end = mask++; while ( (end > ip) && isspace((int)end[-1]) ) end--; *end = 0; /* Now the IP will end at this point */ /* skip whitespace */ while(isspace((int)*mask)) mask++; /* Make sure we're either looking at a valid digit, or a leading * colon, such as can be the case with IPv6 */ if(((family == AF_INET) && isdigit((int)*mask)) || ((family == AF_INET6) && (isxdigit((int)*mask) || *mask == ':'))) { bits = _netmask_str_to_bit_count(mask, sfip_str_to_fam(mask)); } /* No netmask */ else { if(family == AF_INET) bits = 32; else bits = 128; } } /* No netmask */ else { if(family == AF_INET) bits = 32; else bits = 128; } if(sfip_convert_ip_text_to_binary(family, ip, sfaddr_get_ip6_ptr(dst)) != SFIP_SUCCESS) { free(sfip_buf); return SFIP_INET_PARSE_ERR; } dst->family = family; /* Store mask */ bits += (family == AF_INET && bits >= 0) ? 96 : 0; /* Apply mask */ if(sfip_cidr_mask(dst, bits) != SFIP_SUCCESS) { free(sfip_buf); return SFIP_INVALID_MASK; } *srcBits = bits; free(sfip_buf); return SFIP_SUCCESS; } /* Allocate IP address from a character array describing the IP */ sfcidr_t *sfip_alloc(const char *ip, SFIP_RET *status) { SFIP_RET tmp; sfcidr_t *ret; if(!ip) { if(status) *status = SFIP_ARG_ERR; return NULL; } if((ret = _sfip_alloc()) == NULL) { if(status) *status = SFIP_ALLOC_ERR; return NULL; } if( (tmp = sfip_pton(ip, ret)) != SFIP_SUCCESS) { if(status) *status = tmp; sfip_free(ret); return NULL; } if(status) *status = SFIP_SUCCESS; return ret; } /* Allocate IP address from a character array describing the IP */ sfaddr_t *sfaddr_alloc(const char *ip, SFIP_RET *status) { SFIP_RET tmp; sfaddr_t *ret; uint16_t bits; if(!ip) { if(status) *status = SFIP_ARG_ERR; return NULL; } if((ret = (sfaddr_t*)calloc(sizeof(sfaddr_t), 1)) == NULL) { if(status) *status = SFIP_ALLOC_ERR; return NULL; } if( (tmp = _sfip_pton(ip, ret, &bits)) != SFIP_SUCCESS ) { if(status) *status = tmp; sfaddr_free(ret); return NULL; } if (bits != 128) { if(status) *status = SFIP_INET_PARSE_ERR; sfaddr_free(ret); return NULL; } if(status) *status = SFIP_SUCCESS; return ret; } /* Allocate IP address from an array of 8 byte integers */ sfaddr_t *sfip_alloc_raw(void *ip, int family, SFIP_RET *status) { sfaddr_t *ret; if(!ip) { if(status) *status = SFIP_ARG_ERR; return NULL; } if((ret = (sfaddr_t*)calloc(sizeof(sfaddr_t), 1)) == NULL) { if(status) *status = SFIP_ALLOC_ERR; return NULL; } sfip_set_raw(ret, ip, family); if(status) *status = SFIP_SUCCESS; return ret; } /* Converts string IP format to an array of values. Also checks IP address format. Specifically look for issues that inet_pton either overlooks or is inconsistent about. */ SFIP_RET sfip_convert_ip_text_to_binary( const int family, const char *ip, void *dst) { const char *my_ip; sfaddr_t* addr; my_ip = ip; if( my_ip == NULL ) return( SFIP_FAILURE ); /* Across platforms, inet_pton() is inconsistent about leading 0's in AF_INET (ie IPv4 addresses. */ if( family == AF_INET ) { char chr; bool new_octet; new_octet = true; while( (chr = *my_ip++) != '\0') { /* If we are at the first char of a new octet, look for a leading zero followed by another digit */ if( new_octet && (chr == '0') && isdigit(*my_ip)) return( SFIP_INET_PARSE_ERR ); /* when we see an octet separator, set the flag to start looking for a leading zero. */ new_octet = (chr == '.'); } addr = (sfaddr_t*)dst; addr->ia32[0] = addr->ia32[1] = addr->ia16[4] = 0; addr->ia16[5] = 0xFFFF; dst = &addr->ia32[3]; } if( inet_pton(family, ip, dst) < 1 ) return( SFIP_INET_PARSE_ERR ); return( SFIP_SUCCESS ); /* Otherwise, ip is OK */ } SFIP_RET sfaddr_pton(const char *src, sfaddr_t *dst) { SFIP_RET ret; uint16_t bits; ret = _sfip_pton(src, dst, &bits); if (ret == SFIP_SUCCESS && bits != 128) return SFIP_INET_PARSE_ERR; return ret; } SFIP_RET sfip_pton(const char *src, sfcidr_t *dst) { return _sfip_pton(src, &dst->addr, &dst->bits); } /* Sets existing IP, "dst", to be source IP, "src" */ SFIP_RET sfip_set_raw(sfaddr_t *dst, const void *src, int family) { ARG_CHECK3(dst, src, sfaddr_get_ip6_ptr(dst), SFIP_ARG_ERR); dst->family = family; if(family == AF_INET) { dst->ia32[0] = dst->ia32[1] = dst->ia16[4] = 0; dst->ia16[5] = 0xFFFF; dst->ia32[3] = *(uint32_t*)src; } else if(family == AF_INET6) { memcpy(sfaddr_get_ip6_ptr(dst), src, 16); } else { return SFIP_ARG_ERR; } return SFIP_SUCCESS; } /* Obfuscates an IP * Makes 'ip': ob | (ip & mask) */ void sfip_obfuscate(sfcidr_t *ob, sfaddr_t *ip) { uint32_t *ob_p, *ip_p; int index, i; unsigned int mask = 0; if(!ob || !ip) return; ob_p = sfip_get_ip6_ptr(ob); ip_p = sfaddr_get_ip6_ptr(ip); /* Build the netmask by converting "val" into * the corresponding number of bits that are set */ index = (int)ceil(ob->bits / 32.0) - 1; for(i = 0; i < 32- (ob->bits - (index * 32)); i++) mask = (mask<<1) + 1; /* Note: The old-Snort obfuscation code uses !mask for masking. * hence, this code uses the same algorithm as sfip_cidr_mask * except the mask below is not negated. */ ip_p[index] = htonl((ntohl(ip_p[index]) & mask)); /* 0 off the start of the IP */ while ( index > 0 ) ip_p[--index] = 0; /* OR remaining pieces */ ip_p[0] |= ob_p[0]; ip_p[1] |= ob_p[1]; ip_p[2] |= ob_p[2]; ip_p[3] |= ob_p[3]; } /* Check if ip is contained within the network specified by net */ /* Returns SFIP_EQUAL if so. * XXX sfip_contains assumes that "ip" is * not less-specific than "net" XXX */ SFIP_RET sfip_contains(const sfcidr_t *net, const sfaddr_t *ip) { unsigned int bits, mask, temp, i; const uint32_t *p1, *p2; /* SFIP_CONTAINS is returned here due to how IpAddrSetContains * handles zero'ed IPs" */ ARG_CHECK2(net, ip, SFIP_CONTAINS); bits = sfip_bits(net); p1 = sfip_get_ip6_ptr(net); p2 = sfaddr_get_ip6_ptr(ip); /* Iterate over each 32 bit segment */ for(i=0; i < bits/32; i++, p1++, p2++) { if(*p1 != *p2) return SFIP_NOT_CONTAINS; } mask = 32 - (bits - 32*i); if ( mask == 32 ) return SFIP_CONTAINS; /* At this point, there are some number of remaining bits to check. * Mask the bits we don't care about off of "ip" so we can compare * the ints directly */ temp = ntohl(*p2); temp = (temp >> mask) << mask; /* If p1 was setup correctly through this library, there is no need to * mask off any bits of its own. */ if(ntohl(*p1) == temp) return SFIP_CONTAINS; return SFIP_NOT_CONTAINS; } void sfip_raw_ntop(int family, const void *ip_raw, char *buf, int bufsize) { if(!ip_raw || !buf || (family != AF_INET && family != AF_INET6) || /* Make sure if it's IPv6 that the buf is large enough. */ /* Need atleast a max of 8 fields of 4 bytes plus 7 for colons in * between. Need 1 more byte for null. */ (family == AF_INET6 && bufsize < INET6_ADDRSTRLEN) || /* Make sure if it's IPv4 that the buf is large enough. */ /* 4 fields of 3 numbers, plus 3 dots and a null byte */ (family == AF_INET && bufsize < INET_ADDRSTRLEN) ) { if(buf && bufsize > 0) buf[0] = 0; return; } #if defined(HAVE_INET_NTOP) && !defined(REG_TEST) if (!inet_ntop(family, ip_raw, buf, bufsize)) snprintf(buf, bufsize, "ERROR"); #else /* 4 fields of at most 3 characters each */ if(family == AF_INET) { int i; uint8_t *p = (uint8_t*)ip_raw; for(i=0; p < ((uint8_t*)ip_raw) + 4; p++) { i += sprintf(&buf[i], "%d", *p); /* If this is the last iteration, this could technically cause one * extra byte to be written past the end. */ if(i < bufsize && ((p + 1) < ((uint8_t*)ip_raw+4))) buf[i] = '.'; i++; } /* Check if this is really just an IPv4 address represented as 6, * in compatible format */ #if 0 } else if(!field[0] && !field[1] && !field[2]) { unsigned char *p = (unsigned char *)(&ip->ip[12]); for(i=0; p < &ip->ip[16]; p++) i += sprintf(&buf[i], "%d.", *p); #endif } else { int i; uint16_t *p = (uint16_t*)ip_raw; for(i=0; p < ((uint16_t*)ip_raw) + 8; p++) { i += sprintf(&buf[i], "%04x", ntohs(*p)); /* If this is the last iteration, this could technically cause one * extra byte to be written past the end. */ if(i < bufsize && ((p + 1) < ((uint16_t*)ip_raw) + 8)) buf[i] = ':'; i++; } } #endif } void sfip_ntop(const sfaddr_t *ip, char *buf, int bufsize) { int family; if(!ip) { if(buf && bufsize > 0) buf[0] = 0; return; } family = sfaddr_family(ip); sfip_raw_ntop(family, sfaddr_get_ptr(ip), buf, bufsize); } /* Uses a static buffer to return a string representation of the IP */ char *sfip_to_str(const sfaddr_t *ip) { static char buf[INET6_ADDRSTRLEN]; sfip_ntop(ip, buf, sizeof(buf)); return buf; } void sfip_free(sfcidr_t *ip) { if(ip) free(ip); } void sfaddr_free(sfaddr_t *ip) { if(ip) free(ip); } /* Returns 1 if the IP is non-zero. 0 otherwise */ int sfip_is_loopback(const sfaddr_t *ip) { ARG_CHECK1(ip, 0); /* Check the first 80 bits in an IPv6 address, and */ /* verify they're zero. If not, it's not a loopback */ if(ip->ia32[0] || ip->ia32[1] || ip->ia16[4]) return 0; if(ip->ia16[5] == 0xFFFF) { /* ::ffff:7f00:0/104 is ipv4 compatible ipv6 */ return (ip->ia8[12] == 0x7f); } if(!ip->ia16[5]) { /* ::7f00:0/104 is ipv4 compatible ipv6 */ /* ::1 is the IPv6 loopback */ return (ip->ia32[3] == htonl(0x1) || ip->ia8[12] == 0x7f); } return 0; } snort-2.9.20/src/sfutil/sfrim.h0000644000175000017500000000143014241077317014463 0ustar apoapo/* * sfrim.h * * Rule Index Map * * author: marc norton * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. */ #ifndef SFRIM_H #define SFRIM_H typedef struct { unsigned gid; unsigned sid; }rule_number_t; typedef struct { int max_rules; int num_rules; rule_number_t * map; }rule_index_map_t; unsigned RuleIndexMapSid( rule_index_map_t * map, int index ); unsigned RuleIndexMapGid( rule_index_map_t * map, int index ); rule_index_map_t * RuleIndexMapCreate( int max_rules ); void RuleIndexMapFree( rule_index_map_t ** p ); int RuleIndexMapAdd( rule_index_map_t * p, unsigned gid, unsigned sid ); extern void rule_index_map_print_index( int index, char *buf, int ); #endif snort-2.9.20/src/sfutil/bitop.h0000644000175000017500000000266214241077210014460 0ustar apoapo/* ** $Id$ ** ** bitopt.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Dan Roelker ** Marc Norton ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** NOTES ** 5.15.02 - Initial Source Code. Norton/Roelker ** 5.23.02 - Moved bitop functions to bitop.h to inline. Norton/Roelker ** 1.21.04 - Added static initialization. Roelker ** 9.13.05 - Separated type and inline func definitions. Sturges ** */ #ifndef _BITOP_H #define _BITOP_H typedef struct _BITOP { unsigned char *pucBitBuffer; unsigned int uiBitBufferSize; unsigned int uiMaxBits; } BITOP; #endif /* _BITOP_H */ snort-2.9.20/src/sfutil/sf_email_attach_decode.h0000644000175000017500000001534514241077237017764 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Writen by Bhagyashree Bantwal ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SF_EMAIL_ATTACH_DECODE_H_ #define _SF_EMAIL_ATTACH_DECODE_H_ #include "sf_types.h" #include "util_unfold.h" #include "sf_base64decode.h" #include "snort_bounds.h" #include "file_mail_common.h" #include "file_api.h" #define MAX_BUF 65535 #define DECODE_SUCCESS 0 #define DECODE_EXCEEDED 1 /* Decode Complete when we reach the max depths */ #define DECODE_FAIL -1 typedef enum { DECODE_NONE = 0, DECODE_B64, DECODE_QP, DECODE_UU, DECODE_BITENC, DECODE_ALL } DecodeType; typedef struct s_Base64_DecodeState { uint32_t encode_bytes_read; uint32_t decode_bytes_read; int encode_depth; int decode_depth; } Base64_DecodeState; typedef struct s_QP_DecodeState { uint32_t encode_bytes_read; uint32_t decode_bytes_read; int encode_depth; int decode_depth; } QP_DecodeState; typedef struct s_UU_DecodeState { uint32_t encode_bytes_read; uint32_t decode_bytes_read; int encode_depth; int decode_depth; uint8_t begin_found; uint8_t end_found; } UU_DecodeState; typedef struct s_BitEnc_DecodeState { uint32_t bytes_read; int depth; } BitEnc_DecodeState; typedef struct s_Email_DecodeState { DecodeType decode_type; uint8_t decode_present; uint32_t prev_encoded_bytes; unsigned char *prev_encoded_buf; uint32_t decoded_bytes; uint8_t *encodeBuf; uint8_t *decodeBuf; uint8_t *decodePtr; Base64_DecodeState b64_state; QP_DecodeState qp_state; UU_DecodeState uu_state; BitEnc_DecodeState bitenc_state; } Email_DecodeState; typedef struct _MimeStats { uint64_t memcap_exceeded; uint64_t attachments[DECODE_ALL]; uint64_t decoded_bytes[DECODE_ALL]; } MimeStats; // end :: start + length int EmailDecode(const uint8_t *start, const uint8_t *end, Email_DecodeState *, uint8_t *fname, uint32_t *fname_size, bool filename_present); static inline int getCodeDepth(int code_depth, int64_t file_depth) { if (file_depth < 0 ) return code_depth; else if (( file_depth > MAX_BUF) || (!file_depth) ) return 0; else if (file_depth > code_depth) return (int)file_depth; else return code_depth; } static inline void SetEmailDecodeState(Email_DecodeState *ds, void *data, int max_depth, int b64_depth, int qp_depth, int uu_depth, int bitenc_depth, int64_t file_depth) { if ( max_depth & 7 ) { max_depth += (8 - (max_depth & 7)); } ds->decode_type = DECODE_NONE; ds->decode_present = 0; ds->prev_encoded_bytes = 0; ds->prev_encoded_buf = NULL; ds->decoded_bytes = 0; ds->encodeBuf = (uint8_t *)data; ds->decodeBuf = (uint8_t *)data + max_depth; ds->decodePtr = ds->decodeBuf; ds->b64_state.encode_depth = ds->b64_state.decode_depth = getCodeDepth(b64_depth, file_depth); ds->b64_state.encode_bytes_read = ds->b64_state.decode_bytes_read = 0; ds->qp_state.encode_depth = ds->qp_state.decode_depth = getCodeDepth(qp_depth, file_depth); ds->qp_state.encode_bytes_read = ds->qp_state.decode_bytes_read = 0; ds->uu_state.encode_depth = ds->uu_state.decode_depth = getCodeDepth(uu_depth, file_depth); ds->uu_state.encode_bytes_read = ds->uu_state.decode_bytes_read = 0; ds->uu_state.begin_found = 0; ds->uu_state.end_found = 0; ds->bitenc_state.depth = getCodeDepth(bitenc_depth, file_depth); ds->bitenc_state.bytes_read = 0; } static inline void updateMaxDepth(int64_t file_depth, int *max_depth) { if((!file_depth) || (file_depth > MAX_BUF)) { *max_depth = MAX_BUF; } else if (file_depth > (*max_depth)) { *max_depth = (int)file_depth; } } static inline void ClearPrevEncodeBuf(Email_DecodeState *ds) { ds->prev_encoded_bytes = 0; ds->prev_encoded_buf = NULL; } static inline void ResetBytesRead(Email_DecodeState *ds) { ds->uu_state.begin_found = ds->uu_state.end_found = 0; ClearPrevEncodeBuf(ds); ds->b64_state.encode_bytes_read = ds->b64_state.decode_bytes_read = 0; ds->qp_state.encode_bytes_read = ds->qp_state.decode_bytes_read = 0; ds->uu_state.encode_bytes_read = ds->uu_state.decode_bytes_read = 0; ds->bitenc_state.bytes_read = 0; } static inline void ResetDecodedBytes(Email_DecodeState *ds) { ds->decodePtr = NULL; ds->decoded_bytes = 0; ds->decode_present = 0; } static inline void ResetEmailDecodeState(Email_DecodeState *ds) { if ( ds == NULL ) return; ds->uu_state.begin_found = ds->uu_state.end_found = 0; ResetDecodedBytes(ds); ClearPrevEncodeBuf(ds); } static inline void ClearEmailDecodeState(Email_DecodeState *ds) { if(ds == NULL) return; ds->decode_type = DECODE_NONE; ResetEmailDecodeState(ds); } static inline int limitDetection(int depth, int decoded_bytes, int decode_bytes_total) { if (!depth) return decoded_bytes; else if (depth < decode_bytes_total - decoded_bytes) return 0; else if (depth > decode_bytes_total) return decoded_bytes; else return (depth + decoded_bytes - decode_bytes_total); } static inline int getDetectionSize(int b64_depth, int qp_depth, int uu_depth, int bitenc_depth, Email_DecodeState *ds) { int iRet = 0; switch(ds->decode_type) { case DECODE_B64: iRet = limitDetection(b64_depth, ds->decoded_bytes, ds->b64_state.decode_bytes_read); break; case DECODE_QP: iRet = limitDetection(qp_depth, ds->decoded_bytes, ds->qp_state.decode_bytes_read); break; case DECODE_UU: iRet = limitDetection(uu_depth, ds->decoded_bytes, ds->uu_state.decode_bytes_read); break; case DECODE_BITENC: iRet = limitDetection(bitenc_depth, ds->decoded_bytes, ds->bitenc_state.bytes_read); break; default: break; } return iRet; } #endif snort-2.9.20/src/sfutil/util_str.c0000644000175000017500000000662614241077356015222 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file util_str.c * @author Chris Green * @date Fri Jun 27 10:41:59 2003 * * @brief utility string functions * * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "util_str.h" #include #ifdef HAVE_STRINGS_H #include #endif /** * Convert a string to an int and check for problems * * @param str string to parse as an int * @param ret return value for the int * @param allow_negative allow negative values * * @return 0 on sucess, else failure */ int str2int(char *str, int *ret, int allow_negative) { char *endptr; long int value; if(ret && str && *str != '\0') { value = strtol(str, &endptr, 10); if(endptr == str) { /* parsing has failed */ return -1; } if(!allow_negative) { if(value < 0) { return -1; } } *ret = value; return 0; } return -1; } /** * Set opt_value to 1 if the value is on, 0 if it's off * * @param name option name to configure (not used but useful for debugging) * @param value value to configure (should be either on or off ) * @param opt_value ptr to integer to configure * * @returns 0 on success , else failure */ int toggle_option(char *name, char *value, int *opt_value) { int opt_on, opt_off; if(!name || !value || !opt_value || (*value == '\0') || (*name == '\0') ) return -1; opt_on = strcasecmp(value,"on"); opt_off = strcasecmp(value,"off"); if(opt_off && opt_on) { /* * the string is neither "on" or "off" * * we don't know what the hell we're looking at. return error. */ return -2; } if(opt_on == 0) *opt_value = 1; else *opt_value = 0; return 0; } #ifdef TEST_UTIL_STR int main(void) { int value; printf("you should see 4 pass messages\n"); if(str2int("-1",&value,0) != 0) printf("test 1 passed and failed to parse\n"); if(str2int("-1",&value,1) == 0 && value == -1) printf("test 2 passed: %d\n", value); if(str2int("0",&value,1) == 0 && value == 0 ) printf("test 3 passed: %d\n", value); if(str2int("124",&value,1) == 0 && value == 124 ) printf("test 4 passed: %d\n", value); } #endif /* TEST_UTIL_STR */ snort-2.9.20/src/sfutil/sfksearch.h0000644000175000017500000000731314241077271015321 0ustar apoapo/* * ksearch.h * * Trie based multi-pattern matcher * * * Copyright (C) 2001 Marc Norton ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2003-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef KTRIE_H #define KTRIE_H #define ALPHABET_SIZE 256 #ifdef HAVE_CONFIG_H #include "config.h" #endif #define KTRIEMETHOD_STD 0 #define KTRIEMETHOD_QUEUE 1 /* * */ typedef struct _ktriepattern { struct _ktriepattern * next; /* global list of all patterns */ struct _ktriepattern * mnext; /* matching list of duplicate keywords */ unsigned char * P; /* no case */ unsigned char * Pcase; /* case sensitive */ int n; int nocase; int negative; void * id; void * rule_option_tree; void * neg_list; } KTRIEPATTERN; /* * */ typedef struct _ktrienode { int edge; /* character */ struct _ktrienode * sibling; struct _ktrienode * child; KTRIEPATTERN *pkeyword; } KTRIENODE; #define KTRIE_ROOT_NODES 256 #define SFK_MAX_INQ 32 typedef struct { unsigned inq; unsigned inq_flush; void * q[SFK_MAX_INQ]; } SFK_PMQ; /* * */ typedef struct { KTRIEPATTERN * patrn; /* List of patterns, built as they are added */ KTRIENODE * root[KTRIE_ROOT_NODES]; /* KTrie nodes */ int memory; int nchars; int npats; int duplicates; int method; int end_states; /* should equal npats - duplicates */ int bcSize; unsigned short bcShift[KTRIE_ROOT_NODES]; void (*userfree)(void *p); void (*optiontreefree)(void **p); void (*neg_list_free)(void **p); SFK_PMQ q; } KTRIE_STRUCT; KTRIE_STRUCT * KTrieNew(int method, void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)); int KTrieAddPattern( KTRIE_STRUCT *ts, unsigned char * P, int n, int nocase, int negative, void * id ); int KTrieCompile(KTRIE_STRUCT * ts, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); struct _SnortConfig; int KTrieCompileWithSnortConf(struct _SnortConfig *, KTRIE_STRUCT * ts, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); int KTrieSearch( KTRIE_STRUCT * ts, unsigned char * T, int n, int(*match)(void * id, void *tree, int index, void *data, void *neg_list), void *data ); unsigned int KTrieMemUsed(void); void KTrieInitMemUsed(void); void KTrieDelete(KTRIE_STRUCT *k); int KTriePatternCount(KTRIE_STRUCT *k); void sfksearch_print_qinfo(void); #endif snort-2.9.20/src/sfutil/sf_base64decode.c0000644000175000017500000001244114241077233016257 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Writen by Patrick Mullen ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_base64decode.h" uint8_t sf_decode64tab[256] = { 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,62 ,100,100,100, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,100,100,100, 99,100,100, 100, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,100,100,100,100,100, 100, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100, 100,100,100,100,100,100,100,100,100,100,100,100,100,100,100,100}; /* base64decode assumes the input data terminates with '=' and/or at the end of the input buffer * at inbuf_size. If extra characters exist within inbuf before inbuf_size is reached, it will * happily decode what it can and skip over what it can't. This is consistent with other decoders * out there. So, either terminate the string, set inbuf_size correctly, or at least be sure the * data is valid up until the point you care about. Note base64 data does NOT have to end with * '=' and won't if the number of bytes of input data is evenly divisible by 3. */ int sf_base64decode(uint8_t *inbuf, uint32_t inbuf_size, uint8_t *outbuf, uint32_t outbuf_size, uint32_t *bytes_written) { uint8_t *cursor, *endofinbuf; uint8_t *outbuf_ptr; uint8_t base64data[4], *base64data_ptr; /* temporary holder for current base64 chunk */ uint8_t tableval_a, tableval_b, tableval_c, tableval_d; uint32_t n; uint32_t max_base64_chars; /* The max number of decoded base64 chars that fit into outbuf */ int error = 0; /* This algorithm will waste up to 4 bytes but we really don't care. At the end we're going to copy the exact number of bytes requested. */ max_base64_chars = (outbuf_size / 3) * 4 + 4; /* 4 base64 bytes gives 3 data bytes, plus an extra 4 to take care of any rounding */ base64data_ptr = base64data; endofinbuf = inbuf + inbuf_size; /* Strip non-base64 chars from inbuf and decode */ n = 0; *bytes_written = 0; cursor = inbuf; outbuf_ptr = outbuf; while((cursor < endofinbuf) && (n < max_base64_chars)) { if(sf_decode64tab[*cursor] != 100) { *base64data_ptr++ = *cursor; n++; /* Number of base64 bytes we've stored */ if(!(n % 4)) { /* We have four databytes upon which to operate */ if((base64data[0] == '=') || (base64data[1] == '=')) { /* Error in input data */ error = 1; break; } /* retrieve values from lookup table */ tableval_a = sf_decode64tab[base64data[0]]; tableval_b = sf_decode64tab[base64data[1]]; tableval_c = sf_decode64tab[base64data[2]]; tableval_d = sf_decode64tab[base64data[3]]; if(*bytes_written < outbuf_size) { *outbuf_ptr++ = (tableval_a << 2) | (tableval_b >> 4); (*bytes_written)++; } if((base64data[2] != '=') && (*bytes_written < outbuf_size)) { *outbuf_ptr++ = (tableval_b << 4) | (tableval_c >> 2); (*bytes_written)++; } else { break; } if((base64data[3] != '=') && (*bytes_written < outbuf_size)) { *outbuf_ptr++ = (tableval_c << 6) | tableval_d; (*bytes_written)++; } else { break; } /* Reset our decode pointer for the next group of four */ base64data_ptr = base64data; } } cursor++; } if(error) return(-1); else return(0); } snort-2.9.20/src/sfutil/ipobj.c0000644000175000017500000003717614241077221014453 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* ipobj.c IP address encapsulation interface This module provides encapsulation of single IP ADDRESSes as objects, and collections of IP ADDRESSes as objects */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifndef WIN32 #include #include #include #include #endif #include #include "ipobj.h" #include "util.h" #include "snort_bounds.h" /* IP COLLECTION INTERFACE Snort Accepts: IP-Address 192.168.1.1 IP-Address/MaskBits 192.168.1.0/24 IP-Address/Mask 192.168.1.0/255.255.255.0 These can all be handled via the CIDR block notation : IP/MaskBits We use collections (lists) of cidr blocks to represent address blocks and indivdual addresses. For a single IPAddress the implied Mask is 32 bits,or 255.255.255.255, or 0xffffffff, or -1. */ IPSET * ipset_new(void) { IPSET * p = (IPSET *)SnortAlloc( sizeof(IPSET)); sflist_init(&p->ip_list); return p; } IPSET * ipset_copy( IPSET *ipsp ) { IPSET * newset = ipset_new(); IP_PORT *ip_port; for(ip_port =(IP_PORT*)sflist_first( &ipsp->ip_list ); ip_port !=NULL; ip_port =(IP_PORT*)sflist_next( &ipsp->ip_list ) ) { ipset_add(newset, &ip_port->ip, &ip_port->portset, ip_port->notflag); } return newset; } void ipset_free( IPSET * ipc ) { if (ipc) { IP_PORT *p = (IP_PORT *) sflist_first(&ipc->ip_list); while ( p ) { sflist_static_free_all(&p->portset.port_list, free); p = (IP_PORT *) sflist_next(&ipc->ip_list); } sflist_static_free_all(&ipc->ip_list, free); free( ipc ); } } int ipset_add ( IPSET * ipset, sfcidr_t *ip, void * vport, int notflag) { if( !ipset ) return -1; { PORTSET * portset = (PORTSET *) vport; IP_PORT *p = (IP_PORT*)calloc( 1,sizeof(IP_PORT) ); if(!p) return -1; sfip_set_ip(&p->ip, ip); p->portset = *portset; p->notflag = (char)notflag; if( notflag )sflist_add_head( &ipset->ip_list, p ); // test NOT items 1st else sflist_add_tail( &ipset->ip_list, p ); } return 0; } int ipset_contains( IPSET * ipc, sfaddr_t * ip, void *port) { PORTRANGE *pr; unsigned short portu; IP_PORT * p; if( !ipc ) return 0; if ( port ) portu = *((unsigned short *)port); else portu = 0; for(p =(IP_PORT*)sflist_first( &ipc->ip_list ); p!=0; p =(IP_PORT*)sflist_next( &ipc->ip_list ) ) { if( sfip_contains(&p->ip, ip) == SFIP_CONTAINS) { for( pr=(PORTRANGE*)sflist_first(&p->portset.port_list); pr != 0; pr=(PORTRANGE*)sflist_next(&p->portset.port_list) ) { /* * If the matching IP has a wildcard port (pr->port_hi == 0 ) * or if the ports actually match. */ if ( (pr->port_hi == 0) || (portu >= pr->port_lo && portu <= pr->port_hi) ) { if( p->notflag ) return 0; return 1; } } } } return 0; } int ipset_print( IPSET * ipc ) { char ip_str[80]; PORTRANGE * pr; if( !ipc ) return 0; { IP_PORT * p; printf("IPSET\n"); for( p =(IP_PORT*)sflist_first( &ipc->ip_list ); p!=0; p =(IP_PORT*)sflist_next( &ipc->ip_list ) ) { SnortSnprintf(ip_str, 80, "%s", sfip_to_str(&p->ip.addr)); printf("CIDR BLOCK: %c%s", p->notflag ? '!' : ' ', ip_str); for( pr=(PORTRANGE*)sflist_first(&p->portset.port_list); pr != 0; pr=(PORTRANGE*)sflist_next(&p->portset.port_list) ) { printf(" %d", pr->port_lo); if ( pr->port_hi != pr->port_lo ) printf("-%d", pr->port_hi); } printf("\n"); } } return 0; } static void portset_init( PORTSET * portset ) { sflist_init(&portset->port_list); } static int portset_add(PORTSET * portset, unsigned port_lo, unsigned port_hi) { PORTRANGE *p; if( !portset ) return -1; p = (PORTRANGE *) calloc( 1,sizeof(PORTRANGE) ); if(!p) return -1; p->port_lo = port_lo; p->port_hi = port_hi; sflist_add_tail(&portset->port_list, p ); return 0; } static int port_parse(char *portstr, PORTSET *portset) { unsigned port_lo = 0, port_hi = 0; char *port1; char *port_begin; char *port_end; char *port2 = NULL; port_begin = SnortStrdup(portstr); port1 = port_begin; port2 = strstr(port_begin, "-"); { if (*port1 == '\0') { free(port_begin); return -1; } if (port2) { *port2 = '\0'; port2++; } port_lo = strtoul(port1, &port_end, 10); if (port_end == port1) { free(port_begin); return -2; } if (port2) { port_hi = strtoul(port2, &port_end, 10); if (port_end == port2) { free(port_begin); return -3; } } else { port_hi = port_lo; } /* check to see if port is out of range */ if ( port_hi > MAXPORTS-1 || port_lo > MAXPORTS-1) { free(port_begin); return -4; } /* swap ports if necessary */ if (port_hi < port_lo) { unsigned tmp; tmp = port_hi; port_hi = port_lo; port_lo = tmp; } portset_add(portset, port_lo, port_hi); } free(port_begin); return 0; } static int ip_parse(char *ipstr, sfcidr_t *ip, char *not_flag, PORTSET *portset, char **endIP) { char *port_str; char *comma; char *end_bracket; if (*ipstr == '!') { ipstr++; *not_flag = 1; } else { *not_flag = 0; } comma = strchr(ipstr, ','); end_bracket = strrchr(ipstr, ']'); if (comma) { *comma = '\0'; } else if (end_bracket) { *end_bracket = '\0'; } if (sfip_pton(ipstr, ip) != SFIP_SUCCESS) return -1; /* Just to get the IP string out of the way */ port_str = strtok(ipstr, " \t"); /* Is either the port after the 1st space, or NULL */ port_str = strtok(NULL, " \t"); while (port_str) { if (!comma) { comma = strchr(port_str, ','); if (comma) *comma = '\0'; } if (!end_bracket) { end_bracket = strrchr(port_str, ']'); if (end_bracket) *end_bracket = '\0'; } port_parse(port_str, portset); port_str = strtok(NULL, " \t"); } if (portset->port_list.count == 0) { /* Make sure we have at least one port range in list, but * an invalid port range to convey all is good. */ portset_add(portset, 0, 0); } if (comma) { *endIP = comma; *comma = ','; } else if (end_bracket) { *end_bracket = ']'; *endIP = end_bracket; } else { /* Didn't see the comma or end bracket, so set endIP now */ *endIP = port_str; } return 0; } int ipset_parse(IPSET *ipset, char *ipstr) { char *copy, *startIP, *endIP; int parse_count = 0; char set_not_flag = 0; char item_not_flag; char open_bracket = 0; sfcidr_t ip; PORTSET portset; copy = strdup(ipstr); if(!copy) return -2; startIP = copy; if (*startIP == '!') { set_not_flag = 1; startIP++; } while (startIP) { if (*startIP == '[') { open_bracket++; startIP++; if (!*startIP) break; } if ((*startIP == ']') || (*startIP == '\0')) { open_bracket--; break; } portset_init(&portset); if(ip_parse(startIP, &ip, &item_not_flag, &portset, &endIP) != 0) { free(copy); return -5; } if(ipset_add(ipset, &ip, &portset, (item_not_flag ^ set_not_flag)) != 0) { free(copy); return -6; } parse_count++; if (endIP && (*endIP != ']')) { endIP++; } startIP = endIP; } free(copy); if (!parse_count) return -7; if (open_bracket) return -8; return 0; } #ifdef MAIN_IP #include #ifndef WIN32 #define rand random #define srand srandom #endif #define MAXIP 100 #include "sflsq.c" void test_ip4_parsing(void) { unsigned host, mask, not_flag; PORTSET portset; char **curip; int ret; IPADDRESS *adp; char *ips[] = { "138.26.1.24:25", "1.1.1.1/255.255.255.0:444", "1.1.1.1/16:25-28", "1.1.1.1/255.255.255.255:25 27-29", "z/24", "0/0", "0.0.0.0/0.0.0.0:25-26 28-29 31", "0.0.0.0/0.0.2.0", NULL }; for(curip = ips; curip[0] != NULL; curip++) { portset_init(&portset); /* network byte order stuff */ if((ret = ip4_parse(curip[0], 1, ¬_flag, &host, &mask, &portset)) != 0) { fprintf(stderr, "Unable to parse %s with ret %d\n", curip[0], ret); } else { printf("%c", not_flag ? '!' : ' '); printf("%s/", inet_ntoa(*(struct in_addr *) &host)); printf("%s", inet_ntoa(*(struct in_addr *) &mask)); printf(" parsed successfully!\n"); } /* host byte order stuff */ if((ret = ip4_parse(curip[0], 0, ¬_flag, &host, &mask, &portset)) != 0) { fprintf(stderr, "Unable to parse %s with ret %d\n", curip[0], ret); } else { adp = ip_new(IPV4_FAMILY); ip_set(adp, &host, IPV4_FAMILY); ip_fprint(stdout, adp); fprintf(stdout, "*****************\n"); ip_free(adp); } } return; } void test_ip4set_parsing(void) { char **curip; int ret; char *ips[] = { "12.24.24.1/32,!24.24.24.1", "[0.0.0.0/0.0.2.0,241.242.241.22]", "138.26.1.24", "1.1.1.1", "1.1.1.1/16", "1.1.1.1/255.255.255.255", "z/24", "0/0", "0.0.0.0/0.0.0.0", "0.0.0.0/0.0.2.0", NULL }; for(curip = ips; curip[0] != NULL; curip++) { IPSET *ipset = ipset_new(IPV4_FAMILY); /* network byte order stuff */ if((ret = ip4_setparse(ipset, curip[0])) != 0) { ipset_free(ipset); fprintf(stderr, "Unable to parse %s with ret %d\n", curip[0], ret); } else { printf("-[%s]\n ", curip[0]); ipset_print(ipset); printf("---------------------\n "); } } return; } // ----------------------------- void test_ip(void) { int i,k; IPADDRESS * ipa[MAXIP]; unsigned ipaddress,ipx; unsigned short ipaddress6[8], ipx6[8]; printf("IPADDRESS testing\n"); srand( time(0) ); for(i=0;i>27) #define SETSKIP(skip) ((skip)<<22) #define GETSKIP(node) ((node)>>22 & 037) #define SETADR(adr) (adr) #define GETADR(node) ((node) & 017777777) /* extract n bits from str starting at position p */ #define EXTRACT(p, n, str) ((str)<<(p)>>(32-(n))) /* remove the first p bits from string */ #define REMOVE(p, str) ((str)<<(p)>>(p)) /* A next-hop table entry is a 32 bit string */ typedef word policy_t; /* The routing table entries are initially stored in a simple array */ typedef struct entryrec *entry_t; struct entryrec { word data; /* the routing entry */ int len; /* and its length */ policy_t policy; /* the corresponding next-hop */ int pre; /* this auxiliary variable is used in the */ }; /* construction of the final data structure */ /* base vector */ typedef struct baserec *base_t; struct baserec { word str; /* the routing entry */ int len; /* and its length */ int pre; /* pointer to prefix table, -1 if no prefix */ int policy; /* pointer to next-hop table */ }; typedef struct { /* compact version of above */ word str; int len; int pre; int policy; } comp_base_t; /* prefix vector */ typedef struct prerec *pre_t; struct prerec { int len; /* the length of the prefix */ int pre; /* pointer to prefix, -1 if no prefix */ int policy; /* pointer to policy table */ }; typedef struct { /* compact version of above */ int len; int pre; int policy; } comp_pre_t; /* The complete routing table data structure consists of a trie, a base vector, a prefix vector, and a next-hop table. */ typedef struct routtablerec *routtable_t; struct routtablerec { node_t *trie; /* the main trie search structure */ int triesize; comp_base_t *base; /* the base vector */ int basesize; comp_pre_t *pre; /* the prefix vector */ int presize; policy_t *policy; /* the next-hop table */ int policysize; int dirty; /* Whether or not the table needs to be rebuilt */ }; #endif snort-2.9.20/src/sfutil/sfthd.c0000644000175000017500000011415514241077336014460 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /*! * * \file sfthd.c * * An Abstracted Event Thresholding System * * Marc Norton * * 3/5/07 - man - fixed memory leak in global config to limit * of one gid=0, or multiple gid!=0 but not both. * Boris Lytochkin found it. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #ifndef WIN32 #include #endif #include "parser/IpAddrSet.h" #include "sflsq.h" #include "sfghash.h" #include "sfxhash.h" #include "snort.h" #include "sfthd.h" #include "util.h" #include "sfPolicy.h" // Debug Printing //#define THD_DEBUG // This disables adding and testing of Threshold objects //#define CRIPPLE SFXHASH * sfthd_new_hash(unsigned nbytes, size_t key, size_t data) { size_t size = key + data; int nrows; /* Calc max ip nodes for this memory */ if ( nbytes < size ) { nbytes = size; } nrows = nbytes / (size); return sfxhash_new( nrows, /* try one node per row - for speed */ key, /* keys size */ data, /* data size */ nbytes, /* memcap **/ 1, /* ANR flag - true ?- Automatic Node Recovery=ANR */ 0, /* ANR callback - none */ 0, /* user freemem callback - none */ 1 ) ; /* Recycle nodes ?*/ } /*! Create a threshold table, initialize the threshold system, and optionally limit it's memory usage. @param nbytes maximum memory to use for thresholding objects, in bytes. @return THD_STRUCT* @retval 0 error @retval !0 valid THD_STRUCT */ SFXHASH * sfthd_local_new(unsigned bytes) { SFXHASH *local_hash = sfthd_new_hash(bytes, sizeof(THD_IP_NODE_KEY), sizeof(THD_IP_NODE)); #ifdef THD_DEBUG if (local_hash == NULL) printf("Could not allocate the sfxhash table\n"); #endif return local_hash; } SFXHASH * sfthd_global_new(unsigned bytes) { SFXHASH *global_hash = sfthd_new_hash(bytes, sizeof(THD_IP_GNODE_KEY), sizeof(THD_IP_NODE)); #ifdef THD_DEBUG if (global_hash == NULL) printf("Could not allocate the sfxhash table\n"); #endif return global_hash; } THD_STRUCT * sfthd_new(unsigned lbytes, unsigned gbytes) { THD_STRUCT * thd; /* Create the THD struct */ thd = (THD_STRUCT *)SnortAlloc(sizeof(THD_STRUCT)); #ifndef CRIPPLE /* Create hash table for all of the local IP Nodes */ thd->ip_nodes = sfthd_local_new(lbytes); if( !thd->ip_nodes ) { #ifdef THD_DEBUG printf("Could not allocate the sfxhash table\n"); #endif free(thd); return NULL; } if ( gbytes == 0 ) return thd; /* Create hash table for all of the global IP Nodes */ thd->ip_gnodes = sfthd_global_new(gbytes); if( !thd->ip_gnodes ) { #ifdef THD_DEBUG printf("Could not allocate the sfxhash table\n"); #endif sfxhash_delete(thd->ip_nodes); free(thd); return NULL; } #endif return thd; } ThresholdObjects * sfthd_objs_new(void) { return (ThresholdObjects *)SnortAlloc(sizeof(ThresholdObjects)); } static void sfthd_node_free(void *node) { THD_NODE *sfthd_node = (THD_NODE *)node; if (sfthd_node == NULL) return; if (sfthd_node->ip_address != NULL) { IpAddrSetDestroy(sfthd_node->ip_address); } free(sfthd_node); } void sfthd_objs_free(ThresholdObjects *thd_objs) { int i; tSfPolicyId policyId; if (thd_objs == NULL) return; for (i = 0; i < THD_MAX_GENID; i++) { if (thd_objs->sfthd_array[i]) sfghash_delete(thd_objs->sfthd_array[i]); } for (policyId = 0; policyId < thd_objs->numPoliciesAllocated; policyId++) { if (thd_objs->sfthd_garray[policyId] == NULL) continue; if (thd_objs->sfthd_garray[policyId][0] != NULL) { sfthd_node_free((void *)thd_objs->sfthd_garray[policyId][0]); /* Free any individuals */ for (i = 0; i < THD_MAX_GENID; i++) { if (thd_objs->sfthd_garray[policyId][i] != thd_objs->sfthd_garray[policyId][0]) { sfthd_node_free( (void *)thd_objs->sfthd_garray[policyId][i]); } } } else { /* Anything other GID will be allocated individually */ for (i = 1; i < THD_MAX_GENID; i++) { if (thd_objs->sfthd_garray[policyId][i]) { sfthd_node_free((void *)thd_objs->sfthd_garray[policyId][i]); } } } free(thd_objs->sfthd_garray[policyId]); } if (thd_objs->sfthd_garray != NULL) free(thd_objs->sfthd_garray); free(thd_objs); } void sfthd_item_free(void *item) { THD_ITEM *sfthd_item = (THD_ITEM*)item; sflist_free_all(sfthd_item->sfthd_node_list, sfthd_node_free); free(sfthd_item); } void sfthd_free(THD_STRUCT *thd) { if (thd == NULL) return; #ifndef CRIPPLE if (thd->ip_nodes != NULL) sfxhash_delete(thd->ip_nodes); if (thd->ip_gnodes != NULL) sfxhash_delete(thd->ip_gnodes); #endif free(thd); } void * sfthd_create_rule_threshold(int id, int tracking, int type, int count, unsigned int seconds) { THD_NODE *sfthd_node = (THD_NODE *)calloc(1, sizeof(THD_NODE)); if (sfthd_node == NULL) return NULL; sfthd_node->thd_id = id; sfthd_node->tracking = tracking; sfthd_node->type = type; sfthd_node->count = count; sfthd_node->seconds = seconds; return (void *)sfthd_node; } /*! Add a permanent threshold object to the threshold table. Multiple objects may be defined for each gen_id and sig_id pair. Internally a unique threshold id is generated for each pair. Threshold objects track the number of events seen during the time interval specified by seconds. Depending on the type of threshold object and the count value, the thresholding object determines if the current event should be logged or dropped. @param thd Threshold object from sfthd_new() @param gen_id Generator id @param sig_id Signauture id @param tracking Selects tracking by src ip or by dst ip @param type Thresholding type: Limit, Threshold, or Limt+Threshold, Suppress @param priority Assigns a relative priority to this object, higher numbers imply higher priority @param count Number of events @param seconds Time duration over which this threshold object acts. @param ip IP address, for supression @param ip-mask IP mask, applied with ip_mask, for supression @return integer @retval 0 successfully added the thresholding object @retval !0 failed */ static int sfthd_create_threshold_local(SnortConfig *sc, ThresholdObjects *thd_objs, THD_NODE* config) { SFGHASH * sfthd_hash; THD_ITEM * sfthd_item; THD_NODE * sfthd_node; tThdItemKey key; int nrows; int hstatus; tSfPolicyId policy_id = getParserPolicy(sc); if (thd_objs == NULL ) return -1; if( config->gen_id >= THD_MAX_GENID ) return -1; #ifdef CRIPPLE return 0; #endif /* Check for an existing 'gen_id' entry, if none found create one. */ if (thd_objs->sfthd_array[config->gen_id] == NULL) { if( config->gen_id == 1 )/* patmatch rules gen_id, many rules */ { nrows= THD_GEN_ID_1_ROWS; } else /* other gen_id's */ { nrows= THD_GEN_ID_ROWS; } /* Create the hash table for this gen_id */ sfthd_hash = sfghash_new( nrows, sizeof(tThdItemKey), 0, sfthd_item_free ); if( !sfthd_hash ) { return -2; } thd_objs->sfthd_array[config->gen_id] = sfthd_hash; } else { /* Get the hash table for this gen_id */ sfthd_hash = thd_objs->sfthd_array[config->gen_id]; } if (sfthd_hash == NULL) return -2; key.sig_id = config->sig_id; key.policyId = policy_id; /* Check if sig_id is already in the table - if not allocate and add it */ sfthd_item = (THD_ITEM*)sfghash_find( sfthd_hash, (void*)&key ); if( !sfthd_item ) { /* Create the sfthd_item hash node data */ sfthd_item = (THD_ITEM*)calloc(1,sizeof(THD_ITEM)); if( !sfthd_item ) { return -3; } sfthd_item->gen_id = config->gen_id; sfthd_item->sig_id = config->sig_id; sfthd_item->policyId = policy_id; sfthd_item->sfthd_node_list = sflist_new(); if(!sfthd_item->sfthd_node_list) { free(sfthd_item); return -4; } /* Add the sfthd_item to the hash table */ hstatus = sfghash_add( sfthd_hash, (void*)&key, sfthd_item ); if( hstatus ) { sflist_free(sfthd_item->sfthd_node_list); free(sfthd_item); return -5; } } /* * Test that we only have one Limit/Threshold/Both Object at the tail, * we can have multiple suppression nodes at the head */ if( sfthd_item->sfthd_node_list->count > 0 ) { THD_NODE * p; if( !sfthd_item->sfthd_node_list->tail) { /* can you say paranoid- if there is a count, there should be a tail */ return -10; } p = (THD_NODE*)sfthd_item->sfthd_node_list->tail->ndata; if(p) /* just to be safe- if thers a tail, there is is node data */ { if( p->type != THD_TYPE_SUPPRESS && config->type != THD_TYPE_SUPPRESS ) { #ifdef THD_DEBUG printf("THD_DEBUG: Could not add a 2nd Threshold object, " "you can only have 1 per sid: gid=%u, sid=%u\n", config->gen_id, config->sig_id); #endif /* cannot add more than one threshold per sid in version 3.0, wait for 3.2 and CIDR blocks */ return THD_TOO_MANY_THDOBJ; } } } /* Create a THD_NODE for this THD_ITEM (Object) */ sfthd_node = (THD_NODE*)calloc(1,sizeof(THD_NODE)); if( !sfthd_node ) { return -6; } /* Limit priorities to force supression nodes to highest priority */ if( config->priority >= THD_PRIORITY_SUPPRESS ) { config->priority = THD_PRIORITY_SUPPRESS - 1; } /* Copy the node parameters */ sfthd_node->thd_id = config->thd_id; sfthd_node->gen_id = config->gen_id; sfthd_node->sig_id = config->sig_id; sfthd_node->tracking = config->tracking; /* by_src, by_dst */ sfthd_node->type = config->type; sfthd_node->priority = config->priority; sfthd_node->count = config->count; sfthd_node->seconds = config->seconds; sfthd_node->ip_address= config->ip_address; if( config->type == THD_TYPE_SUPPRESS ) { sfthd_node->priority = THD_PRIORITY_SUPPRESS; } /* If sfthd_node list is empty - add as head node */ if( !sfthd_item->sfthd_node_list->count ) { #ifdef THD_DEBUG printf("Threshold node added to head of list\n");fflush(stdout); #endif sflist_add_head(sfthd_item->sfthd_node_list,sfthd_node); } /* else add the sfthd_node using priority to determine where in the list it belongs 3.0 we can have only 1 threshold object but several suppression objects plus a single threshold object is ok. Blocking multiple threshold objects is done above. Suppressions have the highest priority and are at the front of the list, the tail node is either a supprssion node or the only pure thresholding node. */ else { SF_LNODE* lnode; /* Walk the list and insert based on priorities if suppress */ for( lnode = sflist_first_node(sfthd_item->sfthd_node_list); lnode; lnode = sflist_next_node(sfthd_item->sfthd_node_list) ) { THD_NODE* sfthd_n = (THD_NODE*)lnode->ndata; /* check if the new node is higher priority */ if( sfthd_node->priority > sfthd_n->priority ) { /* insert before current node */ #ifdef THD_DEBUG printf("Threshold node added after based on priority\n");fflush(stdout); #endif sflist_add_before(sfthd_item->sfthd_node_list,lnode,sfthd_node); return 0; } /* last node, just insert it here */ if( !lnode->next ) { /* if last node, insert at end of list */ #ifdef THD_DEBUG printf("Threshold node added to tail\n");fflush(stdout); #endif sflist_add_tail(sfthd_item->sfthd_node_list,sfthd_node); return 0; } } } return 0; } /* */ static int sfthd_create_threshold_global(SnortConfig *sc, ThresholdObjects *thd_objs, THD_NODE* config) { THD_NODE *sfthd_node; tSfPolicyId policy_id = getParserPolicy(sc); if (thd_objs == NULL) return -1; if (thd_objs->sfthd_garray[policy_id] == NULL) { thd_objs->sfthd_garray[policy_id] = (THD_NODE **)(calloc(THD_MAX_GENID, sizeof(THD_NODE *))); if (thd_objs->sfthd_garray[policy_id] == NULL) { return -1; } } if ((config->gen_id == 0) && (thd_objs->sfthd_garray[policy_id][config->gen_id] != NULL)) { return THD_TOO_MANY_THDOBJ; } /* Reset the current threshold */ if (thd_objs->sfthd_garray[policy_id][config->gen_id] == thd_objs->sfthd_garray[policy_id][0]) { thd_objs->sfthd_garray[policy_id][config->gen_id] = NULL; } else if(thd_objs->sfthd_garray[policy_id][config->gen_id]) { return THD_TOO_MANY_THDOBJ; } sfthd_node = (THD_NODE*)calloc(1,sizeof(THD_NODE)); if( !sfthd_node ) { return -2; } /* Copy the node parameters */ sfthd_node->thd_id = config->thd_id; sfthd_node->gen_id = config->gen_id; sfthd_node->sig_id = config->sig_id; /* -1 for global thresholds */ sfthd_node->tracking = config->tracking; /* by_src, by_dst */ sfthd_node->type = config->type; sfthd_node->priority = config->priority; sfthd_node->count = config->count; sfthd_node->seconds = config->seconds; sfthd_node->ip_address = config->ip_address; /* need a hash of these where * key=[gen_id,sig_id] => THD_GNODE_KEY * data = THD_NODE's */ if (config->gen_id == 0) /* do em all */ { int i; for (i = 0; i < THD_MAX_GENID; i++) { /* only assign if there wasn't a value */ if (thd_objs->sfthd_garray[policy_id][i] == NULL) { thd_objs->sfthd_garray[policy_id][i] = sfthd_node; } } } else { thd_objs->sfthd_garray[policy_id][config->gen_id] = sfthd_node; } #ifdef THD_DEBUG printf("THD_DEBUG-GLOBAL: created global threshold object " "for gen_id=%d\n",config->gen_id); fflush(stdout); #endif return 0; } /*! Add a permanent threshold object to the threshold table. Multiple objects may be defined for each gen_id and sig_id pair. Internally a unique threshold id is generated for each pair. Threshold objects track the number of events seen during the time interval specified by seconds. Depending on the type of threshold object and the count value, the thresholding object determines if the current event should be logged or dropped. @param thd Threshold object from sfthd_new() @param gen_id Generator id @param sig_id Signauture id @param tracking Selects tracking by src ip or by dst ip @param type Thresholding type: Limit, Threshold, or Limt+Threshold, Suppress @param priority Assigns a relative priority to this object, higher numbers imply higher priority @param count Number of events @param seconds Time duration over which this threshold object acts. @param ip IP address, for supression @param ip-mask IP mask, applied with ip_mask, for supression @return integer @retval 0 successfully added the thresholding object @retval !0 failed --- Local and Global Thresholding is setup here --- */ int sfthd_create_threshold(SnortConfig *sc, ThresholdObjects *thd_objs, unsigned gen_id, unsigned sig_id, int tracking, int type, int priority, int count, int seconds, IpAddrSet* ip_address) { //allocate memory fpr sfthd_array if needed. tSfPolicyId policyId = getParserPolicy(sc); THD_NODE sfthd_node; memset(&sfthd_node, 0, sizeof(sfthd_node)); thd_objs->count++; sfthd_node.thd_id = thd_objs->count; /* produce a unique thd_id for this node */ sfthd_node.gen_id = gen_id; sfthd_node.sig_id = sig_id; sfthd_node.tracking = tracking; /* by_src, by_dst */ sfthd_node.type = type; sfthd_node.priority = priority; sfthd_node.count = count; sfthd_node.seconds = seconds; sfthd_node.ip_address= ip_address; sfDynArrayCheckBounds ((void **)&thd_objs->sfthd_garray, policyId, &thd_objs->numPoliciesAllocated); if (thd_objs->sfthd_garray[policyId] == NULL) { thd_objs->sfthd_garray[policyId] = SnortAlloc(THD_MAX_GENID * sizeof(THD_NODE *)); if (thd_objs->sfthd_garray[policyId] == NULL) { return -1; } } if( sig_id == 0 ) { return sfthd_create_threshold_global(sc, thd_objs, &sfthd_node); } if( gen_id == 0 ) return -1; return sfthd_create_threshold_local(sc, thd_objs, &sfthd_node); } #ifdef THD_DEBUG static char * printIP(unsigned u ) { static char s[80]; SnortSnprintf(s,80,"%d.%d.%d.%d", (u>>24)&0xff, (u>>16)&0xff, (u>>8)&0xff, u&0xff ); return s; } #endif int sfthd_test_rule(SFXHASH *rule_hash, THD_NODE *sfthd_node, sfaddr_t* sip, sfaddr_t* dip, long curtime, detection_option_eval_data_t *eval_data) { int status; if ((rule_hash == NULL) || (sfthd_node == NULL)) return 0; status = sfthd_test_local(rule_hash, sfthd_node, sip, dip, curtime, eval_data); return (status < -1) ? 1 : status; } static inline int sfthd_test_suppress ( THD_NODE* sfthd_node, sfaddr_t* ip) { if ( !sfthd_node->ip_address || IpAddrSetContains(sfthd_node->ip_address, ip) ) { #ifdef THD_DEBUG printf("THD_DEBUG: SUPPRESS NODE, do not log events with this IP\n"); fflush(stdout); #endif /* Don't log, and stop looking( event's to this address * for this gen_id+sig_id) */ sfthd_node->filtered++; return -1; } return 1; /* Keep looking for other suppressors */ } /* * Do the appropriate test for the Threshold Object Type */ static inline int sfthd_test_non_suppress( THD_NODE* sfthd_node, THD_IP_NODE* sfthd_ip_node, time_t curtime) { unsigned dt; if( sfthd_node->type == THD_TYPE_DETECT ) { #ifdef THD_DEBUG printf("\n...Detect Test\n"); fflush(stdout); #endif dt = (unsigned)(curtime - sfthd_ip_node->tstart); if( dt >= sfthd_node->seconds ) { /* reset */ sfthd_ip_node->tstart = curtime; if ( (unsigned)(curtime - sfthd_ip_node->tlast) > sfthd_node->seconds ) sfthd_ip_node->prev = 0; else sfthd_ip_node->prev = sfthd_ip_node->count - 1; sfthd_ip_node->count = 1; } sfthd_ip_node->tlast = curtime; #ifdef THD_DEBUG printf("...dt=%d, sfthd_node->seconds=%d\n",dt, sfthd_node->seconds ); printf("...sfthd_ip_node->count=%d, sfthd_node->count=%d\n", sfthd_ip_node->count,sfthd_node->count ); fflush(stdout); #endif if( (int)sfthd_ip_node->count > sfthd_node->count || (int)sfthd_ip_node->prev > sfthd_node->count ) { return 0; /* Log it, stop looking: log all > 'count' events */ } /* Don't Log yet, don't keep looking: * already logged our limit, don't log this sid */ sfthd_node->filtered++; return -2; } if( sfthd_node->type == THD_TYPE_LIMIT ) { #ifdef THD_DEBUG printf("\n...Limit Test\n"); fflush(stdout); #endif dt = (unsigned)(curtime - sfthd_ip_node->tstart); if( dt >= sfthd_node->seconds ) { /* reset */ sfthd_ip_node->tstart = curtime; sfthd_ip_node->count = 1; } #ifdef THD_DEBUG printf("...dt=%d, sfthd_node->seconds=%d\n",dt, sfthd_node->seconds ); printf("...sfthd_ip_node->count=%d, sfthd_node->count=%d\n", sfthd_ip_node->count,sfthd_node->count ); fflush(stdout); #endif if( (int)sfthd_ip_node->count <= sfthd_node->count ) { return 0; /* Log it, stop looking: only log the 1st 'count' events */ } /* Don't Log yet, don't keep looking: * already logged our limit, don't log this sid */ sfthd_node->filtered++; return -2; } else if( sfthd_node->type == THD_TYPE_THRESHOLD ) { #ifdef THD_DEBUG printf("\n...Threshold Test\n"); fflush(stdout); #endif dt = (unsigned)(curtime - sfthd_ip_node->tstart); if( dt >= sfthd_node->seconds ) { sfthd_ip_node->tstart = curtime; sfthd_ip_node->count = 1; } if( (int)sfthd_ip_node->count >= sfthd_node->count ) { /* reset */ sfthd_ip_node->count = 0; sfthd_ip_node->tstart= curtime; return 0; /* Log it, stop looking */ } sfthd_node->filtered++; return -2; /* don't log yet */ } else if( sfthd_node->type == THD_TYPE_BOTH ) { #ifdef THD_DEBUG printf("\n...Threshold+Limit Test\n"); fflush(stdout); #endif dt = (unsigned)(curtime - sfthd_ip_node->tstart); if( dt >= sfthd_node->seconds ) { sfthd_ip_node->tstart = curtime; sfthd_ip_node->count = 1; /* Don't Log yet, keep looking: * only log after we reach count, which must be > '1' */ sfthd_node->filtered++; return -2; } else { if( (int)sfthd_ip_node->count >= sfthd_node->count ) { if( (int)sfthd_ip_node->count > sfthd_node->count ) { /* don't log it, stop looking: * log once per time interval - than block it */ sfthd_node->filtered++; return -2; } /* Log it, stop looking: * log the 1st event we see past 'count' events */ return 0; } else /* Block it from logging */ { /* don't log it, stop looking: * we must see at least count events 1st */ sfthd_node->filtered++; return -2; } } } #ifdef THD_DEBUG printf("THD_DEBUG: You should not be here...\n"); fflush(stdout); #endif return 0; /* should not get here, so log it just to be safe */ } /*! * * Find/Test/Add an event against a single threshold object. * Events without thresholding objects are automatically loggable. * * @param thd Threshold table pointer * @param sfthd_node Permanent Thresholding Object * @param sip Event/Packet Src IP address- should be host ordered for comparison * @param dip Event/Packet Dst IP address * @param curtime Current Event/Packet time in seconds * * @return integer * @retval 0 : Event is loggable * @retval >0 : Event should not be logged, try next thd object * @retval <0 : Event should never be logged to this user! Suppressed Event+IP * */ int sfthd_test_local( SFXHASH *local_hash, THD_NODE * sfthd_node, sfaddr_t* sip, sfaddr_t* dip, time_t curtime, detection_option_eval_data_t *eval_data) { THD_IP_NODE_KEY key; THD_IP_NODE data,*sfthd_ip_node; int status=0; sfaddr_t* ip; tSfPolicyId policy_id = getIpsRuntimePolicy(); #ifdef THD_DEBUG printf("THD_DEBUG: Key THD_NODE IP=%s,",printIP((unsigned)sfthd_node->ip_address) ); printf(" MASK=%s\n",printIP((unsigned)sfthd_node->ip_mask) ); printf("THD_DEBUG: PKT SIP=%s\n",printIP((unsigned)sip) ); printf("THD_DEBUG: PKT DIP=%s\n",printIP((unsigned)dip) ); fflush(stdout); #endif /* -1 means don't do any limit or thresholding */ if ( sfthd_node->count == THD_NO_THRESHOLD) { #ifdef THD_DEBUG printf("\n...No Threshold applied for this object\n"); fflush(stdout); #endif return 0; } /* * Get The correct IP */ if (sfthd_node->tracking == THD_TRK_SRC) ip = sip; else ip = dip; /* * Check for and test Suppression of this event to this IP */ if( sfthd_node->type == THD_TYPE_SUPPRESS ) { #ifdef THD_DEBUG printf("THD_DEBUG: SUPPRESS NODE Testing...\n");fflush(stdout); #endif return sfthd_test_suppress(sfthd_node, ip); } /* * Go on and do standard thresholding */ /* Set up the key */ key.policyId = policy_id; sfaddr_copy_to_raw(&key.ip, ip); key.thd_id = sfthd_node->thd_id; /* Set up a new data element */ data.count = 0; data.prev = 0; data.tstart = data.tlast = curtime; /* Event time */ /* * Check for any Permanent sig_id objects for this gen_id or add this one ... */ status = sfxhash_add(local_hash, (void*)&key, &data); if (status == SFXHASH_INTABLE || status == SFXHASH_OK) { /* Already in the table */ sfthd_ip_node = local_hash->cnode->data; /* Increment the event count */ if(eval_data) { if(eval_data->detection_filter_count == 0) { eval_data->detection_filter_count = 1; sfthd_ip_node->count++; } } else { sfthd_ip_node->count++; } } else if (status != SFXHASH_OK) { /* hash error */ return 1; /* check the next threshold object */ } else { /* Was not in the table - it was added - work with our copy of the data */ sfthd_ip_node = &data; if(eval_data) { eval_data->detection_filter_count = 1; } } return sfthd_test_non_suppress(sfthd_node, sfthd_ip_node, curtime); } /* * Test a global thresholding object */ static inline int sfthd_test_global( SFXHASH *global_hash, THD_NODE * sfthd_node, unsigned gen_id, /* from current event */ unsigned sig_id, /* from current event */ sfaddr_t* sip, /* " */ sfaddr_t* dip, /* " */ time_t curtime ) { THD_IP_GNODE_KEY key; THD_IP_NODE data, *sfthd_ip_node; int status=0; sfaddr_t* ip; tSfPolicyId policy_id = getIpsRuntimePolicy(); #ifdef THD_DEBUG printf("THD_DEBUG-GLOBAL: gen_id=%u, sig_id=%u\n",gen_id,sig_id); printf("THD_DEBUG: Global THD_NODE IP=%s,",printIP((unsigned)sfthd_node->ip_address) ); printf(" MASK=%s\n",printIP((unsigned)sfthd_node->ip_mask) ); printf("THD_DEBUG: PKT SIP=%s\n",printIP((unsigned)sip) ); printf("THD_DEBUG: PKT DIP=%s\n",printIP((unsigned)dip) ); fflush(stdout); #endif /* -1 means don't do any limit or thresholding */ if ( sfthd_node->count == THD_NO_THRESHOLD) { #ifdef THD_DEBUG printf("\n...No Threshold applied for this object\n"); fflush(stdout); #endif return 0; } /* Get The correct IP */ if (sfthd_node->tracking == THD_TRK_SRC) ip = sip; else ip = dip; /* Check for and test Suppression of this event to this IP */ if( sfthd_node->type == THD_TYPE_SUPPRESS ) { #ifdef THD_DEBUG printf("THD_DEBUG: G-SUPPRESS NODE Testing...\n");fflush(stdout); #endif return sfthd_test_suppress(sfthd_node, ip); } /* * Go on and do standard thresholding */ /* Set up the key */ sfaddr_copy_to_raw(&key.ip, ip); key.gen_id = sfthd_node->gen_id; key.sig_id = sig_id; key.policyId = policy_id; /* Set up a new data element */ data.count = 1; data.prev = 0; data.tstart = data.tlast = curtime; /* Event time */ /* Check for any Permanent sig_id objects for this gen_id or add this one ... */ status = sfxhash_add(global_hash, (void*)&key, &data); if (status == SFXHASH_INTABLE) { /* Already in the table */ sfthd_ip_node = global_hash->cnode->data; /* Increment the event count */ sfthd_ip_node->count++; } else if (status != SFXHASH_OK) { /* hash error */ return 1; /* check the next threshold object */ } else { /* Was not in the table - it was added - work with our copy of the data */ sfthd_ip_node = &data; } return sfthd_test_non_suppress(sfthd_node, sfthd_ip_node, curtime); } /*! * * Test a an event against the threshold database. * Events without thresholding objects are automatically * loggable. * * @param thd Threshold table pointer * @param gen_id Generator Id from the event * @param sig_id Signature Id from the event * @param sip Event/Packet Src IP address * @param dip Event/Packet Dst IP address * @param curtime Current Event/Packet time * * @return integer * @retval 0 : Event is loggable * @retval !0 : Event should not be logged (-1 suppressed, 1 filtered) * */ int sfthd_test_threshold( ThresholdObjects *thd_objs, THD_STRUCT *thd, unsigned gen_id, unsigned sig_id, sfaddr_t* sip, sfaddr_t* dip, long curtime ) { tThdItemKey key; SFGHASH * sfthd_hash; THD_ITEM * sfthd_item; THD_NODE * sfthd_node; THD_NODE * g_thd_node = NULL; #ifdef THD_DEBUG int cnt; #endif int status=0; tSfPolicyId policy_id = getIpsRuntimePolicy(); if ((thd_objs == NULL) || (thd == NULL)) return 0; #ifdef CRIPPLE return 0; #endif #ifdef THD_DEBUG printf("sfthd_test_threshold...\n");fflush(stdout); #endif if( gen_id >= THD_MAX_GENID ) { #ifdef THD_DEBUG printf("THD_DEBUG: invalid gen_id=%u\n",gen_id); fflush(stdout); #endif return 0; /* bogus gen_id */ } /* * Get the hash table for this gen_id */ sfthd_hash = thd_objs->sfthd_array[gen_id]; if (sfthd_hash == NULL) { #ifdef THD_DEBUG printf("THD_DEBUG: no hash table entry for gen_id=%u\n",gen_id); fflush(stdout); #endif goto global_test; /* return 0; */ /* no threshold objects for this gen_id, log it ! */ } key.sig_id = sig_id; key.policyId = policy_id; /* * Check for any Permanent sig_id objects for this gen_id */ sfthd_item = (THD_ITEM *)sfghash_find(sfthd_hash, (void*)&key); if (sfthd_item == NULL) { #ifdef THD_DEBUG printf("THD_DEBUG: no THD objects for gen_id=%u, sig_id=%u\n",gen_id,sig_id); fflush(stdout); #endif /* no matching permanent sig_id objects so, log it ! */ goto global_test; } /* No List of Threshold objects - bail and log it */ if (sfthd_item->sfthd_node_list == NULL) { goto global_test; } /* For each permanent thresholding object, test/add/update the thd object */ /* We maintain a list of thd objects for each gen_id+sig_id */ /* each object has it's own unique thd_id */ /* Suppression nodes have a very high priority, so they are tested 1st */ #ifdef THD_DEBUG cnt=0; #endif for (sfthd_node = (THD_NODE *)sflist_first(sfthd_item->sfthd_node_list); sfthd_node != NULL; sfthd_node = (THD_NODE *)sflist_next(sfthd_item->sfthd_node_list)) { #ifdef THD_DEBUG cnt++; printf("THD_DEBUG: gen_id=%u sig_id=%u testing thd_id=%d thd_type=%d\n", gen_id, sig_id, sfthd_node->thd_id, sfthd_node->type); fflush(stdout); #endif /* * Test SUPPRESSION and THRESHOLDING */ status = sfthd_test_local(thd->ip_nodes, sfthd_node, sip, dip, curtime, NULL); if( status < 0 ) /* -1 == Don't log and stop looking */ { #ifdef THD_DEBUG printf("THD_DEBUG: gen_id=%u sig_id=%u, UnLoggable\n\n",gen_id, sig_id,cnt); fflush(stdout); #endif return (status < -1) ? 1 : -1; /* !0 == Don't log it*/ } else if( status == 0 ) /* Log it and stop looking */ { #ifdef THD_DEBUG printf("THD_DEBUG: gen_id=%u sig_id=%u tested %d THD_NODE's, " "Loggable\n\n",sfthd_item->gen_id, sfthd_item->sig_id,cnt); fflush(stdout); #endif return 0; /* 0 == Log the event */ } /* status > 0 : Log it later but Keep looking * check the next threshold object for a blocking action ... */ } /* * Test for a global threshold object * we're here cause ther were no threshold objects for this gen_id/sig_id pair */ global_test: #ifdef THD_DEBUG printf("THD_DEBUG-GLOBAL: doing global object test\n"); #endif if (thd_objs->sfthd_garray && thd_objs->sfthd_garray[policy_id]) { g_thd_node = thd_objs->sfthd_garray[policy_id][gen_id]; } if( g_thd_node ) { status = sfthd_test_global( thd->ip_gnodes, g_thd_node, gen_id, sig_id, sip, dip, curtime ); if( status < 0 ) /* -1 == Don't log and stop looking */ { #ifdef THD_DEBUG printf("THD_DEBUG-GLOBAL: gen_id=%u sig_id=%u THD_NODE's, " "UnLoggable\n\n",gen_id, sig_id); fflush(stdout); #endif return (status < -1) ? 1 : -1; /* !0 == Don't log it*/ } /* Log it ! */ #ifdef THD_DEBUG printf("THD_DEBUG-GLOBAL: gen_id=%u sig_id=%u THD_NODE's, " "Loggable\n\n",gen_id, sig_id); fflush(stdout); #endif } else { #ifdef THD_DEBUG printf("THD_DEBUG-GLOBAL: no Global THD Object for gen_id=%u, " "sig_id=%u\n\n",gen_id, sig_id); fflush(stdout); #endif } return 0; /* Default: Log it if we did not block the logging action */ } #ifdef THD_DEBUG /*! * A function to print the thresholding objects to stdout. * */ int sfthd_show_objects(ThresholdObjects *thd_objs) { SFGHASH * sfthd_hash; THD_ITEM * sfthd_item; THD_NODE * sfthd_node; int gen_id; SFGHASH_NODE * item_hash_node; tSfPolicyId policyId; for(gen_id=0;gen_id < THD_MAX_GENID ; gen_id++ ) { sfthd_hash = thd_objs->sfthd_array[gen_id]; if (sfthd_hash == NULL) continue; printf("...GEN_ID = %u\n",gen_id); for(item_hash_node = sfghash_findfirst( sfthd_hash ); item_hash_node != 0; item_hash_node = sfghash_findnext( sfthd_hash ) ) { /* Check for any Permanent sig_id objects for this gen_id */ sfthd_item = (THD_ITEM*)item_hash_node->data; printf(".....GEN_ID = %u, SIG_ID = %u, Policy = %u\n",gen_id,sfthd_item->sig_id, sfthd_item->policyId); /* For each permanent thresholding object, test/add/update the thd object */ /* We maintain a list of thd objects for each gen_id+sig_id */ /* each object has it's own unique thd_id */ for( sfthd_node = (THD_NODE*)sflist_first(sfthd_item->sfthd_node_list); sfthd_node != 0; sfthd_node = (THD_NODE*)sflist_next(sfthd_item->sfthd_node_list) ) { printf(".........THD_ID =%d\n",sfthd_node->thd_id ); if( sfthd_node->type == THD_TYPE_SUPPRESS ) printf(".........type =Suppress\n"); if( sfthd_node->type == THD_TYPE_LIMIT ) printf(".........type =Limit\n"); if( sfthd_node->type == THD_TYPE_THRESHOLD ) printf(".........type =Threshold\n"); if( sfthd_node->type == THD_TYPE_BOTH ) printf(".........type =Both\n"); printf(".........tracking=%d\n",sfthd_node->tracking); printf(".........priority=%d\n",sfthd_node->priority); if( sfthd_node->type == THD_TYPE_SUPPRESS ) { printf(".........ip =%s\n", sfip_to_str(&sfthd_node->ip_address)); printf(".........mask =%d\n", sfip_bits(&sfthd_node->ip_address)); printf(".........not_flag=%d\n",sfthd_node->ip_mask); } else { printf(".........count =%d\n",sfthd_node->count); printf(".........seconds =%d\n",sfthd_node->seconds); } } } } return 0; } #endif // THD_DEBUG snort-2.9.20/src/sfutil/sfPolicy.h0000644000175000017500000001070714241077300015132 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _SF_POLICY_H_ #define _SF_POLICY_H_ #include "sf_ip.h" #include "ipv6_port.h" #include "sfrt.h" #include "snort_debug.h" /**Number of additional policies allocated with each re-alloc operation. */ #define POLICY_ALLOCATION_CHUNK 10 #define SF_VLAN_BINDING_MAX 4096 #define SF_POLICY_ID_BINDING_MAX 4096 #define SF_NETWORK_BINDING_MAX 4096 #define SF_POLICY_UNBOUND 0xffffffff #define SF_DEFAULT_POLICY_ID 0 /*vlan id or address range is reduced to policy id. and subsequent processing is done using policy id only. */ typedef struct { /**number of vlans which are member of this group. When membership falls to 0, then this group should be deleted. */ unsigned int refCount; char *filename; unsigned int isConfigProcessed:1; } tSfPolicy; typedef enum { SF_BINDING_TYPE_VLAN, SF_BINDING_TYPE_NETWORK, SF_BINDING_TYPE_POLICY_ID, SF_BINDING_TYPE_UNKNOWN } tSF_BINDING_TYPE; typedef unsigned int tSfPolicyId; typedef struct { /**group id assigned to each file name. The groupId is an abstract concept * to tie multiple vlans into one group. */ tSfPolicy **ppPolicies; tSfPolicyId defaultPolicyId; /**policy id of configuration file or packet being processed. */ tSfPolicyId numAllocatedPolicies; unsigned int numActivePolicies; /**vlan to policyId bindings. */ tSfPolicyId vlanBindings[SF_VLAN_BINDING_MAX]; /**policyId to policyId bindings. */ tSfPolicyId policyIdBindings[SF_POLICY_ID_BINDING_MAX]; /**Network to policyId bindings. */ table_t *netBindTable; } tSfPolicyConfig; tSfPolicyConfig * sfPolicyInit( void ); void sfPolicyFini( tSfPolicyConfig * ); int sfPolicyAdd( tSfPolicyConfig *, char * ); void sfPolicyDelete( tSfPolicyConfig *, tSfPolicyId ); char * sfPolicyGet( tSfPolicyConfig *, tSfPolicyId ); int sfVlanAddBinding( tSfPolicyConfig *, int, char * ); tSfPolicyId sfVlanGetBinding( tSfPolicyConfig *, int ); void sfVlanDeleteBinding( tSfPolicyConfig *, int ); int sfPolicyIdAddBinding( tSfPolicyConfig *, int, char * ); tSfPolicyId sfPolicyIdGetBinding( tSfPolicyConfig *, int ); void sfPolicyIdDeleteBinding( tSfPolicyConfig *, int ); unsigned int sfGetApplicablePolicyId( tSfPolicyConfig *, int, sfaddr_t*, sfaddr_t* ); int sfNetworkAddBinding( tSfPolicyConfig *, sfcidr_t *, char * ); unsigned int sfNetworkGetBinding( tSfPolicyConfig *, sfaddr_t* ); void sfNetworkDeleteBinding( tSfPolicyConfig *, sfaddr_t* ); static inline tSfPolicyId sfGetDefaultPolicy( tSfPolicyConfig *config ) { if (config == NULL) return 0; return config->defaultPolicyId; } static inline void sfSetDefaultPolicy( tSfPolicyConfig *config, tSfPolicyId policyId ) { if ((config == NULL) || (policyId >= config->numAllocatedPolicies)) return; config->defaultPolicyId = policyId; } static inline tSfPolicyId sfPolicyNumAllocated( tSfPolicyConfig *config ) { if (config == NULL) return 0; return config->numAllocatedPolicies; } /*dynamic array functions */ int sfDynArrayCheckBounds ( void ** dynArray, unsigned int index, unsigned int *maxElements ); typedef tSfPolicyId (*GetPolicyFunc)(void); struct _SnortConfig; typedef tSfPolicyId (*GetParserPolicyFunc)(struct _SnortConfig *); #endif snort-2.9.20/src/sfutil/acsmx2.c0000644000175000017500000025251114241077203014535 0ustar apoapo/* ** $Id$ ** ** Copyright(C) 2002,2003,2004 Marc Norton ** Copyright(C) 2003,2004 Daniel Roelker ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** acsmx2.c ** ** Multi-Pattern Search Engine ** ** Aho-Corasick State Machine - version 2.0 ** ** Supports both Non-Deterministic and Deterministic Finite Automata ** ** ** Reference - Efficient String matching: An Aid to Bibliographic Search ** Alfred V Aho and Margaret J Corasick ** Bell Labratories ** Copyright(C) 1975 Association for Computing Machinery,Inc ** ** +++ ** +++ Version 1.0 notes - Marc Norton: ** +++ ** ** Original implementation based on the 4 algorithms in the paper by ** Aho & Corasick, some implementation ideas from 'Practical Algorithms ** in C', and some of my own. ** ** 1) Finds all occurrences of all patterns within a text. ** ** +++ ** +++ Version 2.0 Notes - Marc Norton/Dan Roelker: ** +++ ** ** New implementation modifies the state table storage and access model to ** use compacted sparse vector storage. Dan Roelker and I hammered this ** strategy out amongst many others in order to reduce memory usage and ** improve caching performance. The memory usage is greatly reduced, we ** only use 1/4 of what we use to. The caching performance is better in ** pure benchmarking tests, but does not show overall improvement in Snort. ** Unfortunately, once a pattern match test has been performed Snort moves ** on to doing many other things before we get back to a patteren match test, ** so the cache is voided. ** ** This versions has better caching performance characteristics, reduced ** memory, more state table storage options, and requires no a priori case ** conversions. It does maintain the same public interface. (Snort only ** used banded storage). ** ** 1) Supports NFA and DFA state machines, and basic keyword state machines ** 2) Initial transition table uses Linked Lists ** 3) Improved state table memory options. NFA and DFA state transition ** tables are converted to one of 4 formats during compilation. ** a) Full matrix ** b) Sparse matrix ** c) Banded matrix (Default-this is the only one used in snort) ** d) Sparse-Banded matrix ** 4) Added support for acstate_t in .h file so we can compile states as ** 16, or 32 bit state values for another reduction in memory ** consumption, smaller states allows more of the state table to be ** cached, and improves performance on x86-P4. Your mileage may vary, ** especially on risc systems. ** 5) Added a bool to each state transition list to indicate if there is ** a matching pattern in the state. This prevents us from accessing ** another data array and can improve caching/performance. ** 6) The search functions are very sensitive, don't change them without ** extensive testing, or you'll just spoil the caching and prefetching ** opportunities. ** ** Extras for fellow pattern matchers: ** The table below explains the storage format used at each step. ** You can use an NFA or DFA to match with, the NFA is slower but tiny - ** set the structure directly. ** You can use any of the 4 storage modes above -full, sparse, banded, ** sparse-bands, set the structure directly. ** For applications where you have lots of data and a pattern set to ** search, this version was up to 3x faster than the previous verion, due ** to caching performance. This cannot be fully realized in Snort yet, ** but other applications may have better caching opportunities. ** Snort only needs to use the banded or full storage. ** ** Transition table format at each processing stage. ** ------------------------------------------------- ** Patterns -> Keyword State Table (List) ** Keyword State Table -> NFA (List) ** NFA -> DFA (List) ** DFA (List)-> Sparse Rows O(m-avg # transitions per state) ** -> Banded Rows O(1) ** -> Sparse-Banded Rows O(nb-# bands) ** -> Full Matrix O(1) ** ** Notes: ** ** 8/28/06 ** man - Sparse and SparseBands - fixed off by one in calculating matching index ** SparseBands changed ps increment to 2+n to increment between bands. ** ** 01/2008 ** man - added 2 phase pattern matcher using a pattern match queue. ** ** Matching states are queued, duplicate matches are dropped, ** and after the complete buffer scan the queued matches are ** processed. This improves cacheing performance, and reduces ** duplicate rule processing. The queue is limited in size and ** is flushed if it becomes full during the scan. This allows ** simple insertions. Tracking queue ops is optional, as this can ** impose a modest performance hit of a few percent. ** */ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #ifndef DYNAMIC_PREPROC_CONTEXT #define ACSMX2_TRACK_Q #ifdef ACSMX2_TRACK_Q # include "snort.h" #endif #endif //DYNAMIC_PREPROC_CONTEXT #include "acsmx2.h" #include "util.h" #include "snort_debug.h" #ifdef DYNAMIC_PREPROC_CONTEXT #include "sf_dynamic_preprocessor.h" #endif //DYNAMIC_PREPROC_CONTEXT #define printf LogMessage #define MEMASSERT(p,s) if(!p){FatalError("ACSM-No Memory: %s!\n",s);} static int acsm2_total_memory = 0; static int acsm2_pattern_memory = 0; static int acsm2_matchlist_memory = 0; static int acsm2_transtable_memory = 0; static int acsm2_dfa_memory = 0; static int acsm2_dfa1_memory = 0; static int acsm2_dfa2_memory = 0; static int acsm2_dfa4_memory = 0; static int acsm2_failstate_memory = 0; static int s_verbose=0; typedef struct acsm_summary_s { unsigned num_states; unsigned num_transitions; unsigned num_instances; unsigned num_patterns; unsigned num_characters; unsigned num_match_states; unsigned num_1byte_instances; unsigned num_2byte_instances; unsigned num_4byte_instances; ACSM_STRUCT2 acsm; } acsm_summary_t; static acsm_summary_t summary; void acsm_init_summary(void) { summary.num_states = 0; summary.num_transitions = 0; summary.num_instances = 0; summary.num_patterns = 0; summary.num_characters = 0; summary.num_match_states = 0; summary.num_1byte_instances = 0; summary.num_2byte_instances = 0; summary.num_4byte_instances = 0; memset(&summary.acsm, 0, sizeof(ACSM_STRUCT2)); acsm2_total_memory = 0; acsm2_pattern_memory = 0; acsm2_matchlist_memory = 0; acsm2_transtable_memory = 0; acsm2_dfa_memory = 0; acsm2_failstate_memory = 0; } /* ** Case Translation Table */ static unsigned char xlatcase[256]; /* * */ static void init_xlatcase() { int i; for (i = 0; i < 256; i++) { xlatcase[i] = (unsigned char)toupper(i); } } /* * Case Conversion */ static inline void ConvertCaseEx (unsigned char *d, unsigned char *s, int m) { int i; #ifdef XXXX int n; n = m & 3; m >>= 2; for (i = 0; i < m; i++ ) { d[0] = xlatcase[ s[0] ]; d[2] = xlatcase[ s[2] ]; d[1] = xlatcase[ s[1] ]; d[3] = xlatcase[ s[3] ]; d+=4; s+=4; } for (i=0; i < n; i++) { d[i] = xlatcase[ s[i] ]; } #else for (i=0; i < m; i++) { d[i] = xlatcase[ s[i] ]; } #endif } /* * */ void acsmSetVerbose2(void) { s_verbose = 1; } typedef enum _Acsm2MemoryType { ACSM2_MEMORY_TYPE__NONE = 0, ACSM2_MEMORY_TYPE__PATTERN, ACSM2_MEMORY_TYPE__MATCHLIST, ACSM2_MEMORY_TYPE__TRANSTABLE, ACSM2_MEMORY_TYPE__FAILSTATE } Acsm2MemoryType; /* * */ static void * AC_MALLOC( int n, Acsm2MemoryType type ) { void *p = calloc(1, n); if (p != NULL) { switch (type) { case ACSM2_MEMORY_TYPE__PATTERN: acsm2_pattern_memory += n; break; case ACSM2_MEMORY_TYPE__MATCHLIST: acsm2_matchlist_memory += n; break; case ACSM2_MEMORY_TYPE__TRANSTABLE: acsm2_transtable_memory += n; break; case ACSM2_MEMORY_TYPE__FAILSTATE: acsm2_failstate_memory += n; break; case ACSM2_MEMORY_TYPE__NONE: break; default: FatalError("%s(%d) Invalid memory type\n", __FILE__, __LINE__); break; } acsm2_total_memory += n; } return p; } static void * AC_MALLOC_DFA( int n, int sizeofstate ) { void *p = calloc(1, n); if (p != NULL) { switch (sizeofstate) { case 1: acsm2_dfa1_memory += n; break; case 2: acsm2_dfa2_memory += n; break; case 4: default: acsm2_dfa4_memory += n; break; } acsm2_dfa_memory += n; acsm2_total_memory += n; } return p; } /* * */ static void AC_FREE( void *p, int n, Acsm2MemoryType type ) { if (p != NULL) { switch (type) { case ACSM2_MEMORY_TYPE__PATTERN: acsm2_pattern_memory -= n; break; case ACSM2_MEMORY_TYPE__MATCHLIST: acsm2_matchlist_memory -= n; break; case ACSM2_MEMORY_TYPE__TRANSTABLE: acsm2_transtable_memory -= n; break; case ACSM2_MEMORY_TYPE__FAILSTATE: acsm2_failstate_memory -= n; break; case ACSM2_MEMORY_TYPE__NONE: default: break; } acsm2_total_memory -= n; free(p); } } static void AC_FREE_DFA( void *p, int n, int sizeofstate ) { if (p != NULL) { switch (sizeofstate) { case 1: acsm2_dfa1_memory -= n; break; case 2: acsm2_dfa2_memory -= n; break; case 4: default: acsm2_dfa4_memory -= n; break; } acsm2_dfa_memory -= n; acsm2_total_memory -= n; free(p); } } /* * Simple QUEUE NODE */ typedef struct _qnode { int state; struct _qnode *next; } QNODE; /* * Simple QUEUE Structure */ typedef struct _queue { QNODE * head, *tail; int count; } QUEUE; /* * Initialize the queue */ static void queue_init (QUEUE * s) { s->head = s->tail = 0; s->count= 0; } /* * Add Tail Item to queue (FiFo/LiLo) */ static void queue_add (QUEUE * s, int state) { QNODE * q; if (!s->head) { q = s->tail = s->head = (QNODE *)AC_MALLOC(sizeof(QNODE), ACSM2_MEMORY_TYPE__NONE); MEMASSERT (q, "queue_add"); q->state = state; q->next = 0; } else { q = (QNODE *)AC_MALLOC(sizeof(QNODE), ACSM2_MEMORY_TYPE__NONE); MEMASSERT (q, "queue_add"); q->state = state; q->next = 0; s->tail->next = q; s->tail = q; } s->count++; } /* * Remove Head Item from queue */ static int queue_remove (QUEUE * s) { int state = 0; QNODE * q; if (s->head) { q = s->head; state = q->state; s->head = s->head->next; s->count--; if( !s->head ) { s->tail = 0; s->count = 0; } AC_FREE(q, sizeof(QNODE), ACSM2_MEMORY_TYPE__NONE); } return state; } /* * Return items in the queue */ static int queue_count (QUEUE * s) { return s->count; } /* * Free the queue */ static void queue_free (QUEUE * s) { while (queue_count (s)) { queue_remove (s); } } /* * Get Next State-NFA */ static int List_GetNextState( ACSM_STRUCT2 * acsm, int state, int input ) { trans_node_t * t = acsm->acsmTransTable[state]; while( t ) { if( t->key == (acstate_t)input ) { return t->next_state; } t=t->next; } if( state == 0 ) return 0; return ACSM_FAIL_STATE2; /* Fail state ??? */ } /* * Get Next State-NFA, using direct index to speed up search */ static int List_GetNextStateOpt( ACSM_STRUCT2 * acsm, trans_node_t **acsmTransTableOpt, int state, int input ) { trans_node_t * t; int index = state * acsm->acsmAlphabetSize + input; t = acsmTransTableOpt[index]; if (t) return t->next_state; if( state == 0 ) return 0; return ACSM_FAIL_STATE2; /* Fail state ??? */ } /* * Get Next State-DFA */ static int List_GetNextState2( ACSM_STRUCT2 * acsm, int state, int input ) { trans_node_t * t = acsm->acsmTransTable[state]; while( t ) { if( t->key == (acstate_t)input ) { return t->next_state; } t = t->next; } return 0; /* default state */ } /* * Put Next State - Head insertion, and transition updates */ static int List_PutNextState( ACSM_STRUCT2 * acsm, int state, int input, int next_state ) { trans_node_t * p; trans_node_t * tnew; // printf(" List_PutNextState: state=%d, input='%c', next_state=%d\n",state,input,next_state); /* Check if the transition already exists, if so just update the next_state */ p = acsm->acsmTransTable[state]; while( p ) { /* transition already exists- reset the next state */ if( p->key == (acstate_t)input ) { p->next_state = next_state; return 0; } p=p->next; } /* Definitely not an existing transition - add it */ tnew = (trans_node_t*)AC_MALLOC(sizeof(trans_node_t), ACSM2_MEMORY_TYPE__TRANSTABLE); if( !tnew ) return -1; tnew->key = input; tnew->next_state = next_state; tnew->next = 0; tnew->next = acsm->acsmTransTable[state]; acsm->acsmTransTable[state] = tnew; acsm->acsmNumTrans++; return 0; } /* * Put Next State - Head insertion, and transition updates */ static int List_PutNextStateOpt( ACSM_STRUCT2 * acsm, trans_node_t **acsmTransTableOpt, int state, int input, int next_state ) { trans_node_t * tnew; trans_node_t *t; int index = state * acsm->acsmAlphabetSize + input; t = acsmTransTableOpt[index]; if (t) { t->next_state = next_state; return 0; } /* Definitely not an existing transition - add it */ tnew = (trans_node_t*)AC_MALLOC(sizeof(trans_node_t), ACSM2_MEMORY_TYPE__TRANSTABLE); if( !tnew ) return -1; tnew->key = input; tnew->next_state = next_state; tnew->next = 0; tnew->next = acsm->acsmTransTable[state]; acsm->acsmTransTable[state] = tnew; acsm->acsmNumTrans++; acsmTransTableOpt[index] = tnew; return 0; } /* * Free the entire transition table */ static int List_FreeTransTable( ACSM_STRUCT2 *acsm ) { int i; trans_node_t *t, *p; if (acsm->acsmTransTable == NULL) return 0; for (i = 0; i < acsm->acsmMaxStates; i++) { t = acsm->acsmTransTable[i]; while (t != NULL) { p = t->next; AC_FREE(t, sizeof(trans_node_t), ACSM2_MEMORY_TYPE__TRANSTABLE); t = p; } } AC_FREE(acsm->acsmTransTable, sizeof(void*) * acsm->acsmMaxStates, ACSM2_MEMORY_TYPE__TRANSTABLE); acsm->acsmTransTable = NULL; return 0; } /* * */ /* static int List_FreeList( trans_node_t * t ) { int tcnt=0; trans_node_t *p; while( t ) { p = t->next; free(t); t = p; acsm2_total_memory -= sizeof(trans_node_t); tcnt++; } return tcnt; } */ /* * Print the trans table to stdout */ static int List_PrintTransTable( ACSM_STRUCT2 * acsm ) { int i; trans_node_t * t; ACSM_PATTERN2 * patrn; if( !acsm->acsmTransTable ) return 0; printf("Print Transition Table- %d active states\n",acsm->acsmNumStates); for(i=0;i< acsm->acsmNumStates;i++) { t = acsm->acsmTransTable[i]; printf("state %3d: ",i); while( t ) { if( isascii((int)t->key) && isprint((int)t->key) ) printf("%3c->%-5d\t",t->key,t->next_state); else printf("%3d->%-5d\t",t->key,t->next_state); t = t->next; } patrn =acsm->acsmMatchList[i]; while( patrn ) { printf("%.*s ",patrn->n,patrn->patrn); patrn = patrn->next; } printf("\n"); } return 0; } /* * Converts row of states from list to a full vector format */ static inline int List_ConvToFull( ACSM_STRUCT2 *acsm, acstate_t state, acstate_t *full ) { int tcnt = 0; trans_node_t *t = acsm->acsmTransTable[state]; if (t == NULL) return 0; while (t != NULL) { switch (acsm->sizeofstate) { case 1: *((uint8_t *)full + t->key) = (uint8_t)t->next_state; break; case 2: *((uint16_t *)full + t->key) = (uint16_t)t->next_state; break; default: full[t->key] = t->next_state; break; } tcnt++; t = t->next; } return tcnt; } /* * Copy a Match List Entry - don't dup the pattern data */ static ACSM_PATTERN2* CopyMatchListEntry (ACSM_PATTERN2 * px) { ACSM_PATTERN2 * p; p = (ACSM_PATTERN2 *)AC_MALLOC(sizeof (ACSM_PATTERN2), ACSM2_MEMORY_TYPE__MATCHLIST); MEMASSERT (p, "CopyMatchListEntry"); memcpy (p, px, sizeof (ACSM_PATTERN2)); p->next = 0; return p; } /* * Check if a pattern is in the list already, * validate it using the 'id' field. This must be unique * for every pattern. */ /* static int FindMatchListEntry (ACSM_STRUCT2 * acsm, int state, ACSM_PATTERN2 * px) { ACSM_PATTERN2 * p; p = acsm->acsmMatchList[state]; while( p ) { if( p->id == px->id ) return 1; p = p->next; } return 0; } */ /* * Add a pattern to the list of patterns terminated at this state. * Insert at front of list. */ static void AddMatchListEntry (ACSM_STRUCT2 * acsm, int state, ACSM_PATTERN2 * px) { ACSM_PATTERN2 * p; p = (ACSM_PATTERN2 *)AC_MALLOC(sizeof (ACSM_PATTERN2), ACSM2_MEMORY_TYPE__MATCHLIST); MEMASSERT (p, "AddMatchListEntry"); memcpy (p, px, sizeof (ACSM_PATTERN2)); p->next = acsm->acsmMatchList[state]; acsm->acsmMatchList[state] = p; } static void AddPatternStates (ACSM_STRUCT2 * acsm, ACSM_PATTERN2 * p) { int state, next, n; unsigned char *pattern; n = p->n; pattern = p->patrn; state = 0; if(s_verbose)printf(" Begin AddPatternStates: acsmNumStates=%d\n",acsm->acsmNumStates); if(s_verbose)printf(" adding '%.*s', nocase=%d\n", n,p->patrn, p->nocase ); /* * Match up pattern with existing states */ for (; n > 0; pattern++, n--) { if(s_verbose)printf(" find char='%c'\n", *pattern ); next = List_GetNextState(acsm,state,*pattern); if ((acstate_t)next == ACSM_FAIL_STATE2 || next == 0) { break; } state = next; } /* * Add new states for the rest of the pattern bytes, 1 state per byte */ for (; n > 0; pattern++, n--) { if(s_verbose)printf(" add char='%c' state=%d NumStates=%d\n", *pattern, state, acsm->acsmNumStates ); acsm->acsmNumStates++; List_PutNextState(acsm,state,*pattern,acsm->acsmNumStates); state = acsm->acsmNumStates; } AddMatchListEntry (acsm, state, p ); if(s_verbose)printf(" End AddPatternStates: acsmNumStates=%d\n",acsm->acsmNumStates); } /* * Build A Non-Deterministic Finite Automata * The keyword state table must already be built, via AddPatternStates(). */ static void Build_NFA (ACSM_STRUCT2 * acsm) { int r, s, i; QUEUE q, *queue = &q; acstate_t * FailState = acsm->acsmFailState; ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; ACSM_PATTERN2 * mlist,* px; bool *queue_array; /* Init a Queue */ queue_init (queue); queue_array = (bool *) calloc( acsm->acsmNumStates, sizeof (bool)); /* Add the state 0 transitions 1st, the states at depth 1, fail to state 0 */ for (i = 0; i < acsm->acsmAlphabetSize; i++) { s = List_GetNextState2(acsm,0,i); if( s ) { if (!queue_array[s]) { queue_add(queue, s); queue_array[s] = true; } FailState[s] = 0; } } /* Build the fail state successive layer of transitions */ while (queue_count (queue) > 0) { r = queue_remove (queue); queue_array[r] = false; /* Find Final States for any Failure */ for (i = 0; i < acsm->acsmAlphabetSize; i++) { int fs, next; s = List_GetNextState(acsm,r,i); if( (acstate_t)s != ACSM_FAIL_STATE2 ) { if (!queue_array[s]) { queue_add(queue, s); queue_array[s] = true; } fs = FailState[r]; /* * Locate the next valid state for 'i' starting at fs */ while ((acstate_t)(next = List_GetNextState(acsm,fs,i)) == ACSM_FAIL_STATE2 ) { fs = FailState[fs]; } /* * Update 's' state failure state to point to the next valid state */ FailState[s] = next; /* * Copy 'next'states MatchList to 's' states MatchList, * we copy them so each list can be AC_FREE'd later, * else we could just manipulate pointers to fake the copy. */ for( mlist = MatchList[next]; mlist; mlist = mlist->next) { px = CopyMatchListEntry (mlist); /* Insert at front of MatchList */ px->next = MatchList[s]; MatchList[s] = px; } } } } /* Clean up the queue */ queue_free (queue); free(queue_array); if( s_verbose)printf("End Build_NFA: NumStates=%d\n",acsm->acsmNumStates); } /* * Build Deterministic Finite Automata from the NFA */ static void Convert_NFA_To_DFA (ACSM_STRUCT2 * acsm) { int i, r, s, cFailState; QUEUE q, *queue = &q; acstate_t * FailState = acsm->acsmFailState; trans_node_t **acsmTransTableOpt; bool *queue_array; /* Init a Queue */ queue_init (queue); queue_array = (bool*) calloc( acsm->acsmNumStates, sizeof (bool)); acsmTransTableOpt = (trans_node_t**) calloc(acsm->acsmAlphabetSize * acsm->acsmNumStates, sizeof(trans_node_t*)); for(i=0; iacsmNumStates; i++) { trans_node_t * t = acsm->acsmTransTable[i]; while( t ) { int index = i * acsm->acsmAlphabetSize + t->key; acsmTransTableOpt[index] = t; t=t->next; } } /* Add the state 0 transitions 1st */ for(i=0; iacsmAlphabetSize; i++) { s = List_GetNextStateOpt(acsm, acsmTransTableOpt, 0, i); if ( s != 0 ) { if (!queue_array[s]) { queue_add (queue, s); queue_array[s] = true; } } } /* Start building the next layer of transitions */ while( queue_count(queue) > 0 ) { r = queue_remove(queue); queue_array[r] = false; /* Process this states layer */ for (i = 0; i < acsm->acsmAlphabetSize; i++) { s = List_GetNextStateOpt(acsm, acsmTransTableOpt, r, i); if( (acstate_t)s != ACSM_FAIL_STATE2 && s!= 0) { if (!queue_array[s]) { queue_add(queue, s); queue_array[s] = true; } } else { cFailState = List_GetNextStateOpt(acsm, acsmTransTableOpt,FailState[r],i); if( cFailState != 0 && (acstate_t)cFailState != ACSM_FAIL_STATE2 ) { List_PutNextStateOpt(acsm,acsmTransTableOpt,r,i,cFailState); } } } } /* Clean up the queue */ queue_free (queue); free(acsmTransTableOpt); free(queue_array); if(s_verbose)printf("End Convert_NFA_To_DFA: NumStates=%d\n",acsm->acsmNumStates); } /* * * Convert a row lists for the state table to a full vector format * */ static int Conv_List_To_Full( ACSM_STRUCT2 *acsm ) { acstate_t k; acstate_t *p; acstate_t **NextState = acsm->acsmNextState; for (k = 0; k < (acstate_t)acsm->acsmNumStates; k++) { p = AC_MALLOC_DFA(acsm->sizeofstate * (acsm->acsmAlphabetSize + 2), acsm->sizeofstate); if (p == NULL) return -1; switch (acsm->sizeofstate) { case 1: List_ConvToFull(acsm, k, (acstate_t *)((uint8_t *)p + 2)); *((uint8_t *)p) = ACF_FULL; *((uint8_t *)p + 1) = 0; break; case 2: List_ConvToFull(acsm, k, (acstate_t *)((uint16_t *)p + 2)); *((uint16_t *)p) = ACF_FULL; *((uint16_t *)p + 1) = 0; break; default: List_ConvToFull(acsm, k, (p + 2)); p[0] = ACF_FULL; p[1] = 0; /* no matches yet */ break; } NextState[k] = p; /* now we have a full format row vector */ } return 0; } /* * Convert DFA memory usage from list based storage to a sparse-row storage. * * The Sparse format allows each row to be either full or sparse formatted. If the sparse row has * too many transitions, performance or space may dictate that we use the standard full formatting * for the row. More than 5 or 10 transitions per state ought to really whack performance. So the * user can specify the max state transitions per state allowed in the sparse format. * * Standard Full Matrix Format * --------------------------- * acstate_t ** NextState ( 1st index is row/state, 2nd index is column=event/input) * * example: * * events -> a b c d e f g h i j k l m n o p * states * N 1 7 0 0 0 3 0 0 0 0 0 0 0 0 0 0 * * Sparse Format, each row : Words Value * 1-1 fmt(0-full,1-sparse,2-banded,3-sparsebands) * 2-2 bool match flag (indicates this state has pattern matches) * 3-3 sparse state count ( # of input/next-state pairs ) * 4-3+2*cnt 'input,next-state' pairs... each sizof(acstate_t) * * above example case yields: * Full Format: 0, 1 7 0 0 0 3 0 0 0 0 0 0 0 0 0 0 ... * Sparse format: 1, 3, 'a',1,'b',7,'f',3 - uses 2+2*ntransitions (non-default transitions) */ static int Conv_Full_DFA_To_Sparse(ACSM_STRUCT2 * acsm) { int cnt, m, k, i; acstate_t * p, state, maxstates=0; acstate_t ** NextState = acsm->acsmNextState; acstate_t full[MAX_ALPHABET_SIZE]; for(k=0;kacsmNumStates;k++) { cnt=0; memset(full, 0, acsm->sizeofstate * acsm->acsmAlphabetSize); List_ConvToFull(acsm, (acstate_t)k, full ); for (i = 0; i < acsm->acsmAlphabetSize; i++) { state = full[i]; if( state != 0 && state != ACSM_FAIL_STATE2 ) cnt++; } if( cnt > 0 ) maxstates++; if( k== 0 || cnt > acsm->acsmSparseMaxRowNodes ) { p = AC_MALLOC_DFA(sizeof(acstate_t)*(acsm->acsmAlphabetSize+2), sizeof(acstate_t)); if(!p) return -1; p[0] = ACF_FULL; p[1] = 0; memcpy(&p[2],full,acsm->acsmAlphabetSize*sizeof(acstate_t)); } else { p = AC_MALLOC_DFA(sizeof(acstate_t)*(3+2*cnt), sizeof(acstate_t)); if(!p) return -1; m = 0; p[m++] = ACF_SPARSE; p[m++] = 0; /* no matches */ p[m++] = cnt; for(i = 0; i < acsm->acsmAlphabetSize ; i++) { state = full[i]; if( state != 0 && state != ACSM_FAIL_STATE2 ) { p[m++] = i; p[m++] = state; } } } NextState[k] = p; /* now we are a sparse formatted state transition array */ } return 0; } /* Convert Full matrix to Banded row format. Word values 1 2 -> banded 2 n number of values 3 i index of 1st value (0-256) 4 - 3+n next-state values at each index */ static int Conv_Full_DFA_To_Banded(ACSM_STRUCT2 * acsm) { int first = -1, last; acstate_t * p, state, full[MAX_ALPHABET_SIZE]; acstate_t ** NextState = acsm->acsmNextState; int cnt,m,k,i; for(k=0;kacsmNumStates;k++) { cnt=0; memset(full, 0, acsm->sizeofstate * acsm->acsmAlphabetSize); List_ConvToFull(acsm, (acstate_t)k, full ); first=-1; last =-2; for (i = 0; i < acsm->acsmAlphabetSize; i++) { state = full[i]; if( state !=0 && state != ACSM_FAIL_STATE2 ) { if( first < 0 ) first = i; last = i; } } /* calc band width */ cnt= last - first + 1; p = AC_MALLOC_DFA(sizeof(acstate_t)*(4+cnt), sizeof(acstate_t)); if(!p) return -1; m = 0; p[m++] = ACF_BANDED; p[m++] = 0; /* no matches */ p[m++] = cnt; p[m++] = first; for(i = first; i <= last; i++) { p[m++] = full[i]; } NextState[k] = p; /* now we are a banded formatted state transition array */ } return 0; } /* * Convert full matrix to Sparse Band row format. * * next - Full formatted row of next states * asize - size of alphabet * zcnt - max number of zeros in a run of zeros in any given band. * * Word Values * 1 ACF_SPARSEBANDS * 2 number of bands * repeat 3 - 5+ ....once for each band in this row. * 3 number of items in this band* 4 start index of this band * 5- next-state values in this band... */ static int calcSparseBands( acstate_t * next, int * begin, int * end, int asize, int zmax ) { int i, nbands,zcnt,last=0; acstate_t state; nbands=0; for( i=0; i zmax ) break; } else { zcnt=0; last = i; } } end[nbands++] = last; } } return nbands; } /* * Sparse Bands * * Row Format: * Word * 1 SPARSEBANDS format indicator * 2 bool indicates a pattern match in this state * 3 number of sparse bands * 4 number of elements in this band * 5 start index of this band * 6- list of next states * * m number of elements in this band * m+1 start index of this band * m+2- list of next states */ static int Conv_Full_DFA_To_SparseBands(ACSM_STRUCT2 * acsm) { acstate_t * p; acstate_t ** NextState = acsm->acsmNextState; int cnt,m,k,i,zcnt=acsm->acsmSparseMaxZcnt; int band_begin[MAX_ALPHABET_SIZE]; int band_end[MAX_ALPHABET_SIZE]; int nbands,j; acstate_t full[MAX_ALPHABET_SIZE]; for(k=0;kacsmNumStates;k++) { cnt=0; memset(full, 0, acsm->sizeofstate * acsm->acsmAlphabetSize); List_ConvToFull(acsm, (acstate_t)k, full ); nbands = calcSparseBands( full, band_begin, band_end, acsm->acsmAlphabetSize, zcnt ); /* calc band width space*/ cnt = 3; for(i=0;i= MAX_ALPHABET_SIZE) { AC_FREE_DFA(p, sizeof(acstate_t)*(cnt), sizeof(acstate_t)); return -1; } p[m++] = full[j]; /* some states may be state zero */ } } NextState[k] = p; /* now we are a sparse-banded formatted state transition array */ } return 0; } static void Print_DFA_MatchList( ACSM_STRUCT2 * acsm, int state ) { ACSM_PATTERN2 * mlist; for (mlist = acsm->acsmMatchList[state]; mlist; mlist = mlist->next) { printf("%.*s ", mlist->n, mlist->patrn); } } /* * */ static void Print_DFA(ACSM_STRUCT2 * acsm) { int k,i; acstate_t * p, state, n, fmt, index, nb; acstate_t ** NextState = acsm->acsmNextState; printf("Print DFA - %d active states\n",acsm->acsmNumStates); for(k=0;kacsmNumStates;k++) { p = NextState[k]; if( !p ) continue; fmt = *p++; printf("state %3d, fmt=%d: ",k,fmt); if( fmt ==ACF_SPARSE ) { n = *p++; for( ; n>0; n--, p+=2 ) { if( isascii((int)p[0]) && isprint((int)p[0]) ) printf("%3c->%-5d\t",p[0],p[1]); else printf("%3d->%-5d\t",p[0],p[1]); } } else if( fmt ==ACF_BANDED ) { n = *p++; index = *p++; for( ; n>0; n--, p++ ) { if( isascii((int)p[0]) && isprint((int)p[0]) ) printf("%3c->%-5d\t",index++,p[0]); else printf("%3d->%-5d\t",index++,p[0]); } } else if( fmt ==ACF_SPARSEBANDS ) { nb = *p++; for(i=0;(acstate_t)i0; n--, p++ ) { if( isascii((int)index) && isprint((int)index) ) printf("%3c->%-5d\t",index++,p[0]); else printf("%3d->%-5d\t",index++,p[0]); } } } else if( fmt == ACF_FULL ) { for( i=0; iacsmAlphabetSize; i++ ) { state = p[i]; if( state != 0 && state != ACSM_FAIL_STATE2 ) { if( isascii(i) && isprint(i) ) printf("%3c->%-5d\t",i,state); else printf("%3d->%-5d\t",i,state); } } } Print_DFA_MatchList( acsm, k); printf("\n"); } } /* * Write a state table to disk */ /* static void Write_DFA(ACSM_STRUCT2 * acsm, char * f) { int k,i; acstate_t * p, n, fmt, index, nb, bmatch; acstate_t ** NextState = acsm->acsmNextState; FILE * fp; printf("Dump DFA - %d active states\n",acsm->acsmNumStates); fp = fopen(f,"wb"); if(!fp) { printf("WARNING: could not write dfa to file - %s.\n",f); return; } fwrite( &acsm->acsmNumStates, 4, 1, fp); for(k=0;kacsmNumStates;k++) { p = NextState[k]; if( !p ) continue; fmt = *p++; bmatch = *p++; fwrite( &fmt, sizeof(acstate_t), 1, fp); fwrite( &bmatch, sizeof(acstate_t), 1, fp); if( fmt ==ACF_SPARSE ) { n = *p++; fwrite( &n, sizeof(acstate_t), 1, fp); fwrite( p, n*2*sizeof(acstate_t), 1, fp); } else if( fmt ==ACF_BANDED ) { n = *p++; fwrite( &n, sizeof(acstate_t), 1, fp); index = *p++; fwrite( &index, sizeof(acstate_t), 1, fp); fwrite( p, sizeof(acstate_t), n, fp); } else if( fmt ==ACF_SPARSEBANDS ) { nb = *p++; fwrite( &nb, sizeof(acstate_t), 1, fp); for(i=0;iacsmAlphabetSize, fp); } //Print_DFA_MatchList( acsm, k); } fclose(fp); } */ /* * * Convert an NFA or DFA row from sparse to full format * and store into the 'full' buffer. * * returns: * 0 - failed, no state transitions * *p - pointer to 'full' buffer * */ /* static acstate_t * acsmConvToFull(ACSM_STRUCT2 * acsm, acstate_t k, acstate_t * full ) { int i; acstate_t * p, n, fmt, index, nb, bmatch; acstate_t ** NextState = acsm->acsmNextState; p = NextState[k]; if( !p ) return 0; fmt = *p++; bmatch = *p++; if( fmt ==ACF_SPARSE ) { n = *p++; for( ; n>0; n--, p+=2 ) { full[ p[0] ] = p[1]; } } else if( fmt ==ACF_BANDED ) { n = *p++; index = *p++; for( ; n>0; n--, p++ ) { full[ index++ ] = p[0]; } } else if( fmt ==ACF_SPARSEBANDS ) { nb = *p++; for(i=0;i0; n--, p++ ) { full[ index++ ] = p[0]; } } } else if( fmt == ACF_FULL ) { memcpy(full,p,acsm->acsmAlphabetSize*sizeof(acstate_t)); } return full; } */ /* * Select the desired storage mode */ int acsmSelectFormat2( ACSM_STRUCT2 * acsm, int m ) { switch( m ) { case ACF_FULL: case ACF_SPARSE: case ACF_BANDED: case ACF_SPARSEBANDS: case ACF_FULLQ: acsm->acsmFormat = m; break; default: return -1; } return 0; } /* * */ void acsmSetMaxSparseBandZeros2( ACSM_STRUCT2 * acsm, int n ) { acsm->acsmSparseMaxZcnt = n; } /* * */ void acsmSetMaxSparseElements2( ACSM_STRUCT2 * acsm, int n ) { acsm->acsmSparseMaxRowNodes = n; } /* * */ int acsmSelectFSA2( ACSM_STRUCT2 * acsm, int m ) { switch( m ) { case FSA_TRIE: case FSA_NFA: case FSA_DFA: acsm->acsmFSA = m; default: return -1; } } /* * */ int acsmSetAlphabetSize2( ACSM_STRUCT2 * acsm, int n ) { if( n <= MAX_ALPHABET_SIZE ) { acsm->acsmAlphabetSize = n; } else { return -1; } return 0; } /* * Create a new AC state machine */ ACSM_STRUCT2 * acsmNew2 (void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)) { ACSM_STRUCT2 * p; init_xlatcase (); p = (ACSM_STRUCT2 *)AC_MALLOC(sizeof (ACSM_STRUCT2), ACSM2_MEMORY_TYPE__NONE); MEMASSERT (p, "acsmNew"); if (p) { memset (p, 0, sizeof (ACSM_STRUCT2)); /* Some defaults */ p->acsmFSA = FSA_DFA; p->acsmFormat = ACF_FULL;//ACF_BANDED; p->acsmAlphabetSize = 256; p->acsmSparseMaxRowNodes = 256; p->acsmSparseMaxZcnt = 10; p->userfree = userfree; p->optiontreefree = optiontreefree; p->neg_list_free = neg_list_free; } return p; } /* * Add a pattern to the list of patterns for this state machine * */ int acsmAddPattern2 (ACSM_STRUCT2 * p, unsigned char *pat, int n, int nocase, int offset, int depth, int negative, void * id, int iid) { ACSM_PATTERN2 * plist; plist = (ACSM_PATTERN2 *) AC_MALLOC(sizeof (ACSM_PATTERN2), ACSM2_MEMORY_TYPE__PATTERN); MEMASSERT (plist, "acsmAddPattern"); plist->patrn = (unsigned char *)AC_MALLOC(n, ACSM2_MEMORY_TYPE__PATTERN); MEMASSERT (plist->patrn, "acsmAddPattern"); ConvertCaseEx(plist->patrn, pat, n); plist->casepatrn = (unsigned char *)AC_MALLOC(n, ACSM2_MEMORY_TYPE__PATTERN); MEMASSERT (plist->casepatrn, "acsmAddPattern"); memcpy (plist->casepatrn, pat, n); plist->n = n; plist->nocase = nocase; plist->offset = offset; plist->depth = depth; plist->negative = negative; plist->iid = iid; plist->udata = id; plist->next = p->acsmPatterns; p->acsmPatterns = plist; p->numPatterns++; return 0; } /* * Add a Key to the list of key+data pairs */ int acsmAddKey2(ACSM_STRUCT2 * p, unsigned char *key, int klen, int nocase, void * data) { ACSM_PATTERN2 * plist; plist = (ACSM_PATTERN2 *) AC_MALLOC(sizeof(ACSM_PATTERN2), ACSM2_MEMORY_TYPE__PATTERN); MEMASSERT (plist, "acsmAddPattern"); plist->patrn = (unsigned char *)AC_MALLOC(klen, ACSM2_MEMORY_TYPE__PATTERN); MEMASSERT (plist->patrn, "acsmAddPattern"); memcpy (plist->patrn, key, klen); plist->casepatrn = (unsigned char *)AC_MALLOC(klen, ACSM2_MEMORY_TYPE__PATTERN); MEMASSERT (plist->casepatrn, "acsmAddPattern"); memcpy (plist->casepatrn, key, klen); plist->n = klen; plist->nocase = nocase; plist->offset = 0; plist->depth = 0; plist->iid = 0; plist->udata = 0; plist->next = p->acsmPatterns; p->acsmPatterns = plist; return 0; } /* * Copy a boolean match flag int NextState table, for caching purposes. */ static void acsmUpdateMatchStates( ACSM_STRUCT2 *acsm ) { acstate_t state; acstate_t **NextState = acsm->acsmNextState; ACSM_PATTERN2 **MatchList = acsm->acsmMatchList; for (state = 0; state < (acstate_t)acsm->acsmNumStates; state++) { acstate_t *p = NextState[state]; if (MatchList[state]) { switch (acsm->sizeofstate) { case 1: *((uint8_t *)p + 1) = 1; break; case 2: *((uint16_t *)p + 1) = 1; break; default: p[1] = 1; break; } summary.num_match_states++; } } } static int acsmBuildMatchStateTrees2( ACSM_STRUCT2 * acsm, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list) ) { int i, cnt = 0; ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; ACSM_PATTERN2 * mlist; /* Find the states that have a MatchList */ for (i = 0; i < acsm->acsmNumStates; i++) { for ( mlist=MatchList[i]; mlist!=NULL; mlist=mlist->next ) { if (mlist->udata) { if (mlist->negative) { neg_list_func(mlist->udata, &MatchList[i]->neg_list); } else { build_tree(mlist->udata, &MatchList[i]->rule_option_tree); } } cnt++; } if (MatchList[i]) { /* Last call to finalize the tree */ build_tree(NULL, &MatchList[i]->rule_option_tree); } } return cnt; } static int acsmBuildMatchStateTrees2WithSnortConf( struct _SnortConfig *sc, ACSM_STRUCT2 * acsm, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list) ) { int i, cnt = 0; ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; ACSM_PATTERN2 * mlist; /* Find the states that have a MatchList */ for (i = 0; i < acsm->acsmNumStates; i++) { for ( mlist=MatchList[i]; mlist!=NULL; mlist=mlist->next ) { if (mlist->udata) { if (mlist->negative) { neg_list_func(mlist->udata, &MatchList[i]->neg_list); } else { build_tree(sc, mlist->udata, &MatchList[i]->rule_option_tree); } } cnt++; } if (MatchList[i]) { /* Last call to finalize the tree */ build_tree(sc, NULL, &MatchList[i]->rule_option_tree); } } return cnt; } void acsmCompressStates( ACSM_STRUCT2 *acsm, int flag ) { if (acsm == NULL) return; acsm->compress_states = flag; } /* * Compile State Machine - NFA or DFA and Full or Banded or Sparse or SparseBands */ static inline int _acsmCompile2( ACSM_STRUCT2* acsm ) { ACSM_PATTERN2* plist; /* Count number of possible states */ for (plist = acsm->acsmPatterns; plist != NULL; plist = plist->next) acsm->acsmMaxStates += plist->n; acsm->acsmMaxStates++; /* one extra */ /* Alloc a List based State Transition table */ acsm->acsmTransTable = (trans_node_t**)AC_MALLOC(sizeof(trans_node_t*) * acsm->acsmMaxStates, ACSM2_MEMORY_TYPE__TRANSTABLE); MEMASSERT(acsm->acsmTransTable, "acsmCompile"); if (s_verbose) { printf("ACSMX-Max Memory-TransTable Setup: %d bytes, %d states, " "%d active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); } /* Alloc a MatchList table - this has a lis tof pattern matches for each state, if any */ acsm->acsmMatchList = (ACSM_PATTERN2 **)AC_MALLOC(sizeof(ACSM_PATTERN2*) * acsm->acsmMaxStates, ACSM2_MEMORY_TYPE__MATCHLIST); MEMASSERT(acsm->acsmMatchList, "acsmCompile"); if (s_verbose) { printf("ACSMX-Max Memory- MatchList Table Setup: %d bytes, %d states, " "%d active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); printf("ACSMX-Max Memory-Table Setup: %d bytes, %d states, %d active " "states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); } /* Initialize state zero as a branch */ acsm->acsmNumStates = 0; /* Add each Pattern to the State Table - This forms a keywords state table */ for (plist = acsm->acsmPatterns; plist != NULL; plist = plist->next) { summary.num_patterns++; summary.num_characters += plist->n; AddPatternStates(acsm, plist); } /* Add the 0'th state */ acsm->acsmNumStates++; if (acsm->compress_states) { if (acsm->acsmNumStates < UINT8_MAX) { acsm->sizeofstate = 1; summary.num_1byte_instances++; } else if (acsm->acsmNumStates < UINT16_MAX) { acsm->sizeofstate = 2; summary.num_2byte_instances++; } else { acsm->sizeofstate = 4; summary.num_4byte_instances++; } } else { acsm->sizeofstate = 4; } /* Alloc a failure table - this has a failure state, and a match list for each state */ acsm->acsmFailState = (acstate_t*)AC_MALLOC(sizeof(acstate_t) * acsm->acsmNumStates, ACSM2_MEMORY_TYPE__FAILSTATE); MEMASSERT(acsm->acsmFailState, "acsmCompile"); /* Alloc a separate state transition table == in state 's' due to event 'k', transition to 'next' state */ acsm->acsmNextState = (acstate_t**)AC_MALLOC_DFA(acsm->acsmNumStates * sizeof(acstate_t*), acsm->sizeofstate); MEMASSERT(acsm->acsmNextState, "acsmCompile-NextState"); if (s_verbose) { printf("ACSMX-Max Trie List Memory : %d bytes, %d states, %d " "active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); List_PrintTransTable(acsm); } if ((acsm->acsmFSA == FSA_DFA) || (acsm->acsmFSA == FSA_NFA)) { /* Build the NFA */ if (s_verbose) printf("Build_NFA\n"); Build_NFA(acsm); if (s_verbose) { printf("NFA-Trans-Nodes: %d\n",acsm->acsmNumTrans); printf("ACSMX-Max NFA List Memory : %d bytes, %d states / %d " "active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); List_PrintTransTable(acsm); } } if (acsm->acsmFSA == FSA_DFA) { /* Convert the NFA to a DFA */ if (s_verbose) printf("Convert_NFA_To_DFA\n"); Convert_NFA_To_DFA(acsm); if (s_verbose) { printf("DFA-Trans-Nodes: %d\n",acsm->acsmNumTrans); printf("ACSMX-Max NFA-DFA List Memory : %d bytes, %d states / %d " "active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); List_PrintTransTable( acsm ); } } /* Select Final Transition Table Storage Mode */ if (s_verbose) { printf("Converting Transition Lists -> Transition table, fmt=%d\n", acsm->acsmFormat); } if (acsm->acsmFormat == ACF_SPARSE) { /* Convert DFA Full matrix to a Sparse matrix */ if (Conv_Full_DFA_To_Sparse(acsm)) return -1; if (s_verbose) { printf ("ACSMX-Max Memory-Sparse: %d bytes, %d states, %d " "active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); Print_DFA(acsm); } } else if (acsm->acsmFormat == ACF_BANDED) { /* Convert DFA Full matrix to a Sparse matrix */ if (Conv_Full_DFA_To_Banded(acsm)) return -1; if (s_verbose) { printf("ACSMX-Max Memory-banded: %d bytes, %d states, %d " "active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); Print_DFA(acsm); } } else if (acsm->acsmFormat == ACF_SPARSEBANDS) { /* Convert DFA Full matrix to a Sparse matrix */ if (Conv_Full_DFA_To_SparseBands(acsm)) return -1; if (s_verbose) { printf("ACSMX-Max Memory-sparse-bands: %d bytes, %d states, %d " "active states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); Print_DFA(acsm); } } else if ((acsm->acsmFormat == ACF_FULL) || (acsm->acsmFormat == ACF_FULLQ)) { if (Conv_List_To_Full(acsm)) return -1; if (s_verbose) { printf("ACSMX-Max Memory-Full: %d bytes, %d states, %d active " "states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); Print_DFA(acsm); } /* Don't need the FailState table anymore */ AC_FREE(acsm->acsmFailState, sizeof(acstate_t) * acsm->acsmNumStates, ACSM2_MEMORY_TYPE__FAILSTATE); acsm->acsmFailState = NULL; } /* load boolean match flags into state table */ acsmUpdateMatchStates(acsm); /* Free up the Table Of Transition Lists */ List_FreeTransTable(acsm); if (s_verbose) { printf("ACSMX-Max Memory-Final: %d bytes, %d states, %d active " "states\n", acsm2_total_memory, acsm->acsmMaxStates, acsm->acsmNumStates); } if (s_verbose) acsmPrintInfo2(acsm); /* Accrue Summary State Stats */ summary.num_states += acsm->acsmNumStates; summary.num_transitions += acsm->acsmNumTrans; summary.num_instances++; memcpy(&summary.acsm, acsm, sizeof(ACSM_STRUCT2)); return 0; } int acsmCompile2( ACSM_STRUCT2* acsm, int (*build_tree)(void* id, void** existing_tree), int (*neg_list_func)(void* id, void** list) ) { int rval; if ((rval = _acsmCompile2(acsm))) return rval; if (build_tree && neg_list_func) { acsmBuildMatchStateTrees2(acsm, build_tree, neg_list_func); } return 0; } int acsmCompile2WithSnortConf( struct _SnortConfig *sc, ACSM_STRUCT2* acsm, int (*build_tree)(struct _SnortConfig *, void* id, void** existing_tree), int (*neg_list_func)(void* id, void** list) ) { int rval; if ((rval = _acsmCompile2(acsm))) return rval; if (build_tree && neg_list_func) { acsmBuildMatchStateTrees2WithSnortConf(sc, acsm, build_tree, neg_list_func); } return 0; } /* * Get the NextState from the NFA, all NFA storage formats use this */ static inline acstate_t SparseGetNextStateNFA(acstate_t * ps, acstate_t state, unsigned input) { acstate_t fmt; acstate_t n; unsigned int index; int nb; fmt = *ps++; ps++; /* skip bMatchState */ switch( fmt ) { case ACF_BANDED: { n = ps[0]; index = ps[1]; if( input < index ) { if(state==0) { return 0; } else { return (acstate_t)ACSM_FAIL_STATE2; } } if( input >= index + n ) { if(state==0) { return 0; } else { return (acstate_t)ACSM_FAIL_STATE2; } } if( ps[input-index] == 0 ) { if( state != 0 ) { return ACSM_FAIL_STATE2; } } return (acstate_t) ps[input-index]; } case ACF_SPARSE: { n = *ps++; /* number of sparse index-value entries */ for( ; n>0 ; n-- ) { if( ps[0] > input ) /* cannot match the input, already a higher value than the input */ { return (acstate_t)ACSM_FAIL_STATE2; /* default state */ } else if( ps[0] == input ) { return ps[1]; /* next state */ } ps+=2; } if( state == 0 ) { return 0; } return ACSM_FAIL_STATE2; } case ACF_SPARSEBANDS: { nb = *ps++; /* number of bands */ while( nb > 0 ) /* for each band */ { n = *ps++; /* number of elements */ index = *ps++; /* 1st element value */ if( input < index ) { if( state != 0 ) { return (acstate_t)ACSM_FAIL_STATE2; } return (acstate_t)0; } if( (input >= index) && (input < (index + n)) ) { if( ps[input-index] == 0 ) { if( state != 0 ) { return ACSM_FAIL_STATE2; } } return (acstate_t) ps[input-index]; } nb--; ps += n; } if( state != 0 ) { return (acstate_t)ACSM_FAIL_STATE2; } return (acstate_t)0; } case ACF_FULL: case ACF_FULLQ: { if( ps[input] == 0 ) { if( state != 0 ) { return ACSM_FAIL_STATE2; } } return ps[input]; } } return 0; } /* * Get the NextState from the DFA Next State Transition table * Full and banded are supported separately, this is for * sparse and sparse-bands */ static inline acstate_t SparseGetNextStateDFA(acstate_t * ps, acstate_t state, unsigned input) { acstate_t n, nb; unsigned int index; switch( ps[0] ) { /* BANDED */ case ACF_BANDED: { /* n=ps[2] : number of entries in the band */ /* index=ps[3] : index of the 1st entry, sequential thereafter */ if( input < ps[3] ) return 0; if( input >= (unsigned)(ps[3]+ps[2]) ) return 0; return ps[4+input-ps[3]]; } /* FULL */ case ACF_FULL: { return ps[2+input]; } /* SPARSE */ case ACF_SPARSE: { n = ps[2]; /* number of entries/ key+next pairs */ ps += 3; for( ; n>0 ; n-- ) { if( input < ps[0] ) /* cannot match the input, already a higher value than the input */ { return (acstate_t)0; /* default state */ } else if( ps[0] == input ) { return ps[1]; /* next state */ } ps += 2; } return (acstate_t)0; } /* SPARSEBANDS */ case ACF_SPARSEBANDS: { nb = ps[2]; /* number of bands */ ps += 3; while( nb > 0 ) /* for each band */ { n = ps[0]; /* number of elements in this band */ index = ps[1]; /* start index/char of this band */ if( input < index ) { return (acstate_t)0; } if( (input < (index + n)) ) { return (acstate_t) ps[2+input-index]; } nb--; ps += 2 + n; } return (acstate_t)0; } } return 0; } /* * Search Text or Binary Data for Pattern matches * * Sparse & Sparse-Banded Matrix search */ static inline int acsmSearchSparseDFA(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int* current_state ) { acstate_t state; ACSM_PATTERN2 * mlist; unsigned char * Tend; int nfound = 0; unsigned char * T, * Tc; int index; acstate_t ** NextState = acsm->acsmNextState; ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; Tc = Tx; T = Tx; Tend = T + n; if ( !current_state ) { return 0; } state = *current_state; for( ; T < Tend; T++ ) { state = SparseGetNextStateDFA ( NextState[state], state, xlatcase[*T] ); /* test if this state has any matching patterns */ if( NextState[state][1] ) { mlist = MatchList[state]; if (mlist) { index = T - mlist->n - Tc + 1; nfound++; if (Match (mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) { *current_state = state; return nfound; } } } } *current_state = state; return nfound; } void acsmx2_print_qinfo(void) { #ifdef ACSMX2_TRACK_Q if( snort_conf->max_inq ) { LogMessage("mpse: queue size = %d, max possible = %d\n", snort_conf->max_inq, AC_MAX_INQ); LogMessage("mpse: queue flushes = " STDu64 "\n", snort_conf->tot_inq_flush ); LogMessage("mpse: queue inserts = " STDu64 "\n", snort_conf->tot_inq_inserts ); LogMessage("mpse: queue uinserts = " STDu64 "\n", snort_conf->tot_inq_uinserts ); } #endif } static inline void _init_queue( PMQ * b) { b->inq=0; b->inq_flush=0; } /* uniquely insert into q, should splay elements for performance */ static inline int _add_queue(PMQ * b, void * p ) { int i; #ifdef ACSMX2_TRACK_Q snort_conf->tot_inq_inserts++; #endif for(i=(int)(b->inq)-1;i>=0;i--) if( p == b->q[i] ) return 0; #ifdef ACSMX2_TRACK_Q snort_conf->tot_inq_uinserts++; #endif if( b->inq < AC_MAX_INQ ) { b->q[ b->inq++ ] = p; } if( b->inq == AC_MAX_INQ ) { #ifdef ACSMX2_TRACK_Q b->inq_flush++; #endif return 1; } return 0; } static inline unsigned _process_queue( PMQ * q, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data ) { ACSM_PATTERN2 * mlist; unsigned int i; #ifdef ACSMX2_TRACK_Q if( q->inq > snort_conf->max_inq ) snort_conf->max_inq = q->inq; snort_conf->tot_inq_flush += q->inq_flush; #endif for( i=0; iinq; i++ ) { mlist = q->q[i]; if (mlist) { if (Match (mlist->udata, mlist->rule_option_tree, 0, data, mlist->neg_list) > 0) { q->inq = 0; return 1; } } } q->inq=0; return 0; } /* * Matching states are queued, duplicate matches are dropped, * and after the complete buffer scan, the queued matches are * processed. This improves cacheing performance, and reduces * duplicate rule processing. The queue is limited in size and * is flushed if it becomes full during the scan. This allows * simple insertions. Tracking queue ops is optional, as this can * impose a modest performance hit of a few percent. */ #define AC_SEARCH_Q \ for (; T < Tend; T++) \ { \ ps = NextState[state]; \ sindex = xlatcase[T[0]]; \ if (ps[1]) \ { \ if (MatchList[state]) \ { \ if (_add_queue(&acsm->q,MatchList[state])) \ { \ if (_process_queue(&acsm->q, Match,data)) \ { \ *current_state = state; \ return 1; \ } \ } \ } \ } \ state = ps[2 + sindex]; \ } static inline int acsmSearchSparseDFA_Full_q( ACSM_STRUCT2 *acsm, unsigned char *T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int *current_state ) { unsigned char *Tend; int sindex; acstate_t state; ACSM_PATTERN2 **MatchList = acsm->acsmMatchList; Tend = T + n; if (current_state == NULL) return 0; _init_queue(&acsm->q); state = *current_state; switch (acsm->sizeofstate) { case 1: { uint8_t *ps; uint8_t **NextState = (uint8_t **)acsm->acsmNextState; AC_SEARCH_Q; } break; case 2: { uint16_t *ps; uint16_t **NextState = (uint16_t **)acsm->acsmNextState; AC_SEARCH_Q; } break; default: { acstate_t *ps; acstate_t **NextState = acsm->acsmNextState; AC_SEARCH_Q; } break; } *current_state = state; if (MatchList[state]) _add_queue(&acsm->q,MatchList[state]); _process_queue(&acsm->q,Match,data); return 0; } /* * Matching states are queued, duplicate matches are dropped, * and after the complete buffer scan, the queued matches are * processed. This improves cacheing performance, and reduces * duplicate rule processing. The queue is limited in size and * is flushed if it becomes full during the scan. This allows * simple insertions. Tracking queue ops is optional, as this can * impose a modest performance hit of a few percent. */ #define AC_SEARCH_Q_ALL \ for (; T < Tend; T++) \ { \ ps = NextState[state]; \ sindex = xlatcase[T[0]]; \ if (ps[1]) \ { \ for( mlist = MatchList[state]; \ mlist!= NULL; \ mlist = mlist->next ) \ { \ if( mlist->nocase || (memcmp (mlist->casepatrn, T - mlist->n, mlist->n ) == 0)) \ { \ if (_add_queue(&acsm->q,mlist)) \ { \ if (_process_queue(&acsm->q, Match,data)) \ { \ *current_state = state; \ return 1; \ } \ } \ } \ } \ } \ state = ps[2 + sindex]; \ } static inline int acsmSearchSparseDFA_Full_q_all( ACSM_STRUCT2 *acsm, const unsigned char *T, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int *current_state ) { const unsigned char *Tend; int sindex; acstate_t state; ACSM_PATTERN2 **MatchList = acsm->acsmMatchList; ACSM_PATTERN2 *mlist; Tend = T + n; if (current_state == NULL) return 0; _init_queue(&acsm->q); state = *current_state; switch (acsm->sizeofstate) { case 1: { uint8_t *ps; uint8_t **NextState = (uint8_t **)acsm->acsmNextState; AC_SEARCH_Q_ALL; } break; case 2: { uint16_t *ps; uint16_t **NextState = (uint16_t **)acsm->acsmNextState; AC_SEARCH_Q_ALL; } break; default: { acstate_t *ps; acstate_t **NextState = acsm->acsmNextState; AC_SEARCH_Q_ALL; } break; } *current_state = state; for( mlist = MatchList[state]; mlist!= NULL; mlist = mlist->next ) { if( mlist->nocase || (memcmp (mlist->casepatrn, T - mlist->n, mlist->n ) == 0)) { if (_add_queue(&acsm->q,mlist)) { if (_process_queue(&acsm->q, Match,data)) { *current_state = state; return 1; } } } } _process_queue(&acsm->q,Match,data); return 0; } /* * Full format DFA search * Do not change anything here without testing, caching and prefetching * performance is very sensitive to any changes. * * Perf-Notes: * 1) replaced ConvertCaseEx with inline xlatcase - this improves performance 5-10% * 2) using 'nocase' improves performance again by 10-15%, since memcmp is not needed * 3) */ #define AC_SEARCH \ for( ; T < Tend; T++ ) \ { \ ps = NextState[ state ]; \ sindex = xlatcase[T[0]]; \ if (ps[1]) \ { \ mlist = MatchList[state]; \ if (mlist) \ { \ index = T - mlist->n - Tx; \ nfound++; \ if (Match (mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) \ { \ *current_state = state; \ return nfound; \ } \ } \ } \ state = ps[2u + sindex]; \ } static inline int acsmSearchSparseDFA_Full( ACSM_STRUCT2 *acsm, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int *current_state ) { ACSM_PATTERN2 *mlist; unsigned char *Tend; unsigned char *T; int index; int sindex; int nfound = 0; acstate_t state; ACSM_PATTERN2 **MatchList = acsm->acsmMatchList; T = Tx; Tend = Tx + n; if (current_state == NULL) return 0; state = *current_state; switch (acsm->sizeofstate) { case 1: { uint8_t *ps; uint8_t **NextState = (uint8_t **)acsm->acsmNextState; AC_SEARCH; } break; case 2: { uint16_t *ps; uint16_t **NextState = (uint16_t **)acsm->acsmNextState; AC_SEARCH; } break; default: { acstate_t *ps; acstate_t **NextState = acsm->acsmNextState; AC_SEARCH; } break; } /* Check the last state for a pattern match */ mlist = MatchList[state]; if (mlist) { index = T - mlist->n - Tx; nfound++; if (Match(mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) { *current_state = state; return nfound; } } *current_state = state; return nfound; } /* * Full format DFA search * Do not change anything here without testing, caching and prefetching * performance is very sensitive to any changes. * * Perf-Notes: * 1) replaced ConvertCaseEx with inline xlatcase - this improves performance 5-10% * 2) using 'nocase' improves performance again by 10-15%, since memcmp is not needed * 3) */ #define AC_SEARCH_ALL \ for( ; T < Tend; T++ ) \ { \ ps = NextState[ state ]; \ sindex = xlatcase[T[0]]; \ if (ps[1]) \ { \ for( mlist = MatchList[state]; \ mlist!= NULL; \ mlist = mlist->next ) \ { \ index = T - mlist->n - Tx; \ if( mlist->nocase || (memcmp (mlist->casepatrn, Tx + index, mlist->n ) == 0)) \ { \ nfound++; \ if (Match (mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) \ { \ *current_state = state; \ return nfound; \ } \ } \ } \ } \ state = ps[2u + sindex]; \ } static inline int acsmSearchSparseDFA_Full_All( ACSM_STRUCT2 *acsm, const unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int *current_state ) { ACSM_PATTERN2 *mlist; const unsigned char * Tend; const unsigned char * T; int index; int sindex; int nfound = 0; acstate_t state; ACSM_PATTERN2 **MatchList = acsm->acsmMatchList; T = Tx; Tend = Tx + n; if (current_state == NULL) return 0; state = *current_state; switch (acsm->sizeofstate) { case 1: { uint8_t *ps; uint8_t **NextState = (uint8_t **)acsm->acsmNextState; AC_SEARCH_ALL; } break; case 2: { uint16_t *ps; uint16_t **NextState = (uint16_t **)acsm->acsmNextState; AC_SEARCH_ALL; } break; default: { acstate_t *ps; acstate_t **NextState = acsm->acsmNextState; AC_SEARCH_ALL; } break; } /* Check the last state for a pattern match */ for( mlist = MatchList[state]; mlist!= NULL; mlist = mlist->next ) { index = T - mlist->n - Tx; if( mlist->nocase || (memcmp (mlist->casepatrn, Tx + index, mlist->n) == 0)) { nfound++; if (Match(mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) { *current_state = state; return nfound; } } } *current_state = state; return nfound; } /* * Banded-Row format DFA search * Do not change anything here, caching and prefetching * performance is very sensitive to any changes. * * ps[0] = storage fmt * ps[1] = bool match flag * ps[2] = # elements in band * ps[3] = index of 1st element */ static inline int acsmSearchSparseDFA_Banded(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int* current_state ) { acstate_t state; unsigned char * Tend; unsigned char * T; int sindex; int index; acstate_t ** NextState = acsm->acsmNextState; ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; ACSM_PATTERN2 * mlist; acstate_t * ps; int nfound = 0; T = Tx; Tend = T + n; if ( !current_state ) { return 0; } state = *current_state; for( ; T < Tend; T++ ) { ps = NextState[state]; sindex = xlatcase[ T[0] ]; /* test if this state has any matching patterns */ if( ps[1] ) { mlist = MatchList[state]; if (mlist) { index = T - mlist->n - Tx; nfound++; if (Match (mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) { *current_state = state; return nfound; } } } if( (acstate_t)sindex < ps[3] ) state = 0; else if( (acstate_t)sindex >= (ps[3] + ps[2]) ) state = 0; else state = ps[ 4u + sindex - ps[3] ]; } /* Check the last state for a pattern match */ mlist = MatchList[state]; if (mlist) { index = T - mlist->n - Tx; nfound++; if (Match (mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) { *current_state = state; return nfound; } } return nfound; } /* * Search Text or Binary Data for Pattern matches * * Sparse Storage Version */ static inline int acsmSearchSparseNFA(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int* current_state ) { acstate_t state; ACSM_PATTERN2 * mlist; unsigned char * Tend; int nfound = 0; unsigned char * T; int index; acstate_t ** NextState= acsm->acsmNextState; acstate_t * FailState= acsm->acsmFailState; ACSM_PATTERN2 ** MatchList = acsm->acsmMatchList; unsigned char Tchar; T = Tx; Tend = T + n; if ( !current_state ) { return 0; } state = *current_state; for( ; T < Tend; T++ ) { acstate_t nstate; Tchar = xlatcase[ *T ]; while( (nstate=SparseGetNextStateNFA(NextState[state],state,Tchar))==ACSM_FAIL_STATE2 ) state = FailState[state]; state = nstate; mlist = MatchList[state]; if (mlist) { index = T - mlist->n - Tx; nfound++; if (Match (mlist->udata, mlist->rule_option_tree, index, data, mlist->neg_list) > 0) { *current_state = state; return nfound; } } } return nfound; } /* * Search Function */ int acsmSearch2(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int* current_state ) { switch( acsm->acsmFSA ) { case FSA_DFA: if( acsm->acsmFormat == ACF_FULL ) { return acsmSearchSparseDFA_Full( acsm, Tx, n, Match, data, current_state ); } else if( acsm->acsmFormat == ACF_FULLQ ) { return acsmSearchSparseDFA_Full_q( acsm, Tx, n, Match, data, current_state ); } else if( acsm->acsmFormat == ACF_BANDED ) { return acsmSearchSparseDFA_Banded( acsm, Tx, n, Match, data, current_state ); } else { return acsmSearchSparseDFA( acsm, Tx, n, Match, data, current_state ); } case FSA_NFA: return acsmSearchSparseNFA( acsm, Tx, n, Match, data, current_state ); case FSA_TRIE: return 0; } return 0; } /* * Search Function */ int acsmSearchAll2(ACSM_STRUCT2 * acsm, unsigned char *Tx, int n, int (*Match)(void * id, void *tree, int index, void *data, void *neg_list), void *data, int* current_state ) { switch( acsm->acsmFSA ) { case FSA_DFA: if( acsm->acsmFormat == ACF_FULL ) { return acsmSearchSparseDFA_Full_All( acsm, Tx, n, Match, data, current_state ); } else if( acsm->acsmFormat == ACF_FULLQ ) { return acsmSearchSparseDFA_Full_q_all( acsm, Tx, n, Match, data, current_state ); } else if( acsm->acsmFormat == ACF_BANDED ) { return acsmSearchSparseDFA_Banded( acsm, Tx, n, Match, data, current_state ); } else { return acsmSearchSparseDFA( acsm, Tx, n, Match, data, current_state ); } case FSA_NFA: return acsmSearchSparseNFA( acsm, Tx, n, Match, data, current_state ); case FSA_TRIE: return 0; } return 0; } /* * Free all memory */ void acsmFree2( ACSM_STRUCT2 *acsm ) { int i; ACSM_PATTERN2 * mlist, *ilist, *plist; /* For AC_FREE don't really care at this point about stats */ for (i = 0; i < acsm->acsmNumStates; i++) { mlist = acsm->acsmMatchList[i]; while (mlist) { ilist = mlist; mlist = mlist->next; if (ilist->rule_option_tree && acsm->optiontreefree) acsm->optiontreefree(&(ilist->rule_option_tree)); if (ilist->neg_list && acsm->neg_list_free) acsm->neg_list_free(&(ilist->neg_list)); AC_FREE(ilist, 0, ACSM2_MEMORY_TYPE__NONE); } AC_FREE_DFA(acsm->acsmNextState[i], 0, 0); } for (plist = acsm->acsmPatterns; plist; ) { ACSM_PATTERN2 *tmpPlist = plist->next; if (acsm->userfree && (plist->udata != NULL)) acsm->userfree(plist->udata); AC_FREE(plist->patrn, 0, ACSM2_MEMORY_TYPE__NONE); AC_FREE(plist->casepatrn, 0, ACSM2_MEMORY_TYPE__NONE); AC_FREE(plist, 0, ACSM2_MEMORY_TYPE__NONE); plist = tmpPlist; } AC_FREE_DFA(acsm->acsmNextState, 0, 0); AC_FREE(acsm->acsmFailState, 0, ACSM2_MEMORY_TYPE__NONE); AC_FREE(acsm->acsmMatchList, 0, ACSM2_MEMORY_TYPE__NONE); AC_FREE(acsm, 0, ACSM2_MEMORY_TYPE__NONE); } int acsmPatternCount2 ( ACSM_STRUCT2 * acsm ) { return acsm->numPatterns; } /* * */ void acsmPrintInfo2( ACSM_STRUCT2 * p) { char * sf[]={ "Full Matrix", "Sparse Matrix", "Banded Matrix", "Sparse Banded Matrix", "Full-Q Matrix" }; char * fsa[]={ "TRIE", "NFA", "DFA" }; printf("+--[Pattern Matcher:Aho-Corasick]-----------------------------\n"); printf("| Alphabet Size : %d Chars\n",p->acsmAlphabetSize); if (p->compress_states) printf("| Sizeof State : %d\n", p->sizeofstate); else printf("| Sizeof State : %d bytes\n",(int)(sizeof(acstate_t))); printf("| Storage Format : %s \n",sf[ p->acsmFormat ]); printf("| Sparse Row Nodes : %d Max\n",p->acsmSparseMaxRowNodes); printf("| Sparse Band Zeros: %d Max\n",p->acsmSparseMaxZcnt); printf("| Num States : %d\n",p->acsmNumStates); printf("| Num Transitions : %d\n",p->acsmNumTrans); printf("| State Density : %.1f%%\n",100.0*(double)p->acsmNumTrans/(p->acsmNumStates*p->acsmAlphabetSize)); printf("| Finite Automaton : %s\n", fsa[p->acsmFSA]); if( acsm2_total_memory < 1024*1024 ) printf("| Memory : %.2fKbytes\n", (float)acsm2_total_memory/1024 ); else printf("| Memory : %.2fMbytes\n", (float)acsm2_total_memory/(1024*1024) ); printf("+-------------------------------------------------------------\n"); /* Print_DFA(acsm); */ } /* * */ int acsmPrintDetailInfo2( ACSM_STRUCT2 * p ) { return 0; } /* * Global sumary of all info and all state machines built during this run * This feeds off of the last pattern groupd built within snort, * all groups use the same format, state size, etc.. * Combined with accrued stats, we get an average picture of things. */ int acsmPrintSummaryInfo2(void) { char * sf[]={ "Full", "Sparse", "Banded", "Sparse-Bands", "Full-Q" }; char * fsa[]={ "TRIE", "NFA", "DFA" }; ACSM_STRUCT2 * p = &summary.acsm; if( !summary.num_states ) return 0; LogMessage("+- [ Aho-Corasick Summary ] -------------------------------------\n"); LogMessage("| Storage Format : %s \n",sf[ p->acsmFormat ]); LogMessage("| Finite Automaton : %s\n", fsa[p->acsmFSA]); LogMessage("| Alphabet Size : %d Chars\n",p->acsmAlphabetSize); if (summary.acsm.compress_states) LogMessage("| Sizeof State : Variable (1,2,4 bytes)\n"); else LogMessage("| Sizeof State : %d bytes\n",(int)(sizeof(acstate_t))); LogMessage("| Instances : %u\n",summary.num_instances); if (summary.acsm.compress_states) { LogMessage("| 1 byte states : %u\n", summary.num_1byte_instances); LogMessage("| 2 byte states : %u\n", summary.num_2byte_instances); LogMessage("| 4 byte states : %u\n", summary.num_4byte_instances); } LogMessage("| Characters : %u\n",summary.num_characters); LogMessage("| States : %d\n",summary.num_states); LogMessage("| Transitions : %d\n",summary.num_transitions); LogMessage("| State Density : %.1f%%\n", 100.0*(double)summary.num_transitions/(summary.num_states*p->acsmAlphabetSize)); LogMessage("| Patterns : %u\n",summary.num_patterns); LogMessage("| Match States : %d\n",summary.num_match_states); if( acsm2_total_memory < 1024*1024 ) { LogMessage("| Memory (KB) : %.2f\n", (float)acsm2_total_memory/1024 ); if (acsm2_pattern_memory > 0) LogMessage("| Pattern : %.2f\n", (float)acsm2_pattern_memory/1024 ); if (acsm2_matchlist_memory > 0) LogMessage("| Match Lists : %.2f\n", (float)acsm2_matchlist_memory/1024 ); if (acsm2_transtable_memory > 0) LogMessage("| Transitions : %.2f\n", (float)acsm2_transtable_memory/1024 ); if (acsm2_failstate_memory > 0) LogMessage("| Fail States : %.2f\n", (float)acsm2_failstate_memory/1024 ); if (acsm2_dfa_memory > 0) { if (summary.acsm.compress_states) { LogMessage("| DFA\n"); LogMessage("| 1 byte states : %.2f\n", (float)acsm2_dfa1_memory/1024); LogMessage("| 2 byte states : %.2f\n", (float)acsm2_dfa2_memory/1024); LogMessage("| 4 byte states : %.2f\n", (float)acsm2_dfa4_memory/1024); } else { LogMessage("| DFA : %.2f\n", (float)acsm2_dfa_memory/1024 ); } } } else { LogMessage("| Memory (MB) : %.2f\n", (float)acsm2_total_memory/(1024*1024) ); if (acsm2_pattern_memory > 0) LogMessage("| Patterns : %.2f\n", (float)acsm2_pattern_memory/(1024*1024) ); if (acsm2_matchlist_memory > 0) LogMessage("| Match Lists : %.2f\n", (float)acsm2_matchlist_memory/(1024*1024) ); if (acsm2_transtable_memory > 0) LogMessage("| Transitions : %.2f\n", (float)acsm2_transtable_memory/(1024*1024) ); if (acsm2_failstate_memory > 0) LogMessage("| Fail States : %.2f\n", (float)acsm2_failstate_memory/(1024*1024) ); if (acsm2_dfa_memory > 0) { if (summary.acsm.compress_states) { LogMessage("| DFA\n"); LogMessage("| 1 byte states : %.2f\n", (float)acsm2_dfa1_memory/(1024*1024)); LogMessage("| 2 byte states : %.2f\n", (float)acsm2_dfa2_memory/(1024*1024)); LogMessage("| 4 byte states : %.2f\n", (float)acsm2_dfa4_memory/(1024*1024)); } else { LogMessage("| DFA : %.2f\n", (float)acsm2_dfa_memory/(1024*1024) ); } } } LogMessage("+----------------------------------------------------------------\n"); return 0; } #ifdef ACSMX2S_MAIN /* * Text Data Buffer */ unsigned char text[512]; /* * A Match is found */ int MatchFound (void* id, int index, void *data) { fprintf (stdout, "%s\n", (char *) id); return 0; } /* * */ int main (int argc, char **argv) { int i, nc, nocase = 0; ACSM_STRUCT2 * acsm; char * p; if (argc < 3) { fprintf (stderr,"Usage: %s search-text pattern +pattern... [flags]\n",argv[0]); fprintf (stderr," flags: -nfa -nocase -full -sparse -bands -sparsebands -z zcnt (sparsebands) -sparsetree -v\n"); exit (0); } acsm = acsmNew2 (); if( !acsm ) { printf("acsm-no memory\n"); exit(0); } strncpy (text, argv[1], sizeof(text) - 1); text[sizeof(text) - 1] = '\0'; acsm->acsmFormat = ACF_FULL; for (i = 1; i < argc; i++) { if (strcmp (argv[i], "-nocase") == 0){ nocase = 1; } if (strcmp (argv[i], "-v") == 0){ s_verbose=1; } if (strcmp (argv[i], "-full") == 0){ acsm->acsmFormat = ACF_FULL; } if (strcmp (argv[i], "-fullq") == 0){ acsm->acsmFormat = ACF_FULLQ; } if (strcmp (argv[i], "-sparse") == 0){ acsm->acsmFormat = ACF_SPARSE; acsm->acsmSparseMaxRowNodes = 10; } if (strcmp (argv[i], "-bands") == 0){ acsm->acsmFormat = ACF_BANDED; } if (strcmp (argv[i], "-sparsebands") == 0){ acsm->acsmFormat = ACF_SPARSEBANDS; acsm->acsmSparseMaxZcnt = 10; } if (strcmp (argv[i], "-z") == 0){ acsm->acsmSparseMaxZcnt = atoi(argv[++i]); } if (strcmp (argv[i], "-nfa") == 0){ acsm->acsmFSA = FSA_NFA; } if (strcmp (argv[i], "-dfa") == 0){ acsm->acsmFSA = FSA_DFA; } if (strcmp (argv[i], "-trie") == 0){ acsm->acsmFSA = FSA_TRIE; } } for (i = 2; i < argc; i++) { if (argv[i][0] == '-') continue; p = argv[i]; if ( *p == '+') { nc=1; p++; } else { nc = nocase; } acsmAddPattern2 (acsm, p, strlen(p), nc, 0, 0,(void*)p, i - 2); } if(s_verbose)printf("Patterns added\n"); Print_DFA (acsm); acsmCompile2 (acsm); Write_DFA(acsm, "acsmx2-snort.dfa") ; if(s_verbose) printf("Patterns compiled--written to file.\n"); acsmPrintInfo2 ( acsm ); acsmSearch2 (acsm, text, strlen (text), MatchFound, (void *)0 ); acsmFree2 (acsm); printf ("normal pgm end\n"); return (0); } #endif /* */ snort-2.9.20/src/sfutil/sfmemcap.c0000644000175000017500000000677714241077274015156 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* sfmemcap.c These functions wrap the alloc & free functions. They enforce a memory cap using the MEMCAP structure. The MEMCAP structure tracks memory usage. Each allocation has 4 bytes added to it so we can store the allocation size. This allows us to free a block and accurately track how much memory was recovered. Marc Norton */ #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "sfmemcap.h" #ifndef DYNAMIC_PREPROC_CONTEXT #include "util.h" #else #include "sf_dynamic_preprocessor.h" #endif //DYNAMIC_PREPROC_CONTEXT /* * Set max # bytes & init other variables. */ void sfmemcap_init( MEMCAP * mc, unsigned long nbytes ) { mc->memcap = nbytes; mc->memused= 0; mc->nblocks= 0; } /* * Create and Init a MEMCAP - use free to release it */ MEMCAP * sfmemcap_new( unsigned nbytes ) { MEMCAP * mc; mc = (MEMCAP*)calloc(1,sizeof(MEMCAP)); if( mc ) sfmemcap_init( mc, nbytes ); return mc; } /* * Release the memcap structure */ void sfmemcap_delete( MEMCAP * p ) { if(p)free( p ); } /* * Allocate some memory */ void * sfmemcap_alloc( MEMCAP * mc, unsigned long nbytes ) { long * data; //printf("sfmemcap_alloc: %d bytes requested, memcap=%d, used=%d\n",nbytes,mc->memcap,mc->memused); nbytes += sizeof(long); /* Check if we are limiting memory use */ if( mc->memcap > 0 ) { /* Check if we've maxed out our memory - if we are tracking memory */ if( (mc->memused + nbytes) > mc->memcap ) { return 0; } } //data = (long *) malloc( nbytes ); data = (long *)calloc(1, nbytes); if( data == NULL ) { return 0; } *data++ = (long)nbytes; mc->memused += nbytes; mc->nblocks++; return data; } /* * Free some memory */ void sfmemcap_free( MEMCAP * mc, void * p ) { long * q; q = (long*)p; q--; mc->memused -= (unsigned)(*q); mc->nblocks--; free(q); } /* * For debugging. */ void sfmemcap_showmem( MEMCAP * mc ) { fprintf(stderr, "memcap: memcap = %lu bytes,",mc->memcap); fprintf(stderr, " memused= %lu bytes,",mc->memused); fprintf(stderr, " nblocks= %d blocks\n",mc->nblocks); } /* * Dup Some memory. */ void * sfmemcap_dupmem( MEMCAP * mc, void * src, unsigned long n ) { void * data = (char *)sfmemcap_alloc( mc, n ); if(data == NULL) { return 0; } memcpy( data, src, n ); return data; } snort-2.9.20/src/sfutil/bnfa_search.h0000644000175000017500000001363314241077213015601 0ustar apoapo/* ** bnfa_search.h ** ** Basic NFA based multi-pattern search using Aho_corasick construction, ** and compacted sparse storage. ** ** Version 3.0 ** ** author: marc norton ** date: 12/21/05 ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2005-2013 Sourcefire, Inc. ** ** LICENSE (GPL) ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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 k** */ #include #include #include #ifndef BNFA_SEARCH_H #define BNFA_SEARCH_H /* debugging - allow printing the trie and nfa in list format */ /* #define ALLOW_LIST_PRINT */ /* debugging - enable full format */ /* #define ALLOW_NFA_FULL */ /* * DEFINES and Typedef's */ //#define SPARSE_FULL_STATE_0 #define BNFA_MAX_ALPHABET_SIZE 256 #define BNFA_FAIL_STATE 0xffffffff #define BNFA_SPARSE_LINEAR_SEARCH_LIMIT 6 #define BNFA_SPARSE_MAX_STATE 0x00ffffff #define BNFA_SPARSE_COUNT_SHIFT 24 #define BNFA_SPARSE_VALUE_SHIFT 24 #define BNFA_SPARSE_MATCH_BIT 0x80000000 #define BNFA_SPARSE_FULL_BIT 0x40000000 #define BNFA_SPARSE_COUNT_BITS 0x3f000000 #define BNFA_SPARSE_MAX_ROW_TRANSITIONS 0x3f typedef unsigned int bnfa_state_t; /* * Internal Pattern Representation */ typedef struct bnfa_pattern { struct bnfa_pattern * next; unsigned char * casepatrn; /* case specific */ int n; /* pattern len */ int nocase; /* nocase flag */ int negative; /* pattern is negated */ void * userdata; /* ptr to users pattern data/info */ } bnfa_pattern_t; /* * List format transition node */ typedef struct bnfa_trans_node_s { bnfa_state_t key; bnfa_state_t next_state; struct bnfa_trans_node_s * next; } bnfa_trans_node_t; /* * List format patterns */ typedef struct bnfa_match_node_s { void * data; void * rule_option_tree; void * neg_list; struct bnfa_match_node_s * next; } bnfa_match_node_t; /* * Final storage type for the state transitions */ enum { BNFA_FULL, BNFA_SPARSE }; enum { BNFA_PER_PAT_CASE, BNFA_CASE, BNFA_NOCASE }; /* * Aho-Corasick State Machine Struct */ typedef struct { int bnfaMethod; int bnfaCaseMode; int bnfaFormat; int bnfaAlphabetSize; int bnfaOpt; unsigned bnfaPatternCnt; bnfa_pattern_t * bnfaPatterns; int bnfaMaxStates; int bnfaNumStates; int bnfaNumTrans; int bnfaMatchStates; bnfa_trans_node_t ** bnfaTransTable; bnfa_state_t ** bnfaNextState; bnfa_match_node_t ** bnfaMatchList; bnfa_state_t * bnfaFailState; bnfa_state_t * bnfaTransList; int bnfaForceFullZeroState; int bnfa_memory; int pat_memory; int list_memory; int queue_memory; int nextstate_memory; int failstate_memory; int matchlist_memory; void (*userfree)(void *); void (*optiontreefree)(void **); void (*neg_list_free)(void **); #define MAX_INQ 32 unsigned inq; unsigned inq_flush; void * q[MAX_INQ]; }bnfa_struct_t; /* * Prototypes */ bnfa_struct_t * bnfaNew ( void (*userfree)(void *p), void (*optiontreefree)(void **p), void (*neg_list_free)(void **p)); void bnfaSetOpt(bnfa_struct_t * p, int flag); void bnfaSetCase(bnfa_struct_t * p, int flag); void bnfaFree( bnfa_struct_t * pstruct ); int bnfaAddPattern( bnfa_struct_t * pstruct, unsigned char * pat, int patlen, int nocase, int negative, void * userdata); int bnfaCompile( bnfa_struct_t * pstruct, int (*build_tree)(void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); struct _SnortConfig; int bnfaCompileWithSnortConf( struct _SnortConfig *, bnfa_struct_t * pstruct, int (*build_tree)(struct _SnortConfig *, void * id, void **existing_tree), int (*neg_list_func)(void *id, void **list)); unsigned bnfaSearch( bnfa_struct_t * pstruct, unsigned char * t, int tlen, int (*match)(void * id, void *tree, int index, void *data, void *neg_list), void * sdata, unsigned sindex, int* current_state ); int bnfaPatternCount( bnfa_struct_t * p); void bnfaPrint( bnfa_struct_t * pstruct); /* prints the nfa states-verbose!! */ void bnfaPrintInfo( bnfa_struct_t * pstruct); /* print info on this search engine */ /* * Summary - this tracks search engine information accross multiple instances of * search engines. It helps in snort where we have many search engines, each using * rule grouping, to track total patterns, states, memory, etc... * */ void bnfaPrintInfoEx( bnfa_struct_t * p, char * text ); void bnfaAccumInfo( bnfa_struct_t * pstruct); /* add info to summary over multiple search engines */ void bnfaPrintSummary(void); /* print current summary */ void bnfaInitSummary(void); /* reset accumulator foir global summary over multiple engines */ void bnfa_print_qinfo(void); #endif snort-2.9.20/src/sfutil/sf_textlog.c0000644000175000017500000001775514241077252015533 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file sf_textlog.c * @author Russ Combs * @date * * @brief implements buffered text stream for logging */ #include #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sf_textlog.h" #include "log.h" #include "util.h" /* some reasonable minimums */ #define MIN_BUF (1*K_BYTES) #define MIN_FILE (MIN_BUF) /*------------------------------------------------------------------- * TextLog_Open/Close: open/close associated log file *------------------------------------------------------------------- */ static FILE* TextLog_Open (const char* name) { if ( !name ) return OpenAlertFile(NULL); if ( !strcasecmp(name, "stdout") ) return stdout; return OpenAlertFile(name); } static void TextLog_Close (FILE* file) { if ( !file ) return; if ( file != stdout ) fclose(file); } static size_t TextLog_Size (FILE* file) { struct stat sbuf; int fd = fileno(file); int err = fstat(fd, &sbuf); return err ? 0 : sbuf.st_size; } /*------------------------------------------------------------------- * TextLog_Init: constructor *------------------------------------------------------------------- */ TextLog* TextLog_Init ( const char* name, unsigned int maxBuf, size_t maxFile ) { TextLog* this; if ( maxBuf < MIN_BUF ) maxBuf = MIN_BUF; if ( maxFile < MIN_FILE ) maxFile = MIN_FILE; if ( maxFile < maxBuf ) maxFile = maxBuf; this = (TextLog*)malloc(sizeof(TextLog)+maxBuf); if ( !this ) { FatalError("Unable to allocate a TextLog(%u)!\n", maxBuf); } this->name = name ? SnortStrdup(name) : NULL; this->file = TextLog_Open(this->name); this->size = TextLog_Size(this->file); this->last = time(NULL); this->maxFile = maxFile; this->maxBuf = maxBuf; TextLog_Reset(this); return this; } /*------------------------------------------------------------------- * TextLog_Term: destructor *------------------------------------------------------------------- */ void TextLog_Term (TextLog* this) { if ( !this ) return; TextLog_Flush(this); TextLog_Close(this->file); if ( this->name ) free(this->name); free(this); } /*------------------------------------------------------------------- * TextLog_Flush: start writing to new file * but don't roll over stdout or any sooner * than resolution of filename discriminator *------------------------------------------------------------------- */ static void TextLog_Roll (TextLog* this) { if ( this->file == stdout ) return; if ( this->last >= time(NULL) ) return; TextLog_Close(this->file); RollAlertFile(this->name); this->file = TextLog_Open(this->name); this->last = time(NULL); this->size = 0; } /*------------------------------------------------------------------- * TextLog_Flush: write buffered stream to file *------------------------------------------------------------------- */ bool TextLog_Flush(TextLog* this) { int ok; if ( !this->pos ) return FALSE; if ( this->size + this->pos > this->maxFile ) TextLog_Roll(this); ok = fwrite(this->buf, this->pos, 1, this->file); if ( ok == 1 ) { this->size += this->pos; TextLog_Reset(this); return TRUE; } return FALSE; } /*------------------------------------------------------------------- * TextLog_Putc: append char to buffer *------------------------------------------------------------------- */ bool TextLog_Putc (TextLog* this, char c) { if ( TextLog_Avail(this) < 1 ) { TextLog_Flush(this); } this->buf[this->pos++] = c; this->buf[this->pos] = '\0'; return TRUE; } /*------------------------------------------------------------------- * TextLog_Write: append string to buffer *------------------------------------------------------------------- */ bool TextLog_Write (TextLog* this, const char* str, int len) { int avail = TextLog_Avail(this); if ( len >= avail ) { TextLog_Flush(this); avail = TextLog_Avail(this); } len = snprintf(this->buf+this->pos, avail, "%s", str); if ( len >= avail ) { this->pos = this->maxBuf - 1; this->buf[this->pos] = '\0'; return FALSE; } else if ( len < 0 ) { return FALSE; } this->pos += len; return TRUE; } /*------------------------------------------------------------------- * TextLog_Printf: append formatted string to buffer *------------------------------------------------------------------- */ bool TextLog_Print (TextLog* this, const char* fmt, ...) { int avail = TextLog_Avail(this); int len; va_list ap; va_start(ap, fmt); len = vsnprintf(this->buf+this->pos, avail, fmt, ap); va_end(ap); if ( len >= avail ) { TextLog_Flush(this); avail = TextLog_Avail(this); va_start(ap, fmt); len = vsnprintf(this->buf+this->pos, avail, fmt, ap); va_end(ap); } if ( len >= avail ) { this->pos = this->maxBuf - 1; this->buf[this->pos] = '\0'; return FALSE; } else if ( len < 0 ) { return FALSE; } this->pos += len; return TRUE; } /*------------------------------------------------------------------- * TextLog_PrintUnicode: append formatted string to buffer *------------------------------------------------------------------- */ bool TextLog_PrintUnicode(TextLog* this, uint8_t *buffer, uint32_t length, uint8_t is_little_endian) { uint32_t out_len = length/2 + 1, i, j; wchar_t *outbuf = (wchar_t *)malloc(out_len*sizeof(wchar_t)); if (!outbuf) { FatalError("TextLog_PrintUnicode: Failed to allocate memory for outbuf\n"); return FALSE; } for(i = 0,j = 0; ifile, "%ls", outbuf); free(outbuf); return TRUE; } /*------------------------------------------------------------------- * TextLog_Quote: write string escaping quotes * TBD could be smarter by counting required escapes instead of * checking for 3 *------------------------------------------------------------------- */ bool TextLog_Quote (TextLog* this, const char* qs) { int pos = this->pos; if ( TextLog_Avail(this) < 3 ) { TextLog_Flush(this); } this->buf[pos++] = '"'; while ( *qs && (this->maxBuf - pos > 2) ) { if ( *qs == '"' || *qs == '\\' ) { this->buf[pos++] = '\\'; } this->buf[pos++] = *qs++; } if ( *qs ) return FALSE; this->buf[pos++] = '"'; this->pos = pos; return TRUE; } snort-2.9.20/src/sfutil/sfrt.c0000644000175000017500000005210014241077320014306 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * @file sfrt.c * @author Adam Keeton * @date Thu July 20 10:16:26 EDT 2006 * * Route implements two different routing table lookup mechanisms. The table * lookups have been adapted to return a void pointer so any information can * be associated with each CIDR block. * * As of this writing, the two methods used are Stefan Nilsson and Gunnar * Karlsson's LC-trie, and a multibit-trie method similar to Gupta et-al.'s * DIR-n-m. Presently, the LC-trie is used primarily for testing purposes as * the current implementation does not allow for fast dynamic inserts. * * The intended use is for a user to optionally specify large IP blocks and * then more specific information will be written into the routing tables * from RNA. Ideally, information will only move from less specific to more * specific. If a more general information is to overwrite existing entries, * the table should be free'ed and rebuilt. * * * Implementation: * * The routing tables associate an index into a "data" table with each CIDR. * Each entry in the data table stores a pointer to actual data. This * implementation was chosen so each routing entry only needs one word to * either index the data array, or point to another table. * * Inserts are performed by specifying a CIDR and a pointer to its associated * data. Since a new routing table entry may overwrite previous entries, * a flag selects whether the insert favors the most recent or favors the most * specific. Favoring most specific should be the default behvior. If * the user wishes to overwrite routing entries with more general data, the * table should be flushed, rather than using favor-most-recent. * * Before modifying the routing or data tables, the insert function performs a * lookup on the CIDR-to-be-insertted. If no entry or an entry *of differing * bit length* is found, the data is insertted into the data table, and its * index is used for the new routing table entry. If an entry is found that * is as specific as the new CIDR, the index stored points to where the new * data is written into the data table. * * If more specific CIDR blocks overwrote the data table, then the more * general routing table entries that were not overwritten will be referencing * the wrong data. Alternatively, less specific entries can only overwrite * existing routing table entries if favor-most-recent inserts are used. * * Because there is no quick way to clean the data-table if a user wishes to * use a favor-most-recent insert for more general data, the user should flush * the table with sfrt_free and create one anew. Alternatively, a small * memory leak occurs with the data table, as it will be storing pointers that * no routing table entry cares about. * * * The API calls that should be used are: * sfrt_new - create new table * sfrt_insert - insert entry * sfrt_lookup - lookup entry * sfrt_free - free table */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "sfrt.h" char *rt_error_messages[] = { "Success", "Insert Failure", "Policy Table Exceeded", "Dir Insert Failure", "Dir Lookup Failure", "Memory Allocation Failure" #ifdef SUPPORT_LCTRIE , "LC Trie Compile Failure", "LC Trie Insert Failure", "LC Trie Lookup Failure" #endif }; static inline int allocateTableIndex(table_t *table); /* Create new lookup table * @param table_type Type of table. Uses the types enumeration in route.h * @param ip_type IPv4 or IPv6. Uses the types enumeration in route.h * @param data_size Max number of unique data entries * * Returns the new table. */ table_t *sfrt_new(char table_type, char ip_type, long data_size, uint32_t mem_cap) { table_t *table = (table_t*)malloc(sizeof(table_t)); if(!table) { return NULL; } /* If this limit is exceeded, there will be no way to distinguish * between pointers and indeces into the data table. Only * applies to DIR-n-m. */ #ifdef SUPPORT_LCTRIE #if SIZEOF_LONG_INT == 8 if(data_size >= 0x800000000000000 && table_type == LCT) #else if(data_size >= 0x8000000 && table_type != LCT) #endif #else /* SUPPORT_LCTRIE */ #if SIZEOF_LONG_INT == 8 if(data_size >= 0x800000000000000) #else if(data_size >= 0x8000000) #endif #endif { free(table); return NULL; } /* mem_cap is specified in megabytes, but internally uses bytes. Convert */ mem_cap *= 1024*1024; /* Maximum allowable number of stored entries */ table->max_size = data_size; table->lastAllocatedIndex = 0; table->data = (GENERIC*)calloc(sizeof(GENERIC) * table->max_size, 1); if(!table->data) { free(table); return NULL; } table->allocated = sizeof(table_t) + sizeof(GENERIC) * table->max_size; table->ip_type = ip_type; table->table_type = table_type; /* This will point to the actual table lookup algorithm */ table->rt = NULL; table->rt6 = NULL; /* index 0 will be used for failed lookups, so set this to 1 */ table->num_ent = 1; switch(table_type) { #ifdef SUPPORT_LCTRIE /* Setup LC-trie table */ case LCT: /* LC trie is presently not allowed */ table->insert = sfrt_lct_insert; table->lookup = sfrt_lct_lookup; table->free = sfrt_lct_free; table->usage = sfrt_lct_usage; table->print = NULL; table->remove = NULL; table->rt = sfrt_lct_new(data_size); free(table->data); free(table); return NULL; break; #endif /* Setup DIR-n-m table */ case DIR_24_8: case DIR_16x2: case DIR_16_8x2: case DIR_16_4x4: case DIR_8x4: case DIR_4x8: case DIR_2x16: case DIR_16_4x4_16x5_4x4: case DIR_16x7_4x4: case DIR_16x8: case DIR_8x16: table->insert = sfrt_dir_insert; table->lookup = sfrt_dir_lookup; table->free = sfrt_dir_free; table->usage = sfrt_dir_usage; table->print = sfrt_dir_print; table->remove = sfrt_dir_remove; break; default: free(table->data); free(table); return NULL; }; /* Allocate the user-specified DIR-n-m table */ switch(table_type) { case DIR_24_8: table->rt = sfrt_dir_new(mem_cap, 2, 24,8); break; case DIR_16x2: table->rt = sfrt_dir_new(mem_cap, 2, 16,16); break; case DIR_16_8x2: table->rt = sfrt_dir_new(mem_cap, 3, 16,8,8); break; case DIR_16_4x4: table->rt = sfrt_dir_new(mem_cap, 5, 16,4,4,4,4); break; case DIR_8x4: table->rt = sfrt_dir_new(mem_cap, 4, 8,8,8,8); break; /* There is no reason to use 4x8 except for benchmarking and * comparison purposes. */ case DIR_4x8: table->rt = sfrt_dir_new(mem_cap, 8, 4,4,4,4,4,4,4,4); break; /* There is no reason to use 2x16 except for benchmarking and * comparison purposes. */ case DIR_2x16: table->rt = sfrt_dir_new(mem_cap, 16, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2); break; case DIR_16_4x4_16x5_4x4: table->rt = sfrt_dir_new(mem_cap, 5, 16,4,4,4,4); table->rt6 = sfrt_dir_new(mem_cap, 14, 16,4,4,4,4,16,16,16,16,16,4,4,4,4); break; case DIR_16x7_4x4: table->rt = sfrt_dir_new(mem_cap, 5, 16,4,4,4,4); table->rt6 = sfrt_dir_new(mem_cap, 11, 16,16,16,16,16,16,16,4,4,4,4); break; case DIR_16x8: table->rt = sfrt_dir_new(mem_cap, 2, 16,16); table->rt6 = sfrt_dir_new(mem_cap, 8, 16,16,16,16,16,16,16,16); break; case DIR_8x16: table->rt = sfrt_dir_new(mem_cap, 4, 16,8,4,4); table->rt6 = sfrt_dir_new(mem_cap, 16, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8); break; }; if((!table->rt) || (!table->rt6)) { if (table->rt) table->free( table->rt ); if (table->rt6) table->free( table->rt6 ); free(table->data); free(table); return NULL; } return table; } /* Free lookup table */ void sfrt_free(table_t *table) { if(!table) { /* What are you calling me for? */ return; } if(!table->data) { /* This really really should not have happened */ } else { free(table->data); } if(!table->rt) { /* This should not have happened either */ } else { table->free( table->rt ); } if(!table->rt6) { /* This should not have happened either */ } else { table->free( table->rt6 ); } free(table); } /* Perform a lookup on value contained in "ip" */ GENERIC sfrt_lookup(sfaddr_t* ip, table_t* table) { tuple_t tuple; uint32_t* adr; int numAdrDwords; void *rt; if(!ip) { return NULL; } if(!table || !table->lookup) { return NULL; } if (sfaddr_family(ip) == AF_INET) { adr = sfaddr_get_ip4_ptr(ip); numAdrDwords = 1; rt = table->rt; } else { adr = sfaddr_get_ip6_ptr(ip); numAdrDwords = 4; rt = table->rt6; } tuple = table->lookup(adr, numAdrDwords, rt); if(tuple.index >= table->max_size) { return NULL; } return table->data[tuple.index]; } void sfrt_iterate(table_t* table, sfrt_iterator_callback userfunc) { uint32_t index, count; if (!table) return; for (index = 0, count = 0; index < table->max_size; index++) { if (table->data[index]) { userfunc(table->data[index]); if (++count == table->num_ent) break; } } return; } void sfrt_iterate_with_snort_config(struct _SnortConfig *sc, table_t* table, sfrt_sc_iterator_callback userfunc) { uint32_t index, count; if (!table) return; for (index = 0, count = 0; index < table->max_size; index++) { if (table->data[index]) { userfunc(sc, table->data[index]); if (++count == table->num_ent) break; } } return; } int sfrt_iterate2(table_t* table, sfrt_iterator_callback3 userfunc) { uint32_t index, count; if (!table) return 0; for (index = 0, count = 0; index < table->max_size; index++) { if (table->data[index]) { int ret = userfunc(table->data[index]); if (ret != 0) return ret; if (++count == table->num_ent) break; } } return 0; } int sfrt_iterate2_with_snort_config(struct _SnortConfig *sc, table_t* table, sfrt_sc_iterator_callback3 userfunc) { uint32_t index, count; if (!table) return 0; for (index = 0, count = 0; index < table->max_size; index++) { if (table->data[index]) { int ret = userfunc(sc, table->data[index]); if (ret != 0) return ret; if (++count == table->num_ent) break; } } return 0; } void sfrt_cleanup2( table_t* table, sfrt_iterator_callback2 cleanup_func, void *data ) { uint32_t index, count; if (!table) return; for (index = 0, count = 0; index < table->max_size; index++) { if (table->data[index]) { cleanup_func(table->data[index], data); /* cleanup_func is supposed to free memory associated with this * table->data[index]. Set that to NULL. */ table->data[index] = NULL; if (++count == table->num_ent) break; } } } void sfrt_cleanup(table_t* table, sfrt_iterator_callback cleanup_func) { uint32_t index, count; if (!table) return; for (index = 0, count = 0; index < table->max_size; index++) { if (table->data[index]) { cleanup_func(table->data[index]); /* cleanup_func is supposed to free memory associated with this * table->data[index]. Set that to NULL. */ table->data[index] = NULL; if (++count == table->num_ent) break; } } return; } GENERIC sfrt_search(sfaddr_t* ip, table_t *table) { uint32_t* adr; int numAdrDwords; tuple_t tuple; void *rt = NULL; if ((ip == NULL) || (table == NULL)) return NULL; if (sfaddr_family(ip) == AF_INET) { adr = sfaddr_get_ip4_ptr(ip); numAdrDwords = 1; rt = table->rt; } else { adr = sfaddr_get_ip6_ptr(ip); numAdrDwords = 4; rt = table->rt6; } tuple = table->lookup(adr, numAdrDwords, rt); if(tuple.index >= table->max_size) return NULL; return table->data[tuple.index]; } /* Insert "ip", of length "len", into "table", and have it point to "ptr" */ /* Insert "ip", of length "len", into "table", and have it point to "ptr" */ int sfrt_insert(sfcidr_t* ip, unsigned char len, GENERIC ptr, int behavior, table_t *table) { int index; int newIndex = 0; int res; uint32_t* adr; int numAdrDwords; tuple_t tuple; void *rt = NULL; if(!ip) { return RT_INSERT_FAILURE; } if (len == 0) return RT_INSERT_FAILURE; if(!table || !table->insert || !table->data || !table->lookup) { return RT_INSERT_FAILURE; } if (len > 128) { return RT_INSERT_FAILURE; } /* Check if we can reuse an existing data table entry by * seeing if there is an existing entry with the same length. */ /* Only perform this if the table is not an LC-trie */ #ifdef SUPPORT_LCTRIE if(table->table_type != LCT) { #endif if (sfaddr_family(&ip->addr) == AF_INET) { if (len < 96) { return RT_INSERT_FAILURE; } len -= 96; adr = sfip_get_ip4_ptr(ip); numAdrDwords = 1; rt = table->rt; } else { adr = sfip_get_ip6_ptr(ip); numAdrDwords = 4; rt = table->rt6; } if (!rt) { return RT_INSERT_FAILURE; } tuple = table->lookup(adr, numAdrDwords, rt); #ifdef SUPPORT_LCTRIE } #endif #ifdef SUPPORT_LCTRIE if(table->table_type == LCT || tuple.length != len) { #else if(tuple.length != len) { #endif if( table->num_ent >= table->max_size) { return RT_POLICY_TABLE_EXCEEDED; } index = newIndex = allocateTableIndex(table); if (!index) return RT_POLICY_TABLE_EXCEEDED; } else { index = tuple.index; } /* The actual value that is looked-up is an index * into the data table. */ res = table->insert(adr, numAdrDwords, len, index, behavior, rt); if ((res == RT_SUCCESS) && newIndex) { table->num_ent++; table->data[ index ] = ptr; } return res; } /** Pretty print table * Pretty print sfrt table. * @param table - routing table. */ void sfrt_print(table_t *table) { if(!table || !table->print ) { return; } if (table->rt) table->print(table->rt); if (table->rt6) table->print(table->rt6); } uint32_t sfrt_num_entries(table_t *table) { if(!table || !table->rt || !table->allocated) { return 0; } /* There is always a root node, so subtract 1 for it */ return table->num_ent - 1; } uint32_t sfrt_usage(table_t *table) { uint32_t usage; if(!table || !table->rt || !table->allocated || !table->usage) { return 0; } usage = table->allocated + table->usage( table->rt ); if (table->rt6) { usage += table->usage( table->rt6 ); } return usage; } /** Remove subnet from sfrt table. * Remove subnet identified by ip/len and return associated data. * @param ip - IP address * @param len - length of netmask * @param ptr - void ** that is set to value associated with subnet * @param behavior - RT_FAVOR_SPECIFIC or RT_FAVOR_TIME * @note - For RT_FAVOR_TIME behavior, if partial subnet is removed then table->data[x] is nulled. Any remaining entries * will then point to null data. This can cause hung or crosslinked data. RT_FAVOR_SPECIFIC does not have this drawback. * hung or crosslinked entries. */ int sfrt_remove(sfcidr_t* ip, unsigned char len, GENERIC *ptr, int behavior, table_t *table) { int index; uint32_t* adr; int numAdrDwords; void *rt = NULL; if(!ip) { return RT_REMOVE_FAILURE; } if (len == 0) return RT_REMOVE_FAILURE; if(!table || !table->data || !table->remove || !table->lookup ) { //remove operation will fail for LCT since this operation is not implemented return RT_REMOVE_FAILURE; } if (len > 128) { return RT_REMOVE_FAILURE; } #ifdef SUPPORT_LCTRIE if(table->table_type != LCT) { #endif if (sfaddr_family(&ip->addr) == AF_INET) { if (len < 96) { return RT_REMOVE_FAILURE; } len -= 96; adr = sfip_get_ip4_ptr(ip); numAdrDwords = 1; rt = table->rt; } else { adr = sfip_get_ip6_ptr(ip); numAdrDwords = 4; rt = table->rt6; } #ifdef SUPPORT_LCTRIE } #endif /* The actual value that is looked-up is an index * into the data table. */ index = table->remove(adr, numAdrDwords, len, behavior, rt); /* Remove value into policy table. See TBD in function header*/ if (index) { *ptr = table->data[ index ]; table->data[ index ] = NULL; table->num_ent--; } return RT_SUCCESS; } /**allocate first unused index value. With delete operation, index values can be non-contiguous. * Index 0 is error in this function but this is valid entry in table->data that is used * for failure case. Calling function must check for 0 and take appropriate error action. */ static inline int allocateTableIndex(table_t *table) { uint32_t index; //0 is special index for failed entries. for (index = table->lastAllocatedIndex+1; index != table->lastAllocatedIndex; index = (index+1) % table->max_size) { if (index && !table->data[index]) { table->lastAllocatedIndex = index; return index; } } return 0; } #ifdef DEBUG_SFRT #define NUM_IPS 32 #define NUM_DATA 4 int main() { table_t *dir; uint32_t ip_list[NUM_IPS]; /* entirely arbitrary */ char data[NUM_DATA]; /* also entirely arbitrary */ uint32_t index, val; for(index=0; index %c\n", index, ip_list[index], data[index%NUM_DATA], *(uint32_t*)sfrt_lookup(&ip_list[index], dir)); } for(index=0; index < NUM_IPS; index++) { val = *(uint32_t*)sfrt_lookup(&ip_list[index], dir); printf("\t@%d\t%x: %c. originally:\t%c\n", index, ip_list[index], val, data[index%NUM_DATA]); } printf("Usage: %d bytes\n", ((dir_table_t*)(dir->rt))->allocated); sfrt_free(dir); return 0; } #endif /* DEBUG_SFRT */ snort-2.9.20/src/sfutil/sf_base64decode.h0000644000175000017500000000221414241077234016262 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** Writen by Patrick Mullen ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SF_BASE64DECODE_H_ #define _SF_BASE64DECODE_H_ #include "sf_types.h" #include "util_unfold.h" int sf_base64decode(uint8_t*, uint32_t, uint8_t*, uint32_t, uint32_t*); #endif snort-2.9.20/src/sfutil/strvec.c0000644000175000017500000000475314241077344014657 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2009-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "strvec.h" #include "util.h" typedef struct { char** v; unsigned n; } StringVector; void* StringVector_New (void) { StringVector* sv = SnortAlloc(sizeof(*sv)); sv->v = SnortAlloc(sizeof(*sv->v)); sv->n = 0; return sv; } void StringVector_Delete (void* pv) { unsigned i; StringVector* sv = (StringVector*)pv; if ( !sv ) return; for ( i = 0; i < sv->n; i++ ) free(sv->v[i]); free(sv->v); free(sv); } int StringVector_Add (void* pv, const char* s) { StringVector* sv = (StringVector*)pv; char** v; if ( !sv || !s ) return 0; v = realloc(sv->v, (sv->n+2) * sizeof(char*)); if ( !v ) return 0; sv->v = v; sv->v[sv->n++] = SnortStrdup(s); sv->v[sv->n] = NULL; return 1; } char* StringVector_Get (void* pv, unsigned index) { StringVector* sv = (StringVector*)pv; if ( !sv || index >= sv->n ) return NULL; return sv->v[index]; } int StringVector_AddVector (void* pd, void* ps) { unsigned i = 0; const char* s = StringVector_Get(ps, i++); while ( s ) { if ( !StringVector_Add(pd, s) ) return 0; s = StringVector_Get(ps, i++); } return 1; } const char** StringVector_GetVector (void* pv) { StringVector* sv = (StringVector*)pv; if ( !sv ) return NULL; return (const char**)sv->v; } snort-2.9.20/src/sfutil/sfPolicy.c0000644000175000017500000003115714241077277015144 0ustar apoapo/**************************************************************************** * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2008-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "stdlib.h" #include "stdio.h" #include "string.h" #include "sfPolicy.h" #include "snort_debug.h" #include "sfrt.h" tSfPolicyId napRuntimePolicyId = 0; tSfPolicyId ipsRuntimePolicyId = 0; static inline int IsBound ( tSfPolicyId id ) { return ( id != SF_POLICY_UNBOUND ); } static inline int NotBound ( tSfPolicyId id ) { return !IsBound(id); } static void netBindFree( void *policy, void *config ); tSfPolicyConfig * sfPolicyInit(void) { int i; tSfPolicyConfig *new = (tSfPolicyConfig *)calloc(1, sizeof(tSfPolicyConfig)); if (new == NULL) return NULL; //initialize vlan bindings for (i = 0; i < SF_VLAN_BINDING_MAX; i++) { new->vlanBindings[i] = SF_POLICY_UNBOUND; } for (i = 0; i < SF_POLICY_ID_BINDING_MAX; i++) { new->policyIdBindings[i] = SF_POLICY_UNBOUND; } //initialize net bindings new->netBindTable = sfrt_new(DIR_16x7_4x4, IPv6, SF_NETWORK_BINDING_MAX, 20); return new; } void sfPolicyFini(tSfPolicyConfig *config) { int i; if (config == NULL) return; for (i = 0; i < SF_VLAN_BINDING_MAX; i++) { sfVlanDeleteBinding(config, i); } for (i = 0; i < SF_POLICY_ID_BINDING_MAX; i++) { sfPolicyIdDeleteBinding(config, i); } sfrt_cleanup2(config->netBindTable, netBindFree, config); sfrt_free(config->netBindTable); //policyConfig are deleted when all bindings to it are deleted. /* free default policy */ if (config->ppPolicies != NULL) { sfPolicyDelete(config, config->defaultPolicyId); free(config->ppPolicies); } free(config); } static void netBindFree( void *policyId, void *config ) { if (policyId && config) { sfPolicyDelete((tSfPolicyConfig *)config, *((tSfPolicyId *)policyId)); free(policyId); } } /**Tracks filename to vlan group id */ int sfPolicyAdd(tSfPolicyConfig *config, char *fileName) { tSfPolicy *pObject = NULL; int emptyIndex = -1; tSfPolicyId i; tSfPolicy **ppTmp; if (config == NULL) return SF_POLICY_UNBOUND; for (i = 0; i < config->numAllocatedPolicies; i++) { if (config->ppPolicies[i]) { if (!strcmp(config->ppPolicies[i]->filename, fileName)) { config->ppPolicies[i]->refCount++; return i; } } else if (emptyIndex == -1) { emptyIndex = i; } } if (emptyIndex == -1) { //no empty slot available. Allocate more space for policies ppTmp = (tSfPolicy **)calloc(config->numAllocatedPolicies + POLICY_ALLOCATION_CHUNK, sizeof(tSfPolicy *)); if (!ppTmp) return SF_POLICY_UNBOUND; if (config->numAllocatedPolicies) { memcpy(ppTmp, config->ppPolicies, sizeof(tSfPolicyConfig *) * config->numAllocatedPolicies); free(config->ppPolicies); } config->ppPolicies = ppTmp; emptyIndex = config->numAllocatedPolicies; config->numAllocatedPolicies += POLICY_ALLOCATION_CHUNK; } //allocate and initialize pObject = (tSfPolicy *)calloc(1, sizeof(tSfPolicy)); if (!pObject) return SF_POLICY_UNBOUND; pObject->refCount++; pObject->filename = strdup(fileName); if (!pObject->filename) { free(pObject); return SF_POLICY_UNBOUND; } config->ppPolicies[emptyIndex] = pObject; config->numActivePolicies++; //successfully added. return emptyIndex; } void sfPolicyDelete(tSfPolicyConfig *config, tSfPolicyId policyId) { tSfPolicy *pObject = NULL; if ((config == NULL) || (config->ppPolicies == NULL) || (policyId >= config->numAllocatedPolicies)) { return; } pObject = config->ppPolicies[policyId]; if (pObject) { pObject->refCount--; if (pObject->refCount == 0) { if (pObject->filename) free(pObject->filename); free(pObject); config->ppPolicies[policyId] = NULL; config->numActivePolicies--; DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "sfPolicyDelete: freed policyConfig policyId %d\n", policyId);); } } } char * sfPolicyGet(tSfPolicyConfig *config, tSfPolicyId policyId) { tSfPolicy *pObject = NULL; if ((config == NULL) || (config->ppPolicies == NULL) || (policyId >= config->numAllocatedPolicies)) { return NULL; } pObject = config->ppPolicies[policyId]; if (pObject) return pObject->filename; return NULL; } /**Creates policyId if needed and creates a binding between vlan and policyId. * Tracks vlanId to vlan group id mapping */ //TBD replace calloc with SnortAlloc() int sfVlanAddBinding(tSfPolicyConfig *config, int vlanId, char *fileName) { tSfPolicyId policyId; if (config == NULL || vlanId >= SF_VLAN_BINDING_MAX) return -1; //create a policyId policyId = sfPolicyAdd(config, fileName); if ( NotBound(policyId) ) { return -1; } config->vlanBindings[vlanId] = policyId; DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "Added vlandId %d, file %s, policyId: %d\n", vlanId, fileName, policyId);); return 0; } tSfPolicyId sfVlanGetBinding(tSfPolicyConfig *config, int vlanId) { tSfPolicyId policyId; if(vlanId >= SF_VLAN_BINDING_MAX){ //invalid policyid will never be bound. return default return config->defaultPolicyId; } policyId = config->vlanBindings[vlanId]; if ( NotBound(policyId) ) { //return default policyId for uninitialized binding return config->defaultPolicyId; } return policyId; } void sfVlanDeleteBinding(tSfPolicyConfig *config, int vlanId) { tSfPolicyId policyId; if(vlanId >= SF_VLAN_BINDING_MAX) return; //invalid, can't delete if ((config == NULL) || (vlanId < 0)) return; policyId = config->vlanBindings[vlanId]; if ( IsBound(policyId) ) { sfPolicyDelete(config, policyId); config->vlanBindings[vlanId] = SF_POLICY_UNBOUND; } } int sfPolicyIdAddBinding(tSfPolicyConfig *config, int parsedPolicyId, char *fileName) { tSfPolicyId policyId; if (config == NULL || parsedPolicyId >= SF_POLICY_ID_BINDING_MAX) return -1; //create a policyId policyId = sfPolicyAdd(config, fileName); if ( NotBound(policyId) ) { return -1; } config->policyIdBindings[parsedPolicyId] = policyId; DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES, "Added parsedPolicyId %d, file %s, policyId: %d\n", parsedPolicyId, fileName, policyId);); return 0; } tSfPolicyId sfPolicyIdGetBinding(tSfPolicyConfig *config, int parsedPolicyId) { tSfPolicyId policyId; if(parsedPolicyId >= SF_POLICY_ID_BINDING_MAX){ //invalid policyid will never be bound. return default return config->defaultPolicyId; } policyId = config->policyIdBindings[parsedPolicyId]; if ( NotBound(policyId) ) { //return default policyId for uninitialized binding return config->defaultPolicyId; } return policyId; } void sfPolicyIdDeleteBinding(tSfPolicyConfig *config, int parsedPolicyId) { tSfPolicyId policyId; if(parsedPolicyId >= SF_POLICY_ID_BINDING_MAX) return; //invalid, can't delete if ((config == NULL) || (parsedPolicyId < 0)) return; policyId = config->policyIdBindings[parsedPolicyId]; if ( IsBound(policyId) ) { sfPolicyDelete(config, policyId); config->policyIdBindings[parsedPolicyId] = SF_POLICY_UNBOUND; } } /**Get applicable policy given of a packet. Vlan can be negative * number if vlan header is not present. * * Search policy bound to vlan if vlan is not negative. If matched polciy is default one, * then search using destination IP address. If matched policy is default then search using * source IP address. * * @param vlanId - vlan id from a packet. Should be unbound if vlan tag is not present. * @param srcIP - Source IP address * @param dstIP - Destination IP address * * @returns policyId */ tSfPolicyId sfGetApplicablePolicyId( tSfPolicyConfig *config, int vlanId, sfaddr_t* srcIp, sfaddr_t* dstIp ) { tSfPolicyId dst_id; if (config == NULL) return SF_POLICY_UNBOUND; if (vlanId > 0) { tSfPolicyId vlan_id = sfVlanGetBinding(config, vlanId); if (vlan_id > 0) return vlan_id; } dst_id = sfNetworkGetBinding(config, dstIp); return (dst_id > 0 ? dst_id : sfNetworkGetBinding(config, srcIp)); } /**Add network binding to a policy * @param Ip - IP address or CIDR block to policy identified by file. * @param fileName - name of configuration file * * @returns 0 if successful, -1 otherwise. */ int sfNetworkAddBinding( tSfPolicyConfig *config, sfcidr_t* Ip, char *fileName ) { tSfPolicyId *policyId; int iRet; if ((config == NULL) || (Ip == NULL) || (fileName == NULL)) return -1; if ((policyId = calloc(sizeof(tSfPolicyId), 1)) == NULL) { return -1; } //create a policyId *policyId = sfPolicyAdd(config, fileName); if ( NotBound(*policyId) ) { free(policyId); return -1; } iRet = sfrt_insert(Ip, (unsigned char)Ip->bits, (void *)policyId, RT_FAVOR_SPECIFIC, config->netBindTable); //DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"Added vlandId %d, file %s, policyId: %d\n", vlanId, fileName, policyId);); if (iRet) { free(policyId); return -1; } return 0; } unsigned int sfNetworkGetBinding( tSfPolicyConfig *config, sfaddr_t* ip ) { tSfPolicyId *policyId = NULL; if ((void *)ip == NULL) return config->defaultPolicyId; policyId = (tSfPolicyId *)sfrt_lookup(ip, config->netBindTable); if (!policyId) { return config->defaultPolicyId; } return *policyId; } void sfNetworkDeleteBinding( tSfPolicyConfig *config, sfaddr_t* ip ) { tSfPolicyId *policyId; if ((void *)ip == NULL) return; policyId = (tSfPolicyId *)sfrt_lookup(ip, config->netBindTable); if (!policyId) return; //TBD - delete function is not provided in sfrt.c sfPolicyDelete(config, *policyId); } //Move to sfDynArray.c/h /**Dynamic array bound checks. If index is greater than maxElement then realloc like operation * is performed. * @param dynArray - dynamic array * @param index - 0 based. Index of element that will be accessed by application either as rvalue or lvalue. * @maxElements - Number of elements already allocated in dynArray. 0 value means no elements are allocated * and therefore dynArray[0] will cause memory allocation. */ int sfDynArrayCheckBounds ( void ** dynArray, unsigned int index, unsigned int *maxElements ) { void *ppTmp = NULL; if (index >= *maxElements) { //expand the array ppTmp = calloc(index+POLICY_ALLOCATION_CHUNK, sizeof(void *)); if (!(ppTmp)) { return -1; } if (*maxElements) { memcpy(ppTmp, *dynArray, sizeof(void*)*(*maxElements)); free(*dynArray); } *dynArray = ppTmp; *maxElements = index + POLICY_ALLOCATION_CHUNK; } return 0; } snort-2.9.20/src/sfutil/sf_textlog.h0000644000175000017500000000634614241077253015533 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file sf_textlog.h * @author Russ Combs * @date Fri Jun 27 10:34:37 2003 * * @brief declares buffered text stream for logging * * Declares a TextLog_*() api for buffered logging. This allows * relatively painless transition from fprintf(), fwrite(), etc. * to a buffer that is formatted in memory and written with one * fwrite(). * * Additionally, the file is capped at a maximum size. Beyond * that, the file is closed, renamed, and reopened. The current * file always has the same name. Old files are renamed to that * name plus a timestamp. */ #ifndef _SF_TEXT_LOG_H #define _SF_TEXT_LOG_H #include #include #include #define K_BYTES (1024) #define M_BYTES (K_BYTES*K_BYTES) #define G_BYTES (K_BYTES*M_BYTES) /* * DO NOT ACCESS STRUCT MEMBERS DIRECTLY * EXCEPT FROM WITHIN THE IMPLEMENTATION! */ typedef struct _TextLog { /* private: */ /* file attributes: */ FILE* file; char* name; size_t size; size_t maxFile; time_t last; /* buffer attributes: */ unsigned int pos; unsigned int maxBuf; char buf[1]; } TextLog; TextLog* TextLog_Init ( const char* name, unsigned int maxBuf, size_t maxFile ); void TextLog_Term (TextLog* this); bool TextLog_Putc(TextLog*, char); bool TextLog_Quote(TextLog*, const char*); bool TextLog_Write(TextLog*, const char*, int len); bool TextLog_Print(TextLog*, const char* format, ...); bool TextLog_PrintUnicode(TextLog*, uint8_t *, uint32_t, uint8_t); bool TextLog_Flush(TextLog*); /*------------------------------------------------------------------- * helper functions *------------------------------------------------------------------- */ static inline int TextLog_Tell (TextLog* this) { return this->pos; } static inline int TextLog_Avail (TextLog* this) { return this->maxBuf - this->pos - 1; } static inline void TextLog_Reset (TextLog* this) { this->pos = 0; this->buf[this->pos] = '\0'; } static inline bool TextLog_NewLine (TextLog* this) { return TextLog_Putc(this, '\n'); } static inline bool TextLog_Puts (TextLog* this, const char* str) { return TextLog_Write(this, str, strlen(str)); } #endif /* _SF_TEXT_LOG_H */ snort-2.9.20/src/sfutil/sf_ip.h0000644000175000017500000004325214241077241014447 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** Adam Keeton ** Kevin Liu * ** $ID: $ ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Adam Keeton * sf_ip.h * 11/17/06 */ #ifndef SF_IP_H #define SF_IP_H #ifndef WIN32 #include #include #include #include #endif #ifdef WIN32 #include #endif #include "snort_debug.h" /* for inline definition */ /* define SFIP_ROBUST to check pointers passed into the sfip libs. * Robustification should not be enabled if the client code is trustworthy. * Namely, if pointers are checked once in the client, or are pointers to * data allocated on the stack, there's no need to check them again here. * The intention is to prevent the same stack-allocated variable from being * checked a dozen different times. */ #define SFIP_ROBUST #ifdef SFIP_ROBUST #define ARG_CHECK1(a, z) if(!a) return z; #define ARG_CHECK2(a, b, z) if(!a || !b) return z; #define ARG_CHECK3(a, b, c, z) if(!a || !b || !c) return z; #elif defined(DEBUG) #define ARG_CHECK1(a, z) assert(a); #define ARG_CHECK2(a, b, z) assert(a); assert(b); #define ARG_CHECK3(a, b, c, z) assert(a); assert(b); assert(c); #else #define ARG_CHECK1(a, z) #define ARG_CHECK2(a, b, z) #define ARG_CHECK3(a, b, c, z) #endif #ifndef WIN32 #if !defined(s6_addr8) #define s6_addr8 __u6_addr.__u6_addr8 #endif #if !defined(s6_addr16) #define s6_addr16 __u6_addr.__u6_addr16 #endif #if !defined(s6_addr32) #define s6_addr32 __u6_addr.__u6_addr32 #endif #ifdef _WIN32 #pragma pack(push,1) #endif struct _sfaddr { struct in6_addr ip; uint16_t family; # define ia8 ip.s6_addr # define ia16 ip.s6_addr16 # define ia32 ip.s6_addr32 #ifdef _WIN32 }; #pragma pack(pop) #else } __attribute__((__packed__)); #endif typedef struct _sfaddr sfaddr_t; #ifdef _WIN32 #pragma pack(push,1) #endif struct _ip { sfaddr_t addr; uint16_t bits; # define ip8 addr.ip.s6_addr # define ip16 addr.ip.s6_addr16 # define ip32 addr.ip.s6_addr32 # define ip_family addr.family #ifdef _WIN32 }; #pragma pack(pop) #else } __attribute__((__packed__)); #endif typedef struct _ip sfcidr_t; #else // WIN32 Build #if !defined(s6_addr8) #define s6_addr8 u.u6_addr8 #endif #if !defined(s6_addr16) #define s6_addr16 u.u6_addr16 #endif #if !defined(s6_addr32) #define s6_addr32 u.u6_addr32 #endif struct sf_in6_addr { union { uint8_t u6_addr8[16]; uint16_t u6_addr16[8]; uint32_t u6_addr32[4]; } in6_u; }; #pragma pack(push,1) struct _sfaddr { struct in6_addr ip; uint16_t family; # define ia8 ip.s6_addr # define ia16 ip.s6_addr16 # define ia32 ip.s6_addr32 }; typedef struct _sfaddr sfaddr_t; struct _ip { sfaddr_t addr; uint16_t bits; # define ip8 addr.ip.s6_addr # define ip16 addr.ip.s6_addr16 # define ip32 addr.ip.s6_addr32 # define ip_family addr.family }; typedef struct _ip sfcidr_t; #pragma pack(pop) #endif // WIN32 typedef enum _return_values { SFIP_SUCCESS=0, SFIP_FAILURE, SFIP_LESSER, SFIP_GREATER, SFIP_EQUAL, SFIP_ARG_ERR, SFIP_CIDR_ERR, SFIP_INET_PARSE_ERR, SFIP_INVALID_MASK, SFIP_ALLOC_ERR, SFIP_CONTAINS, SFIP_NOT_CONTAINS, SFIP_DUPLICATE, /* Tried to add a duplicate variable name to table */ SFIP_LOOKUP_FAILURE, /* Failed to lookup a variable from the table */ SFIP_UNMATCHED_BRACKET, /* IP lists that are missing a closing bracket */ SFIP_NOT_ANY, /* For !any */ SFIP_CONFLICT, /* For IP conflicts in IP lists */ SFIP_INVALID_VAR /* variable definition is invalid */ } SFIP_RET; /* IP allocations and setting ******************************************/ /* Parses "src" and stores results in "dst" */ /* If the conversion is invalid, returns SFIP_FAILURE */ SFIP_RET sfaddr_pton(const char *src, sfaddr_t *dst); SFIP_RET sfip_pton(const char *src, sfcidr_t *dst); /* Allocate IP address from a character array describing the IP */ sfcidr_t *sfip_alloc(const char *ip, SFIP_RET *status); /* Frees an sfcidr_t */ void sfip_free(sfcidr_t *ip); /* Allocate IP address from a character array describing the IP */ sfaddr_t *sfaddr_alloc(const char *ip, SFIP_RET *status); /* Frees an sfaddr_t */ void sfaddr_free(sfaddr_t *ip); /* Allocate IP address from an array of integers. The array better be * long enough for the given family! */ sfaddr_t *sfip_alloc_raw(void *ip, int family, SFIP_RET *status); /* Sets existing IP, "dst", to a raw source IP (4 or 16 bytes, * according to family) */ SFIP_RET sfip_set_raw(sfaddr_t *dst, const void *src, int src_family); /* Sets existing IP, "dst", to be source IP, "src" */ #define sfip_set_ip(dst, src) *(dst) = *(src) /* Obfuscates an IP */ void sfip_obfuscate(sfcidr_t *ob, sfaddr_t *ip); /* Member-access *******************************************************/ #define sfip_get_ip4_value(x) ((x)->ip32[3]) #define sfaddr_get_ip4_value(x) ((x)->ia32[3]) #define sfip_get_ip4_ptr(x) (&(x)->ip32[3]) #define sfip_get_ip6_ptr(x) ((x)->ip32) #define sfip_get_ptr(x) (((x)->ip_family == AF_INET) ? &(x)->ip32[3] : (x)->ip32) #define sfaddr_get_ip4_ptr(x) (&(x)->ia32[3]) #define sfaddr_get_ip6_ptr(x) ((x)->ia32) #define sfaddr_get_ptr(x) (((x)->family == AF_INET) ? &(x)->ia32[3] : (x)->ia32) /* Returns the family of "ip", either AF_INET or AF_INET6 */ /* XXX This is a performance critical function, * need to determine if it's safe to not check these pointers */ /* ARG_CHECK1(ip, 0); */ #define sfaddr_family(x) ((x)->family) #define sfip_family(x) ((x)->ip_family) /* Returns the number of bits used for masking "ip" */ static inline unsigned char sfip_bits(const sfcidr_t *ip) { ARG_CHECK1(ip, 0); return (unsigned char)ip->bits; } static inline void sfip_set_bits(sfcidr_t *p, int bits) { if(!p) return; if(bits < 0 || bits > 128) return; p->bits = bits; } /* Returns the raw IP address as an in6_addr */ /*inline struct in6_addr sfip_to_raw(sfcidr_t *); */ /* IP Comparisons ******************************************************/ /* Check if ip is contained within the network specified by net */ /* Returns SFIP_EQUAL if so */ SFIP_RET sfip_contains(const sfcidr_t *net, const sfaddr_t *ip); /* Returns 1 if the IP is non-zero. 0 otherwise */ /* XXX This is a performance critical function, \ * need to determine if it's safe to not check these pointers */\ static inline int sfraw_is_set(const struct in6_addr *addr) { /* ARG_CHECK1(ip, -1); */ return (addr->s6_addr32[3] || addr->s6_addr32[0] || addr->s6_addr32[1] || addr->s6_addr16[4] || (addr->s6_addr16[5] && addr->s6_addr16[5] != 0xFFFF)) ? 1 : 0; } static inline int sfaddr_is_set(const sfaddr_t *addr) { /* ARG_CHECK1(ip, -1); */ return ((addr->family == AF_INET && addr->ia32[3]) || (addr->family == AF_INET6 && (addr->ia32[0] || addr->ia32[1] || addr->ia32[3] || addr->ia16[4] || (addr->ia16[5] && addr->ia16[5] != 0xFFFF)))) ? 1 : 0; } static inline int sfip_is_set(const sfcidr_t *ip) { /* ARG_CHECK1(ip, -1); */ return (sfaddr_is_set(&ip->addr) || ((ip->ip_family == AF_INET || ip->ip_family == AF_INET6) && ip->bits != 128)) ? 1 : 0; } /* Return 1 if the IP is a loopback IP */ int sfip_is_loopback(const sfaddr_t *ip); /* Returns 1 if the IPv6 address appears mapped. 0 otherwise. */ static inline int sfip_ismapped(const sfaddr_t *ip) { ARG_CHECK1(ip, 0); return (ip->ia32[0] || ip->ia32[1] || ip->ia16[4] || (ip->ia16[5] != 0xffff && ip->ia16[5])) ? 0 : 1; } /* Support function for sfip_compare */ static inline SFIP_RET _ip4_cmp(u_int32_t ip1, u_int32_t ip2) { u_int32_t hip1 = htonl(ip1); u_int32_t hip2 = htonl(ip2); if(hip1 < hip2) return SFIP_LESSER; if(hip1 > hip2) return SFIP_GREATER; return SFIP_EQUAL; } /* Support function for sfip_compare */ static inline SFIP_RET _ip6_cmp(const sfaddr_t *ip1, const sfaddr_t *ip2) { SFIP_RET ret; const struct in6_addr p1 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip1); const struct in6_addr p2 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip2); /* XXX * Argument are assumed trusted! * This function is presently only called by sfip_compare * on validated pointers. * XXX */ if( (ret = _ip4_cmp(p1.s6_addr32[0], p2.s6_addr32[0])) != SFIP_EQUAL) return ret; if( (ret = _ip4_cmp(p1.s6_addr32[1], p2.s6_addr32[1])) != SFIP_EQUAL) return ret; if( (ret = _ip4_cmp(p1.s6_addr32[2], p2.s6_addr32[2])) != SFIP_EQUAL) return ret; if( (ret = _ip4_cmp(p1.s6_addr32[3], p2.s6_addr32[3])) != SFIP_EQUAL) return ret; return ret; } /* Compares two IPs * Returns SFIP_LESSER, SFIP_EQUAL, SFIP_GREATER, if ip1 is less than, equal to, * or greater than ip2 In the case of mismatched families, the IPv4 address * is converted to an IPv6 representation. */ /* XXX-IPv6 Should add version of sfip_compare that just tests equality */ static inline SFIP_RET sfip_compare(const sfaddr_t *ip1, const sfaddr_t *ip2) { int f1,f2; ARG_CHECK2(ip1, ip2, SFIP_ARG_ERR); /* This is being done because at some points in the existing Snort code, * an unset IP is considered to match anything. Thus, if either IP is not * set here, it's considered equal. */ if(!sfaddr_is_set(ip1) || !sfaddr_is_set(ip2)) return SFIP_EQUAL; f1 = sfaddr_family(ip1); f2 = sfaddr_family(ip2); if(f1 == AF_INET && f2 == AF_INET) { return _ip4_cmp(sfaddr_get_ip4_value(ip1), sfaddr_get_ip4_value(ip2)); } return _ip6_cmp(ip1, ip2); } /* Compares two CIDRs * Returns SFIP_LESSER, SFIP_EQUAL, SFIP_GREATER, if ip1 is less than, equal to, * or greater than ip2 In the case of mismatched families, the IPv4 address * is converted to an IPv6 representation. */ static inline SFIP_RET sfip_cidr_compare(const sfcidr_t* ip1, const sfcidr_t *ip2) { SFIP_RET ret = sfip_compare(&ip1->addr, &ip2->addr); if(SFIP_EQUAL == ret) { if(ip1->bits < ip2->bits) return SFIP_LESSER; if(ip1->bits > ip2->bits) return SFIP_GREATER; } return ret; } /* Compares two IPs * Returns SFIP_LESSER, SFIP_EQUAL, SFIP_GREATER, if ip1 is less than, equal to, * or greater than ip2 In the case of mismatched families, the IPv4 address * is converted to an IPv6 representation. */ /* XXX-IPv6 Should add version of sfip_compare that just tests equality */ static inline SFIP_RET sfip_compare_unset(const sfaddr_t *ip1, const sfaddr_t *ip2) { int f1,f2; ARG_CHECK2(ip1, ip2, SFIP_ARG_ERR); /* This is to handle the special case when one of the values being * unset is considered to match nothing. This is the opposite of * sfip_compare(), defined above. Thus, if either IP is not * set here, it's considered not equal. */ if(!sfaddr_is_set(ip1) || !sfaddr_is_set(ip2)) return SFIP_FAILURE; f1 = sfaddr_family(ip1); f2 = sfaddr_family(ip2); if(f1 == AF_INET && f2 == AF_INET) { return _ip4_cmp(sfaddr_get_ip4_value(ip1), sfaddr_get_ip4_value(ip2)); } return _ip6_cmp(ip1, ip2); } static inline int sfip_fast_lt4(const sfaddr_t *ip1, const sfaddr_t *ip2) { return sfaddr_get_ip4_value(ip1) < sfaddr_get_ip4_value(ip2); } static inline int sfip_fast_gt4(const sfaddr_t *ip1, const sfaddr_t *ip2) { return sfaddr_get_ip4_value(ip1) > sfaddr_get_ip4_value(ip2); } static inline int sfip_fast_eq4(const sfaddr_t *ip1, const sfaddr_t *ip2) { return sfaddr_get_ip4_value(ip1) == sfaddr_get_ip4_value(ip2); } static inline int sfip_fast_lt6(const sfaddr_t *ip1, const sfaddr_t *ip2) { const struct in6_addr p1 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip1); const struct in6_addr p2 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip2); if(p1.s6_addr32[0] < p2.s6_addr32[0]) return 1; else if(p1.s6_addr32[0] > p2.s6_addr32[0]) return 0; if(p1.s6_addr32[1] < p2.s6_addr32[1]) return 1; else if(p1.s6_addr32[1] > p2.s6_addr32[1]) return 0; if(p1.s6_addr32[2] < p2.s6_addr32[2]) return 1; else if(p1.s6_addr32[2] > p2.s6_addr32[2]) return 0; if(p1.s6_addr32[3] < p2.s6_addr32[3]) return 1; else if(p1.s6_addr32[3] > p2.s6_addr32[3]) return 0; return 0; } static inline int sfip_fast_gt6(const sfaddr_t *ip1, const sfaddr_t *ip2) { const struct in6_addr p1 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip1); const struct in6_addr p2 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip2); if(p1.s6_addr32[0] > p2.s6_addr32[0]) return 1; else if(p1.s6_addr32[0] < p2.s6_addr32[0]) return 0; if(p1.s6_addr32[1] > p2.s6_addr32[1]) return 1; else if(p1.s6_addr32[1] < p2.s6_addr32[1]) return 0; if(p1.s6_addr32[2] > p2.s6_addr32[2]) return 1; else if(p1.s6_addr32[2] < p2.s6_addr32[2]) return 0; if(p1.s6_addr32[3] > p2.s6_addr32[3]) return 1; else if(p1.s6_addr32[3] < p2.s6_addr32[3]) return 0; return 0; } static inline int sfip_fast_eq6(const sfaddr_t *ip1, const sfaddr_t *ip2) { const struct in6_addr p1 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip1); const struct in6_addr p2 = *(struct in6_addr *)sfaddr_get_ip6_ptr(ip2); if(p1.s6_addr32[0] != p2.s6_addr32[0]) return 0; if(p1.s6_addr32[1] != p2.s6_addr32[1]) return 0; if(p1.s6_addr32[2] != p2.s6_addr32[2]) return 0; if(p1.s6_addr32[3] != p2.s6_addr32[3]) return 0; return 1; } /* Checks if ip2 is equal to ip1 or contained within the CIDR ip1 */ static inline int sfip_fast_cont4(const sfcidr_t *ip1, const sfaddr_t *ip2) { uint32_t shift = 128 - sfip_bits(ip1); uint32_t ip = ntohl(sfaddr_get_ip4_value(ip2)); uint32_t ip3 = ntohl(sfip_get_ip4_value(ip1)); ip >>= shift; ip <<= shift; if(ip3 == 0) return 1; return (ip3 == ip); } /* Checks if ip2 is equal to ip1 or contained within the CIDR ip1 */ static inline int sfip_fast_cont6(const sfcidr_t *ip1, const sfaddr_t *ip2) { uint32_t ip; int i, bits = sfip_bits(ip1); int words = bits / 32; bits = 32 - (bits % 32); for ( i = 0; i < words; i++ ) { if ( ip1->ip32[i] != ip2->ia32[i] ) return 0; } if ( bits == 32 ) return 1; ip = ntohl(ip2->ia32[i]); ip >>= bits; ip <<= bits; return ntohl(ip1->ip32[i]) == ip; } /* Compares two IPs * Returns 1 for equal and 0 for not equal */ static inline int sfip_fast_equals_raw(const sfaddr_t *ip1, const sfaddr_t *ip2) { int f1,f2; ARG_CHECK2(ip1, ip2, 0); f1 = sfaddr_family(ip1); f2 = sfaddr_family(ip2); if(f1 == AF_INET) { if(f2 != AF_INET) return 0; if (sfip_fast_eq4(ip1, ip2)) return 1; } else if(f1 == AF_INET6) { if(f2 != AF_INET6) return 0; if (sfip_fast_eq6(ip1, ip2)) return 1; } return 0; } /******************************************************************** * Function: sfip_is_private() * * Checks if the address is local * * Arguments: * sfcidr_t * - IP address to check * * Returns: * 1 if the IP is in local network * 0 otherwise * ********************************************************************/ static inline int sfip_is_private(const sfaddr_t *ip) { ARG_CHECK1(ip, 0); /* Check the first 80 bits in an IPv6 address, and */ /* verify they're zero. If not, it's not a loopback */ if(ip->ia32[0] || ip->ia32[1] || ip->ia16[4]) return 0; if ( ip->ia16[5] == 0xffff ) { /* ::ffff: IPv4 mapped over IPv6 */ /* * 10.0.0.0 - 10.255.255.255 (10/8 prefix) * 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) * 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) * */ return ( (ip->ia8[12] == 10) ||((ip->ia8[12] == 172) && ((ip->ia8[13] & 0xf0 ) == 16)) ||((ip->ia8[12] == 192) && (ip->ia8[13] == 168)) ); } /* Check if the 3rd 32-bit int is zero */ if ( !ip->ia16[5] ) { /* ::ipv4 compatible ipv6 */ /* ::1 is the IPv6 loopback */ return ( (ip->ia8[12] == 10) ||((ip->ia8[12] == 172) && ((ip->ia8[13] & 0xf0 ) == 16)) ||((ip->ia8[12] == 192) && (ip->ia8[13] == 168)) || (ip->ia32[3] == htonl(0x1)) ); } return 0; } static inline void sfaddr_copy_to_raw(struct in6_addr *dst, const sfaddr_t *src) { dst->s6_addr32[0] = src->ia32[0]; dst->s6_addr32[1] = src->ia32[1]; dst->s6_addr32[2] = src->ia32[2]; dst->s6_addr32[3] = src->ia32[3]; } #define sfip_equals(x,y) (sfip_compare(&x, &y) == SFIP_EQUAL) #define sfip_not_equals !sfip_equals #define sfip_clear(x) memset(x, 0, 16) /* Printing ************************************************************/ /* Uses a static buffer to return a string representation of the IP */ char *sfip_to_str(const sfaddr_t *ip); #define sfip_ntoa(x) sfip_to_str(x) void sfip_raw_ntop(int family, const void *ip_raw, char *buf, int bufsize); void sfip_ntop(const sfaddr_t *ip, char *buf, int bufsize); /* Conversions *********************************************************/ SFIP_RET sfip_convert_ip_text_to_binary( const int, const char *src, void *dst ); #endif /* SF_IP_H */ snort-2.9.20/src/sfutil/segment_mem.h0000644000175000017500000000252514241077232015645 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2011-2013 Sourcefire, Inc. ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** 8/7/2011 - Initial implementation ... Hui Cao */ #ifndef _SFSHARE_MEMEORY_H_ #define _SFSHARE_MEMEORY_H_ #include #include "sf_types.h" typedef uint32_t MEM_OFFSET; int segment_meminit(uint8_t*, size_t); MEM_OFFSET segment_malloc ( size_t size ); void segment_free (MEM_OFFSET ptr ); MEM_OFFSET segment_calloc ( size_t num, size_t size ); size_t segment_unusedmem(); void * segment_basePtr(); #endif snort-2.9.20/src/sfutil/getopt.h0000644000175000017500000000232614241077214014646 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SNORT_GETOPT_H_ #define _SNORT_GETOPT_H_ #ifdef SNORT_GETOPT #define _next_char(string) (char)(*(string+1)) extern char * optarg; extern int optind; int getopt(int, char**, char*); #else #include #endif #endif /* _SNORT_GETOPT_H_ */ snort-2.9.20/src/sfutil/sfdebug.h0000644000175000017500000000431014241077260014757 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2006-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _SFDEBUG_H_ #define _SFDEBUG_H_ #include /* ANY CHANGES MADE HERE SHOULD BE DUPLICATED TO toos/sfcontrol/sfcontrol.c */ static inline void DumpHex(FILE *fp, const uint8_t *data, unsigned len) { char str[18]; unsigned i; unsigned pos; char c; for (i=0, pos=0; i */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include /* For variadic */ #include #include /* For memset */ #include "sf_types.h" #include "sfrt_flat.h" #include "sfrt_flat_dir.h" #if SIZEOF_UNSIGNED_LONG_INT == 8 #define ARCH_WIDTH 64 #else #define ARCH_WIDTH 32 #endif typedef struct { uint32_t* adr; int bits; } IPLOOKUP; /* Create new "sub" table of 2^width entries */ static TABLE_PTR _sub_table_flat_new(dir_table_flat_t *root, uint32_t dimension, uint32_t prefill, uint32_t bit_length) { int width = root->dimensions[dimension]; int len = 1 << width; int index; dir_sub_table_flat_t *sub; TABLE_PTR sub_ptr; uint8_t *base; Entry_Value *entries_value; Entry_Len *entries_length; /* Check if creating this node will exceed the memory cap. * The symbols in the conditional (other than cap), come from the * allocs below. */ if( root->mem_cap < ( root->allocated + sizeof(dir_sub_table_flat_t) + sizeof(Entry_Value) * len + sizeof(Entry_Len) * len) || bit_length > 128) { return 0; } /* Set up the initial prefilled "sub table" */ sub_ptr = segment_malloc(sizeof(dir_sub_table_flat_t)); if(!sub_ptr) { return 0; } base = (uint8_t *)segment_basePtr(); sub = (dir_sub_table_flat_t *)(&base[sub_ptr]); /* This keeps the width readily available rather than recalculating it * from the number of entries during an insert or lookup */ sub->width = width; /* need 2^sub->width entries */ /* A "length" needs to be stored with each entry above. The length refers * to how specific the insertion that set the entry was. It is necessary * so that the entry is not overwritten by less general routing * information if "RT_FAVOR_SPECIFIC" insertions are being performed. */ sub->entries_value = segment_malloc(sizeof(MEM_OFFSET) * len); if(!sub->entries_value) { segment_free(sub_ptr); return 0; } sub->entries_length = segment_malloc(sizeof(Entry_Len) * len); if(!sub->entries_length) { segment_free(sub_ptr); return 0; } entries_value = (Entry_Value *)(&base[sub->entries_value]); entries_length = (Entry_Len *)(&base[sub->entries_length]); /* Can't use memset here since prefill is multibyte */ for(index = 0; index < len; index++) { entries_value[index] = prefill; entries_length[index] = (uint8_t)bit_length; } root->allocated += sizeof(dir_sub_table_flat_t) + sizeof(Entry_Value) * len + sizeof(Entry_Len) * len; root->cur_num++; return sub_ptr; } /* Create new dir-n-m root table with 'count' depth */ TABLE_PTR sfrt_dir_flat_new(uint32_t mem_cap, int count,...) { va_list ap; uint32_t val; int index; TABLE_PTR table_ptr; dir_table_flat_t* table; uint8_t *base; table_ptr = segment_malloc(sizeof(dir_table_flat_t)); if(!table_ptr) { return 0; } base = (uint8_t *)segment_basePtr(); table = (dir_table_flat_t *)(&base[table_ptr]); table->allocated = 0; table->dim_size = count; va_start(ap, count); for(index=0; index < count; index++) { val = va_arg(ap, int); table->dimensions[index] = val; } va_end(ap); table->mem_cap = mem_cap; table->cur_num = 0; table->sub_table = _sub_table_flat_new(table, 0, 0, 0); if(!table->sub_table) { segment_free(table_ptr); return 0; } table->allocated += sizeof(dir_table_flat_t) + sizeof(int)*count; return table_ptr; } /* Traverse "sub" tables, freeing each */ static void _sub_table_flat_free(uint32_t *allocated, SUB_TABLE_PTR sub_ptr) { int index; dir_sub_table_flat_t *sub; uint8_t *base; int len; base = (uint8_t *)segment_basePtr(); sub = (dir_sub_table_flat_t *)(&base[sub_ptr]); len = 1 << sub->width; for(index=0; index < len; index++) { /* The following condition will only be true if * this entry is a pointer */ Entry_Value *entries_value = (Entry_Value *)(&base[sub->entries_value]); Entry_Len *entries_length = (Entry_Len *)(&base[sub->entries_length]); if( !entries_value[index] && entries_length[index] ) { _sub_table_flat_free( allocated, entries_value[index] ); } } if(sub->entries_value) { /* This probably does not need to be checked * since if it was not allocated, we would have errored out * in _sub_table_flat_new */ segment_free(sub->entries_value); *allocated -= sizeof(Entry_Value) * len; } if(sub->entries_length) { /* This probably does not need to be checked * since if it was not allocated, we would have errored out * in _sub_table_flat_new */ segment_free(sub->entries_length); *allocated -= sizeof(Entry_Len) * len; } segment_free(sub_ptr); *allocated -= sizeof(dir_sub_table_flat_t); } /* Free the DIR-n-m structure */ void sfrt_dir_flat_free(TABLE_PTR tbl_ptr) { dir_table_flat_t *table; uint8_t *base; if(!tbl_ptr) { return; } base = (uint8_t *)segment_basePtr(); table = (dir_table_flat_t *)(&base[tbl_ptr]); if(table->sub_table) { _sub_table_flat_free(&table->allocated, table->sub_table); } segment_free(tbl_ptr); } static inline void _dir_fill_all(uint32_t *allocated, uint32_t index, uint32_t fill, word length, uint32_t val, SUB_TABLE_PTR sub_ptr) { dir_sub_table_flat_t *subtable; uint8_t *base; base = (uint8_t *)segment_basePtr(); subtable = (dir_sub_table_flat_t *)(&base[sub_ptr]); /* Fill entries */ for(; index < fill; index++) { /* Before overwriting this entry, verify there's not an existing * pointer ... otherwise free it to avoid a huge memory leak. */ Entry_Value *entries_value = (Entry_Value *)(&base[subtable->entries_value]); Entry_Len *entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( entries_value[index] && !entries_length[index] ) { _sub_table_flat_free(allocated, entries_value[index]); } entries_value[index] = val; entries_length[index] = (uint8_t)length; } } static inline void _dir_fill_less_specific(int index, int fill, word length, uint32_t val, SUB_TABLE_PTR sub_ptr) { dir_sub_table_flat_t *subtable; uint8_t *base; base = (uint8_t *)segment_basePtr(); subtable = (dir_sub_table_flat_t *)(&base[sub_ptr]); /* Fill entries */ for(; index < fill; index++) { /* If we encounter a pointer, and we're inserting at this level, we * automatically know that this entry refers to more specific * information. However, there might only be one more specific entry * in the entire block, meaning the rest must be filled. * * For instance, imagine a 24-8 with 1.2.3/24 -> A and 1.2.3.4/32 -> B * There will be a pointer at 1.2.3 in the first table. The second * table needs to have 255 entries pointing A, and 1 entry pointing to * B. * * Therefore, recurse to this next level. */ Entry_Value *entries_value = (Entry_Value *)(&base[subtable->entries_value]); Entry_Len *entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( entries_value[index] && !entries_length[index] ) { dir_sub_table_flat_t *next = (dir_sub_table_flat_t*)(&base[entries_value[index]]); _dir_fill_less_specific(0, 1 << next->width, length, val, entries_value[index]); } else if(length >= (unsigned)entries_length[index]) { entries_value[index] = val; entries_length[index] = (char)length; } } } static inline int64_t _dir_update_info(int index, int fill, word length, uint32_t val, SUB_TABLE_PTR sub_ptr, updateEntryInfoFunc updateEntry, INFO *data) { dir_sub_table_flat_t *subtable; uint8_t *base; int64_t bytesAllocatedTotal = 0; base = (uint8_t *)segment_basePtr(); subtable = (dir_sub_table_flat_t *)(&base[sub_ptr]); /* Fill entries */ for(; index < fill; index++) { /* If we encounter a pointer, and we're inserting at this level, we * automatically know that this entry refers to more specific * information. However, there might only be one more specific entry * in the entire block, meaning the rest must be filled. * * For instance, imagine a 24-8 with 1.2.3/24 -> A and 1.2.3.4/32 -> B * There will be a pointer at 1.2.3 in the first table. The second * table needs to have 255 entries pointing A, and 1 entry pointing to * B. * * Therefore, recurse to this next level. */ Entry_Value *entries_value = (Entry_Value *)(&base[subtable->entries_value]); Entry_Len *entries_length = (Entry_Len *)(&base[subtable->entries_length]); if( entries_value[index] && !entries_length[index] ) { int64_t bytesAllocated; dir_sub_table_flat_t *next = (dir_sub_table_flat_t*)(&base[entries_value[index]]); bytesAllocated = _dir_update_info(0, 1 << next->width, length, val, entries_value[index], updateEntry, data); if (bytesAllocated < 0) return bytesAllocated; else bytesAllocatedTotal += bytesAllocated; } else if(length > (unsigned)entries_length[index]) { if(entries_value[index]) { int64_t bytesAllocated; bytesAllocated = updateEntry(&data[entries_value[index]], data[val], SAVE_TO_NEW, base); if (bytesAllocated < 0) return bytesAllocated; else bytesAllocatedTotal += bytesAllocated; } entries_value[index] = val; entries_length[index] = (uint8_t)length; } else if(entries_value[index]) { int64_t bytesAllocated; bytesAllocated = updateEntry(&data[entries_value[index]], data[val], SAVE_TO_CURRENT, base); if (bytesAllocated < 0) return bytesAllocated; else bytesAllocatedTotal += bytesAllocated; } } return bytesAllocatedTotal; } /* Sub table insertion * This is called by dir_insert and recursively to find the the sub table * that should house the value "ptr" * @param ip IP address structure * @param cur_len Number of bits of the IP left at this depth * @param length Number of bits of the IP used to specify this CIDR * @param ptr Information to be associated with this IP range * @param master_table The table that describes all, returned by dir_new */ static int _dir_sub_insert(IPLOOKUP *ip, int length, int cur_len, INFO ptr, int current_depth, int behavior, SUB_TABLE_PTR sub_ptr, dir_table_flat_t *root_table,updateEntryInfoFunc updateEntry, INFO *data) { word index; uint32_t fill; uint8_t *base = (uint8_t *)segment_basePtr(); dir_sub_table_flat_t *sub_table = (dir_sub_table_flat_t *)(&base[sub_ptr]); { uint32_t local_index, i; /* need to handle bits usage across multiple 32bit vals within IPv6. */ if (ip->bits < 32 ) { i=0; } else if (ip->bits < 64) { i=1; } else if (ip->bits < 96) { i=2; } else { i=3; } local_index = ip->adr[i] << (ip->bits %32); index = local_index >> (sizeof(local_index)*8 - sub_table->width); } /* Check if this is the last table to traverse to */ if(sub_table->width >= cur_len) { /* Calculate how many entries need to be filled * in this table. If the table is 24 bits wide, and the entry * is 20 bytes long, 2^4 entries need to be filled. */ fill = 1 << (sub_table->width - cur_len); index = (index >> (sub_table->width - cur_len)) << (sub_table->width - cur_len); fill += index; /* Favor most recent CIDR */ if(behavior == RT_FAVOR_TIME) { _dir_fill_all(&root_table->allocated, index, fill, length, (word)ptr, sub_ptr); } /* Fill over less specific CIDR */ else if (behavior == RT_FAVOR_SPECIFIC) { _dir_fill_less_specific(index, fill, length, (word)ptr, sub_ptr); } else if (behavior == RT_FAVOR_ALL) { int64_t bytesAllocated; bytesAllocated = _dir_update_info(index, fill, length, (word)ptr, sub_ptr, updateEntry, data); if (bytesAllocated < 0) return MEM_ALLOC_FAILURE; root_table->allocated += (uint32_t)bytesAllocated; if( root_table->mem_cap < root_table->allocated) return MEM_ALLOC_FAILURE; } } /* Need to traverse to a sub-table */ else { Entry_Value *entries_value = (Entry_Value *)(&base[sub_table->entries_value]); Entry_Len *entries_length = (Entry_Len *)(&base[sub_table->entries_length]); /* Check if we need to alloc a new sub table. * If next_sub was 0/NULL, there's no entry at this index * If the length is non-zero, there is an entry */ if(!entries_value[index] || entries_length[index]) { if( root_table->dim_size <= current_depth ) { return RT_INSERT_FAILURE; } entries_value[index] = (word) _sub_table_flat_new(root_table, current_depth+1, (word) entries_value[index], entries_length[index]); entries_length[index] = 0; if(!entries_value[index]) { return MEM_ALLOC_FAILURE; } } /* Recurse to next level. Rightshift off appropriate number of * bits and update the length accordingly. */ ip->bits += sub_table->width; return (_dir_sub_insert(ip, length, cur_len - sub_table->width, ptr, current_depth+1, behavior, entries_value[index], root_table, updateEntry, data)); } return RT_SUCCESS; } /* Insert entry into DIR-n-m tables * @param ip IP address structure * @param len Number of bits of the IP used for lookup * @param ptr Information to be associated with this IP range * @param master_table The table that describes all, returned by dir_new */ int sfrt_dir_flat_insert(uint32_t* adr, int numAdrDwords, int len, word data_index, int behavior, TABLE_PTR table_ptr, updateEntryInfoFunc updateEntry, INFO *data) { dir_table_flat_t *root; uint8_t *base; uint32_t h_adr[4]; IPLOOKUP iplu; iplu.adr = h_adr; iplu.bits = 0; base = (uint8_t *)segment_basePtr(); root = (dir_table_flat_t *)(&base[table_ptr]); /* Validate arguments */ if(!root || !root->sub_table) { return DIR_INSERT_FAILURE; } h_adr[0] = ntohl(adr[0]); if (len > 96) { h_adr[1] = ntohl(adr[1]); h_adr[2] = ntohl(adr[2]); h_adr[3] = ntohl(adr[3]); } else if (len > 64) { h_adr[1] = ntohl(adr[1]); h_adr[2] = ntohl(adr[2]); } else if (len > 32) { h_adr[1] = ntohl(adr[1]); } /* Find the sub table in which to insert */ return _dir_sub_insert(&iplu, len, len, data_index, 0, behavior, root->sub_table, root, updateEntry, data); } /* Traverse sub tables looking for match */ /* Called by dir_lookup and recursively */ static tuple_flat_t _dir_sub_flat_lookup(IPLOOKUP *ip, TABLE_PTR table_ptr) { word index; uint8_t *base = (uint8_t *)segment_basePtr(); Entry_Value *entries_value; Entry_Len *entries_length; dir_sub_table_flat_t *table = (dir_sub_table_flat_t *)(&base[table_ptr]); { uint32_t local_index, i; /* need to handle bits usage across multiple 32bit vals within IPv6. */ if (ip->bits < 32 ) { i=0; } else if (ip->bits < 64) { i=1; } else if (ip->bits < 96) { i=2; } else { i=3; } local_index = ip->adr[i] << (ip->bits %32); index = local_index >> (sizeof(local_index)*8 - table->width); } entries_value = (Entry_Value *)(&base[table->entries_value]); entries_length = (Entry_Len *)(&base[table->entries_length]); if( !entries_value[index] || entries_length[index] ) { tuple_flat_t ret; ret.index = entries_value[index]; ret.length = (word)entries_length[index]; return ret; } ip->bits += table->width; return _dir_sub_flat_lookup( ip, entries_value[index] ); } /* Lookup information associated with the value "ip" */ tuple_flat_t sfrt_dir_flat_lookup(uint32_t* adr, int numAdrDwords, TABLE_PTR table_ptr) { dir_table_flat_t *root; uint8_t *base = (uint8_t *)segment_basePtr(); uint32_t h_adr[4]; int i; IPLOOKUP iplu; iplu.adr = h_adr; iplu.bits = 0; if(!table_ptr ) { tuple_flat_t ret = { 0, 0 }; return ret; } root = (dir_table_flat_t *)(&base[table_ptr]); if(!root->sub_table) { tuple_flat_t ret = { 0, 0 }; return ret; } for (i = 0; i < numAdrDwords; i++) { h_adr[i] = ntohl(adr[i]); } return _dir_sub_flat_lookup(&iplu, root->sub_table); } uint32_t sfrt_dir_flat_usage(TABLE_PTR table_ptr) { dir_table_flat_t *table; uint8_t *base; if(!table_ptr) { return 0; } base = (uint8_t *)segment_basePtr(); table = (dir_table_flat_t *)(&base[table_ptr]); return ((dir_table_flat_t*)(table))->allocated; } snort-2.9.20/src/sfutil/sf_vartable.h0000644000175000017500000000351014241077255015635 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 1998-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Adam Keeton * sf_vartable.h * 11/17/06 * * Library for implementing a variable table. * All API calls have the prefix "sfvt". */ #ifndef SF_VARTABLE_H #define SF_VARTABLE_H #include "ipv6_port.h" #include "sf_ipvar.h" /* Allocates new variable table */ vartable_t * sfvt_alloc_table(void); void sfvt_free_table(vartable_t *table); /* Adds the variable described by "str" to the table "table" */ SFIP_RET sfvt_add_str(vartable_t *table, char *str, sfip_var_t **); SFIP_RET sfvt_define(vartable_t *table, char *name, char *value); /* Adds the variable described by "str" to the variable "dst", * using the vartable for looking variables used within "str" */ SFIP_RET sfvt_add_to_var(vartable_t *table, sfip_var_t *dst, char *src); /* Looks up a variable from the table using the name as the key */ sfip_var_t *sfvt_lookup_var(vartable_t *table, char *name); /* Prints a table's contents */ void sfvt_print(FILE *f, vartable_t *table); #endif snort-2.9.20/src/sfutil/sfxhash.h0000644000175000017500000001702314241077341015011 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * * sfxhash.h * * generic hash table - stores and maps key + data pairs * (supports memcap and automatic memory recovery when out of memory) * * Author: Marc Norton * */ #ifndef _SFXHASH_ #define _SFXHASH_ #include #include #include #include "sfmemcap.h" #include "sfhashfcn.h" /* * ERROR DEFINES */ #define SFXHASH_NOMEM -2 #define SFXHASH_ERR -1 #define SFXHASH_OK 0 #define SFXHASH_INTABLE 1 #define SFXHASH_PENDING 2 /** * HASH NODE */ typedef struct _sfxhash_node { struct _sfxhash_node * gnext, * gprev; /// global node list - used for ageing nodes struct _sfxhash_node * next, * prev; /// row node list int rindex; /// row index of table this node belongs to. void * key; /// Pointer to the key. void * data; /// Pointer to the users data, this is not copied ! } SFXHASH_NODE; typedef int (*SFXHASH_FREE_FCN)( void * key, void * data ); /** * SFGX HASH Table */ typedef struct _sfxhash { SFHASHFCN * sfhashfcn; /// hash function int keysize; /// bytes in key, if <= 0 -> keys are strings int datasize; /// bytes in key, if == 0 -> user data SFXHASH_NODE ** table; /// array of node ptr's */ unsigned nrows; /// # rows int the hash table use a prime number 211, 9871 unsigned count; /// total # nodes in table unsigned crow; /// findfirst/next row in table unsigned pad; SFXHASH_NODE * cnode; /// findfirst/next node ptr int splay; /// whether to splay nodes with same hash bucket unsigned max_nodes; ///maximum # of nodes within a hash MEMCAP mc; unsigned overhead_bytes; /// # of bytes that will be unavailable for nodes inside the table unsigned overhead_blocks; /// # of blocks consumed by the table unsigned find_fail; unsigned find_success; SFXHASH_NODE * ghead, * gtail; /// global - root of all nodes allocated in table SFXHASH_NODE * fhead, * ftail; /// list of free nodes, which are recyled SFXHASH_NODE * gnode; /* gfirst/gnext node ptr */ int recycle_nodes; /// recycle nodes. Nodes are not freed, but are used for subsequent new nodes /**Automatic Node Recover (ANR): When number of nodes in hash is equal to max_nodes, remove the least recently * used nodes and use it for the new node. anr_tries indicates # of ANR tries.*/ unsigned anr_tries; unsigned anr_count; /// # ANR ops performaed int anr_flag; /// 0=off, !0=on unsigned free_count; /// total # nodes in table SFXHASH_FREE_FCN anrfree; SFXHASH_FREE_FCN usrfree; } SFXHASH; /* * HASH PROTOTYPES */ int sfxhash_calcrows(int num); SFXHASH * sfxhash_new( unsigned nrows, int keysize, int datasize, unsigned long memcap, int anr_flag, SFXHASH_FREE_FCN anrfunc, SFXHASH_FREE_FCN usrfunc, int recycle_flag ); void sfxhash_set_max_nodes( SFXHASH *h, int max_nodes ); void sfxhash_delete( SFXHASH * h ); int sfxhash_make_empty(SFXHASH *); int sfxhash_add ( SFXHASH * h, void * key, void * data ); SFXHASH_NODE * sfxhash_get_node( SFXHASH * t, const void * key ); int sfxhash_remove( SFXHASH * h, void * key ); /*! * Get the # of Nodes in HASH the table * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_count( SFXHASH * t ) { return t->count; } /*! * Get the # of Nodes in HASH the table * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_total_count( SFXHASH * t ) { return t->count + t->anr_count; } /*! * Get the # auto recovery * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_anr_count( SFXHASH * t ) { return t->anr_count; } /*! * Get the # finds * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_find_total( SFXHASH * t ) { return t->find_success + t->find_fail; } /*! * Get the # unsucessful finds * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_find_fail( SFXHASH * t ) { return t->find_fail; } /*! * Get the # sucessful finds * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_find_success( SFXHASH * t ) { return t->find_success; } /*! * Get the # of overhead bytes * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_overhead_bytes( SFXHASH * t ) { return t->overhead_bytes; } /*! * Get the # of overhead blocks * * @param t SFXHASH table pointer * */ static inline unsigned sfxhash_overhead_blocks( SFXHASH * t ) { return t->overhead_blocks; } void * sfxhash_mru( SFXHASH * t ); void * sfxhash_lru( SFXHASH * t ); SFXHASH_NODE * sfxhash_mru_node( SFXHASH * t ); SFXHASH_NODE * sfxhash_lru_node( SFXHASH * t ); void * sfxhash_find( SFXHASH * h, void * key ); SFXHASH_NODE * sfxhash_find_node( SFXHASH * t, const void * key); SFXHASH_NODE * sfxhash_findfirst( SFXHASH * h ); SFXHASH_NODE * sfxhash_findnext ( SFXHASH * h ); SFXHASH_NODE * sfxhash_ghead( SFXHASH * h ); SFXHASH_NODE * sfxhash_gnext( SFXHASH_NODE * n ); void sfxhash_gmovetofront( SFXHASH *t, SFXHASH_NODE * hnode ); void sfxhash_splaymode( SFXHASH * h, int mode ); void * sfxhash_alloc( SFXHASH * t, unsigned nbytes ); void sfxhash_free( SFXHASH * t, void * p ); int sfxhash_free_node(SFXHASH *t, SFXHASH_NODE *node); /* sfxhash_free_anr_lru frees a node from the free list or the LRU node */ int sfxhash_free_anr_lru(SFXHASH *t); /* sfxhash_free_anr frees a node from the free list */ int sfxhash_free_anr(SFXHASH *t); unsigned sfxhash_maxdepth( SFXHASH * t ); int sfxhash_set_keyops( SFXHASH *h , unsigned (*hash_fcn)( SFHASHFCN * p, unsigned char *d, int n), int (*keycmp_fcn)( const void *s1, const void *s2, size_t n)); SFXHASH_NODE *sfxhash_gfindfirst( SFXHASH * t ); SFXHASH_NODE *sfxhash_gfindnext( SFXHASH * t ); int sfxhash_add_return_data_ptr( SFXHASH * t, const void * key, void **data ); unsigned sfxhash_calc_maxmem(unsigned num_entries, unsigned entry_cost); int sfxhash_change_memcap( SFXHASH * t, unsigned long new_memcap, unsigned *max_work ); #endif snort-2.9.20/src/sfutil/sfeventq.h0000644000175000017500000000452514241077263015206 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __SF_EVENTQ_H__ #define __SF_EVENTQ_H__ #define NUM_EVENT_QUEUES 3 typedef struct s_SF_EVENTQ_NODE { void *event; struct s_SF_EVENTQ_NODE *prev; struct s_SF_EVENTQ_NODE *next; } SF_EVENTQ_NODE; typedef struct s_SF_EVENTQ { /* ** Handles the actual ordering and memory ** of the event queue and it's nodes. */ SF_EVENTQ_NODE *head; SF_EVENTQ_NODE *last; SF_EVENTQ_NODE *node_mem; char *event_mem; /* ** The reserve event allows us to allocate one extra node ** and compare against the last event in the queue to determine ** if the incoming event is a higher priority than the last ** event in the queue. */ char *reserve_event; /* ** Queue configuration */ int max_nodes; int log_nodes; int event_size; /* ** This element tracks the current number of ** nodes in the event queue. */ int cur_nodes; int cur_events; } SF_EVENTQ; SF_EVENTQ * sfeventq_new(int max_nodes, int log_nodes, int event_size); void * sfeventq_event_alloc(SF_EVENTQ *); void sfeventq_reset(SF_EVENTQ *); int sfeventq_add(SF_EVENTQ *, void *event); int sfeventq_action(SF_EVENTQ *, int (*action_func)(void *event, void *user), void *user); void sfeventq_free(SF_EVENTQ *); #endif snort-2.9.20/src/target-based/0000755000175000017500000000000014242725720014227 5ustar apoaposnort-2.9.20/src/target-based/sf_attribute_table.c0000644000175000017500000022506514242725720020247 0ustar apoapo/* A Bison parser, made by GNU Bison 3.0.5. */ /* Bison implementation for Yacc-like parsers in C Copyright (C) 1984, 1989-1990, 2000-2015, 2018 Free Software Foundation, Inc. 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 3 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, see . */ /* As a special exception, you may create a larger work that contains part or all of the Bison parser skeleton and distribute that work under terms of your choice, so long as that work isn't itself a parser generator using the skeleton or a modified version thereof as a parser skeleton. Alternatively, if you modify or redistribute the parser skeleton itself, you may (at your option) remove this special exception, which will cause the skeleton and the resulting Bison output files to be licensed under the GNU General Public License without this special exception. This special exception was added by the Free Software Foundation in version 2.2 of Bison. */ /* C LALR(1) parser skeleton written by Richard Stallman, by simplifying the original so-called "semantic" parser. */ /* All symbols defined below should begin with yy or YY, to avoid infringing on user name space. This should be done even for local variables, as they might otherwise be expanded by user macros. There are some unavoidable exceptions within include files to define necessary library symbols; they are noted "INFRINGES ON USER NAME SPACE" below. */ /* Identify Bison output. */ #define YYBISON 1 /* Bison version. */ #define YYBISON_VERSION "3.0.5" /* Skeleton name. */ #define YYSKELETON_NAME "yacc.c" /* Pure parsers. */ #define YYPURE 0 /* Push parsers. */ #define YYPUSH 0 /* Pull parsers. */ #define YYPULL 1 /* Substitute the variable and function names. */ #define yyparse sfat_parse #define yylex sfat_lex #define yyerror sfat_error #define yydebug sfat_debug #define yynerrs sfat_nerrs #define yylval sfat_lval #define yychar sfat_char /* Copy the first part of user declarations. */ #line 33 "sf_attribute_table.y" /* yacc.c:339 */ #ifdef TARGET_BASED #include #include #include "sftarget_reader.h" #include "snort_debug.h" #define YYSTACK_USE_ALLOCA 0 /* define the initial stack-sizes */ #ifdef YYMAXDEPTH #undef YYMAXDEPTH #define YYMAXDEPTH 70000 #else #define YYMAXDEPTH 70000 #endif extern ServiceClient sfat_client_or_service; extern char *sfat_grammar_error; extern int sfat_lex(); extern void sfat_error(char*); #line 99 "sf_attribute_table.c" /* yacc.c:339 */ # ifndef YY_NULLPTR # if defined __cplusplus && 201103L <= __cplusplus # define YY_NULLPTR nullptr # else # define YY_NULLPTR 0 # endif # endif /* Enabling verbose error messages. */ #ifdef YYERROR_VERBOSE # undef YYERROR_VERBOSE # define YYERROR_VERBOSE 1 #else # define YYERROR_VERBOSE 0 #endif /* In a future release of Bison, this section will be replaced by #include "sf_attribute_table.h". */ #ifndef YY_SFAT_SF_ATTRIBUTE_TABLE_H_INCLUDED # define YY_SFAT_SF_ATTRIBUTE_TABLE_H_INCLUDED /* Debug traces. */ #ifndef YYDEBUG # define YYDEBUG 0 #endif #if YYDEBUG extern int sfat_debug; #endif /* Token type. */ #ifndef YYTOKENTYPE # define YYTOKENTYPE enum yytokentype { SF_AT_COMMENT = 258, SF_AT_WHITESPACE = 259, SF_START_SNORT_ATTRIBUTES = 260, SF_END_SNORT_ATTRIBUTES = 261, SF_AT_START_MAP_TABLE = 262, SF_AT_END_MAP_TABLE = 263, SF_AT_START_ENTRY = 264, SF_AT_END_ENTRY = 265, SF_AT_START_ENTRY_ID = 266, SF_AT_END_ENTRY_ID = 267, SF_AT_START_ENTRY_VALUE = 268, SF_AT_END_ENTRY_VALUE = 269, SF_AT_START_ATTRIBUTE_TABLE = 270, SF_AT_END_ATTRIBUTE_TABLE = 271, SF_AT_START_HOST = 272, SF_AT_END_HOST = 273, SF_AT_START_HOST_IP = 274, SF_AT_END_HOST_IP = 275, SF_AT_STRING = 276, SF_AT_NUMERIC = 277, SF_AT_IPv6 = 278, SF_AT_IPv6Cidr = 279, SF_AT_START_OS = 280, SF_AT_END_OS = 281, SF_AT_START_ATTRIBUTE_VALUE = 282, SF_AT_END_ATTRIBUTE_VALUE = 283, SF_AT_START_ATTRIBUTE_ID = 284, SF_AT_END_ATTRIBUTE_ID = 285, SF_AT_START_CONFIDENCE = 286, SF_AT_END_CONFIDENCE = 287, SF_AT_START_NAME = 288, SF_AT_END_NAME = 289, SF_AT_START_VENDOR = 290, SF_AT_END_VENDOR = 291, SF_AT_START_VERSION = 292, SF_AT_END_VERSION = 293, SF_AT_START_FRAG_POLICY = 294, SF_AT_END_FRAG_POLICY = 295, SF_AT_START_STREAM_POLICY = 296, SF_AT_END_STREAM_POLICY = 297, SF_AT_START_SERVICES = 298, SF_AT_END_SERVICES = 299, SF_AT_START_SERVICE = 300, SF_AT_END_SERVICE = 301, SF_AT_START_CLIENTS = 302, SF_AT_END_CLIENTS = 303, SF_AT_START_CLIENT = 304, SF_AT_END_CLIENT = 305, SF_AT_START_IPPROTO = 306, SF_AT_END_IPPROTO = 307, SF_AT_START_PORT = 308, SF_AT_END_PORT = 309, SF_AT_START_PROTOCOL = 310, SF_AT_END_PROTOCOL = 311, SF_AT_START_APPLICATION = 312, SF_AT_END_APPLICATION = 313 }; #endif /* Tokens. */ #define SF_AT_COMMENT 258 #define SF_AT_WHITESPACE 259 #define SF_START_SNORT_ATTRIBUTES 260 #define SF_END_SNORT_ATTRIBUTES 261 #define SF_AT_START_MAP_TABLE 262 #define SF_AT_END_MAP_TABLE 263 #define SF_AT_START_ENTRY 264 #define SF_AT_END_ENTRY 265 #define SF_AT_START_ENTRY_ID 266 #define SF_AT_END_ENTRY_ID 267 #define SF_AT_START_ENTRY_VALUE 268 #define SF_AT_END_ENTRY_VALUE 269 #define SF_AT_START_ATTRIBUTE_TABLE 270 #define SF_AT_END_ATTRIBUTE_TABLE 271 #define SF_AT_START_HOST 272 #define SF_AT_END_HOST 273 #define SF_AT_START_HOST_IP 274 #define SF_AT_END_HOST_IP 275 #define SF_AT_STRING 276 #define SF_AT_NUMERIC 277 #define SF_AT_IPv6 278 #define SF_AT_IPv6Cidr 279 #define SF_AT_START_OS 280 #define SF_AT_END_OS 281 #define SF_AT_START_ATTRIBUTE_VALUE 282 #define SF_AT_END_ATTRIBUTE_VALUE 283 #define SF_AT_START_ATTRIBUTE_ID 284 #define SF_AT_END_ATTRIBUTE_ID 285 #define SF_AT_START_CONFIDENCE 286 #define SF_AT_END_CONFIDENCE 287 #define SF_AT_START_NAME 288 #define SF_AT_END_NAME 289 #define SF_AT_START_VENDOR 290 #define SF_AT_END_VENDOR 291 #define SF_AT_START_VERSION 292 #define SF_AT_END_VERSION 293 #define SF_AT_START_FRAG_POLICY 294 #define SF_AT_END_FRAG_POLICY 295 #define SF_AT_START_STREAM_POLICY 296 #define SF_AT_END_STREAM_POLICY 297 #define SF_AT_START_SERVICES 298 #define SF_AT_END_SERVICES 299 #define SF_AT_START_SERVICE 300 #define SF_AT_END_SERVICE 301 #define SF_AT_START_CLIENTS 302 #define SF_AT_END_CLIENTS 303 #define SF_AT_START_CLIENT 304 #define SF_AT_END_CLIENT 305 #define SF_AT_START_IPPROTO 306 #define SF_AT_END_IPPROTO 307 #define SF_AT_START_PORT 308 #define SF_AT_END_PORT 309 #define SF_AT_START_PROTOCOL 310 #define SF_AT_END_PROTOCOL 311 #define SF_AT_START_APPLICATION 312 #define SF_AT_END_APPLICATION 313 /* Value type. */ #if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED union YYSTYPE { #line 59 "sf_attribute_table.y" /* yacc.c:355 */ char stringValue[STD_BUF]; uint32_t numericValue; AttributeData data; MapData mapEntry; #line 262 "sf_attribute_table.c" /* yacc.c:355 */ }; typedef union YYSTYPE YYSTYPE; # define YYSTYPE_IS_TRIVIAL 1 # define YYSTYPE_IS_DECLARED 1 #endif extern YYSTYPE sfat_lval; int sfat_parse (void); #endif /* !YY_SFAT_SF_ATTRIBUTE_TABLE_H_INCLUDED */ /* Copy the second part of user declarations. */ #line 279 "sf_attribute_table.c" /* yacc.c:358 */ #ifdef short # undef short #endif #ifdef YYTYPE_UINT8 typedef YYTYPE_UINT8 yytype_uint8; #else typedef unsigned char yytype_uint8; #endif #ifdef YYTYPE_INT8 typedef YYTYPE_INT8 yytype_int8; #else typedef signed char yytype_int8; #endif #ifdef YYTYPE_UINT16 typedef YYTYPE_UINT16 yytype_uint16; #else typedef unsigned short int yytype_uint16; #endif #ifdef YYTYPE_INT16 typedef YYTYPE_INT16 yytype_int16; #else typedef short int yytype_int16; #endif #ifndef YYSIZE_T # ifdef __SIZE_TYPE__ # define YYSIZE_T __SIZE_TYPE__ # elif defined size_t # define YYSIZE_T size_t # elif ! defined YYSIZE_T # include /* INFRINGES ON USER NAME SPACE */ # define YYSIZE_T size_t # else # define YYSIZE_T unsigned int # endif #endif #define YYSIZE_MAXIMUM ((YYSIZE_T) -1) #ifndef YY_ # if defined YYENABLE_NLS && YYENABLE_NLS # if ENABLE_NLS # include /* INFRINGES ON USER NAME SPACE */ # define YY_(Msgid) dgettext ("bison-runtime", Msgid) # endif # endif # ifndef YY_ # define YY_(Msgid) Msgid # endif #endif #ifndef YY_ATTRIBUTE # if (defined __GNUC__ \ && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__))) \ || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C # define YY_ATTRIBUTE(Spec) __attribute__(Spec) # else # define YY_ATTRIBUTE(Spec) /* empty */ # endif #endif #ifndef YY_ATTRIBUTE_PURE # define YY_ATTRIBUTE_PURE YY_ATTRIBUTE ((__pure__)) #endif #ifndef YY_ATTRIBUTE_UNUSED # define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__)) #endif #if !defined _Noreturn \ && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112) # if defined _MSC_VER && 1200 <= _MSC_VER # define _Noreturn __declspec (noreturn) # else # define _Noreturn YY_ATTRIBUTE ((__noreturn__)) # endif #endif /* Suppress unused-variable warnings by "using" E. */ #if ! defined lint || defined __GNUC__ # define YYUSE(E) ((void) (E)) #else # define YYUSE(E) /* empty */ #endif #if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__ /* Suppress an incorrect diagnostic about yylval being uninitialized. */ # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ _Pragma ("GCC diagnostic push") \ _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\ _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") # define YY_IGNORE_MAYBE_UNINITIALIZED_END \ _Pragma ("GCC diagnostic pop") #else # define YY_INITIAL_VALUE(Value) Value #endif #ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN # define YY_IGNORE_MAYBE_UNINITIALIZED_END #endif #ifndef YY_INITIAL_VALUE # define YY_INITIAL_VALUE(Value) /* Nothing. */ #endif #if ! defined yyoverflow || YYERROR_VERBOSE /* The parser invokes alloca or malloc; define the necessary symbols. */ # ifdef YYSTACK_USE_ALLOCA # if YYSTACK_USE_ALLOCA # ifdef __GNUC__ # define YYSTACK_ALLOC __builtin_alloca # elif defined __BUILTIN_VA_ARG_INCR # include /* INFRINGES ON USER NAME SPACE */ # elif defined _AIX # define YYSTACK_ALLOC __alloca # elif defined _MSC_VER # include /* INFRINGES ON USER NAME SPACE */ # define alloca _alloca # else # define YYSTACK_ALLOC alloca # if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS # include /* INFRINGES ON USER NAME SPACE */ /* Use EXIT_SUCCESS as a witness for stdlib.h. */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # endif # endif # endif # ifdef YYSTACK_ALLOC /* Pacify GCC's 'empty if-body' warning. */ # define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) # ifndef YYSTACK_ALLOC_MAXIMUM /* The OS might guarantee only one guard page at the bottom of the stack, and a page size can be as small as 4096 bytes. So we cannot safely invoke alloca (N) if N exceeds 4096. Use a slightly smaller number to allow for a few compiler-allocated temporary stack slots. */ # define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ # endif # else # define YYSTACK_ALLOC YYMALLOC # define YYSTACK_FREE YYFREE # ifndef YYSTACK_ALLOC_MAXIMUM # define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM # endif # if (defined __cplusplus && ! defined EXIT_SUCCESS \ && ! ((defined YYMALLOC || defined malloc) \ && (defined YYFREE || defined free))) # include /* INFRINGES ON USER NAME SPACE */ # ifndef EXIT_SUCCESS # define EXIT_SUCCESS 0 # endif # endif # ifndef YYMALLOC # define YYMALLOC malloc # if ! defined malloc && ! defined EXIT_SUCCESS void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ # endif # endif # ifndef YYFREE # define YYFREE free # if ! defined free && ! defined EXIT_SUCCESS void free (void *); /* INFRINGES ON USER NAME SPACE */ # endif # endif # endif #endif /* ! defined yyoverflow || YYERROR_VERBOSE */ #if (! defined yyoverflow \ && (! defined __cplusplus \ || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) /* A type that is properly aligned for any stack member. */ union yyalloc { yytype_int16 yyss_alloc; YYSTYPE yyvs_alloc; }; /* The size of the maximum gap between one aligned stack and the next. */ # define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) /* The size of an array large to enough to hold all stacks, each with N elements. */ # define YYSTACK_BYTES(N) \ ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + YYSTACK_GAP_MAXIMUM) # define YYCOPY_NEEDED 1 /* Relocate STACK from its old location to the new one. The local variables YYSIZE and YYSTACKSIZE give the old and new number of elements in the stack, and YYPTR gives the new location of the stack. Advance YYPTR to a properly aligned location for the next stack. */ # define YYSTACK_RELOCATE(Stack_alloc, Stack) \ do \ { \ YYSIZE_T yynewbytes; \ YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ Stack = &yyptr->Stack_alloc; \ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ yyptr += yynewbytes / sizeof (*yyptr); \ } \ while (0) #endif #if defined YYCOPY_NEEDED && YYCOPY_NEEDED /* Copy COUNT objects from SRC to DST. The source and destination do not overlap. */ # ifndef YYCOPY # if defined __GNUC__ && 1 < __GNUC__ # define YYCOPY(Dst, Src, Count) \ __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src))) # else # define YYCOPY(Dst, Src, Count) \ do \ { \ YYSIZE_T yyi; \ for (yyi = 0; yyi < (Count); yyi++) \ (Dst)[yyi] = (Src)[yyi]; \ } \ while (0) # endif # endif #endif /* !YYCOPY_NEEDED */ /* YYFINAL -- State number of the termination state. */ #define YYFINAL 8 /* YYLAST -- Last index in YYTABLE. */ #define YYLAST 133 /* YYNTOKENS -- Number of terminals. */ #define YYNTOKENS 59 /* YYNNTS -- Number of nonterminals. */ #define YYNNTS 54 /* YYNRULES -- Number of rules. */ #define YYNRULES 83 /* YYNSTATES -- Number of states. */ #define YYNSTATES 152 /* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned by yylex, with out-of-bounds checking. */ #define YYUNDEFTOK 2 #define YYMAXUTOK 313 #define YYTRANSLATE(YYX) \ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) /* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM as returned by yylex, without out-of-bounds checking. */ static const yytype_uint8 yytranslate[] = { 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58 }; #if YYDEBUG /* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ static const yytype_uint16 yyrline[] = { 0, 144, 144, 150, 155, 162, 168, 171, 174, 182, 185, 188, 195, 202, 210, 216, 219, 222, 232, 239, 242, 247, 252, 257, 264, 275, 277, 277, 279, 279, 279, 279, 279, 282, 290, 298, 306, 314, 322, 328, 334, 340, 346, 366, 388, 394, 398, 404, 411, 418, 424, 431, 437, 440, 446, 454, 461, 467, 471, 477, 482, 487, 492, 497, 502, 509, 517, 525, 533, 540, 549, 557, 563, 570, 576, 579, 585, 593, 600, 606, 610, 616, 621, 626 }; #endif #if YYDEBUG || YYERROR_VERBOSE || 0 /* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. First, the terminals, then, starting at YYNTOKENS, nonterminals. */ static const char *const yytname[] = { "$end", "error", "$undefined", "SF_AT_COMMENT", "SF_AT_WHITESPACE", "SF_START_SNORT_ATTRIBUTES", "SF_END_SNORT_ATTRIBUTES", "SF_AT_START_MAP_TABLE", "SF_AT_END_MAP_TABLE", "SF_AT_START_ENTRY", "SF_AT_END_ENTRY", "SF_AT_START_ENTRY_ID", "SF_AT_END_ENTRY_ID", "SF_AT_START_ENTRY_VALUE", "SF_AT_END_ENTRY_VALUE", "SF_AT_START_ATTRIBUTE_TABLE", "SF_AT_END_ATTRIBUTE_TABLE", "SF_AT_START_HOST", "SF_AT_END_HOST", "SF_AT_START_HOST_IP", "SF_AT_END_HOST_IP", "SF_AT_STRING", "SF_AT_NUMERIC", "SF_AT_IPv6", "SF_AT_IPv6Cidr", "SF_AT_START_OS", "SF_AT_END_OS", "SF_AT_START_ATTRIBUTE_VALUE", "SF_AT_END_ATTRIBUTE_VALUE", "SF_AT_START_ATTRIBUTE_ID", "SF_AT_END_ATTRIBUTE_ID", "SF_AT_START_CONFIDENCE", "SF_AT_END_CONFIDENCE", "SF_AT_START_NAME", "SF_AT_END_NAME", "SF_AT_START_VENDOR", "SF_AT_END_VENDOR", "SF_AT_START_VERSION", "SF_AT_END_VERSION", "SF_AT_START_FRAG_POLICY", "SF_AT_END_FRAG_POLICY", "SF_AT_START_STREAM_POLICY", "SF_AT_END_STREAM_POLICY", "SF_AT_START_SERVICES", "SF_AT_END_SERVICES", "SF_AT_START_SERVICE", "SF_AT_END_SERVICE", "SF_AT_START_CLIENTS", "SF_AT_END_CLIENTS", "SF_AT_START_CLIENT", "SF_AT_END_CLIENT", "SF_AT_START_IPPROTO", "SF_AT_END_IPPROTO", "SF_AT_START_PORT", "SF_AT_END_PORT", "SF_AT_START_PROTOCOL", "SF_AT_END_PROTOCOL", "SF_AT_START_APPLICATION", "SF_AT_END_APPLICATION", "$accept", "AttributeGrammar", "SnortAttributes", "MappingTable", "ListOfMapEntries", "MapEntry", "MapEntryStart", "MapEntryEnd", "MapEntryData", "MapValue", "MapId", "AttributeTable", "ListOfHosts", "HostEntry", "HostEntryStart", "HostEntryEnd", "HostEntryData", "IpCidr", "HostOS", "OSAttributes", "OSAttribute", "OSName", "OSVendor", "OSVersion", "OSFragPolicy", "OSStreamPolicy", "AttributeInfo", "AttributeValueString", "AttributeValueNumber", "AttributeId", "AttributeConfidence", "ServiceList", "ServiceListStart", "ServiceListEnd", "ServiceListData", "Service", "ServiceStart", "ServiceEnd", "ServiceData", "ServiceDataRequired", "IPProtocol", "Protocol", "Port", "Application", "Version", "ClientList", "ClientListStart", "ClientListEnd", "ClientListData", "Client", "ClientStart", "ClientEnd", "ClientData", "ClientDataRequired", YY_NULLPTR }; #endif # ifdef YYPRINT /* YYTOKNUM[NUM] -- (External) token number corresponding to the (internal) symbol number NUM (which must be that of a token). */ static const yytype_uint16 yytoknum[] = { 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313 }; # endif #define YYPACT_NINF -97 #define yypact_value_is_default(Yystate) \ (!!((Yystate) == (-97))) #define YYTABLE_NINF -1 #define yytable_value_is_error(Yytable_value) \ 0 /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing STATE-NUM. */ static const yytype_int8 yypact[] = { 1, 25, 9, -97, 28, -97, 30, 41, -97, -97, 43, 28, 51, 42, 57, -97, -97, -97, 44, 54, 52, -97, -97, -97, 48, -97, 56, -97, -97, 49, -97, 50, 55, 47, -97, 60, 59, -97, -97, -23, -4, -97, -97, -7, -7, -7, 61, 62, -22, -97, -97, -97, -97, -97, -97, -97, -97, 22, 31, -97, 26, 13, 58, 53, 46, 46, 46, 45, 63, 64, 65, -97, -97, -97, -97, 40, 31, -9, -97, 37, 26, 2, 66, 67, -97, 68, -97, 69, -97, -97, -97, -97, -97, -97, -97, -97, -97, -97, -7, -7, -7, 70, 29, -32, -15, 2, -97, -97, -97, 33, 38, 71, 29, -97, -97, -97, 73, 72, 36, 74, -97, -97, -7, -97, 39, 33, 39, 38, 33, 38, -97, -97, -97, -97, -97, -97, -97, -97, -97, -34, -97, -97, -97, -97, -97, -97, -7, -97, 35, 75, -97, -97 }; /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. Performed when YYTABLE does not specify something else to do. Zero means the default is an error. */ static const yytype_uint8 yydefact[] = { 0, 0, 0, 2, 6, 15, 0, 0, 1, 9, 0, 6, 0, 0, 0, 4, 5, 7, 0, 0, 0, 14, 18, 16, 0, 3, 0, 10, 8, 0, 11, 0, 0, 0, 13, 0, 0, 19, 17, 0, 23, 12, 24, 0, 0, 0, 0, 0, 0, 26, 28, 29, 30, 32, 31, 50, 72, 22, 52, 21, 74, 0, 0, 0, 38, 41, 43, 0, 0, 0, 0, 25, 27, 20, 55, 0, 52, 0, 77, 0, 74, 0, 0, 0, 45, 0, 33, 0, 39, 40, 42, 34, 35, 36, 37, 51, 49, 53, 0, 0, 0, 0, 57, 0, 0, 0, 73, 71, 75, 0, 81, 0, 79, 44, 46, 47, 0, 0, 0, 0, 56, 54, 0, 58, 0, 0, 0, 0, 0, 0, 82, 83, 78, 76, 80, 48, 65, 67, 66, 0, 59, 60, 61, 62, 64, 63, 0, 68, 0, 0, 69, 70 }; /* YYPGOTO[NTERM-NUM]. */ static const yytype_int8 yypgoto[] = { -97, -97, -97, -97, 85, -97, -97, -97, -97, -97, -97, 91, -97, -97, -97, -97, -97, -97, -97, -97, 77, -97, -97, -97, -97, -97, -44, -97, -97, -97, -5, -97, -97, -97, 23, -97, -97, -97, -97, -97, -79, -76, -96, -12, -97, 76, -97, -97, 32, -97, -97, -97, -97, -97 }; /* YYDEFGOTO[NTERM-NUM]. */ static const yytype_int16 yydefgoto[] = { -1, 2, 3, 6, 10, 11, 12, 28, 19, 30, 20, 7, 13, 23, 24, 38, 32, 33, 40, 48, 49, 50, 51, 52, 53, 54, 63, 64, 65, 66, 88, 57, 58, 96, 75, 76, 77, 121, 101, 102, 103, 104, 105, 123, 148, 59, 60, 107, 79, 80, 81, 133, 111, 112 }; /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If positive, shift that token. If negative, reduce the rule whose number is the opposite. If YYTABLE_NINF, syntax error. */ static const yytype_uint8 yytable[] = { 67, 68, 109, 146, 71, 110, 1, 125, 127, 8, 43, 43, 44, 44, 45, 45, 46, 46, 47, 47, 61, 99, 62, 100, 147, 126, 128, 124, 140, 129, 142, 131, 4, 130, 82, 83, 98, 9, 99, 55, 5, 84, 98, 56, 99, 5, 100, 15, 143, 141, 145, 16, 144, 98, 117, 118, 119, 100, 21, 22, 89, 90, 18, 25, 27, 29, 26, 31, 34, 56, 35, 36, 39, 37, 41, 78, 74, 87, 139, 42, 85, 91, 69, 70, 95, 106, 122, 86, 100, 98, 137, 116, 99, 150, 113, 114, 17, 14, 115, 97, 134, 92, 149, 0, 93, 135, 0, 94, 0, 0, 0, 0, 108, 151, 0, 0, 120, 0, 0, 0, 0, 132, 0, 0, 136, 72, 0, 0, 0, 0, 138, 0, 0, 73 }; static const yytype_int16 yycheck[] = { 44, 45, 81, 37, 26, 81, 5, 103, 104, 0, 33, 33, 35, 35, 37, 37, 39, 39, 41, 41, 27, 53, 29, 55, 58, 104, 105, 103, 124, 105, 126, 110, 7, 109, 21, 22, 51, 9, 53, 43, 15, 28, 51, 47, 53, 15, 55, 6, 127, 125, 129, 8, 128, 51, 98, 99, 100, 55, 16, 17, 65, 66, 11, 6, 10, 13, 22, 19, 12, 47, 21, 21, 25, 18, 14, 49, 45, 31, 122, 20, 22, 36, 21, 21, 44, 48, 57, 34, 55, 51, 54, 22, 53, 58, 28, 28, 11, 6, 30, 76, 112, 38, 146, -1, 40, 32, -1, 42, -1, -1, -1, -1, 80, 38, -1, -1, 46, -1, -1, -1, -1, 50, -1, -1, 52, 48, -1, -1, -1, -1, 56, -1, -1, 57 }; /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing symbol of state STATE-NUM. */ static const yytype_uint8 yystos[] = { 0, 5, 60, 61, 7, 15, 62, 70, 0, 9, 63, 64, 65, 71, 70, 6, 8, 63, 11, 67, 69, 16, 17, 72, 73, 6, 22, 10, 66, 13, 68, 19, 75, 76, 12, 21, 21, 18, 74, 25, 77, 14, 20, 33, 35, 37, 39, 41, 78, 79, 80, 81, 82, 83, 84, 43, 47, 90, 91, 104, 105, 27, 29, 85, 86, 87, 88, 85, 85, 21, 21, 26, 79, 104, 45, 93, 94, 95, 49, 107, 108, 109, 21, 22, 28, 22, 34, 31, 89, 89, 89, 36, 38, 40, 42, 44, 92, 93, 51, 53, 55, 97, 98, 99, 100, 101, 48, 106, 107, 99, 100, 111, 112, 28, 28, 30, 22, 85, 85, 85, 46, 96, 57, 102, 100, 101, 99, 101, 99, 100, 100, 99, 50, 110, 102, 32, 52, 54, 56, 85, 101, 100, 101, 99, 100, 99, 37, 58, 103, 85, 58, 38 }; /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ static const yytype_uint8 yyr1[] = { 0, 59, 60, 61, 61, 62, 63, 63, 64, 65, 66, 67, 68, 69, 70, 71, 71, 72, 73, 74, 75, 75, 75, 75, 76, 77, 78, 78, 79, 79, 79, 79, 79, 80, 81, 82, 83, 84, 85, 85, 85, 85, 85, 85, 86, 87, 87, 88, 89, 90, 91, 92, 93, 93, 94, 95, 96, 97, 97, 98, 98, 98, 98, 98, 98, 99, 100, 101, 102, 102, 103, 104, 105, 106, 107, 107, 108, 109, 110, 111, 111, 112, 112, 112 }; /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN. */ static const yytype_uint8 yyr2[] = { 0, 2, 1, 4, 3, 3, 0, 2, 3, 1, 1, 2, 3, 3, 3, 0, 2, 3, 1, 1, 4, 3, 3, 2, 3, 3, 1, 2, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 1, 2, 2, 1, 2, 1, 3, 2, 3, 3, 3, 3, 1, 1, 0, 2, 3, 1, 1, 1, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 3, 3, 1, 1, 0, 2, 3, 1, 1, 1, 2, 1, 2, 2 }; #define yyerrok (yyerrstatus = 0) #define yyclearin (yychar = YYEMPTY) #define YYEMPTY (-2) #define YYEOF 0 #define YYACCEPT goto yyacceptlab #define YYABORT goto yyabortlab #define YYERROR goto yyerrorlab #define YYRECOVERING() (!!yyerrstatus) #define YYBACKUP(Token, Value) \ do \ if (yychar == YYEMPTY) \ { \ yychar = (Token); \ yylval = (Value); \ YYPOPSTACK (yylen); \ yystate = *yyssp; \ goto yybackup; \ } \ else \ { \ yyerror (YY_("syntax error: cannot back up")); \ YYERROR; \ } \ while (0) /* Error token number */ #define YYTERROR 1 #define YYERRCODE 256 /* Enable debugging if requested. */ #if YYDEBUG # ifndef YYFPRINTF # include /* INFRINGES ON USER NAME SPACE */ # define YYFPRINTF fprintf # endif # define YYDPRINTF(Args) \ do { \ if (yydebug) \ YYFPRINTF Args; \ } while (0) /* This macro is provided for backward compatibility. */ #ifndef YY_LOCATION_PRINT # define YY_LOCATION_PRINT(File, Loc) ((void) 0) #endif # define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ do { \ if (yydebug) \ { \ YYFPRINTF (stderr, "%s ", Title); \ yy_symbol_print (stderr, \ Type, Value); \ YYFPRINTF (stderr, "\n"); \ } \ } while (0) /*----------------------------------------. | Print this symbol's value on YYOUTPUT. | `----------------------------------------*/ static void yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) { FILE *yyo = yyoutput; YYUSE (yyo); if (!yyvaluep) return; # ifdef YYPRINT if (yytype < YYNTOKENS) YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); # endif YYUSE (yytype); } /*--------------------------------. | Print this symbol on YYOUTPUT. | `--------------------------------*/ static void yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) { YYFPRINTF (yyoutput, "%s %s (", yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]); yy_symbol_value_print (yyoutput, yytype, yyvaluep); YYFPRINTF (yyoutput, ")"); } /*------------------------------------------------------------------. | yy_stack_print -- Print the state stack from its BOTTOM up to its | | TOP (included). | `------------------------------------------------------------------*/ static void yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) { YYFPRINTF (stderr, "Stack now"); for (; yybottom <= yytop; yybottom++) { int yybot = *yybottom; YYFPRINTF (stderr, " %d", yybot); } YYFPRINTF (stderr, "\n"); } # define YY_STACK_PRINT(Bottom, Top) \ do { \ if (yydebug) \ yy_stack_print ((Bottom), (Top)); \ } while (0) /*------------------------------------------------. | Report that the YYRULE is going to be reduced. | `------------------------------------------------*/ static void yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule) { unsigned long int yylno = yyrline[yyrule]; int yynrhs = yyr2[yyrule]; int yyi; YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", yyrule - 1, yylno); /* The symbols being reduced. */ for (yyi = 0; yyi < yynrhs; yyi++) { YYFPRINTF (stderr, " $%d = ", yyi + 1); yy_symbol_print (stderr, yystos[yyssp[yyi + 1 - yynrhs]], &(yyvsp[(yyi + 1) - (yynrhs)]) ); YYFPRINTF (stderr, "\n"); } } # define YY_REDUCE_PRINT(Rule) \ do { \ if (yydebug) \ yy_reduce_print (yyssp, yyvsp, Rule); \ } while (0) /* Nonzero means print parse trace. It is left uninitialized so that multiple parsers can coexist. */ int yydebug; #else /* !YYDEBUG */ # define YYDPRINTF(Args) # define YY_SYMBOL_PRINT(Title, Type, Value, Location) # define YY_STACK_PRINT(Bottom, Top) # define YY_REDUCE_PRINT(Rule) #endif /* !YYDEBUG */ /* YYINITDEPTH -- initial size of the parser's stacks. */ #ifndef YYINITDEPTH # define YYINITDEPTH 200 #endif /* YYMAXDEPTH -- maximum size the stacks can grow to (effective only if the built-in stack extension method is used). Do not make this value too large; the results are undefined if YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) evaluated with infinite-precision integer arithmetic. */ #ifndef YYMAXDEPTH # define YYMAXDEPTH 10000 #endif #if YYERROR_VERBOSE # ifndef yystrlen # if defined __GLIBC__ && defined _STRING_H # define yystrlen strlen # else /* Return the length of YYSTR. */ static YYSIZE_T yystrlen (const char *yystr) { YYSIZE_T yylen; for (yylen = 0; yystr[yylen]; yylen++) continue; return yylen; } # endif # endif # ifndef yystpcpy # if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE # define yystpcpy stpcpy # else /* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in YYDEST. */ static char * yystpcpy (char *yydest, const char *yysrc) { char *yyd = yydest; const char *yys = yysrc; while ((*yyd++ = *yys++) != '\0') continue; return yyd - 1; } # endif # endif # ifndef yytnamerr /* Copy to YYRES the contents of YYSTR after stripping away unnecessary quotes and backslashes, so that it's suitable for yyerror. The heuristic is that double-quoting is unnecessary unless the string contains an apostrophe, a comma, or backslash (other than backslash-backslash). YYSTR is taken from yytname. If YYRES is null, do not copy; instead, return the length of what the result would have been. */ static YYSIZE_T yytnamerr (char *yyres, const char *yystr) { if (*yystr == '"') { YYSIZE_T yyn = 0; char const *yyp = yystr; for (;;) switch (*++yyp) { case '\'': case ',': goto do_not_strip_quotes; case '\\': if (*++yyp != '\\') goto do_not_strip_quotes; /* Fall through. */ default: if (yyres) yyres[yyn] = *yyp; yyn++; break; case '"': if (yyres) yyres[yyn] = '\0'; return yyn; } do_not_strip_quotes: ; } if (! yyres) return yystrlen (yystr); return yystpcpy (yyres, yystr) - yyres; } # endif /* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message about the unexpected token YYTOKEN for the state stack whose top is YYSSP. Return 0 if *YYMSG was successfully written. Return 1 if *YYMSG is not large enough to hold the message. In that case, also set *YYMSG_ALLOC to the required number of bytes. Return 2 if the required number of bytes is too large to store. */ static int yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg, yytype_int16 *yyssp, int yytoken) { YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]); YYSIZE_T yysize = yysize0; enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; /* Internationalized format string. */ const char *yyformat = YY_NULLPTR; /* Arguments of yyformat. */ char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; /* Number of reported tokens (one for the "unexpected", one per "expected"). */ int yycount = 0; /* There are many possibilities here to consider: - If this state is a consistent state with a default action, then the only way this function was invoked is if the default action is an error action. In that case, don't check for expected tokens because there are none. - The only way there can be no lookahead present (in yychar) is if this state is a consistent state with a default action. Thus, detecting the absence of a lookahead is sufficient to determine that there is no unexpected or expected token to report. In that case, just report a simple "syntax error". - Don't assume there isn't a lookahead just because this state is a consistent state with a default action. There might have been a previous inconsistent state, consistent state with a non-default action, or user semantic action that manipulated yychar. - Of course, the expected token list depends on states to have correct lookahead information, and it depends on the parser not to perform extra reductions after fetching a lookahead from the scanner and before detecting a syntax error. Thus, state merging (from LALR or IELR) and default reductions corrupt the expected token list. However, the list is correct for canonical LR with one exception: it will still contain any token that will not be accepted due to an error action in a later state. */ if (yytoken != YYEMPTY) { int yyn = yypact[*yyssp]; yyarg[yycount++] = yytname[yytoken]; if (!yypact_value_is_default (yyn)) { /* Start YYX at -YYN if negative to avoid negative indexes in YYCHECK. In other words, skip the first -YYN actions for this state because they are default actions. */ int yyxbegin = yyn < 0 ? -yyn : 0; /* Stay within bounds of both yycheck and yytname. */ int yychecklim = YYLAST - yyn + 1; int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; int yyx; for (yyx = yyxbegin; yyx < yyxend; ++yyx) if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR && !yytable_value_is_error (yytable[yyx + yyn])) { if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) { yycount = 1; yysize = yysize0; break; } yyarg[yycount++] = yytname[yyx]; { YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } } } } switch (yycount) { # define YYCASE_(N, S) \ case N: \ yyformat = S; \ break default: /* Avoid compiler warnings. */ YYCASE_(0, YY_("syntax error")); YYCASE_(1, YY_("syntax error, unexpected %s")); YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s")); YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s")); YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s")); YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s")); # undef YYCASE_ } { YYSIZE_T yysize1 = yysize + yystrlen (yyformat); if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM)) return 2; yysize = yysize1; } if (*yymsg_alloc < yysize) { *yymsg_alloc = 2 * yysize; if (! (yysize <= *yymsg_alloc && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM)) *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM; return 1; } /* Avoid sprintf, as that infringes on the user's name space. Don't have undefined behavior even if the translation produced a string with the wrong number of "%s"s. */ { char *yyp = *yymsg; int yyi = 0; while ((*yyp = *yyformat) != '\0') if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount) { yyp += yytnamerr (yyp, yyarg[yyi++]); yyformat += 2; } else { yyp++; yyformat++; } } return 0; } #endif /* YYERROR_VERBOSE */ /*-----------------------------------------------. | Release the memory associated to this symbol. | `-----------------------------------------------*/ static void yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) { YYUSE (yyvaluep); if (!yymsg) yymsg = "Deleting"; YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN YYUSE (yytype); YY_IGNORE_MAYBE_UNINITIALIZED_END } /* The lookahead symbol. */ int yychar; /* The semantic value of the lookahead symbol. */ YYSTYPE yylval; /* Number of syntax errors so far. */ int yynerrs; /*----------. | yyparse. | `----------*/ int yyparse (void) { int yystate; /* Number of tokens to shift before error messages enabled. */ int yyerrstatus; /* The stacks and their tools: 'yyss': related to states. 'yyvs': related to semantic values. Refer to the stacks through separate pointers, to allow yyoverflow to reallocate them elsewhere. */ /* The state stack. */ yytype_int16 yyssa[YYINITDEPTH]; yytype_int16 *yyss; yytype_int16 *yyssp; /* The semantic value stack. */ YYSTYPE yyvsa[YYINITDEPTH]; YYSTYPE *yyvs; YYSTYPE *yyvsp; YYSIZE_T yystacksize; int yyn; int yyresult; /* Lookahead token as an internal (translated) token number. */ int yytoken = 0; /* The variables used to return semantic value and location from the action routines. */ YYSTYPE yyval; #if YYERROR_VERBOSE /* Buffer for error messages, and its allocated size. */ char yymsgbuf[128]; char *yymsg = yymsgbuf; YYSIZE_T yymsg_alloc = sizeof yymsgbuf; #endif #define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) /* The number of symbols on the RHS of the reduced rule. Keep to zero when no symbol should be popped. */ int yylen = 0; yyssp = yyss = yyssa; yyvsp = yyvs = yyvsa; yystacksize = YYINITDEPTH; YYDPRINTF ((stderr, "Starting parse\n")); yystate = 0; yyerrstatus = 0; yynerrs = 0; yychar = YYEMPTY; /* Cause a token to be read. */ goto yysetstate; /*------------------------------------------------------------. | yynewstate -- Push a new state, which is found in yystate. | `------------------------------------------------------------*/ yynewstate: /* In all cases, when you get here, the value and location stacks have just been pushed. So pushing a state here evens the stacks. */ yyssp++; yysetstate: *yyssp = yystate; if (yyss + yystacksize - 1 <= yyssp) { /* Get the current used size of the three stacks, in elements. */ YYSIZE_T yysize = yyssp - yyss + 1; #ifdef yyoverflow { /* Give user a chance to reallocate the stack. Use copies of these so that the &'s don't force the real ones into memory. */ YYSTYPE *yyvs1 = yyvs; yytype_int16 *yyss1 = yyss; /* Each stack pointer address is followed by the size of the data in use in that stack, in bytes. This used to be a conditional around just the two extra args, but that might be undefined if yyoverflow is a macro. */ yyoverflow (YY_("memory exhausted"), &yyss1, yysize * sizeof (*yyssp), &yyvs1, yysize * sizeof (*yyvsp), &yystacksize); yyss = yyss1; yyvs = yyvs1; } #else /* no yyoverflow */ # ifndef YYSTACK_RELOCATE goto yyexhaustedlab; # else /* Extend the stack our own way. */ if (YYMAXDEPTH <= yystacksize) goto yyexhaustedlab; yystacksize *= 2; if (YYMAXDEPTH < yystacksize) yystacksize = YYMAXDEPTH; { yytype_int16 *yyss1 = yyss; union yyalloc *yyptr = (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); if (! yyptr) goto yyexhaustedlab; YYSTACK_RELOCATE (yyss_alloc, yyss); YYSTACK_RELOCATE (yyvs_alloc, yyvs); # undef YYSTACK_RELOCATE if (yyss1 != yyssa) YYSTACK_FREE (yyss1); } # endif #endif /* no yyoverflow */ yyssp = yyss + yysize - 1; yyvsp = yyvs + yysize - 1; YYDPRINTF ((stderr, "Stack size increased to %lu\n", (unsigned long int) yystacksize)); if (yyss + yystacksize - 1 <= yyssp) YYABORT; } YYDPRINTF ((stderr, "Entering state %d\n", yystate)); if (yystate == YYFINAL) YYACCEPT; goto yybackup; /*-----------. | yybackup. | `-----------*/ yybackup: /* Do appropriate processing given the current state. Read a lookahead token if we need one and don't already have one. */ /* First try to decide what to do without reference to lookahead token. */ yyn = yypact[yystate]; if (yypact_value_is_default (yyn)) goto yydefault; /* Not known => get a lookahead token if don't already have one. */ /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ if (yychar == YYEMPTY) { YYDPRINTF ((stderr, "Reading a token: ")); yychar = yylex (); } if (yychar <= YYEOF) { yychar = yytoken = YYEOF; YYDPRINTF ((stderr, "Now at end of input.\n")); } else { yytoken = YYTRANSLATE (yychar); YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); } /* If the proper action on seeing token YYTOKEN is to reduce or to detect an error, take that action. */ yyn += yytoken; if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) goto yydefault; yyn = yytable[yyn]; if (yyn <= 0) { if (yytable_value_is_error (yyn)) goto yyerrlab; yyn = -yyn; goto yyreduce; } /* Count tokens shifted since error; after three, turn off error status. */ if (yyerrstatus) yyerrstatus--; /* Shift the lookahead token. */ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); /* Discard the shifted token. */ yychar = YYEMPTY; yystate = yyn; YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END goto yynewstate; /*-----------------------------------------------------------. | yydefault -- do the default action for the current state. | `-----------------------------------------------------------*/ yydefault: yyn = yydefact[yystate]; if (yyn == 0) goto yyerrlab; goto yyreduce; /*-----------------------------. | yyreduce -- Do a reduction. | `-----------------------------*/ yyreduce: /* yyn is the number of a rule to reduce with. */ yylen = yyr2[yyn]; /* If YYLEN is nonzero, implement the default value of the action: '$$ = $1'. Otherwise, the following line sets YYVAL to garbage. This behavior is undocumented and Bison users should not rely upon it. Assigning to YYVAL unconditionally makes the parser a bit smaller, and it avoids a GCC warning that YYVAL may be used uninitialized. */ yyval = yyvsp[1-yylen]; YY_REDUCE_PRINT (yyn); switch (yyn) { case 2: #line 145 "sf_attribute_table.y" /* yacc.c:1648 */ { YYACCEPT; } #line 1495 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 3: #line 151 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "SnortAttributes: Got Attribute Map & Table\n");); } #line 1503 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 4: #line 156 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "SnortAttributes: Got Attribute Table\n");); } #line 1511 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 5: #line 163 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Got Attribute Map\n");); } #line 1519 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 6: #line 168 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Empty Mapping Table\n");); } #line 1527 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 8: #line 175 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "MapEntry: Name: %s, Id %d\n", (yyvsp[-1].mapEntry).s_mapvalue, (yyvsp[-1].mapEntry).l_mapid);); SFAT_AddMapEntry(&(yyvsp[-1].mapEntry)); } #line 1537 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 11: #line 189 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.mapEntry).l_mapid = (yyvsp[-1].numericValue); SnortStrncpy((yyval.mapEntry).s_mapvalue, (yyvsp[0].stringValue), STD_BUF); } #line 1546 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 12: #line 196 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "MapValue: %s\n", (yyvsp[-1].stringValue));) SnortStrncpy((yyval.stringValue), (yyvsp[-1].stringValue), STD_BUF); } #line 1555 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 13: #line 203 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.numericValue) = (yyvsp[-1].numericValue); DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "MapId: %d\n", (yyvsp[-1].numericValue));); } #line 1564 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 14: #line 211 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Got Attribute Table\n");); } #line 1572 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 15: #line 216 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "EmptyHostEntry\n");); } #line 1580 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 17: #line 223 "sf_attribute_table.y" /* yacc.c:1648 */ { if (SFAT_AddHostEntryToMap() != SFAT_OK) { YYABORT; } DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Host Added\n");); } #line 1592 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 18: #line 233 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Callback to create a host entry object */ SFAT_CreateHostEntry(); } #line 1601 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 20: #line 243 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData\n");); } #line 1609 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 21: #line 248 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData: No Services\n");); } #line 1617 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 22: #line 253 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData: No Clients\n");); } #line 1625 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 23: #line 258 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData: No Services or Clients\n");); } #line 1633 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 24: #line 265 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Convert IP/CIDR to Snort IPCidr Object */ /* determine the number of bits (done in SetHostIp4) */ if (SFAT_SetHostIp((yyvsp[-1].stringValue)) != SFAT_OK) { YYABORT; } } #line 1646 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 33: #line 283 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Copy OSName */ DEBUG_WRAP(PrintAttributeData("OS:Name", &(yyvsp[-1].data));); SFAT_SetOSAttribute(&(yyvsp[-1].data), HOST_INFO_OS); } #line 1656 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 34: #line 291 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Copy OSVendor */ DEBUG_WRAP(PrintAttributeData("OS:Vendor", &(yyvsp[-1].data));); SFAT_SetOSAttribute(&(yyvsp[-1].data), HOST_INFO_VENDOR); } #line 1666 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 35: #line 299 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Copy OSVersion */ DEBUG_WRAP(PrintAttributeData("OS:Version", &(yyvsp[-1].data));); SFAT_SetOSAttribute(&(yyvsp[-1].data), HOST_INFO_VERSION); } #line 1676 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 36: #line 307 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Copy OSFragPolicy */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "OS:FragPolicy: %s\n", (yyvsp[-1].stringValue));); SFAT_SetOSPolicy((yyvsp[-1].stringValue), HOST_INFO_FRAG_POLICY); } #line 1686 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 37: #line 315 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Copy OSStreamPolicy */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "OS:StreamPolicy: %s\n", (yyvsp[-1].stringValue));); SFAT_SetOSPolicy((yyvsp[-1].stringValue), HOST_INFO_STREAM_POLICY); } #line 1696 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 38: #line 323 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.data).type = ATTRIBUTE_NAME; (yyval.data).confidence = 100; SnortStrncpy((yyval.data).value.s_value, (yyvsp[0].stringValue), STD_BUF); } #line 1706 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 39: #line 329 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.data).type = ATTRIBUTE_NAME; (yyval.data).confidence = (yyvsp[0].numericValue); SnortStrncpy((yyval.data).value.s_value, (yyvsp[-1].stringValue), STD_BUF); } #line 1716 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 40: #line 335 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.data).type = ATTRIBUTE_NAME; (yyval.data).confidence = (yyvsp[0].numericValue); SnortSnprintf((yyval.data).value.s_value, STD_BUF, "%d", (yyvsp[-1].numericValue)); } #line 1726 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 41: #line 341 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.data).type = ATTRIBUTE_NAME; (yyval.data).confidence = 100; SnortSnprintf((yyval.data).value.s_value, STD_BUF, "%d", (yyvsp[0].numericValue)); } #line 1736 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 42: #line 347 "sf_attribute_table.y" /* yacc.c:1648 */ { char *mapped_name; (yyval.data).confidence = (yyvsp[0].numericValue); mapped_name = SFAT_LookupAttributeNameById((yyvsp[-1].numericValue)); if (!mapped_name) { (yyval.data).type = ATTRIBUTE_ID; (yyval.data).value.l_value = (yyvsp[-1].numericValue); //FatalError("Unknown/Invalid Attribute ID %d\n", $1); sfat_grammar_error = "Unknown/Invalid Attribute ID"; YYABORT; } else { /* Copy String */ (yyval.data).type = ATTRIBUTE_NAME; SnortStrncpy((yyval.data).value.s_value, mapped_name, STD_BUF); } } #line 1760 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 43: #line 367 "sf_attribute_table.y" /* yacc.c:1648 */ { char *mapped_name; (yyval.data).confidence = 100; mapped_name = SFAT_LookupAttributeNameById((yyvsp[0].numericValue)); if (!mapped_name) { (yyval.data).type = ATTRIBUTE_ID; (yyval.data).value.l_value = (yyvsp[0].numericValue); //FatalError("Unknown/Invalid Attribute ID %d\n", $1); sfat_grammar_error = "Unknown/Invalid Attribute ID"; YYABORT; } else { /* Copy String */ (yyval.data).type = ATTRIBUTE_NAME; SnortStrncpy((yyval.data).value.s_value, mapped_name, STD_BUF); } } #line 1784 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 44: #line 389 "sf_attribute_table.y" /* yacc.c:1648 */ { SnortStrncpy((yyval.stringValue), (yyvsp[-1].stringValue), STD_BUF); } #line 1792 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 45: #line 395 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.numericValue) = 0; } #line 1800 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 46: #line 399 "sf_attribute_table.y" /* yacc.c:1648 */ { (yyval.numericValue) = (yyvsp[-1].numericValue); } #line 1808 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 47: #line 405 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Copy numeric */ (yyval.numericValue) = (yyvsp[-1].numericValue); } #line 1817 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 48: #line 412 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Copy numeric */ (yyval.numericValue) = (yyvsp[-1].numericValue); } #line 1826 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 49: #line 419 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "ServiceList (complete)\n");); } #line 1834 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 50: #line 425 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Start ServiceList\n");); sfat_client_or_service = ATTRIBUTE_SERVICE; } #line 1843 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 51: #line 432 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "End ServiceList\n");); } #line 1851 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 52: #line 437 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "EmptyService\n");); } #line 1859 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 53: #line 441 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service ServiceListData\n");); } #line 1867 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 54: #line 447 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Adding Complete\n");); SFAT_AddApplicationData(); DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Added\n");); } #line 1877 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 55: #line 455 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Start\n");); SFAT_CreateApplicationEntry(); } #line 1886 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 56: #line 462 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service End\n");); } #line 1894 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 57: #line 468 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data (no application)\n");); } #line 1902 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 58: #line 472 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data (application)\n");); } #line 1910 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 59: #line 478 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (IPProto Proto Port)\n");); } #line 1919 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 60: #line 483 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (IPProto Port Proto)\n");); } #line 1928 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 61: #line 488 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Proto IPProto Port)\n");); } #line 1937 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 62: #line 493 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Proto Port IPProto)\n");); } #line 1946 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 63: #line 498 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Port Proto IPProto)\n");); } #line 1955 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 64: #line 503 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Port IPProto Proto)\n");); } #line 1964 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 65: #line 510 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Store IPProto Info */ DEBUG_WRAP(PrintAttributeData("IPProto", &(yyvsp[-1].data));); SFAT_SetApplicationAttribute(&(yyvsp[-1].data), APPLICATION_ENTRY_IPPROTO); } #line 1974 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 66: #line 518 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Store Protocol Info */ DEBUG_WRAP(PrintAttributeData("Protocol", &(yyvsp[-1].data));); SFAT_SetApplicationAttribute(&(yyvsp[-1].data), APPLICATION_ENTRY_PROTO); } #line 1984 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 67: #line 526 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Store Port Info */ DEBUG_WRAP(PrintAttributeData("Port", &(yyvsp[-1].data));); SFAT_SetApplicationAttribute(&(yyvsp[-1].data), APPLICATION_ENTRY_PORT); } #line 1994 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 68: #line 534 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Store Application Info */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Application\n")); DEBUG_WRAP(PrintAttributeData("Application", &(yyvsp[-1].data));); SFAT_SetApplicationAttribute(&(yyvsp[-1].data), APPLICATION_ENTRY_APPLICATION); } #line 2005 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 69: #line 541 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Store Application Info */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Application with Version\n")); DEBUG_WRAP(PrintAttributeData("Application", &(yyvsp[-2].data));); SFAT_SetApplicationAttribute(&(yyvsp[-2].data), APPLICATION_ENTRY_APPLICATION); } #line 2016 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 70: #line 550 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Store Version Info */ DEBUG_WRAP(PrintAttributeData("Version", &(yyvsp[-1].data));); SFAT_SetApplicationAttribute(&(yyvsp[-1].data), APPLICATION_ENTRY_VERSION); } #line 2026 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 71: #line 558 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "ClientList (complete)\n");); } #line 2034 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 72: #line 564 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Start ClientList\n");); sfat_client_or_service = ATTRIBUTE_CLIENT; } #line 2043 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 73: #line 571 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "End ClientList\n");); } #line 2051 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 74: #line 576 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "EmptyClient\n");); } #line 2059 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 75: #line 580 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client ClientListData\n");); } #line 2067 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 76: #line 586 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Adding Complete\n");); SFAT_AddApplicationData(); DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Added\n");); } #line 2077 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 77: #line 594 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Start\n");); SFAT_CreateApplicationEntry(); } #line 2086 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 78: #line 601 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client End\n");); } #line 2094 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 79: #line 607 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data (no application)\n");); } #line 2102 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 80: #line 611 "sf_attribute_table.y" /* yacc.c:1648 */ { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data (application)\n");); } #line 2110 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 81: #line 617 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data Required (Proto)\n");); } #line 2119 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 82: #line 622 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data Required (IPProto Proto)\n");); } #line 2128 "sf_attribute_table.c" /* yacc.c:1648 */ break; case 83: #line 627 "sf_attribute_table.y" /* yacc.c:1648 */ { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data Required (Proto IPProto)\n");); } #line 2137 "sf_attribute_table.c" /* yacc.c:1648 */ break; #line 2141 "sf_attribute_table.c" /* yacc.c:1648 */ default: break; } /* User semantic actions sometimes alter yychar, and that requires that yytoken be updated with the new translation. We take the approach of translating immediately before every use of yytoken. One alternative is translating here after every semantic action, but that translation would be missed if the semantic action invokes YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an incorrect destructor might then be invoked immediately. In the case of YYERROR or YYBACKUP, subsequent parser actions might lead to an incorrect destructor call or verbose syntax error message before the lookahead is translated. */ YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); *++yyvsp = yyval; /* Now 'shift' the result of the reduction. Determine what state that goes to, based on the state we popped back to and the rule number reduced by. */ yyn = yyr1[yyn]; yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) yystate = yytable[yystate]; else yystate = yydefgoto[yyn - YYNTOKENS]; goto yynewstate; /*--------------------------------------. | yyerrlab -- here on detecting error. | `--------------------------------------*/ yyerrlab: /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar); /* If not already recovering from an error, report this error. */ if (!yyerrstatus) { ++yynerrs; #if ! YYERROR_VERBOSE yyerror (YY_("syntax error")); #else # define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \ yyssp, yytoken) { char const *yymsgp = YY_("syntax error"); int yysyntax_error_status; yysyntax_error_status = YYSYNTAX_ERROR; if (yysyntax_error_status == 0) yymsgp = yymsg; else if (yysyntax_error_status == 1) { if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc); if (!yymsg) { yymsg = yymsgbuf; yymsg_alloc = sizeof yymsgbuf; yysyntax_error_status = 2; } else { yysyntax_error_status = YYSYNTAX_ERROR; yymsgp = yymsg; } } yyerror (yymsgp); if (yysyntax_error_status == 2) goto yyexhaustedlab; } # undef YYSYNTAX_ERROR #endif } if (yyerrstatus == 3) { /* If just tried and failed to reuse lookahead token after an error, discard it. */ if (yychar <= YYEOF) { /* Return failure if at end of input. */ if (yychar == YYEOF) YYABORT; } else { yydestruct ("Error: discarding", yytoken, &yylval); yychar = YYEMPTY; } } /* Else will try to reuse lookahead token after shifting the error token. */ goto yyerrlab1; /*---------------------------------------------------. | yyerrorlab -- error raised explicitly by YYERROR. | `---------------------------------------------------*/ yyerrorlab: /* Pacify compilers like GCC when the user code never invokes YYERROR and the label yyerrorlab therefore never appears in user code. */ if (/*CONSTCOND*/ 0) goto yyerrorlab; /* Do not reclaim the symbols of the rule whose action triggered this YYERROR. */ YYPOPSTACK (yylen); yylen = 0; YY_STACK_PRINT (yyss, yyssp); yystate = *yyssp; goto yyerrlab1; /*-------------------------------------------------------------. | yyerrlab1 -- common code for both syntax error and YYERROR. | `-------------------------------------------------------------*/ yyerrlab1: yyerrstatus = 3; /* Each real token shifted decrements this. */ for (;;) { yyn = yypact[yystate]; if (!yypact_value_is_default (yyn)) { yyn += YYTERROR; if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) { yyn = yytable[yyn]; if (0 < yyn) break; } } /* Pop the current state because it cannot handle the error token. */ if (yyssp == yyss) YYABORT; yydestruct ("Error: popping", yystos[yystate], yyvsp); YYPOPSTACK (1); yystate = *yyssp; YY_STACK_PRINT (yyss, yyssp); } YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN *++yyvsp = yylval; YY_IGNORE_MAYBE_UNINITIALIZED_END /* Shift the error token. */ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); yystate = yyn; goto yynewstate; /*-------------------------------------. | yyacceptlab -- YYACCEPT comes here. | `-------------------------------------*/ yyacceptlab: yyresult = 0; goto yyreturn; /*-----------------------------------. | yyabortlab -- YYABORT comes here. | `-----------------------------------*/ yyabortlab: yyresult = 1; goto yyreturn; #if !defined yyoverflow || YYERROR_VERBOSE /*-------------------------------------------------. | yyexhaustedlab -- memory exhaustion comes here. | `-------------------------------------------------*/ yyexhaustedlab: yyerror (YY_("memory exhausted")); yyresult = 2; /* Fall through. */ #endif yyreturn: if (yychar != YYEMPTY) { /* Make sure we have latest lookahead translation. See comments at user semantic actions for why this is necessary. */ yytoken = YYTRANSLATE (yychar); yydestruct ("Cleanup: discarding lookahead", yytoken, &yylval); } /* Do not reclaim the symbols of the rule whose action triggered this YYABORT or YYACCEPT. */ YYPOPSTACK (yylen); YY_STACK_PRINT (yyss, yyssp); while (yyssp != yyss) { yydestruct ("Cleanup: popping", yystos[*yyssp], yyvsp); YYPOPSTACK (1); } #ifndef yyoverflow if (yyss != yyssa) YYSTACK_FREE (yyss); #endif #if YYERROR_VERBOSE if (yymsg != yymsgbuf) YYSTACK_FREE (yymsg); #endif return yyresult; } #line 632 "sf_attribute_table.y" /* yacc.c:1907 */ /* int yywrap(void) { return 1; } */ #endif /* TARGET_BASED */ snort-2.9.20/src/target-based/Makefile.in0000644000175000017500000004640414242725547016313 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/target-based ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libtarget_based_a_AR = $(AR) $(ARFLAGS) libtarget_based_a_LIBADD = am__libtarget_based_a_SOURCES_DIST = sftarget_reader.c \ sftarget_reader.h sftarget_hostentry.c sftarget_hostentry.h \ sftarget_protocol_reference.c sftarget_protocol_reference.h \ sf_attribute_table_parser.l sf_attribute_table.y @HAVE_TARGET_BASED_FALSE@am_libtarget_based_a_OBJECTS = \ @HAVE_TARGET_BASED_FALSE@ sftarget_reader.$(OBJEXT) @HAVE_TARGET_BASED_TRUE@am_libtarget_based_a_OBJECTS = \ @HAVE_TARGET_BASED_TRUE@ sftarget_reader.$(OBJEXT) \ @HAVE_TARGET_BASED_TRUE@ sftarget_hostentry.$(OBJEXT) \ @HAVE_TARGET_BASED_TRUE@ sftarget_protocol_reference.$(OBJEXT) \ @HAVE_TARGET_BASED_TRUE@ sf_attribute_table_parser.$(OBJEXT) \ @HAVE_TARGET_BASED_TRUE@ sf_attribute_table.$(OBJEXT) @HAVE_TARGET_BASED_TRUE@nodist_libtarget_based_a_OBJECTS = \ @HAVE_TARGET_BASED_TRUE@ sf_attribute_table_parser.$(OBJEXT) \ @HAVE_TARGET_BASED_TRUE@ sf_attribute_table.$(OBJEXT) libtarget_based_a_OBJECTS = $(am_libtarget_based_a_OBJECTS) \ $(nodist_libtarget_based_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) AM_V_LEX = $(am__v_LEX_@AM_V@) am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) am__v_LEX_0 = @echo " LEX " $@; am__v_LEX_1 = YLWRAP = $(top_srcdir)/ylwrap am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ -e s/c++$$/h++/ -e s/c$$/h/ YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) AM_V_YACC = $(am__v_YACC_@AM_V@) am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) am__v_YACC_0 = @echo " YACC " $@; am__v_YACC_1 = SOURCES = $(libtarget_based_a_SOURCES) \ $(nodist_libtarget_based_a_SOURCES) DIST_SOURCES = $(am__libtarget_based_a_SOURCES_DIST) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/ylwrap \ sf_attribute_table.c sf_attribute_table_parser.c DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies noinst_LIBRARIES = libtarget_based.a #BUILT_SOURCES = \ #sf_attribute_table_parser.c \ #sf_attribute_table.h \ #sf_attribute_table.c @HAVE_TARGET_BASED_TRUE@nodist_libtarget_based_a_SOURCES = \ @HAVE_TARGET_BASED_TRUE@sf_attribute_table_parser.c \ @HAVE_TARGET_BASED_TRUE@sf_attribute_table.h \ @HAVE_TARGET_BASED_TRUE@sf_attribute_table.c @HAVE_TARGET_BASED_FALSE@libtarget_based_a_SOURCES = sftarget_reader.c @HAVE_TARGET_BASED_TRUE@libtarget_based_a_SOURCES = \ @HAVE_TARGET_BASED_TRUE@sftarget_reader.c \ @HAVE_TARGET_BASED_TRUE@sftarget_reader.h \ @HAVE_TARGET_BASED_TRUE@sftarget_hostentry.c \ @HAVE_TARGET_BASED_TRUE@sftarget_hostentry.h \ @HAVE_TARGET_BASED_TRUE@sftarget_protocol_reference.c \ @HAVE_TARGET_BASED_TRUE@sftarget_protocol_reference.h \ @HAVE_TARGET_BASED_TRUE@sf_attribute_table_parser.l \ @HAVE_TARGET_BASED_TRUE@sf_attribute_table.y all: all-am .SUFFIXES: .SUFFIXES: .c .l .lo .o .obj .y $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/target-based/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/target-based/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libtarget_based.a: $(libtarget_based_a_OBJECTS) $(libtarget_based_a_DEPENDENCIES) $(EXTRA_libtarget_based_a_DEPENDENCIES) $(AM_V_at)-rm -f libtarget_based.a $(AM_V_AR)$(libtarget_based_a_AR) libtarget_based.a $(libtarget_based_a_OBJECTS) $(libtarget_based_a_LIBADD) $(AM_V_at)$(RANLIB) libtarget_based.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -rm -f sf_attribute_table.c -rm -f sf_attribute_table_parser.c clean: clean-am clean-am: clean-generic clean-libtool clean-local \ clean-noinstLIBRARIES mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-local clean-noinstLIBRARIES cscopelist-am \ ctags ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile .y.c: $(YACC) -d -psfat_ -o$@ $? #### Ugly to get the header file built. #### any other suggestions? sf_attribute_table.h: sf_attribute_table.y $(YACC) -d -psfat_ $? mv y.tab.h $@ .l.c: $(LEX) -i -o$@ $? sf_attribute_table_parser.c: sf_attribute_table_parser.l sf_attribute_table.h $(LEX) -i -Psfat -o$@ $< clean-local: rm -f \ sf_attribute_table_parser.c \ sf_attribute_table.h \ sf_attribute_table.c # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/target-based/sftarget_reader.c0000644000175000017500000010504414241077432017537 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sftarget_reader.c */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef HAVE_GETTID #define _GNU_SOURCE #endif #ifdef TARGET_BASED #include #include "mstring.h" #include "util.h" #include "parser.h" #include "sftarget_reader.h" #include "sftarget_protocol_reference.h" #include "sfutil/sfrt.h" #include "sfutil/sfxhash.h" #include "sfutil/util_net.h" #include "sftarget_hostentry.h" #include #include #include #include #include #include #include #ifndef WIN32 #include #include #include #endif #include "snort.h" #include "snort_debug.h" #include "sfPolicy.h" typedef struct { table_t *lookupTable; SFXHASH *mapTable; } tTargetBasedConfig; typedef struct { /**current configuration. */ tTargetBasedConfig curr; /**previous configuration. */ tTargetBasedConfig prev; /**next configuration. */ tTargetBasedConfig next; //XXX recheck this flag usage //char reload_attribute_table_flags; } tTargetBasedPolicyConfig; static tTargetBasedPolicyConfig targetBasedPolicyConfig; static HostAttributeEntry *current_host = NULL; static ApplicationEntry *current_app = NULL; //static MapData *current_map_entry = NULL; ServiceClient sfat_client_or_service; extern char sfat_error_message[STD_BUF]; extern char sfat_grammar_error_printed; extern char sfat_insufficient_space_logged; extern char sfat_fatal_error; int ParseTargetMap(char *filename); void DestroyBufferStack(void); extern char *sfat_saved_file; extern pthread_t attribute_reload_thread_id; extern pid_t attribute_reload_thread_pid; extern volatile int attribute_reload_thread_running; extern volatile int attribute_reload_thread_stop; extern int reload_attribute_table_flags; extern const struct timespec thread_sleep; /*****TODO: cleanup to use config directive *******/ #define ATTRIBUTE_MAP_MAX_ROWS 1024 uint32_t SFAT_NumberOfHosts(void) { tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; if (pConfig->curr.lookupTable) { return sfrt_num_entries(pConfig->curr.lookupTable); } return 0; } int SFAT_AddMapEntry(MapEntry *entry) { tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; if (!pConfig->next.mapTable) { /* Attribute Table node includes memory for each entry, * as defined by sizeof(MapEntry). */ pConfig->next.mapTable = sfxhash_new(ATTRIBUTE_MAP_MAX_ROWS, sizeof(int), sizeof(MapEntry), 0, 1, NULL, NULL, 1); if (!pConfig->next.mapTable) FatalError("Failed to allocate attribute map table\n"); } /* Memcopy MapEntry to newly allocated one and store in * a hash table based on entry->id for easy lookup. */ DEBUG_WRAP( DebugMessage(DEBUG_ATTRIBUTE, "Adding Map Entry: %d %s\n", entry->l_mapid, entry->s_mapvalue);); /* Data from entry will be copied into new node */ sfxhash_add(pConfig->next.mapTable, &entry->l_mapid, entry); return SFAT_OK; } char *SFAT_LookupAttributeNameById(int id) { MapEntry *entry; tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; if (!pConfig->next.mapTable) return NULL; entry = sfxhash_find(pConfig->next.mapTable, &id); if (entry) { DEBUG_WRAP( DebugMessage(DEBUG_ATTRIBUTE, "Found Attribute Name %s for Id %d\n", entry->s_mapvalue, id);); return entry->s_mapvalue; } DEBUG_WRAP( DebugMessage(DEBUG_ATTRIBUTE, "No Attribute Name for Id %d\n", id);); return NULL; } void FreeApplicationEntry(ApplicationEntry *app) { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Freeing ApplicationEntry: 0x%x\n", app);); free(app); } ApplicationEntry * SFAT_CreateApplicationEntry(void) { if (current_app) { /* Something went wrong */ FreeApplicationEntry(current_app); current_app = NULL; } current_app = SnortAlloc(sizeof(ApplicationEntry)); return current_app; } HostAttributeEntry * SFAT_CreateHostEntry(void) { if (current_host) { /* Something went wrong */ FreeHostEntry(current_host); current_host = NULL; } current_host = SnortAlloc(sizeof(HostAttributeEntry)); return current_host; } void FreeHostEntry(HostAttributeEntry *host) { ApplicationEntry *app = NULL, *tmp_app; if (!host) return; DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Freeing HostEntry: 0x%x\n", host);); /* Free the service list */ if (host->services) { do { tmp_app = host->services; app = tmp_app->next; FreeApplicationEntry(tmp_app); host->services = app; } while (app); } /* Free the client list */ if (host->clients) { do { tmp_app = host->clients; app = tmp_app->next; FreeApplicationEntry(tmp_app); host->clients = app; } while (app); } free(host); } void PrintAttributeData(char *prefix, AttributeData *data) { #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "AttributeData for %s\n", prefix); if (data->type == ATTRIBUTE_NAME) { DebugMessage(DEBUG_ATTRIBUTE, "\ttype: %s\tname: %s\t confidence %d\n", "Name", data->value.s_value, data->confidence); } else { DebugMessage(DEBUG_ATTRIBUTE, "\ttype: %s\tid: %s\t confidence %d", "Id", data->value.l_value, data->confidence); } #endif } int SFAT_SetHostIp(const char *ip) { static HostAttributeEntry *tmp_host = NULL; sfcidr_t ipAddr; tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; SFAT_CHECKHOST; if (sfip_pton(ip, &ipAddr) != SFIP_SUCCESS) { return SFAT_ERROR; } tmp_host = sfrt_lookup(&ipAddr.addr, pConfig->next.lookupTable); if (tmp_host && sfip_equals(tmp_host->ipAddr.addr, ipAddr.addr)) { /* Exact match. */ FreeHostEntry(current_host); current_host = tmp_host; } else { /* New entry for this host/CIDR */ sfip_set_ip(¤t_host->ipAddr, &ipAddr); } return SFAT_OK; } int SFAT_SetOSPolicy(char *policy_name, int attribute) { SFAT_CHECKHOST; switch (attribute) { case HOST_INFO_FRAG_POLICY: SnortStrncpy(current_host->hostInfo.fragPolicyName, policy_name, sizeof(current_host->hostInfo.fragPolicyName)); break; case HOST_INFO_STREAM_POLICY: SnortStrncpy(current_host->hostInfo.streamPolicyName, policy_name, sizeof(current_host->hostInfo.streamPolicyName)); break; } return SFAT_OK; } int SFAT_SetOSAttribute(AttributeData *data, int attribute) { SFAT_CHECKHOST; // currently not using os, vendor, or version return SFAT_OK; } static void AppendApplicationData(ApplicationList **list) { if (!list) return; if (*list) { current_app->next = *list; } *list = current_app; current_app = NULL; } int SFAT_AddApplicationData(void) { uint8_t required_fields; SFAT_CHECKAPP; SFAT_CHECKHOST; if (sfat_client_or_service == ATTRIBUTE_SERVICE) { required_fields = (APPLICATION_ENTRY_PORT | APPLICATION_ENTRY_IPPROTO | APPLICATION_ENTRY_PROTO); if ((current_app->fields & required_fields) != required_fields) { FatalError("%s(%d): Missing required field in Service attribute table for host %s\n", file_name, file_line, sfip_to_str(¤t_host->ipAddr.addr) ); } AppendApplicationData(¤t_host->services); } else { required_fields = (APPLICATION_ENTRY_PROTO); /* Currently, client data only includes PROTO, not IPPROTO */ if ((current_app->fields & required_fields) != required_fields) { FatalError("%s(%d): Missing required field in Client attribute table for host %s\n", file_name, file_line, sfip_to_str(¤t_host->ipAddr.addr) ); } AppendApplicationData(¤t_host->clients); } return SFAT_OK; } int SFAT_SetApplicationAttribute(AttributeData *data, int attribute) { SFAT_CHECKAPP; switch(attribute) { case APPLICATION_ENTRY_PORT: /* Convert the port to a integer */ if (data->type == ATTRIBUTE_NAME) { char *endPtr = NULL; unsigned long value = SnortStrtoul(data->value.s_value, &endPtr, 10); if ((endPtr == &data->value.s_value[0]) || (errno == ERANGE) || value > UINT16_MAX) { current_app->port = 0; return SFAT_ERROR; } current_app->port = (uint16_t)value; } else { if (data->value.l_value > UINT16_MAX) { current_app->port = 0; return SFAT_ERROR; } current_app->port = (uint16_t)data->value.l_value; } break; case APPLICATION_ENTRY_IPPROTO: /* Add IP Protocol to the reference list */ current_app->ipproto = AddProtocolReference(data->value.s_value); break; case APPLICATION_ENTRY_PROTO: /* Add Application Protocol to the reference list */ current_app->protocol = AddProtocolReference(data->value.s_value); break; // currently not using application or version default: attribute = 0; } current_app->fields |= attribute; return SFAT_OK; } #ifdef DEBUG_MSGS void PrintHostAttributeEntry(HostAttributeEntry *host) { ApplicationEntry *app; int i = 0; if (!host) return; DebugMessage(DEBUG_ATTRIBUTE, "Host IP: %s/%d\n", sfip_to_str(&host->ipAddr.addr), (sfip_family(&host->ipAddr) == AF_INET) ? ((host->ipAddr.bits >= 96) ? (host->ipAddr.bits - 96) : -1) : host->ipAddr.bits ); DebugMessage(DEBUG_ATTRIBUTE, "\tPolicy Information: frag:%s (%s %u) stream: %s (%s %u)\n", host->hostInfo.fragPolicyName, host->hostInfo.fragPolicySet ? "set":"unset", host->hostInfo.fragPolicy, host->hostInfo.fragPolicyName, host->hostInfo.streamPolicySet ? "set":"unset", host->hostInfo.streamPolicy); DebugMessage(DEBUG_ATTRIBUTE, "\tServices:\n"); for (i=0, app = host->services; app; app = app->next,i++) { DebugMessage(DEBUG_ATTRIBUTE, "\tService #%d:\n", i); DebugMessage(DEBUG_ATTRIBUTE, "\t\tIPProtocol: %s\tPort: %s\tProtocol %s\n", app->ipproto, app->port, app->protocol); } if (i==0) DebugMessage(DEBUG_ATTRIBUTE, "\t\tNone\n"); DebugMessage(DEBUG_ATTRIBUTE, "\tClients:\n"); for (i=0, app = host->clients; app; app = app->next,i++) { DebugMessage(DEBUG_ATTRIBUTE, "\tClient #%d:\n", i); DebugMessage(DEBUG_ATTRIBUTE, "\t\tIPProtocol: %s\tProtocol %s\n", app->ipproto, app->protocol); if (app->fields & APPLICATION_ENTRY_PORT) { DebugMessage(DEBUG_ATTRIBUTE, "\t\tPort: %s\n", app->port); } } if (i==0) { DebugMessage(DEBUG_ATTRIBUTE, "\t\tNone\n"); } } #endif int SFAT_AddHostEntryToMap(void) { HostAttributeEntry *host = current_host; int ret; sfcidr_t *ipAddr; tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; SFAT_CHECKHOST; DEBUG_WRAP(PrintHostAttributeEntry(host);); ipAddr = &host->ipAddr; ret = sfrt_insert(ipAddr, (unsigned char)ipAddr->bits, host, RT_FAVOR_SPECIFIC, pConfig->next.lookupTable); if (ret != RT_SUCCESS) { if (ret == RT_POLICY_TABLE_EXCEEDED) { if (!sfat_insufficient_space_logged) { SnortSnprintf(sfat_error_message, STD_BUF, "AttributeTable insertion failed: %d Insufficient " "space in attribute table, only configured to store %d hosts\n", ret, ScMaxAttrHosts(snort_conf)); sfat_grammar_error_printed = 1; sfat_insufficient_space_logged = 1; sfat_fatal_error = 0; } /* Reset return value and continue w/ only snort_conf->max_attribute_hosts */ ret = RT_SUCCESS; } else { SnortSnprintf(sfat_error_message, STD_BUF, "AttributeTable insertion failed: %d '%s'\n", ret, rt_error_messages[ret]); sfat_grammar_error_printed = 1; } FreeHostEntry(host); } current_host = NULL; return ret == RT_SUCCESS ? SFAT_OK : SFAT_ERROR; } HostAttributeEntry *SFAT_LookupHostEntryByIP(sfaddr_t *ipAddr) { tTargetBasedPolicyConfig *pConfig = NULL; tSfPolicyId policyId = getNapRuntimePolicy(); HostAttributeEntry *host = NULL; TargetBasedConfig *tbc = &snort_conf->targeted_policies[policyId]->target_based_config; if (tbc->args == NULL) { //this policy didn't specify attribute_table return NULL; } pConfig = &targetBasedPolicyConfig; host = sfrt_lookup(ipAddr, pConfig->curr.lookupTable); if (host) { /* Set the policy values for Frag & Stream if not already set */ //TODO: SetTargetBasedPolicy(host); } return host; } HostAttributeEntry *SFAT_LookupHostEntryBySrc(Packet *p) { if (!p || !p->iph_api) return NULL; return SFAT_LookupHostEntryByIP(GET_SRC_IP(p)); } HostAttributeEntry *SFAT_LookupHostEntryByDst(Packet *p) { if (!p || !p->iph_api) return NULL; return SFAT_LookupHostEntryByIP(GET_DST_IP(p)); } static GetPolicyIdFunc updatePolicyCallback; static GetPolicyIdsCallbackList *updatePolicyCallbackList = NULL; void SFAT_SetPolicyCallback(void *host_attr_ent) { HostAttributeEntry *host_entry = (HostAttributeEntry*)host_attr_ent; if (!host_entry) return; updatePolicyCallback(host_entry); return; } void SFAT_SetPolicyIds(GetPolicyIdFunc policyCallback, int snortPolicyId) { tTargetBasedPolicyConfig *pConfig = NULL; GetPolicyIdsCallbackList *list_entry, *new_list_entry = NULL; pConfig = &targetBasedPolicyConfig; updatePolicyCallback = policyCallback; sfrt_iterate(pConfig->curr.lookupTable, SFAT_SetPolicyCallback); if (!updatePolicyCallbackList) { /* No list present, so no attribute table... bye-bye */ return; } /* Look for this callback in the list */ list_entry = updatePolicyCallbackList; while (list_entry) { if (list_entry->policyCallback == policyCallback) return; /* We're done with this one */ if (list_entry->next) { list_entry = list_entry->next; } else { /* Leave list_entry pointint to last node in list */ break; } } /* Wasn't there, add it so that when we reload the table, * we can set those policy entries on reload. */ new_list_entry = (GetPolicyIdsCallbackList *)SnortAlloc(sizeof(GetPolicyIdsCallbackList)); new_list_entry->policyCallback = policyCallback; if (list_entry) { /* list_entry is valid here since there was at least an * empty head entry in the list. */ list_entry->next = new_list_entry; } } void SFAT_CleanupCallback(void *host_attr_ent) { HostAttributeEntry *host_entry = (HostAttributeEntry*)host_attr_ent; FreeHostEntry(host_entry); } void SFAT_CleanPrevConfig(void) { tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; if (pConfig->prev.mapTable) { sfxhash_delete(pConfig->prev.mapTable); pConfig->prev.mapTable = NULL; } if (pConfig->prev.lookupTable) { sfrt_cleanup(pConfig->prev.lookupTable, SFAT_CleanupCallback); sfrt_free(pConfig->prev.lookupTable); pConfig->prev.lookupTable = NULL; } reload_attribute_table_flags = 0; } void SFAT_Cleanup(void) { GetPolicyIdsCallbackList *list_entry, *tmp_list_entry = NULL; tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; if (pConfig->curr.mapTable) { sfxhash_delete(pConfig->curr.mapTable); pConfig->curr.mapTable = NULL; } if (pConfig->prev.mapTable) { sfxhash_delete(pConfig->prev.mapTable); pConfig->prev.mapTable = NULL; } if (pConfig->next.mapTable) { sfxhash_delete(pConfig->next.mapTable); pConfig->next.mapTable = NULL; } if (pConfig->curr.lookupTable) { sfrt_cleanup(pConfig->curr.lookupTable, SFAT_CleanupCallback); sfrt_free(pConfig->curr.lookupTable); pConfig->curr.lookupTable = NULL; } if (pConfig->prev.lookupTable) { sfrt_cleanup(pConfig->prev.lookupTable, SFAT_CleanupCallback); sfrt_free(pConfig->prev.lookupTable); pConfig->prev.lookupTable = NULL; } if (pConfig->next.lookupTable) { sfrt_cleanup(pConfig->next.lookupTable, SFAT_CleanupCallback); sfrt_free(pConfig->next.lookupTable); pConfig->next.lookupTable = NULL; } if (sfat_saved_file) { free(sfat_saved_file); sfat_saved_file = NULL; } if (updatePolicyCallbackList) { list_entry = updatePolicyCallbackList; while (list_entry) { tmp_list_entry = list_entry->next; free(list_entry); list_entry = tmp_list_entry; } updatePolicyCallbackList = NULL; } DestroyBufferStack(); } #define set_attribute_table_flag(flag) \ reload_attribute_table_flags |= flag; #define clear_attribute_table_flag(flag) \ reload_attribute_table_flags &= ~flag; #define check_attribute_table_flag(flag) \ (reload_attribute_table_flags & flag) void SigAttributeTableReloadHandler(int signal) { /* If we're already reloading, don't do anything. */ if (check_attribute_table_flag(ATTRIBUTE_TABLE_RELOADING_FLAG)) return; /* Set flag to reload attribute table */ set_attribute_table_flag(ATTRIBUTE_TABLE_RELOAD_FLAG); } void SFAT_VTAlrmHandler(int signal) { /* Do nothing, just used to wake the sleeping dog... */ return; } void *SFAT_ReloadAttributeTableThread(void *arg) { #ifndef WIN32 sigset_t mtmask, oldmask; int ret; int reloads = 0; tTargetBasedPolicyConfig *pConfig = NULL; pConfig = &targetBasedPolicyConfig; sigemptyset(&mtmask); attribute_reload_thread_pid = gettid(); /* Get the current set of signals inherited from main thread.*/ pthread_sigmask(SIG_UNBLOCK, &mtmask, &oldmask); /* Now block those signals from being delivered to this thread. * now Main receives all signals. */ pthread_sigmask(SIG_BLOCK, &oldmask, NULL); /* And allow SIGVTALRM through */ signal (SIGVTALRM, SFAT_VTAlrmHandler); if(errno!=0) errno=0; sigemptyset(&mtmask); sigaddset(&mtmask, SIGVTALRM); pthread_sigmask(SIG_UNBLOCK, &mtmask, NULL); attribute_reload_thread_running = 1; attribute_reload_thread_stop = 0; /* Checks the flag and terminates the attribute reload thread. * * Receipt of VTALRM signal pulls it out of the idle sleep (at * bottom of while(). Thread exits normally on next iteration * through its loop because stop flag is set. */ while (!attribute_reload_thread_stop) { #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "AttrReloadThread: Checking for new attr table...\n"); #endif ret = SFAT_ERROR; /* Is there an old table waiting to be cleaned up? */ if (check_attribute_table_flag(ATTRIBUTE_TABLE_TAKEN_FLAG)) { if (check_attribute_table_flag(ATTRIBUTE_TABLE_AVAILABLE_FLAG)) { #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "AttrReloadThread: Freeing old attr table...\n"); #endif /* Free the map and attribute tables that are stored in * prev.mapTable and prev.lookupTable */ sfxhash_delete(pConfig->prev.mapTable); pConfig->prev.mapTable = NULL; sfrt_cleanup(pConfig->prev.lookupTable, SFAT_CleanupCallback); sfrt_free(pConfig->prev.lookupTable); pConfig->prev.lookupTable = NULL; clear_attribute_table_flag(ATTRIBUTE_TABLE_AVAILABLE_FLAG); } clear_attribute_table_flag(ATTRIBUTE_TABLE_PARSE_FAILED_FLAG); clear_attribute_table_flag(ATTRIBUTE_TABLE_TAKEN_FLAG); continue; } else if (check_attribute_table_flag(ATTRIBUTE_TABLE_RELOAD_FLAG) && !check_attribute_table_flag(ATTRIBUTE_TABLE_AVAILABLE_FLAG) && !check_attribute_table_flag(ATTRIBUTE_TABLE_PARSE_FAILED_FLAG)) { /* Is there an new table ready? */ set_attribute_table_flag(ATTRIBUTE_TABLE_RELOADING_FLAG); #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "AttrReloadThread: loading new attr table.\n"); #endif reloads++; if (sfat_saved_file) { /* Initialize a new lookup table */ if (!pConfig->next.lookupTable) { /* Add 1 to max for table purposes * We use max_hosts to limit memcap, assume 16k per entry costs*/ pConfig->next.lookupTable = sfrt_new(DIR_8x16, IPv6, ScMaxAttrHosts(snort_conf) + 1, ((ScMaxAttrHosts(snort_conf))>>6) + 1); if (!pConfig->next.lookupTable) { SnortSnprintf(sfat_error_message, STD_BUF, "Failed to initialize memory for new attribute table\n"); clear_attribute_table_flag(ATTRIBUTE_TABLE_RELOAD_FLAG); clear_attribute_table_flag(ATTRIBUTE_TABLE_RELOADING_FLAG); set_attribute_table_flag(ATTRIBUTE_TABLE_PARSE_FAILED_FLAG); continue; } } ret = ParseTargetMap(sfat_saved_file); if (ret == SFAT_OK) { GetPolicyIdsCallbackList *list_entry = NULL; /* Set flag that a new table is available. Main * process will check that flag and do the swap. * After the new table is in use, the available * flag should be cleared, the taken flag gets set * and we'll go off and free the old one. */ /* Set the policy IDs in the new table... */ list_entry = (GetPolicyIdsCallbackList *)arg; while (list_entry) { if (list_entry->policyCallback) { sfrt_iterate(pConfig->next.lookupTable, (sfrt_iterator_callback)list_entry->policyCallback); } list_entry = list_entry->next; } set_attribute_table_flag(ATTRIBUTE_TABLE_AVAILABLE_FLAG); } else { /* Failed to parse, clean it up */ if (pConfig->next.mapTable) sfxhash_delete(pConfig->next.mapTable); pConfig->next.mapTable = NULL; sfrt_cleanup(pConfig->next.lookupTable, SFAT_CleanupCallback); sfrt_free(pConfig->next.lookupTable); pConfig->next.lookupTable = NULL; set_attribute_table_flag(ATTRIBUTE_TABLE_PARSE_FAILED_FLAG); } } clear_attribute_table_flag(ATTRIBUTE_TABLE_RELOAD_FLAG); clear_attribute_table_flag(ATTRIBUTE_TABLE_RELOADING_FLAG); } else { /* Sleep for 60 seconds */ #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "AttrReloadThread: Checked for new attr table... sleeping.\n"); #endif sleep(60); } } #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "AttrReloadThread: exiting... Handled %d reloads\n", reloads); #endif attribute_reload_thread_running = 0; pthread_exit(NULL); #endif /* !Win32 */ return NULL; } void AttributeTableReloadCheck(void) { tTargetBasedPolicyConfig *pConfig = NULL; pConfig = &targetBasedPolicyConfig; if (check_attribute_table_flag(ATTRIBUTE_TABLE_TAKEN_FLAG)) { return; /* Nothing to do, waiting for thread to clear this * flag... */ } /* Swap the attribute table pointers. */ else if ((pConfig != NULL) && check_attribute_table_flag(ATTRIBUTE_TABLE_AVAILABLE_FLAG)) { LogMessage("Swapping Attribute Tables.\n"); /***Do this on receipt of new packet ****/ /***Avoids need for mutex****/ pConfig->prev.lookupTable = pConfig->curr.lookupTable; pConfig->curr.lookupTable = pConfig->next.lookupTable; pConfig->next.lookupTable = NULL; pConfig->prev.mapTable = pConfig->curr.mapTable; pConfig->curr.mapTable = pConfig->next.mapTable; pConfig->next.mapTable = NULL; /* Set taken to indicate we've taken the new table */ set_attribute_table_flag(ATTRIBUTE_TABLE_TAKEN_FLAG); sfBase.iAttributeHosts = SFAT_NumberOfHosts(); sfBase.iAttributeReloads++; pc.attribute_table_reloads++; } else if (check_attribute_table_flag(ATTRIBUTE_TABLE_PARSE_FAILED_FLAG)) { LogMessage("%s", sfat_error_message); /* Set taken to indicate we've taken the error message */ set_attribute_table_flag(ATTRIBUTE_TABLE_TAKEN_FLAG); } } /**called once during initialization. Reads attribute table for the first time.*/ int SFAT_ParseAttributeTable(char *args, SnortConfig *sc) { char **toks; int num_toks; int ret; tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; DEBUG_WRAP(DebugMessage(DEBUG_CONFIGRULES,"AttributeTable\n");); /* Initialize lookup table */ if (!pConfig->next.lookupTable) { /* Add 1 to max for table purposes * We use max_hosts to limit memcap, assume 16k per entry costs*/ pConfig->next.lookupTable = sfrt_new(DIR_8x16, IPv6, ScMaxAttrHosts(sc) + 1, ((ScMaxAttrHosts(sc))>>6)+ 1); if (!pConfig->next.lookupTable) { FatalError("Failed to initialize attribute table memory\n"); } } /* Parse filename */ toks = mSplit(args, " \t", 0, &num_toks, 0); if (num_toks != 2) { FatalError("%s(%d) ==> attribute_table must have 2 parameters\n", file_name, file_line); } if (!(strcasecmp(toks[0], "filename") == 0)) { FatalError("%s(%d) ==> attribute_table must have 2 arguments, the 1st " "is 'filename'\n", file_name, file_line); } /* Reset... */ sfat_insufficient_space_logged = 0; sfat_fatal_error = 1; ret = ParseTargetMap(toks[1]); if (ret == SFAT_OK) { pConfig->curr.lookupTable = pConfig->next.lookupTable; pConfig->next.lookupTable = NULL; pConfig->curr.mapTable = pConfig->next.mapTable; pConfig->next.mapTable = NULL; if (sfat_insufficient_space_logged) LogMessage("%s", sfat_error_message); } else { LogMessage("%s", sfat_error_message); if (sfat_fatal_error) FatalError("%s(%d) ==> failed to load attribute table from %s\n", file_name, file_line, toks[1]); } mSplitFree(&toks, num_toks); /* Create Thread to handle reparsing stuff... */ sfBase.iAttributeHosts = SFAT_NumberOfHosts(); LogMessage("Attribute Table Loaded with " STDu64 " hosts\n", sfBase.iAttributeHosts); /* Set up the head (empty) node in the policy callback list to * pass to thread.*/ updatePolicyCallbackList = (GetPolicyIdsCallbackList *)SnortAlloc(sizeof(GetPolicyIdsCallbackList)); return SFAT_OK; } void SFAT_StartReloadThread(void) { #ifndef WIN32 LogMessage("Attribute Table Reload Thread Starting...\n"); if (pthread_create(&attribute_reload_thread_id, NULL, SFAT_ReloadAttributeTableThread, updatePolicyCallbackList)) { FatalError("Failed to start thread to handle reloading attribute table\n"); } while (!attribute_reload_thread_running) nanosleep(&thread_sleep, NULL); LogMessage("Attribute Table Reload Thread Started, thread %p (%u)\n", (void*)attribute_reload_thread_id, attribute_reload_thread_pid); #endif } int IsAdaptiveConfigured( void ) { SnortConfig *sc = snort_conf; tSfPolicyId id = getDefaultPolicy( ); if (id >= sc->num_policies_allocated) { ErrorMessage("%s(%d) Policy id is greater than the number of policies " "allocated.\n", __FILE__, __LINE__); return 0; } if ((sc->targeted_policies[id] == NULL) || (sc->targeted_policies[id]->target_based_config.args == NULL)) { return 0; } return 1; } int IsAdaptiveConfiguredForSnortConfig(struct _SnortConfig *sc) { tSfPolicyId id = getDefaultPolicy( ); if (sc == NULL) { FatalError("%s(%d) Snort conf for parsing is NULL.\n", __FILE__, __LINE__); } if (id >= sc->num_policies_allocated) { ErrorMessage("%s(%d) Policy id is greater than the number of policies " "allocated.\n", __FILE__, __LINE__); return 0; } if ((sc->targeted_policies[id] == NULL) || (sc->targeted_policies[id]->target_based_config.args == NULL)) { return 0; } return 1; } void SFAT_UpdateApplicationProtocol(sfaddr_t *ipAddr, uint16_t port, uint16_t protocol, uint16_t id) { HostAttributeEntry *host_entry; ApplicationEntry *service; tTargetBasedPolicyConfig *pConfig = &targetBasedPolicyConfig; unsigned service_count = 0; int rval; pConfig = &targetBasedPolicyConfig; host_entry = sfrt_lookup(ipAddr, pConfig->curr.lookupTable); if (!host_entry) { GetPolicyIdsCallbackList *list_entry; if (sfrt_num_entries(pConfig->curr.lookupTable) >= ScMaxAttrHosts(snort_conf)) return; host_entry = SnortAlloc(sizeof(*host_entry)); sfip_set_ip(&host_entry->ipAddr.addr, ipAddr); host_entry->ipAddr.bits = 128; if ((rval = sfrt_insert(&host_entry->ipAddr, (unsigned char)host_entry->ipAddr.bits, host_entry, RT_FAVOR_SPECIFIC, pConfig->curr.lookupTable)) != RT_SUCCESS) { FreeHostEntry(host_entry); return; } for (list_entry = updatePolicyCallbackList; list_entry; list_entry = list_entry->next) { if (list_entry->policyCallback) list_entry->policyCallback(host_entry); } service = NULL; } else { for (service = host_entry->services; service; service = service->next) { if (service->ipproto == protocol && (uint16_t)service->port == port) { break; } service_count++; } } if (!service) { if ( service_count >= ScMaxAttrServicesPerHost() ) return; service = SnortAlloc(sizeof(*service)); service->port = port; service->ipproto = protocol; service->next = host_entry->services; host_entry->services = service; service->protocol = id; } else if (service->protocol != id) { service->protocol = id; } } #ifdef SNORT_RELOAD void SFAT_ReloadCheck(SnortConfig *sc) { /* Adaptive profile has changed from OFF -> ON */ if(!IsAdaptiveConfiguredForSnortConfig(snort_conf) && IsAdaptiveConfiguredForSnortConfig(sc)) { tSfPolicyId defaultPolicyId = sfGetDefaultPolicy(sc->policy_config); TargetBasedConfig *tbc = &sc->targeted_policies[defaultPolicyId]->target_based_config; if (tbc && tbc->args) { SFAT_ParseAttributeTable(tbc->args, sc); } if(!(sc->run_flags & RUN_FLAG__DISABLE_ATTRIBUTE_RELOAD_THREAD)) SFAT_StartReloadThread(); #ifdef REG_TEST if(REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("Adaptive profile enabled, started attribute reload thread.\n"); } #endif } /* Adaptive profile already ON, enabled profile updates */ else if(IsAdaptiveConfiguredForSnortConfig(snort_conf) && IsAdaptiveConfiguredForSnortConfig(sc)) { if((snort_conf->run_flags & RUN_FLAG__DISABLE_ATTRIBUTE_RELOAD_THREAD) && !(sc->run_flags & RUN_FLAG__DISABLE_ATTRIBUTE_RELOAD_THREAD)) { SFAT_StartReloadThread(); } } } void ReloadAttributeThreadStop(void) { // Stop the attribute reload thread if (attribute_reload_thread_running) { /* Doing same stuff that is done in SnortCleanup() */ LogMessage("Terminating attribute reload thread\n"); attribute_reload_thread_stop = 1; pthread_kill(attribute_reload_thread_id, SIGVTALRM); pthread_join(attribute_reload_thread_id, NULL); } } #endif #endif /* TARGET_BASED */ snort-2.9.20/src/target-based/sf_attribute_table_parser.c0000644000175000017500000140263114242725720021620 0ustar apoapo#line 2 "sf_attribute_table_parser.c" #line 4 "sf_attribute_table_parser.c" #define YY_INT_ALIGNED short int /* A lexical scanner generated by flex */ #define yy_create_buffer sfat_create_buffer #define yy_delete_buffer sfat_delete_buffer #define yy_flex_debug sfat_flex_debug #define yy_init_buffer sfat_init_buffer #define yy_flush_buffer sfat_flush_buffer #define yy_load_buffer_state sfat_load_buffer_state #define yy_switch_to_buffer sfat_switch_to_buffer #define yyin sfatin #define yyleng sfatleng #define yylex sfatlex #define yylineno sfatlineno #define yyout sfatout #define yyrestart sfatrestart #define yytext sfattext #define yywrap sfatwrap #define yyalloc sfatalloc #define yyrealloc sfatrealloc #define yyfree sfatfree #define FLEX_SCANNER #define YY_FLEX_MAJOR_VERSION 2 #define YY_FLEX_MINOR_VERSION 6 #define YY_FLEX_SUBMINOR_VERSION 1 #if YY_FLEX_SUBMINOR_VERSION > 0 #define FLEX_BETA #endif /* First, we deal with platform-specific or compiler-specific issues. */ /* begin standard C headers. */ #include #include #include #include /* end standard C headers. */ /* flex integer type definitions */ #ifndef FLEXINT_H #define FLEXINT_H /* C99 systems have . Non-C99 systems may or may not. */ #if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, * if you want the limit (max/min) macros for int types. */ #ifndef __STDC_LIMIT_MACROS #define __STDC_LIMIT_MACROS 1 #endif #include typedef int8_t flex_int8_t; typedef uint8_t flex_uint8_t; typedef int16_t flex_int16_t; typedef uint16_t flex_uint16_t; typedef int32_t flex_int32_t; typedef uint32_t flex_uint32_t; #else typedef signed char flex_int8_t; typedef short int flex_int16_t; typedef int flex_int32_t; typedef unsigned char flex_uint8_t; typedef unsigned short int flex_uint16_t; typedef unsigned int flex_uint32_t; /* Limits of integral types. */ #ifndef INT8_MIN #define INT8_MIN (-128) #endif #ifndef INT16_MIN #define INT16_MIN (-32767-1) #endif #ifndef INT32_MIN #define INT32_MIN (-2147483647-1) #endif #ifndef INT8_MAX #define INT8_MAX (127) #endif #ifndef INT16_MAX #define INT16_MAX (32767) #endif #ifndef INT32_MAX #define INT32_MAX (2147483647) #endif #ifndef UINT8_MAX #define UINT8_MAX (255U) #endif #ifndef UINT16_MAX #define UINT16_MAX (65535U) #endif #ifndef UINT32_MAX #define UINT32_MAX (4294967295U) #endif #endif /* ! C99 */ #endif /* ! FLEXINT_H */ /* TODO: this is always defined, so inline it */ #define yyconst const #if defined(__GNUC__) && __GNUC__ >= 3 #define yynoreturn __attribute__((__noreturn__)) #else #define yynoreturn #endif /* Returned upon end-of-file. */ #define YY_NULL 0 /* Promotes a possibly negative, possibly signed char to an unsigned * integer for use as an array index. If the signed char is negative, * we want to instead treat it as an 8-bit unsigned char, hence the * double cast. */ #define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) /* Enter a start condition. This macro really ought to take a parameter, * but we do it the disgusting crufty way forced on us by the ()-less * definition of BEGIN. */ #define BEGIN (yy_start) = 1 + 2 * /* Translate the current start state into a value that can be later handed * to BEGIN to return to the state. The YYSTATE alias is for lex * compatibility. */ #define YY_START (((yy_start) - 1) / 2) #define YYSTATE YY_START /* Action number for EOF rule of a given start state. */ #define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) /* Special action meaning "start processing a new file". */ #define YY_NEW_FILE sfatrestart(sfatin ) #define YY_END_OF_BUFFER_CHAR 0 /* Size of default input buffer. */ #ifndef YY_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k. * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case. * Ditto for the __ia64__ case accordingly. */ #define YY_BUF_SIZE 32768 #else #define YY_BUF_SIZE 16384 #endif /* __ia64__ */ #endif /* The state buf must be large enough to hold one state per character in the main buffer. */ #define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) #ifndef YY_TYPEDEF_YY_BUFFER_STATE #define YY_TYPEDEF_YY_BUFFER_STATE typedef struct yy_buffer_state *YY_BUFFER_STATE; #endif #ifndef YY_TYPEDEF_YY_SIZE_T #define YY_TYPEDEF_YY_SIZE_T typedef size_t yy_size_t; #endif extern int sfatleng; extern FILE *sfatin, *sfatout; #define EOB_ACT_CONTINUE_SCAN 0 #define EOB_ACT_END_OF_FILE 1 #define EOB_ACT_LAST_MATCH 2 #define YY_LESS_LINENO(n) #define YY_LINENO_REWIND_TO(ptr) /* Return all but the first "n" matched characters back to the input stream. */ #define yyless(n) \ do \ { \ /* Undo effects of setting up sfattext. */ \ yy_size_t yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ *yy_cp = (yy_hold_char); \ YY_RESTORE_YY_MORE_OFFSET \ (yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ YY_DO_BEFORE_ACTION; /* set up sfattext again */ \ } \ while ( 0 ) #define unput(c) yyunput( c, (yytext_ptr) ) #ifndef YY_STRUCT_YY_BUFFER_STATE #define YY_STRUCT_YY_BUFFER_STATE struct yy_buffer_state { FILE *yy_input_file; char *yy_ch_buf; /* input buffer */ char *yy_buf_pos; /* current position in input buffer */ /* Size of input buffer in bytes, not including room for EOB * characters. */ int yy_buf_size; /* Number of characters read into yy_ch_buf, not including EOB * characters. */ int yy_n_chars; /* Whether we "own" the buffer - i.e., we know we created it, * and can realloc() it to grow it, and should free() it to * delete it. */ int yy_is_our_buffer; /* Whether this is an "interactive" input source; if so, and * if we're using stdio for input, then we want to use getc() * instead of fread(), to make sure we stop fetching input after * each newline. */ int yy_is_interactive; /* Whether we're considered to be at the beginning of a line. * If so, '^' rules will be active on the next match, otherwise * not. */ int yy_at_bol; int yy_bs_lineno; /**< The line count. */ int yy_bs_column; /**< The column count. */ /* Whether to try to fill the input buffer when we reach the * end of it. */ int yy_fill_buffer; int yy_buffer_status; #define YY_BUFFER_NEW 0 #define YY_BUFFER_NORMAL 1 /* When an EOF's been seen but there's still some text to process * then we mark the buffer as YY_EOF_PENDING, to indicate that we * shouldn't try reading from the input source any more. We might * still have a bunch of tokens to match, though, because of * possible backing-up. * * When we actually see the EOF, we change the status to "new" * (via sfatrestart()), so that the user can continue scanning by * just pointing sfatin at a new input file. */ #define YY_BUFFER_EOF_PENDING 2 }; #endif /* !YY_STRUCT_YY_BUFFER_STATE */ /* Stack of input buffers. */ static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */ static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */ static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */ /* We provide macros for accessing buffer states in case in the * future we want to put the buffer states in a more general * "scanner state". * * Returns the top of the stack, or NULL. */ #define YY_CURRENT_BUFFER ( (yy_buffer_stack) \ ? (yy_buffer_stack)[(yy_buffer_stack_top)] \ : NULL) /* Same as previous macro, but useful when we know that the buffer stack is not * NULL or when we need an lvalue. For internal use only. */ #define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)] /* yy_hold_char holds the character lost when sfattext is formed. */ static char yy_hold_char; static int yy_n_chars; /* number of characters read into yy_ch_buf */ int sfatleng; /* Points to current character in buffer. */ static char *yy_c_buf_p = NULL; static int yy_init = 0; /* whether we need to initialize */ static int yy_start = 0; /* start state number */ /* Flag which is used to allow sfatwrap()'s to do buffer switches * instead of setting up a fresh sfatin. A bit of a hack ... */ static int yy_did_buffer_switch_on_eof; void sfatrestart (FILE *input_file ); void sfat_switch_to_buffer (YY_BUFFER_STATE new_buffer ); YY_BUFFER_STATE sfat_create_buffer (FILE *file,int size ); void sfat_delete_buffer (YY_BUFFER_STATE b ); void sfat_flush_buffer (YY_BUFFER_STATE b ); void sfatpush_buffer_state (YY_BUFFER_STATE new_buffer ); void sfatpop_buffer_state (void ); static void sfatensure_buffer_stack (void ); static void sfat_load_buffer_state (void ); static void sfat_init_buffer (YY_BUFFER_STATE b,FILE *file ); #define YY_FLUSH_BUFFER sfat_flush_buffer(YY_CURRENT_BUFFER ) YY_BUFFER_STATE sfat_scan_buffer (char *base,yy_size_t size ); YY_BUFFER_STATE sfat_scan_string (yyconst char *yy_str ); YY_BUFFER_STATE sfat_scan_bytes (yyconst char *bytes,int len ); void *sfatalloc (yy_size_t ); void *sfatrealloc (void *,yy_size_t ); void sfatfree (void * ); #define yy_new_buffer sfat_create_buffer #define yy_set_interactive(is_interactive) \ { \ if ( ! YY_CURRENT_BUFFER ){ \ sfatensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ sfat_create_buffer(sfatin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ } #define yy_set_bol(at_bol) \ { \ if ( ! YY_CURRENT_BUFFER ){\ sfatensure_buffer_stack (); \ YY_CURRENT_BUFFER_LVALUE = \ sfat_create_buffer(sfatin,YY_BUF_SIZE ); \ } \ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ } #define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) /* Begin user sect3 */ #define sfatwrap() (/*CONSTCOND*/1) #define YY_SKIP_YYWRAP typedef unsigned char YY_CHAR; FILE *sfatin = NULL, *sfatout = NULL; typedef int yy_state_type; extern int sfatlineno; int sfatlineno = 1; extern char *sfattext; #ifdef yytext_ptr #undef yytext_ptr #endif #define yytext_ptr sfattext static yy_state_type yy_get_previous_state (void ); static yy_state_type yy_try_NUL_trans (yy_state_type current_state ); static int yy_get_next_buffer (void ); static void yynoreturn yy_fatal_error (yyconst char* msg ); /* Done after the current pattern has been matched and before the * corresponding action - sets up sfattext. */ #define YY_DO_BEFORE_ACTION \ (yytext_ptr) = yy_bp; \ sfatleng = (int) (yy_cp - yy_bp); \ (yy_hold_char) = *yy_cp; \ *yy_cp = '\0'; \ (yy_c_buf_p) = yy_cp; #define YY_NUM_RULES 57 #define YY_END_OF_BUFFER 58 /* This struct is not used in this scanner, but its presence is necessary. */ struct yy_trans_info { flex_int32_t yy_verify; flex_int32_t yy_nxt; }; static yyconst flex_int16_t yy_accept[8615] = { 0, 1, 1, 0, 0, 0, 0, 58, 56, 1, 54, 56, 53, 53, 52, 56, 57, 1, 53, 54, 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 53, 52, 52, 0, 55, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 0, 0, 0, 9, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 26, 0, 48, 0, 0, 0, 0, 0, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 0, 0, 15, 0, 27, 0, 49, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 11, 0, 0, 0, 0, 42, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 0, 1, 53, 52, 52, 0, 0, 43, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 0, 0, 0, 40, 0, 0, 44, 0, 0, 38, 0, 0, 0, 30, 1, 53, 52, 52, 0, 0, 41, 0, 0, 45, 0, 0, 39, 0, 0, 0, 31, 0, 0, 0, 0, 0, 46, 36, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 0, 47, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 0, 0, 0, 1, 53, 52, 52, 0, 0, 0, 0, 0, 25, 0, 0, 0, 0, 50, 0, 0, 0, 0, 32, 0, 0, 0, 1, 53, 52, 52, 51, 0, 0, 0, 0, 33, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 1, 53, 52, 52, 23, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 34, 1, 53, 52, 52, 5, 0, 0, 0, 0, 35, 0, 0, 0, 0, 1, 53, 52, 0, 0, 0, 0, 12, 20, 0, 0, 1, 53, 13, 21, 0, 0, 18, 2, 1, 53, 19, 3, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 53, 1, 0 } ; static yyconst YY_CHAR yy_ec[256] = { 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 6, 7, 6, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 6, 6, 10, 6, 11, 6, 6, 12, 13, 14, 15, 16, 17, 18, 19, 20, 6, 6, 21, 22, 23, 24, 25, 6, 26, 27, 28, 29, 30, 6, 6, 31, 6, 6, 6, 6, 6, 32, 6, 33, 34, 35, 36, 37, 38, 39, 40, 41, 6, 6, 42, 43, 44, 45, 46, 6, 47, 48, 49, 50, 51, 6, 6, 52, 6, 6, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } ; static yyconst YY_CHAR yy_meta[53] = { 0, 1, 2, 3, 1, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 } ; static yyconst flex_uint16_t yy_base[12713] = { 0, 0, 11, 0, 0, 0, 0, 8934, 8935, 8931, 8935, 8929, 0, 8922, 8921, 18, 8935, 8927, 0, 8935, 8919, 8918, 8915, 58, 0, 12, 6, 5, 15, 61, 7, 19, 47, 85, 98, 8923, 0, 8915, 8914, 8911, 8910, 87, 95, 24, 53, 60, 96, 105, 93, 99, 125, 112, 101, 102, 123, 127, 124, 142, 128, 8909, 145, 135, 142, 133, 136, 135, 139, 139, 145, 162, 8917, 0, 8909, 8908, 143, 144, 157, 160, 162, 180, 167, 8905, 185, 173, 181, 173, 176, 175, 178, 177, 184, 191, 186, 182, 196, 202, 199, 209, 202, 8935, 8935, 206, 218, 210, 209, 211, 211, 216, 227, 215, 230, 220, 8913, 0, 8905, 8904, 228, 224, 236, 237, 230, 241, 233, 8935, 8935, 241, 253, 246, 246, 248, 248, 253, 265, 254, 269, 258, 266, 267, 266, 271, 261, 8880, 8900, 270, 8899, 284, 8898, 274, 281, 275, 297, 295, 290, 296, 8906, 0, 8898, 8897, 298, 300, 298, 303, 294, 8873, 8893, 302, 8892, 315, 8891, 304, 309, 303, 321, 318, 312, 318, 326, 330, 317, 336, 8890, 328, 8935, 328, 8935, 330, 8935, 346, 348, 8868, 341, 8888, 339, 343, 8896, 0, 8888, 8887, 354, 356, 342, 356, 8884, 348, 8935, 347, 8935, 348, 8935, 364, 366, 8862, 360, 8882, 359, 363, 381, 366, 391, 382, 8935, 376, 380, 385, 383, 393, 398, 8860, 8935, 8880, 388, 8888, 0, 8880, 8879, 400, 384, 409, 401, 8935, 398, 399, 404, 403, 413, 423, 8855, 8935, 8875, 414, 412, 413, 8935, 8874, 419, 425, 8873, 424, 428, 442, 423, 427, 8935, 8872, 8880, 0, 8872, 8871, 426, 427, 8935, 8868, 436, 439, 8867, 441, 444, 455, 443, 449, 8935, 8866, 457, 462, 8935, 465, 463, 8935, 466, 8865, 8935, 8864, 459, 464, 8935, 8872, 0, 8864, 8863, 469, 475, 8935, 479, 474, 8935, 478, 8860, 8935, 8859, 469, 477, 8935, 478, 8837, 490, 493, 8836, 8935, 8935, 485, 492, 8865, 0, 8857, 8856, 492, 8832, 503, 506, 8831, 8935, 8935, 495, 503, 502, 527, 8851, 498, 503, 511, 513, 8859, 0, 8851, 8850, 512, 559, 8847, 507, 512, 523, 524, 8846, 533, 541, 549, 550, 8935, 8845, 532, 554, 557, 8853, 0, 8845, 8844, 8841, 557, 561, 563, 565, 8935, 8840, 549, 572, 572, 8935, 8839, 565, 578, 574, 8935, 570, 570, 572, 8847, 0, 8839, 8838, 8935, 8835, 579, 592, 588, 8935, 586, 585, 584, 8935, 8834, 596, 590, 593, 594, 8833, 8841, 0, 8833, 8832, 8935, 8829, 602, 598, 600, 601, 8828, 8935, 615, 616, 617, 621, 8935, 8836, 0, 8828, 0, 8935, 623, 625, 629, 630, 8935, 8825, 8824, 625, 624, 8832, 0, 0, 8822, 8821, 633, 629, 8935, 8935, 8820, 8819, 8827, 0, 8935, 8935, 8817, 8816, 8935, 8935, 8824, 0, 8935, 8935, 8823, 0, 8822, 0, 8821, 0, 8820, 0, 8819, 0, 8818, 0, 8817, 0, 8816, 0, 8815, 0, 8814, 0, 8813, 0, 8812, 0, 8811, 0, 8810, 0, 8809, 0, 8808, 0, 8807, 0, 8806, 0, 8805, 0, 8804, 0, 8803, 0, 8802, 0, 8801, 0, 8800, 0, 8799, 0, 8798, 0, 8797, 0, 8796, 0, 8795, 0, 8794, 0, 8793, 0, 8792, 0, 8791, 0, 8790, 0, 8789, 0, 8788, 0, 8787, 0, 8786, 0, 8785, 0, 8784, 0, 8783, 0, 8782, 0, 8781, 0, 8780, 0, 8779, 0, 8778, 0, 8777, 0, 8776, 0, 8775, 0, 8774, 0, 8773, 0, 8772, 0, 8771, 0, 8770, 0, 8769, 0, 8768, 0, 8767, 0, 8766, 0, 8765, 0, 8764, 0, 8763, 0, 8762, 0, 8761, 0, 8760, 0, 8759, 0, 8758, 0, 8757, 0, 8756, 0, 8755, 0, 8754, 0, 8753, 0, 8752, 0, 8751, 0, 8750, 0, 8749, 0, 8748, 0, 8747, 0, 8746, 0, 8745, 0, 8744, 0, 8743, 0, 8742, 0, 8741, 0, 8740, 0, 8739, 0, 8738, 0, 8737, 0, 8736, 0, 8735, 0, 8734, 0, 8733, 0, 8732, 0, 8731, 0, 8730, 0, 8729, 0, 8728, 0, 8727, 0, 8726, 0, 8725, 0, 8724, 0, 8723, 0, 8722, 0, 8721, 0, 8720, 0, 8719, 0, 8718, 0, 8717, 0, 8716, 0, 8715, 0, 8714, 0, 8713, 0, 8712, 0, 8711, 0, 8710, 0, 8709, 0, 8708, 0, 8707, 0, 8706, 0, 8705, 0, 8704, 0, 8703, 0, 8702, 0, 8701, 0, 8700, 0, 8699, 0, 8698, 0, 8697, 0, 8696, 0, 8695, 0, 8694, 0, 8693, 0, 8692, 0, 8691, 0, 8690, 0, 8689, 0, 8688, 0, 8687, 0, 8686, 0, 8685, 0, 8684, 0, 8683, 0, 8682, 0, 8681, 0, 8680, 0, 8679, 0, 8678, 0, 8677, 0, 8676, 0, 8675, 0, 8674, 0, 8673, 0, 8672, 0, 8671, 0, 8670, 0, 8669, 0, 8668, 0, 8667, 0, 8666, 0, 8665, 0, 8664, 0, 8663, 0, 8662, 0, 8661, 0, 8660, 0, 8659, 0, 8658, 0, 8657, 0, 8656, 0, 8655, 0, 8654, 0, 8653, 0, 8652, 0, 8651, 0, 8650, 0, 8649, 0, 8648, 0, 8647, 0, 8646, 0, 8645, 0, 8644, 0, 8643, 0, 8642, 0, 8641, 0, 8640, 0, 8639, 0, 8638, 0, 8637, 0, 8636, 0, 8635, 0, 8634, 0, 8633, 0, 8632, 0, 8631, 0, 8630, 0, 8629, 0, 8628, 0, 8627, 0, 8626, 0, 8625, 0, 8624, 0, 8623, 0, 8622, 0, 8621, 0, 8620, 0, 8619, 0, 8618, 0, 8617, 0, 8616, 0, 8615, 0, 8614, 0, 8613, 0, 8612, 0, 8611, 0, 8610, 0, 8609, 0, 8608, 0, 8607, 0, 8606, 0, 8605, 0, 8604, 0, 8603, 0, 8602, 0, 8601, 0, 8600, 0, 8599, 0, 8598, 0, 8597, 0, 8596, 0, 8595, 0, 8594, 0, 8593, 0, 8592, 0, 8591, 0, 8590, 0, 8589, 0, 8588, 0, 8587, 0, 8586, 0, 8585, 0, 8584, 0, 8583, 0, 8582, 0, 8581, 0, 8580, 0, 8579, 0, 8578, 0, 8577, 0, 8576, 0, 8575, 0, 8574, 0, 8573, 0, 8572, 0, 8571, 0, 8570, 0, 8569, 0, 8568, 0, 8567, 0, 8566, 0, 8565, 0, 8564, 0, 8563, 0, 8562, 0, 8561, 0, 8560, 0, 8559, 0, 8558, 0, 8557, 0, 8556, 0, 8555, 0, 8554, 0, 8553, 0, 8552, 0, 8551, 0, 8550, 0, 8549, 0, 8548, 0, 8547, 0, 8546, 0, 8545, 0, 8544, 0, 8543, 0, 8542, 0, 8541, 0, 8540, 0, 8539, 0, 8538, 0, 8537, 0, 8536, 0, 8535, 0, 8534, 0, 8533, 0, 8532, 0, 8531, 0, 8530, 0, 8529, 0, 8528, 0, 8527, 0, 8526, 0, 8525, 0, 8524, 0, 8523, 0, 8522, 0, 8521, 0, 8520, 0, 8519, 0, 8518, 0, 8517, 0, 8516, 0, 8515, 0, 8514, 0, 8513, 0, 8512, 0, 8511, 0, 8510, 0, 8509, 0, 8508, 0, 8507, 0, 8506, 0, 8505, 0, 8504, 0, 8503, 0, 8502, 0, 8501, 0, 8500, 0, 8499, 0, 8498, 0, 8497, 0, 8496, 0, 8495, 0, 8494, 0, 8493, 0, 8492, 0, 8491, 0, 8490, 0, 8489, 0, 8488, 0, 8487, 0, 8486, 0, 8485, 0, 8484, 0, 8483, 0, 8482, 0, 8481, 0, 8480, 0, 8479, 0, 8478, 0, 8477, 0, 8476, 0, 8475, 0, 8474, 0, 8473, 0, 8472, 0, 8471, 0, 8470, 0, 8469, 0, 8468, 0, 8467, 0, 8466, 0, 8465, 0, 8464, 0, 8463, 0, 8462, 0, 8461, 0, 8460, 0, 8459, 0, 8458, 0, 8457, 0, 8456, 0, 8455, 0, 8454, 0, 8453, 0, 8452, 0, 8451, 0, 8450, 0, 8449, 0, 8448, 0, 8447, 0, 8446, 0, 8445, 0, 8444, 0, 8443, 0, 8442, 0, 8441, 0, 8440, 0, 8439, 0, 8438, 0, 8437, 0, 8436, 0, 8435, 0, 8434, 0, 3, 0, 4, 0, 6, 0, 14, 0, 15, 0, 22, 0, 25, 0, 59, 0, 65, 0, 78, 0, 85, 0, 87, 0, 88, 0, 118, 0, 125, 0, 165, 0, 413, 0, 524, 0, 534, 0, 556, 0, 564, 0, 655, 0, 657, 0, 659, 0, 661, 0, 662, 0, 663, 0, 667, 0, 668, 0, 669, 0, 671, 0, 672, 0, 673, 0, 676, 0, 677, 0, 678, 0, 679, 0, 680, 0, 681, 0, 682, 0, 683, 0, 684, 0, 685, 0, 686, 0, 687, 0, 688, 0, 689, 0, 690, 0, 691, 0, 692, 0, 693, 0, 694, 0, 695, 0, 696, 0, 697, 0, 698, 0, 699, 0, 700, 0, 701, 0, 702, 0, 703, 0, 704, 0, 705, 0, 706, 0, 707, 0, 708, 0, 709, 0, 710, 0, 711, 0, 712, 0, 713, 0, 714, 0, 715, 0, 716, 0, 717, 0, 718, 0, 719, 0, 720, 0, 721, 0, 722, 0, 723, 0, 724, 0, 725, 0, 726, 0, 727, 0, 728, 0, 729, 0, 730, 0, 731, 0, 732, 0, 733, 0, 734, 0, 735, 0, 736, 0, 737, 0, 738, 0, 739, 0, 740, 0, 741, 0, 742, 0, 743, 0, 744, 0, 745, 0, 746, 0, 747, 0, 748, 0, 749, 0, 750, 0, 751, 0, 752, 0, 753, 0, 754, 0, 755, 0, 756, 0, 757, 0, 758, 0, 759, 0, 760, 0, 761, 0, 762, 0, 763, 0, 764, 0, 765, 0, 766, 0, 767, 0, 768, 0, 769, 0, 770, 0, 771, 0, 772, 0, 773, 0, 774, 0, 775, 0, 776, 0, 777, 0, 778, 0, 779, 0, 780, 0, 781, 0, 782, 0, 783, 0, 784, 0, 785, 0, 786, 0, 787, 0, 788, 0, 789, 0, 790, 0, 791, 0, 792, 0, 793, 0, 794, 0, 795, 0, 796, 0, 797, 0, 798, 0, 799, 0, 800, 0, 801, 0, 802, 0, 803, 0, 804, 0, 805, 0, 806, 0, 807, 0, 808, 0, 809, 0, 810, 0, 811, 0, 812, 0, 813, 0, 814, 0, 815, 0, 816, 0, 817, 0, 818, 0, 819, 0, 820, 0, 821, 0, 822, 0, 823, 0, 824, 0, 825, 0, 826, 0, 827, 0, 828, 0, 829, 0, 830, 0, 831, 0, 832, 0, 833, 0, 834, 0, 835, 0, 836, 0, 837, 0, 838, 0, 839, 0, 840, 0, 841, 0, 842, 0, 843, 0, 844, 0, 845, 0, 846, 0, 847, 0, 848, 0, 849, 0, 850, 0, 851, 0, 852, 0, 853, 0, 854, 0, 855, 0, 856, 0, 857, 0, 858, 0, 859, 0, 860, 0, 861, 0, 862, 0, 863, 0, 864, 0, 865, 0, 866, 0, 867, 0, 868, 0, 869, 0, 870, 0, 871, 0, 872, 0, 873, 0, 874, 0, 875, 0, 876, 0, 877, 0, 878, 0, 879, 0, 880, 0, 881, 0, 882, 0, 883, 0, 884, 0, 885, 0, 886, 0, 887, 0, 888, 0, 889, 0, 890, 0, 891, 0, 892, 0, 893, 0, 894, 0, 895, 0, 896, 0, 897, 0, 898, 0, 899, 0, 900, 0, 901, 0, 902, 0, 903, 0, 904, 0, 905, 0, 906, 0, 907, 0, 908, 0, 909, 0, 910, 0, 911, 0, 912, 0, 913, 0, 914, 0, 915, 0, 916, 0, 917, 0, 918, 0, 919, 0, 920, 0, 921, 0, 922, 0, 923, 0, 924, 0, 925, 0, 926, 0, 927, 0, 928, 0, 929, 0, 930, 0, 931, 0, 932, 0, 933, 0, 934, 0, 935, 0, 936, 0, 937, 0, 938, 0, 939, 0, 940, 0, 941, 0, 942, 0, 943, 0, 944, 0, 945, 0, 946, 0, 947, 0, 948, 0, 949, 0, 950, 0, 951, 0, 952, 0, 953, 0, 954, 0, 955, 0, 956, 0, 957, 0, 958, 0, 959, 0, 960, 0, 961, 0, 962, 0, 963, 0, 964, 0, 965, 0, 966, 0, 967, 0, 968, 0, 969, 0, 970, 0, 971, 0, 972, 0, 973, 0, 974, 0, 975, 0, 976, 0, 977, 0, 978, 0, 979, 0, 980, 0, 981, 0, 982, 0, 983, 0, 984, 0, 985, 0, 986, 0, 987, 0, 988, 0, 989, 0, 990, 0, 991, 0, 992, 0, 993, 0, 994, 0, 995, 0, 996, 0, 997, 0, 998, 0, 999, 0, 1000, 0, 1001, 0, 1002, 0, 1003, 0, 1004, 0, 1005, 0, 1006, 0, 1007, 0, 1008, 0, 1009, 0, 1010, 0, 1011, 0, 1012, 0, 1013, 0, 1014, 0, 1015, 0, 1016, 0, 1017, 0, 1018, 0, 1019, 0, 1020, 0, 1021, 0, 1022, 0, 1023, 0, 1024, 0, 1025, 0, 1026, 0, 1027, 0, 1028, 0, 1029, 0, 1030, 0, 1031, 0, 1032, 0, 1033, 0, 1034, 0, 1035, 0, 1036, 0, 1037, 0, 1038, 0, 1039, 0, 1040, 0, 1041, 0, 1042, 0, 1043, 0, 1044, 0, 1045, 0, 1046, 0, 1047, 0, 1048, 0, 1049, 0, 1050, 0, 1051, 0, 1052, 0, 1053, 0, 1054, 0, 1055, 0, 1056, 0, 1057, 0, 1058, 0, 1059, 0, 1060, 0, 1061, 0, 1062, 0, 1063, 0, 1064, 0, 1065, 0, 1066, 0, 1067, 0, 1068, 0, 1069, 0, 1070, 0, 1071, 0, 1072, 0, 1073, 0, 1074, 0, 1075, 0, 1076, 0, 1077, 0, 1078, 0, 1079, 0, 1080, 0, 1081, 0, 1082, 0, 1083, 0, 1084, 0, 1085, 0, 1086, 0, 1087, 0, 1088, 0, 1089, 0, 1090, 0, 1091, 0, 1092, 0, 1093, 0, 1094, 0, 1095, 0, 1096, 0, 1097, 0, 1098, 0, 1099, 0, 1100, 0, 1101, 0, 1102, 0, 1103, 0, 1104, 0, 1105, 0, 1106, 0, 1107, 0, 1108, 0, 1109, 0, 1110, 0, 1111, 0, 1112, 0, 1113, 0, 1114, 0, 1115, 0, 1116, 0, 1117, 0, 1118, 0, 1119, 0, 1120, 0, 1121, 0, 1122, 0, 1123, 0, 1124, 0, 1125, 0, 1126, 0, 1127, 0, 1128, 0, 1129, 0, 1130, 0, 1131, 0, 1132, 0, 1133, 0, 1134, 0, 1135, 0, 1136, 0, 1137, 0, 1138, 0, 1139, 0, 1140, 0, 1141, 0, 1142, 0, 1143, 0, 1144, 0, 1145, 0, 1146, 0, 1147, 0, 1148, 0, 1149, 0, 1150, 0, 1151, 0, 1152, 0, 1153, 0, 1154, 0, 1155, 0, 1156, 0, 1157, 0, 1158, 0, 1159, 0, 1160, 0, 1161, 0, 1162, 0, 1163, 0, 1164, 0, 1165, 0, 1166, 0, 1167, 0, 1168, 0, 1169, 0, 1170, 0, 1171, 0, 1172, 0, 1173, 0, 1174, 0, 1175, 0, 1176, 0, 1177, 0, 1178, 0, 1179, 0, 1180, 0, 1181, 0, 1182, 0, 1183, 0, 1184, 0, 1185, 0, 1186, 0, 1187, 0, 1188, 0, 1189, 0, 1190, 0, 1191, 0, 1192, 0, 1193, 0, 1194, 0, 1195, 0, 1196, 0, 1197, 0, 1198, 0, 1199, 0, 1200, 0, 1201, 0, 1202, 0, 1203, 0, 1204, 0, 1205, 0, 1206, 0, 1207, 0, 1208, 0, 1209, 0, 1210, 0, 1211, 0, 1212, 0, 1213, 0, 1214, 0, 1215, 0, 1216, 0, 1217, 0, 1218, 0, 1219, 0, 1220, 0, 1221, 0, 1222, 0, 1223, 0, 1224, 0, 1225, 0, 1226, 0, 1227, 0, 1228, 0, 1229, 0, 1230, 0, 1231, 0, 1232, 0, 1233, 0, 1234, 0, 1235, 0, 1236, 0, 1237, 0, 1238, 0, 1239, 0, 1240, 0, 1241, 0, 1242, 0, 1243, 0, 1244, 0, 1245, 0, 1246, 0, 1247, 0, 1248, 0, 1249, 0, 1250, 0, 1251, 0, 1252, 0, 1253, 0, 1254, 0, 1255, 0, 1256, 0, 1257, 0, 1258, 0, 1259, 0, 1260, 0, 1261, 0, 1262, 0, 1263, 0, 1264, 0, 1265, 0, 1266, 0, 1267, 0, 1268, 0, 1269, 0, 1270, 0, 1271, 0, 1272, 0, 1273, 0, 1274, 0, 1275, 0, 1276, 0, 1277, 0, 1278, 0, 1279, 0, 1280, 0, 1281, 0, 1282, 0, 1283, 0, 1284, 0, 1285, 0, 1286, 0, 1287, 0, 1288, 0, 1289, 0, 1290, 0, 1291, 0, 1292, 0, 1293, 0, 1294, 0, 1295, 0, 1296, 0, 1297, 0, 1298, 0, 1299, 0, 1300, 0, 1301, 0, 1302, 0, 1303, 0, 1304, 0, 1305, 0, 1306, 0, 1307, 0, 1308, 0, 1309, 0, 1310, 0, 1311, 0, 1312, 0, 1313, 0, 1314, 0, 1315, 0, 1316, 0, 1317, 0, 1318, 0, 1319, 0, 1320, 0, 1321, 0, 1322, 0, 1323, 0, 1324, 0, 1325, 0, 1326, 0, 1327, 0, 1328, 0, 1329, 0, 1330, 0, 1331, 0, 1332, 0, 1333, 0, 1334, 0, 1335, 0, 1336, 0, 1337, 0, 1338, 0, 1339, 0, 1340, 0, 1341, 0, 1342, 0, 1343, 0, 1344, 0, 1345, 0, 1346, 0, 1347, 0, 1348, 0, 1349, 0, 1350, 0, 1351, 0, 1352, 0, 1353, 0, 1354, 0, 1355, 0, 1356, 0, 1357, 0, 1358, 0, 1359, 0, 1360, 0, 1361, 0, 1362, 0, 1363, 0, 1364, 0, 1365, 0, 1366, 0, 1367, 0, 1368, 0, 1369, 0, 1370, 0, 1371, 0, 1372, 0, 1373, 0, 1374, 0, 1375, 0, 1376, 0, 1377, 0, 1378, 0, 1379, 0, 1380, 0, 1381, 0, 1382, 0, 1383, 0, 1384, 0, 1385, 0, 1386, 0, 1387, 0, 1388, 0, 1389, 0, 1390, 0, 1391, 0, 1392, 0, 1393, 0, 1394, 0, 1395, 0, 1396, 0, 1397, 0, 1398, 0, 1399, 0, 1400, 0, 1401, 0, 1402, 0, 1403, 0, 1404, 0, 1405, 0, 1406, 0, 1407, 0, 1408, 0, 1409, 0, 1410, 0, 1411, 0, 1412, 0, 1413, 0, 1414, 0, 1415, 0, 1416, 0, 1417, 0, 1418, 0, 1419, 0, 1420, 0, 1421, 0, 1422, 0, 1423, 0, 1424, 0, 1425, 0, 1426, 0, 1427, 0, 1428, 0, 1429, 0, 1430, 0, 1431, 0, 1432, 0, 1433, 0, 1434, 0, 1435, 0, 1436, 0, 1437, 0, 1438, 0, 1439, 0, 1440, 0, 1441, 0, 1442, 0, 1443, 0, 1444, 0, 1445, 0, 1446, 0, 1447, 0, 1448, 0, 1449, 0, 1450, 0, 1451, 0, 1452, 0, 1453, 0, 1454, 0, 1455, 0, 1456, 0, 1457, 0, 1458, 0, 1459, 0, 1460, 0, 1461, 0, 1462, 0, 1463, 0, 1464, 0, 1465, 0, 1466, 0, 1467, 0, 1468, 0, 1469, 0, 1470, 0, 1471, 0, 1472, 0, 1473, 0, 1474, 0, 1475, 0, 1476, 0, 1477, 0, 1478, 0, 1479, 0, 1480, 0, 1481, 0, 1482, 0, 1483, 0, 1484, 0, 1485, 0, 1486, 0, 1487, 0, 1488, 0, 1489, 0, 1490, 0, 1491, 0, 1492, 0, 1493, 0, 1494, 0, 1495, 0, 1496, 0, 1497, 0, 1498, 0, 1499, 0, 1500, 0, 1501, 0, 1502, 0, 1503, 0, 1504, 0, 1505, 0, 1506, 0, 1507, 0, 1508, 0, 1509, 0, 1510, 0, 1511, 0, 1512, 0, 1513, 0, 1514, 0, 1515, 0, 1516, 0, 1517, 0, 1518, 0, 1519, 0, 1520, 0, 1521, 0, 1522, 0, 1523, 0, 1524, 0, 1525, 0, 1526, 0, 1527, 0, 1528, 0, 1529, 0, 1530, 0, 1531, 0, 1532, 0, 1533, 0, 1534, 0, 1535, 0, 1536, 0, 1537, 0, 1538, 0, 1539, 0, 1540, 0, 1541, 0, 1542, 0, 1543, 0, 1544, 0, 1545, 0, 1546, 0, 1547, 0, 1548, 0, 1549, 0, 1550, 0, 1551, 0, 1552, 0, 1553, 0, 1554, 0, 1555, 0, 1556, 0, 1557, 0, 1558, 0, 1559, 0, 1560, 0, 1561, 0, 1562, 0, 1563, 0, 1564, 0, 1565, 0, 1566, 0, 1567, 0, 1568, 0, 1569, 0, 1570, 0, 1571, 0, 1572, 0, 1573, 0, 1574, 0, 1575, 0, 1576, 0, 1577, 0, 1578, 0, 1579, 0, 1580, 0, 1581, 0, 1582, 0, 1583, 0, 1584, 0, 1585, 0, 1586, 0, 1587, 0, 1588, 0, 1589, 0, 1590, 0, 1591, 0, 1592, 0, 1593, 0, 1594, 0, 1595, 0, 1596, 0, 1597, 0, 1598, 0, 1599, 0, 1600, 0, 1601, 0, 1602, 0, 1603, 0, 1604, 0, 1605, 0, 1606, 0, 1607, 0, 1608, 0, 1609, 0, 1610, 0, 1611, 0, 1612, 0, 1613, 0, 1614, 0, 1615, 0, 1616, 0, 1617, 0, 1618, 0, 1619, 0, 1620, 0, 1621, 0, 1622, 0, 1623, 0, 1624, 0, 1625, 0, 1626, 0, 1627, 0, 1628, 0, 1629, 0, 1630, 0, 1631, 0, 1632, 0, 1633, 0, 1634, 0, 1635, 0, 1636, 0, 1637, 0, 1638, 0, 1639, 0, 1640, 0, 1641, 0, 1642, 0, 1643, 0, 1644, 0, 1645, 0, 1646, 0, 1647, 0, 1648, 0, 1649, 0, 1650, 0, 1651, 0, 1652, 0, 1653, 0, 1654, 0, 1655, 0, 1656, 0, 1657, 0, 1658, 0, 1659, 0, 1660, 0, 1661, 0, 1662, 0, 1663, 0, 1664, 0, 1665, 0, 1666, 0, 1667, 0, 1668, 0, 1669, 0, 1670, 0, 1671, 0, 1672, 0, 1673, 0, 1674, 0, 1675, 0, 1676, 0, 1677, 0, 1678, 0, 1679, 0, 1680, 0, 1681, 0, 1682, 0, 1683, 0, 1684, 0, 1685, 0, 1686, 0, 1687, 0, 1688, 0, 1689, 0, 1690, 0, 1691, 0, 1692, 0, 1693, 0, 1694, 0, 1695, 0, 1696, 0, 1697, 0, 1698, 0, 1699, 0, 1700, 0, 1701, 0, 1702, 0, 1703, 0, 1704, 0, 1705, 0, 1706, 0, 1707, 0, 1708, 0, 1709, 0, 1710, 0, 1711, 0, 1712, 0, 1713, 0, 1714, 0, 1715, 0, 1716, 0, 1717, 0, 1718, 0, 1719, 0, 1720, 0, 1721, 0, 1722, 0, 1723, 0, 1724, 0, 1725, 0, 1726, 0, 1727, 0, 1728, 0, 1729, 0, 1730, 0, 1731, 0, 1732, 0, 1733, 0, 1734, 0, 1735, 0, 1736, 0, 1737, 0, 1738, 0, 1739, 0, 1740, 0, 1741, 0, 1742, 0, 1743, 0, 1744, 0, 1745, 0, 1746, 0, 1747, 0, 1748, 0, 1749, 0, 1750, 0, 1751, 0, 1752, 0, 1753, 0, 1754, 0, 1755, 0, 1756, 0, 1757, 0, 1758, 0, 1759, 0, 1760, 0, 1761, 0, 1762, 0, 1763, 0, 1764, 0, 1765, 0, 1766, 0, 1767, 0, 1768, 0, 1769, 0, 1770, 0, 1771, 0, 1772, 0, 1773, 0, 1774, 0, 1775, 0, 1776, 0, 1777, 0, 1778, 0, 1779, 0, 1780, 0, 1781, 0, 1782, 0, 1783, 0, 1784, 0, 1785, 0, 1786, 0, 1787, 0, 1788, 0, 1789, 0, 1790, 0, 1791, 0, 1792, 0, 1793, 0, 1794, 0, 1795, 0, 1796, 0, 1797, 0, 1798, 0, 1799, 0, 1800, 0, 1801, 0, 1802, 0, 1803, 0, 1804, 0, 1805, 0, 1806, 0, 1807, 0, 1808, 0, 1809, 0, 1810, 0, 1811, 0, 1812, 0, 1813, 0, 1814, 0, 1815, 0, 1816, 0, 1817, 0, 1818, 0, 1819, 0, 1820, 0, 1821, 0, 1822, 0, 1823, 0, 1824, 0, 1825, 0, 1826, 0, 1827, 0, 1828, 0, 1829, 0, 1830, 0, 1831, 0, 1832, 0, 1833, 0, 1834, 0, 1835, 0, 1836, 0, 1837, 0, 1838, 0, 1839, 0, 1840, 0, 1841, 0, 1842, 0, 1843, 0, 1844, 0, 1845, 0, 1846, 0, 1847, 0, 1848, 0, 1849, 0, 1850, 0, 1851, 0, 1852, 0, 1853, 0, 1854, 0, 1855, 0, 1856, 0, 1857, 0, 1858, 0, 1859, 0, 1860, 0, 1861, 0, 1862, 0, 1863, 0, 1864, 0, 1865, 0, 1866, 0, 1867, 0, 1868, 0, 1869, 0, 1870, 0, 1871, 0, 1872, 0, 1873, 0, 1874, 0, 1875, 0, 1876, 0, 1877, 0, 1878, 0, 1879, 0, 1880, 0, 1881, 0, 1882, 0, 1883, 0, 1884, 0, 1885, 0, 1886, 0, 1887, 0, 1888, 0, 1889, 0, 1890, 0, 1891, 0, 1892, 0, 1893, 0, 1894, 0, 1895, 0, 1896, 0, 1897, 0, 1898, 0, 1899, 0, 1900, 0, 1901, 0, 1902, 0, 1903, 0, 1904, 0, 1905, 0, 1906, 0, 1907, 0, 1908, 0, 1909, 0, 1910, 0, 1911, 0, 1912, 0, 1913, 0, 1914, 0, 1915, 0, 1916, 0, 1917, 0, 1918, 0, 1919, 0, 1920, 0, 1921, 0, 1922, 0, 1923, 0, 1924, 0, 1925, 0, 1926, 0, 1927, 0, 1928, 0, 1929, 0, 1930, 0, 1931, 0, 1932, 0, 1933, 0, 1934, 0, 1935, 0, 1936, 0, 1937, 0, 1938, 0, 1939, 0, 1940, 0, 1941, 0, 1942, 0, 1943, 0, 1944, 0, 1945, 0, 1946, 0, 1947, 0, 1948, 0, 1949, 0, 1950, 0, 1951, 0, 1952, 0, 1953, 0, 1954, 0, 1955, 0, 1956, 0, 1957, 0, 1958, 0, 1959, 0, 1960, 0, 1961, 0, 1962, 0, 1963, 0, 1964, 0, 1965, 0, 1966, 0, 1967, 0, 1968, 0, 1969, 0, 1970, 0, 1971, 0, 1972, 0, 1973, 0, 1974, 0, 1975, 0, 1976, 0, 1977, 0, 1978, 0, 1979, 0, 1980, 0, 1981, 0, 1982, 0, 1983, 0, 1984, 0, 1985, 0, 1986, 0, 1987, 0, 1988, 0, 1989, 0, 1990, 0, 1991, 0, 1992, 0, 1993, 0, 1994, 0, 1995, 0, 1996, 0, 1997, 0, 1998, 0, 1999, 0, 2000, 0, 2001, 0, 2002, 0, 2003, 0, 2004, 0, 2005, 0, 2006, 0, 2007, 0, 2008, 0, 2009, 0, 2010, 0, 2011, 0, 2012, 0, 2013, 0, 2014, 0, 2015, 0, 2016, 0, 2017, 0, 2018, 0, 2019, 0, 2020, 0, 2021, 0, 2022, 0, 2023, 0, 2024, 0, 2025, 0, 2026, 0, 2027, 0, 2028, 0, 2029, 0, 2030, 0, 2031, 0, 2032, 0, 2033, 0, 2034, 0, 2035, 0, 2036, 0, 2037, 0, 2038, 0, 2039, 0, 2040, 0, 2041, 0, 2042, 0, 2043, 0, 2044, 0, 2045, 0, 2046, 0, 2047, 0, 2048, 0, 2049, 0, 2050, 0, 2051, 0, 2052, 0, 2053, 0, 2054, 0, 2055, 0, 2056, 0, 2057, 0, 2058, 0, 2059, 0, 2060, 0, 2061, 0, 2062, 0, 2063, 0, 2064, 0, 2065, 0, 2066, 0, 2067, 0, 2068, 0, 2069, 0, 2070, 0, 2071, 0, 2072, 0, 2073, 0, 2074, 0, 2075, 0, 2076, 0, 2077, 0, 2078, 0, 2079, 0, 2080, 0, 2081, 0, 2082, 0, 2083, 0, 2084, 0, 2085, 0, 2086, 0, 2087, 0, 2088, 0, 2089, 0, 2090, 0, 2091, 0, 2092, 0, 2093, 0, 2094, 0, 2095, 0, 2096, 0, 2097, 0, 2098, 0, 2099, 0, 2100, 0, 2101, 0, 2102, 0, 2103, 0, 2104, 0, 2105, 0, 2106, 0, 2107, 0, 2108, 0, 2109, 0, 2110, 0, 2111, 0, 2112, 0, 2113, 0, 2114, 0, 2115, 0, 2116, 0, 2117, 0, 2118, 0, 2119, 0, 2120, 0, 2121, 0, 2122, 0, 2123, 0, 2124, 0, 2125, 0, 2126, 0, 2127, 0, 2128, 0, 2129, 0, 2130, 0, 2131, 0, 2132, 0, 2133, 0, 2134, 0, 2135, 0, 2136, 0, 2137, 0, 2138, 0, 2139, 0, 2140, 0, 2141, 0, 2142, 0, 2143, 0, 2144, 0, 2145, 0, 2146, 0, 2147, 0, 2148, 0, 2149, 0, 2150, 0, 2151, 0, 2152, 0, 2153, 0, 2154, 0, 2155, 0, 2156, 0, 2157, 0, 2158, 0, 2159, 0, 2160, 0, 2161, 0, 2162, 0, 2163, 0, 2164, 0, 2165, 0, 2166, 0, 2167, 0, 2168, 0, 2169, 0, 2170, 0, 2171, 0, 2172, 0, 2173, 0, 2174, 0, 2175, 0, 2176, 0, 2177, 0, 2178, 0, 2179, 0, 2180, 0, 2181, 0, 2182, 0, 2183, 0, 2184, 0, 2185, 0, 2186, 0, 2187, 0, 2188, 0, 2189, 0, 2190, 0, 2191, 0, 2192, 0, 2193, 0, 2194, 0, 2195, 0, 2196, 0, 2197, 0, 2198, 0, 2199, 0, 2200, 0, 2201, 0, 2202, 0, 2203, 0, 2204, 0, 2205, 0, 2206, 0, 2207, 0, 2208, 0, 2209, 0, 2210, 0, 2211, 0, 2212, 0, 2213, 0, 2214, 0, 2215, 0, 2216, 0, 2217, 0, 2218, 0, 2219, 0, 2220, 0, 2221, 0, 2222, 0, 2223, 0, 2224, 0, 2225, 0, 2226, 0, 2227, 0, 2228, 0, 2229, 0, 2230, 0, 2231, 0, 2232, 0, 2233, 0, 2234, 0, 2235, 0, 2236, 0, 2237, 0, 2238, 0, 2239, 0, 2240, 0, 2241, 0, 2242, 0, 2243, 0, 2244, 0, 2245, 0, 2246, 0, 2247, 0, 2248, 0, 2249, 0, 2250, 0, 2251, 0, 2252, 0, 2253, 0, 2254, 0, 2255, 0, 2256, 0, 2257, 0, 2258, 0, 2259, 0, 2260, 0, 2261, 0, 2262, 0, 2263, 0, 2264, 0, 2265, 0, 2266, 0, 2267, 0, 2268, 0, 2269, 0, 2270, 0, 2271, 0, 2272, 0, 2273, 0, 2274, 0, 2275, 0, 2276, 0, 2277, 0, 2278, 0, 2279, 0, 2280, 0, 2281, 0, 2282, 0, 2283, 0, 2284, 0, 2285, 0, 2286, 0, 2287, 0, 2288, 0, 2289, 0, 2290, 0, 2291, 0, 2292, 0, 2293, 0, 2294, 0, 2295, 0, 2296, 0, 2297, 0, 2298, 0, 2299, 0, 2300, 0, 2301, 0, 2302, 0, 2303, 0, 2304, 0, 2305, 0, 2306, 0, 2307, 0, 2308, 0, 2309, 0, 2310, 0, 2311, 0, 2312, 0, 2313, 0, 2314, 0, 2315, 0, 2316, 0, 2317, 0, 2318, 0, 2319, 0, 2320, 0, 2321, 0, 2322, 0, 2323, 0, 2324, 0, 2325, 0, 2326, 0, 2327, 0, 2328, 0, 2329, 0, 2330, 0, 2331, 0, 2332, 0, 2333, 0, 2334, 0, 2335, 0, 2336, 0, 2337, 0, 2338, 0, 2339, 0, 2340, 0, 2341, 0, 2342, 0, 2343, 0, 2344, 0, 2345, 0, 2346, 0, 2347, 0, 2348, 0, 2349, 0, 2350, 0, 2351, 0, 2352, 0, 2353, 0, 2354, 0, 2355, 0, 2356, 0, 2357, 0, 2358, 0, 2359, 0, 2360, 0, 2361, 0, 2362, 0, 2363, 0, 2364, 0, 2365, 0, 2366, 0, 2367, 0, 2368, 0, 2369, 0, 2370, 0, 2371, 0, 2372, 0, 2373, 0, 2374, 0, 2375, 0, 2376, 0, 2377, 0, 2378, 0, 2379, 0, 2380, 0, 2381, 0, 2382, 0, 2383, 0, 2384, 0, 2385, 0, 2386, 0, 2387, 0, 2388, 0, 2389, 0, 2390, 0, 2391, 0, 2392, 0, 2393, 0, 2394, 0, 2395, 0, 2396, 0, 2397, 0, 2398, 0, 2399, 0, 2400, 0, 2401, 0, 2402, 0, 2403, 0, 2404, 0, 2405, 0, 2406, 0, 2407, 0, 2408, 0, 2409, 0, 2410, 0, 2411, 0, 2412, 0, 2413, 0, 2414, 0, 2415, 0, 2416, 0, 2417, 0, 2418, 0, 2419, 0, 2420, 0, 2421, 0, 2422, 0, 2423, 0, 2424, 0, 2425, 0, 2426, 0, 2427, 0, 2428, 0, 2429, 0, 2430, 0, 2431, 0, 2432, 0, 2433, 0, 2434, 0, 2435, 0, 2436, 0, 2437, 0, 2438, 0, 2439, 0, 2440, 0, 2441, 0, 2442, 0, 2443, 0, 2444, 0, 2445, 0, 2446, 0, 2447, 0, 2448, 0, 2449, 0, 2450, 0, 2451, 0, 2452, 0, 2453, 0, 2454, 0, 2455, 0, 2456, 0, 2457, 0, 2458, 0, 2459, 0, 2460, 0, 2461, 0, 2462, 0, 2463, 0, 2464, 0, 2465, 0, 2466, 0, 2467, 0, 2468, 0, 2469, 0, 2470, 0, 2471, 0, 2472, 0, 2473, 0, 2474, 0, 2475, 0, 2476, 0, 2477, 0, 2478, 0, 2479, 0, 2480, 0, 2481, 0, 2482, 0, 2483, 0, 2484, 0, 2485, 0, 2486, 0, 2487, 0, 2488, 0, 2489, 0, 2490, 0, 2491, 0, 2492, 0, 2493, 0, 2494, 0, 2495, 0, 2496, 0, 2497, 0, 2498, 0, 2499, 0, 2500, 0, 2501, 0, 2502, 0, 2503, 0, 2504, 0, 2505, 0, 2506, 0, 2507, 0, 2508, 0, 2509, 0, 2510, 0, 2511, 0, 2512, 0, 2513, 0, 2514, 0, 2515, 0, 2516, 0, 2517, 0, 2518, 0, 2519, 0, 2520, 0, 2521, 0, 2522, 0, 2523, 0, 2524, 0, 2525, 0, 2526, 0, 2527, 0, 2528, 0, 2529, 0, 2530, 0, 2531, 0, 2532, 0, 2533, 0, 2534, 0, 2535, 0, 2536, 0, 2537, 0, 2538, 0, 2539, 0, 2540, 0, 2541, 0, 2542, 0, 2543, 0, 2544, 0, 2545, 0, 2546, 0, 2547, 0, 2548, 0, 2549, 0, 2550, 0, 2551, 0, 2552, 0, 2553, 0, 2554, 0, 2555, 0, 2556, 0, 2557, 0, 2558, 0, 2559, 0, 2560, 0, 2561, 0, 2562, 0, 2563, 0, 2564, 0, 2565, 0, 2566, 0, 2567, 0, 2568, 0, 2569, 0, 2570, 0, 2571, 0, 2572, 0, 2573, 0, 2574, 0, 2575, 0, 2576, 0, 2577, 0, 2578, 0, 2579, 0, 2580, 0, 2581, 0, 2582, 0, 2583, 0, 2584, 0, 2585, 0, 2586, 0, 2587, 0, 2588, 0, 2589, 0, 2590, 0, 2591, 0, 2592, 0, 2593, 0, 2594, 0, 2595, 0, 2596, 0, 2597, 0, 2598, 0, 2599, 0, 2600, 0, 2601, 0, 2602, 0, 2603, 0, 2604, 0, 2605, 0, 2606, 0, 2607, 0, 2608, 0, 2609, 0, 2610, 0, 2611, 0, 2612, 0, 2613, 0, 2614, 0, 2615, 0, 2616, 0, 2617, 0, 2618, 0, 2619, 0, 2620, 0, 2621, 0, 2622, 0, 2623, 0, 2624, 0, 2625, 0, 2626, 0, 2627, 0, 2628, 0, 2629, 0, 2630, 0, 2631, 0, 2632, 0, 2633, 0, 2634, 0, 2635, 0, 2636, 0, 2637, 0, 2638, 0, 2639, 0, 2640, 0, 2641, 0, 2642, 0, 2643, 0, 2644, 0, 2645, 0, 2646, 0, 2647, 0, 2648, 0, 2649, 0, 2650, 0, 2651, 0, 2652, 0, 2653, 0, 2654, 0, 2655, 0, 2656, 0, 2657, 0, 2658, 0, 2659, 0, 2660, 0, 2661, 0, 2662, 0, 2663, 0, 2664, 0, 2665, 0, 2666, 0, 2667, 0, 2668, 0, 2669, 0, 2670, 0, 2671, 0, 2672, 0, 2673, 0, 2674, 0, 2675, 0, 2676, 0, 2677, 0, 2678, 0, 2679, 0, 2680, 0, 2681, 0, 2682, 0, 2683, 0, 2684, 0, 2685, 0, 2686, 0, 2687, 0, 2688, 0, 2689, 0, 2690, 0, 2691, 0, 2692, 0, 2693, 0, 2694, 0, 2695, 0, 2696, 0, 2697, 0, 2698, 0, 2699, 0, 2700, 0, 2701, 0, 2702, 0, 2703, 0, 2704, 0, 2705, 0, 2706, 0, 2707, 0, 2708, 0, 2709, 0, 2710, 0, 2711, 0, 2712, 0, 2713, 0, 2714, 0, 2715, 0, 2716, 0, 2717, 0, 2718, 0, 2719, 0, 2720, 0, 2721, 0, 2722, 0, 2723, 0, 2724, 0, 2725, 0, 2726, 0, 2727, 0, 2728, 0, 2729, 0, 2730, 0, 2731, 0, 2732, 0, 2733, 0, 2734, 0, 2735, 0, 2736, 0, 2737, 0, 2738, 0, 2739, 0, 2740, 0, 2741, 0, 2742, 0, 2743, 0, 2744, 0, 2745, 0, 2746, 0, 2747, 0, 2748, 0, 2749, 0, 2750, 0, 2751, 0, 2752, 0, 2753, 0, 2754, 0, 2755, 0, 2756, 0, 2757, 0, 2758, 0, 2759, 0, 2760, 0, 2761, 0, 2762, 0, 2763, 0, 2764, 0, 2765, 0, 2766, 0, 2767, 0, 2768, 0, 2769, 0, 2770, 0, 2771, 0, 2772, 0, 2773, 0, 2774, 0, 2775, 0, 2776, 0, 2777, 0, 2778, 0, 2779, 0, 2780, 0, 2781, 0, 2782, 0, 2783, 0, 2784, 0, 2785, 0, 2786, 0, 2787, 0, 2788, 0, 2789, 0, 2790, 0, 2791, 0, 2792, 0, 2793, 0, 2794, 0, 2795, 0, 2796, 0, 2797, 0, 2798, 0, 2799, 0, 2800, 0, 2801, 0, 2802, 0, 2803, 0, 2804, 0, 2805, 0, 2806, 0, 2807, 0, 2808, 0, 2809, 0, 2810, 0, 2811, 0, 2812, 0, 2813, 0, 2814, 0, 2815, 0, 2816, 0, 2817, 0, 2818, 0, 2819, 0, 2820, 0, 2821, 0, 2822, 0, 2823, 0, 2824, 0, 2825, 0, 2826, 0, 2827, 0, 2828, 0, 2829, 0, 2830, 0, 2831, 0, 2832, 0, 2833, 0, 2834, 0, 2835, 0, 2836, 0, 2837, 0, 2838, 0, 2839, 0, 2840, 0, 2841, 0, 2842, 0, 2843, 0, 2844, 0, 2845, 0, 2846, 0, 2847, 0, 2848, 0, 2849, 0, 2850, 0, 2851, 0, 2852, 0, 2853, 0, 2854, 0, 2855, 0, 2856, 0, 2857, 0, 2858, 0, 2859, 0, 2860, 0, 2861, 0, 2862, 0, 2863, 0, 2864, 0, 2865, 0, 2866, 0, 2867, 0, 2868, 0, 2869, 0, 2870, 0, 2871, 0, 2872, 0, 2873, 0, 2874, 0, 2875, 0, 2876, 0, 2877, 0, 2878, 0, 2879, 0, 2880, 0, 2881, 0, 2882, 0, 2883, 0, 2884, 0, 2885, 0, 2886, 0, 2887, 0, 2888, 0, 2889, 0, 2890, 0, 2891, 0, 2892, 0, 2893, 0, 2894, 0, 2895, 0, 2896, 0, 2897, 0, 2898, 0, 2899, 0, 2900, 0, 2901, 0, 2902, 0, 2903, 0, 2904, 0, 2905, 0, 2906, 0, 2907, 0, 2908, 0, 2909, 0, 2910, 0, 2911, 0, 2912, 0, 2913, 0, 2914, 0, 2915, 0, 2916, 0, 2917, 0, 2918, 0, 2919, 0, 2920, 0, 2921, 0, 2922, 0, 2923, 0, 2924, 0, 2925, 0, 2926, 0, 2927, 0, 2928, 0, 2929, 0, 2930, 0, 2931, 0, 2932, 0, 2933, 0, 2934, 0, 2935, 0, 2936, 0, 2937, 0, 2938, 0, 2939, 0, 2940, 0, 2941, 0, 2942, 0, 2943, 0, 2944, 0, 2945, 0, 2946, 0, 2947, 0, 2948, 0, 2949, 0, 2950, 0, 2951, 0, 2952, 0, 2953, 0, 2954, 0, 2955, 0, 2956, 0, 2957, 0, 2958, 0, 2959, 0, 2960, 0, 2961, 0, 2962, 0, 2963, 0, 2964, 0, 2965, 0, 2966, 0, 2967, 0, 2968, 0, 2969, 0, 2970, 0, 2971, 0, 2972, 0, 2973, 0, 2974, 0, 2975, 0, 2976, 0, 2977, 0, 2978, 0, 2979, 0, 2980, 0, 2981, 0, 2982, 0, 2983, 0, 2984, 0, 2985, 0, 2986, 0, 2987, 0, 2988, 0, 2989, 0, 2990, 0, 2991, 0, 2992, 0, 2993, 0, 2994, 0, 2995, 0, 2996, 0, 2997, 0, 2998, 0, 2999, 0, 3000, 0, 3001, 0, 3002, 0, 3003, 0, 3004, 0, 3005, 0, 3006, 0, 3007, 0, 3008, 0, 3009, 0, 3010, 0, 3011, 0, 3012, 0, 3013, 0, 3014, 0, 3015, 0, 3016, 0, 3017, 0, 3018, 0, 3019, 0, 3020, 0, 3021, 0, 3022, 0, 3023, 0, 3024, 0, 3025, 0, 3026, 0, 3027, 0, 3028, 0, 3029, 0, 3030, 0, 3031, 0, 3032, 0, 3033, 0, 3034, 0, 3035, 0, 3036, 0, 3037, 0, 3038, 0, 3039, 0, 3040, 0, 3041, 0, 3042, 0, 3043, 0, 3044, 0, 3045, 0, 3046, 0, 3047, 0, 3048, 0, 3049, 0, 3050, 0, 3051, 0, 3052, 0, 3053, 0, 3054, 0, 3055, 0, 3056, 0, 3057, 0, 3058, 0, 3059, 0, 3060, 0, 3061, 0, 3062, 0, 3063, 0, 3064, 0, 3065, 0, 3066, 0, 3067, 0, 3068, 0, 3069, 0, 3070, 0, 3071, 0, 3072, 0, 3073, 0, 3074, 0, 3075, 0, 3076, 0, 3077, 0, 3078, 0, 3079, 0, 3080, 0, 3081, 0, 3082, 0, 3083, 0, 3084, 0, 3085, 0, 3086, 0, 3087, 0, 3088, 0, 3089, 0, 3090, 0, 3091, 0, 3092, 0, 3093, 0, 3094, 0, 3095, 0, 3096, 0, 3097, 0, 3098, 0, 3099, 0, 3100, 0, 3101, 0, 3102, 0, 3103, 0, 3104, 0, 3105, 0, 3106, 0, 3107, 0, 3108, 0, 3109, 0, 3110, 0, 3111, 0, 3112, 0, 3113, 0, 3114, 0, 3115, 0, 3116, 0, 3117, 0, 3118, 0, 3119, 0, 3120, 0, 3121, 0, 3122, 0, 3123, 0, 3124, 0, 3125, 0, 3126, 0, 3127, 0, 3128, 0, 3129, 0, 3130, 0, 3131, 0, 3132, 0, 3133, 0, 3134, 0, 3135, 0, 3136, 0, 3137, 0, 3138, 0, 3139, 0, 3140, 0, 3141, 0, 3142, 0, 3143, 0, 3144, 0, 3145, 0, 3146, 0, 3147, 0, 3148, 0, 3149, 0, 3150, 0, 3151, 0, 3152, 0, 3153, 0, 3154, 0, 3155, 0, 3156, 0, 3157, 0, 3158, 0, 3159, 0, 3160, 0, 3161, 0, 3162, 0, 3163, 0, 3164, 0, 3165, 0, 3166, 0, 3167, 0, 3168, 0, 3169, 0, 3170, 0, 3171, 0, 3172, 0, 3173, 0, 3174, 0, 3175, 0, 3176, 0, 3177, 0, 3178, 0, 3179, 0, 3180, 0, 3181, 0, 3182, 0, 3183, 0, 3184, 0, 3185, 0, 3186, 0, 3187, 0, 3188, 0, 3189, 0, 3190, 0, 3191, 0, 3192, 0, 3193, 0, 3194, 0, 3195, 0, 3196, 0, 3197, 0, 3198, 0, 3199, 0, 3200, 0, 3201, 0, 3202, 0, 3203, 0, 3204, 0, 3205, 0, 3206, 0, 3207, 0, 3208, 0, 3209, 0, 3210, 0, 3211, 0, 3212, 0, 3213, 0, 3214, 0, 3215, 0, 3216, 0, 3217, 0, 3218, 0, 3219, 0, 3220, 0, 3221, 0, 3222, 0, 3223, 0, 3224, 0, 3225, 0, 3226, 0, 3227, 0, 3228, 0, 3229, 0, 3230, 0, 3231, 0, 3232, 0, 3233, 0, 3234, 0, 3235, 0, 3236, 0, 3237, 0, 3238, 0, 3239, 0, 3240, 0, 3241, 0, 3242, 0, 3243, 0, 3244, 0, 3245, 0, 3246, 0, 3247, 0, 3248, 0, 3249, 0, 3250, 0, 3251, 0, 3252, 0, 3253, 0, 3254, 0, 3255, 0, 3256, 0, 3257, 0, 3258, 0, 3259, 0, 3260, 0, 3261, 0, 3262, 0, 3263, 0, 3264, 0, 3265, 0, 3266, 0, 3267, 0, 3268, 0, 3269, 0, 3270, 0, 3271, 0, 3272, 0, 3273, 0, 3274, 0, 3275, 0, 3276, 0, 3277, 0, 3278, 0, 3279, 0, 3280, 0, 3281, 0, 3282, 0, 3283, 0, 3284, 0, 3285, 0, 3286, 0, 3287, 0, 3288, 0, 3289, 0, 3290, 0, 3291, 0, 3292, 0, 3293, 0, 3294, 0, 3295, 0, 3296, 0, 3297, 0, 3298, 0, 3299, 0, 3300, 0, 3301, 0, 3302, 0, 3303, 0, 3304, 0, 3305, 0, 3306, 0, 3307, 0, 3308, 0, 3309, 0, 3310, 0, 3311, 0, 3312, 0, 3313, 0, 3314, 0, 3315, 0, 3316, 0, 3317, 0, 3318, 0, 3319, 0, 3320, 0, 3321, 0, 3322, 0, 3323, 0, 3324, 0, 3325, 0, 3326, 0, 3327, 0, 3328, 0, 3329, 0, 3330, 0, 3331, 0, 3332, 0, 3333, 0, 3334, 0, 3335, 0, 3336, 0, 3337, 0, 3338, 0, 3339, 0, 3340, 0, 3341, 0, 3342, 0, 3343, 0, 3344, 0, 3345, 0, 3346, 0, 3347, 0, 3348, 0, 3349, 0, 3350, 0, 3351, 0, 3352, 0, 3353, 0, 3354, 0, 3355, 0, 3356, 0, 3357, 0, 3358, 0, 3359, 0, 3360, 0, 3361, 0, 3362, 0, 3363, 0, 3364, 0, 3365, 0, 3366, 0, 3367, 0, 3368, 0, 3369, 0, 3370, 0, 3371, 0, 3372, 0, 3373, 0, 3374, 0, 3375, 0, 3376, 0, 3377, 0, 3378, 0, 3379, 0, 3380, 0, 3381, 0, 3382, 0, 3383, 0, 3384, 0, 3385, 0, 3386, 0, 3387, 0, 3388, 0, 3389, 0, 3390, 0, 3391, 0, 3392, 0, 3393, 0, 3394, 0, 3395, 0, 3396, 0, 3397, 0, 3398, 0, 3399, 0, 3400, 0, 3401, 0, 3402, 0, 3403, 0, 3404, 0, 3405, 0, 3406, 0, 3407, 0, 3408, 0, 3409, 0, 3410, 0, 3411, 0, 3412, 0, 3413, 0, 3414, 0, 3415, 0, 3416, 0, 3417, 0, 3418, 0, 3419, 0, 3420, 0, 3421, 0, 3422, 0, 3423, 0, 3424, 0, 3425, 0, 3426, 0, 3427, 0, 3428, 0, 3429, 0, 3430, 0, 3431, 0, 3432, 0, 3433, 0, 3434, 0, 3435, 0, 3436, 0, 3437, 0, 3438, 0, 3439, 0, 3440, 0, 3441, 0, 3442, 0, 3443, 0, 3444, 0, 3445, 0, 3446, 0, 3447, 0, 3448, 0, 3449, 0, 3450, 0, 3451, 0, 3452, 0, 3453, 0, 3454, 0, 3455, 0, 3456, 0, 3457, 0, 3458, 0, 3459, 0, 3460, 0, 3461, 0, 3462, 0, 3463, 0, 3464, 0, 3465, 0, 3466, 0, 3467, 0, 3468, 0, 3469, 0, 3470, 0, 3471, 0, 3472, 0, 3473, 0, 3474, 0, 3475, 0, 3476, 0, 3477, 0, 3478, 0, 3479, 0, 3480, 0, 3481, 0, 3482, 0, 3483, 0, 3484, 0, 3485, 0, 3486, 0, 3487, 0, 3488, 0, 3489, 0, 3490, 0, 3491, 0, 3492, 0, 3493, 0, 3494, 0, 3495, 0, 3496, 0, 3497, 0, 3498, 0, 3499, 0, 3500, 0, 3501, 0, 3502, 0, 3503, 0, 3504, 0, 3505, 0, 3506, 0, 3507, 0, 3508, 0, 3509, 0, 3510, 0, 3511, 0, 3512, 0, 3513, 0, 3514, 0, 3515, 0, 3516, 0, 3517, 0, 3518, 0, 3519, 0, 3520, 0, 3521, 0, 3522, 0, 3523, 0, 3524, 0, 3525, 0, 3526, 0, 3527, 0, 3528, 0, 3529, 0, 3530, 0, 3531, 0, 3532, 0, 3533, 0, 3534, 0, 3535, 0, 3536, 0, 3537, 0, 3538, 0, 3539, 0, 3540, 0, 3541, 0, 3542, 0, 3543, 0, 3544, 0, 3545, 0, 3546, 0, 3547, 0, 3548, 0, 3549, 0, 3550, 0, 3551, 0, 3552, 0, 3553, 0, 3554, 0, 3555, 0, 3556, 0, 3557, 0, 3558, 0, 3559, 0, 3560, 0, 3561, 0, 3562, 0, 3563, 0, 3564, 0, 3565, 0, 3566, 0, 3567, 0, 3568, 0, 3569, 0, 3570, 0, 3571, 0, 3572, 0, 3573, 0, 3574, 0, 3575, 0, 3576, 0, 3577, 0, 3578, 0, 3579, 0, 3580, 0, 3581, 0, 3582, 0, 3583, 0, 3584, 0, 3585, 0, 3586, 0, 3587, 0, 3588, 0, 3589, 0, 3590, 0, 3591, 0, 3592, 0, 3593, 0, 3594, 0, 3595, 0, 3596, 0, 3597, 0, 3598, 0, 3599, 0, 3600, 0, 3601, 0, 3602, 0, 3603, 0, 3604, 0, 3605, 0, 3606, 0, 3607, 0, 3608, 0, 3609, 0, 3610, 0, 3611, 0, 3612, 0, 3613, 0, 3614, 0, 3615, 0, 3616, 0, 3617, 0, 3618, 0, 3619, 0, 3620, 0, 3621, 0, 3622, 0, 3623, 0, 3624, 0, 3625, 0, 3626, 0, 3627, 0, 3628, 0, 3629, 0, 3630, 0, 3631, 0, 3632, 0, 3633, 0, 3634, 0, 3635, 0, 3636, 0, 3637, 0, 3638, 0, 3639, 0, 3640, 0, 3641, 0, 3642, 0, 3643, 0, 3644, 0, 3645, 0, 3646, 0, 3647, 0, 3648, 0, 3649, 0, 3650, 0, 3651, 0, 3652, 0, 3653, 0, 3654, 0, 3655, 0, 3656, 0, 3657, 0, 3658, 0, 3659, 0, 3660, 0, 3661, 0, 3662, 0, 3663, 0, 3664, 0, 3665, 0, 3666, 0, 3667, 0, 3668, 0, 3669, 0, 3670, 0, 3671, 0, 3672, 0, 3673, 0, 3674, 0, 3675, 0, 3676, 0, 3677, 0, 3678, 0, 3679, 0, 3680, 0, 3681, 0, 3682, 0, 3683, 0, 3684, 0, 3685, 0, 3686, 0, 3687, 0, 3688, 0, 3689, 0, 3690, 0, 3691, 0, 3692, 0, 3693, 0, 3694, 0, 3695, 0, 3696, 0, 3697, 0, 3698, 0, 3699, 0, 3700, 0, 3701, 0, 3702, 0, 3703, 0, 3704, 0, 3705, 0, 3706, 0, 3707, 0, 3708, 0, 3709, 0, 3710, 0, 3711, 0, 3712, 0, 3713, 0, 3714, 0, 3715, 0, 3716, 0, 3717, 0, 3718, 0, 3719, 0, 3720, 0, 3721, 0, 3722, 0, 3723, 0, 3724, 0, 3725, 0, 3726, 0, 3727, 0, 3728, 0, 3729, 0, 3730, 0, 3731, 0, 3732, 0, 3733, 0, 3734, 0, 3735, 0, 3736, 0, 3737, 0, 3738, 0, 3739, 0, 3740, 0, 3741, 0, 3742, 0, 3743, 0, 3744, 0, 3745, 0, 3746, 0, 3747, 0, 3748, 0, 3749, 0, 3750, 0, 3751, 0, 3752, 0, 3753, 0, 3754, 0, 3755, 0, 3756, 0, 3757, 0, 3758, 0, 3759, 0, 3760, 0, 3761, 0, 3762, 0, 3763, 0, 3764, 0, 3765, 0, 3766, 0, 3767, 0, 3768, 0, 3769, 0, 3770, 0, 3771, 0, 3772, 0, 3773, 0, 3774, 0, 3775, 0, 3776, 0, 3777, 0, 3778, 0, 3779, 0, 3780, 0, 3781, 0, 3782, 0, 3783, 0, 3784, 0, 3785, 0, 3786, 0, 3787, 0, 3788, 0, 3789, 0, 3790, 0, 3791, 0, 3792, 0, 3793, 0, 3794, 0, 3795, 0, 3796, 0, 3797, 0, 3798, 0, 3799, 0, 3800, 0, 3801, 0, 3802, 0, 3803, 0, 3804, 0, 3805, 0, 3806, 0, 3807, 0, 3808, 0, 3809, 0, 3810, 0, 3811, 0, 3812, 0, 3813, 0, 3814, 0, 3815, 0, 3816, 0, 3817, 0, 3818, 0, 3819, 0, 3820, 0, 3821, 0, 3822, 0, 3823, 0, 3824, 0, 3825, 0, 3826, 0, 3827, 0, 3828, 0, 3829, 0, 3830, 0, 3831, 0, 3832, 0, 3833, 0, 3834, 0, 3835, 0, 3836, 0, 3837, 0, 3838, 0, 3839, 0, 3840, 0, 3841, 0, 3842, 0, 3843, 0, 3844, 0, 3845, 0, 3846, 0, 3847, 0, 3848, 0, 3849, 0, 3850, 0, 3851, 0, 3852, 0, 3853, 0, 3854, 0, 3855, 0, 3856, 0, 3857, 0, 3858, 0, 3859, 0, 3860, 0, 3861, 0, 3862, 0, 3863, 0, 3864, 0, 3865, 0, 3866, 0, 3867, 0, 3868, 0, 3869, 0, 3870, 0, 3871, 0, 3872, 0, 3873, 0, 3874, 0, 3875, 0, 3876, 0, 3877, 0, 3878, 0, 3879, 0, 3880, 0, 3881, 0, 3882, 0, 3883, 0, 3884, 0, 3885, 0, 3886, 0, 3887, 0, 3888, 0, 3889, 0, 3890, 0, 3891, 0, 3892, 0, 3893, 0, 3894, 0, 3895, 0, 3896, 0, 3897, 0, 3898, 0, 3899, 0, 3900, 0, 3901, 0, 3902, 0, 3903, 0, 3904, 0, 3905, 0, 3906, 0, 3907, 0, 3908, 0, 3909, 0, 3910, 0, 3911, 0, 3912, 0, 3913, 0, 3914, 0, 3915, 0, 3916, 0, 3917, 0, 3918, 0, 3919, 0, 3920, 0, 3921, 0, 3922, 0, 3923, 0, 3924, 0, 3925, 0, 3926, 0, 3927, 0, 3928, 0, 3929, 0, 3930, 0, 3931, 0, 3932, 0, 3933, 0, 3934, 0, 3935, 0, 3936, 0, 3937, 0, 3938, 0, 3939, 0, 3940, 0, 3941, 0, 3942, 0, 3943, 0, 3944, 0, 3945, 0, 3946, 0, 3947, 0, 3948, 0, 3949, 0, 3950, 0, 3951, 0, 3952, 0, 3953, 0, 3954, 0, 3955, 0, 3956, 0, 3957, 0, 3958, 0, 3959, 0, 3960, 0, 3961, 0, 3962, 0, 3963, 0, 3964, 0, 3965, 0, 3966, 0, 3967, 0, 3968, 0, 3969, 0, 3970, 0, 3971, 0, 3972, 0, 3973, 0, 3974, 0, 3975, 0, 3976, 0, 3977, 0, 3978, 0, 3979, 0, 3980, 0, 3981, 0, 3982, 0, 3983, 0, 3984, 0, 3985, 0, 3986, 0, 3987, 0, 3988, 0, 3989, 0, 3990, 0, 3991, 0, 3992, 0, 3993, 0, 3994, 0, 3995, 0, 3996, 0, 3997, 0, 3998, 0, 3999, 0, 4000, 0, 4001, 0, 4002, 0, 4003, 0, 4004, 0, 4005, 0, 4006, 0, 4007, 0, 4008, 0, 4009, 0, 4010, 0, 4011, 0, 4012, 0, 4013, 0, 4014, 0, 4015, 0, 4016, 0, 4017, 0, 4018, 0, 4019, 0, 4020, 0, 4021, 0, 4022, 0, 4023, 0, 4024, 0, 4025, 0, 4026, 0, 4027, 0, 4028, 0, 4029, 0, 4030, 0, 4031, 0, 4032, 0, 4033, 0, 4034, 0, 4035, 0, 4036, 0, 4037, 0, 4038, 0, 4039, 0, 4040, 0, 4041, 0, 4042, 0, 4043, 0, 4044, 0, 4045, 0, 4046, 0, 4047, 0, 4048, 0, 4049, 0, 4050, 0, 4051, 0, 4052, 0, 4053, 0, 4054, 0, 4055, 0, 4056, 0, 4057, 0, 4058, 0, 4059, 0, 4060, 0, 4061, 0, 4062, 0, 4063, 0, 4064, 0, 4065, 0, 4066, 0, 4067, 0, 4068, 0, 4069, 0, 4070, 0, 4071, 0, 4072, 0, 4073, 0, 4074, 0, 4075, 0, 4076, 0, 4077, 0, 4078, 0, 4079, 0, 4080, 0, 4081, 0, 4082, 0, 4083, 0, 4084, 0, 4085, 0, 4086, 0, 4087, 0, 4088, 0, 4089, 0, 4090, 0, 4091, 0, 4092, 0, 4093, 0, 4094, 0, 4095, 0, 4096, 0, 4097, 0, 4098, 0, 4099, 0, 4100, 0, 4101, 0, 4102, 0, 4103, 0, 4104, 0, 4105, 0, 4106, 0, 4107, 0, 4108, 0, 4109, 0, 4110, 0, 4111, 0, 4112, 0, 4113, 0, 4114, 0, 4115, 0, 4116, 0, 4117, 0, 4118, 0, 4119, 0, 4120, 0, 4121, 0, 4122, 0, 4123, 0, 4124, 0, 4125, 0, 4126, 0, 4127, 0, 4128, 0, 4129, 0, 4130, 0, 4131, 0, 4132, 0, 4133, 0, 4134, 0, 4135, 0, 4136, 0, 4137, 0, 4138, 0, 4139, 0, 4140, 0, 4141, 0, 4142, 0, 4143, 0, 4144, 0, 4145, 0, 4146, 0, 4147, 0, 4148, 0, 4149, 0, 4150, 0, 4151, 0, 4152, 0, 4153, 0, 4154, 0, 4155, 0, 4156, 0, 4157, 0, 4158, 0, 4159, 0, 4160, 0, 4161, 0, 4162, 0, 4163, 0, 4164, 0, 4165, 0, 4166, 0, 4167, 0, 4168, 0, 4169, 0, 4170, 0, 4171, 0, 4172, 0, 4173, 0, 4174, 0, 4175, 0, 4176, 0, 4177, 0, 4178, 0, 4179, 0, 4180, 0, 4181, 0, 4182, 0, 4183, 0, 4184, 0, 4185, 0, 4186, 0, 4187, 0, 4188, 0, 4189, 0, 4190, 0, 4191, 0, 4192, 0, 4193, 0, 4194, 0, 4195, 0, 4196, 0, 4197, 0, 4198, 0, 4199, 0, 4200, 0, 4201, 0, 4202, 0, 4203, 0, 4204, 0, 4205, 0, 4206, 0, 4207, 0, 4208, 0, 4209, 0, 4210, 0, 4211, 0, 4212, 0, 4213, 0, 4214, 0, 4215, 0, 4216, 0, 4217, 0, 4218, 0, 4219, 0, 4220, 0, 4221, 0, 4222, 0, 4223, 0, 4224, 0, 4225, 0, 4226, 0, 4227, 0, 4228, 0, 4229, 0, 4230, 0, 4231, 0, 4232, 0, 4233, 0, 4234, 0, 4235, 0, 4236, 0, 4237, 0, 4238, 0, 4239, 0, 4240, 0, 4241, 0, 4242, 0, 4243, 0, 4244, 0, 4245, 0, 4246, 0, 4247, 0, 4248, 0, 4249, 0, 4250, 0, 4251, 0, 4252, 0, 4253, 0, 4254, 0, 4255, 0, 4256, 0, 4257, 0, 4258, 0, 4259, 0, 4260, 0, 4261, 0, 4262, 0, 4263, 0, 4264, 0, 4265, 0, 4266, 0, 4267, 0, 4268, 0, 4269, 0, 4270, 0, 4271, 0, 4272, 0, 4273, 0, 4274, 0, 4275, 0, 4276, 0, 4277, 0, 4278, 0, 4279, 0, 4280, 0, 4281, 0, 4282, 0, 4283, 0, 4284, 0, 4285, 0, 4286, 0, 4287, 0, 4288, 0, 4289, 0, 4290, 0, 4291, 0, 4292, 0, 4293, 0, 4294, 0, 4295, 0, 4296, 0, 4297, 0, 4298, 0, 4299, 0, 4300, 0, 4301, 0, 4302, 0, 4303, 0, 4304, 0, 4305, 0, 4306, 0, 4307, 0, 4308, 0, 4309, 0, 4310, 0, 4311, 0, 4312, 0, 4313, 0, 4314, 0, 4315, 0, 4316, 0, 4317, 0, 4318, 0, 4319, 0, 4320, 0, 4321, 0, 4322, 0, 4323, 0, 4324, 0, 4325, 0, 4326, 0, 4327, 0, 4328, 0, 4329, 8935, 4330, 8935, 4332, 4335, 4337, 4338, 4340, 4341, 4342, 4343, 4344, 4345, 4346, 4347, 4348, 4349, 4350, 4351, 4352, 4353, 4354, 4355, 4356, 4357, 4358, 4359, 4360, 4361, 4362, 4363, 4364, 4365, 4366, 4367, 4368, 4369, 4370, 4371, 4372, 4373, 4374, 4375, 4376, 4377, 4378, 4379, 4380, 4381, 4382, 4383, 4384, 4385, 4386, 4387, 4388, 4389, 4390, 4391, 4392, 4393, 4394, 4395, 4396, 4397, 4398, 4399, 4400, 4401, 4402, 4403, 4404, 4405, 4406, 4407, 4408, 4409, 4410, 4411, 4412, 4413, 4414, 4415, 4416, 4417, 4418, 4419, 4420, 4421, 4422, 4423, 4424, 4425, 4426, 4427, 4428, 4429, 4430, 4431, 4432, 4433, 4434, 4435, 4436, 4437, 4438, 4439, 4440, 4441, 4442, 4443, 4444, 4445, 4446, 4447, 4448, 4449, 4450, 4451, 4452, 4453, 4454, 4455, 4456, 4457, 4458, 4459, 4460, 4461, 4462, 4463, 4464, 4465, 4466, 4467, 4468, 4469, 4470, 4471, 4472, 4473, 4474, 4475, 4476, 4477, 4478, 4479, 4480, 4481, 4482, 4483, 4484, 4485, 4486, 4487, 4488, 4489, 4490, 4491, 4492, 4493, 4494, 4495, 4496, 4497, 4498, 4499, 4500, 4501, 4502, 4503, 4504, 4505, 4506, 4507, 4508, 4509, 4510, 4511, 4512, 4513, 4514, 4515, 4516, 4517, 4518, 4519, 4520, 4521, 4522, 4523, 4524, 4525, 4526, 4527, 4528, 4529, 4530, 4531, 4532, 4533, 4534, 4535, 4536, 4537, 4538, 4539, 4540, 4541, 4542, 4543, 4544, 4545, 4546, 4547, 4548, 4549, 4550, 4551, 4552, 4553, 4554, 4555, 4556, 4557, 4558, 4559, 4560, 4561, 4562, 4563, 4564, 4565, 4566, 4567, 4568, 4569, 4570, 4571, 4572, 4573, 4574, 4575, 4576, 4577, 4578, 4579, 4580, 4581, 4582, 4583, 4584, 4585, 4586, 4587, 4588, 4589, 4590, 4591, 4592, 4593, 4594, 4595, 4596, 4597, 4598, 4599, 4600, 4601, 4602, 4603, 4604, 4605, 4606, 4607, 4608, 4609, 4610, 4611, 4612, 4613, 4614, 4615, 4616, 4617, 4618, 4619, 4620, 4621, 4622, 4623, 4624, 4625, 4626, 4627, 4628, 4629, 4630, 4631, 4632, 4633, 4634, 4635, 4636, 4637, 4638, 4639, 4640, 4641, 4642, 4643, 4644, 4645, 4646, 4647, 4648, 4649, 4650, 4651, 4652, 4653, 4654, 4655, 4656, 4657, 4658, 4659, 4660, 4661, 4662, 4663, 4664, 4665, 4666, 4667, 4668, 4669, 4670, 4671, 4672, 4673, 4674, 4675, 4676, 4677, 4678, 4679, 4680, 4681, 4682, 4683, 4684, 4685, 4686, 4687, 4688, 4689, 4690, 4691, 4692, 4693, 4694, 4695, 4696, 4697, 4698, 4699, 4700, 4701, 4702, 4703, 4704, 4705, 4706, 4707, 4708, 4709, 4710, 4711, 4712, 4713, 4714, 4715, 4716, 4717, 4718, 4719, 4720, 4721, 4722, 4723, 4724, 4725, 4726, 4727, 4728, 4729, 4730, 4731, 4732, 4733, 4734, 4735, 4736, 4737, 4738, 4739, 4740, 4741, 4742, 4743, 4744, 4745, 4746, 4747, 4748, 4749, 4750, 4751, 4752, 4753, 4754, 4755, 4756, 4757, 4758, 4759, 4760, 4761, 4762, 4763, 4764, 4765, 4766, 4767, 4768, 4769, 4770, 4771, 4772, 4773, 4774, 4775, 4776, 4777, 4778, 4779, 4780, 4781, 4782, 4783, 4784, 4785, 4786, 4787, 4788, 4789, 4790, 4791, 4792, 4793, 4794, 4795, 4796, 4797, 4798, 4799, 4800, 4801, 4802, 4803, 4804, 4805, 4806, 4807, 4808, 4809, 4810, 4811, 4812, 4813, 4814, 4815, 4816, 4817, 4818, 4819, 4820, 4821, 4822, 4823, 4824, 4825, 4826, 4827, 4828, 4829, 4830, 4831, 4832, 4833, 4834, 4835, 4836, 4837, 4838, 4839, 4840, 4841, 4842, 4843, 4844, 4845, 4846, 4847, 4848, 4849, 4850, 4851, 4852, 4853, 4854, 4855, 4856, 4857, 4858, 4859, 4860, 4861, 4862, 4863, 4864, 4865, 4866, 4867, 4868, 4869, 4870, 4871, 4872, 4873, 4874, 4875, 4876, 4877, 4878, 4879, 4880, 4881, 4882, 4883, 4884, 4885, 4886, 4887, 4888, 4889, 4890, 4891, 4892, 4893, 4894, 4895, 4896, 4897, 4898, 4899, 4900, 4901, 4902, 4903, 4904, 4905, 4906, 4907, 4908, 4909, 4910, 4911, 4912, 4913, 4914, 4915, 4916, 4917, 4918, 4919, 4920, 4921, 4922, 4923, 4924, 4925, 4926, 4927, 4928, 4929, 4930, 4931, 4932, 4933, 4934, 4935, 4936, 4937, 4938, 4939, 4940, 4941, 4942, 4943, 4944, 4945, 4946, 4947, 4948, 4949, 4950, 4951, 4952, 4953, 4954, 4955, 4956, 4957, 4958, 4959, 4960, 4961, 4962, 4963, 4964, 4965, 4966, 4967, 4968, 4969, 4970, 4971, 4972, 4973, 4974, 4975, 4976, 4977, 4978, 4979, 4980, 4981, 4982, 4983, 4984, 4985, 4986, 4987, 4988, 4989, 4990, 4991, 4992, 4993, 4994, 4995, 4996, 4997, 4998, 4999, 5000, 5001, 5002, 5003, 5004, 5005, 5006, 5007, 5008, 5009, 5010, 5011, 5012, 5013, 5014, 5015, 5016, 5017, 5018, 5019, 5020, 5021, 5022, 5023, 5024, 5025, 5026, 5027, 5028, 5029, 5030, 5031, 5032, 5033, 5034, 5035, 5036, 5037, 5038, 5039, 5040, 5041, 5042, 5043, 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055, 5056, 5057, 5058, 5059, 5060, 5061, 5062, 5063, 5064, 5065, 5066, 5067, 5068, 5069, 5070, 5071, 5072, 5073, 5074, 5075, 5076, 5077, 5078, 5079, 5080, 5081, 5082, 5083, 5084, 5085, 5086, 5087, 5088, 5089, 5090, 5091, 5092, 5093, 5094, 5095, 5096, 5097, 5098, 5099, 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108, 5109, 5110, 5111, 5112, 5113, 5114, 5115, 5116, 5117, 5118, 5119, 5120, 5121, 5122, 5123, 5124, 5125, 5126, 5127, 5128, 5129, 5130, 5131, 5132, 5133, 5134, 5135, 5136, 5137, 5138, 5139, 5140, 5141, 5142, 5143, 5144, 5145, 5146, 5147, 5148, 5149, 5150, 5151, 5152, 5153, 5154, 5155, 5156, 5157, 5158, 5159, 5160, 5161, 5162, 5163, 5164, 5165, 5166, 5167, 5168, 5169, 5170, 5171, 5172, 5173, 5174, 5175, 5176, 5177, 5178, 5179, 5180, 5181, 5182, 5183, 5184, 5185, 5186, 5187, 5188, 5189, 5190, 5191, 5192, 5193, 5194, 5195, 5196, 5197, 5198, 5199, 5200, 5201, 5202, 5203, 5204, 5205, 5206, 5207, 5208, 5209, 5210, 5211, 5212, 5213, 5214, 5215, 5216, 5217, 5218, 5219, 5220, 5221, 5222, 5223, 5224, 5225, 5226, 5227, 5228, 5229, 5230, 5231, 5232, 5233, 5234, 5235, 5236, 5237, 5238, 5239, 5240, 5241, 5242, 5243, 5244, 5245, 5246, 5247, 5248, 5249, 5250, 5251, 5252, 5253, 5254, 5255, 5256, 5257, 5258, 5259, 5260, 5261, 5262, 5263, 5264, 5265, 5266, 5267, 5268, 5269, 5270, 5271, 5272, 5273, 5274, 5275, 5276, 5277, 5278, 5279, 5280, 5281, 5282, 5283, 5284, 5285, 5286, 5287, 5288, 5289, 5290, 5291, 5292, 5293, 5294, 5295, 5296, 5297, 5298, 5299, 5300, 5301, 5302, 5303, 5304, 5305, 5306, 5307, 5308, 5309, 5310, 5311, 5312, 5313, 5314, 5315, 5316, 5317, 5318, 5319, 5320, 5321, 5322, 5323, 5324, 5325, 5326, 5327, 5328, 5329, 5330, 5331, 5332, 5333, 5334, 5335, 5336, 5337, 5338, 5339, 5340, 5341, 5342, 5343, 5344, 5345, 5346, 5347, 5348, 5349, 5350, 5351, 5352, 5353, 5354, 5355, 5356, 5357, 5358, 5359, 5360, 5361, 5362, 5363, 5364, 5365, 5366, 5367, 5368, 5369, 5370, 5371, 5372, 5373, 5374, 5375, 5376, 5377, 5378, 5379, 5380, 5381, 5382, 5383, 5384, 5385, 5386, 5387, 5388, 5389, 5390, 5391, 5392, 5393, 5394, 5395, 5396, 5397, 5398, 5399, 5400, 5401, 5402, 5403, 5404, 5405, 5406, 5407, 5408, 5409, 5410, 5411, 5412, 5413, 5414, 5415, 5416, 5417, 5418, 5419, 5420, 5421, 5422, 5423, 5424, 5425, 5426, 5427, 5428, 5429, 5430, 5431, 5432, 5433, 5434, 5435, 5436, 5437, 5438, 5439, 5440, 5441, 5442, 5443, 5444, 5445, 5446, 5447, 5448, 5449, 5450, 5451, 5452, 5453, 5454, 5455, 5456, 5457, 5458, 5459, 5460, 5461, 5462, 5463, 5464, 5465, 5466, 5467, 5468, 5469, 5470, 5471, 5472, 5473, 5474, 5475, 5476, 5477, 5478, 5479, 5480, 5481, 5482, 5483, 5484, 5485, 5486, 5487, 5488, 5489, 5490, 5491, 5492, 5493, 5494, 5495, 5496, 5497, 5498, 5499, 5500, 5501, 5502, 5503, 5504, 5505, 5506, 5507, 5508, 5509, 5510, 5511, 5512, 5513, 5514, 5515, 5516, 5517, 5518, 5519, 5520, 5521, 5522, 5523, 5524, 5525, 5526, 5527, 5528, 5529, 5530, 5531, 5532, 5533, 5534, 5535, 5536, 5537, 5538, 5539, 5540, 5541, 5542, 5543, 5544, 5545, 5546, 5547, 5548, 5549, 5550, 5551, 5552, 5553, 5554, 5555, 5556, 5557, 5558, 5559, 5560, 5561, 5562, 5563, 5564, 5565, 5566, 5567, 5568, 5569, 5570, 5571, 5572, 5573, 5574, 5575, 5576, 5577, 5578, 5579, 5580, 5581, 5582, 5583, 5584, 5585, 5586, 5587, 5588, 5589, 5590, 5591, 5592, 5593, 5594, 5595, 5596, 5597, 5598, 5599, 5600, 5601, 5602, 5603, 5604, 5605, 5606, 5607, 5608, 5609, 5610, 5611, 5612, 5613, 5614, 5615, 5616, 5617, 5618, 5619, 5620, 5621, 5622, 5623, 5624, 5625, 5626, 5627, 5628, 5629, 5630, 5631, 5632, 5633, 5634, 5635, 5636, 5637, 5638, 5639, 5640, 5641, 5642, 5643, 5644, 5645, 5646, 5647, 5648, 5649, 5650, 5651, 5652, 5653, 5654, 5655, 5656, 5657, 5658, 5659, 5660, 5661, 5662, 5663, 5664, 5665, 5666, 5667, 5668, 5669, 5670, 5671, 5672, 5673, 5674, 5675, 5676, 5677, 5678, 5679, 5680, 5681, 5682, 5683, 5684, 5685, 5686, 5687, 5688, 5689, 5690, 5691, 5692, 5693, 5694, 5695, 5696, 5697, 5698, 5699, 5700, 5701, 5702, 5703, 5704, 5705, 5706, 5707, 5708, 5709, 5710, 5711, 5712, 5713, 5714, 5715, 5716, 5717, 5718, 5719, 5720, 5721, 5722, 5723, 5724, 5725, 5726, 5727, 5728, 5729, 5730, 5731, 5732, 5733, 5734, 5735, 5736, 5737, 5738, 5739, 5740, 5741, 5742, 5743, 5744, 5745, 5746, 5747, 5748, 5749, 5750, 5751, 5752, 5753, 5754, 5755, 5756, 5757, 5758, 5759, 5760, 5761, 5762, 5763, 5764, 5765, 5766, 5767, 5768, 5769, 5770, 5771, 5772, 5773, 5774, 5775, 5776, 5777, 5778, 5779, 5780, 5781, 5782, 5783, 5784, 5785, 5786, 5787, 5788, 5789, 5790, 5791, 5792, 5793, 5794, 5795, 5796, 5797, 5798, 5799, 5800, 5801, 5802, 5803, 5804, 5805, 5806, 5807, 5808, 5809, 5810, 5811, 5812, 5813, 5814, 5815, 5816, 5817, 5818, 5819, 5820, 5821, 5822, 5823, 5824, 5825, 5826, 5827, 5828, 5829, 5830, 5831, 5832, 5833, 5834, 5835, 5836, 5837, 5838, 5839, 5840, 5841, 5842, 5843, 5844, 5845, 5846, 5847, 5848, 5849, 5850, 5851, 5852, 5853, 5854, 5855, 5856, 5857, 5858, 5859, 5860, 5861, 5862, 5863, 5864, 5865, 5866, 5867, 5868, 5869, 5870, 5871, 5872, 5873, 5874, 5875, 5876, 5877, 5878, 5879, 5880, 5881, 5882, 5883, 5884, 5885, 5886, 5887, 5888, 5889, 5890, 5891, 5892, 5893, 5894, 5895, 5896, 5897, 5898, 5899, 5900, 5901, 5902, 5903, 5904, 5905, 5906, 5907, 5908, 5909, 5910, 5911, 5912, 5913, 5914, 5915, 5916, 5917, 5918, 5919, 5920, 5921, 5922, 5923, 5924, 5925, 5926, 5927, 5928, 5929, 5930, 5931, 5932, 5933, 5934, 5935, 5936, 5937, 5938, 5939, 5940, 5941, 5942, 5943, 5944, 5945, 5946, 5947, 5948, 5949, 5950, 5951, 5952, 5953, 5954, 5955, 5956, 5957, 5958, 5959, 5960, 5961, 5962, 5963, 5964, 5965, 5966, 5967, 5968, 5969, 5970, 5971, 5972, 5973, 5974, 5975, 5976, 5977, 5978, 5979, 5980, 5981, 5982, 5983, 5984, 5985, 5986, 5987, 5988, 5989, 5990, 5991, 5992, 5993, 5994, 5995, 5996, 5997, 5998, 5999, 6000, 6001, 6002, 6003, 6004, 6005, 6006, 6007, 6008, 6009, 6010, 6011, 6012, 6013, 6014, 6015, 6016, 6017, 6018, 6019, 6020, 6021, 6022, 6023, 6024, 6025, 6026, 6027, 6028, 6029, 6030, 6031, 6032, 6033, 6034, 6035, 6036, 6037, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, 6048, 6049, 6050, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6061, 6062, 6063, 6064, 6065, 6066, 6067, 6068, 6069, 6070, 6071, 6072, 6073, 6074, 6075, 6076, 6077, 6078, 6079, 6080, 6081, 6082, 6083, 6084, 6085, 6086, 6087, 6088, 6089, 6090, 6091, 6092, 6093, 6094, 6095, 6096, 6097, 6098, 6099, 6100, 6101, 6102, 6103, 6104, 6105, 6106, 6107, 6108, 6109, 6110, 6111, 6112, 6113, 6114, 6115, 6116, 6117, 6118, 6119, 6120, 6121, 6122, 6123, 6124, 6125, 6126, 6127, 6128, 6129, 6130, 6131, 6132, 6133, 6134, 6135, 6136, 6137, 6138, 6139, 6140, 6141, 6142, 6143, 6144, 6145, 6146, 6147, 6148, 6149, 6150, 6151, 6152, 6153, 6154, 6155, 6156, 6157, 6158, 6159, 6160, 6161, 6162, 6163, 6164, 6165, 6166, 6167, 6168, 6169, 6170, 6171, 6172, 6173, 6174, 6175, 6176, 6177, 6178, 6179, 6180, 6181, 6182, 6183, 6184, 6185, 6186, 6187, 6188, 6189, 6190, 6191, 6192, 6193, 6194, 6195, 6196, 6197, 6198, 6199, 6200, 6201, 6202, 6203, 6204, 6205, 6206, 6207, 6208, 6209, 6210, 6211, 6212, 6213, 6214, 6215, 6216, 6217, 6218, 6219, 6220, 6221, 6222, 6223, 6224, 6225, 6226, 6227, 6228, 6229, 6230, 6231, 6232, 6233, 6234, 6235, 6236, 6237, 6238, 6239, 6240, 6241, 6242, 6243, 6244, 6245, 6246, 6247, 6248, 6249, 6250, 6251, 6252, 6253, 6254, 6255, 6256, 6257, 6258, 6259, 6260, 6261, 6262, 6263, 6264, 6265, 6266, 6267, 6268, 6269, 6270, 6271, 6272, 6273, 6274, 6275, 6276, 6277, 6278, 6279, 6280, 6281, 6282, 6283, 6284, 6285, 6286, 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6295, 6296, 6297, 6298, 6299, 6300, 6301, 6302, 6303, 6304, 6305, 6306, 6307, 6308, 6309, 6310, 6311, 6312, 6313, 6314, 6315, 6316, 6317, 6318, 6319, 6320, 6321, 6322, 6323, 6324, 6325, 6326, 6327, 6328, 6329, 6330, 6331, 6332, 6333, 6334, 6335, 6336, 6337, 6338, 6339, 6340, 6341, 6342, 6343, 6344, 6345, 6346, 6347, 6348, 6349, 6350, 6351, 6352, 6353, 6354, 6355, 6356, 6357, 6358, 6359, 6360, 6361, 6362, 6363, 6364, 6365, 6366, 6367, 6368, 6369, 6370, 6371, 6372, 6373, 6374, 6375, 6376, 6377, 6378, 6379, 6380, 6381, 6382, 6383, 6384, 6385, 6386, 6387, 6388, 6389, 6390, 6391, 6392, 6393, 6394, 6395, 6396, 6397, 6398, 6399, 6400, 6401, 6402, 6403, 6404, 6405, 6406, 6407, 6408, 6409, 6410, 6411, 6412, 6413, 6414, 6415, 6416, 6417, 6418, 6419, 6420, 6421, 6422, 6423, 6424, 6425, 6426, 6427, 6428, 6429, 6430, 6431, 6432, 6433, 6434, 6435, 6436, 6437, 6438, 6439, 6440, 6441, 6442, 6443, 6444, 6445, 6446, 6447, 6448, 6449, 6450, 6451, 6452, 6453, 6454, 6455, 6456, 6457, 6458, 6459, 6460, 6461, 6462, 6463, 6464, 6465, 6466, 6467, 6468, 6469, 6470, 6471, 6472, 6473, 6474, 6475, 6476, 6477, 6478, 6479, 6480, 6481, 6482, 6483, 6484, 6485, 6486, 6487, 6488, 6489, 6490, 6491, 6492, 6493, 6494, 6495, 6496, 6497, 6498, 6499, 6500, 6501, 6502, 6503, 6504, 6505, 6506, 6507, 6508, 6509, 6510, 6511, 6512, 6513, 6514, 6515, 6516, 6517, 6518, 6519, 6520, 6521, 6522, 6523, 6524, 6525, 6526, 6527, 6528, 6529, 6530, 6531, 6532, 6533, 6534, 6535, 6536, 6537, 6538, 6539, 6540, 6541, 6542, 6543, 6544, 6545, 6546, 6547, 6548, 6549, 6550, 6551, 6552, 6553, 6554, 6555, 6556, 6557, 6558, 6559, 6560, 6561, 6562, 6563, 6564, 6565, 6566, 6567, 6568, 6569, 6570, 6571, 6572, 6573, 6574, 6575, 6576, 6577, 6578, 6579, 6580, 6581, 6582, 6583, 6584, 6585, 6586, 6587, 6588, 6589, 6590, 6591, 6592, 6593, 6594, 6595, 6596, 6597, 6598, 6599, 6600, 6601, 6602, 6603, 6604, 6605, 6606, 6607, 6608, 6609, 6610, 6611, 6612, 6613, 6614, 6615, 6616, 6617, 6618, 6619, 6620, 6621, 6622, 6623, 6624, 6625, 6626, 6627, 6628, 6629, 6630, 6631, 6632, 6633, 6634, 6635, 6636, 6637, 6638, 6639, 6640, 6641, 6642, 6643, 6644, 6645, 6646, 6647, 6648, 6649, 6650, 6651, 6652, 6653, 6654, 6655, 6656, 6657, 6658, 6659, 6660, 6661, 6662, 6663, 6664, 6665, 6666, 6667, 6668, 6669, 6670, 6671, 6672, 6673, 6674, 6675, 6676, 6677, 6678, 6679, 6680, 6681, 6682, 6683, 6684, 6685, 6686, 6687, 6688, 6689, 6690, 6691, 6692, 6693, 6694, 6695, 6696, 6697, 6698, 6699, 6700, 6701, 6702, 6703, 6704, 6705, 6706, 6707, 6708, 6709, 6710, 6711, 6712, 6713, 6714, 6715, 6716, 6717, 6718, 6719, 6720, 6721, 6722, 6723, 6724, 6725, 6726, 6727, 6728, 6729, 6730, 6731, 6732, 6733, 6734, 6735, 6736, 6737, 6738, 6739, 6740, 6741, 6742, 6743, 6744, 6745, 6746, 6747, 6748, 6749, 6750, 6751, 6752, 6753, 6754, 6755, 6756, 6757, 6758, 6759, 6760, 6761, 6762, 6763, 6764, 6765, 6766, 6767, 6768, 6769, 6770, 6771, 6772, 6773, 6774, 6775, 6776, 6777, 6778, 6779, 6780, 6781, 6782, 6783, 6784, 6785, 6786, 6787, 6788, 6789, 6790, 6791, 6792, 6793, 6794, 6795, 6796, 6797, 6798, 6799, 6800, 6801, 6802, 6803, 6804, 6805, 6806, 6807, 6808, 6809, 6810, 6811, 6812, 6813, 6814, 6815, 6816, 6817, 6818, 6819, 6820, 6821, 6822, 6823, 6824, 6825, 6826, 6827, 6828, 6829, 6830, 6831, 6832, 6833, 6834, 6835, 6836, 6837, 6838, 6839, 6840, 6841, 6842, 6843, 6844, 6845, 6846, 6847, 6848, 6849, 6850, 6851, 6852, 6853, 6854, 6855, 6856, 6857, 6858, 6859, 6860, 6861, 6862, 6863, 6864, 6865, 6866, 6867, 6868, 6869, 6870, 6871, 6872, 6873, 6874, 6875, 6876, 6877, 6878, 6879, 6880, 6881, 6882, 6883, 6884, 6885, 6886, 6887, 6888, 6889, 6890, 6891, 6892, 6893, 6894, 6895, 6896, 6897, 6898, 6899, 6900, 6901, 6902, 6903, 6904, 6905, 6906, 6907, 6908, 6909, 6910, 6911, 6912, 6913, 6914, 6915, 6916, 6917, 6918, 6919, 6920, 6921, 6922, 6923, 6924, 6925, 6926, 6927, 6928, 6929, 6930, 6931, 6932, 6933, 6934, 6935, 6936, 6937, 6938, 6939, 6940, 6941, 6942, 6943, 6944, 6945, 6946, 6947, 6948, 6949, 6950, 6951, 6952, 6953, 6954, 6955, 6956, 6957, 6958, 6959, 6960, 6961, 6962, 6963, 6964, 6965, 6966, 6967, 6968, 6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978, 6979, 6980, 6981, 6982, 6983, 6984, 6985, 6986, 6987, 6988, 6989, 6990, 6991, 6992, 6993, 6994, 6995, 6996, 6997, 6998, 6999, 7000, 7001, 7002, 7003, 7004, 7005, 7006, 7007, 7008, 7009, 7010, 7011, 7012, 7013, 7014, 7015, 7016, 7017, 7018, 7019, 7020, 7021, 7022, 7023, 7024, 7025, 7026, 7027, 7028, 7029, 7030, 7031, 7032, 7033, 7034, 7035, 7036, 7037, 7038, 7039, 7040, 7041, 7042, 7043, 7044, 7045, 7046, 7047, 7048, 7049, 7050, 7051, 7052, 7053, 7054, 7055, 7056, 7057, 7058, 7059, 7060, 7061, 7062, 7063, 7064, 7065, 7066, 7067, 7068, 7069, 7070, 7071, 7072, 7073, 7074, 7075, 7076, 7077, 7078, 7079, 7080, 7081, 7082, 7083, 7084, 7085, 7086, 7087, 7088, 7089, 7090, 7091, 7092, 7093, 7094, 7095, 7096, 7097, 7098, 7099, 7100, 7101, 7102, 7103, 7104, 7105, 7106, 7107, 7108, 7109, 7110, 7111, 7112, 7113, 7114, 7115, 7116, 7117, 7118, 7119, 7120, 7121, 7122, 7123, 7124, 7125, 7126, 7127, 7128, 7129, 7130, 7131, 7132, 7133, 7134, 7135, 7136, 7137, 7138, 7139, 7140, 7141, 7142, 7143, 7144, 7145, 7146, 7147, 7148, 7149, 7150, 7151, 7152, 7153, 7154, 7155, 7156, 7157, 7158, 7159, 7160, 7161, 7162, 7163, 7164, 7165, 7166, 7167, 7168, 7169, 7170, 7171, 7172, 7173, 7174, 7175, 7176, 7177, 7178, 7179, 7180, 7181, 7182, 7183, 7184, 7185, 7186, 7187, 7188, 7189, 7190, 7191, 7192, 7193, 7194, 7195, 7196, 7197, 7198, 7199, 7200, 7201, 7202, 7203, 7204, 7205, 7206, 7207, 7208, 7209, 7210, 7211, 7212, 7213, 7214, 7215, 7216, 7217, 7218, 7219, 7220, 7221, 7222, 7223, 7224, 7225, 7226, 7227, 7228, 7229, 7230, 7231, 7232, 7233, 7234, 7235, 7236, 7237, 7238, 7239, 7240, 7241, 7242, 7243, 7244, 7245, 7246, 7247, 7248, 7249, 7250, 7251, 7252, 7253, 7254, 7255, 7256, 7257, 7258, 7259, 7260, 7261, 7262, 7263, 7264, 7265, 7266, 7267, 7268, 7269, 7270, 7271, 7272, 7273, 7274, 7275, 7276, 7277, 7278, 7279, 7280, 7281, 7282, 7283, 7284, 7285, 7286, 7287, 7288, 7289, 7290, 7291, 7292, 7293, 7294, 7295, 7296, 7297, 7298, 7299, 7300, 7301, 7302, 7303, 7304, 7305, 7306, 7307, 7308, 7309, 7310, 7311, 7312, 7313, 7314, 7315, 7316, 7317, 7318, 7319, 7320, 7321, 7322, 7323, 7324, 7325, 7326, 7327, 7328, 7329, 7330, 7331, 7332, 7333, 7334, 7335, 7336, 7337, 7338, 7339, 7340, 7341, 7342, 7343, 7344, 7345, 7346, 7347, 7348, 7349, 7350, 7351, 7352, 7353, 7354, 7355, 7356, 7357, 7358, 7359, 7360, 7361, 7362, 7363, 7364, 7365, 7366, 7367, 7368, 7369, 7370, 7371, 7372, 7373, 7374, 7375, 7376, 7377, 7378, 7379, 7380, 7381, 7382, 7383, 7384, 7385, 7386, 7387, 7388, 7389, 7390, 7391, 7392, 7393, 7394, 7395, 7396, 7397, 7398, 7399, 7400, 7401, 7402, 7403, 7404, 7405, 7406, 7407, 7408, 7409, 7410, 7411, 7412, 7413, 7414, 7415, 7416, 7417, 7418, 7419, 7420, 7421, 7422, 7423, 7424, 7425, 7426, 7427, 7428, 7429, 7430, 7431, 7432, 7433, 7434, 7435, 7436, 7437, 7438, 7439, 7440, 7441, 7442, 7443, 7444, 7445, 7446, 7447, 7448, 7449, 7450, 7451, 7452, 7453, 7454, 7455, 7456, 7457, 7458, 7459, 7460, 7461, 7462, 7463, 7464, 7465, 7466, 7467, 7468, 7469, 7470, 7471, 7472, 7473, 7474, 7475, 7476, 7477, 7478, 7479, 7480, 7481, 7482, 7483, 7484, 7485, 7486, 7487, 7488, 7489, 7490, 7491, 7492, 7493, 7494, 7495, 7496, 7497, 7498, 7499, 7500, 7501, 7502, 7503, 7504, 7505, 7506, 7507, 7508, 7509, 7510, 7511, 7512, 7513, 7514, 7515, 7516, 7517, 7518, 7519, 7520, 7521, 7522, 7523, 7524, 7525, 7526, 7527, 7528, 7529, 7530, 7531, 7532, 7533, 7534, 7535, 7536, 7537, 7538, 7539, 7540, 7541, 7542, 7543, 7544, 7545, 7546, 7547, 7548, 7549, 7550, 7551, 7552, 7553, 7554, 7555, 7556, 7557, 7558, 7559, 7560, 7561, 7562, 7563, 7564, 7565, 7566, 7567, 7568, 7569, 7570, 7571, 7572, 7573, 7574, 7575, 7576, 7577, 7578, 7579, 7580, 7581, 7582, 7583, 7584, 7585, 7586, 7587, 7588, 7589, 7590, 7591, 7592, 7593, 7594, 7595, 7596, 7597, 7598, 7599, 7600, 7601, 7602, 7603, 7604, 7605, 7606, 7607, 7608, 7609, 7610, 7611, 7612, 7613, 7614, 7615, 7616, 7617, 7618, 7619, 7620, 7621, 7622, 7623, 7624, 7625, 7626, 7627, 7628, 7629, 7630, 7631, 7632, 7633, 7634, 7635, 7636, 7637, 7638, 7639, 7640, 7641, 7642, 7643, 7644, 7645, 7646, 7647, 7648, 7649, 7650, 7651, 7652, 7653, 7654, 7655, 7656, 7657, 7658, 7659, 7660, 7661, 7662, 7663, 7664, 7665, 7666, 7667, 7668, 7669, 7670, 7671, 7672, 7673, 7674, 7675, 7676, 7677, 7678, 7679, 7680, 7681, 7682, 7683, 7684, 7685, 7686, 7687, 7688, 7689, 7690, 7691, 7692, 7693, 7694, 7695, 7696, 7697, 7698, 7699, 7700, 7701, 7702, 7703, 7704, 7705, 7706, 7707, 7708, 7709, 7710, 7711, 7712, 7713, 7714, 7715, 7716, 7717, 7718, 7719, 7720, 7721, 7722, 7723, 7724, 7725, 7726, 7727, 7728, 7729, 7730, 7731, 7732, 7733, 7734, 7735, 7736, 7737, 7738, 7739, 7740, 7741, 7742, 7743, 7744, 7745, 7746, 7747, 7748, 7749, 7750, 7751, 7752, 7753, 7754, 7755, 7756, 7757, 7758, 7759, 7760, 7761, 7762, 7763, 7764, 7765, 7766, 7767, 7768, 7769, 7770, 7771, 7772, 7773, 7774, 7775, 7776, 7777, 7778, 7779, 7780, 7781, 7782, 7783, 7784, 7785, 7786, 7787, 7788, 7789, 7790, 7791, 7792, 7793, 7794, 7795, 7796, 7797, 7798, 7799, 7800, 7801, 7802, 7803, 7804, 7805, 7806, 7807, 7808, 7809, 7810, 7811, 7812, 7813, 7814, 7815, 7816, 7817, 7818, 7819, 7820, 7821, 7822, 7823, 7824, 7825, 7826, 7827, 7828, 7829, 7830, 7831, 7832, 7833, 7834, 7835, 7836, 7837, 7838, 7839, 7840, 7841, 7842, 7843, 7844, 7845, 7846, 7847, 7848, 7849, 7850, 7851, 7852, 7853, 7854, 7855, 7856, 7857, 7858, 7859, 7860, 7861, 7862, 7863, 7864, 7865, 7866, 7867, 7868, 7869, 7870, 7871, 7872, 7873, 7874, 7875, 7876, 7877, 7878, 7879, 7880, 7881, 7882, 7883, 7884, 7885, 7886, 7887, 7888, 7889, 7890, 7891, 7892, 7893, 7894, 7895, 7896, 7897, 7898, 7899, 7900, 7901, 7902, 7903, 7904, 7905, 7906, 7907, 7908, 7909, 7910, 7911, 7912, 7913, 7914, 7915, 7916, 7917, 7918, 7919, 7920, 7921, 7922, 7923, 7924, 7925, 7926, 7927, 7928, 7929, 7930, 7931, 7932, 7933, 7934, 7935, 7936, 7937, 7938, 7939, 7940, 7941, 7942, 7943, 7944, 7945, 7946, 7947, 7948, 7949, 7950, 7951, 7952, 7953, 7954, 7955, 7956, 7957, 7958, 7959, 7960, 7961, 7962, 7963, 7964, 7965, 7966, 7967, 7968, 7969, 7970, 7971, 7972, 7973, 7974, 7975, 7976, 7977, 7978, 7979, 7980, 7981, 7982, 7983, 7984, 7985, 7986, 7987, 7988, 7989, 7990, 7991, 7992, 7993, 7994, 7995, 7996, 7997, 7998, 7999, 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 8008, 8009, 8010, 8011, 8012, 8013, 8014, 8015, 8016, 8017, 8018, 8019, 8020, 8021, 8022, 8023, 8024, 8025, 8026, 8027, 8028, 8029, 8030, 8031, 8032, 8033, 8034, 8035, 8036, 8037, 8038, 8039, 8040, 8041, 8042, 8043, 8044, 8045, 8046, 8047, 8048, 8049, 8050, 8051, 8052, 8053, 8054, 8055, 8056, 8057, 8058, 8059, 8060, 8061, 8062, 8063, 8064, 8065, 8066, 8067, 8068, 8069, 8070, 8071, 8072, 8073, 8074, 8075, 8076, 8077, 8078, 8079, 8080, 8081, 8082, 8083, 8084, 8085, 8086, 8087, 8088, 8089, 8090, 8091, 8092, 8093, 8094, 8095, 8096, 8097, 8098, 8099, 8100, 8101, 8102, 8103, 8104, 8105, 8106, 8107, 8108, 8109, 8110, 8111, 8112, 8113, 8114, 8115, 8116, 8117, 8118, 8119, 8120, 8121, 8122, 8123, 8124, 8125, 8126, 8127, 8128, 8129, 8130, 8131, 8132, 8133, 8134, 8135, 8136, 8137, 8138, 8139, 8140, 8141, 8142, 8143, 8144, 8145, 8146, 8147, 8148, 8149, 8150, 8151, 8152, 8153, 8154, 8155, 8156, 8157, 8158, 8159, 8160, 8161, 8162, 8163, 8164, 8165, 8166, 8167, 8168, 8169, 8170, 8171, 8172, 8173, 8174, 8175, 8176, 8177, 8178, 8179, 8180, 8181, 8182, 8183, 8184, 8185, 8186, 8187, 8188, 8189, 8190, 8191, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8203, 8204, 8205, 8206, 8207, 8208, 8209, 8210, 8211, 8212, 8213, 8214, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8223, 8224, 8225, 8226, 8227, 8228, 8229, 8230, 8231, 8232, 8233, 8234, 8235, 8236, 8237, 8238, 8239, 8240, 8241, 8242, 8243, 8244, 8245, 8246, 8247, 8248, 8249, 8250, 8251, 8252, 8253, 8254, 8255, 8256, 8257, 8258, 8259, 8260, 8261, 8262, 8263, 8264, 8265, 8266, 8267, 8268, 8269, 8270, 8271, 8272, 8273, 8274, 8275, 8276, 8277, 8278, 8279, 8280, 8281, 8282, 8283, 8284, 8285, 8286, 8287, 8288, 8289, 8290, 8291, 8292, 8293, 8294, 8295, 8296, 8297, 8298, 8299, 8300, 8301, 8302, 8303, 8304, 8305, 8306, 8307, 8308, 8309, 8310, 8311, 8312, 8313, 8314, 8315, 8316, 8317, 8318, 8319, 8320, 8321, 8322, 8323, 8324, 8325, 8326, 8327, 8328, 8329, 8330, 8331, 8332, 8333, 8334, 8335, 8336, 8337, 8338, 8339, 8340, 8341, 8342, 8343, 8344, 8345, 8346, 8347, 8348, 8349, 8350, 8351, 8352, 8353, 8354, 8355, 8356, 8357, 8358, 8359, 8360, 8361, 8362, 8363, 8364, 8365, 8366, 8367, 8368, 8369, 8370, 8371, 8372, 8373, 8374, 8375, 8376, 8377, 8378, 8379, 8380, 8381, 8382, 8383, 8384, 8385, 8386, 8387, 8388, 8389, 8390, 8391, 8392, 8393, 8394, 8395, 8396, 8397, 8398, 8399, 8400, 8401, 8402, 8403, 8404, 8405, 8406, 8407, 8408, 8409, 8410, 8411, 8412, 8413, 8414, 8415, 8416, 8417, 8418, 8419, 8420, 8421, 8422, 8423, 8424, 8425, 8426, 8427, 8428, 8429, 8430, 8431, 8432, 8433 } ; static yyconst flex_int16_t yy_def[12713] = { 0, 8615, 8615, 8616, 8616, 8616, 8616, 8614, 8614, 8617, 8614, 8614, 8617, 8617, 8617, 8614, 8614, 8618, 8618, 8614, 8618, 8618, 8619, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8620, 8620, 8620, 8620, 8619, 8619, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8621, 8621, 8621, 8621, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8622, 8622, 8622, 8622, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8623, 8623, 8623, 8623, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8624, 8624, 8624, 8624, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8625, 8625, 8625, 8625, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8626, 8626, 8626, 8626, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8627, 8627, 8627, 8627, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8628, 8628, 8628, 8628, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8629, 8629, 8629, 8629, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8630, 8630, 8630, 8630, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8631, 8631, 8631, 8631, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8632, 8632, 8632, 8632, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8633, 8633, 8633, 8633, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8634, 8634, 8634, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8635, 8635, 8614, 8614, 8614, 8614, 8614, 8614, 8636, 8636, 8614, 8614, 8637, 8637, 8638, 8638, 8639, 8639, 8640, 8640, 8641, 8641, 8642, 8642, 8643, 8643, 8644, 8644, 8645, 8645, 8646, 8646, 8647, 8647, 8648, 8648, 8649, 8649, 8650, 8650, 8651, 8651, 8652, 8652, 8653, 8653, 8654, 8654, 8655, 8655, 8656, 8656, 8657, 8657, 8658, 8658, 8659, 8659, 8660, 8660, 8661, 8661, 8662, 8662, 8663, 8663, 8664, 8664, 8665, 8665, 8666, 8666, 8667, 8667, 8668, 8668, 8669, 8669, 8670, 8670, 8671, 8671, 8672, 8672, 8673, 8673, 8674, 8674, 8675, 8675, 8676, 8676, 8677, 8677, 8678, 8678, 8679, 8679, 8680, 8680, 8681, 8681, 8682, 8682, 8683, 8683, 8684, 8684, 8685, 8685, 8686, 8686, 8687, 8687, 8688, 8688, 8689, 8689, 8690, 8690, 8691, 8691, 8692, 8692, 8693, 8693, 8694, 8694, 8695, 8695, 8696, 8696, 8697, 8697, 8698, 8698, 8699, 8699, 8700, 8700, 8701, 8701, 8702, 8702, 8703, 8703, 8704, 8704, 8705, 8705, 8706, 8706, 8707, 8707, 8708, 8708, 8709, 8709, 8710, 8710, 8711, 8711, 8712, 8712, 8713, 8713, 8714, 8714, 8715, 8715, 8716, 8716, 8717, 8717, 8718, 8718, 8719, 8719, 8720, 8720, 8721, 8721, 8722, 8722, 8723, 8723, 8724, 8724, 8725, 8725, 8726, 8726, 8727, 8727, 8728, 8728, 8729, 8729, 8730, 8730, 8731, 8731, 8732, 8732, 8733, 8733, 8734, 8734, 8735, 8735, 8736, 8736, 8737, 8737, 8738, 8738, 8739, 8739, 8740, 8740, 8741, 8741, 8742, 8742, 8743, 8743, 8744, 8744, 8745, 8745, 8746, 8746, 8747, 8747, 8748, 8748, 8749, 8749, 8750, 8750, 8751, 8751, 8752, 8752, 8753, 8753, 8754, 8754, 8755, 8755, 8756, 8756, 8757, 8757, 8758, 8758, 8759, 8759, 8760, 8760, 8761, 8761, 8762, 8762, 8763, 8763, 8764, 8764, 8765, 8765, 8766, 8766, 8767, 8767, 8768, 8768, 8769, 8769, 8770, 8770, 8771, 8771, 8772, 8772, 8773, 8773, 8774, 8774, 8775, 8775, 8776, 8776, 8777, 8777, 8778, 8778, 8779, 8779, 8780, 8780, 8781, 8781, 8782, 8782, 8783, 8783, 8784, 8784, 8785, 8785, 8786, 8786, 8787, 8787, 8788, 8788, 8789, 8789, 8790, 8790, 8791, 8791, 8792, 8792, 8793, 8793, 8794, 8794, 8795, 8795, 8796, 8796, 8797, 8797, 8798, 8798, 8799, 8799, 8800, 8800, 8801, 8801, 8802, 8802, 8803, 8803, 8804, 8804, 8805, 8805, 8806, 8806, 8807, 8807, 8808, 8808, 8809, 8809, 8810, 8810, 8811, 8811, 8812, 8812, 8813, 8813, 8814, 8814, 8815, 8815, 8816, 8816, 8817, 8817, 8818, 8818, 8819, 8819, 8820, 8820, 8821, 8821, 8822, 8822, 8823, 8823, 8824, 8824, 8825, 8825, 8826, 8826, 8827, 8827, 8828, 8828, 8829, 8829, 8830, 8830, 8831, 8831, 8832, 8832, 8833, 8833, 8834, 8834, 8835, 8835, 8836, 8836, 8837, 8837, 8838, 8838, 8839, 8839, 8840, 8840, 8841, 8841, 8842, 8842, 8843, 8843, 8844, 8844, 8845, 8845, 8846, 8846, 8847, 8847, 8848, 8848, 8849, 8849, 8850, 8850, 8851, 8851, 8852, 8852, 8853, 8853, 8854, 8854, 8855, 8855, 8856, 8856, 8857, 8857, 8858, 8858, 8859, 8859, 8860, 8860, 8861, 8861, 8862, 8862, 8863, 8863, 8864, 8864, 8865, 8865, 8866, 8866, 8867, 8867, 8868, 8868, 8869, 8869, 8870, 8870, 8871, 8871, 8872, 8872, 8873, 8873, 8874, 8874, 8875, 8875, 8876, 8876, 8877, 8877, 8878, 8878, 8879, 8879, 8880, 8880, 8881, 8881, 8882, 8882, 8883, 8883, 8884, 8884, 8885, 8885, 8886, 8886, 8887, 8887, 8888, 8888, 8889, 8889, 8890, 8890, 8891, 8891, 8892, 8892, 8893, 8893, 8894, 8894, 8895, 8895, 8896, 8896, 8897, 8897, 8898, 8898, 8899, 8899, 8900, 8900, 8901, 8901, 8902, 8902, 8903, 8903, 8904, 8904, 8905, 8905, 8906, 8906, 8907, 8907, 8908, 8908, 8909, 8909, 8910, 8910, 8911, 8911, 8912, 8912, 8913, 8913, 8914, 8914, 8915, 8915, 8916, 8916, 8917, 8917, 8918, 8918, 8919, 8919, 8920, 8920, 8921, 8921, 8922, 8922, 8923, 8923, 8924, 8924, 8925, 8925, 8926, 8926, 8927, 8927, 8928, 8928, 8929, 8929, 8930, 8930, 8931, 8931, 8932, 8932, 8933, 8933, 8934, 8934, 8935, 8935, 8936, 8936, 8937, 8937, 8938, 8938, 8939, 8939, 8940, 8940, 8941, 8941, 8942, 8942, 8943, 8943, 8944, 8944, 8945, 8945, 8946, 8946, 8947, 8947, 8948, 8948, 8949, 8949, 8950, 8950, 8951, 8951, 8952, 8952, 8953, 8953, 8954, 8954, 8955, 8955, 8956, 8956, 8957, 8957, 8958, 8958, 8959, 8959, 8960, 8960, 8961, 8961, 8962, 8962, 8963, 8963, 8964, 8964, 8965, 8965, 8966, 8966, 8967, 8967, 8968, 8968, 8969, 8969, 8970, 8970, 8971, 8971, 8972, 8972, 8973, 8973, 8974, 8974, 8975, 8975, 8976, 8976, 8977, 8977, 8978, 8978, 8979, 8979, 8980, 8980, 8981, 8981, 8982, 8982, 8983, 8983, 8984, 8984, 8985, 8985, 8986, 8986, 8987, 8987, 8988, 8988, 8989, 8989, 8990, 8990, 8991, 8991, 8992, 8992, 8993, 8993, 8994, 8994, 8995, 8995, 8996, 8996, 8997, 8997, 8998, 8998, 8999, 8999, 9000, 9000, 9001, 9001, 9002, 9002, 9003, 9003, 9004, 9004, 9005, 9005, 9006, 9006, 9007, 9007, 9008, 9008, 9009, 9009, 9010, 9010, 9011, 9011, 9012, 9012, 9013, 9013, 9014, 9014, 9015, 9015, 9016, 9016, 9017, 9017, 9018, 9018, 9019, 9019, 9020, 9020, 9021, 9021, 9022, 9022, 9023, 9023, 9024, 9024, 9025, 9025, 9026, 9026, 9027, 9027, 9028, 9028, 9029, 9029, 9030, 9030, 9031, 9031, 9032, 9032, 9033, 9033, 9034, 9034, 9035, 9035, 9036, 9036, 9037, 9037, 9038, 9038, 9039, 9039, 9040, 9040, 9041, 9041, 9042, 9042, 9043, 9043, 9044, 9044, 9045, 9045, 9046, 9046, 9047, 9047, 9048, 9048, 9049, 9049, 9050, 9050, 9051, 9051, 9052, 9052, 9053, 9053, 9054, 9054, 9055, 9055, 9056, 9056, 9057, 9057, 9058, 9058, 9059, 9059, 9060, 9060, 9061, 9061, 9062, 9062, 9063, 9063, 9064, 9064, 9065, 9065, 9066, 9066, 9067, 9067, 9068, 9068, 9069, 9069, 9070, 9070, 9071, 9071, 9072, 9072, 9073, 9073, 9074, 9074, 9075, 9075, 9076, 9076, 9077, 9077, 9078, 9078, 9079, 9079, 9080, 9080, 9081, 9081, 9082, 9082, 9083, 9083, 9084, 9084, 9085, 9085, 9086, 9086, 9087, 9087, 9088, 9088, 9089, 9089, 9090, 9090, 9091, 9091, 9092, 9092, 9093, 9093, 9094, 9094, 9095, 9095, 9096, 9096, 9097, 9097, 9098, 9098, 9099, 9099, 9100, 9100, 9101, 9101, 9102, 9102, 9103, 9103, 9104, 9104, 9105, 9105, 9106, 9106, 9107, 9107, 9108, 9108, 9109, 9109, 9110, 9110, 9111, 9111, 9112, 9112, 9113, 9113, 9114, 9114, 9115, 9115, 9116, 9116, 9117, 9117, 9118, 9118, 9119, 9119, 9120, 9120, 9121, 9121, 9122, 9122, 9123, 9123, 9124, 9124, 9125, 9125, 9126, 9126, 9127, 9127, 9128, 9128, 9129, 9129, 9130, 9130, 9131, 9131, 9132, 9132, 9133, 9133, 9134, 9134, 9135, 9135, 9136, 9136, 9137, 9137, 9138, 9138, 9139, 9139, 9140, 9140, 9141, 9141, 9142, 9142, 9143, 9143, 9144, 9144, 9145, 9145, 9146, 9146, 9147, 9147, 9148, 9148, 9149, 9149, 9150, 9150, 9151, 9151, 9152, 9152, 9153, 9153, 9154, 9154, 9155, 9155, 9156, 9156, 9157, 9157, 9158, 9158, 9159, 9159, 9160, 9160, 9161, 9161, 9162, 9162, 9163, 9163, 9164, 9164, 9165, 9165, 9166, 9166, 9167, 9167, 9168, 9168, 9169, 9169, 9170, 9170, 9171, 9171, 9172, 9172, 9173, 9173, 9174, 9174, 9175, 9175, 9176, 9176, 9177, 9177, 9178, 9178, 9179, 9179, 9180, 9180, 9181, 9181, 9182, 9182, 9183, 9183, 9184, 9184, 9185, 9185, 9186, 9186, 9187, 9187, 9188, 9188, 9189, 9189, 9190, 9190, 9191, 9191, 9192, 9192, 9193, 9193, 9194, 9194, 9195, 9195, 9196, 9196, 9197, 9197, 9198, 9198, 9199, 9199, 9200, 9200, 9201, 9201, 9202, 9202, 9203, 9203, 9204, 9204, 9205, 9205, 9206, 9206, 9207, 9207, 9208, 9208, 9209, 9209, 9210, 9210, 9211, 9211, 9212, 9212, 9213, 9213, 9214, 9214, 9215, 9215, 9216, 9216, 9217, 9217, 9218, 9218, 9219, 9219, 9220, 9220, 9221, 9221, 9222, 9222, 9223, 9223, 9224, 9224, 9225, 9225, 9226, 9226, 9227, 9227, 9228, 9228, 9229, 9229, 9230, 9230, 9231, 9231, 9232, 9232, 9233, 9233, 9234, 9234, 9235, 9235, 9236, 9236, 9237, 9237, 9238, 9238, 9239, 9239, 9240, 9240, 9241, 9241, 9242, 9242, 9243, 9243, 9244, 9244, 9245, 9245, 9246, 9246, 9247, 9247, 9248, 9248, 9249, 9249, 9250, 9250, 9251, 9251, 9252, 9252, 9253, 9253, 9254, 9254, 9255, 9255, 9256, 9256, 9257, 9257, 9258, 9258, 9259, 9259, 9260, 9260, 9261, 9261, 9262, 9262, 9263, 9263, 9264, 9264, 9265, 9265, 9266, 9266, 9267, 9267, 9268, 9268, 9269, 9269, 9270, 9270, 9271, 9271, 9272, 9272, 9273, 9273, 9274, 9274, 9275, 9275, 9276, 9276, 9277, 9277, 9278, 9278, 9279, 9279, 9280, 9280, 9281, 9281, 9282, 9282, 9283, 9283, 9284, 9284, 9285, 9285, 9286, 9286, 9287, 9287, 9288, 9288, 9289, 9289, 9290, 9290, 9291, 9291, 9292, 9292, 9293, 9293, 9294, 9294, 9295, 9295, 9296, 9296, 9297, 9297, 9298, 9298, 9299, 9299, 9300, 9300, 9301, 9301, 9302, 9302, 9303, 9303, 9304, 9304, 9305, 9305, 9306, 9306, 9307, 9307, 9308, 9308, 9309, 9309, 9310, 9310, 9311, 9311, 9312, 9312, 9313, 9313, 9314, 9314, 9315, 9315, 9316, 9316, 9317, 9317, 9318, 9318, 9319, 9319, 9320, 9320, 9321, 9321, 9322, 9322, 9323, 9323, 9324, 9324, 9325, 9325, 9326, 9326, 9327, 9327, 9328, 9328, 9329, 9329, 9330, 9330, 9331, 9331, 9332, 9332, 9333, 9333, 9334, 9334, 9335, 9335, 9336, 9336, 9337, 9337, 9338, 9338, 9339, 9339, 9340, 9340, 9341, 9341, 9342, 9342, 9343, 9343, 9344, 9344, 9345, 9345, 9346, 9346, 9347, 9347, 9348, 9348, 9349, 9349, 9350, 9350, 9351, 9351, 9352, 9352, 9353, 9353, 9354, 9354, 9355, 9355, 9356, 9356, 9357, 9357, 9358, 9358, 9359, 9359, 9360, 9360, 9361, 9361, 9362, 9362, 9363, 9363, 9364, 9364, 9365, 9365, 9366, 9366, 9367, 9367, 9368, 9368, 9369, 9369, 9370, 9370, 9371, 9371, 9372, 9372, 9373, 9373, 9374, 9374, 9375, 9375, 9376, 9376, 9377, 9377, 9378, 9378, 9379, 9379, 9380, 9380, 9381, 9381, 9382, 9382, 9383, 9383, 9384, 9384, 9385, 9385, 9386, 9386, 9387, 9387, 9388, 9388, 9389, 9389, 9390, 9390, 9391, 9391, 9392, 9392, 9393, 9393, 9394, 9394, 9395, 9395, 9396, 9396, 9397, 9397, 9398, 9398, 9399, 9399, 9400, 9400, 9401, 9401, 9402, 9402, 9403, 9403, 9404, 9404, 9405, 9405, 9406, 9406, 9407, 9407, 9408, 9408, 9409, 9409, 9410, 9410, 9411, 9411, 9412, 9412, 9413, 9413, 9414, 9414, 9415, 9415, 9416, 9416, 9417, 9417, 9418, 9418, 9419, 9419, 9420, 9420, 9421, 9421, 9422, 9422, 9423, 9423, 9424, 9424, 9425, 9425, 9426, 9426, 9427, 9427, 9428, 9428, 9429, 9429, 9430, 9430, 9431, 9431, 9432, 9432, 9433, 9433, 9434, 9434, 9435, 9435, 9436, 9436, 9437, 9437, 9438, 9438, 9439, 9439, 9440, 9440, 9441, 9441, 9442, 9442, 9443, 9443, 9444, 9444, 9445, 9445, 9446, 9446, 9447, 9447, 9448, 9448, 9449, 9449, 9450, 9450, 9451, 9451, 9452, 9452, 9453, 9453, 9454, 9454, 9455, 9455, 9456, 9456, 9457, 9457, 9458, 9458, 9459, 9459, 9460, 9460, 9461, 9461, 9462, 9462, 9463, 9463, 9464, 9464, 9465, 9465, 9466, 9466, 9467, 9467, 9468, 9468, 9469, 9469, 9470, 9470, 9471, 9471, 9472, 9472, 9473, 9473, 9474, 9474, 9475, 9475, 9476, 9476, 9477, 9477, 9478, 9478, 9479, 9479, 9480, 9480, 9481, 9481, 9482, 9482, 9483, 9483, 9484, 9484, 9485, 9485, 9486, 9486, 9487, 9487, 9488, 9488, 9489, 9489, 9490, 9490, 9491, 9491, 9492, 9492, 9493, 9493, 9494, 9494, 9495, 9495, 9496, 9496, 9497, 9497, 9498, 9498, 9499, 9499, 9500, 9500, 9501, 9501, 9502, 9502, 9503, 9503, 9504, 9504, 9505, 9505, 9506, 9506, 9507, 9507, 9508, 9508, 9509, 9509, 9510, 9510, 9511, 9511, 9512, 9512, 9513, 9513, 9514, 9514, 9515, 9515, 9516, 9516, 9517, 9517, 9518, 9518, 9519, 9519, 9520, 9520, 9521, 9521, 9522, 9522, 9523, 9523, 9524, 9524, 9525, 9525, 9526, 9526, 9527, 9527, 9528, 9528, 9529, 9529, 9530, 9530, 9531, 9531, 9532, 9532, 9533, 9533, 9534, 9534, 9535, 9535, 9536, 9536, 9537, 9537, 9538, 9538, 9539, 9539, 9540, 9540, 9541, 9541, 9542, 9542, 9543, 9543, 9544, 9544, 9545, 9545, 9546, 9546, 9547, 9547, 9548, 9548, 9549, 9549, 9550, 9550, 9551, 9551, 9552, 9552, 9553, 9553, 9554, 9554, 9555, 9555, 9556, 9556, 9557, 9557, 9558, 9558, 9559, 9559, 9560, 9560, 9561, 9561, 9562, 9562, 9563, 9563, 9564, 9564, 9565, 9565, 9566, 9566, 9567, 9567, 9568, 9568, 9569, 9569, 9570, 9570, 9571, 9571, 9572, 9572, 9573, 9573, 9574, 9574, 9575, 9575, 9576, 9576, 9577, 9577, 9578, 9578, 9579, 9579, 9580, 9580, 9581, 9581, 9582, 9582, 9583, 9583, 9584, 9584, 9585, 9585, 9586, 9586, 9587, 9587, 9588, 9588, 9589, 9589, 9590, 9590, 9591, 9591, 9592, 9592, 9593, 9593, 9594, 9594, 9595, 9595, 9596, 9596, 9597, 9597, 9598, 9598, 9599, 9599, 9600, 9600, 9601, 9601, 9602, 9602, 9603, 9603, 9604, 9604, 9605, 9605, 9606, 9606, 9607, 9607, 9608, 9608, 9609, 9609, 9610, 9610, 9611, 9611, 9612, 9612, 9613, 9613, 9614, 9614, 9615, 9615, 9616, 9616, 9617, 9617, 9618, 9618, 9619, 9619, 9620, 9620, 9621, 9621, 9622, 9622, 9623, 9623, 9624, 9624, 9625, 9625, 9626, 9626, 9627, 9627, 9628, 9628, 9629, 9629, 9630, 9630, 9631, 9631, 9632, 9632, 9633, 9633, 9634, 9634, 9635, 9635, 9636, 9636, 9637, 9637, 9638, 9638, 9639, 9639, 9640, 9640, 9641, 9641, 9642, 9642, 9643, 9643, 9644, 9644, 9645, 9645, 9646, 9646, 9647, 9647, 9648, 9648, 9649, 9649, 9650, 9650, 9651, 9651, 9652, 9652, 9653, 9653, 9654, 9654, 9655, 9655, 9656, 9656, 9657, 9657, 9658, 9658, 9659, 9659, 9660, 9660, 9661, 9661, 9662, 9662, 9663, 9663, 9664, 9664, 9665, 9665, 9666, 9666, 9667, 9667, 9668, 9668, 9669, 9669, 9670, 9670, 9671, 9671, 9672, 9672, 9673, 9673, 9674, 9674, 9675, 9675, 9676, 9676, 9677, 9677, 9678, 9678, 9679, 9679, 9680, 9680, 9681, 9681, 9682, 9682, 9683, 9683, 9684, 9684, 9685, 9685, 9686, 9686, 9687, 9687, 9688, 9688, 9689, 9689, 9690, 9690, 9691, 9691, 9692, 9692, 9693, 9693, 9694, 9694, 9695, 9695, 9696, 9696, 9697, 9697, 9698, 9698, 9699, 9699, 9700, 9700, 9701, 9701, 9702, 9702, 9703, 9703, 9704, 9704, 9705, 9705, 9706, 9706, 9707, 9707, 9708, 9708, 9709, 9709, 9710, 9710, 9711, 9711, 9712, 9712, 9713, 9713, 9714, 9714, 9715, 9715, 9716, 9716, 9717, 9717, 9718, 9718, 9719, 9719, 9720, 9720, 9721, 9721, 9722, 9722, 9723, 9723, 9724, 9724, 9725, 9725, 9726, 9726, 9727, 9727, 9728, 9728, 9729, 9729, 9730, 9730, 9731, 9731, 9732, 9732, 9733, 9733, 9734, 9734, 9735, 9735, 9736, 9736, 9737, 9737, 9738, 9738, 9739, 9739, 9740, 9740, 9741, 9741, 9742, 9742, 9743, 9743, 9744, 9744, 9745, 9745, 9746, 9746, 9747, 9747, 9748, 9748, 9749, 9749, 9750, 9750, 9751, 9751, 9752, 9752, 9753, 9753, 9754, 9754, 9755, 9755, 9756, 9756, 9757, 9757, 9758, 9758, 9759, 9759, 9760, 9760, 9761, 9761, 9762, 9762, 9763, 9763, 9764, 9764, 9765, 9765, 9766, 9766, 9767, 9767, 9768, 9768, 9769, 9769, 9770, 9770, 9771, 9771, 9772, 9772, 9773, 9773, 9774, 9774, 9775, 9775, 9776, 9776, 9777, 9777, 9778, 9778, 9779, 9779, 9780, 9780, 9781, 9781, 9782, 9782, 9783, 9783, 9784, 9784, 9785, 9785, 9786, 9786, 9787, 9787, 9788, 9788, 9789, 9789, 9790, 9790, 9791, 9791, 9792, 9792, 9793, 9793, 9794, 9794, 9795, 9795, 9796, 9796, 9797, 9797, 9798, 9798, 9799, 9799, 9800, 9800, 9801, 9801, 9802, 9802, 9803, 9803, 9804, 9804, 9805, 9805, 9806, 9806, 9807, 9807, 9808, 9808, 9809, 9809, 9810, 9810, 9811, 9811, 9812, 9812, 9813, 9813, 9814, 9814, 9815, 9815, 9816, 9816, 9817, 9817, 9818, 9818, 9819, 9819, 9820, 9820, 9821, 9821, 9822, 9822, 9823, 9823, 9824, 9824, 9825, 9825, 9826, 9826, 9827, 9827, 9828, 9828, 9829, 9829, 9830, 9830, 9831, 9831, 9832, 9832, 9833, 9833, 9834, 9834, 9835, 9835, 9836, 9836, 9837, 9837, 9838, 9838, 9839, 9839, 9840, 9840, 9841, 9841, 9842, 9842, 9843, 9843, 9844, 9844, 9845, 9845, 9846, 9846, 9847, 9847, 9848, 9848, 9849, 9849, 9850, 9850, 9851, 9851, 9852, 9852, 9853, 9853, 9854, 9854, 9855, 9855, 9856, 9856, 9857, 9857, 9858, 9858, 9859, 9859, 9860, 9860, 9861, 9861, 9862, 9862, 9863, 9863, 9864, 9864, 9865, 9865, 9866, 9866, 9867, 9867, 9868, 9868, 9869, 9869, 9870, 9870, 9871, 9871, 9872, 9872, 9873, 9873, 9874, 9874, 9875, 9875, 9876, 9876, 9877, 9877, 9878, 9878, 9879, 9879, 9880, 9880, 9881, 9881, 9882, 9882, 9883, 9883, 9884, 9884, 9885, 9885, 9886, 9886, 9887, 9887, 9888, 9888, 9889, 9889, 9890, 9890, 9891, 9891, 9892, 9892, 9893, 9893, 9894, 9894, 9895, 9895, 9896, 9896, 9897, 9897, 9898, 9898, 9899, 9899, 9900, 9900, 9901, 9901, 9902, 9902, 9903, 9903, 9904, 9904, 9905, 9905, 9906, 9906, 9907, 9907, 9908, 9908, 9909, 9909, 9910, 9910, 9911, 9911, 9912, 9912, 9913, 9913, 9914, 9914, 9915, 9915, 9916, 9916, 9917, 9917, 9918, 9918, 9919, 9919, 9920, 9920, 9921, 9921, 9922, 9922, 9923, 9923, 9924, 9924, 9925, 9925, 9926, 9926, 9927, 9927, 9928, 9928, 9929, 9929, 9930, 9930, 9931, 9931, 9932, 9932, 9933, 9933, 9934, 9934, 9935, 9935, 9936, 9936, 9937, 9937, 9938, 9938, 9939, 9939, 9940, 9940, 9941, 9941, 9942, 9942, 9943, 9943, 9944, 9944, 9945, 9945, 9946, 9946, 9947, 9947, 9948, 9948, 9949, 9949, 9950, 9950, 9951, 9951, 9952, 9952, 9953, 9953, 9954, 9954, 9955, 9955, 9956, 9956, 9957, 9957, 9958, 9958, 9959, 9959, 9960, 9960, 9961, 9961, 9962, 9962, 9963, 9963, 9964, 9964, 9965, 9965, 9966, 9966, 9967, 9967, 9968, 9968, 9969, 9969, 9970, 9970, 9971, 9971, 9972, 9972, 9973, 9973, 9974, 9974, 9975, 9975, 9976, 9976, 9977, 9977, 9978, 9978, 9979, 9979, 9980, 9980, 9981, 9981, 9982, 9982, 9983, 9983, 9984, 9984, 9985, 9985, 9986, 9986, 9987, 9987, 9988, 9988, 9989, 9989, 9990, 9990, 9991, 9991, 9992, 9992, 9993, 9993, 9994, 9994, 9995, 9995, 9996, 9996, 9997, 9997, 9998, 9998, 9999, 9999,10000,10000,10001,10001,10002,10002, 10003,10003,10004,10004,10005,10005,10006,10006,10007,10007, 10008,10008,10009,10009,10010,10010,10011,10011,10012,10012, 10013,10013,10014,10014,10015,10015,10016,10016,10017,10017, 10018,10018,10019,10019,10020,10020,10021,10021,10022,10022, 10023,10023,10024,10024,10025,10025,10026,10026,10027,10027, 10028,10028,10029,10029,10030,10030,10031,10031,10032,10032, 10033,10033,10034,10034,10035,10035,10036,10036,10037,10037, 10038,10038,10039,10039,10040,10040,10041,10041,10042,10042, 10043,10043,10044,10044,10045,10045,10046,10046,10047,10047, 10048,10048,10049,10049,10050,10050,10051,10051,10052,10052, 10053,10053,10054,10054,10055,10055,10056,10056,10057,10057, 10058,10058,10059,10059,10060,10060,10061,10061,10062,10062, 10063,10063,10064,10064,10065,10065,10066,10066,10067,10067, 10068,10068,10069,10069,10070,10070,10071,10071,10072,10072, 10073,10073,10074,10074,10075,10075,10076,10076,10077,10077, 10078,10078,10079,10079,10080,10080,10081,10081,10082,10082, 10083,10083,10084,10084,10085,10085,10086,10086,10087,10087, 10088,10088,10089,10089,10090,10090,10091,10091,10092,10092, 10093,10093,10094,10094,10095,10095,10096,10096,10097,10097, 10098,10098,10099,10099,10100,10100,10101,10101,10102,10102, 10103,10103,10104,10104,10105,10105,10106,10106,10107,10107, 10108,10108,10109,10109,10110,10110,10111,10111,10112,10112, 10113,10113,10114,10114,10115,10115,10116,10116,10117,10117, 10118,10118,10119,10119,10120,10120,10121,10121,10122,10122, 10123,10123,10124,10124,10125,10125,10126,10126,10127,10127, 10128,10128,10129,10129,10130,10130,10131,10131,10132,10132, 10133,10133,10134,10134,10135,10135,10136,10136,10137,10137, 10138,10138,10139,10139,10140,10140,10141,10141,10142,10142, 10143,10143,10144,10144,10145,10145,10146,10146,10147,10147, 10148,10148,10149,10149,10150,10150,10151,10151,10152,10152, 10153,10153,10154,10154,10155,10155,10156,10156,10157,10157, 10158,10158,10159,10159,10160,10160,10161,10161,10162,10162, 10163,10163,10164,10164,10165,10165,10166,10166,10167,10167, 10168,10168,10169,10169,10170,10170,10171,10171,10172,10172, 10173,10173,10174,10174,10175,10175,10176,10176,10177,10177, 10178,10178,10179,10179,10180,10180,10181,10181,10182,10182, 10183,10183,10184,10184,10185,10185,10186,10186,10187,10187, 10188,10188,10189,10189,10190,10190,10191,10191,10192,10192, 10193,10193,10194,10194,10195,10195,10196,10196,10197,10197, 10198,10198,10199,10199,10200,10200,10201,10201,10202,10202, 10203,10203,10204,10204,10205,10205,10206,10206,10207,10207, 10208,10208,10209,10209,10210,10210,10211,10211,10212,10212, 10213,10213,10214,10214,10215,10215,10216,10216,10217,10217, 10218,10218,10219,10219,10220,10220,10221,10221,10222,10222, 10223,10223,10224,10224,10225,10225,10226,10226,10227,10227, 10228,10228,10229,10229,10230,10230,10231,10231,10232,10232, 10233,10233,10234,10234,10235,10235,10236,10236,10237,10237, 10238,10238,10239,10239,10240,10240,10241,10241,10242,10242, 10243,10243,10244,10244,10245,10245,10246,10246,10247,10247, 10248,10248,10249,10249,10250,10250,10251,10251,10252,10252, 10253,10253,10254,10254,10255,10255,10256,10256,10257,10257, 10258,10258,10259,10259,10260,10260,10261,10261,10262,10262, 10263,10263,10264,10264,10265,10265,10266,10266,10267,10267, 10268,10268,10269,10269,10270,10270,10271,10271,10272,10272, 10273,10273,10274,10274,10275,10275,10276,10276,10277,10277, 10278,10278,10279,10279,10280,10280,10281,10281,10282,10282, 10283,10283,10284,10284,10285,10285,10286,10286,10287,10287, 10288,10288,10289,10289,10290,10290,10291,10291,10292,10292, 10293,10293,10294,10294,10295,10295,10296,10296,10297,10297, 10298,10298,10299,10299,10300,10300,10301,10301,10302,10302, 10303,10303,10304,10304,10305,10305,10306,10306,10307,10307, 10308,10308,10309,10309,10310,10310,10311,10311,10312,10312, 10313,10313,10314,10314,10315,10315,10316,10316,10317,10317, 10318,10318,10319,10319,10320,10320,10321,10321,10322,10322, 10323,10323,10324,10324,10325,10325,10326,10326,10327,10327, 10328,10328,10329,10329,10330,10330,10331,10331,10332,10332, 10333,10333,10334,10334,10335,10335,10336,10336,10337,10337, 10338,10338,10339,10339,10340,10340,10341,10341,10342,10342, 10343,10343,10344,10344,10345,10345,10346,10346,10347,10347, 10348,10348,10349,10349,10350,10350,10351,10351,10352,10352, 10353,10353,10354,10354,10355,10355,10356,10356,10357,10357, 10358,10358,10359,10359,10360,10360,10361,10361,10362,10362, 10363,10363,10364,10364,10365,10365,10366,10366,10367,10367, 10368,10368,10369,10369,10370,10370,10371,10371,10372,10372, 10373,10373,10374,10374,10375,10375,10376,10376,10377,10377, 10378,10378,10379,10379,10380,10380,10381,10381,10382,10382, 10383,10383,10384,10384,10385,10385,10386,10386,10387,10387, 10388,10388,10389,10389,10390,10390,10391,10391,10392,10392, 10393,10393,10394,10394,10395,10395,10396,10396,10397,10397, 10398,10398,10399,10399,10400,10400,10401,10401,10402,10402, 10403,10403,10404,10404,10405,10405,10406,10406,10407,10407, 10408,10408,10409,10409,10410,10410,10411,10411,10412,10412, 10413,10413,10414,10414,10415,10415,10416,10416,10417,10417, 10418,10418,10419,10419,10420,10420,10421,10421,10422,10422, 10423,10423,10424,10424,10425,10425,10426,10426,10427,10427, 10428,10428,10429,10429,10430,10430,10431,10431,10432,10432, 10433,10433,10434,10434,10435,10435,10436,10436,10437,10437, 10438,10438,10439,10439,10440,10440,10441,10441,10442,10442, 10443,10443,10444,10444,10445,10445,10446,10446,10447,10447, 10448,10448,10449,10449,10450,10450,10451,10451,10452,10452, 10453,10453,10454,10454,10455,10455,10456,10456,10457,10457, 10458,10458,10459,10459,10460,10460,10461,10461,10462,10462, 10463,10463,10464,10464,10465,10465,10466,10466,10467,10467, 10468,10468,10469,10469,10470,10470,10471,10471,10472,10472, 10473,10473,10474,10474,10475,10475,10476,10476,10477,10477, 10478,10478,10479,10479,10480,10480,10481,10481,10482,10482, 10483,10483,10484,10484,10485,10485,10486,10486,10487,10487, 10488,10488,10489,10489,10490,10490,10491,10491,10492,10492, 10493,10493,10494,10494,10495,10495,10496,10496,10497,10497, 10498,10498,10499,10499,10500,10500,10501,10501,10502,10502, 10503,10503,10504,10504,10505,10505,10506,10506,10507,10507, 10508,10508,10509,10509,10510,10510,10511,10511,10512,10512, 10513,10513,10514,10514,10515,10515,10516,10516,10517,10517, 10518,10518,10519,10519,10520,10520,10521,10521,10522,10522, 10523,10523,10524,10524,10525,10525,10526,10526,10527,10527, 10528,10528,10529,10529,10530,10530,10531,10531,10532,10532, 10533,10533,10534,10534,10535,10535,10536,10536,10537,10537, 10538,10538,10539,10539,10540,10540,10541,10541,10542,10542, 10543,10543,10544,10544,10545,10545,10546,10546,10547,10547, 10548,10548,10549,10549,10550,10550,10551,10551,10552,10552, 10553,10553,10554,10554,10555,10555,10556,10556,10557,10557, 10558,10558,10559,10559,10560,10560,10561,10561,10562,10562, 10563,10563,10564,10564,10565,10565,10566,10566,10567,10567, 10568,10568,10569,10569,10570,10570,10571,10571,10572,10572, 10573,10573,10574,10574,10575,10575,10576,10576,10577,10577, 10578,10578,10579,10579,10580,10580,10581,10581,10582,10582, 10583,10583,10584,10584,10585,10585,10586,10586,10587,10587, 10588,10588,10589,10589,10590,10590,10591,10591,10592,10592, 10593,10593,10594,10594,10595,10595,10596,10596,10597,10597, 10598,10598,10599,10599,10600,10600,10601,10601,10602,10602, 10603,10603,10604,10604,10605,10605,10606,10606,10607,10607, 10608,10608,10609,10609,10610,10610,10611,10611,10612,10612, 10613,10613,10614,10614,10615,10615,10616,10616,10617,10617, 10618,10618,10619,10619,10620,10620,10621,10621,10622,10622, 10623,10623,10624,10624,10625,10625,10626,10626,10627,10627, 10628,10628,10629,10629,10630,10630,10631,10631,10632,10632, 10633,10633,10634,10634,10635,10635,10636,10636,10637,10637, 10638,10638,10639,10639,10640,10640,10641,10641,10642,10642, 10643,10643,10644,10644,10645,10645,10646,10646,10647,10647, 10648,10648,10649,10649,10650,10650,10651,10651,10652,10652, 10653,10653,10654,10654,10655,10655,10656,10656,10657,10657, 10658,10658,10659,10659,10660,10660,10661,10661,10662,10662, 10663,10663,10664,10664,10665,10665,10666,10666,10667,10667, 10668,10668,10669,10669,10670,10670,10671,10671,10672,10672, 10673,10673,10674,10674,10675,10675,10676,10676,10677,10677, 10678,10678,10679,10679,10680,10680,10681,10681,10682,10682, 10683,10683,10684,10684,10685,10685,10686,10686,10687,10687, 10688,10688,10689,10689,10690,10690,10691,10691,10692,10692, 10693,10693,10694,10694,10695,10695,10696,10696,10697,10697, 10698,10698,10699,10699,10700,10700,10701,10701,10702,10702, 10703,10703,10704,10704,10705,10705,10706,10706,10707,10707, 10708,10708,10709,10709,10710,10710,10711,10711,10712,10712, 10713,10713,10714,10714,10715,10715,10716,10716,10717,10717, 10718,10718,10719,10719,10720,10720,10721,10721,10722,10722, 10723,10723,10724,10724,10725,10725,10726,10726,10727,10727, 10728,10728,10729,10729,10730,10730,10731,10731,10732,10732, 10733,10733,10734,10734,10735,10735,10736,10736,10737,10737, 10738,10738,10739,10739,10740,10740,10741,10741,10742,10742, 10743,10743,10744,10744,10745,10745,10746,10746,10747,10747, 10748,10748,10749,10749,10750,10750,10751,10751,10752,10752, 10753,10753,10754,10754,10755,10755,10756,10756,10757,10757, 10758,10758,10759,10759,10760,10760,10761,10761,10762,10762, 10763,10763,10764,10764,10765,10765,10766,10766,10767,10767, 10768,10768,10769,10769,10770,10770,10771,10771,10772,10772, 10773,10773,10774,10774,10775,10775,10776,10776,10777,10777, 10778,10778,10779,10779,10780,10780,10781,10781,10782,10782, 10783,10783,10784,10784,10785,10785,10786,10786,10787,10787, 10788,10788,10789,10789,10790,10790,10791,10791,10792,10792, 10793,10793,10794,10794,10795,10795,10796,10796,10797,10797, 10798,10798,10799,10799,10800,10800,10801,10801,10802,10802, 10803,10803,10804,10804,10805,10805,10806,10806,10807,10807, 10808,10808,10809,10809,10810,10810,10811,10811,10812,10812, 10813,10813,10814,10814,10815,10815,10816,10816,10817,10817, 10818,10818,10819,10819,10820,10820,10821,10821,10822,10822, 10823,10823,10824,10824,10825,10825,10826,10826,10827,10827, 10828,10828,10829,10829,10830,10830,10831,10831,10832,10832, 10833,10833,10834,10834,10835,10835,10836,10836,10837,10837, 10838,10838,10839,10839,10840,10840,10841,10841,10842,10842, 10843,10843,10844,10844,10845,10845,10846,10846,10847,10847, 10848,10848,10849,10849,10850,10850,10851,10851,10852,10852, 10853,10853,10854,10854,10855,10855,10856,10856,10857,10857, 10858,10858,10859,10859,10860,10860,10861,10861,10862,10862, 10863,10863,10864,10864,10865,10865,10866,10866,10867,10867, 10868,10868,10869,10869,10870,10870,10871,10871,10872,10872, 10873,10873,10874,10874,10875,10875,10876,10876,10877,10877, 10878,10878,10879,10879,10880,10880,10881,10881,10882,10882, 10883,10883,10884,10884,10885,10885,10886,10886,10887,10887, 10888,10888,10889,10889,10890,10890,10891,10891,10892,10892, 10893,10893,10894,10894,10895,10895,10896,10896,10897,10897, 10898,10898,10899,10899,10900,10900,10901,10901,10902,10902, 10903,10903,10904,10904,10905,10905,10906,10906,10907,10907, 10908,10908,10909,10909,10910,10910,10911,10911,10912,10912, 10913,10913,10914,10914,10915,10915,10916,10916,10917,10917, 10918,10918,10919,10919,10920,10920,10921,10921,10922,10922, 10923,10923,10924,10924,10925,10925,10926,10926,10927,10927, 10928,10928,10929,10929,10930,10930,10931,10931,10932,10932, 10933,10933,10934,10934,10935,10935,10936,10936,10937,10937, 10938,10938,10939,10939,10940,10940,10941,10941,10942,10942, 10943,10943,10944,10944,10945,10945,10946,10946,10947,10947, 10948,10948,10949,10949,10950,10950,10951,10951,10952,10952, 10953,10953,10954,10954,10955,10955,10956,10956,10957,10957, 10958,10958,10959,10959,10960,10960,10961,10961,10962,10962, 10963,10963,10964,10964,10965,10965,10966,10966,10967,10967, 10968,10968,10969,10969,10970,10970,10971,10971,10972,10972, 10973,10973,10974,10974,10975,10975,10976,10976,10977,10977, 10978,10978,10979,10979,10980,10980,10981,10981,10982,10982, 10983,10983,10984,10984,10985,10985,10986,10986,10987,10987, 10988,10988,10989,10989,10990,10990,10991,10991,10992,10992, 10993,10993,10994,10994,10995,10995,10996,10996,10997,10997, 10998,10998,10999,10999,11000,11000,11001,11001,11002,11002, 11003,11003,11004,11004,11005,11005,11006,11006,11007,11007, 11008,11008,11009,11009,11010,11010,11011,11011,11012,11012, 11013,11013,11014,11014,11015,11015,11016,11016,11017,11017, 11018,11018,11019,11019,11020,11020,11021,11021,11022,11022, 11023,11023,11024,11024,11025,11025,11026,11026,11027,11027, 11028,11028,11029,11029,11030,11030,11031,11031,11032,11032, 11033,11033,11034,11034,11035,11035,11036,11036,11037,11037, 11038,11038,11039,11039,11040,11040,11041,11041,11042,11042, 11043,11043,11044,11044,11045,11045,11046,11046,11047,11047, 11048,11048,11049,11049,11050,11050,11051,11051,11052,11052, 11053,11053,11054,11054,11055,11055,11056,11056,11057,11057, 11058,11058,11059,11059,11060,11060,11061,11061,11062,11062, 11063,11063,11064,11064,11065,11065,11066,11066,11067,11067, 11068,11068,11069,11069,11070,11070,11071,11071,11072,11072, 11073,11073,11074,11074,11075,11075,11076,11076,11077,11077, 11078,11078,11079,11079,11080,11080,11081,11081,11082,11082, 11083,11083,11084,11084,11085,11085,11086,11086,11087,11087, 11088,11088,11089,11089,11090,11090,11091,11091,11092,11092, 11093,11093,11094,11094,11095,11095,11096,11096,11097,11097, 11098,11098,11099,11099,11100,11100,11101,11101,11102,11102, 11103,11103,11104,11104,11105,11105,11106,11106,11107,11107, 11108,11108,11109,11109,11110,11110,11111,11111,11112,11112, 11113,11113,11114,11114,11115,11115,11116,11116,11117,11117, 11118,11118,11119,11119,11120,11120,11121,11121,11122,11122, 11123,11123,11124,11124,11125,11125,11126,11126,11127,11127, 11128,11128,11129,11129,11130,11130,11131,11131,11132,11132, 11133,11133,11134,11134,11135,11135,11136,11136,11137,11137, 11138,11138,11139,11139,11140,11140,11141,11141,11142,11142, 11143,11143,11144,11144,11145,11145,11146,11146,11147,11147, 11148,11148,11149,11149,11150,11150,11151,11151,11152,11152, 11153,11153,11154,11154,11155,11155,11156,11156,11157,11157, 11158,11158,11159,11159,11160,11160,11161,11161,11162,11162, 11163,11163,11164,11164,11165,11165,11166,11166,11167,11167, 11168,11168,11169,11169,11170,11170,11171,11171,11172,11172, 11173,11173,11174,11174,11175,11175,11176,11176,11177,11177, 11178,11178,11179,11179,11180,11180,11181,11181,11182,11182, 11183,11183,11184,11184,11185,11185,11186,11186,11187,11187, 11188,11188,11189,11189,11190,11190,11191,11191,11192,11192, 11193,11193,11194,11194,11195,11195,11196,11196,11197,11197, 11198,11198,11199,11199,11200,11200,11201,11201,11202,11202, 11203,11203,11204,11204,11205,11205,11206,11206,11207,11207, 11208,11208,11209,11209,11210,11210,11211,11211,11212,11212, 11213,11213,11214,11214,11215,11215,11216,11216,11217,11217, 11218,11218,11219,11219,11220,11220,11221,11221,11222,11222, 11223,11223,11224,11224,11225,11225,11226,11226,11227,11227, 11228,11228,11229,11229,11230,11230,11231,11231,11232,11232, 11233,11233,11234,11234,11235,11235,11236,11236,11237,11237, 11238,11238,11239,11239,11240,11240,11241,11241,11242,11242, 11243,11243,11244,11244,11245,11245,11246,11246,11247,11247, 11248,11248,11249,11249,11250,11250,11251,11251,11252,11252, 11253,11253,11254,11254,11255,11255,11256,11256,11257,11257, 11258,11258,11259,11259,11260,11260,11261,11261,11262,11262, 11263,11263,11264,11264,11265,11265,11266,11266,11267,11267, 11268,11268,11269,11269,11270,11270,11271,11271,11272,11272, 11273,11273,11274,11274,11275,11275,11276,11276,11277,11277, 11278,11278,11279,11279,11280,11280,11281,11281,11282,11282, 11283,11283,11284,11284,11285,11285,11286,11286,11287,11287, 11288,11288,11289,11289,11290,11290,11291,11291,11292,11292, 11293,11293,11294,11294,11295,11295,11296,11296,11297,11297, 11298,11298,11299,11299,11300,11300,11301,11301,11302,11302, 11303,11303,11304,11304,11305,11305,11306,11306,11307,11307, 11308,11308,11309,11309,11310,11310,11311,11311,11312,11312, 11313,11313,11314,11314,11315,11315,11316,11316,11317,11317, 11318,11318,11319,11319,11320,11320,11321,11321,11322,11322, 11323,11323,11324,11324,11325,11325,11326,11326,11327,11327, 11328,11328,11329,11329,11330,11330,11331,11331,11332,11332, 11333,11333,11334,11334,11335,11335,11336,11336,11337,11337, 11338,11338,11339,11339,11340,11340,11341,11341,11342,11342, 11343,11343,11344,11344,11345,11345,11346,11346,11347,11347, 11348,11348,11349,11349,11350,11350,11351,11351,11352,11352, 11353,11353,11354,11354,11355,11355,11356,11356,11357,11357, 11358,11358,11359,11359,11360,11360,11361,11361,11362,11362, 11363,11363,11364,11364,11365,11365,11366,11366,11367,11367, 11368,11368,11369,11369,11370,11370,11371,11371,11372,11372, 11373,11373,11374,11374,11375,11375,11376,11376,11377,11377, 11378,11378,11379,11379,11380,11380,11381,11381,11382,11382, 11383,11383,11384,11384,11385,11385,11386,11386,11387,11387, 11388,11388,11389,11389,11390,11390,11391,11391,11392,11392, 11393,11393,11394,11394,11395,11395,11396,11396,11397,11397, 11398,11398,11399,11399,11400,11400,11401,11401,11402,11402, 11403,11403,11404,11404,11405,11405,11406,11406,11407,11407, 11408,11408,11409,11409,11410,11410,11411,11411,11412,11412, 11413,11413,11414,11414,11415,11415,11416,11416,11417,11417, 11418,11418,11419,11419,11420,11420,11421,11421,11422,11422, 11423,11423,11424,11424,11425,11425,11426,11426,11427,11427, 11428,11428,11429,11429,11430,11430,11431,11431,11432,11432, 11433,11433,11434,11434,11435,11435,11436,11436,11437,11437, 11438,11438,11439,11439,11440,11440,11441,11441,11442,11442, 11443,11443,11444,11444,11445,11445,11446,11446,11447,11447, 11448,11448,11449,11449,11450,11450,11451,11451,11452,11452, 11453,11453,11454,11454,11455,11455,11456,11456,11457,11457, 11458,11458,11459,11459,11460,11460,11461,11461,11462,11462, 11463,11463,11464,11464,11465,11465,11466,11466,11467,11467, 11468,11468,11469,11469,11470,11470,11471,11471,11472,11472, 11473,11473,11474,11474,11475,11475,11476,11476,11477,11477, 11478,11478,11479,11479,11480,11480,11481,11481,11482,11482, 11483,11483,11484,11484,11485,11485,11486,11486,11487,11487, 11488,11488,11489,11489,11490,11490,11491,11491,11492,11492, 11493,11493,11494,11494,11495,11495,11496,11496,11497,11497, 11498,11498,11499,11499,11500,11500,11501,11501,11502,11502, 11503,11503,11504,11504,11505,11505,11506,11506,11507,11507, 11508,11508,11509,11509,11510,11510,11511,11511,11512,11512, 11513,11513,11514,11514,11515,11515,11516,11516,11517,11517, 11518,11518,11519,11519,11520,11520,11521,11521,11522,11522, 11523,11523,11524,11524,11525,11525,11526,11526,11527,11527, 11528,11528,11529,11529,11530,11530,11531,11531,11532,11532, 11533,11533,11534,11534,11535,11535,11536,11536,11537,11537, 11538,11538,11539,11539,11540,11540,11541,11541,11542,11542, 11543,11543,11544,11544,11545,11545,11546,11546,11547,11547, 11548,11548,11549,11549,11550,11550,11551,11551,11552,11552, 11553,11553,11554,11554,11555,11555,11556,11556,11557,11557, 11558,11558,11559,11559,11560,11560,11561,11561,11562,11562, 11563,11563,11564,11564,11565,11565,11566,11566,11567,11567, 11568,11568,11569,11569,11570,11570,11571,11571,11572,11572, 11573,11573,11574,11574,11575,11575,11576,11576,11577,11577, 11578,11578,11579,11579,11580,11580,11581,11581,11582,11582, 11583,11583,11584,11584,11585,11585,11586,11586,11587,11587, 11588,11588,11589,11589,11590,11590,11591,11591,11592,11592, 11593,11593,11594,11594,11595,11595,11596,11596,11597,11597, 11598,11598,11599,11599,11600,11600,11601,11601,11602,11602, 11603,11603,11604,11604,11605,11605,11606,11606,11607,11607, 11608,11608,11609,11609,11610,11610,11611,11611,11612,11612, 11613,11613,11614,11614,11615,11615,11616,11616,11617,11617, 11618,11618,11619,11619,11620,11620,11621,11621,11622,11622, 11623,11623,11624,11624,11625,11625,11626,11626,11627,11627, 11628,11628,11629,11629,11630,11630,11631,11631,11632,11632, 11633,11633,11634,11634,11635,11635,11636,11636,11637,11637, 11638,11638,11639,11639,11640,11640,11641,11641,11642,11642, 11643,11643,11644,11644,11645,11645,11646,11646,11647,11647, 11648,11648,11649,11649,11650,11650,11651,11651,11652,11652, 11653,11653,11654,11654,11655,11655,11656,11656,11657,11657, 11658,11658,11659,11659,11660,11660,11661,11661,11662,11662, 11663,11663,11664,11664,11665,11665,11666,11666,11667,11667, 11668,11668,11669,11669,11670,11670,11671,11671,11672,11672, 11673,11673,11674,11674,11675,11675,11676,11676,11677,11677, 11678,11678,11679,11679,11680,11680,11681,11681,11682,11682, 11683,11683,11684,11684,11685,11685,11686,11686,11687,11687, 11688,11688,11689,11689,11690,11690,11691,11691,11692,11692, 11693,11693,11694,11694,11695,11695,11696,11696,11697,11697, 11698,11698,11699,11699,11700,11700,11701,11701,11702,11702, 11703,11703,11704,11704,11705,11705,11706,11706,11707,11707, 11708,11708,11709,11709,11710,11710,11711,11711,11712,11712, 11713,11713,11714,11714,11715,11715,11716,11716,11717,11717, 11718,11718,11719,11719,11720,11720,11721,11721,11722,11722, 11723,11723,11724,11724,11725,11725,11726,11726,11727,11727, 11728,11728,11729,11729,11730,11730,11731,11731,11732,11732, 11733,11733,11734,11734,11735,11735,11736,11736,11737,11737, 11738,11738,11739,11739,11740,11740,11741,11741,11742,11742, 11743,11743,11744,11744,11745,11745,11746,11746,11747,11747, 11748,11748,11749,11749,11750,11750,11751,11751,11752,11752, 11753,11753,11754,11754,11755,11755,11756,11756,11757,11757, 11758,11758,11759,11759,11760,11760,11761,11761,11762,11762, 11763,11763,11764,11764,11765,11765,11766,11766,11767,11767, 11768,11768,11769,11769,11770,11770,11771,11771,11772,11772, 11773,11773,11774,11774,11775,11775,11776,11776,11777,11777, 11778,11778,11779,11779,11780,11780,11781,11781,11782,11782, 11783,11783,11784,11784,11785,11785,11786,11786,11787,11787, 11788,11788,11789,11789,11790,11790,11791,11791,11792,11792, 11793,11793,11794,11794,11795,11795,11796,11796,11797,11797, 11798,11798,11799,11799,11800,11800,11801,11801,11802,11802, 11803,11803,11804,11804,11805,11805,11806,11806,11807,11807, 11808,11808,11809,11809,11810,11810,11811,11811,11812,11812, 11813,11813,11814,11814,11815,11815,11816,11816,11817,11817, 11818,11818,11819,11819,11820,11820,11821,11821,11822,11822, 11823,11823,11824,11824,11825,11825,11826,11826,11827,11827, 11828,11828,11829,11829,11830,11830,11831,11831,11832,11832, 11833,11833,11834,11834,11835,11835,11836,11836,11837,11837, 11838,11838,11839,11839,11840,11840,11841,11841,11842,11842, 11843,11843,11844,11844,11845,11845,11846,11846,11847,11847, 11848,11848,11849,11849,11850,11850,11851,11851,11852,11852, 11853,11853,11854,11854,11855,11855,11856,11856,11857,11857, 11858,11858,11859,11859,11860,11860,11861,11861,11862,11862, 11863,11863,11864,11864,11865,11865,11866,11866,11867,11867, 11868,11868,11869,11869,11870,11870,11871,11871,11872,11872, 11873,11873,11874,11874,11875,11875,11876,11876,11877,11877, 11878,11878,11879,11879,11880,11880,11881,11881,11882,11882, 11883,11883,11884,11884,11885,11885,11886,11886,11887,11887, 11888,11888,11889,11889,11890,11890,11891,11891,11892,11892, 11893,11893,11894,11894,11895,11895,11896,11896,11897,11897, 11898,11898,11899,11899,11900,11900,11901,11901,11902,11902, 11903,11903,11904,11904,11905,11905,11906,11906,11907,11907, 11908,11908,11909,11909,11910,11910,11911,11911,11912,11912, 11913,11913,11914,11914,11915,11915,11916,11916,11917,11917, 11918,11918,11919,11919,11920,11920,11921,11921,11922,11922, 11923,11923,11924,11924,11925,11925,11926,11926,11927,11927, 11928,11928,11929,11929,11930,11930,11931,11931,11932,11932, 11933,11933,11934,11934,11935,11935,11936,11936,11937,11937, 11938,11938,11939,11939,11940,11940,11941,11941,11942,11942, 11943,11943,11944,11944,11945,11945,11946,11946,11947,11947, 11948,11948,11949,11949,11950,11950,11951,11951,11952,11952, 11953,11953,11954,11954,11955,11955,11956,11956,11957,11957, 11958,11958,11959,11959,11960,11960,11961,11961,11962,11962, 11963,11963,11964,11964,11965,11965,11966,11966,11967,11967, 11968,11968,11969,11969,11970,11970,11971,11971,11972,11972, 11973,11973,11974,11974,11975,11975,11976,11976,11977,11977, 11978,11978,11979,11979,11980,11980,11981,11981,11982,11982, 11983,11983,11984,11984,11985,11985,11986,11986,11987,11987, 11988,11988,11989,11989,11990,11990,11991,11991,11992,11992, 11993,11993,11994,11994,11995,11995,11996,11996,11997,11997, 11998,11998,11999,11999,12000,12000,12001,12001,12002,12002, 12003,12003,12004,12004,12005,12005,12006,12006,12007,12007, 12008,12008,12009,12009,12010,12010,12011,12011,12012,12012, 12013,12013,12014,12014,12015,12015,12016,12016,12017,12017, 12018,12018,12019,12019,12020,12020,12021,12021,12022,12022, 12023,12023,12024,12024,12025,12025,12026,12026,12027,12027, 12028,12028,12029,12029,12030,12030,12031,12031,12032,12032, 12033,12033,12034,12034,12035,12035,12036,12036,12037,12037, 12038,12038,12039,12039,12040,12040,12041,12041,12042,12042, 12043,12043,12044,12044,12045,12045,12046,12046,12047,12047, 12048,12048,12049,12049,12050,12050,12051,12051,12052,12052, 12053,12053,12054,12054,12055,12055,12056,12056,12057,12057, 12058,12058,12059,12059,12060,12060,12061,12061,12062,12062, 12063,12063,12064,12064,12065,12065,12066,12066,12067,12067, 12068,12068,12069,12069,12070,12070,12071,12071,12072,12072, 12073,12073,12074,12074,12075,12075,12076,12076,12077,12077, 12078,12078,12079,12079,12080,12080,12081,12081,12082,12082, 12083,12083,12084,12084,12085,12085,12086,12086,12087,12087, 12088,12088,12089,12089,12090,12090,12091,12091,12092,12092, 12093,12093,12094,12094,12095,12095,12096,12096,12097,12097, 12098,12098,12099,12099,12100,12100,12101,12101,12102,12102, 12103,12103,12104,12104,12105,12105,12106,12106,12107,12107, 12108,12108,12109,12109,12110,12110,12111,12111,12112,12112, 12113,12113,12114,12114,12115,12115,12116,12116,12117,12117, 12118,12118,12119,12119,12120,12120,12121,12121,12122,12122, 12123,12123,12124,12124,12125,12125,12126,12126,12127,12127, 12128,12128,12129,12129,12130,12130,12131,12131,12132,12132, 12133,12133,12134,12134,12135,12135,12136,12136,12137,12137, 12138,12138,12139,12139,12140,12140,12141,12141,12142,12142, 12143,12143,12144,12144,12145,12145,12146,12146,12147,12147, 12148,12148,12149,12149,12150,12150,12151,12151,12152,12152, 12153,12153,12154,12154,12155,12155,12156,12156,12157,12157, 12158,12158,12159,12159,12160,12160,12161,12161,12162,12162, 12163,12163,12164,12164,12165,12165,12166,12166,12167,12167, 12168,12168,12169,12169,12170,12170,12171,12171,12172,12172, 12173,12173,12174,12174,12175,12175,12176,12176,12177,12177, 12178,12178,12179,12179,12180,12180,12181,12181,12182,12182, 12183,12183,12184,12184,12185,12185,12186,12186,12187,12187, 12188,12188,12189,12189,12190,12190,12191,12191,12192,12192, 12193,12193,12194,12194,12195,12195,12196,12196,12197,12197, 12198,12198,12199,12199,12200,12200,12201,12201,12202,12202, 12203,12203,12204,12204,12205,12205,12206,12206,12207,12207, 12208,12208,12209,12209,12210,12210,12211,12211,12212,12212, 12213,12213,12214,12214,12215,12215,12216,12216,12217,12217, 12218,12218,12219,12219,12220,12220,12221,12221,12222,12222, 12223,12223,12224,12224,12225,12225,12226,12226,12227,12227, 12228,12228,12229,12229,12230,12230,12231,12231,12232,12232, 12233,12233,12234,12234,12235,12235,12236,12236,12237,12237, 12238,12238,12239,12239,12240,12240,12241,12241,12242,12242, 12243,12243,12244,12244,12245,12245,12246,12246,12247,12247, 12248,12248,12249,12249,12250,12250,12251,12251,12252,12252, 12253,12253,12254,12254,12255,12255,12256,12256,12257,12257, 12258,12258,12259,12259,12260,12260,12261,12261,12262,12262, 12263,12263,12264,12264,12265,12265,12266,12266,12267,12267, 12268,12268,12269,12269,12270,12270,12271,12271,12272,12272, 12273,12273,12274,12274,12275,12275,12276,12276,12277,12277, 12278,12278,12279,12279,12280,12280,12281,12281,12282,12282, 12283,12283,12284,12284,12285,12285,12286,12286,12287,12287, 12288,12288,12289,12289,12290,12290,12291,12291,12292,12292, 12293,12293,12294,12294,12295,12295,12296,12296,12297,12297, 12298,12298,12299,12299,12300,12300,12301,12301,12302,12302, 12303,12303,12304,12304,12305,12305,12306,12306,12307,12307, 12308,12308,12309,12309,12310,12310,12311,12311,12312,12312, 12313,12313,12314,12314,12315,12315,12316,12316,12317,12317, 12318,12318,12319,12319,12320,12320,12321,12321,12322,12322, 12323,12323,12324,12324,12325,12325,12326,12326,12327,12327, 12328,12328,12329,12329,12330,12330,12331,12331,12332,12332, 12333,12333,12334,12334,12335,12335,12336,12336,12337,12337, 12338,12338,12339,12339,12340,12340,12341,12341,12342,12342, 12343,12343,12344,12344,12345,12345,12346,12346,12347,12347, 12348,12348,12349,12349,12350,12350,12351,12351,12352,12352, 12353,12353,12354,12354,12355,12355,12356,12356,12357,12357, 12358,12358,12359,12359,12360,12360,12361,12361,12362,12362, 12363,12363,12364,12364,12365,12365,12366,12366,12367,12367, 12368,12368,12369,12369,12370,12370,12371,12371,12372,12372, 12373,12373,12374,12374,12375,12375,12376,12376,12377,12377, 12378,12378,12379,12379,12380,12380,12381,12381,12382,12382, 12383,12383,12384,12384,12385,12385,12386,12386,12387,12387, 12388,12388,12389,12389,12390,12390,12391,12391,12392,12392, 12393,12393,12394,12394,12395,12395,12396,12396,12397,12397, 12398,12398,12399,12399,12400,12400,12401,12401,12402,12402, 12403,12403,12404,12404,12405,12405,12406,12406,12407,12407, 12408,12408,12409,12409,12410,12410,12411,12411,12412,12412, 12413,12413,12414,12414,12415,12415,12416,12416,12417,12417, 12418,12418,12419,12419,12420,12420,12421,12421,12422,12422, 12423,12423,12424,12424,12425,12425,12426,12426,12427,12427, 12428,12428,12429,12429,12430,12430,12431,12431,12432,12432, 12433,12433,12434,12434,12435,12435,12436,12436,12437,12437, 12438,12438,12439,12439,12440,12440,12441,12441,12442,12442, 12443,12443,12444,12444,12445,12445,12446,12446,12447,12447, 12448,12448,12449,12449,12450,12450,12451,12451,12452,12452, 12453,12453,12454,12454,12455,12455,12456,12456,12457,12457, 12458,12458,12459,12459,12460,12460,12461,12461,12462,12462, 12463,12463,12464,12464,12465,12465,12466,12466,12467,12467, 12468,12468,12469,12469,12470,12470,12471,12471,12472,12472, 12473,12473,12474,12474,12475,12475,12476,12476,12477,12477, 12478,12478,12479,12479,12480,12480,12481,12481,12482,12482, 12483,12483,12484,12484,12485,12485,12486,12486,12487,12487, 12488,12488,12489,12489,12490,12490,12491,12491,12492,12492, 12493,12493,12494,12494,12495,12495,12496,12496,12497,12497, 12498,12498,12499,12499,12500,12500,12501,12501,12502,12502, 12503,12503,12504,12504,12505,12505,12506,12506,12507,12507, 12508,12508,12509,12509,12510,12510,12511,12511,12512,12512, 12513,12513,12514,12514,12515,12515,12516,12516,12517,12517, 12518,12518,12519,12519,12520,12520,12521,12521,12522,12522, 12523,12523,12524,12524,12525,12525,12526,12526,12527,12527, 12528,12528,12529,12529,12530,12530,12531,12531,12532,12532, 12533,12533,12534,12534,12535,12535,12536,12536,12537,12537, 12538,12538,12539,12539,12540,12540,12541,12541,12542,12542, 12543,12543,12544,12544,12545,12545,12546,12546,12547,12547, 12548,12548,12549,12549,12550,12550,12551,12551,12552,12552, 12553,12553,12554,12554,12555,12555,12556,12556,12557,12557, 12558,12558,12559,12559,12560,12560,12561,12561,12562,12562, 12563,12563,12564,12564,12565,12565,12566,12566,12567,12567, 12568,12568,12569,12569,12570,12570,12571,12571,12572,12572, 12573,12573,12574,12574,12575,12575,12576,12576,12577,12577, 12578,12578,12579,12579,12580,12580,12581,12581,12582,12582, 12583,12583,12584,12584,12585,12585,12586,12586,12587,12587, 12588,12588,12589,12589,12590,12590,12591,12591,12592,12592, 12593,12593,12594,12594,12595,12595,12596,12596,12597,12597, 12598,12598,12599,12599,12600,12600,12601,12601,12602,12602, 12603,12603,12604,12604,12605,12605,12606,12606,12607,12607, 12608,12608,12609,12609,12610,12610,12611,12611,12612,12612, 12613,12613,12614,12614,12615,12615,12616,12616,12617,12617, 12618,12618,12619,12619,12620,12620,12621,12621,12622,12622, 12623,12623,12624,12624,12625,12625,12626,12626,12627,12627, 12628,12628,12629,12629,12630,12630,12631,12631,12632,12632, 12633,12633,12634,12634,12635,12635,12636,12636,12637,12637, 12638,12638,12639,12639,12640,12640,12641,12641,12642,12642, 12643,12643,12644,12644,12645,12645,12646,12646,12647,12647, 12648,12648,12649,12649,12650,12650,12651,12651,12652,12652, 12653,12653,12654,12654,12655,12655,12656,12656,12657,12657, 12658,12658,12659,12659,12660,12660,12661,12661,12662,12662, 12663,12663,12664,12664,12665,12665,12666,12666,12667,12667, 12668,12668,12669,12669,12670,12670,12671,12671,12672,12672, 12673,12673,12674,12674,12675,12675,12676,12676,12677,12677, 12678,12678,12679,12679,12680,12680,12681,12681,12682,12682, 12683,12683,12684,12684,12685,12685,12686,12686,12687,12687, 12688,12688,12689,12689,12690,12690,12691,12691,12692,12692, 12693,12693,12694,12694,12695,12695,12696,12696,12697,12697, 12698,12698,12699,12699,12700,12700,12701,12701,12702,12702, 12703,12703,12704,12704,12705,12705,12706,12706,12707,12707, 12708,12708,12709,12709,12710,12710,12711,12711,12712,12712, 8614, 8614, 8614, 0, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614 } ; static yyconst flex_uint16_t yy_nxt[8988] = { 0, 8, 9, 10, 11, 1241, 1243, 13, 1245, 14, 15, 8, 8, 9, 10, 11, 1247, 1249, 13, 61, 14, 15, 8, 22, 1251, 52, 23, 1253, 53, 56, 24, 57, 25, 54, 26, 27, 55, 28, 29, 58, 61, 30, 31, 32, 62, 33, 52, 78, 34, 53, 56, 24, 57, 25, 54, 26, 27, 55, 28, 29, 58, 1255, 30, 31, 32, 62, 33, 1257, 78, 34, 41, 63, 42, 64, 43, 44, 59, 45, 46, 79, 1259, 47, 48, 49, 80, 50, 60, 1261, 51, 1263, 1265, 41, 63, 42, 64, 43, 44, 59, 45, 46, 79, 65, 47, 48, 49, 80, 50, 60, 66, 51, 68, 81, 74, 67, 69, 75, 76, 83, 84, 77, 1267, 82, 65, 85, 90, 86, 92, 1269, 91, 66, 93, 68, 81, 74, 67, 69, 75, 76, 83, 84, 77, 87, 82, 94, 85, 90, 86, 92, 88, 91, 95, 93, 96, 89, 97, 98, 100, 102, 103, 104, 105, 106, 87, 107, 94, 108, 109, 1271, 116, 88, 101, 95, 117, 96, 89, 97, 98, 118, 102, 103, 104, 105, 106, 119, 107, 110, 108, 109, 111, 116, 120, 101, 121, 117, 122, 126, 124, 127, 118, 128, 129, 130, 131, 132, 119, 133, 110, 136, 137, 111, 125, 120, 138, 121, 134, 122, 126, 135, 127, 139, 128, 129, 130, 131, 132, 140, 133, 141, 136, 137, 142, 125, 143, 138, 144, 134, 145, 146, 135, 147, 139, 148, 149, 150, 151, 152, 140, 153, 141, 158, 159, 142, 160, 143, 161, 144, 162, 145, 146, 163, 147, 164, 148, 149, 150, 151, 152, 165, 153, 166, 158, 159, 167, 160, 168, 161, 169, 162, 170, 171, 163, 172, 164, 173, 174, 175, 176, 177, 165, 178, 166, 179, 180, 167, 183, 168, 185, 169, 187, 170, 171, 188, 172, 189, 173, 174, 175, 176, 177, 190, 178, 191, 179, 180, 192, 183, 193, 185, 198, 187, 199, 200, 188, 201, 189, 202, 205, 207, 209, 210, 190, 211, 191, 212, 213, 192, 214, 193, 215, 198, 216, 199, 200, 217, 201, 218, 202, 205, 207, 209, 210, 219, 211, 221, 212, 213, 222, 214, 223, 215, 224, 216, 225, 227, 217, 229, 218, 230, 235, 236, 237, 238, 219, 240, 221, 241, 242, 222, 243, 223, 244, 224, 246, 225, 227, 248, 229, 249, 230, 235, 236, 237, 238, 250, 240, 251, 241, 242, 254, 243, 255, 244, 252, 246, 256, 257, 248, 258, 249, 259, 260, 263, 268, 269, 250, 1273, 251, 272, 253, 254, 270, 255, 273, 274, 275, 256, 257, 276, 258, 277, 259, 260, 263, 268, 269, 278, 271, 281, 272, 253, 282, 283, 285, 273, 274, 275, 286, 288, 276, 289, 277, 292, 293, 290, 299, 300, 278, 271, 281, 302, 303, 282, 283, 285, 305, 306, 307, 286, 288, 291, 289, 309, 292, 293, 310, 299, 300, 312, 313, 314, 302, 303, 308, 315, 316, 305, 306, 319, 320, 325, 291, 326, 309, 327, 328, 310, 329, 332, 312, 313, 314, 333, 334, 308, 315, 316, 336, 337, 319, 320, 325, 339, 326, 340, 327, 328, 345, 329, 332, 347, 348, 350, 333, 334, 351, 352, 1275, 336, 337, 358, 359, 360, 339, 361, 340, 366, 1277, 345, 372, 373, 347, 348, 350, 374, 375, 351, 352, 353, 377, 354, 358, 359, 360, 378, 361, 355, 366, 356, 1279, 372, 373, 379, 380, 382, 374, 375, 1281, 383, 353, 377, 354, 384, 390, 391, 378, 392, 355, 393, 356, 367, 395, 368, 379, 380, 382, 396, 397, 369, 383, 370, 399, 400, 384, 390, 391, 401, 392, 402, 393, 403, 367, 395, 368, 404, 410, 411, 396, 397, 369, 412, 370, 399, 400, 413, 414, 415, 401, 417, 402, 418, 403, 419, 420, 427, 404, 410, 411, 428, 429, 430, 412, 432, 433, 434, 413, 414, 415, 435, 417, 439, 418, 440, 419, 420, 427, 441, 442, 445, 428, 429, 430, 446, 432, 433, 434, 451, 452, 1283, 435, 1285, 439, 1287, 440, 1289, 1291, 1293, 441, 442, 445, 1295, 1297, 1299, 446, 1301, 1303, 1305, 451, 452, 1307, 1309, 1311, 1313, 1315, 1317, 1319, 1321, 1323, 1325, 1327, 1329, 1331, 1333, 1335, 1337, 1339, 1341, 1343, 1345, 1347, 1349, 1351, 1353, 1355, 1357, 1359, 1361, 1363, 1365, 1367, 1369, 1371, 1373, 1375, 1377, 1379, 1381, 1383, 1385, 1387, 1389, 1391, 1393, 1395, 1397, 1399, 1401, 1403, 1405, 1407, 1409, 1411, 1413, 1415, 1417, 1419, 1421, 1423, 1425, 1427, 1429, 1431, 1433, 1435, 1437, 1439, 1441, 1443, 1445, 1447, 1449, 1451, 1453, 1455, 1457, 1459, 1461, 1463, 1465, 1467, 1469, 1471, 1473, 1475, 1477, 1479, 1481, 1483, 1485, 1487, 1489, 1491, 1493, 1495, 1497, 1499, 1501, 1503, 1505, 1507, 1509, 1511, 1513, 1515, 1517, 1519, 1521, 1523, 1525, 1527, 1529, 1531, 1533, 1535, 1537, 1539, 1541, 1543, 1545, 1547, 1549, 1551, 1553, 1555, 1557, 1559, 1561, 1563, 1565, 1567, 1569, 1571, 1573, 1575, 1577, 1579, 1581, 1583, 1585, 1587, 1589, 1591, 1593, 1595, 1597, 1599, 1601, 1603, 1605, 1607, 1609, 1611, 1613, 1615, 1617, 1619, 1621, 1623, 1625, 1627, 1629, 1631, 1633, 1635, 1637, 1639, 1641, 1643, 1645, 1647, 1649, 1651, 1653, 1655, 1657, 1659, 1661, 1663, 1665, 1667, 1669, 1671, 1673, 1675, 1677, 1679, 1681, 1683, 1685, 1687, 1689, 1691, 1693, 1695, 1697, 1699, 1701, 1703, 1705, 1707, 1709, 1711, 1713, 1715, 1717, 1719, 1721, 1723, 1725, 1727, 1729, 1731, 1733, 1735, 1737, 1739, 1741, 1743, 1745, 1747, 1749, 1751, 1753, 1755, 1757, 1759, 1761, 1763, 1765, 1767, 1769, 1771, 1773, 1775, 1777, 1779, 1781, 1783, 1785, 1787, 1789, 1791, 1793, 1795, 1797, 1799, 1801, 1803, 1805, 1807, 1809, 1811, 1813, 1815, 1817, 1819, 1821, 1823, 1825, 1827, 1829, 1831, 1833, 1835, 1837, 1839, 1841, 1843, 1845, 1847, 1849, 1851, 1853, 1855, 1857, 1859, 1861, 1863, 1865, 1867, 1869, 1871, 1873, 1875, 1877, 1879, 1881, 1883, 1885, 1887, 1889, 1891, 1893, 1895, 1897, 1899, 1901, 1903, 1905, 1907, 1909, 1911, 1913, 1915, 1917, 1919, 1921, 1923, 1925, 1927, 1929, 1931, 1933, 1935, 1937, 1939, 1941, 1943, 1945, 1947, 1949, 1951, 1953, 1955, 1957, 1959, 1961, 1963, 1965, 1967, 1969, 1971, 1973, 1975, 1977, 1979, 1981, 1983, 1985, 1987, 1989, 1991, 1993, 1995, 1997, 1999, 2001, 2003, 2005, 2007, 2009, 2011, 2013, 2015, 2017, 2019, 2021, 2023, 2025, 2027, 2029, 2031, 2033, 2035, 2037, 2039, 2041, 2043, 2045, 2047, 2049, 2051, 2053, 2055, 2057, 2059, 2061, 2063, 2065, 2067, 2069, 2071, 2073, 2075, 2077, 2079, 2081, 2083, 2085, 2087, 2089, 2091, 2093, 2095, 2097, 2099, 2101, 2103, 2105, 2107, 2109, 2111, 2113, 2115, 2117, 2119, 2121, 2123, 2125, 2127, 2129, 2131, 2133, 2135, 2137, 2139, 2141, 2143, 2145, 2147, 2149, 2151, 2153, 2155, 2157, 2159, 2161, 2163, 2165, 2167, 2169, 2171, 2173, 2175, 2177, 2179, 2181, 2183, 2185, 2187, 2189, 2191, 2193, 2195, 2197, 2199, 2201, 2203, 2205, 2207, 2209, 2211, 2213, 2215, 2217, 2219, 2221, 2223, 2225, 2227, 2229, 2231, 2233, 2235, 2237, 2239, 2241, 2243, 2245, 2247, 2249, 2251, 2253, 2255, 2257, 2259, 2261, 2263, 2265, 2267, 2269, 2271, 2273, 2275, 2277, 2279, 2281, 2283, 2285, 2287, 2289, 2291, 2293, 2295, 2297, 2299, 2301, 2303, 2305, 2307, 2309, 2311, 2313, 2315, 2317, 2319, 2321, 2323, 2325, 2327, 2329, 2331, 2333, 2335, 2337, 2339, 2341, 2343, 2345, 2347, 2349, 2351, 2353, 2355, 2357, 2359, 2361, 2363, 2365, 2367, 2369, 2371, 2373, 2375, 2377, 2379, 2381, 2383, 2385, 2387, 2389, 2391, 2393, 2395, 2397, 2399, 2401, 2403, 2405, 2407, 2409, 2411, 2413, 2415, 2417, 2419, 2421, 2423, 2425, 2427, 2429, 2431, 2433, 2435, 2437, 2439, 2441, 2443, 2445, 2447, 2449, 2451, 2453, 2455, 2457, 2459, 2461, 2463, 2465, 2467, 2469, 2471, 2473, 2475, 2477, 2479, 2481, 2483, 2485, 2487, 2489, 2491, 2493, 2495, 2497, 2499, 2501, 2503, 2505, 2507, 2509, 2511, 2513, 2515, 2517, 2519, 2521, 2523, 2525, 2527, 2529, 2531, 2533, 2535, 2537, 2539, 2541, 2543, 2545, 2547, 2549, 2551, 2553, 2555, 2557, 2559, 2561, 2563, 2565, 2567, 2569, 2571, 2573, 2575, 2577, 2579, 2581, 2583, 2585, 2587, 2589, 2591, 2593, 2595, 2597, 2599, 2601, 2603, 2605, 2607, 2609, 2611, 2613, 2615, 2617, 2619, 2621, 2623, 2625, 2627, 2629, 2631, 2633, 2635, 2637, 2639, 2641, 2643, 2645, 2647, 2649, 2651, 2653, 2655, 2657, 2659, 2661, 2663, 2665, 2667, 2669, 2671, 2673, 2675, 2677, 2679, 2681, 2683, 2685, 2687, 2689, 2691, 2693, 2695, 2697, 2699, 2701, 2703, 2705, 2707, 2709, 2711, 2713, 2715, 2717, 2719, 2721, 2723, 2725, 2727, 2729, 2731, 2733, 2735, 2737, 2739, 2741, 2743, 2745, 2747, 2749, 2751, 2753, 2755, 2757, 2759, 2761, 2763, 2765, 2767, 2769, 2771, 2773, 2775, 2777, 2779, 2781, 2783, 2785, 2787, 2789, 2791, 2793, 2795, 2797, 2799, 2801, 2803, 2805, 2807, 2809, 2811, 2813, 2815, 2817, 2819, 2821, 2823, 2825, 2827, 2829, 2831, 2833, 2835, 2837, 2839, 2841, 2843, 2845, 2847, 2849, 2851, 2853, 2855, 2857, 2859, 2861, 2863, 2865, 2867, 2869, 2871, 2873, 2875, 2877, 2879, 2881, 2883, 2885, 2887, 2889, 2891, 2893, 2895, 2897, 2899, 2901, 2903, 2905, 2907, 2909, 2911, 2913, 2915, 2917, 2919, 2921, 2923, 2925, 2927, 2929, 2931, 2933, 2935, 2937, 2939, 2941, 2943, 2945, 2947, 2949, 2951, 2953, 2955, 2957, 2959, 2961, 2963, 2965, 2967, 2969, 2971, 2973, 2975, 2977, 2979, 2981, 2983, 2985, 2987, 2989, 2991, 2993, 2995, 2997, 2999, 3001, 3003, 3005, 3007, 3009, 3011, 3013, 3015, 3017, 3019, 3021, 3023, 3025, 3027, 3029, 3031, 3033, 3035, 3037, 3039, 3041, 3043, 3045, 3047, 3049, 3051, 3053, 3055, 3057, 3059, 3061, 3063, 3065, 3067, 3069, 3071, 3073, 3075, 3077, 3079, 3081, 3083, 3085, 3087, 3089, 3091, 3093, 3095, 3097, 3099, 3101, 3103, 3105, 3107, 3109, 3111, 3113, 3115, 3117, 3119, 3121, 3123, 3125, 3127, 3129, 3131, 3133, 3135, 3137, 3139, 3141, 3143, 3145, 3147, 3149, 3151, 3153, 3155, 3157, 3159, 3161, 3163, 3165, 3167, 3169, 3171, 3173, 3175, 3177, 3179, 3181, 3183, 3185, 3187, 3189, 3191, 3193, 3195, 3197, 3199, 3201, 3203, 3205, 3207, 3209, 3211, 3213, 3215, 3217, 3219, 3221, 3223, 3225, 3227, 3229, 3231, 3233, 3235, 3237, 3239, 3241, 3243, 3245, 3247, 3249, 3251, 3253, 3255, 3257, 3259, 3261, 3263, 3265, 3267, 3269, 3271, 3273, 3275, 3277, 3279, 3281, 3283, 3285, 3287, 3289, 3291, 3293, 3295, 3297, 3299, 3301, 3303, 3305, 3307, 3309, 3311, 3313, 3315, 3317, 3319, 3321, 3323, 3325, 3327, 3329, 3331, 3333, 3335, 3337, 3339, 3341, 3343, 3345, 3347, 3349, 3351, 3353, 3355, 3357, 3359, 3361, 3363, 3365, 3367, 3369, 3371, 3373, 3375, 3377, 3379, 3381, 3383, 3385, 3387, 3389, 3391, 3393, 3395, 3397, 3399, 3401, 3403, 3405, 3407, 3409, 3411, 3413, 3415, 3417, 3419, 3421, 3423, 3425, 3427, 3429, 3431, 3433, 3435, 3437, 3439, 3441, 3443, 3445, 3447, 3449, 3451, 3453, 3455, 3457, 3459, 3461, 3463, 3465, 3467, 3469, 3471, 3473, 3475, 3477, 3479, 3481, 3483, 3485, 3487, 3489, 3491, 3493, 3495, 3497, 3499, 3501, 3503, 3505, 3507, 3509, 3511, 3513, 3515, 3517, 3519, 3521, 3523, 3525, 3527, 3529, 3531, 3533, 3535, 3537, 3539, 3541, 3543, 3545, 3547, 3549, 3551, 3553, 3555, 3557, 3559, 3561, 3563, 3565, 3567, 3569, 3571, 3573, 3575, 3577, 3579, 3581, 3583, 3585, 3587, 3589, 3591, 3593, 3595, 3597, 3599, 3601, 3603, 3605, 3607, 3609, 3611, 3613, 3615, 3617, 3619, 3621, 3623, 3625, 3627, 3629, 3631, 3633, 3635, 3637, 3639, 3641, 3643, 3645, 3647, 3649, 3651, 3653, 3655, 3657, 3659, 3661, 3663, 3665, 3667, 3669, 3671, 3673, 3675, 3677, 3679, 3681, 3683, 3685, 3687, 3689, 3691, 3693, 3695, 3697, 3699, 3701, 3703, 3705, 3707, 3709, 3711, 3713, 3715, 3717, 3719, 3721, 3723, 3725, 3727, 3729, 3731, 3733, 3735, 3737, 3739, 3741, 3743, 3745, 3747, 3749, 3751, 3753, 3755, 3757, 3759, 3761, 3763, 3765, 3767, 3769, 3771, 3773, 3775, 3777, 3779, 3781, 3783, 3785, 3787, 3789, 3791, 3793, 3795, 3797, 3799, 3801, 3803, 3805, 3807, 3809, 3811, 3813, 3815, 3817, 3819, 3821, 3823, 3825, 3827, 3829, 3831, 3833, 3835, 3837, 3839, 3841, 3843, 3845, 3847, 3849, 3851, 3853, 3855, 3857, 3859, 3861, 3863, 3865, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3885, 3887, 3889, 3891, 3893, 3895, 3897, 3899, 3901, 3903, 3905, 3907, 3909, 3911, 3913, 3915, 3917, 3919, 3921, 3923, 3925, 3927, 3929, 3931, 3933, 3935, 3937, 3939, 3941, 3943, 3945, 3947, 3949, 3951, 3953, 3955, 3957, 3959, 3961, 3963, 3965, 3967, 3969, 3971, 3973, 3975, 3977, 3979, 3981, 3983, 3985, 3987, 3989, 3991, 3993, 3995, 3997, 3999, 4001, 4003, 4005, 4007, 4009, 4011, 4013, 4015, 4017, 4019, 4021, 4023, 4025, 4027, 4029, 4031, 4033, 4035, 4037, 4039, 4041, 4043, 4045, 4047, 4049, 4051, 4053, 4055, 4057, 4059, 4061, 4063, 4065, 4067, 4069, 4071, 4073, 4075, 4077, 4079, 4081, 4083, 4085, 4087, 4089, 4091, 4093, 4095, 4097, 4099, 4101, 4103, 4105, 4107, 4109, 4111, 4113, 4115, 4117, 4119, 4121, 4123, 4125, 4127, 4129, 4131, 4133, 4135, 4137, 4139, 4141, 4143, 4145, 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, 4163, 4165, 4167, 4169, 4171, 4173, 4175, 4177, 4179, 4181, 4183, 4185, 4187, 4189, 4191, 4193, 4195, 4197, 4199, 4201, 4203, 4205, 4207, 4209, 4211, 4213, 4215, 4217, 4219, 4221, 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, 4239, 4241, 4243, 4245, 4247, 4249, 4251, 4253, 4255, 4257, 4259, 4261, 4263, 4265, 4267, 4269, 4271, 4273, 4275, 4277, 4279, 4281, 4283, 4285, 4287, 4289, 4291, 4293, 4295, 4297, 4299, 4301, 4303, 4305, 4307, 4309, 4311, 4313, 4315, 4317, 4319, 4321, 4323, 4325, 4327, 4329, 4331, 4333, 4335, 4337, 4339, 4341, 4343, 4345, 4347, 4349, 4351, 4353, 4355, 4357, 4359, 4361, 4363, 4365, 4367, 4369, 4371, 4373, 4375, 4377, 4379, 4381, 4383, 4385, 4387, 4389, 4391, 4393, 4395, 4397, 4399, 4401, 4403, 4405, 4407, 4409, 4411, 4413, 4415, 4417, 4419, 4421, 4423, 4425, 4427, 4429, 4431, 4433, 4435, 4437, 4439, 4441, 4443, 4445, 4447, 4449, 4451, 4453, 4455, 4457, 4459, 4461, 4463, 4465, 4467, 4469, 4471, 4473, 4475, 4477, 4479, 4481, 4483, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, 4501, 4503, 4505, 4507, 4509, 4511, 4513, 4515, 4517, 4519, 4521, 4523, 4525, 4527, 4529, 4531, 4533, 4535, 4537, 4539, 4541, 4543, 4545, 4547, 4549, 4551, 4553, 4555, 4557, 4559, 4561, 4563, 4565, 4567, 4569, 4571, 4573, 4575, 4577, 4579, 4581, 4583, 4585, 4587, 4589, 4591, 4593, 4595, 4597, 4599, 4601, 4603, 4605, 4607, 4609, 4611, 4613, 4615, 4617, 4619, 4621, 4623, 4625, 4627, 4629, 4631, 4633, 4635, 4637, 4639, 4641, 4643, 4645, 4647, 4649, 4651, 4653, 4655, 4657, 4659, 4661, 4663, 4665, 4667, 4669, 4671, 4673, 4675, 4677, 4679, 4681, 4683, 4685, 4687, 4689, 4691, 4693, 4695, 4697, 4699, 4701, 4703, 4705, 4707, 4709, 4711, 4713, 4715, 4717, 4719, 4721, 4723, 4725, 4727, 4729, 4731, 4733, 4735, 4737, 4739, 4741, 4743, 4745, 4747, 4749, 4751, 4753, 4755, 4757, 4759, 4761, 4763, 4765, 4767, 4769, 4771, 4773, 4775, 4777, 4779, 4781, 4783, 4785, 4787, 4789, 4791, 4793, 4795, 4797, 4799, 4801, 4803, 4805, 4807, 4809, 4811, 4813, 4815, 4817, 4819, 4821, 4823, 4825, 4827, 4829, 4831, 4833, 4835, 4837, 4839, 4841, 4843, 4845, 4847, 4849, 4851, 4853, 4855, 4857, 4859, 4861, 4863, 4865, 4867, 4869, 4871, 4873, 4875, 4877, 4879, 4881, 4883, 4885, 4887, 4889, 4891, 4893, 4895, 4897, 4899, 4901, 4903, 4905, 4907, 4909, 4911, 4913, 4915, 4917, 4919, 4921, 4923, 4925, 4927, 4929, 4931, 4933, 4935, 4937, 4939, 4941, 4943, 4945, 4947, 4949, 4951, 4953, 4955, 4957, 4959, 4961, 4963, 4965, 4967, 4969, 4971, 4973, 4975, 4977, 4979, 4981, 4983, 4985, 4987, 4989, 4991, 4993, 4995, 4997, 4999, 5001, 5003, 5005, 5007, 5009, 5011, 5013, 5015, 5017, 5019, 5021, 5023, 5025, 5027, 5029, 5031, 5033, 5035, 5037, 5039, 5041, 5043, 5045, 5047, 5049, 5051, 5053, 5055, 5057, 5059, 5061, 5063, 5065, 5067, 5069, 5071, 5073, 5075, 5077, 5079, 5081, 5083, 5085, 5087, 5089, 5091, 5093, 5095, 5097, 5099, 5101, 5103, 5105, 5107, 5109, 5111, 5113, 5115, 5117, 5119, 5121, 5123, 5125, 5127, 5129, 5131, 5133, 5135, 5137, 5139, 5141, 5143, 5145, 5147, 5149, 5151, 5153, 5155, 5157, 5159, 5161, 5163, 5165, 5167, 5169, 5171, 5173, 5175, 5177, 5179, 5181, 5183, 5185, 5187, 5189, 5191, 5193, 5195, 5197, 5199, 5201, 5203, 5205, 5207, 5209, 5211, 5213, 5215, 5217, 5219, 5221, 5223, 5225, 5227, 5229, 5231, 5233, 5235, 5237, 5239, 5241, 5243, 5245, 5247, 5249, 5251, 5253, 5255, 5257, 5259, 5261, 5263, 5265, 5267, 5269, 5271, 5273, 5275, 5277, 5279, 5281, 5283, 5285, 5287, 5289, 5291, 5293, 5295, 5297, 5299, 5301, 5303, 5305, 5307, 5309, 5311, 5313, 5315, 5317, 5319, 5321, 5323, 5325, 5327, 5329, 5331, 5333, 5335, 5337, 5339, 5341, 5343, 5345, 5347, 5349, 5351, 5353, 5355, 5357, 5359, 5361, 5363, 5365, 5367, 5369, 5371, 5373, 5375, 5377, 5379, 5381, 5383, 5385, 5387, 5389, 5391, 5393, 5395, 5397, 5399, 5401, 5403, 5405, 5407, 5409, 5411, 5413, 5415, 5417, 5419, 5421, 5423, 5425, 5427, 5429, 5431, 5433, 5435, 5437, 5439, 5441, 5443, 5445, 5447, 5449, 5451, 5453, 5455, 5457, 5459, 5461, 5463, 5465, 5467, 5469, 5471, 5473, 5475, 5477, 5479, 5481, 5483, 5485, 5487, 5489, 5491, 5493, 5495, 5497, 5499, 5501, 5503, 5505, 5507, 5509, 5511, 5513, 5515, 5517, 5519, 5521, 5523, 5525, 5527, 5529, 5531, 5533, 5535, 5537, 5539, 5541, 5543, 5545, 5547, 5549, 5551, 5553, 5555, 5557, 5559, 5561, 5563, 5565, 5567, 5569, 5571, 5573, 5575, 5577, 5579, 5581, 5583, 5585, 5587, 5589, 5591, 5593, 5595, 5597, 5599, 5601, 5603, 5605, 5607, 5609, 5611, 5613, 5615, 5617, 5619, 5621, 5623, 5625, 5627, 5629, 5631, 5633, 5635, 5637, 5639, 5641, 5643, 5645, 5647, 5649, 5651, 5653, 5655, 5657, 5659, 5661, 5663, 5665, 5667, 5669, 5671, 5673, 5675, 5677, 5679, 5681, 5683, 5685, 5687, 5689, 5691, 5693, 5695, 5697, 5699, 5701, 5703, 5705, 5707, 5709, 5711, 5713, 5715, 5717, 5719, 5721, 5723, 5725, 5727, 5729, 5731, 5733, 5735, 5737, 5739, 5741, 5743, 5745, 5747, 5749, 5751, 5753, 5755, 5757, 5759, 5761, 5763, 5765, 5767, 5769, 5771, 5773, 5775, 5777, 5779, 5781, 5783, 5785, 5787, 5789, 5791, 5793, 5795, 5797, 5799, 5801, 5803, 5805, 5807, 5809, 5811, 5813, 5815, 5817, 5819, 5821, 5823, 5825, 5827, 5829, 5831, 5833, 5835, 5837, 5839, 5841, 5843, 5845, 5847, 5849, 5851, 5853, 5855, 5857, 5859, 5861, 5863, 5865, 5867, 5869, 5871, 5873, 5875, 5877, 5879, 5881, 5883, 5885, 5887, 5889, 5891, 5893, 5895, 5897, 5899, 5901, 5903, 5905, 5907, 5909, 5911, 5913, 5915, 5917, 5919, 5921, 5923, 5925, 5927, 5929, 5931, 5933, 5935, 5937, 5939, 5941, 5943, 5945, 5947, 5949, 5951, 5953, 5955, 5957, 5959, 5961, 5963, 5965, 5967, 5969, 5971, 5973, 5975, 5977, 5979, 5981, 5983, 5985, 5987, 5989, 5991, 5993, 5995, 5997, 5999, 6001, 6003, 6005, 6007, 6009, 6011, 6013, 6015, 6017, 6019, 6021, 6023, 6025, 6027, 6029, 6031, 6033, 6035, 6037, 6039, 6041, 6043, 6045, 6047, 6049, 6051, 6053, 6055, 6057, 6059, 6061, 6063, 6065, 6067, 6069, 6071, 6073, 6075, 6077, 6079, 6081, 6083, 6085, 6087, 6089, 6091, 6093, 6095, 6097, 6099, 6101, 6103, 6105, 6107, 6109, 6111, 6113, 6115, 6117, 6119, 6121, 6123, 6125, 6127, 6129, 6131, 6133, 6135, 6137, 6139, 6141, 6143, 6145, 6147, 6149, 6151, 6153, 6155, 6157, 6159, 6161, 6163, 6165, 6167, 6169, 6171, 6173, 6175, 6177, 6179, 6181, 6183, 6185, 6187, 6189, 6191, 6193, 6195, 6197, 6199, 6201, 6203, 6205, 6207, 6209, 6211, 6213, 6215, 6217, 6219, 6221, 6223, 6225, 6227, 6229, 6231, 6233, 6235, 6237, 6239, 6241, 6243, 6245, 6247, 6249, 6251, 6253, 6255, 6257, 6259, 6261, 6263, 6265, 6267, 6269, 6271, 6273, 6275, 6277, 6279, 6281, 6283, 6285, 6287, 6289, 6291, 6293, 6295, 6297, 6299, 6301, 6303, 6305, 6307, 6309, 6311, 6313, 6315, 6317, 6319, 6321, 6323, 6325, 6327, 6329, 6331, 6333, 6335, 6337, 6339, 6341, 6343, 6345, 6347, 6349, 6351, 6353, 6355, 6357, 6359, 6361, 6363, 6365, 6367, 6369, 6371, 6373, 6375, 6377, 6379, 6381, 6383, 6385, 6387, 6389, 6391, 6393, 6395, 6397, 6399, 6401, 6403, 6405, 6407, 6409, 6411, 6413, 6415, 6417, 6419, 6421, 6423, 6425, 6427, 6429, 6431, 6433, 6435, 6437, 6439, 6441, 6443, 6445, 6447, 6449, 6451, 6453, 6455, 6457, 6459, 6461, 6463, 6465, 6467, 6469, 6471, 6473, 6475, 6477, 6479, 6481, 6483, 6485, 6487, 6489, 6491, 6493, 6495, 6497, 6499, 6501, 6503, 6505, 6507, 6509, 6511, 6513, 6515, 6517, 6519, 6521, 6523, 6525, 6527, 6529, 6531, 6533, 6535, 6537, 6539, 6541, 6543, 6545, 6547, 6549, 6551, 6553, 6555, 6557, 6559, 6561, 6563, 6565, 6567, 6569, 6571, 6573, 6575, 6577, 6579, 6581, 6583, 6585, 6587, 6589, 6591, 6593, 6595, 6597, 6599, 6601, 6603, 6605, 6607, 6609, 6611, 6613, 6615, 6617, 6619, 6621, 6623, 6625, 6627, 6629, 6631, 6633, 6635, 6637, 6639, 6641, 6643, 6645, 6647, 6649, 6651, 6653, 6655, 6657, 6659, 6661, 6663, 6665, 6667, 6669, 6671, 6673, 6675, 6677, 6679, 6681, 6683, 6685, 6687, 6689, 6691, 6693, 6695, 6697, 6699, 6701, 6703, 6705, 6707, 6709, 6711, 6713, 6715, 6717, 6719, 6721, 6723, 6725, 6727, 6729, 6731, 6733, 6735, 6737, 6739, 6741, 6743, 6745, 6747, 6749, 6751, 6753, 6755, 6757, 6759, 6761, 6763, 6765, 6767, 6769, 6771, 6773, 6775, 6777, 6779, 6781, 6783, 6785, 6787, 6789, 6791, 6793, 6795, 6797, 6799, 6801, 6803, 6805, 6807, 6809, 6811, 6813, 6815, 6817, 6819, 6821, 6823, 6825, 6827, 6829, 6831, 6833, 6835, 6837, 6839, 6841, 6843, 6845, 6847, 6849, 6851, 6853, 6855, 6857, 6859, 6861, 6863, 6865, 6867, 6869, 6871, 6873, 6875, 6877, 6879, 6881, 6883, 6885, 6887, 6889, 6891, 6893, 6895, 6897, 6899, 6901, 6903, 6905, 6907, 6909, 6911, 6913, 6915, 6917, 6919, 6921, 6923, 6925, 6927, 6929, 6931, 6933, 6935, 6937, 6939, 6941, 6943, 6945, 6947, 6949, 6951, 6953, 6955, 6957, 6959, 6961, 6963, 6965, 6967, 6969, 6971, 6973, 6975, 6977, 6979, 6981, 6983, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7001, 7003, 7005, 7007, 7009, 7011, 7013, 7015, 7017, 7019, 7021, 7023, 7025, 7027, 7029, 7031, 7033, 7035, 7037, 7039, 7041, 7043, 7045, 7047, 7049, 7051, 7053, 7055, 7057, 7059, 7061, 7063, 7065, 7067, 7069, 7071, 7073, 7075, 7077, 7079, 7081, 7083, 7085, 7087, 7089, 7091, 7093, 7095, 7097, 7099, 7101, 7103, 7105, 7107, 7109, 7111, 7113, 7115, 7117, 7119, 7121, 7123, 7125, 7127, 7129, 7131, 7133, 7135, 7137, 7139, 7141, 7143, 7145, 7147, 7149, 7151, 7153, 7155, 7157, 7159, 7161, 7163, 7165, 7167, 7169, 7171, 7173, 7175, 7177, 7179, 7181, 7183, 7185, 7187, 7189, 7191, 7193, 7195, 7197, 7199, 7201, 7203, 7205, 7207, 7209, 7211, 7213, 7215, 7217, 7219, 7221, 7223, 7225, 7227, 7229, 7231, 7233, 7235, 7237, 7239, 7241, 7243, 7245, 7247, 7249, 7251, 7253, 7255, 7257, 7259, 7261, 7263, 7265, 7267, 7269, 7271, 7273, 7275, 7277, 7279, 7281, 7283, 7285, 7287, 7289, 7291, 7293, 7295, 7297, 7299, 7301, 7303, 7305, 7307, 7309, 7311, 7313, 7315, 7317, 7319, 7321, 7323, 7325, 7327, 7329, 7331, 7333, 7335, 7337, 7339, 7341, 7343, 7345, 7347, 7349, 7351, 7353, 7355, 7357, 7359, 7361, 7363, 7365, 7367, 7369, 7371, 7373, 7375, 7377, 7379, 7381, 7383, 7385, 7387, 7389, 7391, 7393, 7395, 7397, 7399, 7401, 7403, 7405, 7407, 7409, 7411, 7413, 7415, 7417, 7419, 7421, 7423, 7425, 7427, 7429, 7431, 7433, 7435, 7437, 7439, 7441, 7443, 7445, 7447, 7449, 7451, 7453, 7455, 7457, 7459, 7461, 7463, 7465, 7467, 7469, 7471, 7473, 7475, 7477, 7479, 7481, 7483, 7485, 7487, 7489, 7491, 7493, 7495, 7497, 7499, 7501, 7503, 7505, 7507, 7509, 7511, 7513, 7515, 7517, 7519, 7521, 7523, 7525, 7527, 7529, 7531, 7533, 7535, 7537, 7539, 7541, 7543, 7545, 7547, 7549, 7551, 7553, 7555, 7557, 7559, 7561, 7563, 7565, 7567, 7569, 7571, 7573, 7575, 7577, 7579, 7581, 7583, 7585, 7587, 7589, 7591, 7593, 7595, 7597, 7599, 7601, 7603, 7605, 7607, 7609, 7611, 7613, 7615, 7617, 7619, 7621, 7623, 7625, 7627, 7629, 7631, 7633, 7635, 7637, 7639, 7641, 7643, 7645, 7647, 7649, 7651, 7653, 7655, 7657, 7659, 7661, 7663, 7665, 7667, 7669, 7671, 7673, 7675, 7677, 7679, 7681, 7683, 7685, 7687, 7689, 7691, 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, 7709, 7711, 7713, 7715, 7717, 7719, 7721, 7723, 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, 7749, 7751, 7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, 7783, 7785, 7787, 7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, 7821, 7823, 7825, 7827, 7829, 7831, 7833, 7835, 7837, 7839, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859, 7861, 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895, 7897, 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, 7927, 7929, 7931, 7933, 7935, 7937, 7939, 7941, 7943, 7945, 7947, 7949, 7951, 7953, 7955, 7957, 7959, 7961, 7963, 7965, 7967, 7969, 7971, 7973, 7975, 7977, 7979, 7981, 7983, 7985, 7987, 7989, 7991, 7993, 7995, 7997, 7999, 8001, 8003, 8005, 8007, 8009, 8011, 8013, 8015, 8017, 8019, 8021, 8023, 8025, 8027, 8029, 8031, 8033, 8035, 8037, 8039, 8041, 8043, 8045, 8047, 8049, 8051, 8053, 8055, 8057, 8059, 8061, 8063, 8065, 8067, 8069, 8071, 8073, 8075, 8077, 8079, 8081, 8083, 8085, 8087, 8089, 8091, 8093, 8095, 8097, 8099, 8101, 8103, 8105, 8107, 8109, 8111, 8113, 8115, 8117, 8119, 8121, 8123, 8125, 8127, 8129, 8131, 8133, 8135, 8137, 8139, 8141, 8143, 8145, 8147, 8149, 8151, 8153, 8155, 8157, 8159, 8161, 8163, 8165, 8167, 8169, 8171, 8173, 8175, 8177, 8179, 8181, 8183, 8185, 8187, 8189, 8191, 8193, 8195, 8197, 8199, 8201, 8203, 8205, 8207, 8209, 8211, 8213, 8215, 8217, 8219, 8221, 8223, 8225, 8227, 8229, 8231, 8233, 8235, 8237, 8239, 8241, 8243, 8245, 8247, 8249, 8251, 8253, 8255, 8257, 8259, 8261, 8263, 8265, 8267, 8269, 8271, 8273, 8275, 8277, 8279, 8281, 8283, 8285, 8287, 8289, 8291, 8293, 8295, 8297, 8299, 8301, 8303, 8305, 8307, 8309, 8311, 8313, 8315, 8317, 8319, 8321, 8323, 8325, 8327, 8329, 8331, 8333, 8335, 8337, 8339, 8341, 8343, 8345, 8347, 8349, 8351, 8353, 8355, 8357, 8359, 8361, 8363, 8365, 8367, 8369, 8371, 8373, 8375, 8377, 8379, 8381, 8383, 8385, 8387, 8389, 8391, 8393, 8395, 8397, 8399, 8401, 8403, 8405, 8407, 8409, 8411, 8413, 8415, 8417, 8419, 8421, 8423, 8425, 8427, 8429, 8431, 8433, 8435, 8437, 8439, 8441, 8443, 8445, 8447, 8449, 8451, 8453, 8455, 8457, 8459, 8461, 8463, 8465, 8467, 8469, 8471, 8473, 8475, 8477, 8479, 8481, 8483, 8485, 8487, 8489, 8491, 8493, 8495, 8497, 8499, 8501, 8503, 8505, 8507, 8509, 8511, 8513, 8515, 8517, 8519, 8521, 8523, 8525, 8527, 8529, 8531, 8533, 8535, 8537, 8539, 8541, 8543, 8545, 8547, 8549, 8551, 8553, 8555, 8557, 8559, 8561, 8563, 8565, 8567, 8569, 8571, 8573, 8575, 8577, 8579, 8581, 8583, 8585, 8587, 8589, 8591, 8593, 8595, 8597, 8599, 8601, 8603, 8605, 8607, 8609, 8611, 8613, 8613, 12, 12, 12, 16, 16, 16, 18, 36, 39, 39, 71, 113, 155, 195, 232, 265, 296, 322, 342, 363, 386, 406, 423, 437, 448, 456, 460, 462, 464, 466, 468, 470, 472, 474, 476, 478, 480, 482, 484, 486, 488, 490, 492, 494, 496, 498, 500, 502, 504, 506, 508, 510, 512, 514, 516, 518, 520, 522, 524, 526, 528, 530, 532, 534, 536, 538, 540, 542, 544, 546, 548, 550, 552, 554, 556, 558, 560, 562, 564, 566, 568, 570, 572, 574, 576, 578, 580, 582, 584, 586, 588, 590, 592, 594, 596, 598, 600, 602, 604, 606, 608, 610, 612, 614, 616, 618, 620, 622, 624, 626, 628, 630, 632, 634, 636, 638, 640, 642, 644, 646, 648, 650, 652, 654, 656, 658, 660, 662, 664, 666, 668, 670, 672, 674, 676, 678, 680, 682, 684, 686, 688, 690, 692, 694, 696, 698, 700, 702, 704, 706, 708, 710, 712, 714, 716, 718, 720, 722, 724, 726, 728, 730, 732, 734, 736, 738, 740, 742, 744, 746, 748, 750, 752, 754, 756, 758, 760, 762, 764, 766, 768, 770, 772, 774, 776, 778, 780, 782, 784, 786, 788, 790, 792, 794, 796, 798, 800, 802, 804, 806, 808, 810, 812, 814, 816, 818, 820, 822, 824, 826, 828, 830, 832, 834, 836, 838, 840, 842, 844, 846, 848, 850, 852, 854, 856, 858, 860, 862, 864, 866, 868, 870, 872, 874, 876, 878, 880, 882, 884, 886, 888, 890, 892, 894, 896, 898, 900, 902, 904, 906, 908, 910, 912, 914, 916, 918, 920, 922, 924, 926, 928, 930, 932, 934, 936, 938, 940, 942, 944, 946, 948, 950, 952, 954, 956, 958, 960, 962, 964, 966, 968, 970, 972, 974, 976, 978, 980, 982, 984, 986, 988, 990, 992, 994, 996, 998, 1000, 1002, 1004, 1006, 1008, 1010, 1012, 1014, 1016, 1018, 1020, 1022, 1024, 1026, 1028, 1030, 1032, 1034, 1036, 1038, 1040, 1042, 1044, 1046, 1048, 1050, 1052, 1054, 1056, 1058, 1060, 1062, 1064, 1066, 1068, 1070, 1072, 1074, 1076, 1078, 1080, 1082, 1084, 1086, 1088, 1090, 1092, 1094, 1096, 1098, 1100, 1102, 1104, 1106, 1108, 1110, 1112, 1114, 1116, 1118, 1120, 1122, 1124, 1126, 1128, 1130, 1132, 1134, 1136, 1138, 1140, 1142, 1144, 1146, 1148, 1150, 1152, 1154, 1156, 1158, 1160, 1162, 1164, 1166, 1168, 1170, 1172, 1174, 1176, 1178, 1180, 1182, 1184, 1186, 1188, 1190, 1192, 1194, 1196, 1198, 1200, 1202, 1204, 1206, 1208, 1210, 1212, 1214, 1216, 1218, 1220, 1222, 1224, 1226, 1228, 1230, 1232, 1234, 1236, 1238, 1240, 1242, 1244, 1246, 1248, 1250, 1252, 1254, 1256, 1258, 1260, 1262, 1264, 1266, 1268, 1270, 1272, 1274, 1276, 1278, 1280, 1282, 1284, 1286, 1288, 1290, 1292, 1294, 1296, 1298, 1300, 1302, 1304, 1306, 1308, 1310, 1312, 1314, 1316, 1318, 1320, 1322, 1324, 1326, 1328, 1330, 1332, 1334, 1336, 1338, 1340, 1342, 1344, 1346, 1348, 1350, 1352, 1354, 1356, 1358, 1360, 1362, 1364, 1366, 1368, 1370, 1372, 1374, 1376, 1378, 1380, 1382, 1384, 1386, 1388, 1390, 1392, 1394, 1396, 1398, 1400, 1402, 1404, 1406, 1408, 1410, 1412, 1414, 1416, 1418, 1420, 1422, 1424, 1426, 1428, 1430, 1432, 1434, 1436, 1438, 1440, 1442, 1444, 1446, 1448, 1450, 1452, 1454, 1456, 1458, 1460, 1462, 1464, 1466, 1468, 1470, 1472, 1474, 1476, 1478, 1480, 1482, 1484, 1486, 1488, 1490, 1492, 1494, 1496, 1498, 1500, 1502, 1504, 1506, 1508, 1510, 1512, 1514, 1516, 1518, 1520, 1522, 1524, 1526, 1528, 1530, 1532, 1534, 1536, 1538, 1540, 1542, 1544, 1546, 1548, 1550, 1552, 1554, 1556, 1558, 1560, 1562, 1564, 1566, 1568, 1570, 1572, 1574, 1576, 1578, 1580, 1582, 1584, 1586, 1588, 1590, 1592, 1594, 1596, 1598, 1600, 1602, 1604, 1606, 1608, 1610, 1612, 1614, 1616, 1618, 1620, 1622, 1624, 1626, 1628, 1630, 1632, 1634, 1636, 1638, 1640, 1642, 1644, 1646, 1648, 1650, 1652, 1654, 1656, 1658, 1660, 1662, 1664, 1666, 1668, 1670, 1672, 1674, 1676, 1678, 1680, 1682, 1684, 1686, 1688, 1690, 1692, 1694, 1696, 1698, 1700, 1702, 1704, 1706, 1708, 1710, 1712, 1714, 1716, 1718, 1720, 1722, 1724, 1726, 1728, 1730, 1732, 1734, 1736, 1738, 1740, 1742, 1744, 1746, 1748, 1750, 1752, 1754, 1756, 1758, 1760, 1762, 1764, 1766, 1768, 1770, 1772, 1774, 1776, 1778, 1780, 1782, 1784, 1786, 1788, 1790, 1792, 1794, 1796, 1798, 1800, 1802, 1804, 1806, 1808, 1810, 1812, 1814, 1816, 1818, 1820, 1822, 1824, 1826, 1828, 1830, 1832, 1834, 1836, 1838, 1840, 1842, 1844, 1846, 1848, 1850, 1852, 1854, 1856, 1858, 1860, 1862, 1864, 1866, 1868, 1870, 1872, 1874, 1876, 1878, 1880, 1882, 1884, 1886, 1888, 1890, 1892, 1894, 1896, 1898, 1900, 1902, 1904, 1906, 1908, 1910, 1912, 1914, 1916, 1918, 1920, 1922, 1924, 1926, 1928, 1930, 1932, 1934, 1936, 1938, 1940, 1942, 1944, 1946, 1948, 1950, 1952, 1954, 1956, 1958, 1960, 1962, 1964, 1966, 1968, 1970, 1972, 1974, 1976, 1978, 1980, 1982, 1984, 1986, 1988, 1990, 1992, 1994, 1996, 1998, 2000, 2002, 2004, 2006, 2008, 2010, 2012, 2014, 2016, 2018, 2020, 2022, 2024, 2026, 2028, 2030, 2032, 2034, 2036, 2038, 2040, 2042, 2044, 2046, 2048, 2050, 2052, 2054, 2056, 2058, 2060, 2062, 2064, 2066, 2068, 2070, 2072, 2074, 2076, 2078, 2080, 2082, 2084, 2086, 2088, 2090, 2092, 2094, 2096, 2098, 2100, 2102, 2104, 2106, 2108, 2110, 2112, 2114, 2116, 2118, 2120, 2122, 2124, 2126, 2128, 2130, 2132, 2134, 2136, 2138, 2140, 2142, 2144, 2146, 2148, 2150, 2152, 2154, 2156, 2158, 2160, 2162, 2164, 2166, 2168, 2170, 2172, 2174, 2176, 2178, 2180, 2182, 2184, 2186, 2188, 2190, 2192, 2194, 2196, 2198, 2200, 2202, 2204, 2206, 2208, 2210, 2212, 2214, 2216, 2218, 2220, 2222, 2224, 2226, 2228, 2230, 2232, 2234, 2236, 2238, 2240, 2242, 2244, 2246, 2248, 2250, 2252, 2254, 2256, 2258, 2260, 2262, 2264, 2266, 2268, 2270, 2272, 2274, 2276, 2278, 2280, 2282, 2284, 2286, 2288, 2290, 2292, 2294, 2296, 2298, 2300, 2302, 2304, 2306, 2308, 2310, 2312, 2314, 2316, 2318, 2320, 2322, 2324, 2326, 2328, 2330, 2332, 2334, 2336, 2338, 2340, 2342, 2344, 2346, 2348, 2350, 2352, 2354, 2356, 2358, 2360, 2362, 2364, 2366, 2368, 2370, 2372, 2374, 2376, 2378, 2380, 2382, 2384, 2386, 2388, 2390, 2392, 2394, 2396, 2398, 2400, 2402, 2404, 2406, 2408, 2410, 2412, 2414, 2416, 2418, 2420, 2422, 2424, 2426, 2428, 2430, 2432, 2434, 2436, 2438, 2440, 2442, 2444, 2446, 2448, 2450, 2452, 2454, 2456, 2458, 2460, 2462, 2464, 2466, 2468, 2470, 2472, 2474, 2476, 2478, 2480, 2482, 2484, 2486, 2488, 2490, 2492, 2494, 2496, 2498, 2500, 2502, 2504, 2506, 2508, 2510, 2512, 2514, 2516, 2518, 2520, 2522, 2524, 2526, 2528, 2530, 2532, 2534, 2536, 2538, 2540, 2542, 2544, 2546, 2548, 2550, 2552, 2554, 2556, 2558, 2560, 2562, 2564, 2566, 2568, 2570, 2572, 2574, 2576, 2578, 2580, 2582, 2584, 2586, 2588, 2590, 2592, 2594, 2596, 2598, 2600, 2602, 2604, 2606, 2608, 2610, 2612, 2614, 2616, 2618, 2620, 2622, 2624, 2626, 2628, 2630, 2632, 2634, 2636, 2638, 2640, 2642, 2644, 2646, 2648, 2650, 2652, 2654, 2656, 2658, 2660, 2662, 2664, 2666, 2668, 2670, 2672, 2674, 2676, 2678, 2680, 2682, 2684, 2686, 2688, 2690, 2692, 2694, 2696, 2698, 2700, 2702, 2704, 2706, 2708, 2710, 2712, 2714, 2716, 2718, 2720, 2722, 2724, 2726, 2728, 2730, 2732, 2734, 2736, 2738, 2740, 2742, 2744, 2746, 2748, 2750, 2752, 2754, 2756, 2758, 2760, 2762, 2764, 2766, 2768, 2770, 2772, 2774, 2776, 2778, 2780, 2782, 2784, 2786, 2788, 2790, 2792, 2794, 2796, 2798, 2800, 2802, 2804, 2806, 2808, 2810, 2812, 2814, 2816, 2818, 2820, 2822, 2824, 2826, 2828, 2830, 2832, 2834, 2836, 2838, 2840, 2842, 2844, 2846, 2848, 2850, 2852, 2854, 2856, 2858, 2860, 2862, 2864, 2866, 2868, 2870, 2872, 2874, 2876, 2878, 2880, 2882, 2884, 2886, 2888, 2890, 2892, 2894, 2896, 2898, 2900, 2902, 2904, 2906, 2908, 2910, 2912, 2914, 2916, 2918, 2920, 2922, 2924, 2926, 2928, 2930, 2932, 2934, 2936, 2938, 2940, 2942, 2944, 2946, 2948, 2950, 2952, 2954, 2956, 2958, 2960, 2962, 2964, 2966, 2968, 2970, 2972, 2974, 2976, 2978, 2980, 2982, 2984, 2986, 2988, 2990, 2992, 2994, 2996, 2998, 3000, 3002, 3004, 3006, 3008, 3010, 3012, 3014, 3016, 3018, 3020, 3022, 3024, 3026, 3028, 3030, 3032, 3034, 3036, 3038, 3040, 3042, 3044, 3046, 3048, 3050, 3052, 3054, 3056, 3058, 3060, 3062, 3064, 3066, 3068, 3070, 3072, 3074, 3076, 3078, 3080, 3082, 3084, 3086, 3088, 3090, 3092, 3094, 3096, 3098, 3100, 3102, 3104, 3106, 3108, 3110, 3112, 3114, 3116, 3118, 3120, 3122, 3124, 3126, 3128, 3130, 3132, 3134, 3136, 3138, 3140, 3142, 3144, 3146, 3148, 3150, 3152, 3154, 3156, 3158, 3160, 3162, 3164, 3166, 3168, 3170, 3172, 3174, 3176, 3178, 3180, 3182, 3184, 3186, 3188, 3190, 3192, 3194, 3196, 3198, 3200, 3202, 3204, 3206, 3208, 3210, 3212, 3214, 3216, 3218, 3220, 3222, 3224, 3226, 3228, 3230, 3232, 3234, 3236, 3238, 3240, 3242, 3244, 3246, 3248, 3250, 3252, 3254, 3256, 3258, 3260, 3262, 3264, 3266, 3268, 3270, 3272, 3274, 3276, 3278, 3280, 3282, 3284, 3286, 3288, 3290, 3292, 3294, 3296, 3298, 3300, 3302, 3304, 3306, 3308, 3310, 3312, 3314, 3316, 3318, 3320, 3322, 3324, 3326, 3328, 3330, 3332, 3334, 3336, 3338, 3340, 3342, 3344, 3346, 3348, 3350, 3352, 3354, 3356, 3358, 3360, 3362, 3364, 3366, 3368, 3370, 3372, 3374, 3376, 3378, 3380, 3382, 3384, 3386, 3388, 3390, 3392, 3394, 3396, 3398, 3400, 3402, 3404, 3406, 3408, 3410, 3412, 3414, 3416, 3418, 3420, 3422, 3424, 3426, 3428, 3430, 3432, 3434, 3436, 3438, 3440, 3442, 3444, 3446, 3448, 3450, 3452, 3454, 3456, 3458, 3460, 3462, 3464, 3466, 3468, 3470, 3472, 3474, 3476, 3478, 3480, 3482, 3484, 3486, 3488, 3490, 3492, 3494, 3496, 3498, 3500, 3502, 3504, 3506, 3508, 3510, 3512, 3514, 3516, 3518, 3520, 3522, 3524, 3526, 3528, 3530, 3532, 3534, 3536, 3538, 3540, 3542, 3544, 3546, 3548, 3550, 3552, 3554, 3556, 3558, 3560, 3562, 3564, 3566, 3568, 3570, 3572, 3574, 3576, 3578, 3580, 3582, 3584, 3586, 3588, 3590, 3592, 3594, 3596, 3598, 3600, 3602, 3604, 3606, 3608, 3610, 3612, 3614, 3616, 3618, 3620, 3622, 3624, 3626, 3628, 3630, 3632, 3634, 3636, 3638, 3640, 3642, 3644, 3646, 3648, 3650, 3652, 3654, 3656, 3658, 3660, 3662, 3664, 3666, 3668, 3670, 3672, 3674, 3676, 3678, 3680, 3682, 3684, 3686, 3688, 3690, 3692, 3694, 3696, 3698, 3700, 3702, 3704, 3706, 3708, 3710, 3712, 3714, 3716, 3718, 3720, 3722, 3724, 3726, 3728, 3730, 3732, 3734, 3736, 3738, 3740, 3742, 3744, 3746, 3748, 3750, 3752, 3754, 3756, 3758, 3760, 3762, 3764, 3766, 3768, 3770, 3772, 3774, 3776, 3778, 3780, 3782, 3784, 3786, 3788, 3790, 3792, 3794, 3796, 3798, 3800, 3802, 3804, 3806, 3808, 3810, 3812, 3814, 3816, 3818, 3820, 3822, 3824, 3826, 3828, 3830, 3832, 3834, 3836, 3838, 3840, 3842, 3844, 3846, 3848, 3850, 3852, 3854, 3856, 3858, 3860, 3862, 3864, 3866, 3868, 3870, 3872, 3874, 3876, 3878, 3880, 3882, 3884, 3886, 3888, 3890, 3892, 3894, 3896, 3898, 3900, 3902, 3904, 3906, 3908, 3910, 3912, 3914, 3916, 3918, 3920, 3922, 3924, 3926, 3928, 3930, 3932, 3934, 3936, 3938, 3940, 3942, 3944, 3946, 3948, 3950, 3952, 3954, 3956, 3958, 3960, 3962, 3964, 3966, 3968, 3970, 3972, 3974, 3976, 3978, 3980, 3982, 3984, 3986, 3988, 3990, 3992, 3994, 3996, 3998, 4000, 4002, 4004, 4006, 4008, 4010, 4012, 4014, 4016, 4018, 4020, 4022, 4024, 4026, 4028, 4030, 4032, 4034, 4036, 4038, 4040, 4042, 4044, 4046, 4048, 4050, 4052, 4054, 4056, 4058, 4060, 4062, 4064, 4066, 4068, 4070, 4072, 4074, 4076, 4078, 4080, 4082, 4084, 4086, 4088, 4090, 4092, 4094, 4096, 4098, 4100, 4102, 4104, 4106, 4108, 4110, 4112, 4114, 4116, 4118, 4120, 4122, 4124, 4126, 4128, 4130, 4132, 4134, 4136, 4138, 4140, 4142, 4144, 4146, 4148, 4150, 4152, 4154, 4156, 4158, 4160, 4162, 4164, 4166, 4168, 4170, 4172, 4174, 4176, 4178, 4180, 4182, 4184, 4186, 4188, 4190, 4192, 4194, 4196, 4198, 4200, 4202, 4204, 4206, 4208, 4210, 4212, 4214, 4216, 4218, 4220, 4222, 4224, 4226, 4228, 4230, 4232, 4234, 4236, 4238, 4240, 4242, 4244, 4246, 4248, 4250, 4252, 4254, 4256, 4258, 4260, 4262, 4264, 4266, 4268, 4270, 4272, 4274, 4276, 4278, 4280, 4282, 4284, 4286, 4288, 4290, 4292, 4294, 4296, 4298, 4300, 4302, 4304, 4306, 4308, 4310, 4312, 4314, 4316, 4318, 4320, 4322, 4324, 4326, 4328, 4330, 4332, 4334, 4336, 4338, 4340, 4342, 4344, 4346, 4348, 4350, 4352, 4354, 4356, 4358, 4360, 4362, 4364, 4366, 4368, 4370, 4372, 4374, 4376, 4378, 4380, 4382, 4384, 4386, 4388, 4390, 4392, 4394, 4396, 4398, 4400, 4402, 4404, 4406, 4408, 4410, 4412, 4414, 4416, 4418, 4420, 4422, 4424, 4426, 4428, 4430, 4432, 4434, 4436, 4438, 4440, 4442, 4444, 4446, 4448, 4450, 4452, 4454, 4456, 4458, 4460, 4462, 4464, 4466, 4468, 4470, 4472, 4474, 4476, 4478, 4480, 4482, 4484, 4486, 4488, 4490, 4492, 4494, 4496, 4498, 4500, 4502, 4504, 4506, 4508, 4510, 4512, 4514, 4516, 4518, 4520, 4522, 4524, 4526, 4528, 4530, 4532, 4534, 4536, 4538, 4540, 4542, 4544, 4546, 4548, 4550, 4552, 4554, 4556, 4558, 4560, 4562, 4564, 4566, 4568, 4570, 4572, 4574, 4576, 4578, 4580, 4582, 4584, 4586, 4588, 4590, 4592, 4594, 4596, 4598, 4600, 4602, 4604, 4606, 4608, 4610, 4612, 4614, 4616, 4618, 4620, 4622, 4624, 4626, 4628, 4630, 4632, 4634, 4636, 4638, 4640, 4642, 4644, 4646, 4648, 4650, 4652, 4654, 4656, 4658, 4660, 4662, 4664, 4666, 4668, 4670, 4672, 4674, 4676, 4678, 4680, 4682, 4684, 4686, 4688, 4690, 4692, 4694, 4696, 4698, 4700, 4702, 4704, 4706, 4708, 4710, 4712, 4714, 4716, 4718, 4720, 4722, 4724, 4726, 4728, 4730, 4732, 4734, 4736, 4738, 4740, 4742, 4744, 4746, 4748, 4750, 4752, 4754, 4756, 4758, 4760, 4762, 4764, 4766, 4768, 4770, 4772, 4774, 4776, 4778, 4780, 4782, 4784, 4786, 4788, 4790, 4792, 4794, 4796, 4798, 4800, 4802, 4804, 4806, 4808, 4810, 4812, 4814, 4816, 4818, 4820, 4822, 4824, 4826, 4828, 4830, 4832, 4834, 4836, 4838, 4840, 4842, 4844, 4846, 4848, 4850, 4852, 4854, 4856, 4858, 4860, 4862, 4864, 4866, 4868, 4870, 4872, 4874, 4876, 4878, 4880, 4882, 4884, 4886, 4888, 4890, 4892, 4894, 4896, 4898, 4900, 4902, 4904, 4906, 4908, 4910, 4912, 4914, 4916, 4918, 4920, 4922, 4924, 4926, 4928, 4930, 4932, 4934, 4936, 4938, 4940, 4942, 4944, 4946, 4948, 4950, 4952, 4954, 4956, 4958, 4960, 4962, 4964, 4966, 4968, 4970, 4972, 4974, 4976, 4978, 4980, 4982, 4984, 4986, 4988, 4990, 4992, 4994, 4996, 4998, 5000, 5002, 5004, 5006, 5008, 5010, 5012, 5014, 5016, 5018, 5020, 5022, 5024, 5026, 5028, 5030, 5032, 5034, 5036, 5038, 5040, 5042, 5044, 5046, 5048, 5050, 5052, 5054, 5056, 5058, 5060, 5062, 5064, 5066, 5068, 5070, 5072, 5074, 5076, 5078, 5080, 5082, 5084, 5086, 5088, 5090, 5092, 5094, 5096, 5098, 5100, 5102, 5104, 5106, 5108, 5110, 5112, 5114, 5116, 5118, 5120, 5122, 5124, 5126, 5128, 5130, 5132, 5134, 5136, 5138, 5140, 5142, 5144, 5146, 5148, 5150, 5152, 5154, 5156, 5158, 5160, 5162, 5164, 5166, 5168, 5170, 5172, 5174, 5176, 5178, 5180, 5182, 5184, 5186, 5188, 5190, 5192, 5194, 5196, 5198, 5200, 5202, 5204, 5206, 5208, 5210, 5212, 5214, 5216, 5218, 5220, 5222, 5224, 5226, 5228, 5230, 5232, 5234, 5236, 5238, 5240, 5242, 5244, 5246, 5248, 5250, 5252, 5254, 5256, 5258, 5260, 5262, 5264, 5266, 5268, 5270, 5272, 5274, 5276, 5278, 5280, 5282, 5284, 5286, 5288, 5290, 5292, 5294, 5296, 5298, 5300, 5302, 5304, 5306, 5308, 5310, 5312, 5314, 5316, 5318, 5320, 5322, 5324, 5326, 5328, 5330, 5332, 5334, 5336, 5338, 5340, 5342, 5344, 5346, 5348, 5350, 5352, 5354, 5356, 5358, 5360, 5362, 5364, 5366, 5368, 5370, 5372, 5374, 5376, 5378, 5380, 5382, 5384, 5386, 5388, 5390, 5392, 5394, 5396, 5398, 5400, 5402, 5404, 5406, 5408, 5410, 5412, 5414, 5416, 5418, 5420, 5422, 5424, 5426, 5428, 5430, 5432, 5434, 5436, 5438, 5440, 5442, 5444, 5446, 5448, 5450, 5452, 5454, 5456, 5458, 5460, 5462, 5464, 5466, 5468, 5470, 5472, 5474, 5476, 5478, 5480, 5482, 5484, 5486, 5488, 5490, 5492, 5494, 5496, 5498, 5500, 5502, 5504, 5506, 5508, 5510, 5512, 5514, 5516, 5518, 5520, 5522, 5524, 5526, 5528, 5530, 5532, 5534, 5536, 5538, 5540, 5542, 5544, 5546, 5548, 5550, 5552, 5554, 5556, 5558, 5560, 5562, 5564, 5566, 5568, 5570, 5572, 5574, 5576, 5578, 5580, 5582, 5584, 5586, 5588, 5590, 5592, 5594, 5596, 5598, 5600, 5602, 5604, 5606, 5608, 5610, 5612, 5614, 5616, 5618, 5620, 5622, 5624, 5626, 5628, 5630, 5632, 5634, 5636, 5638, 5640, 5642, 5644, 5646, 5648, 5650, 5652, 5654, 5656, 5658, 5660, 5662, 5664, 5666, 5668, 5670, 5672, 5674, 5676, 5678, 5680, 5682, 5684, 5686, 5688, 5690, 5692, 5694, 5696, 5698, 5700, 5702, 5704, 5706, 5708, 5710, 5712, 5714, 5716, 5718, 5720, 5722, 5724, 5726, 5728, 5730, 5732, 5734, 5736, 5738, 5740, 5742, 5744, 5746, 5748, 5750, 5752, 5754, 5756, 5758, 5760, 5762, 5764, 5766, 5768, 5770, 5772, 5774, 5776, 5778, 5780, 5782, 5784, 5786, 5788, 5790, 5792, 5794, 5796, 5798, 5800, 5802, 5804, 5806, 5808, 5810, 5812, 5814, 5816, 5818, 5820, 5822, 5824, 5826, 5828, 5830, 5832, 5834, 5836, 5838, 5840, 5842, 5844, 5846, 5848, 5850, 5852, 5854, 5856, 5858, 5860, 5862, 5864, 5866, 5868, 5870, 5872, 5874, 5876, 5878, 5880, 5882, 5884, 5886, 5888, 5890, 5892, 5894, 5896, 5898, 5900, 5902, 5904, 5906, 5908, 5910, 5912, 5914, 5916, 5918, 5920, 5922, 5924, 5926, 5928, 5930, 5932, 5934, 5936, 5938, 5940, 5942, 5944, 5946, 5948, 5950, 5952, 5954, 5956, 5958, 5960, 5962, 5964, 5966, 5968, 5970, 5972, 5974, 5976, 5978, 5980, 5982, 5984, 5986, 5988, 5990, 5992, 5994, 5996, 5998, 6000, 6002, 6004, 6006, 6008, 6010, 6012, 6014, 6016, 6018, 6020, 6022, 6024, 6026, 6028, 6030, 6032, 6034, 6036, 6038, 6040, 6042, 6044, 6046, 6048, 6050, 6052, 6054, 6056, 6058, 6060, 6062, 6064, 6066, 6068, 6070, 6072, 6074, 6076, 6078, 6080, 6082, 6084, 6086, 6088, 6090, 6092, 6094, 6096, 6098, 6100, 6102, 6104, 6106, 6108, 6110, 6112, 6114, 6116, 6118, 6120, 6122, 6124, 6126, 6128, 6130, 6132, 6134, 6136, 6138, 6140, 6142, 6144, 6146, 6148, 6150, 6152, 6154, 6156, 6158, 6160, 6162, 6164, 6166, 6168, 6170, 6172, 6174, 6176, 6178, 6180, 6182, 6184, 6186, 6188, 6190, 6192, 6194, 6196, 6198, 6200, 6202, 6204, 6206, 6208, 6210, 6212, 6214, 6216, 6218, 6220, 6222, 6224, 6226, 6228, 6230, 6232, 6234, 6236, 6238, 6240, 6242, 6244, 6246, 6248, 6250, 6252, 6254, 6256, 6258, 6260, 6262, 6264, 6266, 6268, 6270, 6272, 6274, 6276, 6278, 6280, 6282, 6284, 6286, 6288, 6290, 6292, 6294, 6296, 6298, 6300, 6302, 6304, 6306, 6308, 6310, 6312, 6314, 6316, 6318, 6320, 6322, 6324, 6326, 6328, 6330, 6332, 6334, 6336, 6338, 6340, 6342, 6344, 6346, 6348, 6350, 6352, 6354, 6356, 6358, 6360, 6362, 6364, 6366, 6368, 6370, 6372, 6374, 6376, 6378, 6380, 6382, 6384, 6386, 6388, 6390, 6392, 6394, 6396, 6398, 6400, 6402, 6404, 6406, 6408, 6410, 6412, 6414, 6416, 6418, 6420, 6422, 6424, 6426, 6428, 6430, 6432, 6434, 6436, 6438, 6440, 6442, 6444, 6446, 6448, 6450, 6452, 6454, 6456, 6458, 6460, 6462, 6464, 6466, 6468, 6470, 6472, 6474, 6476, 6478, 6480, 6482, 6484, 6486, 6488, 6490, 6492, 6494, 6496, 6498, 6500, 6502, 6504, 6506, 6508, 6510, 6512, 6514, 6516, 6518, 6520, 6522, 6524, 6526, 6528, 6530, 6532, 6534, 6536, 6538, 6540, 6542, 6544, 6546, 6548, 6550, 6552, 6554, 6556, 6558, 6560, 6562, 6564, 6566, 6568, 6570, 6572, 6574, 6576, 6578, 6580, 6582, 6584, 6586, 6588, 6590, 6592, 6594, 6596, 6598, 6600, 6602, 6604, 6606, 6608, 6610, 6612, 6614, 6616, 6618, 6620, 6622, 6624, 6626, 6628, 6630, 6632, 6634, 6636, 6638, 6640, 6642, 6644, 6646, 6648, 6650, 6652, 6654, 6656, 6658, 6660, 6662, 6664, 6666, 6668, 6670, 6672, 6674, 6676, 6678, 6680, 6682, 6684, 6686, 6688, 6690, 6692, 6694, 6696, 6698, 6700, 6702, 6704, 6706, 6708, 6710, 6712, 6714, 6716, 6718, 6720, 6722, 6724, 6726, 6728, 6730, 6732, 6734, 6736, 6738, 6740, 6742, 6744, 6746, 6748, 6750, 6752, 6754, 6756, 6758, 6760, 6762, 6764, 6766, 6768, 6770, 6772, 6774, 6776, 6778, 6780, 6782, 6784, 6786, 6788, 6790, 6792, 6794, 6796, 6798, 6800, 6802, 6804, 6806, 6808, 6810, 6812, 6814, 6816, 6818, 6820, 6822, 6824, 6826, 6828, 6830, 6832, 6834, 6836, 6838, 6840, 6842, 6844, 6846, 6848, 6850, 6852, 6854, 6856, 6858, 6860, 6862, 6864, 6866, 6868, 6870, 6872, 6874, 6876, 6878, 6880, 6882, 6884, 6886, 6888, 6890, 6892, 6894, 6896, 6898, 6900, 6902, 6904, 6906, 6908, 6910, 6912, 6914, 6916, 6918, 6920, 6922, 6924, 6926, 6928, 6930, 6932, 6934, 6936, 6938, 6940, 6942, 6944, 6946, 6948, 6950, 6952, 6954, 6956, 6958, 6960, 6962, 6964, 6966, 6968, 6970, 6972, 6974, 6976, 6978, 6980, 6982, 6984, 6986, 6988, 6990, 6992, 6994, 6996, 6998, 7000, 7002, 7004, 7006, 7008, 7010, 7012, 7014, 7016, 7018, 7020, 7022, 7024, 7026, 7028, 7030, 7032, 7034, 7036, 7038, 7040, 7042, 7044, 7046, 7048, 7050, 7052, 7054, 7056, 7058, 7060, 7062, 7064, 7066, 7068, 7070, 7072, 7074, 7076, 7078, 7080, 7082, 7084, 7086, 7088, 7090, 7092, 7094, 7096, 7098, 7100, 7102, 7104, 7106, 7108, 7110, 7112, 7114, 7116, 7118, 7120, 7122, 7124, 7126, 7128, 7130, 7132, 7134, 7136, 7138, 7140, 7142, 7144, 7146, 7148, 7150, 7152, 7154, 7156, 7158, 7160, 7162, 7164, 7166, 7168, 7170, 7172, 7174, 7176, 7178, 7180, 7182, 7184, 7186, 7188, 7190, 7192, 7194, 7196, 7198, 7200, 7202, 7204, 7206, 7208, 7210, 7212, 7214, 7216, 7218, 7220, 7222, 7224, 7226, 7228, 7230, 7232, 7234, 7236, 7238, 7240, 7242, 7244, 7246, 7248, 7250, 7252, 7254, 7256, 7258, 7260, 7262, 7264, 7266, 7268, 7270, 7272, 7274, 7276, 7278, 7280, 7282, 7284, 7286, 7288, 7290, 7292, 7294, 7296, 7298, 7300, 7302, 7304, 7306, 7308, 7310, 7312, 7314, 7316, 7318, 7320, 7322, 7324, 7326, 7328, 7330, 7332, 7334, 7336, 7338, 7340, 7342, 7344, 7346, 7348, 7350, 7352, 7354, 7356, 7358, 7360, 7362, 7364, 7366, 7368, 7370, 7372, 7374, 7376, 7378, 7380, 7382, 7384, 7386, 7388, 7390, 7392, 7394, 7396, 7398, 7400, 7402, 7404, 7406, 7408, 7410, 7412, 7414, 7416, 7418, 7420, 7422, 7424, 7426, 7428, 7430, 7432, 7434, 7436, 7438, 7440, 7442, 7444, 7446, 7448, 7450, 7452, 7454, 7456, 7458, 7460, 7462, 7464, 7466, 7468, 7470, 7472, 7474, 7476, 7478, 7480, 7482, 7484, 7486, 7488, 7490, 7492, 7494, 7496, 7498, 7500, 7502, 7504, 7506, 7508, 7510, 7512, 7514, 7516, 7518, 7520, 7522, 7524, 7526, 7528, 7530, 7532, 7534, 7536, 7538, 7540, 7542, 7544, 7546, 7548, 7550, 7552, 7554, 7556, 7558, 7560, 7562, 7564, 7566, 7568, 7570, 7572, 7574, 7576, 7578, 7580, 7582, 7584, 7586, 7588, 7590, 7592, 7594, 7596, 7598, 7600, 7602, 7604, 7606, 7608, 7610, 7612, 7614, 7616, 7618, 7620, 7622, 7624, 7626, 7628, 7630, 7632, 7634, 7636, 7638, 7640, 7642, 7644, 7646, 7648, 7650, 7652, 7654, 7656, 7658, 7660, 7662, 7664, 7666, 7668, 7670, 7672, 7674, 7676, 7678, 7680, 7682, 7684, 7686, 7688, 7690, 7692, 7694, 7696, 7698, 7700, 7702, 7704, 7706, 7708, 7710, 7712, 7714, 7716, 7718, 7720, 7722, 7724, 7726, 7728, 7730, 7732, 7734, 7736, 7738, 7740, 7742, 7744, 7746, 7748, 7750, 7752, 7754, 7756, 7758, 7760, 7762, 7764, 7766, 7768, 7770, 7772, 7774, 7776, 7778, 7780, 7782, 7784, 7786, 7788, 7790, 7792, 7794, 7796, 7798, 7800, 7802, 7804, 7806, 7808, 7810, 7812, 7814, 7816, 7818, 7820, 7822, 7824, 7826, 7828, 7830, 7832, 7834, 7836, 7838, 7840, 7842, 7844, 7846, 7848, 7850, 7852, 7854, 7856, 7858, 7860, 7862, 7864, 7866, 7868, 7870, 7872, 7874, 7876, 7878, 7880, 7882, 7884, 7886, 7888, 7890, 7892, 7894, 7896, 7898, 7900, 7902, 7904, 7906, 7908, 7910, 7912, 7914, 7916, 7918, 7920, 7922, 7924, 7926, 7928, 7930, 7932, 7934, 7936, 7938, 7940, 7942, 7944, 7946, 7948, 7950, 7952, 7954, 7956, 7958, 7960, 7962, 7964, 7966, 7968, 7970, 7972, 7974, 7976, 7978, 7980, 7982, 7984, 7986, 7988, 7990, 7992, 7994, 7996, 7998, 8000, 8002, 8004, 8006, 8008, 8010, 8012, 8014, 8016, 8018, 8020, 8022, 8024, 8026, 8028, 8030, 8032, 8034, 8036, 8038, 8040, 8042, 8044, 8046, 8048, 8050, 8052, 8054, 8056, 8058, 8060, 8062, 8064, 8066, 8068, 8070, 8072, 8074, 8076, 8078, 8080, 8082, 8084, 8086, 8088, 8090, 8092, 8094, 8096, 8098, 8100, 8102, 8104, 8106, 8108, 8110, 8112, 8114, 8116, 8118, 8120, 8122, 8124, 8126, 8128, 8130, 8132, 8134, 8136, 8138, 8140, 8142, 8144, 8146, 8148, 8150, 8152, 8154, 8156, 8158, 8160, 8162, 8164, 8166, 8168, 8170, 8172, 8174, 8176, 8178, 8180, 8182, 8184, 8186, 8188, 8190, 8192, 8194, 8196, 8198, 8200, 8202, 8204, 8206, 8208, 8210, 8212, 8214, 8216, 8218, 8220, 8222, 8224, 8226, 8228, 8230, 8232, 8234, 8236, 8238, 8240, 8242, 8244, 8246, 8248, 8250, 8252, 8254, 8256, 8258, 8260, 8262, 8264, 8266, 8268, 8270, 8272, 8274, 8276, 8278, 8280, 8282, 8284, 8286, 8288, 8290, 8292, 8294, 8296, 8298, 8300, 8302, 8304, 8306, 8308, 8310, 8312, 8314, 8316, 8318, 8320, 8322, 8324, 8326, 8328, 8330, 8332, 8334, 8336, 8338, 8340, 8342, 8344, 8346, 8348, 8350, 8352, 8354, 8356, 8358, 8360, 8362, 8364, 8366, 8368, 8370, 8372, 8374, 8376, 8378, 8380, 8382, 8384, 8386, 8388, 8390, 8392, 8394, 8396, 8398, 8400, 8402, 8404, 8406, 8408, 8410, 8412, 8414, 8416, 8418, 8420, 8422, 8424, 8426, 8428, 8430, 8432, 8434, 8436, 8438, 8440, 8442, 8444, 8446, 8448, 8450, 8452, 8454, 8456, 8458, 8460, 8462, 8464, 8466, 8468, 8470, 8472, 8474, 8476, 8478, 8480, 8482, 8484, 8486, 8488, 8490, 8492, 8494, 8496, 8498, 8500, 8502, 8504, 8506, 8508, 8510, 8512, 8514, 8516, 8518, 8520, 8522, 8524, 8526, 8528, 8530, 8532, 8534, 8536, 8538, 8540, 8542, 8544, 8546, 8548, 8550, 8552, 8554, 8556, 8558, 8560, 8562, 8564, 8566, 8568, 8570, 8572, 8574, 8576, 8578, 8580, 8582, 8584, 8586, 8588, 8590, 8592, 8594, 8596, 8598, 8600, 8602, 8604, 8606, 8608, 8610, 8612, 1239, 1237, 1235, 1233, 1231, 1229, 1227, 1225, 1223, 1221, 1219, 1217, 1215, 1213, 1211, 1209, 1207, 1205, 1203, 1201, 1199, 1197, 1195, 1193, 1191, 1189, 1187, 1185, 1183, 1181, 1179, 1177, 1175, 1173, 1171, 1169, 1167, 1165, 1163, 1161, 1159, 1157, 1155, 1153, 1151, 1149, 1147, 1145, 1143, 1141, 1139, 1137, 1135, 1133, 1131, 1129, 1127, 1125, 1123, 1121, 1119, 1117, 1115, 1113, 1111, 1109, 1107, 1105, 1103, 1101, 1099, 1097, 1095, 1093, 1091, 1089, 1087, 1085, 1083, 1081, 1079, 1077, 1075, 1073, 1071, 1069, 1067, 1065, 1063, 1061, 1059, 1057, 1055, 1053, 1051, 1049, 1047, 1045, 1043, 1041, 1039, 1037, 1035, 1033, 1031, 1029, 1027, 1025, 1023, 1021, 1019, 1017, 1015, 1013, 1011, 1009, 1007, 1005, 1003, 1001, 999, 997, 995, 993, 991, 989, 987, 985, 983, 981, 979, 977, 975, 973, 971, 969, 967, 965, 963, 961, 959, 957, 955, 953, 951, 949, 947, 945, 943, 941, 939, 937, 935, 933, 931, 929, 927, 925, 923, 921, 919, 917, 915, 913, 911, 909, 907, 905, 903, 901, 899, 897, 895, 893, 891, 889, 887, 885, 883, 881, 879, 877, 875, 873, 871, 869, 867, 865, 863, 861, 859, 857, 855, 853, 851, 849, 847, 845, 843, 841, 839, 837, 835, 833, 831, 829, 827, 825, 823, 821, 819, 817, 815, 813, 811, 809, 807, 805, 803, 801, 799, 797, 795, 793, 791, 789, 787, 785, 783, 781, 779, 777, 775, 773, 771, 769, 767, 765, 763, 761, 759, 757, 755, 753, 751, 749, 747, 745, 743, 741, 739, 737, 735, 733, 731, 729, 727, 725, 723, 721, 719, 717, 715, 713, 711, 709, 707, 705, 703, 701, 699, 697, 695, 693, 691, 689, 687, 685, 683, 681, 679, 677, 675, 673, 671, 669, 667, 665, 663, 661, 659, 657, 655, 653, 651, 649, 647, 645, 643, 641, 639, 637, 635, 633, 631, 629, 627, 625, 623, 621, 619, 617, 615, 613, 611, 609, 607, 605, 603, 601, 599, 597, 595, 593, 591, 589, 587, 585, 583, 581, 579, 577, 575, 573, 571, 569, 567, 565, 563, 561, 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, 533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 509, 507, 505, 503, 501, 499, 497, 495, 493, 491, 489, 487, 485, 483, 481, 479, 477, 475, 473, 471, 469, 467, 465, 463, 461, 459, 458, 457, 455, 454, 453, 450, 449, 447, 444, 443, 438, 436, 431, 426, 425, 424, 422, 421, 416, 409, 408, 407, 405, 398, 394, 389, 388, 387, 385, 381, 376, 371, 365, 364, 362, 357, 349, 346, 344, 343, 341, 338, 335, 331, 330, 324, 323, 321, 318, 317, 311, 304, 301, 298, 297, 295, 294, 287, 284, 280, 279, 267, 266, 264, 262, 261, 247, 245, 239, 234, 233, 231, 228, 226, 220, 208, 206, 204, 203, 197, 196, 194, 186, 184, 182, 181, 157, 156, 154, 123, 115, 114, 112, 99, 40, 40, 73, 72, 70, 40, 38, 37, 35, 21, 20, 19, 17, 8614, 7, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614 } ; static yyconst flex_int16_t yy_chk[8988] = { 0, 1, 1, 1, 1, 1239, 1241, 1, 1243, 1, 1, 1, 2, 2, 2, 2, 1245, 1247, 2, 30, 2, 2, 2, 15, 1249, 24, 15, 1251, 24, 26, 15, 27, 15, 25, 15, 15, 25, 15, 15, 28, 30, 15, 15, 15, 31, 15, 24, 43, 15, 24, 26, 15, 27, 15, 25, 15, 15, 25, 15, 15, 28, 1253, 15, 15, 15, 31, 15, 1255, 43, 15, 23, 32, 23, 32, 23, 23, 29, 23, 23, 44, 1257, 23, 23, 23, 45, 23, 29, 1259, 23, 1261, 1263, 23, 32, 23, 32, 23, 23, 29, 23, 23, 44, 33, 23, 23, 23, 45, 23, 29, 33, 23, 34, 46, 41, 33, 34, 41, 42, 47, 48, 42, 1265, 46, 33, 49, 51, 49, 52, 1267, 51, 33, 53, 34, 46, 41, 33, 34, 41, 42, 47, 48, 42, 50, 46, 54, 49, 51, 49, 52, 50, 51, 55, 53, 56, 50, 57, 58, 60, 61, 62, 63, 64, 65, 50, 66, 54, 67, 68, 1269, 74, 50, 60, 55, 75, 56, 50, 57, 58, 76, 61, 62, 63, 64, 65, 77, 66, 69, 67, 68, 69, 74, 78, 60, 79, 75, 80, 83, 82, 84, 76, 85, 86, 87, 88, 89, 77, 90, 69, 92, 93, 69, 82, 78, 94, 79, 91, 80, 83, 91, 84, 95, 85, 86, 87, 88, 89, 96, 90, 97, 92, 93, 98, 82, 101, 94, 102, 91, 103, 104, 91, 105, 95, 106, 107, 108, 109, 110, 96, 111, 97, 116, 117, 98, 118, 101, 119, 102, 120, 103, 104, 121, 105, 122, 106, 107, 108, 109, 110, 125, 111, 126, 116, 117, 127, 118, 128, 119, 129, 120, 130, 131, 121, 132, 122, 133, 134, 135, 136, 137, 125, 138, 126, 139, 140, 127, 143, 128, 145, 129, 147, 130, 131, 148, 132, 149, 133, 134, 135, 136, 137, 150, 138, 151, 139, 140, 152, 143, 153, 145, 158, 147, 159, 160, 148, 161, 149, 162, 165, 167, 169, 170, 150, 171, 151, 172, 173, 152, 174, 153, 175, 158, 176, 159, 160, 177, 161, 178, 162, 165, 167, 169, 170, 179, 171, 181, 172, 173, 183, 174, 185, 175, 187, 176, 188, 190, 177, 192, 178, 193, 198, 199, 200, 201, 179, 203, 181, 205, 207, 183, 209, 185, 210, 187, 212, 188, 190, 214, 192, 215, 193, 198, 199, 200, 201, 216, 203, 217, 205, 207, 219, 209, 221, 210, 218, 212, 222, 223, 214, 224, 215, 225, 226, 230, 235, 236, 216, 1271, 217, 238, 218, 219, 237, 221, 240, 241, 242, 222, 223, 243, 224, 244, 225, 226, 230, 235, 236, 245, 237, 249, 238, 218, 250, 251, 254, 240, 241, 242, 255, 257, 243, 258, 244, 260, 261, 259, 268, 269, 245, 237, 249, 272, 273, 250, 251, 254, 275, 276, 277, 255, 257, 259, 258, 278, 260, 261, 279, 268, 269, 282, 283, 285, 272, 273, 277, 286, 288, 275, 276, 292, 293, 299, 259, 300, 278, 302, 303, 279, 305, 309, 282, 283, 285, 310, 312, 277, 286, 288, 314, 315, 292, 293, 299, 319, 300, 320, 302, 303, 325, 305, 309, 327, 328, 332, 310, 312, 333, 334, 1273, 314, 315, 337, 338, 339, 319, 340, 320, 345, 1275, 325, 348, 349, 327, 328, 332, 350, 351, 333, 334, 335, 353, 335, 337, 338, 339, 354, 340, 335, 345, 335, 1277, 348, 349, 355, 356, 359, 350, 351, 1279, 360, 335, 353, 335, 361, 367, 368, 354, 369, 335, 370, 335, 346, 373, 346, 355, 356, 359, 374, 375, 346, 360, 346, 378, 379, 361, 367, 368, 380, 369, 382, 370, 383, 346, 373, 346, 384, 391, 392, 374, 375, 346, 393, 346, 378, 379, 395, 396, 397, 380, 400, 382, 401, 383, 402, 403, 411, 384, 391, 392, 412, 413, 414, 393, 417, 418, 419, 395, 396, 397, 420, 400, 427, 401, 428, 402, 403, 411, 429, 430, 434, 412, 413, 414, 435, 417, 418, 419, 441, 442, 1281, 420, 1283, 427, 1285, 428, 1287, 1289, 1291, 429, 430, 434, 1293, 1295, 1297, 435, 1299, 1301, 1303, 441, 442, 1305, 1307, 1309, 1311, 1313, 1315, 1317, 1319, 1321, 1323, 1325, 1327, 1329, 1331, 1333, 1335, 1337, 1339, 1341, 1343, 1345, 1347, 1349, 1351, 1353, 1355, 1357, 1359, 1361, 1363, 1365, 1367, 1369, 1371, 1373, 1375, 1377, 1379, 1381, 1383, 1385, 1387, 1389, 1391, 1393, 1395, 1397, 1399, 1401, 1403, 1405, 1407, 1409, 1411, 1413, 1415, 1417, 1419, 1421, 1423, 1425, 1427, 1429, 1431, 1433, 1435, 1437, 1439, 1441, 1443, 1445, 1447, 1449, 1451, 1453, 1455, 1457, 1459, 1461, 1463, 1465, 1467, 1469, 1471, 1473, 1475, 1477, 1479, 1481, 1483, 1485, 1487, 1489, 1491, 1493, 1495, 1497, 1499, 1501, 1503, 1505, 1507, 1509, 1511, 1513, 1515, 1517, 1519, 1521, 1523, 1525, 1527, 1529, 1531, 1533, 1535, 1537, 1539, 1541, 1543, 1545, 1547, 1549, 1551, 1553, 1555, 1557, 1559, 1561, 1563, 1565, 1567, 1569, 1571, 1573, 1575, 1577, 1579, 1581, 1583, 1585, 1587, 1589, 1591, 1593, 1595, 1597, 1599, 1601, 1603, 1605, 1607, 1609, 1611, 1613, 1615, 1617, 1619, 1621, 1623, 1625, 1627, 1629, 1631, 1633, 1635, 1637, 1639, 1641, 1643, 1645, 1647, 1649, 1651, 1653, 1655, 1657, 1659, 1661, 1663, 1665, 1667, 1669, 1671, 1673, 1675, 1677, 1679, 1681, 1683, 1685, 1687, 1689, 1691, 1693, 1695, 1697, 1699, 1701, 1703, 1705, 1707, 1709, 1711, 1713, 1715, 1717, 1719, 1721, 1723, 1725, 1727, 1729, 1731, 1733, 1735, 1737, 1739, 1741, 1743, 1745, 1747, 1749, 1751, 1753, 1755, 1757, 1759, 1761, 1763, 1765, 1767, 1769, 1771, 1773, 1775, 1777, 1779, 1781, 1783, 1785, 1787, 1789, 1791, 1793, 1795, 1797, 1799, 1801, 1803, 1805, 1807, 1809, 1811, 1813, 1815, 1817, 1819, 1821, 1823, 1825, 1827, 1829, 1831, 1833, 1835, 1837, 1839, 1841, 1843, 1845, 1847, 1849, 1851, 1853, 1855, 1857, 1859, 1861, 1863, 1865, 1867, 1869, 1871, 1873, 1875, 1877, 1879, 1881, 1883, 1885, 1887, 1889, 1891, 1893, 1895, 1897, 1899, 1901, 1903, 1905, 1907, 1909, 1911, 1913, 1915, 1917, 1919, 1921, 1923, 1925, 1927, 1929, 1931, 1933, 1935, 1937, 1939, 1941, 1943, 1945, 1947, 1949, 1951, 1953, 1955, 1957, 1959, 1961, 1963, 1965, 1967, 1969, 1971, 1973, 1975, 1977, 1979, 1981, 1983, 1985, 1987, 1989, 1991, 1993, 1995, 1997, 1999, 2001, 2003, 2005, 2007, 2009, 2011, 2013, 2015, 2017, 2019, 2021, 2023, 2025, 2027, 2029, 2031, 2033, 2035, 2037, 2039, 2041, 2043, 2045, 2047, 2049, 2051, 2053, 2055, 2057, 2059, 2061, 2063, 2065, 2067, 2069, 2071, 2073, 2075, 2077, 2079, 2081, 2083, 2085, 2087, 2089, 2091, 2093, 2095, 2097, 2099, 2101, 2103, 2105, 2107, 2109, 2111, 2113, 2115, 2117, 2119, 2121, 2123, 2125, 2127, 2129, 2131, 2133, 2135, 2137, 2139, 2141, 2143, 2145, 2147, 2149, 2151, 2153, 2155, 2157, 2159, 2161, 2163, 2165, 2167, 2169, 2171, 2173, 2175, 2177, 2179, 2181, 2183, 2185, 2187, 2189, 2191, 2193, 2195, 2197, 2199, 2201, 2203, 2205, 2207, 2209, 2211, 2213, 2215, 2217, 2219, 2221, 2223, 2225, 2227, 2229, 2231, 2233, 2235, 2237, 2239, 2241, 2243, 2245, 2247, 2249, 2251, 2253, 2255, 2257, 2259, 2261, 2263, 2265, 2267, 2269, 2271, 2273, 2275, 2277, 2279, 2281, 2283, 2285, 2287, 2289, 2291, 2293, 2295, 2297, 2299, 2301, 2303, 2305, 2307, 2309, 2311, 2313, 2315, 2317, 2319, 2321, 2323, 2325, 2327, 2329, 2331, 2333, 2335, 2337, 2339, 2341, 2343, 2345, 2347, 2349, 2351, 2353, 2355, 2357, 2359, 2361, 2363, 2365, 2367, 2369, 2371, 2373, 2375, 2377, 2379, 2381, 2383, 2385, 2387, 2389, 2391, 2393, 2395, 2397, 2399, 2401, 2403, 2405, 2407, 2409, 2411, 2413, 2415, 2417, 2419, 2421, 2423, 2425, 2427, 2429, 2431, 2433, 2435, 2437, 2439, 2441, 2443, 2445, 2447, 2449, 2451, 2453, 2455, 2457, 2459, 2461, 2463, 2465, 2467, 2469, 2471, 2473, 2475, 2477, 2479, 2481, 2483, 2485, 2487, 2489, 2491, 2493, 2495, 2497, 2499, 2501, 2503, 2505, 2507, 2509, 2511, 2513, 2515, 2517, 2519, 2521, 2523, 2525, 2527, 2529, 2531, 2533, 2535, 2537, 2539, 2541, 2543, 2545, 2547, 2549, 2551, 2553, 2555, 2557, 2559, 2561, 2563, 2565, 2567, 2569, 2571, 2573, 2575, 2577, 2579, 2581, 2583, 2585, 2587, 2589, 2591, 2593, 2595, 2597, 2599, 2601, 2603, 2605, 2607, 2609, 2611, 2613, 2615, 2617, 2619, 2621, 2623, 2625, 2627, 2629, 2631, 2633, 2635, 2637, 2639, 2641, 2643, 2645, 2647, 2649, 2651, 2653, 2655, 2657, 2659, 2661, 2663, 2665, 2667, 2669, 2671, 2673, 2675, 2677, 2679, 2681, 2683, 2685, 2687, 2689, 2691, 2693, 2695, 2697, 2699, 2701, 2703, 2705, 2707, 2709, 2711, 2713, 2715, 2717, 2719, 2721, 2723, 2725, 2727, 2729, 2731, 2733, 2735, 2737, 2739, 2741, 2743, 2745, 2747, 2749, 2751, 2753, 2755, 2757, 2759, 2761, 2763, 2765, 2767, 2769, 2771, 2773, 2775, 2777, 2779, 2781, 2783, 2785, 2787, 2789, 2791, 2793, 2795, 2797, 2799, 2801, 2803, 2805, 2807, 2809, 2811, 2813, 2815, 2817, 2819, 2821, 2823, 2825, 2827, 2829, 2831, 2833, 2835, 2837, 2839, 2841, 2843, 2845, 2847, 2849, 2851, 2853, 2855, 2857, 2859, 2861, 2863, 2865, 2867, 2869, 2871, 2873, 2875, 2877, 2879, 2881, 2883, 2885, 2887, 2889, 2891, 2893, 2895, 2897, 2899, 2901, 2903, 2905, 2907, 2909, 2911, 2913, 2915, 2917, 2919, 2921, 2923, 2925, 2927, 2929, 2931, 2933, 2935, 2937, 2939, 2941, 2943, 2945, 2947, 2949, 2951, 2953, 2955, 2957, 2959, 2961, 2963, 2965, 2967, 2969, 2971, 2973, 2975, 2977, 2979, 2981, 2983, 2985, 2987, 2989, 2991, 2993, 2995, 2997, 2999, 3001, 3003, 3005, 3007, 3009, 3011, 3013, 3015, 3017, 3019, 3021, 3023, 3025, 3027, 3029, 3031, 3033, 3035, 3037, 3039, 3041, 3043, 3045, 3047, 3049, 3051, 3053, 3055, 3057, 3059, 3061, 3063, 3065, 3067, 3069, 3071, 3073, 3075, 3077, 3079, 3081, 3083, 3085, 3087, 3089, 3091, 3093, 3095, 3097, 3099, 3101, 3103, 3105, 3107, 3109, 3111, 3113, 3115, 3117, 3119, 3121, 3123, 3125, 3127, 3129, 3131, 3133, 3135, 3137, 3139, 3141, 3143, 3145, 3147, 3149, 3151, 3153, 3155, 3157, 3159, 3161, 3163, 3165, 3167, 3169, 3171, 3173, 3175, 3177, 3179, 3181, 3183, 3185, 3187, 3189, 3191, 3193, 3195, 3197, 3199, 3201, 3203, 3205, 3207, 3209, 3211, 3213, 3215, 3217, 3219, 3221, 3223, 3225, 3227, 3229, 3231, 3233, 3235, 3237, 3239, 3241, 3243, 3245, 3247, 3249, 3251, 3253, 3255, 3257, 3259, 3261, 3263, 3265, 3267, 3269, 3271, 3273, 3275, 3277, 3279, 3281, 3283, 3285, 3287, 3289, 3291, 3293, 3295, 3297, 3299, 3301, 3303, 3305, 3307, 3309, 3311, 3313, 3315, 3317, 3319, 3321, 3323, 3325, 3327, 3329, 3331, 3333, 3335, 3337, 3339, 3341, 3343, 3345, 3347, 3349, 3351, 3353, 3355, 3357, 3359, 3361, 3363, 3365, 3367, 3369, 3371, 3373, 3375, 3377, 3379, 3381, 3383, 3385, 3387, 3389, 3391, 3393, 3395, 3397, 3399, 3401, 3403, 3405, 3407, 3409, 3411, 3413, 3415, 3417, 3419, 3421, 3423, 3425, 3427, 3429, 3431, 3433, 3435, 3437, 3439, 3441, 3443, 3445, 3447, 3449, 3451, 3453, 3455, 3457, 3459, 3461, 3463, 3465, 3467, 3469, 3471, 3473, 3475, 3477, 3479, 3481, 3483, 3485, 3487, 3489, 3491, 3493, 3495, 3497, 3499, 3501, 3503, 3505, 3507, 3509, 3511, 3513, 3515, 3517, 3519, 3521, 3523, 3525, 3527, 3529, 3531, 3533, 3535, 3537, 3539, 3541, 3543, 3545, 3547, 3549, 3551, 3553, 3555, 3557, 3559, 3561, 3563, 3565, 3567, 3569, 3571, 3573, 3575, 3577, 3579, 3581, 3583, 3585, 3587, 3589, 3591, 3593, 3595, 3597, 3599, 3601, 3603, 3605, 3607, 3609, 3611, 3613, 3615, 3617, 3619, 3621, 3623, 3625, 3627, 3629, 3631, 3633, 3635, 3637, 3639, 3641, 3643, 3645, 3647, 3649, 3651, 3653, 3655, 3657, 3659, 3661, 3663, 3665, 3667, 3669, 3671, 3673, 3675, 3677, 3679, 3681, 3683, 3685, 3687, 3689, 3691, 3693, 3695, 3697, 3699, 3701, 3703, 3705, 3707, 3709, 3711, 3713, 3715, 3717, 3719, 3721, 3723, 3725, 3727, 3729, 3731, 3733, 3735, 3737, 3739, 3741, 3743, 3745, 3747, 3749, 3751, 3753, 3755, 3757, 3759, 3761, 3763, 3765, 3767, 3769, 3771, 3773, 3775, 3777, 3779, 3781, 3783, 3785, 3787, 3789, 3791, 3793, 3795, 3797, 3799, 3801, 3803, 3805, 3807, 3809, 3811, 3813, 3815, 3817, 3819, 3821, 3823, 3825, 3827, 3829, 3831, 3833, 3835, 3837, 3839, 3841, 3843, 3845, 3847, 3849, 3851, 3853, 3855, 3857, 3859, 3861, 3863, 3865, 3867, 3869, 3871, 3873, 3875, 3877, 3879, 3881, 3883, 3885, 3887, 3889, 3891, 3893, 3895, 3897, 3899, 3901, 3903, 3905, 3907, 3909, 3911, 3913, 3915, 3917, 3919, 3921, 3923, 3925, 3927, 3929, 3931, 3933, 3935, 3937, 3939, 3941, 3943, 3945, 3947, 3949, 3951, 3953, 3955, 3957, 3959, 3961, 3963, 3965, 3967, 3969, 3971, 3973, 3975, 3977, 3979, 3981, 3983, 3985, 3987, 3989, 3991, 3993, 3995, 3997, 3999, 4001, 4003, 4005, 4007, 4009, 4011, 4013, 4015, 4017, 4019, 4021, 4023, 4025, 4027, 4029, 4031, 4033, 4035, 4037, 4039, 4041, 4043, 4045, 4047, 4049, 4051, 4053, 4055, 4057, 4059, 4061, 4063, 4065, 4067, 4069, 4071, 4073, 4075, 4077, 4079, 4081, 4083, 4085, 4087, 4089, 4091, 4093, 4095, 4097, 4099, 4101, 4103, 4105, 4107, 4109, 4111, 4113, 4115, 4117, 4119, 4121, 4123, 4125, 4127, 4129, 4131, 4133, 4135, 4137, 4139, 4141, 4143, 4145, 4147, 4149, 4151, 4153, 4155, 4157, 4159, 4161, 4163, 4165, 4167, 4169, 4171, 4173, 4175, 4177, 4179, 4181, 4183, 4185, 4187, 4189, 4191, 4193, 4195, 4197, 4199, 4201, 4203, 4205, 4207, 4209, 4211, 4213, 4215, 4217, 4219, 4221, 4223, 4225, 4227, 4229, 4231, 4233, 4235, 4237, 4239, 4241, 4243, 4245, 4247, 4249, 4251, 4253, 4255, 4257, 4259, 4261, 4263, 4265, 4267, 4269, 4271, 4273, 4275, 4277, 4279, 4281, 4283, 4285, 4287, 4289, 4291, 4293, 4295, 4297, 4299, 4301, 4303, 4305, 4307, 4309, 4311, 4313, 4315, 4317, 4319, 4321, 4323, 4325, 4327, 4329, 4331, 4333, 4335, 4337, 4339, 4341, 4343, 4345, 4347, 4349, 4351, 4353, 4355, 4357, 4359, 4361, 4363, 4365, 4367, 4369, 4371, 4373, 4375, 4377, 4379, 4381, 4383, 4385, 4387, 4389, 4391, 4393, 4395, 4397, 4399, 4401, 4403, 4405, 4407, 4409, 4411, 4413, 4415, 4417, 4419, 4421, 4423, 4425, 4427, 4429, 4431, 4433, 4435, 4437, 4439, 4441, 4443, 4445, 4447, 4449, 4451, 4453, 4455, 4457, 4459, 4461, 4463, 4465, 4467, 4469, 4471, 4473, 4475, 4477, 4479, 4481, 4483, 4485, 4487, 4489, 4491, 4493, 4495, 4497, 4499, 4501, 4503, 4505, 4507, 4509, 4511, 4513, 4515, 4517, 4519, 4521, 4523, 4525, 4527, 4529, 4531, 4533, 4535, 4537, 4539, 4541, 4543, 4545, 4547, 4549, 4551, 4553, 4555, 4557, 4559, 4561, 4563, 4565, 4567, 4569, 4571, 4573, 4575, 4577, 4579, 4581, 4583, 4585, 4587, 4589, 4591, 4593, 4595, 4597, 4599, 4601, 4603, 4605, 4607, 4609, 4611, 4613, 4615, 4617, 4619, 4621, 4623, 4625, 4627, 4629, 4631, 4633, 4635, 4637, 4639, 4641, 4643, 4645, 4647, 4649, 4651, 4653, 4655, 4657, 4659, 4661, 4663, 4665, 4667, 4669, 4671, 4673, 4675, 4677, 4679, 4681, 4683, 4685, 4687, 4689, 4691, 4693, 4695, 4697, 4699, 4701, 4703, 4705, 4707, 4709, 4711, 4713, 4715, 4717, 4719, 4721, 4723, 4725, 4727, 4729, 4731, 4733, 4735, 4737, 4739, 4741, 4743, 4745, 4747, 4749, 4751, 4753, 4755, 4757, 4759, 4761, 4763, 4765, 4767, 4769, 4771, 4773, 4775, 4777, 4779, 4781, 4783, 4785, 4787, 4789, 4791, 4793, 4795, 4797, 4799, 4801, 4803, 4805, 4807, 4809, 4811, 4813, 4815, 4817, 4819, 4821, 4823, 4825, 4827, 4829, 4831, 4833, 4835, 4837, 4839, 4841, 4843, 4845, 4847, 4849, 4851, 4853, 4855, 4857, 4859, 4861, 4863, 4865, 4867, 4869, 4871, 4873, 4875, 4877, 4879, 4881, 4883, 4885, 4887, 4889, 4891, 4893, 4895, 4897, 4899, 4901, 4903, 4905, 4907, 4909, 4911, 4913, 4915, 4917, 4919, 4921, 4923, 4925, 4927, 4929, 4931, 4933, 4935, 4937, 4939, 4941, 4943, 4945, 4947, 4949, 4951, 4953, 4955, 4957, 4959, 4961, 4963, 4965, 4967, 4969, 4971, 4973, 4975, 4977, 4979, 4981, 4983, 4985, 4987, 4989, 4991, 4993, 4995, 4997, 4999, 5001, 5003, 5005, 5007, 5009, 5011, 5013, 5015, 5017, 5019, 5021, 5023, 5025, 5027, 5029, 5031, 5033, 5035, 5037, 5039, 5041, 5043, 5045, 5047, 5049, 5051, 5053, 5055, 5057, 5059, 5061, 5063, 5065, 5067, 5069, 5071, 5073, 5075, 5077, 5079, 5081, 5083, 5085, 5087, 5089, 5091, 5093, 5095, 5097, 5099, 5101, 5103, 5105, 5107, 5109, 5111, 5113, 5115, 5117, 5119, 5121, 5123, 5125, 5127, 5129, 5131, 5133, 5135, 5137, 5139, 5141, 5143, 5145, 5147, 5149, 5151, 5153, 5155, 5157, 5159, 5161, 5163, 5165, 5167, 5169, 5171, 5173, 5175, 5177, 5179, 5181, 5183, 5185, 5187, 5189, 5191, 5193, 5195, 5197, 5199, 5201, 5203, 5205, 5207, 5209, 5211, 5213, 5215, 5217, 5219, 5221, 5223, 5225, 5227, 5229, 5231, 5233, 5235, 5237, 5239, 5241, 5243, 5245, 5247, 5249, 5251, 5253, 5255, 5257, 5259, 5261, 5263, 5265, 5267, 5269, 5271, 5273, 5275, 5277, 5279, 5281, 5283, 5285, 5287, 5289, 5291, 5293, 5295, 5297, 5299, 5301, 5303, 5305, 5307, 5309, 5311, 5313, 5315, 5317, 5319, 5321, 5323, 5325, 5327, 5329, 5331, 5333, 5335, 5337, 5339, 5341, 5343, 5345, 5347, 5349, 5351, 5353, 5355, 5357, 5359, 5361, 5363, 5365, 5367, 5369, 5371, 5373, 5375, 5377, 5379, 5381, 5383, 5385, 5387, 5389, 5391, 5393, 5395, 5397, 5399, 5401, 5403, 5405, 5407, 5409, 5411, 5413, 5415, 5417, 5419, 5421, 5423, 5425, 5427, 5429, 5431, 5433, 5435, 5437, 5439, 5441, 5443, 5445, 5447, 5449, 5451, 5453, 5455, 5457, 5459, 5461, 5463, 5465, 5467, 5469, 5471, 5473, 5475, 5477, 5479, 5481, 5483, 5485, 5487, 5489, 5491, 5493, 5495, 5497, 5499, 5501, 5503, 5505, 5507, 5509, 5511, 5513, 5515, 5517, 5519, 5521, 5523, 5525, 5527, 5529, 5531, 5533, 5535, 5537, 5539, 5541, 5543, 5545, 5547, 5549, 5551, 5553, 5555, 5557, 5559, 5561, 5563, 5565, 5567, 5569, 5571, 5573, 5575, 5577, 5579, 5581, 5583, 5585, 5587, 5589, 5591, 5593, 5595, 5597, 5599, 5601, 5603, 5605, 5607, 5609, 5611, 5613, 5615, 5617, 5619, 5621, 5623, 5625, 5627, 5629, 5631, 5633, 5635, 5637, 5639, 5641, 5643, 5645, 5647, 5649, 5651, 5653, 5655, 5657, 5659, 5661, 5663, 5665, 5667, 5669, 5671, 5673, 5675, 5677, 5679, 5681, 5683, 5685, 5687, 5689, 5691, 5693, 5695, 5697, 5699, 5701, 5703, 5705, 5707, 5709, 5711, 5713, 5715, 5717, 5719, 5721, 5723, 5725, 5727, 5729, 5731, 5733, 5735, 5737, 5739, 5741, 5743, 5745, 5747, 5749, 5751, 5753, 5755, 5757, 5759, 5761, 5763, 5765, 5767, 5769, 5771, 5773, 5775, 5777, 5779, 5781, 5783, 5785, 5787, 5789, 5791, 5793, 5795, 5797, 5799, 5801, 5803, 5805, 5807, 5809, 5811, 5813, 5815, 5817, 5819, 5821, 5823, 5825, 5827, 5829, 5831, 5833, 5835, 5837, 5839, 5841, 5843, 5845, 5847, 5849, 5851, 5853, 5855, 5857, 5859, 5861, 5863, 5865, 5867, 5869, 5871, 5873, 5875, 5877, 5879, 5881, 5883, 5885, 5887, 5889, 5891, 5893, 5895, 5897, 5899, 5901, 5903, 5905, 5907, 5909, 5911, 5913, 5915, 5917, 5919, 5921, 5923, 5925, 5927, 5929, 5931, 5933, 5935, 5937, 5939, 5941, 5943, 5945, 5947, 5949, 5951, 5953, 5955, 5957, 5959, 5961, 5963, 5965, 5967, 5969, 5971, 5973, 5975, 5977, 5979, 5981, 5983, 5985, 5987, 5989, 5991, 5993, 5995, 5997, 5999, 6001, 6003, 6005, 6007, 6009, 6011, 6013, 6015, 6017, 6019, 6021, 6023, 6025, 6027, 6029, 6031, 6033, 6035, 6037, 6039, 6041, 6043, 6045, 6047, 6049, 6051, 6053, 6055, 6057, 6059, 6061, 6063, 6065, 6067, 6069, 6071, 6073, 6075, 6077, 6079, 6081, 6083, 6085, 6087, 6089, 6091, 6093, 6095, 6097, 6099, 6101, 6103, 6105, 6107, 6109, 6111, 6113, 6115, 6117, 6119, 6121, 6123, 6125, 6127, 6129, 6131, 6133, 6135, 6137, 6139, 6141, 6143, 6145, 6147, 6149, 6151, 6153, 6155, 6157, 6159, 6161, 6163, 6165, 6167, 6169, 6171, 6173, 6175, 6177, 6179, 6181, 6183, 6185, 6187, 6189, 6191, 6193, 6195, 6197, 6199, 6201, 6203, 6205, 6207, 6209, 6211, 6213, 6215, 6217, 6219, 6221, 6223, 6225, 6227, 6229, 6231, 6233, 6235, 6237, 6239, 6241, 6243, 6245, 6247, 6249, 6251, 6253, 6255, 6257, 6259, 6261, 6263, 6265, 6267, 6269, 6271, 6273, 6275, 6277, 6279, 6281, 6283, 6285, 6287, 6289, 6291, 6293, 6295, 6297, 6299, 6301, 6303, 6305, 6307, 6309, 6311, 6313, 6315, 6317, 6319, 6321, 6323, 6325, 6327, 6329, 6331, 6333, 6335, 6337, 6339, 6341, 6343, 6345, 6347, 6349, 6351, 6353, 6355, 6357, 6359, 6361, 6363, 6365, 6367, 6369, 6371, 6373, 6375, 6377, 6379, 6381, 6383, 6385, 6387, 6389, 6391, 6393, 6395, 6397, 6399, 6401, 6403, 6405, 6407, 6409, 6411, 6413, 6415, 6417, 6419, 6421, 6423, 6425, 6427, 6429, 6431, 6433, 6435, 6437, 6439, 6441, 6443, 6445, 6447, 6449, 6451, 6453, 6455, 6457, 6459, 6461, 6463, 6465, 6467, 6469, 6471, 6473, 6475, 6477, 6479, 6481, 6483, 6485, 6487, 6489, 6491, 6493, 6495, 6497, 6499, 6501, 6503, 6505, 6507, 6509, 6511, 6513, 6515, 6517, 6519, 6521, 6523, 6525, 6527, 6529, 6531, 6533, 6535, 6537, 6539, 6541, 6543, 6545, 6547, 6549, 6551, 6553, 6555, 6557, 6559, 6561, 6563, 6565, 6567, 6569, 6571, 6573, 6575, 6577, 6579, 6581, 6583, 6585, 6587, 6589, 6591, 6593, 6595, 6597, 6599, 6601, 6603, 6605, 6607, 6609, 6611, 6613, 6615, 6617, 6619, 6621, 6623, 6625, 6627, 6629, 6631, 6633, 6635, 6637, 6639, 6641, 6643, 6645, 6647, 6649, 6651, 6653, 6655, 6657, 6659, 6661, 6663, 6665, 6667, 6669, 6671, 6673, 6675, 6677, 6679, 6681, 6683, 6685, 6687, 6689, 6691, 6693, 6695, 6697, 6699, 6701, 6703, 6705, 6707, 6709, 6711, 6713, 6715, 6717, 6719, 6721, 6723, 6725, 6727, 6729, 6731, 6733, 6735, 6737, 6739, 6741, 6743, 6745, 6747, 6749, 6751, 6753, 6755, 6757, 6759, 6761, 6763, 6765, 6767, 6769, 6771, 6773, 6775, 6777, 6779, 6781, 6783, 6785, 6787, 6789, 6791, 6793, 6795, 6797, 6799, 6801, 6803, 6805, 6807, 6809, 6811, 6813, 6815, 6817, 6819, 6821, 6823, 6825, 6827, 6829, 6831, 6833, 6835, 6837, 6839, 6841, 6843, 6845, 6847, 6849, 6851, 6853, 6855, 6857, 6859, 6861, 6863, 6865, 6867, 6869, 6871, 6873, 6875, 6877, 6879, 6881, 6883, 6885, 6887, 6889, 6891, 6893, 6895, 6897, 6899, 6901, 6903, 6905, 6907, 6909, 6911, 6913, 6915, 6917, 6919, 6921, 6923, 6925, 6927, 6929, 6931, 6933, 6935, 6937, 6939, 6941, 6943, 6945, 6947, 6949, 6951, 6953, 6955, 6957, 6959, 6961, 6963, 6965, 6967, 6969, 6971, 6973, 6975, 6977, 6979, 6981, 6983, 6985, 6987, 6989, 6991, 6993, 6995, 6997, 6999, 7001, 7003, 7005, 7007, 7009, 7011, 7013, 7015, 7017, 7019, 7021, 7023, 7025, 7027, 7029, 7031, 7033, 7035, 7037, 7039, 7041, 7043, 7045, 7047, 7049, 7051, 7053, 7055, 7057, 7059, 7061, 7063, 7065, 7067, 7069, 7071, 7073, 7075, 7077, 7079, 7081, 7083, 7085, 7087, 7089, 7091, 7093, 7095, 7097, 7099, 7101, 7103, 7105, 7107, 7109, 7111, 7113, 7115, 7117, 7119, 7121, 7123, 7125, 7127, 7129, 7131, 7133, 7135, 7137, 7139, 7141, 7143, 7145, 7147, 7149, 7151, 7153, 7155, 7157, 7159, 7161, 7163, 7165, 7167, 7169, 7171, 7173, 7175, 7177, 7179, 7181, 7183, 7185, 7187, 7189, 7191, 7193, 7195, 7197, 7199, 7201, 7203, 7205, 7207, 7209, 7211, 7213, 7215, 7217, 7219, 7221, 7223, 7225, 7227, 7229, 7231, 7233, 7235, 7237, 7239, 7241, 7243, 7245, 7247, 7249, 7251, 7253, 7255, 7257, 7259, 7261, 7263, 7265, 7267, 7269, 7271, 7273, 7275, 7277, 7279, 7281, 7283, 7285, 7287, 7289, 7291, 7293, 7295, 7297, 7299, 7301, 7303, 7305, 7307, 7309, 7311, 7313, 7315, 7317, 7319, 7321, 7323, 7325, 7327, 7329, 7331, 7333, 7335, 7337, 7339, 7341, 7343, 7345, 7347, 7349, 7351, 7353, 7355, 7357, 7359, 7361, 7363, 7365, 7367, 7369, 7371, 7373, 7375, 7377, 7379, 7381, 7383, 7385, 7387, 7389, 7391, 7393, 7395, 7397, 7399, 7401, 7403, 7405, 7407, 7409, 7411, 7413, 7415, 7417, 7419, 7421, 7423, 7425, 7427, 7429, 7431, 7433, 7435, 7437, 7439, 7441, 7443, 7445, 7447, 7449, 7451, 7453, 7455, 7457, 7459, 7461, 7463, 7465, 7467, 7469, 7471, 7473, 7475, 7477, 7479, 7481, 7483, 7485, 7487, 7489, 7491, 7493, 7495, 7497, 7499, 7501, 7503, 7505, 7507, 7509, 7511, 7513, 7515, 7517, 7519, 7521, 7523, 7525, 7527, 7529, 7531, 7533, 7535, 7537, 7539, 7541, 7543, 7545, 7547, 7549, 7551, 7553, 7555, 7557, 7559, 7561, 7563, 7565, 7567, 7569, 7571, 7573, 7575, 7577, 7579, 7581, 7583, 7585, 7587, 7589, 7591, 7593, 7595, 7597, 7599, 7601, 7603, 7605, 7607, 7609, 7611, 7613, 7615, 7617, 7619, 7621, 7623, 7625, 7627, 7629, 7631, 7633, 7635, 7637, 7639, 7641, 7643, 7645, 7647, 7649, 7651, 7653, 7655, 7657, 7659, 7661, 7663, 7665, 7667, 7669, 7671, 7673, 7675, 7677, 7679, 7681, 7683, 7685, 7687, 7689, 7691, 7693, 7695, 7697, 7699, 7701, 7703, 7705, 7707, 7709, 7711, 7713, 7715, 7717, 7719, 7721, 7723, 7725, 7727, 7729, 7731, 7733, 7735, 7737, 7739, 7741, 7743, 7745, 7747, 7749, 7751, 7753, 7755, 7757, 7759, 7761, 7763, 7765, 7767, 7769, 7771, 7773, 7775, 7777, 7779, 7781, 7783, 7785, 7787, 7789, 7791, 7793, 7795, 7797, 7799, 7801, 7803, 7805, 7807, 7809, 7811, 7813, 7815, 7817, 7819, 7821, 7823, 7825, 7827, 7829, 7831, 7833, 7835, 7837, 7839, 7841, 7843, 7845, 7847, 7849, 7851, 7853, 7855, 7857, 7859, 7861, 7863, 7865, 7867, 7869, 7871, 7873, 7875, 7877, 7879, 7881, 7883, 7885, 7887, 7889, 7891, 7893, 7895, 7897, 7899, 7901, 7903, 7905, 7907, 7909, 7911, 7913, 7915, 7917, 7919, 7921, 7923, 7925, 7927, 7929, 7931, 7933, 7935, 7937, 7939, 7941, 7943, 7945, 7947, 7949, 7951, 7953, 7955, 7957, 7959, 7961, 7963, 7965, 7967, 7969, 7971, 7973, 7975, 7977, 7979, 7981, 7983, 7985, 7987, 7989, 7991, 7993, 7995, 7997, 7999, 8001, 8003, 8005, 8007, 8009, 8011, 8013, 8015, 8017, 8019, 8021, 8023, 8025, 8027, 8029, 8031, 8033, 8035, 8037, 8039, 8041, 8043, 8045, 8047, 8049, 8051, 8053, 8055, 8057, 8059, 8061, 8063, 8065, 8067, 8069, 8071, 8073, 8075, 8077, 8079, 8081, 8083, 8085, 8087, 8089, 8091, 8093, 8095, 8097, 8099, 8101, 8103, 8105, 8107, 8109, 8111, 8113, 8115, 8117, 8119, 8121, 8123, 8125, 8127, 8129, 8131, 8133, 8135, 8137, 8139, 8141, 8143, 8145, 8147, 8149, 8151, 8153, 8155, 8157, 8159, 8161, 8163, 8165, 8167, 8169, 8171, 8173, 8175, 8177, 8179, 8181, 8183, 8185, 8187, 8189, 8191, 8193, 8195, 8197, 8199, 8201, 8203, 8205, 8207, 8209, 8211, 8213, 8215, 8217, 8219, 8221, 8223, 8225, 8227, 8229, 8231, 8233, 8235, 8237, 8239, 8241, 8243, 8245, 8247, 8249, 8251, 8253, 8255, 8257, 8259, 8261, 8263, 8265, 8267, 8269, 8271, 8273, 8275, 8277, 8279, 8281, 8283, 8285, 8287, 8289, 8291, 8293, 8295, 8297, 8299, 8301, 8303, 8305, 8307, 8309, 8311, 8313, 8315, 8317, 8319, 8321, 8323, 8325, 8327, 8329, 8331, 8333, 8335, 8337, 8339, 8341, 8343, 8345, 8347, 8349, 8351, 8353, 8355, 8357, 8359, 8361, 8363, 8365, 8367, 8369, 8371, 8373, 8375, 8377, 8379, 8381, 8383, 8385, 8387, 8389, 8391, 8393, 8395, 8397, 8399, 8401, 8403, 8405, 8407, 8409, 8411, 8413, 8415, 8417, 8419, 8421, 8423, 8425, 8427, 8429, 8431, 8433, 8435, 8437, 8439, 8441, 8443, 8445, 8447, 8449, 8451, 8453, 8455, 8457, 8459, 8461, 8463, 8465, 8467, 8469, 8471, 8473, 8475, 8477, 8479, 8481, 8483, 8485, 8487, 8489, 8491, 8493, 8495, 8497, 8499, 8501, 8503, 8505, 8507, 8509, 8511, 8513, 8515, 8517, 8519, 8521, 8523, 8525, 8527, 8529, 8531, 8533, 8535, 8537, 8539, 8541, 8543, 8545, 8547, 8549, 8551, 8553, 8555, 8557, 8559, 8561, 8563, 8565, 8567, 8569, 8571, 8573, 8575, 8577, 8579, 8581, 8583, 8585, 8587, 8589, 8591, 8593, 8595, 8597, 8599, 8601, 8603, 8605, 8607, 8609, 8611, 8613, 8615, 8615, 8615, 8616, 8616, 8616, 8617, 8618, 8619, 8619, 8620, 8621, 8622, 8623, 8624, 8625, 8626, 8627, 8628, 8629, 8630, 8631, 8632, 8633, 8634, 8635, 8636, 8637, 8638, 8639, 8640, 8641, 8642, 8643, 8644, 8645, 8646, 8647, 8648, 8649, 8650, 8651, 8652, 8653, 8654, 8655, 8656, 8657, 8658, 8659, 8660, 8661, 8662, 8663, 8664, 8665, 8666, 8667, 8668, 8669, 8670, 8671, 8672, 8673, 8674, 8675, 8676, 8677, 8678, 8679, 8680, 8681, 8682, 8683, 8684, 8685, 8686, 8687, 8688, 8689, 8690, 8691, 8692, 8693, 8694, 8695, 8696, 8697, 8698, 8699, 8700, 8701, 8702, 8703, 8704, 8705, 8706, 8707, 8708, 8709, 8710, 8711, 8712, 8713, 8714, 8715, 8716, 8717, 8718, 8719, 8720, 8721, 8722, 8723, 8724, 8725, 8726, 8727, 8728, 8729, 8730, 8731, 8732, 8733, 8734, 8735, 8736, 8737, 8738, 8739, 8740, 8741, 8742, 8743, 8744, 8745, 8746, 8747, 8748, 8749, 8750, 8751, 8752, 8753, 8754, 8755, 8756, 8757, 8758, 8759, 8760, 8761, 8762, 8763, 8764, 8765, 8766, 8767, 8768, 8769, 8770, 8771, 8772, 8773, 8774, 8775, 8776, 8777, 8778, 8779, 8780, 8781, 8782, 8783, 8784, 8785, 8786, 8787, 8788, 8789, 8790, 8791, 8792, 8793, 8794, 8795, 8796, 8797, 8798, 8799, 8800, 8801, 8802, 8803, 8804, 8805, 8806, 8807, 8808, 8809, 8810, 8811, 8812, 8813, 8814, 8815, 8816, 8817, 8818, 8819, 8820, 8821, 8822, 8823, 8824, 8825, 8826, 8827, 8828, 8829, 8830, 8831, 8832, 8833, 8834, 8835, 8836, 8837, 8838, 8839, 8840, 8841, 8842, 8843, 8844, 8845, 8846, 8847, 8848, 8849, 8850, 8851, 8852, 8853, 8854, 8855, 8856, 8857, 8858, 8859, 8860, 8861, 8862, 8863, 8864, 8865, 8866, 8867, 8868, 8869, 8870, 8871, 8872, 8873, 8874, 8875, 8876, 8877, 8878, 8879, 8880, 8881, 8882, 8883, 8884, 8885, 8886, 8887, 8888, 8889, 8890, 8891, 8892, 8893, 8894, 8895, 8896, 8897, 8898, 8899, 8900, 8901, 8902, 8903, 8904, 8905, 8906, 8907, 8908, 8909, 8910, 8911, 8912, 8913, 8914, 8915, 8916, 8917, 8918, 8919, 8920, 8921, 8922, 8923, 8924, 8925, 8926, 8927, 8928, 8929, 8930, 8931, 8932, 8933, 8934, 8935, 8936, 8937, 8938, 8939, 8940, 8941, 8942, 8943, 8944, 8945, 8946, 8947, 8948, 8949, 8950, 8951, 8952, 8953, 8954, 8955, 8956, 8957, 8958, 8959, 8960, 8961, 8962, 8963, 8964, 8965, 8966, 8967, 8968, 8969, 8970, 8971, 8972, 8973, 8974, 8975, 8976, 8977, 8978, 8979, 8980, 8981, 8982, 8983, 8984, 8985, 8986, 8987, 8988, 8989, 8990, 8991, 8992, 8993, 8994, 8995, 8996, 8997, 8998, 8999, 9000, 9001, 9002, 9003, 9004, 9005, 9006, 9007, 9008, 9009, 9010, 9011, 9012, 9013, 9014, 9015, 9016, 9017, 9018, 9019, 9020, 9021, 9022, 9023, 9024, 9025, 9026, 9027, 9028, 9029, 9030, 9031, 9032, 9033, 9034, 9035, 9036, 9037, 9038, 9039, 9040, 9041, 9042, 9043, 9044, 9045, 9046, 9047, 9048, 9049, 9050, 9051, 9052, 9053, 9054, 9055, 9056, 9057, 9058, 9059, 9060, 9061, 9062, 9063, 9064, 9065, 9066, 9067, 9068, 9069, 9070, 9071, 9072, 9073, 9074, 9075, 9076, 9077, 9078, 9079, 9080, 9081, 9082, 9083, 9084, 9085, 9086, 9087, 9088, 9089, 9090, 9091, 9092, 9093, 9094, 9095, 9096, 9097, 9098, 9099, 9100, 9101, 9102, 9103, 9104, 9105, 9106, 9107, 9108, 9109, 9110, 9111, 9112, 9113, 9114, 9115, 9116, 9117, 9118, 9119, 9120, 9121, 9122, 9123, 9124, 9125, 9126, 9127, 9128, 9129, 9130, 9131, 9132, 9133, 9134, 9135, 9136, 9137, 9138, 9139, 9140, 9141, 9142, 9143, 9144, 9145, 9146, 9147, 9148, 9149, 9150, 9151, 9152, 9153, 9154, 9155, 9156, 9157, 9158, 9159, 9160, 9161, 9162, 9163, 9164, 9165, 9166, 9167, 9168, 9169, 9170, 9171, 9172, 9173, 9174, 9175, 9176, 9177, 9178, 9179, 9180, 9181, 9182, 9183, 9184, 9185, 9186, 9187, 9188, 9189, 9190, 9191, 9192, 9193, 9194, 9195, 9196, 9197, 9198, 9199, 9200, 9201, 9202, 9203, 9204, 9205, 9206, 9207, 9208, 9209, 9210, 9211, 9212, 9213, 9214, 9215, 9216, 9217, 9218, 9219, 9220, 9221, 9222, 9223, 9224, 9225, 9226, 9227, 9228, 9229, 9230, 9231, 9232, 9233, 9234, 9235, 9236, 9237, 9238, 9239, 9240, 9241, 9242, 9243, 9244, 9245, 9246, 9247, 9248, 9249, 9250, 9251, 9252, 9253, 9254, 9255, 9256, 9257, 9258, 9259, 9260, 9261, 9262, 9263, 9264, 9265, 9266, 9267, 9268, 9269, 9270, 9271, 9272, 9273, 9274, 9275, 9276, 9277, 9278, 9279, 9280, 9281, 9282, 9283, 9284, 9285, 9286, 9287, 9288, 9289, 9290, 9291, 9292, 9293, 9294, 9295, 9296, 9297, 9298, 9299, 9300, 9301, 9302, 9303, 9304, 9305, 9306, 9307, 9308, 9309, 9310, 9311, 9312, 9313, 9314, 9315, 9316, 9317, 9318, 9319, 9320, 9321, 9322, 9323, 9324, 9325, 9326, 9327, 9328, 9329, 9330, 9331, 9332, 9333, 9334, 9335, 9336, 9337, 9338, 9339, 9340, 9341, 9342, 9343, 9344, 9345, 9346, 9347, 9348, 9349, 9350, 9351, 9352, 9353, 9354, 9355, 9356, 9357, 9358, 9359, 9360, 9361, 9362, 9363, 9364, 9365, 9366, 9367, 9368, 9369, 9370, 9371, 9372, 9373, 9374, 9375, 9376, 9377, 9378, 9379, 9380, 9381, 9382, 9383, 9384, 9385, 9386, 9387, 9388, 9389, 9390, 9391, 9392, 9393, 9394, 9395, 9396, 9397, 9398, 9399, 9400, 9401, 9402, 9403, 9404, 9405, 9406, 9407, 9408, 9409, 9410, 9411, 9412, 9413, 9414, 9415, 9416, 9417, 9418, 9419, 9420, 9421, 9422, 9423, 9424, 9425, 9426, 9427, 9428, 9429, 9430, 9431, 9432, 9433, 9434, 9435, 9436, 9437, 9438, 9439, 9440, 9441, 9442, 9443, 9444, 9445, 9446, 9447, 9448, 9449, 9450, 9451, 9452, 9453, 9454, 9455, 9456, 9457, 9458, 9459, 9460, 9461, 9462, 9463, 9464, 9465, 9466, 9467, 9468, 9469, 9470, 9471, 9472, 9473, 9474, 9475, 9476, 9477, 9478, 9479, 9480, 9481, 9482, 9483, 9484, 9485, 9486, 9487, 9488, 9489, 9490, 9491, 9492, 9493, 9494, 9495, 9496, 9497, 9498, 9499, 9500, 9501, 9502, 9503, 9504, 9505, 9506, 9507, 9508, 9509, 9510, 9511, 9512, 9513, 9514, 9515, 9516, 9517, 9518, 9519, 9520, 9521, 9522, 9523, 9524, 9525, 9526, 9527, 9528, 9529, 9530, 9531, 9532, 9533, 9534, 9535, 9536, 9537, 9538, 9539, 9540, 9541, 9542, 9543, 9544, 9545, 9546, 9547, 9548, 9549, 9550, 9551, 9552, 9553, 9554, 9555, 9556, 9557, 9558, 9559, 9560, 9561, 9562, 9563, 9564, 9565, 9566, 9567, 9568, 9569, 9570, 9571, 9572, 9573, 9574, 9575, 9576, 9577, 9578, 9579, 9580, 9581, 9582, 9583, 9584, 9585, 9586, 9587, 9588, 9589, 9590, 9591, 9592, 9593, 9594, 9595, 9596, 9597, 9598, 9599, 9600, 9601, 9602, 9603, 9604, 9605, 9606, 9607, 9608, 9609, 9610, 9611, 9612, 9613, 9614, 9615, 9616, 9617, 9618, 9619, 9620, 9621, 9622, 9623, 9624, 9625, 9626, 9627, 9628, 9629, 9630, 9631, 9632, 9633, 9634, 9635, 9636, 9637, 9638, 9639, 9640, 9641, 9642, 9643, 9644, 9645, 9646, 9647, 9648, 9649, 9650, 9651, 9652, 9653, 9654, 9655, 9656, 9657, 9658, 9659, 9660, 9661, 9662, 9663, 9664, 9665, 9666, 9667, 9668, 9669, 9670, 9671, 9672, 9673, 9674, 9675, 9676, 9677, 9678, 9679, 9680, 9681, 9682, 9683, 9684, 9685, 9686, 9687, 9688, 9689, 9690, 9691, 9692, 9693, 9694, 9695, 9696, 9697, 9698, 9699, 9700, 9701, 9702, 9703, 9704, 9705, 9706, 9707, 9708, 9709, 9710, 9711, 9712, 9713, 9714, 9715, 9716, 9717, 9718, 9719, 9720, 9721, 9722, 9723, 9724, 9725, 9726, 9727, 9728, 9729, 9730, 9731, 9732, 9733, 9734, 9735, 9736, 9737, 9738, 9739, 9740, 9741, 9742, 9743, 9744, 9745, 9746, 9747, 9748, 9749, 9750, 9751, 9752, 9753, 9754, 9755, 9756, 9757, 9758, 9759, 9760, 9761, 9762, 9763, 9764, 9765, 9766, 9767, 9768, 9769, 9770, 9771, 9772, 9773, 9774, 9775, 9776, 9777, 9778, 9779, 9780, 9781, 9782, 9783, 9784, 9785, 9786, 9787, 9788, 9789, 9790, 9791, 9792, 9793, 9794, 9795, 9796, 9797, 9798, 9799, 9800, 9801, 9802, 9803, 9804, 9805, 9806, 9807, 9808, 9809, 9810, 9811, 9812, 9813, 9814, 9815, 9816, 9817, 9818, 9819, 9820, 9821, 9822, 9823, 9824, 9825, 9826, 9827, 9828, 9829, 9830, 9831, 9832, 9833, 9834, 9835, 9836, 9837, 9838, 9839, 9840, 9841, 9842, 9843, 9844, 9845, 9846, 9847, 9848, 9849, 9850, 9851, 9852, 9853, 9854, 9855, 9856, 9857, 9858, 9859, 9860, 9861, 9862, 9863, 9864, 9865, 9866, 9867, 9868, 9869, 9870, 9871, 9872, 9873, 9874, 9875, 9876, 9877, 9878, 9879, 9880, 9881, 9882, 9883, 9884, 9885, 9886, 9887, 9888, 9889, 9890, 9891, 9892, 9893, 9894, 9895, 9896, 9897, 9898, 9899, 9900, 9901, 9902, 9903, 9904, 9905, 9906, 9907, 9908, 9909, 9910, 9911, 9912, 9913, 9914, 9915, 9916, 9917, 9918, 9919, 9920, 9921, 9922, 9923, 9924, 9925, 9926, 9927, 9928, 9929, 9930, 9931, 9932, 9933, 9934, 9935, 9936, 9937, 9938, 9939, 9940, 9941, 9942, 9943, 9944, 9945, 9946, 9947, 9948, 9949, 9950, 9951, 9952, 9953, 9954, 9955, 9956, 9957, 9958, 9959, 9960, 9961, 9962, 9963, 9964, 9965, 9966, 9967, 9968, 9969, 9970, 9971, 9972, 9973, 9974, 9975, 9976, 9977, 9978, 9979, 9980, 9981, 9982, 9983, 9984, 9985, 9986, 9987, 9988, 9989, 9990, 9991, 9992, 9993, 9994, 9995, 9996, 9997, 9998, 9999,10000,10001,10002,10003,10004,10005,10006,10007, 10008,10009,10010,10011,10012,10013,10014,10015,10016,10017, 10018,10019,10020,10021,10022,10023,10024,10025,10026,10027, 10028,10029,10030,10031,10032,10033,10034,10035,10036,10037, 10038,10039,10040,10041,10042,10043,10044,10045,10046,10047, 10048,10049,10050,10051,10052,10053,10054,10055,10056,10057, 10058,10059,10060,10061,10062,10063,10064,10065,10066,10067, 10068,10069,10070,10071,10072,10073,10074,10075,10076,10077, 10078,10079,10080,10081,10082,10083,10084,10085,10086,10087, 10088,10089,10090,10091,10092,10093,10094,10095,10096,10097, 10098,10099,10100,10101,10102,10103,10104,10105,10106,10107, 10108,10109,10110,10111,10112,10113,10114,10115,10116,10117, 10118,10119,10120,10121,10122,10123,10124,10125,10126,10127, 10128,10129,10130,10131,10132,10133,10134,10135,10136,10137, 10138,10139,10140,10141,10142,10143,10144,10145,10146,10147, 10148,10149,10150,10151,10152,10153,10154,10155,10156,10157, 10158,10159,10160,10161,10162,10163,10164,10165,10166,10167, 10168,10169,10170,10171,10172,10173,10174,10175,10176,10177, 10178,10179,10180,10181,10182,10183,10184,10185,10186,10187, 10188,10189,10190,10191,10192,10193,10194,10195,10196,10197, 10198,10199,10200,10201,10202,10203,10204,10205,10206,10207, 10208,10209,10210,10211,10212,10213,10214,10215,10216,10217, 10218,10219,10220,10221,10222,10223,10224,10225,10226,10227, 10228,10229,10230,10231,10232,10233,10234,10235,10236,10237, 10238,10239,10240,10241,10242,10243,10244,10245,10246,10247, 10248,10249,10250,10251,10252,10253,10254,10255,10256,10257, 10258,10259,10260,10261,10262,10263,10264,10265,10266,10267, 10268,10269,10270,10271,10272,10273,10274,10275,10276,10277, 10278,10279,10280,10281,10282,10283,10284,10285,10286,10287, 10288,10289,10290,10291,10292,10293,10294,10295,10296,10297, 10298,10299,10300,10301,10302,10303,10304,10305,10306,10307, 10308,10309,10310,10311,10312,10313,10314,10315,10316,10317, 10318,10319,10320,10321,10322,10323,10324,10325,10326,10327, 10328,10329,10330,10331,10332,10333,10334,10335,10336,10337, 10338,10339,10340,10341,10342,10343,10344,10345,10346,10347, 10348,10349,10350,10351,10352,10353,10354,10355,10356,10357, 10358,10359,10360,10361,10362,10363,10364,10365,10366,10367, 10368,10369,10370,10371,10372,10373,10374,10375,10376,10377, 10378,10379,10380,10381,10382,10383,10384,10385,10386,10387, 10388,10389,10390,10391,10392,10393,10394,10395,10396,10397, 10398,10399,10400,10401,10402,10403,10404,10405,10406,10407, 10408,10409,10410,10411,10412,10413,10414,10415,10416,10417, 10418,10419,10420,10421,10422,10423,10424,10425,10426,10427, 10428,10429,10430,10431,10432,10433,10434,10435,10436,10437, 10438,10439,10440,10441,10442,10443,10444,10445,10446,10447, 10448,10449,10450,10451,10452,10453,10454,10455,10456,10457, 10458,10459,10460,10461,10462,10463,10464,10465,10466,10467, 10468,10469,10470,10471,10472,10473,10474,10475,10476,10477, 10478,10479,10480,10481,10482,10483,10484,10485,10486,10487, 10488,10489,10490,10491,10492,10493,10494,10495,10496,10497, 10498,10499,10500,10501,10502,10503,10504,10505,10506,10507, 10508,10509,10510,10511,10512,10513,10514,10515,10516,10517, 10518,10519,10520,10521,10522,10523,10524,10525,10526,10527, 10528,10529,10530,10531,10532,10533,10534,10535,10536,10537, 10538,10539,10540,10541,10542,10543,10544,10545,10546,10547, 10548,10549,10550,10551,10552,10553,10554,10555,10556,10557, 10558,10559,10560,10561,10562,10563,10564,10565,10566,10567, 10568,10569,10570,10571,10572,10573,10574,10575,10576,10577, 10578,10579,10580,10581,10582,10583,10584,10585,10586,10587, 10588,10589,10590,10591,10592,10593,10594,10595,10596,10597, 10598,10599,10600,10601,10602,10603,10604,10605,10606,10607, 10608,10609,10610,10611,10612,10613,10614,10615,10616,10617, 10618,10619,10620,10621,10622,10623,10624,10625,10626,10627, 10628,10629,10630,10631,10632,10633,10634,10635,10636,10637, 10638,10639,10640,10641,10642,10643,10644,10645,10646,10647, 10648,10649,10650,10651,10652,10653,10654,10655,10656,10657, 10658,10659,10660,10661,10662,10663,10664,10665,10666,10667, 10668,10669,10670,10671,10672,10673,10674,10675,10676,10677, 10678,10679,10680,10681,10682,10683,10684,10685,10686,10687, 10688,10689,10690,10691,10692,10693,10694,10695,10696,10697, 10698,10699,10700,10701,10702,10703,10704,10705,10706,10707, 10708,10709,10710,10711,10712,10713,10714,10715,10716,10717, 10718,10719,10720,10721,10722,10723,10724,10725,10726,10727, 10728,10729,10730,10731,10732,10733,10734,10735,10736,10737, 10738,10739,10740,10741,10742,10743,10744,10745,10746,10747, 10748,10749,10750,10751,10752,10753,10754,10755,10756,10757, 10758,10759,10760,10761,10762,10763,10764,10765,10766,10767, 10768,10769,10770,10771,10772,10773,10774,10775,10776,10777, 10778,10779,10780,10781,10782,10783,10784,10785,10786,10787, 10788,10789,10790,10791,10792,10793,10794,10795,10796,10797, 10798,10799,10800,10801,10802,10803,10804,10805,10806,10807, 10808,10809,10810,10811,10812,10813,10814,10815,10816,10817, 10818,10819,10820,10821,10822,10823,10824,10825,10826,10827, 10828,10829,10830,10831,10832,10833,10834,10835,10836,10837, 10838,10839,10840,10841,10842,10843,10844,10845,10846,10847, 10848,10849,10850,10851,10852,10853,10854,10855,10856,10857, 10858,10859,10860,10861,10862,10863,10864,10865,10866,10867, 10868,10869,10870,10871,10872,10873,10874,10875,10876,10877, 10878,10879,10880,10881,10882,10883,10884,10885,10886,10887, 10888,10889,10890,10891,10892,10893,10894,10895,10896,10897, 10898,10899,10900,10901,10902,10903,10904,10905,10906,10907, 10908,10909,10910,10911,10912,10913,10914,10915,10916,10917, 10918,10919,10920,10921,10922,10923,10924,10925,10926,10927, 10928,10929,10930,10931,10932,10933,10934,10935,10936,10937, 10938,10939,10940,10941,10942,10943,10944,10945,10946,10947, 10948,10949,10950,10951,10952,10953,10954,10955,10956,10957, 10958,10959,10960,10961,10962,10963,10964,10965,10966,10967, 10968,10969,10970,10971,10972,10973,10974,10975,10976,10977, 10978,10979,10980,10981,10982,10983,10984,10985,10986,10987, 10988,10989,10990,10991,10992,10993,10994,10995,10996,10997, 10998,10999,11000,11001,11002,11003,11004,11005,11006,11007, 11008,11009,11010,11011,11012,11013,11014,11015,11016,11017, 11018,11019,11020,11021,11022,11023,11024,11025,11026,11027, 11028,11029,11030,11031,11032,11033,11034,11035,11036,11037, 11038,11039,11040,11041,11042,11043,11044,11045,11046,11047, 11048,11049,11050,11051,11052,11053,11054,11055,11056,11057, 11058,11059,11060,11061,11062,11063,11064,11065,11066,11067, 11068,11069,11070,11071,11072,11073,11074,11075,11076,11077, 11078,11079,11080,11081,11082,11083,11084,11085,11086,11087, 11088,11089,11090,11091,11092,11093,11094,11095,11096,11097, 11098,11099,11100,11101,11102,11103,11104,11105,11106,11107, 11108,11109,11110,11111,11112,11113,11114,11115,11116,11117, 11118,11119,11120,11121,11122,11123,11124,11125,11126,11127, 11128,11129,11130,11131,11132,11133,11134,11135,11136,11137, 11138,11139,11140,11141,11142,11143,11144,11145,11146,11147, 11148,11149,11150,11151,11152,11153,11154,11155,11156,11157, 11158,11159,11160,11161,11162,11163,11164,11165,11166,11167, 11168,11169,11170,11171,11172,11173,11174,11175,11176,11177, 11178,11179,11180,11181,11182,11183,11184,11185,11186,11187, 11188,11189,11190,11191,11192,11193,11194,11195,11196,11197, 11198,11199,11200,11201,11202,11203,11204,11205,11206,11207, 11208,11209,11210,11211,11212,11213,11214,11215,11216,11217, 11218,11219,11220,11221,11222,11223,11224,11225,11226,11227, 11228,11229,11230,11231,11232,11233,11234,11235,11236,11237, 11238,11239,11240,11241,11242,11243,11244,11245,11246,11247, 11248,11249,11250,11251,11252,11253,11254,11255,11256,11257, 11258,11259,11260,11261,11262,11263,11264,11265,11266,11267, 11268,11269,11270,11271,11272,11273,11274,11275,11276,11277, 11278,11279,11280,11281,11282,11283,11284,11285,11286,11287, 11288,11289,11290,11291,11292,11293,11294,11295,11296,11297, 11298,11299,11300,11301,11302,11303,11304,11305,11306,11307, 11308,11309,11310,11311,11312,11313,11314,11315,11316,11317, 11318,11319,11320,11321,11322,11323,11324,11325,11326,11327, 11328,11329,11330,11331,11332,11333,11334,11335,11336,11337, 11338,11339,11340,11341,11342,11343,11344,11345,11346,11347, 11348,11349,11350,11351,11352,11353,11354,11355,11356,11357, 11358,11359,11360,11361,11362,11363,11364,11365,11366,11367, 11368,11369,11370,11371,11372,11373,11374,11375,11376,11377, 11378,11379,11380,11381,11382,11383,11384,11385,11386,11387, 11388,11389,11390,11391,11392,11393,11394,11395,11396,11397, 11398,11399,11400,11401,11402,11403,11404,11405,11406,11407, 11408,11409,11410,11411,11412,11413,11414,11415,11416,11417, 11418,11419,11420,11421,11422,11423,11424,11425,11426,11427, 11428,11429,11430,11431,11432,11433,11434,11435,11436,11437, 11438,11439,11440,11441,11442,11443,11444,11445,11446,11447, 11448,11449,11450,11451,11452,11453,11454,11455,11456,11457, 11458,11459,11460,11461,11462,11463,11464,11465,11466,11467, 11468,11469,11470,11471,11472,11473,11474,11475,11476,11477, 11478,11479,11480,11481,11482,11483,11484,11485,11486,11487, 11488,11489,11490,11491,11492,11493,11494,11495,11496,11497, 11498,11499,11500,11501,11502,11503,11504,11505,11506,11507, 11508,11509,11510,11511,11512,11513,11514,11515,11516,11517, 11518,11519,11520,11521,11522,11523,11524,11525,11526,11527, 11528,11529,11530,11531,11532,11533,11534,11535,11536,11537, 11538,11539,11540,11541,11542,11543,11544,11545,11546,11547, 11548,11549,11550,11551,11552,11553,11554,11555,11556,11557, 11558,11559,11560,11561,11562,11563,11564,11565,11566,11567, 11568,11569,11570,11571,11572,11573,11574,11575,11576,11577, 11578,11579,11580,11581,11582,11583,11584,11585,11586,11587, 11588,11589,11590,11591,11592,11593,11594,11595,11596,11597, 11598,11599,11600,11601,11602,11603,11604,11605,11606,11607, 11608,11609,11610,11611,11612,11613,11614,11615,11616,11617, 11618,11619,11620,11621,11622,11623,11624,11625,11626,11627, 11628,11629,11630,11631,11632,11633,11634,11635,11636,11637, 11638,11639,11640,11641,11642,11643,11644,11645,11646,11647, 11648,11649,11650,11651,11652,11653,11654,11655,11656,11657, 11658,11659,11660,11661,11662,11663,11664,11665,11666,11667, 11668,11669,11670,11671,11672,11673,11674,11675,11676,11677, 11678,11679,11680,11681,11682,11683,11684,11685,11686,11687, 11688,11689,11690,11691,11692,11693,11694,11695,11696,11697, 11698,11699,11700,11701,11702,11703,11704,11705,11706,11707, 11708,11709,11710,11711,11712,11713,11714,11715,11716,11717, 11718,11719,11720,11721,11722,11723,11724,11725,11726,11727, 11728,11729,11730,11731,11732,11733,11734,11735,11736,11737, 11738,11739,11740,11741,11742,11743,11744,11745,11746,11747, 11748,11749,11750,11751,11752,11753,11754,11755,11756,11757, 11758,11759,11760,11761,11762,11763,11764,11765,11766,11767, 11768,11769,11770,11771,11772,11773,11774,11775,11776,11777, 11778,11779,11780,11781,11782,11783,11784,11785,11786,11787, 11788,11789,11790,11791,11792,11793,11794,11795,11796,11797, 11798,11799,11800,11801,11802,11803,11804,11805,11806,11807, 11808,11809,11810,11811,11812,11813,11814,11815,11816,11817, 11818,11819,11820,11821,11822,11823,11824,11825,11826,11827, 11828,11829,11830,11831,11832,11833,11834,11835,11836,11837, 11838,11839,11840,11841,11842,11843,11844,11845,11846,11847, 11848,11849,11850,11851,11852,11853,11854,11855,11856,11857, 11858,11859,11860,11861,11862,11863,11864,11865,11866,11867, 11868,11869,11870,11871,11872,11873,11874,11875,11876,11877, 11878,11879,11880,11881,11882,11883,11884,11885,11886,11887, 11888,11889,11890,11891,11892,11893,11894,11895,11896,11897, 11898,11899,11900,11901,11902,11903,11904,11905,11906,11907, 11908,11909,11910,11911,11912,11913,11914,11915,11916,11917, 11918,11919,11920,11921,11922,11923,11924,11925,11926,11927, 11928,11929,11930,11931,11932,11933,11934,11935,11936,11937, 11938,11939,11940,11941,11942,11943,11944,11945,11946,11947, 11948,11949,11950,11951,11952,11953,11954,11955,11956,11957, 11958,11959,11960,11961,11962,11963,11964,11965,11966,11967, 11968,11969,11970,11971,11972,11973,11974,11975,11976,11977, 11978,11979,11980,11981,11982,11983,11984,11985,11986,11987, 11988,11989,11990,11991,11992,11993,11994,11995,11996,11997, 11998,11999,12000,12001,12002,12003,12004,12005,12006,12007, 12008,12009,12010,12011,12012,12013,12014,12015,12016,12017, 12018,12019,12020,12021,12022,12023,12024,12025,12026,12027, 12028,12029,12030,12031,12032,12033,12034,12035,12036,12037, 12038,12039,12040,12041,12042,12043,12044,12045,12046,12047, 12048,12049,12050,12051,12052,12053,12054,12055,12056,12057, 12058,12059,12060,12061,12062,12063,12064,12065,12066,12067, 12068,12069,12070,12071,12072,12073,12074,12075,12076,12077, 12078,12079,12080,12081,12082,12083,12084,12085,12086,12087, 12088,12089,12090,12091,12092,12093,12094,12095,12096,12097, 12098,12099,12100,12101,12102,12103,12104,12105,12106,12107, 12108,12109,12110,12111,12112,12113,12114,12115,12116,12117, 12118,12119,12120,12121,12122,12123,12124,12125,12126,12127, 12128,12129,12130,12131,12132,12133,12134,12135,12136,12137, 12138,12139,12140,12141,12142,12143,12144,12145,12146,12147, 12148,12149,12150,12151,12152,12153,12154,12155,12156,12157, 12158,12159,12160,12161,12162,12163,12164,12165,12166,12167, 12168,12169,12170,12171,12172,12173,12174,12175,12176,12177, 12178,12179,12180,12181,12182,12183,12184,12185,12186,12187, 12188,12189,12190,12191,12192,12193,12194,12195,12196,12197, 12198,12199,12200,12201,12202,12203,12204,12205,12206,12207, 12208,12209,12210,12211,12212,12213,12214,12215,12216,12217, 12218,12219,12220,12221,12222,12223,12224,12225,12226,12227, 12228,12229,12230,12231,12232,12233,12234,12235,12236,12237, 12238,12239,12240,12241,12242,12243,12244,12245,12246,12247, 12248,12249,12250,12251,12252,12253,12254,12255,12256,12257, 12258,12259,12260,12261,12262,12263,12264,12265,12266,12267, 12268,12269,12270,12271,12272,12273,12274,12275,12276,12277, 12278,12279,12280,12281,12282,12283,12284,12285,12286,12287, 12288,12289,12290,12291,12292,12293,12294,12295,12296,12297, 12298,12299,12300,12301,12302,12303,12304,12305,12306,12307, 12308,12309,12310,12311,12312,12313,12314,12315,12316,12317, 12318,12319,12320,12321,12322,12323,12324,12325,12326,12327, 12328,12329,12330,12331,12332,12333,12334,12335,12336,12337, 12338,12339,12340,12341,12342,12343,12344,12345,12346,12347, 12348,12349,12350,12351,12352,12353,12354,12355,12356,12357, 12358,12359,12360,12361,12362,12363,12364,12365,12366,12367, 12368,12369,12370,12371,12372,12373,12374,12375,12376,12377, 12378,12379,12380,12381,12382,12383,12384,12385,12386,12387, 12388,12389,12390,12391,12392,12393,12394,12395,12396,12397, 12398,12399,12400,12401,12402,12403,12404,12405,12406,12407, 12408,12409,12410,12411,12412,12413,12414,12415,12416,12417, 12418,12419,12420,12421,12422,12423,12424,12425,12426,12427, 12428,12429,12430,12431,12432,12433,12434,12435,12436,12437, 12438,12439,12440,12441,12442,12443,12444,12445,12446,12447, 12448,12449,12450,12451,12452,12453,12454,12455,12456,12457, 12458,12459,12460,12461,12462,12463,12464,12465,12466,12467, 12468,12469,12470,12471,12472,12473,12474,12475,12476,12477, 12478,12479,12480,12481,12482,12483,12484,12485,12486,12487, 12488,12489,12490,12491,12492,12493,12494,12495,12496,12497, 12498,12499,12500,12501,12502,12503,12504,12505,12506,12507, 12508,12509,12510,12511,12512,12513,12514,12515,12516,12517, 12518,12519,12520,12521,12522,12523,12524,12525,12526,12527, 12528,12529,12530,12531,12532,12533,12534,12535,12536,12537, 12538,12539,12540,12541,12542,12543,12544,12545,12546,12547, 12548,12549,12550,12551,12552,12553,12554,12555,12556,12557, 12558,12559,12560,12561,12562,12563,12564,12565,12566,12567, 12568,12569,12570,12571,12572,12573,12574,12575,12576,12577, 12578,12579,12580,12581,12582,12583,12584,12585,12586,12587, 12588,12589,12590,12591,12592,12593,12594,12595,12596,12597, 12598,12599,12600,12601,12602,12603,12604,12605,12606,12607, 12608,12609,12610,12611,12612,12613,12614,12615,12616,12617, 12618,12619,12620,12621,12622,12623,12624,12625,12626,12627, 12628,12629,12630,12631,12632,12633,12634,12635,12636,12637, 12638,12639,12640,12641,12642,12643,12644,12645,12646,12647, 12648,12649,12650,12651,12652,12653,12654,12655,12656,12657, 12658,12659,12660,12661,12662,12663,12664,12665,12666,12667, 12668,12669,12670,12671,12672,12673,12674,12675,12676,12677, 12678,12679,12680,12681,12682,12683,12684,12685,12686,12687, 12688,12689,12690,12691,12692,12693,12694,12695,12696,12697, 12698,12699,12700,12701,12702,12703,12704,12705,12706,12707, 12708,12709,12710,12711,12712, 1237, 1235, 1233, 1231, 1229, 1227, 1225, 1223, 1221, 1219, 1217, 1215, 1213, 1211, 1209, 1207, 1205, 1203, 1201, 1199, 1197, 1195, 1193, 1191, 1189, 1187, 1185, 1183, 1181, 1179, 1177, 1175, 1173, 1171, 1169, 1167, 1165, 1163, 1161, 1159, 1157, 1155, 1153, 1151, 1149, 1147, 1145, 1143, 1141, 1139, 1137, 1135, 1133, 1131, 1129, 1127, 1125, 1123, 1121, 1119, 1117, 1115, 1113, 1111, 1109, 1107, 1105, 1103, 1101, 1099, 1097, 1095, 1093, 1091, 1089, 1087, 1085, 1083, 1081, 1079, 1077, 1075, 1073, 1071, 1069, 1067, 1065, 1063, 1061, 1059, 1057, 1055, 1053, 1051, 1049, 1047, 1045, 1043, 1041, 1039, 1037, 1035, 1033, 1031, 1029, 1027, 1025, 1023, 1021, 1019, 1017, 1015, 1013, 1011, 1009, 1007, 1005, 1003, 1001, 999, 997, 995, 993, 991, 989, 987, 985, 983, 981, 979, 977, 975, 973, 971, 969, 967, 965, 963, 961, 959, 957, 955, 953, 951, 949, 947, 945, 943, 941, 939, 937, 935, 933, 931, 929, 927, 925, 923, 921, 919, 917, 915, 913, 911, 909, 907, 905, 903, 901, 899, 897, 895, 893, 891, 889, 887, 885, 883, 881, 879, 877, 875, 873, 871, 869, 867, 865, 863, 861, 859, 857, 855, 853, 851, 849, 847, 845, 843, 841, 839, 837, 835, 833, 831, 829, 827, 825, 823, 821, 819, 817, 815, 813, 811, 809, 807, 805, 803, 801, 799, 797, 795, 793, 791, 789, 787, 785, 783, 781, 779, 777, 775, 773, 771, 769, 767, 765, 763, 761, 759, 757, 755, 753, 751, 749, 747, 745, 743, 741, 739, 737, 735, 733, 731, 729, 727, 725, 723, 721, 719, 717, 715, 713, 711, 709, 707, 705, 703, 701, 699, 697, 695, 693, 691, 689, 687, 685, 683, 681, 679, 677, 675, 673, 671, 669, 667, 665, 663, 661, 659, 657, 655, 653, 651, 649, 647, 645, 643, 641, 639, 637, 635, 633, 631, 629, 627, 625, 623, 621, 619, 617, 615, 613, 611, 609, 607, 605, 603, 601, 599, 597, 595, 593, 591, 589, 587, 585, 583, 581, 579, 577, 575, 573, 571, 569, 567, 565, 563, 561, 559, 557, 555, 553, 551, 549, 547, 545, 543, 541, 539, 537, 535, 533, 531, 529, 527, 525, 523, 521, 519, 517, 515, 513, 511, 509, 507, 505, 503, 501, 499, 497, 495, 493, 491, 489, 487, 485, 483, 481, 479, 477, 475, 473, 471, 469, 467, 465, 463, 461, 459, 455, 452, 451, 447, 446, 445, 440, 439, 436, 433, 432, 424, 422, 415, 410, 408, 407, 405, 404, 399, 390, 388, 387, 385, 377, 372, 366, 365, 364, 362, 358, 352, 347, 344, 343, 341, 336, 329, 326, 324, 323, 321, 316, 313, 308, 306, 298, 297, 295, 291, 289, 281, 274, 271, 267, 266, 264, 263, 256, 253, 248, 246, 234, 233, 231, 229, 227, 213, 211, 202, 197, 196, 194, 191, 189, 180, 168, 166, 164, 163, 157, 156, 154, 146, 144, 142, 141, 115, 114, 112, 81, 73, 72, 70, 59, 40, 39, 38, 37, 35, 22, 21, 20, 17, 14, 13, 11, 9, 7, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614, 8614 } ; static yy_state_type yy_last_accepting_state; static char *yy_last_accepting_cpos; extern int sfat_flex_debug; int sfat_flex_debug = 0; /* The intent behind this definition is that it'll catch * any uses of REJECT which flex missed. */ #define REJECT reject_used_but_not_detected #define yymore() yymore_used_but_not_detected #define YY_MORE_ADJ 0 #define YY_RESTORE_YY_MORE_OFFSET char *sfattext; #line 1 "sf_attribute_table_parser.l" /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sf_attribute_table_parser.l */ /* * Lex for Attribute Table */ /* Definitions Section. * Definitions required by the rules section are in here prior to first * "%%" seperator */ /* Include code between "%{ %}" separators at top of generated * lexer source file */ #line 39 "sf_attribute_table_parser.l" #ifdef TARGET_BASED #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "util.h" #include "snort.h" #include "parser.h" #include "sftarget_reader.h" #include "sf_attribute_table.h" /* Generated from YACC */ extern ServiceClient sfat_client_or_service; static int sfat_linenumber = 0; static char* sfat_filename; void sfat_error(char *err); int sfat_parse(void); /* Change flex buffer size from default 8K to STD_BUF bytes */ #ifdef YY_BUF_SIZE #undef YY_BUF_SIZE #endif #define YY_BUF_SIZE STD_BUF #define YY_DECL int sfat_lex(void) /* At end-of-file return assuming no more files to scan */ /* Not using this functionality */ #define YY_NO_INPUT 1 /* Optimise lexer for interactive use */ /* Declare exclusive start conditions. * Start conditions are included here to illustrate how to add simple * state-machine functionality to the lexer */ /* Define some common patterns for use below */ /* Rules Section. * All rules are in here prior to second "%%" seperator */ #line 6270 "sf_attribute_table_parser.c" #define INITIAL 0 #define waiting_for_comma_prior_to_data 1 #define waiting_for_data 2 #ifndef YY_NO_UNISTD_H /* Special case for "unistd.h", since it is non-ANSI. We include it way * down here because we want the user's section 1 to have been scanned first. * The user has a chance to override it with an option. */ #include #endif #ifndef YY_EXTRA_TYPE #define YY_EXTRA_TYPE void * #endif static int yy_init_globals (void ); /* Accessor methods to globals. These are made visible to non-reentrant scanners for convenience. */ int sfatlex_destroy (void ); int sfatget_debug (void ); void sfatset_debug (int debug_flag ); YY_EXTRA_TYPE sfatget_extra (void ); void sfatset_extra (YY_EXTRA_TYPE user_defined ); FILE *sfatget_in (void ); void sfatset_in (FILE * _in_str ); FILE *sfatget_out (void ); void sfatset_out (FILE * _out_str ); int sfatget_leng (void ); char *sfatget_text (void ); int sfatget_lineno (void ); void sfatset_lineno (int _line_number ); /* Macros after this point can all be overridden by user definitions in * section 1. */ #ifndef YY_SKIP_YYWRAP #ifdef __cplusplus extern "C" int sfatwrap (void ); #else extern int sfatwrap (void ); #endif #endif #ifndef YY_NO_UNPUT #endif #ifndef yytext_ptr static void yy_flex_strncpy (char *,yyconst char *,int ); #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * ); #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void ); #else static int input (void ); #endif #endif /* Amount of stuff to slurp up with each read. */ #ifndef YY_READ_BUF_SIZE #ifdef __ia64__ /* On IA-64, the buffer size is 16k, not 8k */ #define YY_READ_BUF_SIZE 16384 #else #define YY_READ_BUF_SIZE 8192 #endif /* __ia64__ */ #endif /* Copy whatever the last rule matched to the standard output. */ #ifndef ECHO /* This used to be an fputs(), but since the string might contain NUL's, * we now use fwrite(). */ #define ECHO do { if (fwrite( sfattext, (size_t) sfatleng, 1, sfatout )) {} } while (0) #endif /* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, * is returned in "result". */ #ifndef YY_INPUT #define YY_INPUT(buf,result,max_size) \ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ { \ int c = '*'; \ int n; \ for ( n = 0; n < max_size && \ (c = getc( sfatin )) != EOF && c != '\n'; ++n ) \ buf[n] = (char) c; \ if ( c == '\n' ) \ buf[n++] = (char) c; \ if ( c == EOF && ferror( sfatin ) ) \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ result = n; \ } \ else \ { \ errno=0; \ while ( (result = (int) fread(buf, 1, (yy_size_t) max_size, sfatin)) == 0 && ferror(sfatin)) \ { \ if( errno != EINTR) \ { \ YY_FATAL_ERROR( "input in flex scanner failed" ); \ break; \ } \ errno=0; \ clearerr(sfatin); \ } \ }\ \ #endif /* No semi-colon after return; correct usage is to write "yyterminate();" - * we don't want an extra ';' after the "return" because that will cause * some compilers to complain about unreachable statements. */ #ifndef yyterminate #define yyterminate() return YY_NULL #endif /* Number of entries by which start-condition stack grows. */ #ifndef YY_START_STACK_INCR #define YY_START_STACK_INCR 25 #endif /* Report a fatal error. */ #ifndef YY_FATAL_ERROR #define YY_FATAL_ERROR(msg) yy_fatal_error( msg ) #endif /* end tables serialization structures and prototypes */ /* Default declaration of generated scanner - a define so the user can * easily add parameters. */ #ifndef YY_DECL #define YY_DECL_IS_OURS 1 extern int sfatlex (void); #define YY_DECL int sfatlex (void) #endif /* !YY_DECL */ /* Code executed at the beginning of each rule, after sfattext and sfatleng * have been set up. */ #ifndef YY_USER_ACTION #define YY_USER_ACTION #endif /* Code executed at the end of each rule. */ #ifndef YY_BREAK #define YY_BREAK /*LINTED*/break; #endif #define YY_RULE_SETUP \ YY_USER_ACTION /** The main scanner function which does all the work. */ YY_DECL { yy_state_type yy_current_state; char *yy_cp, *yy_bp; int yy_act; if ( !(yy_init) ) { (yy_init) = 1; #ifdef YY_USER_INIT YY_USER_INIT; #endif if ( ! (yy_start) ) (yy_start) = 1; /* first start state */ if ( ! sfatin ) sfatin = stdin; if ( ! sfatout ) sfatout = stdout; if ( ! YY_CURRENT_BUFFER ) { sfatensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = sfat_create_buffer(sfatin,YY_BUF_SIZE ); } sfat_load_buffer_state( ); } { #line 101 "sf_attribute_table_parser.l" #line 6490 "sf_attribute_table_parser.c" while ( /*CONSTCOND*/1 ) /* loops until end-of-file is reached */ { yy_cp = (yy_c_buf_p); /* Support of sfattext. */ *yy_cp = (yy_hold_char); /* yy_bp points to the position in yy_ch_buf of the start of * the current run. */ yy_bp = yy_cp; yy_current_state = (yy_start); yy_match: do { YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)] ; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 8615 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; ++yy_cp; } while ( yy_current_state != 8614 ); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); yy_find_action: yy_act = yy_accept[yy_current_state]; YY_DO_BEFORE_ACTION; do_action: /* This label is used only to access EOF actions. */ switch ( yy_act ) { /* beginning of action switch */ case 0: /* must back up */ /* undo the effects of YY_DO_BEFORE_ACTION */ *yy_cp = (yy_hold_char); yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; case 1: YY_RULE_SETUP #line 102 "sf_attribute_table_parser.l" { ; } /* Handle empty whitespace */ YY_BREAK case 2: YY_RULE_SETUP #line 103 "sf_attribute_table_parser.l" { return SF_START_SNORT_ATTRIBUTES; } YY_BREAK case 3: YY_RULE_SETUP #line 104 "sf_attribute_table_parser.l" { return SF_END_SNORT_ATTRIBUTES; } YY_BREAK case 4: YY_RULE_SETUP #line 106 "sf_attribute_table_parser.l" { return SF_AT_START_MAP_TABLE; } YY_BREAK case 5: YY_RULE_SETUP #line 107 "sf_attribute_table_parser.l" { return SF_AT_END_MAP_TABLE; } YY_BREAK case 6: YY_RULE_SETUP #line 108 "sf_attribute_table_parser.l" { return SF_AT_START_ENTRY; } YY_BREAK case 7: YY_RULE_SETUP #line 109 "sf_attribute_table_parser.l" { return SF_AT_END_ENTRY; } YY_BREAK case 8: YY_RULE_SETUP #line 110 "sf_attribute_table_parser.l" { return SF_AT_START_ENTRY_ID; } YY_BREAK case 9: YY_RULE_SETUP #line 111 "sf_attribute_table_parser.l" { return SF_AT_END_ENTRY_ID; } YY_BREAK case 10: YY_RULE_SETUP #line 112 "sf_attribute_table_parser.l" { return SF_AT_START_ENTRY_VALUE; } YY_BREAK case 11: YY_RULE_SETUP #line 113 "sf_attribute_table_parser.l" { return SF_AT_END_ENTRY_VALUE; } YY_BREAK case 12: YY_RULE_SETUP #line 114 "sf_attribute_table_parser.l" { return SF_AT_START_ATTRIBUTE_TABLE; } YY_BREAK case 13: YY_RULE_SETUP #line 115 "sf_attribute_table_parser.l" { return SF_AT_END_ATTRIBUTE_TABLE; } YY_BREAK case 14: YY_RULE_SETUP #line 116 "sf_attribute_table_parser.l" { return SF_AT_START_HOST; } YY_BREAK case 15: YY_RULE_SETUP #line 117 "sf_attribute_table_parser.l" { return SF_AT_END_HOST; } YY_BREAK case 16: YY_RULE_SETUP #line 118 "sf_attribute_table_parser.l" { return SF_AT_START_HOST_IP; } YY_BREAK case 17: YY_RULE_SETUP #line 119 "sf_attribute_table_parser.l" { return SF_AT_END_HOST_IP; } YY_BREAK case 18: YY_RULE_SETUP #line 120 "sf_attribute_table_parser.l" { return SF_AT_START_OS; } YY_BREAK case 19: YY_RULE_SETUP #line 121 "sf_attribute_table_parser.l" { return SF_AT_END_OS; } YY_BREAK case 20: YY_RULE_SETUP #line 122 "sf_attribute_table_parser.l" { return SF_AT_START_ATTRIBUTE_VALUE; } YY_BREAK case 21: YY_RULE_SETUP #line 123 "sf_attribute_table_parser.l" { return SF_AT_END_ATTRIBUTE_VALUE; } YY_BREAK case 22: YY_RULE_SETUP #line 124 "sf_attribute_table_parser.l" { return SF_AT_START_ATTRIBUTE_ID; } YY_BREAK case 23: YY_RULE_SETUP #line 125 "sf_attribute_table_parser.l" { return SF_AT_END_ATTRIBUTE_ID; } YY_BREAK case 24: YY_RULE_SETUP #line 126 "sf_attribute_table_parser.l" { return SF_AT_START_CONFIDENCE; } YY_BREAK case 25: YY_RULE_SETUP #line 127 "sf_attribute_table_parser.l" { return SF_AT_END_CONFIDENCE; } YY_BREAK case 26: YY_RULE_SETUP #line 128 "sf_attribute_table_parser.l" { return SF_AT_START_NAME; } YY_BREAK case 27: YY_RULE_SETUP #line 129 "sf_attribute_table_parser.l" { return SF_AT_END_NAME; } YY_BREAK case 28: YY_RULE_SETUP #line 130 "sf_attribute_table_parser.l" { return SF_AT_START_VENDOR; } YY_BREAK case 29: YY_RULE_SETUP #line 131 "sf_attribute_table_parser.l" { return SF_AT_END_VENDOR; } YY_BREAK case 30: YY_RULE_SETUP #line 132 "sf_attribute_table_parser.l" { return SF_AT_START_VERSION; } YY_BREAK case 31: YY_RULE_SETUP #line 133 "sf_attribute_table_parser.l" { return SF_AT_END_VERSION; } YY_BREAK case 32: YY_RULE_SETUP #line 134 "sf_attribute_table_parser.l" { return SF_AT_START_FRAG_POLICY; } YY_BREAK case 33: YY_RULE_SETUP #line 135 "sf_attribute_table_parser.l" { return SF_AT_END_FRAG_POLICY; } YY_BREAK case 34: YY_RULE_SETUP #line 136 "sf_attribute_table_parser.l" { return SF_AT_START_STREAM_POLICY; } YY_BREAK case 35: YY_RULE_SETUP #line 137 "sf_attribute_table_parser.l" { return SF_AT_END_STREAM_POLICY; } YY_BREAK case 36: YY_RULE_SETUP #line 138 "sf_attribute_table_parser.l" { return SF_AT_START_SERVICES; } YY_BREAK case 37: YY_RULE_SETUP #line 139 "sf_attribute_table_parser.l" { return SF_AT_END_SERVICES; } YY_BREAK case 38: YY_RULE_SETUP #line 140 "sf_attribute_table_parser.l" { return SF_AT_START_SERVICE; } YY_BREAK case 39: YY_RULE_SETUP #line 141 "sf_attribute_table_parser.l" { return SF_AT_END_SERVICE; } YY_BREAK case 40: YY_RULE_SETUP #line 142 "sf_attribute_table_parser.l" { return SF_AT_START_CLIENTS; } YY_BREAK case 41: YY_RULE_SETUP #line 143 "sf_attribute_table_parser.l" { return SF_AT_END_CLIENTS; } YY_BREAK case 42: YY_RULE_SETUP #line 144 "sf_attribute_table_parser.l" { return SF_AT_START_CLIENT; } YY_BREAK case 43: YY_RULE_SETUP #line 145 "sf_attribute_table_parser.l" { return SF_AT_END_CLIENT; } YY_BREAK case 44: YY_RULE_SETUP #line 146 "sf_attribute_table_parser.l" { return SF_AT_START_IPPROTO; } YY_BREAK case 45: YY_RULE_SETUP #line 147 "sf_attribute_table_parser.l" { return SF_AT_END_IPPROTO; } YY_BREAK case 46: YY_RULE_SETUP #line 148 "sf_attribute_table_parser.l" { return SF_AT_START_PROTOCOL; } YY_BREAK case 47: YY_RULE_SETUP #line 149 "sf_attribute_table_parser.l" { return SF_AT_END_PROTOCOL; } YY_BREAK case 48: YY_RULE_SETUP #line 150 "sf_attribute_table_parser.l" { return SF_AT_START_PORT; } YY_BREAK case 49: YY_RULE_SETUP #line 151 "sf_attribute_table_parser.l" { return SF_AT_END_PORT; } YY_BREAK case 50: YY_RULE_SETUP #line 152 "sf_attribute_table_parser.l" { return SF_AT_START_APPLICATION; } YY_BREAK case 51: YY_RULE_SETUP #line 153 "sf_attribute_table_parser.l" { return SF_AT_END_APPLICATION; } YY_BREAK case 52: YY_RULE_SETUP #line 155 "sf_attribute_table_parser.l" { sfat_lval.numericValue = strtol( sfattext, NULL, 10 ); #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "Number Value: [%d]\n", sfat_lval.numericValue); #endif return SF_AT_NUMERIC; } YY_BREAK case 53: YY_RULE_SETUP #line 163 "sf_attribute_table_parser.l" { /* Store the value of the string, but not * more than STD_BUF. */ int i; for (i=0; i < sfatleng && i < STD_BUF-1; i++) { sfat_lval.stringValue[i] = sfattext[i]; } sfat_lval.stringValue[i] = '\0'; #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "String Value: [%s]\n", sfat_lval.stringValue); #endif return SF_AT_STRING; } YY_BREAK case 54: /* rule 54 can match eol */ YY_RULE_SETUP #line 179 "sf_attribute_table_parser.l" { sfat_linenumber++; } YY_BREAK case 55: YY_RULE_SETUP #line 180 "sf_attribute_table_parser.l" { ; /* Do nothing -- ignore it */} YY_BREAK case 56: YY_RULE_SETUP #line 182 "sf_attribute_table_parser.l" { return 0; } YY_BREAK /* Error, no meaningful input provided */ case YY_STATE_EOF(INITIAL): case YY_STATE_EOF(waiting_for_comma_prior_to_data): case YY_STATE_EOF(waiting_for_data): #line 185 "sf_attribute_table_parser.l" { yyterminate(); } YY_BREAK case 57: YY_RULE_SETUP #line 187 "sf_attribute_table_parser.l" ECHO; YY_BREAK #line 6858 "sf_attribute_table_parser.c" case YY_END_OF_BUFFER: { /* Amount of text matched not including the EOB char. */ int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1; /* Undo the effects of YY_DO_BEFORE_ACTION. */ *yy_cp = (yy_hold_char); YY_RESTORE_YY_MORE_OFFSET if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) { /* We're scanning a new file or input source. It's * possible that this happened because the user * just pointed sfatin at a new source and called * sfatlex(). If so, then we have to assure * consistency between YY_CURRENT_BUFFER and our * globals. Here is the right place to do so, because * this is the first action (other than possibly a * back-up) that will match for the new input source. */ (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; YY_CURRENT_BUFFER_LVALUE->yy_input_file = sfatin; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; } /* Note that here we test for yy_c_buf_p "<=" to the position * of the first EOB in the buffer, since yy_c_buf_p will * already have been incremented past the NUL character * (since all states make transitions on EOB to the * end-of-buffer state). Contrast this with the test * in input(). */ if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) { /* This was really a NUL. */ yy_state_type yy_next_state; (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); /* Okay, we're now positioned to make the NUL * transition. We couldn't have * yy_get_previous_state() go ahead and do it * for us because it doesn't know how to deal * with the possibility of jamming (and we don't * want to build jamming into it because then it * will run more slowly). */ yy_next_state = yy_try_NUL_trans( yy_current_state ); yy_bp = (yytext_ptr) + YY_MORE_ADJ; if ( yy_next_state ) { /* Consume the NUL. */ yy_cp = ++(yy_c_buf_p); yy_current_state = yy_next_state; goto yy_match; } else { yy_cp = (yy_last_accepting_cpos); yy_current_state = (yy_last_accepting_state); goto yy_find_action; } } else switch ( yy_get_next_buffer( ) ) { case EOB_ACT_END_OF_FILE: { (yy_did_buffer_switch_on_eof) = 0; if ( sfatwrap( ) ) { /* Note: because we've taken care in * yy_get_next_buffer() to have set up * sfattext, we can now set up * yy_c_buf_p so that if some total * hoser (like flex itself) wants to * call the scanner after we return the * YY_NULL, it'll still work - another * YY_NULL will get returned. */ (yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ; yy_act = YY_STATE_EOF(YY_START); goto do_action; } else { if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; } break; } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_match; case EOB_ACT_LAST_MATCH: (yy_c_buf_p) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)]; yy_current_state = yy_get_previous_state( ); yy_cp = (yy_c_buf_p); yy_bp = (yytext_ptr) + YY_MORE_ADJ; goto yy_find_action; } break; } default: YY_FATAL_ERROR( "fatal flex scanner internal error--no action found" ); } /* end of action switch */ } /* end of scanning one token */ } /* end of user's declarations */ } /* end of sfatlex */ /* yy_get_next_buffer - try to read in a new buffer * * Returns a code representing an action: * EOB_ACT_LAST_MATCH - * EOB_ACT_CONTINUE_SCAN - continue scanning from current position * EOB_ACT_END_OF_FILE - end of file */ static int yy_get_next_buffer (void) { char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; char *source = (yytext_ptr); yy_size_t number_to_move, i; int ret_val; if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] ) YY_FATAL_ERROR( "fatal flex scanner internal error--end of buffer missed" ); if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) { /* Don't try to fill the buffer, so this is an EOF. */ if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 ) { /* We matched a single character, the EOB, so * treat this as a final EOF. */ return EOB_ACT_END_OF_FILE; } else { /* We matched some text prior to the EOB, first * process it. */ return EOB_ACT_LAST_MATCH; } } /* Try to read more data. */ /* First move last chars to start of buffer. */ number_to_move = (yy_size_t) ((yy_c_buf_p) - (yytext_ptr)) - 1; for ( i = 0; i < number_to_move; ++i ) *(dest++) = *(source++); if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) /* don't do the read, it's not guaranteed to return an EOF, * just force an EOF */ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0; else { int num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; while ( num_to_read <= 0 ) { /* Not enough room in the buffer - grow it. */ /* just a shorter name for the current buffer */ YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE; int yy_c_buf_p_offset = (int) ((yy_c_buf_p) - b->yy_ch_buf); if ( b->yy_is_our_buffer ) { int new_size = b->yy_buf_size * 2; if ( new_size <= 0 ) b->yy_buf_size += b->yy_buf_size / 8; else b->yy_buf_size *= 2; b->yy_ch_buf = (char *) /* Include room in for 2 EOB chars. */ sfatrealloc((void *) b->yy_ch_buf,(yy_size_t) (b->yy_buf_size + 2) ); } else /* Can't grow it, we don't own it. */ b->yy_ch_buf = NULL; if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); (yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset]; num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; } if ( num_to_read > YY_READ_BUF_SIZE ) num_to_read = YY_READ_BUF_SIZE; /* Read in more data. */ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), (yy_n_chars), num_to_read ); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } if ( (yy_n_chars) == 0 ) { if ( number_to_move == YY_MORE_ADJ ) { ret_val = EOB_ACT_END_OF_FILE; sfatrestart(sfatin ); } else { ret_val = EOB_ACT_LAST_MATCH; YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_EOF_PENDING; } } else ret_val = EOB_ACT_CONTINUE_SCAN; if ((int) ((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { /* Extend the array by 50%, plus the number we really need. */ int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1); YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) sfatrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,(yy_size_t) new_size ); if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); } (yy_n_chars) += number_to_move; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR; YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR; (yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; return ret_val; } /* yy_get_previous_state - get the state just before the EOB char was reached */ static yy_state_type yy_get_previous_state (void) { yy_state_type yy_current_state; char *yy_cp; yy_current_state = (yy_start); for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp ) { YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 8615 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; } return yy_current_state; } /* yy_try_NUL_trans - try to make a transition on the NUL character * * synopsis * next_state = yy_try_NUL_trans( current_state ); */ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state ) { int yy_is_jam; char *yy_cp = (yy_c_buf_p); YY_CHAR yy_c = 1; if ( yy_accept[yy_current_state] ) { (yy_last_accepting_state) = yy_current_state; (yy_last_accepting_cpos) = yy_cp; } while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) { yy_current_state = (int) yy_def[yy_current_state]; if ( yy_current_state >= 8615 ) yy_c = yy_meta[(unsigned int) yy_c]; } yy_current_state = yy_nxt[yy_base[yy_current_state] + (flex_int16_t) yy_c]; yy_is_jam = (yy_current_state == 8614); return yy_is_jam ? 0 : yy_current_state; } #ifndef YY_NO_UNPUT #endif #ifndef YY_NO_INPUT #ifdef __cplusplus static int yyinput (void) #else static int input (void) #endif { int c; *(yy_c_buf_p) = (yy_hold_char); if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR ) { /* yy_c_buf_p now points to the character we want to return. * If this occurs *before* the EOB characters, then it's a * valid NUL; if not, then we've hit the end of the buffer. */ if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] ) /* This was really a NUL. */ *(yy_c_buf_p) = '\0'; else { /* need more input */ int offset = (yy_c_buf_p) - (yytext_ptr); ++(yy_c_buf_p); switch ( yy_get_next_buffer( ) ) { case EOB_ACT_LAST_MATCH: /* This happens because yy_g_n_b() * sees that we've accumulated a * token and flags that we need to * try matching the token before * proceeding. But for input(), * there's no matching to consider. * So convert the EOB_ACT_LAST_MATCH * to EOB_ACT_END_OF_FILE. */ /* Reset buffer status. */ sfatrestart(sfatin ); /*FALLTHROUGH*/ case EOB_ACT_END_OF_FILE: { if ( sfatwrap( ) ) return 0; if ( ! (yy_did_buffer_switch_on_eof) ) YY_NEW_FILE; #ifdef __cplusplus return yyinput(); #else return input(); #endif } case EOB_ACT_CONTINUE_SCAN: (yy_c_buf_p) = (yytext_ptr) + offset; break; } } } c = *(unsigned char *) (yy_c_buf_p); /* cast for 8-bit char's */ *(yy_c_buf_p) = '\0'; /* preserve sfattext */ (yy_hold_char) = *++(yy_c_buf_p); return c; } #endif /* ifndef YY_NO_INPUT */ /** Immediately switch to a different input stream. * @param input_file A readable stream. * * @note This function does not reset the start condition to @c INITIAL . */ void sfatrestart (FILE * input_file ) { if ( ! YY_CURRENT_BUFFER ){ sfatensure_buffer_stack (); YY_CURRENT_BUFFER_LVALUE = sfat_create_buffer(sfatin,YY_BUF_SIZE ); } sfat_init_buffer(YY_CURRENT_BUFFER,input_file ); sfat_load_buffer_state( ); } /** Switch to a different input buffer. * @param new_buffer The new input buffer. * */ void sfat_switch_to_buffer (YY_BUFFER_STATE new_buffer ) { /* TODO. We should be able to replace this entire function body * with * sfatpop_buffer_state(); * sfatpush_buffer_state(new_buffer); */ sfatensure_buffer_stack (); if ( YY_CURRENT_BUFFER == new_buffer ) return; if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } YY_CURRENT_BUFFER_LVALUE = new_buffer; sfat_load_buffer_state( ); /* We don't actually know whether we did this switch during * EOF (sfatwrap()) processing, but the only time this flag * is looked at is after sfatwrap() is called, so it's safe * to go ahead and always set it. */ (yy_did_buffer_switch_on_eof) = 1; } static void sfat_load_buffer_state (void) { (yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; (yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; sfatin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; (yy_hold_char) = *(yy_c_buf_p); } /** Allocate and initialize an input buffer state. * @param file A readable stream. * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. * * @return the allocated buffer state. */ YY_BUFFER_STATE sfat_create_buffer (FILE * file, int size ) { YY_BUFFER_STATE b; b = (YY_BUFFER_STATE) sfatalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in sfat_create_buffer()" ); b->yy_buf_size = size; /* yy_ch_buf has to be 2 characters longer than the size given because * we need to put in 2 end-of-buffer characters. */ b->yy_ch_buf = (char *) sfatalloc((yy_size_t) (b->yy_buf_size + 2) ); if ( ! b->yy_ch_buf ) YY_FATAL_ERROR( "out of dynamic memory in sfat_create_buffer()" ); b->yy_is_our_buffer = 1; sfat_init_buffer(b,file ); return b; } /** Destroy the buffer. * @param b a buffer created with sfat_create_buffer() * */ void sfat_delete_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; if ( b->yy_is_our_buffer ) sfatfree((void *) b->yy_ch_buf ); sfatfree((void *) b ); } /* Initializes or reinitializes a buffer. * This function is sometimes called more than once on the same buffer, * such as during a sfatrestart() or at EOF. */ static void sfat_init_buffer (YY_BUFFER_STATE b, FILE * file ) { int oerrno = errno; sfat_flush_buffer(b ); b->yy_input_file = file; b->yy_fill_buffer = 1; /* If b is the current buffer, then sfat_init_buffer was _probably_ * called from sfatrestart() or through yy_get_next_buffer. * In that case, we don't want to reset the lineno or column. */ if (b != YY_CURRENT_BUFFER){ b->yy_bs_lineno = 1; b->yy_bs_column = 0; } b->yy_is_interactive = 0; errno = oerrno; } /** Discard all buffered characters. On the next scan, YY_INPUT will be called. * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. * */ void sfat_flush_buffer (YY_BUFFER_STATE b ) { if ( ! b ) return; b->yy_n_chars = 0; /* We always need two end-of-buffer characters. The first causes * a transition to the end-of-buffer state. The second causes * a jam in that state. */ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; b->yy_buf_pos = &b->yy_ch_buf[0]; b->yy_at_bol = 1; b->yy_buffer_status = YY_BUFFER_NEW; if ( b == YY_CURRENT_BUFFER ) sfat_load_buffer_state( ); } /** Pushes the new state onto the stack. The new state becomes * the current state. This function will allocate the stack * if necessary. * @param new_buffer The new state. * */ void sfatpush_buffer_state (YY_BUFFER_STATE new_buffer ) { if (new_buffer == NULL) return; sfatensure_buffer_stack(); /* This block is copied from sfat_switch_to_buffer. */ if ( YY_CURRENT_BUFFER ) { /* Flush out information for old buffer. */ *(yy_c_buf_p) = (yy_hold_char); YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p); YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars); } /* Only push if top exists. Otherwise, replace top. */ if (YY_CURRENT_BUFFER) (yy_buffer_stack_top)++; YY_CURRENT_BUFFER_LVALUE = new_buffer; /* copied from sfat_switch_to_buffer. */ sfat_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } /** Removes and deletes the top of the stack, if present. * The next element becomes the new top. * */ void sfatpop_buffer_state (void) { if (!YY_CURRENT_BUFFER) return; sfat_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; if ((yy_buffer_stack_top) > 0) --(yy_buffer_stack_top); if (YY_CURRENT_BUFFER) { sfat_load_buffer_state( ); (yy_did_buffer_switch_on_eof) = 1; } } /* Allocates the stack if it does not exist. * Guarantees space for at least one push. */ static void sfatensure_buffer_stack (void) { int num_to_alloc; if (!(yy_buffer_stack)) { /* First allocation is just for 2 elements, since we don't know if this * scanner will even need a stack. We use 2 instead of 1 to avoid an * immediate realloc on the next call. */ num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */ (yy_buffer_stack) = (struct yy_buffer_state**)sfatalloc (num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in sfatensure_buffer_stack()" ); memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; (yy_buffer_stack_top) = 0; return; } if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){ /* Increase the buffer to prepare for a possible push. */ yy_size_t grow_size = 8 /* arbitrary grow size */; num_to_alloc = (yy_buffer_stack_max) + grow_size; (yy_buffer_stack) = (struct yy_buffer_state**)sfatrealloc ((yy_buffer_stack), num_to_alloc * sizeof(struct yy_buffer_state*) ); if ( ! (yy_buffer_stack) ) YY_FATAL_ERROR( "out of dynamic memory in sfatensure_buffer_stack()" ); /* zero only the new slots.*/ memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*)); (yy_buffer_stack_max) = num_to_alloc; } } /** Setup the input buffer state to scan directly from a user-specified character buffer. * @param base the character buffer * @param size the size in bytes of the character buffer * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE sfat_scan_buffer (char * base, yy_size_t size ) { YY_BUFFER_STATE b; if ( size < 2 || base[size-2] != YY_END_OF_BUFFER_CHAR || base[size-1] != YY_END_OF_BUFFER_CHAR ) /* They forgot to leave room for the EOB's. */ return NULL; b = (YY_BUFFER_STATE) sfatalloc(sizeof( struct yy_buffer_state ) ); if ( ! b ) YY_FATAL_ERROR( "out of dynamic memory in sfat_scan_buffer()" ); b->yy_buf_size = (int) (size - 2); /* "- 2" to take care of EOB's */ b->yy_buf_pos = b->yy_ch_buf = base; b->yy_is_our_buffer = 0; b->yy_input_file = NULL; b->yy_n_chars = b->yy_buf_size; b->yy_is_interactive = 0; b->yy_at_bol = 1; b->yy_fill_buffer = 0; b->yy_buffer_status = YY_BUFFER_NEW; sfat_switch_to_buffer(b ); return b; } /** Setup the input buffer state to scan a string. The next call to sfatlex() will * scan from a @e copy of @a str. * @param yystr a NUL-terminated string to scan * * @return the newly allocated buffer state object. * @note If you want to scan bytes that may contain NUL values, then use * sfat_scan_bytes() instead. */ YY_BUFFER_STATE sfat_scan_string (yyconst char * yystr ) { return sfat_scan_bytes(yystr,(int) strlen(yystr) ); } /** Setup the input buffer state to scan the given bytes. The next call to sfatlex() will * scan from a @e copy of @a bytes. * @param yybytes the byte buffer to scan * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes. * * @return the newly allocated buffer state object. */ YY_BUFFER_STATE sfat_scan_bytes (yyconst char * yybytes, int _yybytes_len ) { YY_BUFFER_STATE b; char *buf; yy_size_t n; int i; /* Get memory for full buffer, including space for trailing EOB's. */ n = (yy_size_t) (_yybytes_len + 2); buf = (char *) sfatalloc(n ); if ( ! buf ) YY_FATAL_ERROR( "out of dynamic memory in sfat_scan_bytes()" ); for ( i = 0; i < _yybytes_len; ++i ) buf[i] = yybytes[i]; buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; b = sfat_scan_buffer(buf,n ); if ( ! b ) YY_FATAL_ERROR( "bad buffer in sfat_scan_bytes()" ); /* It's okay to grow etc. this buffer, and we should throw it * away when we're done. */ b->yy_is_our_buffer = 1; return b; } #ifndef YY_EXIT_FAILURE #define YY_EXIT_FAILURE 2 #endif static void yynoreturn yy_fatal_error (yyconst char* msg ) { (void) fprintf( stderr, "%s\n", msg ); exit( YY_EXIT_FAILURE ); } /* Redefine yyless() so it works in section 3 code. */ #undef yyless #define yyless(n) \ do \ { \ /* Undo effects of setting up sfattext. */ \ yy_size_t yyless_macro_arg = (n); \ YY_LESS_LINENO(yyless_macro_arg);\ sfattext[sfatleng] = (yy_hold_char); \ (yy_c_buf_p) = sfattext + yyless_macro_arg; \ (yy_hold_char) = *(yy_c_buf_p); \ *(yy_c_buf_p) = '\0'; \ sfatleng = yyless_macro_arg; \ } \ while ( 0 ) /* Accessor methods (get/set functions) to struct members. */ /** Get the current line number. * */ int sfatget_lineno (void) { return sfatlineno; } /** Get the input stream. * */ FILE *sfatget_in (void) { return sfatin; } /** Get the output stream. * */ FILE *sfatget_out (void) { return sfatout; } /** Get the length of the current token. * */ int sfatget_leng (void) { return sfatleng; } /** Get the current token. * */ char *sfatget_text (void) { return sfattext; } /** Set the current line number. * @param _line_number line number * */ void sfatset_lineno (int _line_number ) { sfatlineno = _line_number; } /** Set the input stream. This does not discard the current * input buffer. * @param _in_str A readable stream. * * @see sfat_switch_to_buffer */ void sfatset_in (FILE * _in_str ) { sfatin = _in_str ; } void sfatset_out (FILE * _out_str ) { sfatout = _out_str ; } int sfatget_debug (void) { return sfat_flex_debug; } void sfatset_debug (int _bdebug ) { sfat_flex_debug = _bdebug ; } static int yy_init_globals (void) { /* Initialization is the same as for the non-reentrant scanner. * This function is called from sfatlex_destroy(), so don't allocate here. */ (yy_buffer_stack) = NULL; (yy_buffer_stack_top) = 0; (yy_buffer_stack_max) = 0; (yy_c_buf_p) = NULL; (yy_init) = 0; (yy_start) = 0; /* Defined in main.c */ #ifdef YY_STDINIT sfatin = stdin; sfatout = stdout; #else sfatin = NULL; sfatout = NULL; #endif /* For future reference: Set errno on error, since we are called by * sfatlex_init() */ return 0; } /* sfatlex_destroy is for both reentrant and non-reentrant scanners. */ int sfatlex_destroy (void) { /* Pop the buffer stack, destroying each element. */ while(YY_CURRENT_BUFFER){ sfat_delete_buffer(YY_CURRENT_BUFFER ); YY_CURRENT_BUFFER_LVALUE = NULL; sfatpop_buffer_state(); } /* Destroy the stack itself. */ sfatfree((yy_buffer_stack) ); (yy_buffer_stack) = NULL; /* Reset the globals. This is important in a non-reentrant scanner so the next time * sfatlex() is called, initialization will occur. */ yy_init_globals( ); return 0; } /* * Internal utility routines. */ #ifndef yytext_ptr static void yy_flex_strncpy (char* s1, yyconst char * s2, int n ) { int i; for ( i = 0; i < n; ++i ) s1[i] = s2[i]; } #endif #ifdef YY_NEED_STRLEN static int yy_flex_strlen (yyconst char * s ) { int n; for ( n = 0; s[n]; ++n ) ; return n; } #endif void *sfatalloc (yy_size_t size ) { return malloc(size); } void *sfatrealloc (void * ptr, yy_size_t size ) { /* The cast to (char *) in the following accommodates both * implementations that use char* generic pointers, and those * that use void* generic pointers. It works with the latter * because both ANSI C and C++ allow castless assignment from * any pointer type to void*, and deal with argument conversions * as though doing an assignment. */ return realloc(ptr, size); } void sfatfree (void * ptr ) { free( (char *) ptr ); /* see sfatrealloc() for (char *) cast */ } #define YYTABLES_NAME "yytables" #line 187 "sf_attribute_table_parser.l" char *sfat_grammar_error=NULL; char sfat_grammar_error_printed=0; char sfat_insufficient_space_logged=0; char sfat_fatal_error=1; char *sfat_saved_file = NULL; static char sfat_saved_file_set = 0; char parse_error = 0; char sfat_error_message[STD_BUF]; int ParseTargetMap(char *filename) { int error; int ret = SFAT_ERROR; parse_error = 0; sfat_grammar_error_printed = 0; if (!filename) { return SFAT_OK; } sfatin = fopen(filename, "r"); if (!sfatin) { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d): Failed to open target-based attribute file: '%s'\n", file_name, file_line, filename); return ret; } sfat_filename = filename; if (feof(sfatin)) { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d): Emtpy target-based attribute file: '%s'\n", file_name, file_line, filename); fclose(sfatin); return ret; } error = sfat_parse(); if (error) { sfat_error(""); fclose(sfatin); return ret; } fclose(sfatin); if (parse_error == 1) { return ret; } ret = SFAT_OK; /* Everything parsed ok, save off the filename */ if (sfat_saved_file_set && sfat_saved_file) { if (!strcmp(sfat_saved_file, sfat_filename)) { /* Same filename, we're done. */ return ret; } sfat_saved_file_set = 0; } /* Save off the new filename. */ if (sfat_saved_file) { free(sfat_saved_file); } sfat_saved_file = SnortStrdup(sfat_filename); sfat_saved_file_set = 1; return ret; } void DestroyBufferStack(void) { #ifdef HAVE_YYLEX_DESTROY sfatlex_destroy(); #endif } void sfat_error(char *err) { if (sfat_grammar_error_printed != 0) { parse_error = 1; return; } if (sfat_grammar_error) { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d) ==> Invalid Attribute Table specification: '%s'.\n" "Please verify the grammar at or near line %d (tag '%s'): %s\n", file_name, file_line, sfat_filename, sfat_linenumber, sfattext, sfat_grammar_error); } else { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d) ==> Invalid Attribute Table specification: '%s'.\n" "Please verify the grammar at or near line %d (tag '%s').\n", file_name, file_line, sfat_filename, sfat_linenumber, sfattext); } parse_error = 1; } #endif /* TARGET_BASED */ snort-2.9.20/src/target-based/sf_attribute_table_parser.l0000444000175000017500000002223114241077424021620 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sf_attribute_table_parser.l */ /* * Lex for Attribute Table */ /* Definitions Section. * Definitions required by the rules section are in here prior to first * "%%" seperator */ /* Include code between "%{ %}" separators at top of generated * lexer source file */ %{ #ifdef TARGET_BASED #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include "util.h" #include "snort.h" #include "parser.h" #include "sftarget_reader.h" #include "sf_attribute_table.h" /* Generated from YACC */ extern ServiceClient sfat_client_or_service; static int sfat_linenumber = 0; static char* sfat_filename; void sfat_error(char *err); int sfat_parse(void); /* Change flex buffer size from default 8K to STD_BUF bytes */ #ifdef YY_BUF_SIZE #undef YY_BUF_SIZE #endif #define YY_BUF_SIZE STD_BUF #define YY_DECL int sfat_lex(void) %} /* At end-of-file return assuming no more files to scan */ %option noyywrap /* Not using this functionality */ %option nounput %option noinput /* Optimise lexer for interactive use */ %option never-interactive /* Declare exclusive start conditions. * Start conditions are included here to illustrate how to add simple * state-machine functionality to the lexer */ %x waiting_for_comma_prior_to_data %x waiting_for_data /* Define some common patterns for use below */ newline \r\n|\n whitespace [ \t]* comma , digit [0-9] numericValue [+-]?{digit}{1,16} stringValue [ \t_/\-a-zA-Z0-9,\.;:()#"'~`!@#$%^&*()=+\[\]|\{\}\?\\]{1,4096} commentStart \ comment {commentStart}.*{commentEnd} /* Rules Section. * All rules are in here prior to second "%%" seperator */ %% {whitespace} { ; } /* Handle empty whitespace */ \ { return SF_START_SNORT_ATTRIBUTES; } \<\/SNORT_ATTRIBUTES\> { return SF_END_SNORT_ATTRIBUTES; } \ { return SF_AT_START_MAP_TABLE; } \<\/ATTRIBUTE_MAP\> { return SF_AT_END_MAP_TABLE; } \ { return SF_AT_START_ENTRY; } \<\/ENTRY\> { return SF_AT_END_ENTRY; } \ { return SF_AT_START_ENTRY_ID; } \<\/ID\> { return SF_AT_END_ENTRY_ID; } \ { return SF_AT_START_ENTRY_VALUE; } \<\/VALUE\> { return SF_AT_END_ENTRY_VALUE; } \ { return SF_AT_START_ATTRIBUTE_TABLE; } \<\/ATTRIBUTE_TABLE\> { return SF_AT_END_ATTRIBUTE_TABLE; } \ { return SF_AT_START_HOST; } \<\/HOST\> { return SF_AT_END_HOST; } \ { return SF_AT_START_HOST_IP; } \<\/IP\> { return SF_AT_END_HOST_IP; } \ { return SF_AT_START_OS; } \<\/OPERATING_SYSTEM\> { return SF_AT_END_OS; } \ { return SF_AT_START_ATTRIBUTE_VALUE; } \<\/ATTRIBUTE_VALUE\> { return SF_AT_END_ATTRIBUTE_VALUE; } \ { return SF_AT_START_ATTRIBUTE_ID; } \<\/ATTRIBUTE_ID\> { return SF_AT_END_ATTRIBUTE_ID; } \ { return SF_AT_START_CONFIDENCE; } \<\/CONFIDENCE\> { return SF_AT_END_CONFIDENCE; } \ { return SF_AT_START_NAME; } \<\/NAME\> { return SF_AT_END_NAME; } \ { return SF_AT_START_VENDOR; } \<\/VENDOR\> { return SF_AT_END_VENDOR; } \ { return SF_AT_START_VERSION; } \<\/VERSION\> { return SF_AT_END_VERSION; } \ { return SF_AT_START_FRAG_POLICY; } \<\/FRAG_POLICY\> { return SF_AT_END_FRAG_POLICY; } \ { return SF_AT_START_STREAM_POLICY; } \<\/STREAM_POLICY\> { return SF_AT_END_STREAM_POLICY; } \ { return SF_AT_START_SERVICES; } \<\/SERVICES\> { return SF_AT_END_SERVICES; } \ { return SF_AT_START_SERVICE; } \<\/SERVICE\> { return SF_AT_END_SERVICE; } \ { return SF_AT_START_CLIENTS; } \<\/CLIENTS\> { return SF_AT_END_CLIENTS; } \ { return SF_AT_START_CLIENT; } \<\/CLIENT\> { return SF_AT_END_CLIENT; } \ { return SF_AT_START_IPPROTO; } \<\/IPPROTO\> { return SF_AT_END_IPPROTO; } \ { return SF_AT_START_PROTOCOL; } \<\/PROTOCOL\> { return SF_AT_END_PROTOCOL; } \ { return SF_AT_START_PORT; } \<\/PORT\> { return SF_AT_END_PORT; } \ { return SF_AT_START_APPLICATION; } \<\/APPLICATION\> { return SF_AT_END_APPLICATION; } {numericValue} { sfat_lval.numericValue = strtol( yytext, NULL, 10 ); #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "Number Value: [%d]\n", sfat_lval.numericValue); #endif return SF_AT_NUMERIC; } {stringValue} { /* Store the value of the string, but not * more than STD_BUF. */ int i; for (i=0; i < yyleng && i < STD_BUF-1; i++) { sfat_lval.stringValue[i] = yytext[i]; } sfat_lval.stringValue[i] = '\0'; #ifdef DEBUG_MSGS DebugMessage(DEBUG_ATTRIBUTE, "String Value: [%s]\n", sfat_lval.stringValue); #endif return SF_AT_STRING; } {newline} { sfat_linenumber++; } {comment} { ; /* Do nothing -- ignore it */} . { return 0; } /* Error, no meaningful input provided */ <> { yyterminate(); } %% char *sfat_grammar_error=NULL; char sfat_grammar_error_printed=0; char sfat_insufficient_space_logged=0; char sfat_fatal_error=1; char *sfat_saved_file = NULL; static char sfat_saved_file_set = 0; char parse_error = 0; char sfat_error_message[STD_BUF]; int ParseTargetMap(char *filename) { int error; int ret = SFAT_ERROR; parse_error = 0; sfat_grammar_error_printed = 0; if (!filename) { return SFAT_OK; } yyin = fopen(filename, "r"); if (!yyin) { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d): Failed to open target-based attribute file: '%s'\n", file_name, file_line, filename); return ret; } sfat_filename = filename; if (feof(yyin)) { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d): Emtpy target-based attribute file: '%s'\n", file_name, file_line, filename); fclose(yyin); return ret; } error = sfat_parse(); if (error) { sfat_error(""); fclose(yyin); return ret; } fclose(yyin); if (parse_error == 1) { return ret; } ret = SFAT_OK; /* Everything parsed ok, save off the filename */ if (sfat_saved_file_set && sfat_saved_file) { if (!strcmp(sfat_saved_file, sfat_filename)) { /* Same filename, we're done. */ return ret; } sfat_saved_file_set = 0; } /* Save off the new filename. */ if (sfat_saved_file) { free(sfat_saved_file); } sfat_saved_file = SnortStrdup(sfat_filename); sfat_saved_file_set = 1; return ret; } void DestroyBufferStack(void) { #ifdef HAVE_YYLEX_DESTROY sfatlex_destroy(); #endif } void sfat_error(char *err) { if (sfat_grammar_error_printed != 0) { parse_error = 1; return; } if (sfat_grammar_error) { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d) ==> Invalid Attribute Table specification: '%s'.\n" "Please verify the grammar at or near line %d (tag '%s'): %s\n", file_name, file_line, sfat_filename, sfat_linenumber, yytext, sfat_grammar_error); } else { SnortSnprintf(sfat_error_message, STD_BUF, "%s(%d) ==> Invalid Attribute Table specification: '%s'.\n" "Please verify the grammar at or near line %d (tag '%s').\n", file_name, file_line, sfat_filename, sfat_linenumber, yytext); } parse_error = 1; } #endif /* TARGET_BASED */ snort-2.9.20/src/target-based/sftarget_hostentry.c0000644000175000017500000001155514241077425020341 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sftarget_hostentry.c */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sftarget_hostentry.h" int hasService(HostAttributeEntry *host_entry, int ipprotocol, int protocol, int application) { ApplicationEntry *service; if (!host_entry) return SFTARGET_NOMATCH; for (service = host_entry->services; service; service = service->next) { if (ipprotocol && (service->ipproto == ipprotocol)) { if (protocol && (service->protocol == protocol)) { if (!application) { /* match of ipproto, proto. * application not speicifed */ return SFTARGET_MATCH; } } else if (!protocol) { /* match of ipproto. * protocol not speicifed */ return SFTARGET_MATCH; } } /* No ipprotocol specified, huh? */ } return SFTARGET_NOMATCH; } int hasClient(HostAttributeEntry *host_entry, int ipprotocol, int protocol, int application) { ApplicationEntry *client; if (!host_entry) return SFTARGET_NOMATCH; for (client = host_entry->clients; client; client = client->next) { if (ipprotocol && (client->ipproto == ipprotocol)) { if (protocol && (client->protocol == protocol)) { if (!application) { /* match of ipproto, proto. * application not speicifed */ return SFTARGET_MATCH; } } else if (!protocol) { /* match of ipproto. * protocol not speicifed */ return SFTARGET_MATCH; } } /* No ipprotocol specified, huh? */ } return SFTARGET_NOMATCH; } int hasProtocol(HostAttributeEntry *host_entry, int ipprotocol, int protocol, int application) { int ret = SFTARGET_NOMATCH; ret = hasService(host_entry, ipprotocol, protocol, application); if (ret == SFTARGET_MATCH) return ret; ret = hasClient(host_entry, ipprotocol, protocol, application); if (ret == SFTARGET_MATCH) return ret; return ret; } char isFragPolicySet(HostAttributeEntry *host_entry) { if (host_entry && host_entry->hostInfo.fragPolicySet) { return POLICY_SET; } return POLICY_NOT_SET; } char isStreamPolicySet(HostAttributeEntry *host_entry) { if (host_entry && host_entry->hostInfo.streamPolicySet) { return POLICY_SET; } return POLICY_NOT_SET; } uint16_t getFragPolicy(HostAttributeEntry *host_entry) { if (!host_entry) return SFAT_UNKNOWN_FRAG_POLICY; if (!host_entry->hostInfo.fragPolicySet) return SFAT_UNKNOWN_FRAG_POLICY; return host_entry->hostInfo.fragPolicy; } uint16_t getStreamPolicy(HostAttributeEntry *host_entry) { if (!host_entry) return SFAT_UNKNOWN_STREAM_POLICY; if (!host_entry->hostInfo.streamPolicySet) return SFAT_UNKNOWN_STREAM_POLICY; return host_entry->hostInfo.streamPolicy; } int getApplicationProtocolId(HostAttributeEntry *host_entry, int ipprotocol, uint16_t port, char direction) { ApplicationEntry *application; if (!host_entry) return 0; if (direction == SFAT_SERVICE) { for (application = host_entry->services; application; application = application->next) { if (application->ipproto == ipprotocol) { if ((uint16_t)application->port == port) { return application->protocol; } } } } /* TODO: client? doesn't make much sense in terms of specific port */ return 0; } snort-2.9.20/src/target-based/sftarget_reader.h0000644000175000017500000001140214241077433017537 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sftarget_reader.c */ #ifndef SF_TARGET_READER_H_ #define SF_TARGET_READER_H_ #include "snort.h" #ifdef REG_TEST #include "reg_test.h" #endif #define SFAT_OK 0 #define SFAT_ERROR -1 #define SFAT_CHECKHOST \ if (!current_host) return SFAT_ERROR; #define SFAT_CHECKAPP \ if (!current_app) return SFAT_ERROR; void SigAttributeTableReloadHandler(int signal); typedef enum { ATTRIBUTE_NAME, ATTRIBUTE_ID } AttributeTypes; typedef enum { ATTRIBUTE_SERVICE, ATTRIBUTE_CLIENT } ServiceClient; typedef struct _MapData { char s_mapvalue[STD_BUF]; uint32_t l_mapid; } MapData; typedef MapData MapEntry; typedef struct _AttributeData { AttributeTypes type; union { char s_value[STD_BUF]; uint32_t l_value; } value; int confidence; int16_t attributeOrdinal; } AttributeData; #define APPLICATION_ENTRY_PORT 0x01 #define APPLICATION_ENTRY_IPPROTO 0x02 #define APPLICATION_ENTRY_PROTO 0x04 #define APPLICATION_ENTRY_APPLICATION 0x08 #define APPLICATION_ENTRY_VERSION 0x10 typedef struct _ApplicationEntry { struct _ApplicationEntry *next; uint16_t port; uint16_t ipproto; uint16_t protocol; uint8_t fields; } ApplicationEntry; typedef ApplicationEntry ApplicationList; #define HOST_INFO_OS 1 #define HOST_INFO_VENDOR 2 #define HOST_INFO_VERSION 3 #define HOST_INFO_FRAG_POLICY 4 #define HOST_INFO_STREAM_POLICY 5 #define POLICY_SET 1 #define POLICY_NOT_SET 0 typedef struct _HostInfo { char streamPolicyName[16]; char fragPolicyName[16]; uint16_t streamPolicy; uint16_t fragPolicy; char streamPolicySet; char fragPolicySet; } HostInfo; #define SFAT_SERVICE 1 #define SFAT_CLIENT 2 typedef struct _HostAttributeEntry { sfcidr_t ipAddr; HostInfo hostInfo; ApplicationList *services; ApplicationList *clients; } HostAttributeEntry; /* Callback Functions from YACC */ int SFAT_AddMapEntry(MapEntry *); char *SFAT_LookupAttributeNameById(int id); HostAttributeEntry * SFAT_CreateHostEntry(void); int SFAT_AddHostEntryToMap(void); int SFAT_SetHostIp(const char *); int SFAT_SetOSAttribute(AttributeData *data, int attribute); int SFAT_SetOSPolicy(char *policy_name, int attribute); ApplicationEntry * SFAT_CreateApplicationEntry(void); int SFAT_AddApplicationData(void); int SFAT_SetApplicationAttribute(AttributeData *data, int attribute); void PrintAttributeData(char *prefix, AttributeData *data); /* Callback to set frag & stream policy IDs */ typedef int (*GetPolicyIdFunc)(HostAttributeEntry *); typedef struct _GetPolicyIdsCallbackList { GetPolicyIdFunc policyCallback; struct _GetPolicyIdsCallbackList *next; } GetPolicyIdsCallbackList; void SFAT_SetPolicyIds(GetPolicyIdFunc policyCallback, int snortPolicyId); /* Cleanup Functions, called by Snort shutdown */ void SFAT_Cleanup(void); void FreeHostEntry(HostAttributeEntry *host); /* Parsing Functions -- to be called by Snort parser */ int SFAT_ParseAttributeTable(char *args, SnortConfig *sc); /* Function to swap out new table */ void AttributeTableReloadCheck(void); /* Status functions */ uint32_t SFAT_NumberOfHosts(void); /* API Lookup functions, to be called by Stream & Frag */ HostAttributeEntry *SFAT_LookupHostEntryByIP(sfaddr_t *ipAddr); HostAttributeEntry *SFAT_LookupHostEntryBySrc(Packet *p); HostAttributeEntry *SFAT_LookupHostEntryByDst(Packet *p); void SFAT_UpdateApplicationProtocol(sfaddr_t *ipAddr, uint16_t port, uint16_t protocol, uint16_t id); /* Returns whether this has been configured */ int IsAdaptiveConfigured( void ); int IsAdaptiveConfiguredForSnortConfig(struct _SnortConfig *); void SFAT_StartReloadThread(void); void SFLAT_init(void); void SFLAT_fini(void); int SFLAT_isEnabled(tSfPolicyId id, int parsing); #ifdef SNORT_RELOAD void SFAT_ReloadCheck(struct _SnortConfig *); void ReloadAttributeThreadStop(void); void SFAT_CleanPrevConfig(void); #endif #endif /* SF_TARGET_READER_H_ */ snort-2.9.20/src/target-based/sf_attribute_table.y0000444000175000017500000003713314241077423020267 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sf_attribute_table.y */ /* * * AttributeTable * * YACC Grammar/language definition */ %{ #ifdef TARGET_BASED #include #include #include "sftarget_reader.h" #include "snort_debug.h" #define YYSTACK_USE_ALLOCA 0 /* define the initial stack-sizes */ #ifdef YYMAXDEPTH #undef YYMAXDEPTH #define YYMAXDEPTH 70000 #else #define YYMAXDEPTH 70000 #endif extern ServiceClient sfat_client_or_service; extern char *sfat_grammar_error; extern int sfat_lex(); extern void sfat_error(char*); %} %union { char stringValue[STD_BUF]; uint32_t numericValue; AttributeData data; MapData mapEntry; } %token SF_AT_COMMENT %token SF_AT_WHITESPACE %token SF_START_SNORT_ATTRIBUTES %token SF_END_SNORT_ATTRIBUTES %token SF_AT_START_MAP_TABLE %token SF_AT_END_MAP_TABLE %token SF_AT_START_ENTRY %token SF_AT_END_ENTRY %token SF_AT_START_ENTRY_ID %token SF_AT_END_ENTRY_ID %token SF_AT_START_ENTRY_VALUE %token SF_AT_END_ENTRY_VALUE %token SF_AT_START_ATTRIBUTE_TABLE %token SF_AT_END_ATTRIBUTE_TABLE %token SF_AT_START_HOST %token SF_AT_END_HOST %token SF_AT_START_HOST_IP %token SF_AT_END_HOST_IP %token SF_AT_STRING %token SF_AT_NUMERIC /* %token SF_AT_IPv4 %token SF_AT_IPv4CIDR */ %token SF_AT_IPv6 %token SF_AT_IPv6Cidr %token SF_AT_START_OS %token SF_AT_END_OS %token SF_AT_START_ATTRIBUTE_VALUE %token SF_AT_END_ATTRIBUTE_VALUE %token SF_AT_START_ATTRIBUTE_ID %token SF_AT_END_ATTRIBUTE_ID %token SF_AT_START_CONFIDENCE %token SF_AT_END_CONFIDENCE %token SF_AT_START_NAME %token SF_AT_END_NAME %token SF_AT_START_VENDOR %token SF_AT_END_VENDOR %token SF_AT_START_VERSION %token SF_AT_END_VERSION %token SF_AT_START_FRAG_POLICY %token SF_AT_END_FRAG_POLICY %token SF_AT_START_STREAM_POLICY %token SF_AT_END_STREAM_POLICY %token SF_AT_START_SERVICES %token SF_AT_END_SERVICES %token SF_AT_START_SERVICE %token SF_AT_END_SERVICE %token SF_AT_START_CLIENTS %token SF_AT_END_CLIENTS %token SF_AT_START_CLIENT %token SF_AT_END_CLIENT %token SF_AT_START_IPPROTO %token SF_AT_END_IPPROTO %token SF_AT_START_PORT %token SF_AT_END_PORT %token SF_AT_START_PROTOCOL %token SF_AT_END_PROTOCOL %token SF_AT_START_APPLICATION %token SF_AT_END_APPLICATION %type MapEntryData %type AttributeInfo %type MapValue %type MapId %type AttributeValueString %type AttributeValueNumber %type AttributeConfidence %type AttributeId %% /* Grammar rules and actions follow */ /* The Main Grammar... Either a mapping table and attribute table, * or just the attribute table by itself. */ AttributeGrammar: SnortAttributes { YYACCEPT; }; SnortAttributes: SF_START_SNORT_ATTRIBUTES MappingTable AttributeTable SF_END_SNORT_ATTRIBUTES { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "SnortAttributes: Got Attribute Map & Table\n");); } | SF_START_SNORT_ATTRIBUTES AttributeTable SF_END_SNORT_ATTRIBUTES { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "SnortAttributes: Got Attribute Table\n");); }; /* The name-id map table for data reduction */ MappingTable: SF_AT_START_MAP_TABLE ListOfMapEntries SF_AT_END_MAP_TABLE { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Got Attribute Map\n");); }; ListOfMapEntries: { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Empty Mapping Table\n");); } | MapEntry ListOfMapEntries; MapEntry: MapEntryStart MapEntryData MapEntryEnd { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "MapEntry: Name: %s, Id %d\n", $2.s_mapvalue, $2.l_mapid);); SFAT_AddMapEntry(&$2); }; MapEntryStart: SF_AT_START_ENTRY; MapEntryEnd: SF_AT_END_ENTRY; MapEntryData: MapId MapValue { $$.l_mapid = $1; SnortStrncpy($$.s_mapvalue, $2, STD_BUF); }; MapValue: SF_AT_START_ENTRY_VALUE SF_AT_STRING SF_AT_END_ENTRY_VALUE { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "MapValue: %s\n", $2);) SnortStrncpy($$, $2, STD_BUF); }; MapId: SF_AT_START_ENTRY_ID SF_AT_NUMERIC SF_AT_END_ENTRY_ID { $$ = $2; DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "MapId: %d\n", $2);); }; /* The table of hosts and their respective attributes */ AttributeTable: SF_AT_START_ATTRIBUTE_TABLE ListOfHosts SF_AT_END_ATTRIBUTE_TABLE { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Got Attribute Table\n");); }; ListOfHosts: { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "EmptyHostEntry\n");); } | ListOfHosts HostEntry; HostEntry: HostEntryStart HostEntryData HostEntryEnd { if (SFAT_AddHostEntryToMap() != SFAT_OK) { YYABORT; } DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Host Added\n");); }; HostEntryStart: SF_AT_START_HOST { /* Callback to create a host entry object */ SFAT_CreateHostEntry(); }; HostEntryEnd: SF_AT_END_HOST; HostEntryData: IpCidr HostOS ServiceList ClientList { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData\n");); } | IpCidr HostOS ClientList { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData: No Services\n");); } | IpCidr HostOS ServiceList { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData: No Clients\n");); } | IpCidr HostOS { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "HostEntryData: No Services or Clients\n");); } ; IpCidr: SF_AT_START_HOST_IP SF_AT_STRING SF_AT_END_HOST_IP { /* Convert IP/CIDR to Snort IPCidr Object */ /* determine the number of bits (done in SetHostIp4) */ if (SFAT_SetHostIp($2) != SFAT_OK) { YYABORT; } }; HostOS: SF_AT_START_OS OSAttributes SF_AT_END_OS; OSAttributes: OSAttribute | OSAttributes OSAttribute; OSAttribute: OSName | OSVendor | OSVersion | OSStreamPolicy | OSFragPolicy; OSName: SF_AT_START_NAME AttributeInfo SF_AT_END_NAME { /* Copy OSName */ DEBUG_WRAP(PrintAttributeData("OS:Name", &$2);); SFAT_SetOSAttribute(&$2, HOST_INFO_OS); }; OSVendor: SF_AT_START_VENDOR AttributeInfo SF_AT_END_VENDOR { /* Copy OSVendor */ DEBUG_WRAP(PrintAttributeData("OS:Vendor", &$2);); SFAT_SetOSAttribute(&$2, HOST_INFO_VENDOR); }; OSVersion: SF_AT_START_VERSION AttributeInfo SF_AT_END_VERSION { /* Copy OSVersion */ DEBUG_WRAP(PrintAttributeData("OS:Version", &$2);); SFAT_SetOSAttribute(&$2, HOST_INFO_VERSION); }; OSFragPolicy: SF_AT_START_FRAG_POLICY SF_AT_STRING SF_AT_END_FRAG_POLICY { /* Copy OSFragPolicy */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "OS:FragPolicy: %s\n", $2);); SFAT_SetOSPolicy($2, HOST_INFO_FRAG_POLICY); }; OSStreamPolicy: SF_AT_START_STREAM_POLICY SF_AT_STRING SF_AT_END_STREAM_POLICY { /* Copy OSStreamPolicy */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "OS:StreamPolicy: %s\n", $2);); SFAT_SetOSPolicy($2, HOST_INFO_STREAM_POLICY); }; AttributeInfo: AttributeValueString { $$.type = ATTRIBUTE_NAME; $$.confidence = 100; SnortStrncpy($$.value.s_value, $1, STD_BUF); } | AttributeValueString AttributeConfidence { $$.type = ATTRIBUTE_NAME; $$.confidence = $2; SnortStrncpy($$.value.s_value, $1, STD_BUF); } | AttributeValueNumber AttributeConfidence { $$.type = ATTRIBUTE_NAME; $$.confidence = $2; SnortSnprintf($$.value.s_value, STD_BUF, "%d", $1); } | AttributeValueNumber { $$.type = ATTRIBUTE_NAME; $$.confidence = 100; SnortSnprintf($$.value.s_value, STD_BUF, "%d", $1); } | AttributeId AttributeConfidence { char *mapped_name; $$.confidence = $2; mapped_name = SFAT_LookupAttributeNameById($1); if (!mapped_name) { $$.type = ATTRIBUTE_ID; $$.value.l_value = $1; //FatalError("Unknown/Invalid Attribute ID %d\n", $1); sfat_grammar_error = "Unknown/Invalid Attribute ID"; YYABORT; } else { /* Copy String */ $$.type = ATTRIBUTE_NAME; SnortStrncpy($$.value.s_value, mapped_name, STD_BUF); } } | AttributeId { char *mapped_name; $$.confidence = 100; mapped_name = SFAT_LookupAttributeNameById($1); if (!mapped_name) { $$.type = ATTRIBUTE_ID; $$.value.l_value = $1; //FatalError("Unknown/Invalid Attribute ID %d\n", $1); sfat_grammar_error = "Unknown/Invalid Attribute ID"; YYABORT; } else { /* Copy String */ $$.type = ATTRIBUTE_NAME; SnortStrncpy($$.value.s_value, mapped_name, STD_BUF); } }; AttributeValueString: SF_AT_START_ATTRIBUTE_VALUE SF_AT_STRING SF_AT_END_ATTRIBUTE_VALUE { SnortStrncpy($$, $2, STD_BUF); }; AttributeValueNumber: SF_AT_START_ATTRIBUTE_VALUE SF_AT_END_ATTRIBUTE_VALUE { $$ = 0; } | SF_AT_START_ATTRIBUTE_VALUE SF_AT_NUMERIC SF_AT_END_ATTRIBUTE_VALUE { $$ = $2; }; AttributeId: SF_AT_START_ATTRIBUTE_ID SF_AT_NUMERIC SF_AT_END_ATTRIBUTE_ID { /* Copy numeric */ $$ = $2; }; AttributeConfidence: SF_AT_START_CONFIDENCE SF_AT_NUMERIC SF_AT_END_CONFIDENCE { /* Copy numeric */ $$ = $2; }; ServiceList: ServiceListStart ServiceListData ServiceListEnd { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "ServiceList (complete)\n");); }; ServiceListStart: SF_AT_START_SERVICES { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Start ServiceList\n");); sfat_client_or_service = ATTRIBUTE_SERVICE; }; ServiceListEnd: SF_AT_END_SERVICES { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "End ServiceList\n");); }; ServiceListData: { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "EmptyService\n");); } | Service ServiceListData { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service ServiceListData\n");); }; Service: ServiceStart ServiceData ServiceEnd { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Adding Complete\n");); SFAT_AddApplicationData(); DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Added\n");); }; ServiceStart: SF_AT_START_SERVICE { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Start\n");); SFAT_CreateApplicationEntry(); }; ServiceEnd: SF_AT_END_SERVICE { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service End\n");); }; ServiceData: ServiceDataRequired { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data (no application)\n");); } | ServiceDataRequired Application { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data (application)\n");); }; ServiceDataRequired: IPProtocol Protocol Port { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (IPProto Proto Port)\n");); } | IPProtocol Port Protocol { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (IPProto Port Proto)\n");); } | Protocol IPProtocol Port { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Proto IPProto Port)\n");); } | Protocol Port IPProtocol { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Proto Port IPProto)\n");); } | Port Protocol IPProtocol { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Port Proto IPProto)\n");); } | Port IPProtocol Protocol { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Service Data Required (Port IPProto Proto)\n");); }; IPProtocol: SF_AT_START_IPPROTO AttributeInfo SF_AT_END_IPPROTO { /* Store IPProto Info */ DEBUG_WRAP(PrintAttributeData("IPProto", &$2);); SFAT_SetApplicationAttribute(&$2, APPLICATION_ENTRY_IPPROTO); }; Protocol: SF_AT_START_PROTOCOL AttributeInfo SF_AT_END_PROTOCOL { /* Store Protocol Info */ DEBUG_WRAP(PrintAttributeData("Protocol", &$2);); SFAT_SetApplicationAttribute(&$2, APPLICATION_ENTRY_PROTO); }; Port: SF_AT_START_PORT AttributeInfo SF_AT_END_PORT { /* Store Port Info */ DEBUG_WRAP(PrintAttributeData("Port", &$2);); SFAT_SetApplicationAttribute(&$2, APPLICATION_ENTRY_PORT); }; Application: SF_AT_START_APPLICATION AttributeInfo SF_AT_END_APPLICATION { /* Store Application Info */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Application\n")); DEBUG_WRAP(PrintAttributeData("Application", &$2);); SFAT_SetApplicationAttribute(&$2, APPLICATION_ENTRY_APPLICATION); } | SF_AT_START_APPLICATION AttributeInfo Version SF_AT_END_APPLICATION { /* Store Application Info */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Application with Version\n")); DEBUG_WRAP(PrintAttributeData("Application", &$2);); SFAT_SetApplicationAttribute(&$2, APPLICATION_ENTRY_APPLICATION); }; Version: SF_AT_START_VERSION AttributeInfo SF_AT_END_VERSION { /* Store Version Info */ DEBUG_WRAP(PrintAttributeData("Version", &$2);); SFAT_SetApplicationAttribute(&$2, APPLICATION_ENTRY_VERSION); }; ClientList: ClientListStart ClientListData ClientListEnd { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "ClientList (complete)\n");); }; ClientListStart: SF_AT_START_CLIENTS { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Start ClientList\n");); sfat_client_or_service = ATTRIBUTE_CLIENT; }; ClientListEnd: SF_AT_END_CLIENTS { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "End ClientList\n");); }; ClientListData: { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "EmptyClient\n");); } | Client ClientListData { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client ClientListData\n");); }; Client: ClientStart ClientData ClientEnd { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Adding Complete\n");); SFAT_AddApplicationData(); DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Added\n");); }; ClientStart: SF_AT_START_CLIENT { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Start\n");); SFAT_CreateApplicationEntry(); }; ClientEnd: SF_AT_END_CLIENT { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client End\n");); }; ClientData: ClientDataRequired { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data (no application)\n");); } | ClientDataRequired Application { DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data (application)\n");); }; ClientDataRequired: Protocol { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data Required (Proto)\n");); } | IPProtocol Protocol { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data Required (IPProto Proto)\n");); } | Protocol IPProtocol { /* Order independent */ DEBUG_WRAP(DebugMessage(DEBUG_ATTRIBUTE, "Client Data Required (Proto IPProto)\n");); }; %% /* int yywrap(void) { return 1; } */ #endif /* TARGET_BASED */ snort-2.9.20/src/target-based/sftarget_protocol_reference.c0000644000175000017500000002033114241077427022153 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sftarget_protocol_reference.c */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef TARGET_BASED #include "sftarget_protocol_reference.h" #include "sfutil/sfghash.h" #include "log.h" #include "util.h" #include "snort_debug.h" #include "session_api.h" #include "stream_api.h" #include "spp_frag3.h" #include "sftarget_reader.h" #include "sftarget_hostentry.h" int16_t protocolReferenceTCP; int16_t protocolReferenceUDP; int16_t protocolReferenceICMP; static SFGHASH *proto_reference_table = NULL; #if defined(FEAT_OPEN_APPID) static SFTargetProtocolReference** proto_name_table = NULL; #endif /* defined(FEAT_OPEN_APPID) */ static int16_t protocol_number = 1; static char *standard_protocols[] = { /* Transport Protocols */ "ip", "tcp", "udp", "icmp", /* Application Protocols */ "http", "ftp", "telnet", "smtp", "ssh", "dcerpc", "netbios-dgm", "netbios-ns", "netbios-ssn", "nntp", "dns", "isakmp", "finger", "imap", "oracle", "pop2", "pop3", "snmp", "tftp", "x11", "ftp-data", "http2", NULL }; /* XXX XXX Probably need to do this during swap time since the * proto_reference_table is accessed during runtime */ int16_t AddProtocolReference(const char *protocol) { SFTargetProtocolReference *reference; if (!protocol) return SFTARGET_UNKNOWN_PROTOCOL; if (!proto_reference_table) { InitializeProtocolReferenceTable(); } reference = sfghash_find(proto_reference_table, (void *)protocol); if (reference) { DEBUG_WRAP( DebugMessage(DEBUG_ATTRIBUTE, "Protocol Reference for %s exists as %d\n", protocol, reference->ordinal);); return reference->ordinal; } reference = SnortAlloc(sizeof(SFTargetProtocolReference)); reference->ordinal = protocol_number++; if (protocol_number > MAX_PROTOCOL_ORDINAL) { /* XXX: If we see this warning message, should * increase MAX_PROTOCOL_ORDINAL definition. The ordinal is * stored as a signed 16bit int, so it can be increased upto * 32k without requiring a change in space. It is currently * defined as 8192. */ LogMessage("WARNING: protocol_number wrapped. This may result" "in odd behavior and potential false positives.\n"); /* 1 is the first protocol id we use. */ /* 0 is not used */ /* -1 means unknwon */ protocol_number = 1; } SnortStrncpy(reference->name, protocol, STD_BUF); sfghash_add(proto_reference_table, reference->name, reference); #if defined(FEAT_OPEN_APPID) proto_name_table[reference->ordinal-1] = reference; #endif /* defined(FEAT_OPEN_APPID) */ DEBUG_WRAP( DebugMessage(DEBUG_ATTRIBUTE, "Added Protocol Reference for %s as %d\n", protocol, reference->ordinal);); return reference->ordinal; } int16_t FindProtocolReference(const char *protocol) { SFTargetProtocolReference *reference; if (!protocol) return SFTARGET_UNKNOWN_PROTOCOL; if (!proto_reference_table) { InitializeProtocolReferenceTable(); } reference = sfghash_find(proto_reference_table, (void *)protocol); if (reference) return reference->ordinal; return SFTARGET_UNKNOWN_PROTOCOL; } #if defined(FEAT_OPEN_APPID) const char * FindProtocolName(int16_t protocol_number) { if (protocol_number <= 0 || protocol_number > MAX_PROTOCOL_ORDINAL) return NULL; if (!proto_reference_table) { return NULL; } if (proto_name_table[protocol_number-1]) return proto_name_table[protocol_number-1]->name; return NULL; } #endif /* defined(FEAT_OPEN_APPID) */ void InitializeProtocolReferenceTable(void) { char **protocol; /* If already initialized, we're done */ if (proto_reference_table) return; proto_reference_table = sfghash_new(65, 0, 1, free); if (!proto_reference_table) { FatalError("Failed to Initialize Target-Based Protocol Reference Table\n"); } #if defined(FEAT_OPEN_APPID) proto_name_table = calloc(MAX_PROTOCOL_ORDINAL, sizeof(*proto_name_table)); if (!proto_name_table) { FatalError("Failed to Initialize Target-Based Protocol name Table\n"); } #endif /* defined(FEAT_OPEN_APPID) */ /* Initialize the standard protocols from the list above */ for (protocol = standard_protocols; *protocol; protocol++) { AddProtocolReference(*protocol); } protocolReferenceTCP = FindProtocolReference("tcp"); protocolReferenceUDP = FindProtocolReference("udp"); protocolReferenceICMP = FindProtocolReference("icmp"); } void FreeProtoocolReferenceTable(void) { sfghash_delete(proto_reference_table); proto_reference_table = NULL; #if defined(FEAT_OPEN_APPID) free(proto_name_table); proto_name_table = NULL; #endif /* defined(FEAT_OPEN_APPID) */ } int16_t GetProtocolReference(Packet *p) { int16_t protocol = 0; int16_t ipprotocol = 0; if (!p) return protocol; if (p->application_protocol_ordinal != 0) return p->application_protocol_ordinal; do /* Simple do loop to break out of quickly, not really a loop */ { HostAttributeEntry *host_entry; if ( session_api && session_api->is_session_verified( p->ssnptr ) ) { /* Use session information */ protocol = session_api->get_application_protocol_id( p->ssnptr ); if (protocol != 0) { break; } } if (p->fragtracker) { protocol = fragGetApplicationProtocolId(p); /* Use information from frag tracker */ if (protocol != 0) { break; } } switch (GET_IPH_PROTO(p)) { case IPPROTO_TCP: ipprotocol = protocolReferenceTCP; break; case IPPROTO_UDP: ipprotocol = protocolReferenceUDP; break; case IPPROTO_ICMP: ipprotocol = protocolReferenceICMP; break; } /* Lookup the destination host to find the protocol for the * destination port */ host_entry = SFAT_LookupHostEntryByDst(p); if (host_entry) { protocol = getApplicationProtocolId(host_entry, ipprotocol, p->dp, SFAT_SERVICE); } if (protocol != 0) { session_api->set_application_protocol_id( p->ssnptr, protocol ); break; } /* If not found, do same for src host/src port. */ host_entry = SFAT_LookupHostEntryBySrc(p); if (host_entry) { protocol = getApplicationProtocolId(host_entry, ipprotocol, p->sp, SFAT_SERVICE); } if (protocol != 0) { session_api->set_application_protocol_id( p->ssnptr, protocol ); break; } } while (0); /* Simple do loop to break out of quickly, not really a loop */ /* Store it to alleviate future lookups */ p->application_protocol_ordinal = protocol; return protocol; } #endif /* TARGET_BASED */ snort-2.9.20/src/target-based/Makefile.am0000444000175000017500000000201614230012554016247 0ustar apoapoAUTOMAKE_OPTIONS=foreign no-dependencies noinst_LIBRARIES = libtarget_based.a INCLUDES = @INCLUDES@ if HAVE_TARGET_BASED #BUILT_SOURCES = \ #sf_attribute_table_parser.c \ #sf_attribute_table.h \ #sf_attribute_table.c nodist_libtarget_based_a_SOURCES = \ sf_attribute_table_parser.c \ sf_attribute_table.h \ sf_attribute_table.c libtarget_based_a_SOURCES = \ sftarget_reader.c \ sftarget_reader.h \ sftarget_hostentry.c \ sftarget_hostentry.h \ sftarget_protocol_reference.c \ sftarget_protocol_reference.h \ sf_attribute_table_parser.l \ sf_attribute_table.y else libtarget_based_a_SOURCES = sftarget_reader.c endif .y.c: $(YACC) -d -psfat_ -o$@ $? #### Ugly to get the header file built. #### any other suggestions? sf_attribute_table.h: sf_attribute_table.y $(YACC) -d -psfat_ $? mv y.tab.h $@ .l.c: $(LEX) -i -o$@ $? sf_attribute_table_parser.c: sf_attribute_table_parser.l sf_attribute_table.h $(LEX) -i -Psfat -o$@ $< clean-local: rm -f \ sf_attribute_table_parser.c \ sf_attribute_table.h \ sf_attribute_table.c snort-2.9.20/src/target-based/sftarget_hostentry.h0000644000175000017500000000416414241077426020345 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sftarget_hostentry.h */ #ifndef _SFTARGET_HOSTENTRY_H_ #define _SFTARGET_HOSTENTRY_H_ #include "sftarget_reader.h" #define SFTARGET_MATCH 1 #define SFTARGET_NOMATCH 0 /* API for HostAttributeEntry 'class' */ int hasService(HostAttributeEntry *hostEntry, int ipprotocol, int protocol, int application); int hasClient(HostAttributeEntry *hostEntry, int ipprotocol, int protocol, int application); int hasProtocol(HostAttributeEntry *hostEntry, int ipprotocol, int protocol, int application); int getProtocol(HostAttributeEntry *hostEntry, int ipprotocol, uint16_t port); int getApplicationProtocolId(HostAttributeEntry *host_entry, int ipprotocol, uint16_t port, char direction); #define SFAT_UNKNOWN_STREAM_POLICY 0 uint16_t getStreamPolicy(HostAttributeEntry *host_entry); char isStreamPolicySet(HostAttributeEntry *host_entry); #define SFAT_UNKNOWN_FRAG_POLICY 0 uint16_t getFragPolicy(HostAttributeEntry *host_entry); char isFragPolicySet(HostAttributeEntry *host_entry); #endif /* _SFTARGET_HOSTENTRY_H_ */ snort-2.9.20/src/target-based/sftarget_protocol_reference.h0000644000175000017500000000335014241077431022155 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2006-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Author: Steven Sturges * sftarget_protocol_reference.h */ #ifndef SFTARGET_PROTOCOL_REFERENCE_TABLE_H_ #define SFTARGET_PROTOCOL_REFERENCE_TABLE_H_ #include "decode.h" #include "util.h" #define MAX_PROTOCOL_ORDINAL 8192 typedef struct _SFTargetProtocolReference { char name[STD_BUF]; int16_t ordinal; } SFTargetProtocolReference; extern int16_t protocolReferenceTCP; extern int16_t protocolReferenceUDP; extern int16_t protocolReferenceICMP; void InitializeProtocolReferenceTable(void); void FreeProtoocolReferenceTable(void); int16_t AddProtocolReference(const char *protocol); int16_t FindProtocolReference(const char *protocol); #if defined(FEAT_OPEN_APPID) const char * FindProtocolName(int16_t protocol_id); #endif /* defined(FEAT_OPEN_APPID) */ int16_t GetProtocolReference(Packet *p); #endif /* SFTARGET_PROTOCOL_REFERENCE_TABLE_H_ */ snort-2.9.20/src/mempool.h0000644000175000017500000000607114241076640013511 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _MEMPOOL_H #define _MEMPOOL_H typedef struct _MemBucket { struct _MemBucket* next; struct _MemBucket* prev; void *data; size_t obj_size; void *scbPtr; } MemBucket; typedef struct _MemPool { MemBucket* used_list_head; MemBucket* used_list_tail; MemBucket* free_list; size_t obj_size; size_t max_memory; size_t used_memory; size_t free_memory; } MemPool; MemBucket* mempool_get_lru_bucket(MemPool *memory_pool); unsigned mempool_prune_freelist(MemPool *memory_pool, size_t new_max_memory, unsigned maxWork); int mempool_init(MemPool *mempool, unsigned num_objects, size_t obj_size); int mempool_init_optional_prealloc(MemPool *mempool, unsigned num_objects, size_t obj_size, int prealloc); int mempool_destroy(MemPool *mempool); MemBucket *mempool_alloc(MemPool *mempool); MemBucket *mempool_force_alloc(MemPool *mempool); void mempool_free(MemPool *mempool, MemBucket *obj); int mempool_free_bucket(MemPool *mempool); int mempool_clean(MemPool *mempool); static inline MemBucket* mempool_oldestUsedBucket( MemPool *mempool ) { return mempool->used_list_head; } static inline unsigned int mempool_numUsedBuckets( MemPool *mempool ) { return (unsigned int)((mempool->used_memory + mempool->obj_size - 1) / mempool->obj_size); } static inline unsigned int mempool_numFreeBuckets( MemPool *mempool ) { return (unsigned int)((mempool->free_memory + mempool->obj_size - 1) / mempool->obj_size); } static inline unsigned int mempool_numTotalBuckets( MemPool *mempool ) { return (unsigned int)((mempool->used_memory + mempool->free_memory + mempool->obj_size - 1) / mempool->obj_size); } static inline void mempool_setNumObjects( MemPool *mempool, unsigned num_objects ) { mempool->max_memory = num_objects * mempool->obj_size; } static inline void mempool_setObjectSize( MemPool *mempool, unsigned num_objects, size_t obj_size ) { mempool->obj_size = obj_size; mempool->max_memory = num_objects * obj_size; } #endif /* _MEMPOOL_H */ snort-2.9.20/src/Makefile.in0000644000175000017500000006662214242725545013753 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = snort$(EXEEXT) @BUILD_CONTROL_SOCKET_TRUE@am__append_1 = dump.c dump.h @BUILD_SIDE_CHANNEL_TRUE@am__append_2 = \ @BUILD_SIDE_CHANNEL_TRUE@side-channel/libsidechannel.a \ @BUILD_SIDE_CHANNEL_TRUE@side-channel/plugins/libsscm.a @BUILD_SIDE_CHANNEL_TRUE@am__append_3 = side-channel @BUILD_SNORT_RELOAD_TRUE@am__append_4 = reload-adjust/libreload_adjust.a @BUILD_SNORT_RELOAD_TRUE@am__append_5 = reload-adjust subdir = src ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" PROGRAMS = $(bin_PROGRAMS) am__snort_SOURCES_DIST = cdefs.h event.h generators.h sf_protocols.h \ plugin_enum.h rules.h treenodes.h checksum.h debug.c \ snort_debug.h decode.c decode.h encode.c encode.h active.c \ active.h log.c log.h mstring.c mstring.h hashstring.c \ hashstring.h parser.c parser.h profiler.c profiler.h \ plugbase.c plugbase.h preprocids.h snort.c snort.h build.h \ snprintf.c snprintf.h strlcatu.c strlcatu.h strlcpyu.c \ strlcpyu.h tag.c tag.h util.c util.h detect.c detect.h \ signature.c signature.h mempool.c mempool.h sf_sdlist.c \ sf_sdlist.h sf_sdlist_types.h fpcreate.c fpcreate.h fpdetect.c \ fpdetect.h pcrm.c pcrm.h snort_bounds.h byte_extract.c \ byte_extract.h timersub.h spo_plugbase.h sfthreshold.c \ sfthreshold.h packet_time.c packet_time.h event_wrapper.c \ event_wrapper.h event_queue.c event_queue.h ipv6_port.h ppm.c \ ppm.h pcap_pkthdr32.h cpuclock.h sf_types.h log_text.c \ log_text.h detection_filter.c detection_filter.h \ detection_util.c detection_util.h rate_filter.c rate_filter.h \ pkt_tracer.c pkt_tracer.h obfuscation.c obfuscation.h \ rule_option_types.h sfdaq.c sfdaq.h reload.c reload.h \ reload_api.h idle_processing.c idle_processing.h \ idle_processing_funcs.h appIdApi.h smtp_api.h reg_test.h \ reg_test.c memory_stats.h memory_stats.c dump.c dump.h @BUILD_SNPRINTF_TRUE@am__objects_1 = snprintf.$(OBJEXT) @BUILD_CONTROL_SOCKET_TRUE@am__objects_2 = dump.$(OBJEXT) am_snort_OBJECTS = debug.$(OBJEXT) decode.$(OBJEXT) encode.$(OBJEXT) \ active.$(OBJEXT) log.$(OBJEXT) mstring.$(OBJEXT) \ hashstring.$(OBJEXT) parser.$(OBJEXT) profiler.$(OBJEXT) \ plugbase.$(OBJEXT) snort.$(OBJEXT) $(am__objects_1) \ strlcatu.$(OBJEXT) strlcpyu.$(OBJEXT) tag.$(OBJEXT) \ util.$(OBJEXT) detect.$(OBJEXT) signature.$(OBJEXT) \ mempool.$(OBJEXT) sf_sdlist.$(OBJEXT) fpcreate.$(OBJEXT) \ fpdetect.$(OBJEXT) pcrm.$(OBJEXT) byte_extract.$(OBJEXT) \ sfthreshold.$(OBJEXT) packet_time.$(OBJEXT) \ event_wrapper.$(OBJEXT) event_queue.$(OBJEXT) ppm.$(OBJEXT) \ log_text.$(OBJEXT) detection_filter.$(OBJEXT) \ detection_util.$(OBJEXT) rate_filter.$(OBJEXT) \ pkt_tracer.$(OBJEXT) obfuscation.$(OBJEXT) sfdaq.$(OBJEXT) \ reload.$(OBJEXT) idle_processing.$(OBJEXT) reg_test.$(OBJEXT) \ memory_stats.$(OBJEXT) $(am__objects_2) snort_OBJECTS = $(am_snort_OBJECTS) snort_DEPENDENCIES = output-plugins/libspo.a \ detection-plugins/libspd.a dynamic-plugins/libdynamic.a \ dynamic-output/plugins/liboutput.a preprocessors/libspp.a \ parser/libparser.a target-based/libtarget_based.a \ preprocessors/HttpInspect/libhttp_inspect.a \ preprocessors/Session/libsession.a \ preprocessors/Stream6/libstream6.a sfutil/libsfutil.a \ control/libsfcontrol.a file-process/libfileAPI.a \ file-process/libs/libfile.a $(am__append_2) $(am__append_4) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(snort_SOURCES) DIST_SOURCES = $(am__snort_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = sfutil win32 output-plugins detection-plugins \ dynamic-plugins preprocessors parser dynamic-preprocessors \ dynamic-output target-based control file-process \ dynamic-examples side-channel reload-adjust am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies @BUILD_SNPRINTF_TRUE@SNPRINTF_SOURCES = snprintf.c snprintf.h snort_SOURCES = cdefs.h event.h generators.h sf_protocols.h \ plugin_enum.h rules.h treenodes.h checksum.h debug.c \ snort_debug.h decode.c decode.h encode.c encode.h active.c \ active.h log.c log.h mstring.c mstring.h hashstring.c \ hashstring.h parser.c parser.h profiler.c profiler.h \ plugbase.c plugbase.h preprocids.h snort.c snort.h build.h \ $(SNPRINTF_SOURCES) strlcatu.c strlcatu.h strlcpyu.c \ strlcpyu.h tag.c tag.h util.c util.h detect.c detect.h \ signature.c signature.h mempool.c mempool.h sf_sdlist.c \ sf_sdlist.h sf_sdlist_types.h fpcreate.c fpcreate.h fpdetect.c \ fpdetect.h pcrm.c pcrm.h snort_bounds.h byte_extract.c \ byte_extract.h timersub.h spo_plugbase.h sfthreshold.c \ sfthreshold.h packet_time.c packet_time.h event_wrapper.c \ event_wrapper.h event_queue.c event_queue.h ipv6_port.h ppm.c \ ppm.h pcap_pkthdr32.h cpuclock.h sf_types.h log_text.c \ log_text.h detection_filter.c detection_filter.h \ detection_util.c detection_util.h rate_filter.c rate_filter.h \ pkt_tracer.c pkt_tracer.h obfuscation.c obfuscation.h \ rule_option_types.h sfdaq.c sfdaq.h reload.c reload.h \ reload_api.h idle_processing.c idle_processing.h \ idle_processing_funcs.h appIdApi.h smtp_api.h reg_test.h \ reg_test.c memory_stats.h memory_stats.c $(am__append_1) snort_LDADD = output-plugins/libspo.a detection-plugins/libspd.a \ dynamic-plugins/libdynamic.a \ dynamic-output/plugins/liboutput.a preprocessors/libspp.a \ parser/libparser.a target-based/libtarget_based.a \ preprocessors/HttpInspect/libhttp_inspect.a \ preprocessors/Session/libsession.a \ preprocessors/Stream6/libstream6.a sfutil/libsfutil.a \ control/libsfcontrol.a file-process/libfileAPI.a \ file-process/libs/libfile.a $(am__append_2) $(am__append_4) @BUILD_DYNAMIC_EXAMPLES_TRUE@EXAMPLES_DIR = dynamic-examples SUBDIRS = sfutil win32 output-plugins detection-plugins \ dynamic-plugins preprocessors parser dynamic-preprocessors \ dynamic-output target-based control file-process \ $(EXAMPLES_DIR) $(am__append_3) $(am__append_5) all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ if test -n "$$list"; then \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ || test -f $$p1 \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ } \ ; done uninstall-binPROGRAMS: @$(NORMAL_UNINSTALL) @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files clean-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list snort$(EXEEXT): $(snort_OBJECTS) $(snort_DEPENDENCIES) $(EXTRA_snort_DEPENDENCIES) @rm -f snort$(EXEEXT) $(AM_V_CCLD)$(LINK) $(snort_OBJECTS) $(snort_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(PROGRAMS) installdirs: installdirs-recursive installdirs-am: for dir in "$(DESTDIR)$(bindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-binPROGRAMS install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: uninstall-binPROGRAMS .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-binPROGRAMS clean-generic clean-libtool \ cscopelist-am ctags ctags-am distclean distclean-compile \ distclean-generic distclean-libtool distclean-tags distdir dvi \ dvi-am html html-am info info-am install install-am \ install-binPROGRAMS install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/sf_sdlist.h0000644000175000017500000000525014241077171014031 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 2002 Martin Roesch ** ** This is hi ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef _SF_SDLIST #define _SF_SDLIST #include "sf_sdlist_types.h" /* based off Linked List structure p. 57 _Mastering algorithms in C_ * * Differs from sf_list by using static listitem blocks. * * Use mempool as the interface to this code instead of trying to use it directly * */ /* initialize a DList */ int sf_sdlist_init(sfSDList *list, void (*destroy)(void *data)); /* delete an DList */ int sf_sdlist_delete(sfSDList *list); /* insert item, putting data in container */ int sf_sdlist_insert_next(sfSDList *list, SDListItem *item, void *data, SDListItem *container); /* remove the item after the item */ int sf_sdlist_remove_next(sfSDList *list, SDListItem *item); /* remove this item from the list */ int sf_sdlist_remove(sfSDList *list, SDListItem *item); /* append at the end of the list */ int sf_sdlist_append(sfSDList *list, void *data, SDListItem *container); void print_sdlist(sfSDList *list); // list functions that handle memory allocation for inserted list items int sf_sdlist_ins_next(sfSDList *list, SDListItem *item, const void *data); int sf_sdlist_ins_prev(sfSDList *list, SDListItem *item, const void *data); int sf_sdlist_rem_item(sfSDList *list, SDListItem *item, void **data); int sf_sdlist_purge(sfSDList *list); // macro implementation of simple doubly linked list operations #define sf_sdlist_size(list) ((list)->size) #define sf_sdlist_head(list) ((list)->head) #define sf_sdlist_tail(list) ((list)->tail) #define sf_sdlist_is_head(item) ((item)->prev == NULL ? 1 : 0) #define sf_sdlist_is_tail(item) ((item)->next == NULL ? 1 : 0) #define sf_sdlist_data(item) ((item)->data) #define sf_sdlist_next(item) ((item)->next) #define sf_sdlist_prev(item) ((item)->prev) #endif /* _SF_DLIST */ snort-2.9.20/src/smtp_api.h0000644000175000017500000000276314241077404013660 0ustar apoapo/* ** Copyright (C) 2021-2022 Cisco and/or its affiliates. All rights reserved. * ** This program is free software; you can redistribute it and/or modify * ** it under the terms of the GNU General Public License Version 2 as * ** published by the Free Software Foundation. You may not use, modify or * ** distribute this program under any other version of the GNU General * ** Public License. * ** * ** 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. * */ /**************************************************************************/ #ifndef __SMTP_API_H__ #define __SMTP_API_H__ typedef struct _smtp_api { int (*smtp_session_exists)(void *data); int (*smtp_get_file_name)(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int (*smtp_get_mail_from)(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int (*smtp_get_recv_to)(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int (*smtp_get_email_hdr)(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); } SmtpAPI; #endif snort-2.9.20/src/detect.h0000644000175000017500000001010414241074717013304 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* I N C L U D E S ************************************************/ #ifndef __DETECT_H__ #define __DETECT_H__ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "snort_debug.h" #include "decode.h" #include "rules.h" #include "treenodes.h" #include "parser.h" #include "plugbase.h" #include "log.h" #include "event.h" #include "sfutil/sfportobject.h" /* P R O T O T Y P E S ******************************************************/ extern int do_detect; extern int do_detect_content; extern uint16_t event_id; /* rule match action functions */ int PassAction(void); int ActivateAction(Packet *, OptTreeNode *, RuleTreeNode *, Event *); int AlertAction(Packet *, OptTreeNode *, RuleTreeNode *, Event *); int DropAction(Packet *, OptTreeNode *, RuleTreeNode *, Event *); int SDropAction(Packet *, OptTreeNode *, Event *); int DynamicAction(Packet *, OptTreeNode *, RuleTreeNode *, Event *); int LogAction(Packet *, OptTreeNode *, RuleTreeNode *, Event *); /* detection/manipulation funcs */ int Preprocess(Packet *); int Detect(Packet *); void CallOutputPlugins(Packet *); int EvalPacket(ListHead *, int, Packet * ); int EvalHeader(RuleTreeNode *, Packet *, int); int EvalOpts(OptTreeNode *, Packet *); void TriggerResponses(Packet *, OptTreeNode *); int CheckAddrPort(sfip_var_t *, PortObject* , Packet *, uint32_t, int); /* detection modules */ int CheckBidirectional(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckSrcIP(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckDstIP(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckSrcIPNotEq(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckDstIPNotEq(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckSrcPortEqual(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckDstPortEqual(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckSrcPortNotEq(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int CheckDstPortNotEq(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int RuleListEnd(Packet *, struct _RuleTreeNode *, RuleFpList *, int); int OptListEnd(void *option_data, Packet *p); void CallLogPlugins(Packet *, const char *, Event *); void CallAlertPlugins(Packet *, const char *, Event *); void CallLogFuncs(Packet *, const char *, ListHead *, Event *); void CallAlertFuncs(Packet *, const char *, ListHead *, Event *); static inline void DisableDetect( Packet *p ) { DisableAppPreprocessors( p ); do_detect_content = 0; } static inline void DisableAllDetect( Packet *p ) { DisableAppPreprocessors( p ); do_detect = do_detect_content = 0; } static inline void EnableContentDetect( void ) { do_detect_content = 1; } static inline void DisablePacketAnalysis( Packet *p ) { DisableAllPreprocessors ( p ); do_detect = do_detect_content = 0; } static inline void EnableContentPreprocDetection( Packet *p, PreprocEnableMask enabled_pps ) { EnableContentDetect(); EnablePreprocessors( p, enabled_pps ); } /* counter for number of times we evaluate rules. Used to * cache result of check for rule option tree nodes. */ extern uint64_t rule_eval_pkt_count; #endif /* __DETECT_H__ */ snort-2.9.20/src/hashstring.h0000644000175000017500000000234514241076622014213 0ustar apoapo /* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifndef __HASHSTRING_H__ #define __HASHSTRING_H__ #include "sf_sechash.h" /* D E F I N E S *******************************************************/ /* P R O T O T Y P E S *************************************************/ int hashSearchFixed(const char *, int, const Secure_Hash_Type type, const char *); #endif /* __HASHSTRING_H__ */ snort-2.9.20/src/preprocessors/0000755000175000017500000000000014242725713014600 5ustar apoaposnort-2.9.20/src/preprocessors/str_search.c0000644000175000017500000002201714241077127017101 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "str_search.h" #include "mpse.h" typedef struct tag_search { void *mpse; unsigned int max_len; int in_use; } t_search; static t_search *_mpse = NULL; static unsigned int _num_mpse=0; void SearchFreeId(unsigned int id); int SearchInit(unsigned int num) { unsigned int i; _num_mpse = num; _mpse = malloc(sizeof(t_search) * num); if ( _mpse == NULL ) return -1; for ( i = 0; i < num; i++ ) { _mpse[i].mpse = mpseNew(MPSE_AC_BNFA, MPSE_DONT_INCREMENT_GLOBAL_COUNT, NULL, NULL, NULL); if ( !_mpse[i].mpse ) return -1; _mpse[i].max_len = 0; _mpse[i].in_use=1; } return 0; } int SearchReInit(unsigned int i) { if ( _mpse[i].mpse != NULL ) mpseFree(_mpse[i].mpse); _mpse[i].mpse = mpseNew(MPSE_AC_BNFA, MPSE_DONT_INCREMENT_GLOBAL_COUNT, NULL, NULL, NULL); _mpse[i].max_len = 0; _mpse[i].in_use=1; if ( !_mpse[i].mpse ) return -1; return 0; } void SearchFree(void) { unsigned int i; if ( _mpse != NULL ) { for ( i = 0; i < _num_mpse; i++ ) { if ( _mpse[i].mpse != NULL ) mpseFree(_mpse[i].mpse); _mpse[i].in_use=0; } free(_mpse); } } void SearchFreeId(unsigned int id) { if ( id < _num_mpse && _mpse != NULL ) { if ( _mpse[id].mpse != NULL ) mpseFree(_mpse[id].mpse); _mpse[id].mpse=NULL; } } int SearchGetHandle(void) { unsigned int i; for ( i = 0; i < _num_mpse; i++ ) { if ( !_mpse[i].mpse && !_mpse[i].in_use ) { _mpse[i].in_use=1; return i; } } return -1; } int SearchPutHandle(unsigned int id) { if( (id > 0 && id < _num_mpse) && _mpse[id].in_use ) { SearchFreeId(id); _mpse[id].in_use=0; return 0; } return -1; } /* Do efficient search of data @param mpse_id specify which engine to use to search @param str string to search @param str_len length of string to search @param confine 1 means only search at beginning of string (confine to length of max search string) @param Match function callback when string match found */ int SearchFindString(unsigned int mpse_id, const char *str, unsigned int str_len, int confine, int (*Match) (void *, void *, int, void *, void *)) { int num; int start_state; if ( confine && _mpse[mpse_id].max_len != 0 ) { if ( _mpse[mpse_id].max_len < str_len ) { str_len = _mpse[mpse_id].max_len; } } start_state = 0; num = mpseSearch(_mpse[mpse_id].mpse, (unsigned char*)str, str_len, Match, (void *) str, &start_state); return num; } void SearchAdd(unsigned int mpse_id, const char *pat, unsigned int pat_len, int id) { mpseAddPattern(_mpse[mpse_id].mpse, (void *)pat, pat_len, 1, 0, 0, 0, (void *)(long) id, 0); if ( pat_len > _mpse[mpse_id].max_len ) _mpse[mpse_id].max_len = pat_len; } void SearchPrepPatterns(unsigned int mpse_id) { mpsePrepPatterns(_mpse[mpse_id].mpse, NULL, NULL); } /* * Instance Functions * * max_len is not handled by */ void * SearchInstanceNew(void) { return SearchInstanceNewEx(MPSE_AC_BNFA); } void * SearchInstanceNewEx(unsigned method) { t_search * search = malloc(sizeof(t_search)); if( !search ) return NULL; search->mpse = mpseNew(method, MPSE_DONT_INCREMENT_GLOBAL_COUNT, NULL, NULL, NULL); if (search-> mpse == NULL ) { free(search); return NULL; } search->in_use=1; search->max_len=0; return search; } void SearchInstanceFree( void * instance ) { t_search * search = (t_search*)instance; if( instance ) { mpseFree( search->mpse ); free( instance ); } } void SearchInstanceAdd( void*instance, const char *pat, unsigned int pat_len, int id) { t_search * search = (t_search*)instance; if( search && search->mpse ) mpseAddPattern( search->mpse, (void *)pat, pat_len, STR_SEARCH_CASE_INSENSITIVE, 0, 0, 0, (void *)(long) id, 0); if ( search && pat_len > search->max_len ) search->max_len = pat_len; } void SearchInstanceAddEx( void*instance, const char *pat, unsigned int pat_len, void* id, unsigned nocase) { t_search * search = (t_search*)instance; if( search && search->mpse ) mpseAddPattern( search->mpse, (void *)pat, pat_len, nocase, 0, 0, 0, id, 0); if ( search && pat_len > search->max_len ) search->max_len = pat_len; } void SearchInstancePrepPatterns(void * instance) { t_search * search = (t_search*)instance; if( search && search->mpse ) { mpsePrepPatterns( search->mpse, NULL, NULL); } } int SearchInstanceFindString(void * instance, const char *str, unsigned int str_len, int confine, int (*Match) (void *, void *, int, void *, void *)) { int num; int start_state = 0; t_search * search = (t_search*)instance; if ( confine && (search->max_len > 0) ) { if ( search->max_len < str_len ) { str_len = search->max_len; } } num = mpseSearch( search->mpse, (unsigned char*)str, str_len, Match, (void *)str, &start_state); return num; } int SearchInstanceFindStringAll(void * instance, const char *str, unsigned int str_len, int confine, int (*Match) (void *, void *, int, void *, void *), void *userData) { int num; int start_state = 0; t_search * search = (t_search*)instance; if ( confine && (search->max_len > 0) ) { if ( search->max_len < str_len ) { str_len = search->max_len; } } num = mpseSearchAll( search->mpse, (unsigned char*)str, str_len, Match, userData? userData:(void *)str, &start_state); return num; } int StatefulSearchInstanceFindString(void * instance, const char *str, unsigned int str_len, int confine, int (*Match) (void *, void *, int, void *, void *), int *state) { int num; t_search * search = (t_search*)instance; if ( confine && (search->max_len > 0) ) { if ( search->max_len < str_len ) { str_len = search->max_len; } } num = mpseSearch( search->mpse, (unsigned char*)str, str_len, Match, (void *) str, state); return num; } char *SearchInstanceFindStringEnd(char *match_ptr, int buflen, char *search_str, int search_len) { char *end; int i = 0; if(buflen < search_len) { i = i + (search_len - buflen); search_len = buflen; } end = match_ptr + search_len; for (;i < search_len;i++) { if (memcmp(match_ptr, search_str + i, (search_len - i) ) == 0) { end = match_ptr + search_len - i; break; } } return end; } /* API exported by this module */ SearchAPI searchAPI = { SearchInit, SearchReInit, SearchFree, SearchAdd, SearchPrepPatterns, SearchFindString, SearchFreeId, SearchGetHandle, SearchPutHandle, SearchInstanceNew, SearchInstanceNewEx, SearchInstanceFree, SearchInstanceAdd, SearchInstanceAddEx, SearchInstancePrepPatterns, SearchInstanceFindString, SearchInstanceFindStringAll, SearchInstanceFindStringEnd, StatefulSearchInstanceFindString, }; SearchAPI *search_api = &searchAPI; snort-2.9.20/src/preprocessors/perf.c0000644000175000017500000006253714241077052015710 0ustar apoapo/* ** $Id$ ** ** perf.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Dan Roelker ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** ** DESCRIPTION ** These are the basic functions that are needed to call performance ** functions. ** */ #include #include #include #include #include #include #ifndef WIN32 # include # include #endif /* WIN32 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "util.h" #include "perf.h" #include "sf_types.h" #include "decode.h" #include "snort.h" SFBASE sfBase; SFFLOW sfFlow; SFEVENT sfEvent; int perfmon_rotate_perf_file = 0; static uint32_t pkt_cnt = 0; #ifdef SNORT_RELOAD extern SFPERF* perfmon_config; PERFRELOAD_STATUS perfmon_reload_status = PERF_NOT_RELOADING; #endif static void UpdatePerfStats(SFPERF *, Packet *p); static bool CheckSampleInterval(SFPERF *, time_t); static inline bool sfCheckFileSize(FILE *, uint32_t); static inline void sfProcessBaseStats(SFPERF *); static inline void sfProcessFlowStats(SFPERF *); static inline void sfProcessFlowIpStats(SFPERF *); static inline void sfProcessEventStats(SFPERF *); static inline int sfRotateFlowIPStatsFile(SFPERF *); static int sfRotateFile(const char *, FILE *, const char *, uint32_t); void sfInitPerformanceStatistics(SFPERF *sfPerf) { memset(sfPerf, 0, sizeof(SFPERF)); sfPerf->sample_interval = 60; sfPerf->flow_max_port_to_track = 1023; sfPerf->perf_flags |= SFPERF_BASE; sfPerf->pkt_cnt = 10000; sfPerf->max_file_size = MAX_PERF_FILE_SIZE; sfPerf->flowip_memcap = 50*1024*1024; sfPerf->base_reset = 1; #ifdef LINUX_SMP sfInitProcPidStats(&(sfBase.sfProcPidStats)); #endif } static void WriteTimeStamp(FILE *fh, const char *action) { time_t curr_time = time(NULL); if (fh == NULL) return; fprintf(fh, "################################### " "Perfmon %s: pid=%u at=%.24s (%lu) " "###################################\n", action, getpid(), ctime(&curr_time), (unsigned long)curr_time); fflush(fh); } FILE * sfOpenBaseStatsFile(const char *file) { static bool start_up = true; FILE *fh = NULL; bool reload = false; #ifdef SNORT_RELAOD reload = perfmon_reload_status != PERF_NOT_RELOADING; #endif #ifndef WIN32 // This file needs to be readable by everyone mode_t old_umask = umask(022); #endif if (file != NULL) { // Append to the existing file if just starting up or reloading, otherwise we've // rotated so start a new one. bool append = start_up || reload; fh = fopen(file, append ? "a" : "w"); if (fh != NULL) { WriteTimeStamp(fh, append ? "start" : "rotate"); LogBasePerfHeader(fh); } } #ifndef WIN32 umask(old_umask); #endif if (start_up) start_up = false; return fh; } void sfCloseBaseStatsFile(SFPERF *sfPerf) { if (sfPerf->fh == NULL) return; WriteTimeStamp(sfPerf->fh, "stop"); fclose(sfPerf->fh); sfPerf->fh = NULL; } FILE * sfOpenFlowStatsFile(const char *file) { static bool start_up = true; FILE *fh = NULL; bool reload = false; #ifdef SNORT_RELOAD reload = perfmon_reload_status != PERF_NOT_RELOADING; #endif #ifndef WIN32 // This file needs to be readable by everyone mode_t old_umask = umask(022); #endif if (file != NULL) { // Append to the existing file if just starting up or reloading, otherwise we've // rotated so start a new one. bool append = start_up || reload; fh = fopen(file, append ? "a" : "w"); if (fh != NULL) { WriteTimeStamp(fh, append ? "start" : "rotate"); LogFlowPerfHeader(fh); } } #ifndef WIN32 umask(old_umask); #endif if (start_up) start_up = false; return fh; } void sfCloseFlowStatsFile(SFPERF *sfPerf) { if (sfPerf->flow_fh == NULL) return; WriteTimeStamp(sfPerf->flow_fh, "stop"); fclose(sfPerf->flow_fh); sfPerf->flow_fh = NULL; } FILE * sfOpenFlowIPStatsFile(const char *file) { static bool start_up = true; FILE *fh = NULL; bool reload = false; #ifdef SNORT_RELOAD reload = perfmon_reload_status != PERF_NOT_RELOADING; #endif #ifndef WIN32 // This file needs to be readable by everyone mode_t old_umask = umask(022); #endif if (file != NULL) { // Append to the existing file if just starting up or reloading, otherwise we've // rotated so start a new one. fh = fopen(file, start_up || reload ? "a" : "w"); } #ifndef WIN32 umask(old_umask); #endif if (start_up) start_up = false; return fh; } void sfCloseFlowIPStatsFile(SFPERF *sfPerf) { if (sfPerf->flowip_fh == NULL) return; fclose(sfPerf->flowip_fh); sfPerf->flowip_fh = NULL; } static int sfRotateFile(const char *old_file, FILE *old_fh, const char *rotate_prefix, uint32_t max_file_size) { time_t ts; struct tm *tm; char rotate_file[PATH_MAX]; char *path_ptr; int path_len = 0; struct stat file_stats; if (old_file == NULL) return -1; if (old_fh == NULL) { ErrorMessage("Perfmonitor: Performance stats file \"%s\" " "isn't open.\n", old_file); return -1; } // Close the current stats file if it's already open fclose(old_fh); old_fh = NULL; // Rename current stats file with yesterday's date #ifndef WIN32 path_ptr = strrchr(old_file, '/'); #else path_ptr = strrchr(old_file, '\\'); #endif if (path_ptr != NULL) { // Take the length of the file/path name up to the path separator and // add one to include path separator path_len = (path_ptr - old_file) + 1; } // Get current time, then subtract one day to get yesterday ts = time(NULL); ts -= (24*60*60); tm = localtime(&ts); // Create rotate file name based on path, optional prefix and date SnortSnprintf(rotate_file, PATH_MAX, "%.*s%s%s%d-%02d-%02d", path_len, old_file, (rotate_prefix != NULL) ? rotate_prefix : "", (rotate_prefix != NULL) ? "-" : "", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); // If the rotate file doesn't exist, just rename the old one to the new one if (stat(rotate_file, &file_stats) != 0) { if (rename(old_file, rotate_file) != 0) { ErrorMessage("Perfmonitor: Could not rename performance stats " "file from \"%s\" to \"%s\": %s.\n", old_file, rotate_file, strerror(errno)); } } else // Otherwise, if it does exist, append data from current stats file to it { char read_buf[4096]; size_t num_read, num_wrote; FILE *rotate_fh; int rotate_index = 0; char rotate_file_with_index[PATH_MAX]; #ifndef WIN32 // This file needs to be readable by everyone mode_t old_umask = umask(022); #endif do { do { rotate_index++; // Check to see if there are any files already rotated and indexed SnortSnprintf(rotate_file_with_index, PATH_MAX, "%s.%02d", rotate_file, rotate_index); } while (stat(rotate_file_with_index, &file_stats) == 0); // Subtract one to append to last existing file rotate_index--; if (rotate_index == 0) { rotate_file_with_index[0] = 0; rotate_fh = fopen(rotate_file, "a"); } else { SnortSnprintf(rotate_file_with_index, PATH_MAX, "%s.%02d", rotate_file, rotate_index); rotate_fh = fopen(rotate_file_with_index, "a"); } if (rotate_fh == NULL) { ErrorMessage("Perfmonitor: Could not open performance stats " "archive file \"%s\" for appending: %s.\n", *rotate_file_with_index ? rotate_file_with_index : rotate_file, strerror(errno)); break; } old_fh = fopen(old_file, "r"); if (old_fh == NULL) { ErrorMessage("Perfmonitor: Could not open performance stats file " "\"%s\" for reading to copy to archive \"%s\": %s.\n", old_file, (*rotate_file_with_index ? rotate_file_with_index : rotate_file), strerror(errno)); break; } while (!feof(old_fh)) { // This includes the newline from the file. if (fgets(read_buf, sizeof(read_buf), old_fh) == NULL) { if (feof(old_fh)) break; if (ferror(old_fh)) { // A read error occurred ErrorMessage("Perfmonitor: Error reading performance stats " "file \"%s\": %s.\n", old_file, strerror(errno)); break; } } num_read = strlen(read_buf); if (num_read > 0) { int rotate_fd = fileno(rotate_fh); if (fstat(rotate_fd, &file_stats) != 0) { ErrorMessage("Perfmonitor: Error getting file " "information for \"%s\": %s.\n", *rotate_file_with_index ? rotate_file_with_index : rotate_file, strerror(errno)); break; } if (((uint32_t)file_stats.st_size + num_read) > max_file_size) { fclose(rotate_fh); rotate_index++; // Create new file same as before but with an index added to the end SnortSnprintf(rotate_file_with_index, PATH_MAX, "%s.%02d", rotate_file, rotate_index); rotate_fh = fopen(rotate_file_with_index, "a"); if (rotate_fh == NULL) { ErrorMessage("Perfmonitor: Could not open performance " "stats archive file \"%s\" for writing: %s.\n", rotate_file_with_index, strerror(errno)); break; } } num_wrote = fprintf(rotate_fh, "%s", read_buf); if ((num_wrote != num_read) && ferror(rotate_fh)) { // A bad write occurred ErrorMessage("Perfmonitor: Error writing to performance " "stats archive file \"%s\": %s.\n", rotate_file, strerror(errno)); break; } fflush(rotate_fh); } } } while (0); if (rotate_fh != NULL) fclose(rotate_fh); if (old_fh != NULL) fclose(old_fh); #ifndef WIN32 umask(old_umask); #endif } return 0; } int sfRotateBaseStatsFile(SFPERF *sfPerf) { if ((sfPerf != NULL) && (sfPerf->file != NULL)) { int ret = sfRotateFile(sfPerf->file, sfPerf->fh, NULL, sfPerf->max_file_size); if (ret != 0) return ret; if ((sfPerf->fh = sfOpenBaseStatsFile(sfPerf->file)) == NULL) { FatalError("Cannot open performance stats file \"%s\": %s.\n", sfPerf->file, strerror(errno)); } } return 0; } int sfRotateFlowStatsFile(SFPERF *sfPerf) { if ((sfPerf != NULL) && (sfPerf->flow_file != NULL)) { int ret = sfRotateFile(sfPerf->flow_file, sfPerf->flow_fh, "flow", sfPerf->max_file_size); if (ret != 0) return ret; if ((sfPerf->flow_fh = sfOpenFlowStatsFile(sfPerf->flow_file)) == NULL) { FatalError("Perfmonitor: Cannot open flow stats file \"%s\": %s.\n", sfPerf->flow_file, strerror(errno)); } } return 0; } static inline int sfRotateFlowIPStatsFile(SFPERF *sfPerf) { if ((sfPerf != NULL) && (sfPerf->flowip_file != NULL)) { int ret = sfRotateFile(sfPerf->flowip_file, sfPerf->flowip_fh, "flow-ip", sfPerf->max_file_size); if (ret != 0) return ret; if ((sfPerf->flowip_fh = sfOpenFlowIPStatsFile(sfPerf->flowip_file)) == NULL) { FatalError("Perfmonitor: Cannot open flow-ip stats file \"%s\": %s.\n", sfPerf->flowip_file, strerror(errno)); } } return 0; } void sfPerformanceStats(SFPERF *sfPerf, Packet *p) { // Update stats first since other stats from various places like frag3 and // stream5 have been added. UpdatePerfStats(sfPerf, p); if ((sfPerf->perf_flags & SFPERF_TIME_COUNT) && !PacketIsRebuilt(p)) { pkt_cnt++; sfPerformanceStatsOOB(sfPerf, p->pkth->ts.tv_sec); } } void sfPerformanceStatsOOB(SFPERF *sfPerf, time_t curr_time) { if (sfPerf && pkt_cnt >= sfPerf->pkt_cnt) //For perfect alignment, sfPerf->pkt_count should be set to 0 in config { if (CheckSampleInterval(sfPerf, curr_time)) { pkt_cnt = 0; if (!(sfPerf->perf_flags & SFPERF_SUMMARY_BASE)) { if (sfPerf->perf_flags & SFPERF_BASE) { sfProcessBaseStats(sfPerf); InitBaseStats(&sfBase); } } if (!(sfPerf->perf_flags & SFPERF_SUMMARY_FLOW)) { if (sfPerf->perf_flags & SFPERF_FLOW) { sfProcessFlowStats(sfPerf); InitFlowStats(&sfFlow); } } if (!(sfPerf->perf_flags & SFPERF_SUMMARY_FLOWIP)) { if (sfPerf->perf_flags & SFPERF_FLOWIP) { sfProcessFlowIpStats(sfPerf); InitFlowIPStats(&sfFlow); } } if (!(sfPerf->perf_flags & SFPERF_SUMMARY_EVENT)) { if (sfPerf->perf_flags & SFPERF_EVENT) { sfProcessEventStats(sfPerf); InitEventStats(&sfEvent); } } } } } static bool CheckSampleInterval(SFPERF *sfPerf, time_t curr_time) { static time_t last_true = 0; //Initial alignment if (last_true == 0) { last_true = curr_time; last_true -= last_true % sfPerf->sample_interval; } //Dump on aligned time if possible. If not, get close and say we did. if (curr_time - last_true >= sfPerf->sample_interval) { curr_time -= curr_time % sfPerf->sample_interval; sfBase.time = curr_time; sfFlow.time = curr_time; last_true = curr_time; return true; } return false; } void InitPerfStats(SFPERF *sfPerf) { #ifdef LINUX_SMP memset(&sfBase, 0, offsetof(SFBASE, sfProcPidStats)); #else memset(&sfBase, 0, sizeof(SFBASE)); #endif memset(&sfFlow, 0, sizeof(SFFLOW)); memset(&sfEvent, 0, sizeof(SFEVENT)); if (sfPerf->perf_flags & SFPERF_BASE) InitBaseStats(&sfBase); if (sfPerf->perf_flags & SFPERF_FLOW) InitFlowStats(&sfFlow); if (sfPerf->perf_flags & SFPERF_FLOWIP) InitFlowIPStats(&sfFlow); if (sfPerf->perf_flags & SFPERF_EVENT) InitEventStats(&sfEvent); } static void UpdatePerfStats(SFPERF *sfPerf, Packet *p) { bool rebuilt = PacketIsRebuilt(p); if (sfPerf->perf_flags & SFPERF_BASE) UpdateBaseStats(&sfBase, p, rebuilt); if ((sfPerf->perf_flags & SFPERF_FLOW) && !rebuilt) UpdateFlowStats(&sfFlow, p); if ((sfPerf->perf_flags & SFPERF_FLOWIP) && IsIP(p) && !rebuilt) { SFSType type = SFS_TYPE_OTHER; if (p->tcph != NULL) type = SFS_TYPE_TCP; else if (p->udph != NULL) type = SFS_TYPE_UDP; UpdateFlowIPStats(&sfFlow, GET_SRC_IP(p), GET_DST_IP(p), p->pkth->caplen, type); } } static inline bool sfCheckFileSize(FILE *fh, uint32_t max_file_size) { int fd; struct stat file_stats; if (fh == NULL) return false; fd = fileno(fh); if ((fstat(fd, &file_stats) == 0) && ((uint32_t)file_stats.st_size >= max_file_size)) return true; return false; } static inline void sfProcessBaseStats(SFPERF *sfPerf) { if (!(sfPerf->perf_flags & SFPERF_BASE)) return; ProcessBaseStats(&sfBase, sfPerf->fh, sfPerf->perf_flags & SFPERF_CONSOLE, sfPerf->perf_flags & SFPERF_MAX_BASE_STATS); if ((sfPerf->fh != NULL) && sfCheckFileSize(sfPerf->fh, sfPerf->max_file_size)) { sfRotateBaseStatsFile(sfPerf); } } static inline void sfProcessFlowStats(SFPERF *sfPerf) { if (!(sfPerf->perf_flags & SFPERF_FLOW)) return; ProcessFlowStats(&sfFlow, sfPerf->flow_fh, sfPerf->perf_flags & SFPERF_CONSOLE); if ((sfPerf->flow_fh != NULL) && sfCheckFileSize(sfPerf->flow_fh, sfPerf->max_file_size)) { sfRotateFlowStatsFile(sfPerf); } } static inline void sfProcessFlowIpStats(SFPERF *sfPerf) { if (!(sfPerf->perf_flags & SFPERF_FLOWIP)) return; ProcessFlowIPStats(&sfFlow, sfPerf->flowip_fh, sfPerf->perf_flags & SFPERF_CONSOLE); if ((sfPerf->flowip_fh != NULL) && sfCheckFileSize(sfPerf->flowip_fh, sfPerf->max_file_size)) { sfRotateFlowIPStatsFile(sfPerf); } } static inline void sfProcessEventStats(SFPERF *sfPerf) { if (!(sfPerf->perf_flags & SFPERF_EVENT)) return; if (sfPerf->perf_flags & SFPERF_CONSOLE) ProcessEventStats(&sfEvent); } void sfPerfStatsSummary(SFPERF *sfPerf) { if (sfPerf == NULL) return; if (sfPerf->perf_flags & SFPERF_SUMMARY_BASE) sfProcessBaseStats(sfPerf); if (sfPerf->perf_flags & SFPERF_SUMMARY_FLOW) sfProcessFlowStats(sfPerf); if (sfPerf->perf_flags & SFPERF_SUMMARY_FLOWIP) sfProcessFlowIpStats(sfPerf); if (sfPerf->perf_flags & SFPERF_SUMMARY_EVENT) sfProcessEventStats(sfPerf); } static void sfInitBaseStats(SFPERF *sfPerf) { if (sfPerf->perf_flags & SFPERF_BASE) InitBaseStats(&sfBase); } static void sfInitFlowStats(SFPERF *sfPerf) { if (sfPerf->perf_flags & SFPERF_FLOW) InitFlowStats(&sfFlow); } static void sfInitFlowIPStats(SFPERF *sfPerf) { if (sfPerf->perf_flags & SFPERF_FLOWIP) InitFlowIPStats(&sfFlow); } static void sfInitEventStats(SFPERF *sfPerf) { if (sfPerf->perf_flags & SFPERF_EVENT) InitEventStats(&sfEvent); } static void sfFreeFlowStats(SFPERF *sfPerf) { FreeFlowStats(&sfFlow); } static void sfFreeFlowIPStats(SFPERF *sfPerf) { FreeFlowIPStats(&sfFlow); } #ifdef SNORT_RELOAD //this function determines what is necessary to swap configs and keep stats and files synchronized //it will perform the actions necessary durring the appropriate stage of ReloadSwap // or you can force it to perform all actions by setting fullSync to a non-zero value // //if fullSync is set to 0 then actions are taken under the following conditions //If files need to be opened they are opened durring ReloadVerify //If stats need to be written to files and flushed it is done durring ReloadSwap //If files need to be closed it is done durring ReloadSwapFree static void syncStats( SFPERF *currentSFPerf, SFPERF *newSFPerf, int flag, int summaryFlag, //if summaryFlag is set then flag is set char *currentFile, char *newFile, FILE **currentFH, FILE **newFH, void (*processFunc)(SFPERF *), void (*initFunc)(SFPERF *), void (*freeFunc)(SFPERF *), FILE* (*openFileFunc)(const char *), void (*closeFileFunc)(SFPERF *), int fullSync) { int old_flags = currentSFPerf->perf_flags; int new_flags = newSFPerf->perf_flags; int config_flags = old_flags | new_flags; //summaryFlag implies that flag is set //otherwise code breaks <- I got this implication from other sets of code (i.e. InitPerfStats) //these stats were recorded, but are not recorded in new config if (old_flags & flag && !(new_flags & flag)) { //we need to flush the stats at the correct time if (perfmon_reload_status == PERF_RELOAD_SWAP || fullSync) //write stats for last period (or summary) (*processFunc)(currentSFPerf); //only close files in ReloadSwapFree to avoid latencies if (perfmon_reload_status == PERF_RELOAD_SWAP_FREE || fullSync) { //not every stat group has a file if (closeFileFunc != NULL) (*closeFileFunc)(currentSFPerf); //not every stat group uses dynamic memory if (freeFunc != NULL) (*freeFunc)(currentSFPerf); } } //these stats were not recorded, but will be recorded in new config else if ( !(old_flags & flag) && new_flags & flag ) { if (perfmon_reload_status == PERF_RELOAD_VERIFY || fullSync) { //init/reset stats (*initFunc)(newSFPerf); //open up new file if (openFileFunc) *newFH = (*openFileFunc)(newFile); } } // both configs have flag else if (config_flags & flag) { //one config is not summary if (!(old_flags & summaryFlag && new_flags & summaryFlag)) { if (perfmon_reload_status == PERF_RELOAD_SWAP || fullSync) { //log and clear stats (*processFunc)(currentSFPerf); (*initFunc)(currentSFPerf); } } //stat group must have log files if ( openFileFunc != NULL && closeFileFunc != NULL) { //the new file has a new name if ( newFile != NULL && (currentFile == NULL || strcmp(currentFile,newFile) != 0)) { if (perfmon_reload_status == PERF_RELOAD_VERIFY || fullSync) *newFH = (*openFileFunc)(newFile); if (perfmon_reload_status == PERF_RELOAD_SWAP_FREE || fullSync) (*closeFileFunc)(currentSFPerf); } //use the same file else if (*currentFH != NULL) { if (perfmon_reload_status == PERF_RELOAD_SWAP || fullSync) *newFH = *currentFH; } } } //else neither config has stats logged } //you can have SFPERF_SUMMARY_FLAG without a SFPERF_FLAG, but //perfmon acts as though neither flag is set and doesn't do any logging //for those statisitcs void syncAllStats(SFPERF *currentSFPerf, SFPERF *newSFPerf) { if (currentSFPerf == NULL || newSFPerf == NULL) return; //base stats syncStats( currentSFPerf, newSFPerf, SFPERF_BASE, SFPERF_SUMMARY_BASE, currentSFPerf->file, newSFPerf->file, &(currentSFPerf->fh), &(newSFPerf->fh), &sfProcessBaseStats, &sfInitBaseStats, NULL, &sfOpenBaseStatsFile, &sfCloseBaseStatsFile, 0); //flow stats syncStats( currentSFPerf,newSFPerf, SFPERF_FLOW, SFPERF_SUMMARY_FLOW, currentSFPerf->flow_file, newSFPerf->flow_file, &(currentSFPerf->flow_fh), &(newSFPerf->flow_fh), &sfProcessFlowStats, &sfInitFlowStats, &sfFreeFlowStats, &sfOpenFlowStatsFile, &sfCloseFlowStatsFile, 0); //flowip stats syncStats( currentSFPerf, newSFPerf, SFPERF_FLOWIP, SFPERF_SUMMARY_FLOWIP, currentSFPerf->flowip_file, newSFPerf->flowip_file, &(currentSFPerf->flowip_fh), &(newSFPerf->flowip_fh), &sfProcessFlowIpStats, &sfInitFlowIPStats, &sfFreeFlowIPStats, &sfOpenFlowIPStatsFile, &sfCloseFlowIPStatsFile, 0); //event stats syncStats( currentSFPerf, newSFPerf, SFPERF_EVENT, SFPERF_SUMMARY_EVENT, NULL, NULL, NULL, NULL, &sfProcessEventStats, &sfInitEventStats, NULL, NULL, NULL, 0); } #endif snort-2.9.20/src/preprocessors/sfprocpidstats.c0000644000175000017500000002010014241077074020004 0ustar apoapo/* ** $Id$ ** ** sfprocpidstats.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Dan Roelker ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** ** DESCRIPTION ** This file gets the correct CPU usage for SMP linux machines. ** */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sfprocpidstats.h" #ifdef LINUX_SMP #include #include #include #include #include #include #include #include "snort.h" #include "util.h" #define PROC_STAT "/proc/stat" #define PROC_SELF_CPU "/proc/self/cpu" #define PROC_SELF_STAT "/proc/self/stat" typedef struct _USERSYS { u_long user; u_long sys; u_long idle; } USERSYS; static int giCPUs = 1; static USERSYS *gpStatCPUs = NULL; static USERSYS *gpStatCPUs_2 = NULL; static FILE *proc_stat; static int GetProcStatCpu(USERSYS *pStatCPUs, int iCPUs) { int iRet; int iCtr; u_long ulUser; u_long ulNice; u_long ulSys; u_long ulIdle; char buf[256]; rewind(proc_stat); /* ** Read the total CPU usage, don't use right now. ** ** But we do want to read it if there is only one CPU. */ if(iCPUs != 1) { if(!fgets(buf, sizeof(buf), proc_stat)) return -1; } /* ** Read the individual CPU usages. This tells us where ** sniffing and snorting is occurring. */ for(iCtr = 0; iCtr < iCPUs; iCtr++) { if(!fgets(buf, sizeof(buf), proc_stat)) return -1; iRet = sscanf(buf, "%*s %lu %lu %lu %lu", &ulUser, &ulNice, &ulSys, &ulIdle); if(iRet == EOF || iRet < 4) return -1; pStatCPUs[iCtr].user = ulUser + ulNice; pStatCPUs[iCtr].sys = ulSys; pStatCPUs[iCtr].idle = ulIdle; } return 0; } static int GetCpuNum(void) { int iRet; int iCPUs = 0; char acCpuName[10+1]; char buf[256]; rewind(proc_stat); while(1) { if(!fgets(buf, sizeof(buf), proc_stat)) return 0; iRet = sscanf(buf, "%10s %*u %*u %*u %*u", acCpuName); if(errno == ERANGE) errno = 0; if(iRet < 1 || iRet == EOF ) { return 0; } acCpuName[sizeof(acCpuName)-1] = 0x00; if(strncmp(acCpuName, "cpu", 3)) { break; } iCPUs++; } /* ** If there are more then one CPU, then we subtract one because ** the first CPU entry combines all CPUs. This should be ** backward compatible with 2.2 not compiled with SMP support. */ if(iCPUs > 1) iCPUs--; return iCPUs; } int sfInitProcPidStats(SFPROCPIDSTATS *sfProcPidStats) { bool skipCpuStat = (bool)(snort_conf->suppress_config_log); /* Do not re-allocate memory */ if (gpStatCPUs != NULL) return 0; proc_stat = fopen(PROC_STAT, "r"); if(!proc_stat) { FatalError("PERFMONITOR: Can't open %s.", PROC_STAT); } giCPUs = GetCpuNum(); if(giCPUs <= 0) { FatalError("PERFMONITOR: Error reading CPUs from %s.", PROC_STAT); } if(!skipCpuStat) { gpStatCPUs = (USERSYS *)calloc(giCPUs, sizeof(USERSYS)); if(!gpStatCPUs) FatalError("PERFMONITOR: Error allocating CPU mem."); gpStatCPUs_2 = (USERSYS *)calloc(giCPUs, sizeof(USERSYS)); if(!gpStatCPUs_2) FatalError("PERFMONITOR: Error allocating CPU mem."); } /* ** Allocate for sfProcPidStats CPUs */ sfProcPidStats->SysCPUs = (CPUSTAT *)calloc(giCPUs, sizeof(CPUSTAT)); if(!sfProcPidStats->SysCPUs) FatalError("PERFMONITOR: Error allocating SysCPU mem."); sfProcPidStats->iCPUs = giCPUs; if(!skipCpuStat) { if(GetProcStatCpu(gpStatCPUs, giCPUs)) FatalError("PERFMONITOR: Error while reading '%s'.", PROC_STAT); } fclose(proc_stat); return 0; } void FreeProcPidStats(SFPROCPIDSTATS *sfProcPidStats) { if (gpStatCPUs != NULL) { free(gpStatCPUs); gpStatCPUs = NULL; } if (gpStatCPUs_2 != NULL) { free(gpStatCPUs_2); gpStatCPUs_2 = NULL; } if (sfProcPidStats->SysCPUs != NULL) { free(sfProcPidStats->SysCPUs); sfProcPidStats->SysCPUs = NULL; } } int sfProcessProcPidStats(SFPROCPIDSTATS *sfProcPidStats) { static int iError = 0; int iCtr; bool skipCpuStat = (bool)(snort_conf->suppress_config_log); u_long ulCPUjiffies; if(skipCpuStat) { memset(sfProcPidStats->SysCPUs,0,giCPUs*sizeof(CPUSTAT)); return 0; } proc_stat = fopen(PROC_STAT, "r"); if(!proc_stat) { if(!iError) { ErrorMessage("PERFMONITOR ERROR: Cannot open %s.", PROC_STAT); iError = 1; } return -1; } if(GetProcStatCpu(gpStatCPUs_2, giCPUs)) { if(!iError) { ErrorMessage("PERFMONITOR ERROR: Error while reading '%s'.", PROC_STAT); iError = 1; } return -1; } fclose(proc_stat); /* ** SysCPUs (The system's CPU usage, like top gives you) */ for(iCtr = 0; iCtr < giCPUs; iCtr++) { ulCPUjiffies = (gpStatCPUs_2[iCtr].user - gpStatCPUs[iCtr].user) + (gpStatCPUs_2[iCtr].sys - gpStatCPUs[iCtr].sys) + (gpStatCPUs_2[iCtr].idle - gpStatCPUs[iCtr].idle); if(gpStatCPUs_2[iCtr].user > gpStatCPUs[iCtr].user) { sfProcPidStats->SysCPUs[iCtr].user = (((double)(gpStatCPUs_2[iCtr].user - gpStatCPUs[iCtr].user)) / ulCPUjiffies) * 100.0; if(sfProcPidStats->SysCPUs[iCtr].user < .01) { sfProcPidStats->SysCPUs[iCtr].user = 0; } } else { sfProcPidStats->SysCPUs[iCtr].user = 0; } if(gpStatCPUs_2[iCtr].sys > gpStatCPUs[iCtr].sys) { sfProcPidStats->SysCPUs[iCtr].sys = (((double)(gpStatCPUs_2[iCtr].sys - gpStatCPUs[iCtr].sys)) / ulCPUjiffies) * 100.0; if(sfProcPidStats->SysCPUs[iCtr].sys < .01) { sfProcPidStats->SysCPUs[iCtr].sys = 0; } } else { sfProcPidStats->SysCPUs[iCtr].sys = 0; } if(gpStatCPUs_2[iCtr].idle > gpStatCPUs[iCtr].idle) { sfProcPidStats->SysCPUs[iCtr].idle = (((double)(gpStatCPUs_2[iCtr].idle - gpStatCPUs[iCtr].idle)) / ulCPUjiffies) * 100.0; if(sfProcPidStats->SysCPUs[iCtr].idle < .01) { sfProcPidStats->SysCPUs[iCtr].idle = 0; } } else { sfProcPidStats->SysCPUs[iCtr].idle = 0; } /* ** Set statistics for next processing. */ gpStatCPUs[iCtr].user = gpStatCPUs_2[iCtr].user; gpStatCPUs[iCtr].sys = gpStatCPUs_2[iCtr].sys; gpStatCPUs[iCtr].idle = gpStatCPUs_2[iCtr].idle; } return 0; } #endif snort-2.9.20/src/preprocessors/spp_sfportscan.h0000644000175000017500000000242114241077123020006 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* ** @file spp_sfportscan.h ** ** @author Daniel Roelker ** ** @brief APIs for Snort Portscan */ #ifndef __SPP_SFPORTSCAN_H__ #define __SPP_SFPORTSCAN_H__ void SetupSfPortscan(void); #endif snort-2.9.20/src/preprocessors/spp_normalize.c0000644000175000017500000006524014241077112017625 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2010-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ #ifdef NORMALIZER #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "active.h" #include "mstring.h" #include "normalize.h" #include "parser.h" #include "plugbase.h" #include "profiler.h" #include "sf_types.h" #include "sfPolicy.h" #include "snort.h" #include "spp_normalize.h" #include "snort_stream_tcp.h" // Priority for Normalize preproc #define PP_NORMALIZE_PRIORITY PRIORITY_CORE + PP_CORE_ORDER_NORML static tSfPolicyUserContextId base_set = NULL; #ifdef PERF_PROFILING PreprocStats norm_perf_stats; #endif static void Preproc_Execute(Packet* , void*); static void Preproc_CleanExit(int, void*); static void Preproc_Reset(int, void*); static void Preproc_PostConfigInit(struct _SnortConfig *, void*); static int Preproc_CheckConfig (struct _SnortConfig *); static void Preproc_ResetStats(int, void*); static void Preproc_PrintStats(int); static void Preproc_Install(struct _SnortConfig *); static void Init_IP4(struct _SnortConfig *, char*); static void Init_ICMP4(struct _SnortConfig *, char*); static void Init_IP6(struct _SnortConfig *, char*); static void Init_ICMP6(struct _SnortConfig *, char*); static void Init_TCP(struct _SnortConfig *, char*); static void Parse_IP4(struct _SnortConfig *, NormalizerContext*, char*); static void Parse_ICMP4(NormalizerContext*, char*); static void Parse_IP6(struct _SnortConfig *, NormalizerContext*, char*); static void Parse_ICMP6(NormalizerContext*, char*); static void Parse_TCP(NormalizerContext*, char*); static void Print_IP4(struct _SnortConfig *, const NormalizerContext*); static void Print_ICMP4(const NormalizerContext*); static void Print_IP6(struct _SnortConfig *, const NormalizerContext*); static void Print_ICMP6(const NormalizerContext*); static void Print_TCP(const NormalizerContext*); #ifdef SNORT_RELOAD static void Reload_IP4(struct _SnortConfig *, char*, void **); static void Reload_ICMP4(struct _SnortConfig *, char*, void **); static void Reload_IP6(struct _SnortConfig *, char*, void **); static void Reload_ICMP6(struct _SnortConfig *, char*, void **); static void Reload_TCP(struct _SnortConfig *, char*, void **); static int Reload_Verify(struct _SnortConfig *, void *); static void* Reload_Swap(struct _SnortConfig *, void *); static void Reload_Free(void*); #endif #ifdef SNORT_RELOAD #define NORM_FUNCS(p) Init_ ## p, Reload_ ## p, Reload_Verify, Reload_Swap, Reload_Free #else #define NORM_FUNCS(p) Init_ ## p #endif void SetupNormalizer (void) { RegisterPreprocessor("normalize_ip4", NORM_FUNCS(IP4)); RegisterPreprocessor("normalize_icmp4", NORM_FUNCS(ICMP4)); RegisterPreprocessor("normalize_ip6", NORM_FUNCS(IP6)); RegisterPreprocessor("normalize_icmp6", NORM_FUNCS(ICMP6)); RegisterPreprocessor("normalize_tcp", NORM_FUNCS(TCP)); } //------------------------------------------------------------------------- // basic initialization stuff //------------------------------------------------------------------------- #define PROTO_BITS (PROTO_BIT__IP|PROTO_BIT__ICMP|PROTO_BIT__TCP) static NormalizerContext* Init_GetContext (struct _SnortConfig *sc) { NormalizerContext* pc = NULL; tSfPolicyId policy_id = getParserPolicy(sc); if ( ScNapPassiveModeNewConf(sc) ) return NULL; if ( !base_set ) { base_set = sfPolicyConfigCreate(); Preproc_Install(sc); } sc->normalizer_set = true; sfPolicyUserPolicySet(base_set, policy_id); pc = sfPolicyUserDataGetCurrent(base_set); if ( !pc ) { pc = (NormalizerContext* )SnortAlloc(sizeof(NormalizerContext)); sfPolicyUserDataSetCurrent(base_set, pc); if( pc->regFunc != reload) { AddFuncToPreprocList( sc, Preproc_Execute, PP_NORMALIZE_PRIORITY, PP_NORMALIZE, PROTO_BITS); pc->regFunc = init; } session_api->enable_preproc_all_ports( sc, PP_NORMALIZE, PROTO_BITS ); } pc->normMode = ScNapInlineTestModeNewConf(sc) ? NORM_MODE_WOULDA : NORM_MODE_ON; return pc; } #define NOT_INLINE "WARNING: %s normalizations disabled because not inline.\n" static void Init_IP4 (struct _SnortConfig *sc, char* args) { NormalizerContext* pc = Init_GetContext(sc); if ( pc ) Parse_IP4(sc, pc, args); else LogMessage(NOT_INLINE, "ip4"); } static void Init_ICMP4 (struct _SnortConfig *sc, char* args) { NormalizerContext* pc = Init_GetContext(sc); if ( pc ) Parse_ICMP4(pc, args); else LogMessage(NOT_INLINE, "icmp4"); } static void Init_IP6 (struct _SnortConfig *sc, char* args) { NormalizerContext* pc = Init_GetContext(sc); if ( pc ) Parse_IP6(sc, pc, args); else LogMessage(NOT_INLINE, "ip6"); } static void Init_ICMP6 (struct _SnortConfig *sc, char* args) { NormalizerContext* pc = Init_GetContext(sc); if ( pc ) Parse_ICMP6(pc, args); else LogMessage(NOT_INLINE, "icmp6"); } static void Init_TCP (struct _SnortConfig *sc, char* args) { NormalizerContext* pc = Init_GetContext(sc); if ( pc ) Parse_TCP(pc, args); else LogMessage(NOT_INLINE, "tcp"); } //------------------------------------------------------------------------- // parsing stuff //------------------------------------------------------------------------- // options may appear in any order separated by ',': // preprocessor normalize_ip4: [id] [df] [rf] [tos] [trim] static void Parse_IP4 (struct _SnortConfig *sc, NormalizerContext* pc, char* args) { char** toks; int num_toks; int i; Norm_Enable(pc, NORM_IP4); if ( !args ) args = ""; toks = mSplit(args, ", ", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { #if 0 if ( !strcasecmp(toks[i], "id") ) { Norm_Enable(pc, NORM_IP4_ID); } else #endif if ( !strcasecmp(toks[i], "df") ) { Norm_Enable(pc, NORM_IP4_DF); } else if ( !strcasecmp(toks[i], "rf") ) { Norm_Enable(pc, NORM_IP4_RF); } else if ( !strcasecmp(toks[i], "tos") ) { Norm_Enable(pc, NORM_IP4_TOS); } else if ( !strcasecmp(toks[i], "trim") ) { Norm_Enable(pc, NORM_IP4_TRIM); } else { ParseError("Invalid preprocessor normalize_ip4 option '%s'", toks[i]); } } { tSfPolicyId pid = getParserPolicy(sc); SnortPolicy* policy = sc->targeted_policies[pid]; if ( (policy->new_ttl > 1) && (policy->new_ttl >= policy->min_ttl) ) { Norm_Enable(pc, NORM_IP4_TTL); } } mSplitFree(&toks, num_toks); Print_IP4(sc, pc); } // preprocessor normalize_icmp4 static void Parse_ICMP4 (NormalizerContext* pc, char* args) { Norm_Enable(pc, NORM_ICMP4); Print_ICMP4(pc); } // preprocessor normalize_ip6 static void Parse_IP6 (struct _SnortConfig *sc, NormalizerContext* pc, char* args) { Norm_Enable(pc, NORM_IP6); { tSfPolicyId pid = getParserPolicy(sc); SnortPolicy* policy = sc->targeted_policies[pid]; if ( (policy->new_ttl > 1) && (policy->new_ttl >= policy->min_ttl) ) { Norm_Enable(pc, NORM_IP6_TTL); } } Print_IP6(sc, pc); } // preprocessor normalize_icmp6 static void Parse_ICMP6 (NormalizerContext* pc, char* args) { Norm_Enable(pc, NORM_ICMP6); Print_ICMP6(pc); } // options may appear in any order separated by ',': // preprocessor normalize_tcp: [ecn packet|stream] [urp] [opts] [allow +] // // ::= sack|echo|partial_order|alt_checksum|md5|# // where 2 <= # <= 255. static void Parse_TCP (NormalizerContext* pc, char* args) { char **toks; int num_toks; int i, state = 0, opts = 0; if ( !args ) args = ""; toks = mSplit(args, ", ", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { switch ( state ) { case 0: if ( !strcasecmp(toks[i], "ecn") ) { state = 1; } else if ( !strcasecmp(toks[i], "block") ) { Norm_Enable(pc, NORM_TCP_BLOCK); } else if ( !strcasecmp(toks[i], "rsv") ) { Norm_Enable(pc, NORM_TCP_RSV); } else if ( !strcasecmp(toks[i], "pad") ) { Norm_Enable(pc, NORM_TCP_PAD); } else if ( !strcasecmp(toks[i], "req_urg") ) { Norm_Enable(pc, NORM_TCP_REQ_URG); } else if ( !strcasecmp(toks[i], "req_pay") ) { Norm_Enable(pc, NORM_TCP_REQ_PAY); } else if ( !strcasecmp(toks[i], "req_urp") ) { Norm_Enable(pc, NORM_TCP_URP); } else if ( !strcasecmp(toks[i], "urp") ) { Norm_Enable(pc, NORM_TCP_URP); } else if ( !strcasecmp(toks[i], "opts") ) { Norm_Enable(pc, NORM_TCP_OPT); } else if ( !strcasecmp(toks[i], "allow") ) { state = 2; opts = 0; } else if ( !strcasecmp(toks[i], "ips") ) { Norm_Enable(pc, NORM_TCP_IPS); } else if ( !strcasecmp(toks[i], "trim_syn") ) { Norm_Enable(pc, NORM_TCP_TRIM_SYN); } else if ( !strcasecmp(toks[i], "trim_rst") ) { Norm_Enable(pc, NORM_TCP_TRIM_RST); } else if ( !strcasecmp(toks[i], "trim_win") ) { Norm_Enable(pc, NORM_TCP_TRIM_WIN); } else if ( !strcasecmp(toks[i], "trim_mss") ) { Norm_Enable(pc, NORM_TCP_TRIM_MSS); } else if ( !strcasecmp(toks[i], "trim") ) { //Catch-all / backwards compatible Norm_Enable(pc, NORM_TCP_TRIM_SYN); Norm_Enable(pc, NORM_TCP_TRIM_RST); Norm_Enable(pc, NORM_TCP_TRIM_WIN); Norm_Enable(pc, NORM_TCP_TRIM_MSS); } else { ParseError("Invalid preprocessor normalize_tcp option '%s'", toks[i]); } break; case 1: if ( !strcasecmp(toks[i], "stream") ) { Norm_Enable(pc, NORM_TCP_ECN_STR); state = 0; } else if ( !strcasecmp(toks[i], "packet") ) { Norm_Enable(pc, NORM_TCP_ECN_PKT); state = 0; } else { ParseError("Unknown ecn argument '%s'" " Need packet|stream", toks[i]); } break; case 2: if ( !strcasecmp(toks[i], "sack") ) { Norm_TcpPassOption(pc, 4); Norm_TcpPassOption(pc, 5); opts++; } else if ( !strcasecmp(toks[i], "echo") ) { Norm_TcpPassOption(pc, 6); Norm_TcpPassOption(pc, 7); opts++; } else if ( !strcasecmp(toks[i], "partial_order") ) { Norm_TcpPassOption(pc, 9); Norm_TcpPassOption(pc, 10); opts++; } else if ( !strcasecmp(toks[i], "conn_count") ) { Norm_TcpPassOption(pc, 11); Norm_TcpPassOption(pc, 12); Norm_TcpPassOption(pc, 13); opts++; } else if ( !strcasecmp(toks[i], "alt_checksum") ) { Norm_TcpPassOption(pc, 14); Norm_TcpPassOption(pc, 15); opts++; } else if ( !strcasecmp(toks[i], "md5") ) { Norm_TcpPassOption(pc, 19); opts++; } else if ( isdigit(*toks[i]) ) { int opt = atoi(toks[i]); if ( 1 < opt && opt < 256 ) { Norm_TcpPassOption(pc, (uint8_t)opt); opts++; } else { ParseError("Bad TCP option number '%s'; must be" " between 2 and 255 inclusive", toks[i]); } } else if ( opts > 0 ) { i--; state = 0; } else { ParseError("Bad TCP option '%s'; must be" " sack|echo|partial_order|conn_count|alt_checksum|md5|#", toks[i]); } break; } } if ( state == 1 ) { ParseError("Missing argument for '%s'", toks[i-1]); } mSplitFree(&toks, num_toks); Print_TCP(pc); } //------------------------------------------------------------------------- // printing stuff //------------------------------------------------------------------------- #define ON "on" #define OFF "off" static inline void LogConf (const char* p, const char* s) { LogMessage("%12s: %s\n", p, s); } static inline void LogFlag ( const char* p, const NormalizerContext* nc, NormFlags nf) { const char* s = Norm_IsEnabled(nc, nf) ? ON : OFF; LogConf(p, s); } static void Print_IP4 (struct _SnortConfig *sc, const NormalizerContext* nc) { LogMessage("Normalizer config:\n"); LogFlag("ip4", nc, NORM_IP4); if ( Norm_IsEnabled(nc, NORM_IP4) ) { //LogFlag("ip4::id", nc, NORM_IP4_ID); LogFlag("ip4::df", nc, NORM_IP4_DF); LogFlag("ip4::rf", nc, NORM_IP4_RF); LogFlag("ip4::tos", nc, NORM_IP4_TOS); LogFlag("ip4::trim", nc, NORM_IP4_TRIM); if ( Norm_IsEnabled(nc, NORM_IP4_TTL) ) { tSfPolicyId pid = getParserPolicy(sc); int min = sc->targeted_policies[pid]->min_ttl; int new = sc->targeted_policies[pid]->new_ttl; LogMessage("%12s: %s (min=%d, new=%d)\n", "ip4::ttl", ON, min, new); } else LogConf("ip4::ttl", OFF); } } static void Print_ICMP4 (const NormalizerContext* nc) { LogMessage("Normalizer config:\n"); LogFlag("icmp4", nc, NORM_ICMP4); } static void Print_IP6 (struct _SnortConfig *sc, const NormalizerContext* nc) { LogMessage("Normalizer config:\n"); LogFlag("ip6", nc, NORM_IP6); if ( Norm_IsEnabled(nc, NORM_IP6) ) { if ( Norm_IsEnabled(nc, NORM_IP6_TTL) ) { tSfPolicyId pid = getParserPolicy(sc); int min = sc->targeted_policies[pid]->min_ttl; int new = sc->targeted_policies[pid]->new_ttl; LogMessage("%12s: %s (min=%d, new=%d)\n", "ip6::hops", ON, min, new); } else LogConf("ip6::hops", OFF); } } static void Print_ICMP6 (const NormalizerContext* nc) { LogMessage("Normalizer config:\n"); LogFlag("icmp6", nc, NORM_ICMP6); } static void Print_TCP (const NormalizerContext* nc) { LogMessage("Normalizer config:\n"); LogFlag("tcp", nc, NORM_TCP); if ( Norm_IsEnabled(nc, NORM_TCP) ) { const char* s; if ( Norm_IsEnabled(nc, NORM_TCP_ECN_PKT) ) s = "packet"; else if ( Norm_IsEnabled(nc, NORM_TCP_ECN_STR) ) s = "stream"; else s = OFF; LogConf("tcp::ecn", s); LogFlag("tcp::block", nc, NORM_TCP_BLOCK); LogFlag("tcp::rsv", nc, NORM_TCP_RSV); LogFlag("tcp::pad", nc, NORM_TCP_PAD); LogFlag("tcp::req_urg", nc, NORM_TCP_REQ_URG); LogFlag("tcp::req_pay", nc, NORM_TCP_REQ_PAY); LogFlag("tcp::req_urp", nc, NORM_TCP_REQ_URP); LogFlag("tcp::urp", nc, NORM_TCP_URP); if ( Norm_IsEnabled(nc, NORM_TCP_OPT) ) { char buf[1024] = ""; char* p = buf; int opt; size_t min; p += snprintf(p, buf+sizeof(buf)-p, "%s", "(allow "); min = strlen(buf); // TBD translate options to keywords allowed by parser for ( opt = 2; opt < 256; opt++ ) { const char* fmt = (strlen(buf) > min) ? ",%d" : "%d"; if ( Norm_TcpIsOptional(nc, opt) ) p += snprintf(p, buf+sizeof(buf)-p, fmt, opt); } if ( strlen(buf) > min ) { snprintf(p, buf+sizeof(buf)-p, "%c", ')'); buf[sizeof(buf)-1] = '\0'; } LogMessage("%12s: %s %s\n", "tcp::opt", ON, buf); } else LogConf("tcp::opt", OFF); LogFlag("tcp::ips", nc, NORM_TCP_IPS); LogFlag("tcp::trim_syn", nc, NORM_TCP_TRIM_SYN); LogFlag("tcp::trim_rst", nc, NORM_TCP_TRIM_RST); LogFlag("tcp::trim_win", nc, NORM_TCP_TRIM_WIN); LogFlag("tcp::trim_mss", nc, NORM_TCP_TRIM_MSS); } } //------------------------------------------------------------------------- // preproc (main) stuff //------------------------------------------------------------------------- static void Preproc_Install (struct _SnortConfig *sc) { #ifdef PERF_PROFILING RegisterPreprocessorProfile( "normalize", &norm_perf_stats, 0, &totalPerfStats, NULL); #endif AddFuncToPreprocCleanExitList( Preproc_CleanExit, NULL, PRIORITY_LAST, PP_NORMALIZE); AddFuncToPreprocResetList( Preproc_Reset, NULL, PP_NORMALIZE_PRIORITY, PP_NORMALIZE); AddFuncToPreprocResetStatsList( Preproc_ResetStats, NULL, PP_NORMALIZE_PRIORITY, PP_NORMALIZE); AddFuncToConfigCheckList(sc, Preproc_CheckConfig); AddFuncToPreprocPostConfigList(sc, Preproc_PostConfigInit, NULL); RegisterPreprocStats("normalize", Preproc_PrintStats); } //------------------------------------------------------------------------- static int Preproc_CheckPolicy ( struct _SnortConfig *sc, tSfPolicyUserContextId set, tSfPolicyId pid, void* pv) { //NormalizerContext* pc = (NormalizerContext*)pv; return 0; } static int Preproc_CheckConfig (struct _SnortConfig *sc) { int rval; if ( !base_set ) return 0; if ((rval = sfPolicyUserDataIterate(sc, base_set, Preproc_CheckPolicy))) return rval; return 0; } static int Preproc_PostInit ( struct _SnortConfig *sc, tSfPolicyUserContextId set, tSfPolicyId pid, void* pv) { NormalizerContext *pc = (NormalizerContext *)pv; SnortPolicy* policy = sc->targeted_policies[pid]; if ( policy->new_ttl && policy->new_ttl < policy->min_ttl ) { policy->new_ttl = policy->min_ttl; } Norm_SetConfig(pc); return 0; } static void Preproc_PostConfigInit (struct _SnortConfig *sc, void* pv) { sfPolicyUserDataIterate(sc, base_set, Preproc_PostInit); } //------------------------------------------------------------------------- static void Preproc_Execute (Packet *p, void *context) { tSfPolicyId pid = getNapRuntimePolicy(); NormalizerContext* pc = (NormalizerContext*)sfPolicyUserDataGet(base_set, pid); PROFILE_VARS; if ( !pc ) return; PREPROC_PROFILE_START(norm_perf_stats); if ( DAQ_GetInterfaceMode(p->pkth) == DAQ_MODE_INLINE ) if ( !Active_PacketWasDropped() ) if ( pc->normMode == NORM_MODE_ON || pc->normMode == NORM_MODE_WOULDA ) Norm_Packet(pc, p); PREPROC_PROFILE_END(norm_perf_stats); return; } //------------------------------------------------------------------------- static void Preproc_FreeContext (NormalizerContext* pc) { if ( pc ) free(pc); } static int Preproc_FreePolicy( tSfPolicyUserContextId set, tSfPolicyId pid, void* pv ) { NormalizerContext* pc = (NormalizerContext*)pv; sfPolicyUserDataClear(set, pid); Preproc_FreeContext(pc); return 0; } static void Preproc_FreeSet (tSfPolicyUserContextId set) { if ( !set ) return; sfPolicyUserDataFreeIterate(set, Preproc_FreePolicy); sfPolicyConfigDelete(set); } //------------------------------------------------------------------------- static void Preproc_CleanExit (int signal, void *foo) { Preproc_FreeSet(base_set); } static void Preproc_Reset (int signal, void *foo) { } static void Preproc_PrintStats(int exiting) { if(!ScNapPassiveMode()) { Norm_PrintStats(); Stream_PrintNormalizationStats(); } } static void Preproc_ResetStats (int signal, void *foo) { Norm_ResetStats(); Stream_ResetNormalizationStats(); } //------------------------------------------------------------------------- // reload stuff //------------------------------------------------------------------------- #ifdef SNORT_RELOAD static NormalizerContext* Reload_GetContext (struct _SnortConfig *sc, void **new_config) { tSfPolicyUserContextId swap_set; NormalizerContext* pc = NULL; tSfPolicyId policy_id = getParserPolicy(sc); if ( ScNapPassiveModeNewConf(sc) ) return NULL; if (!(swap_set = (tSfPolicyUserContextId)*new_config)) if (!(swap_set = (tSfPolicyUserContextId)GetRelatedReloadData(sc, "normalize_ip4"))) if (!(swap_set = (tSfPolicyUserContextId)GetRelatedReloadData(sc, "normalize_ip6"))) if (!(swap_set = (tSfPolicyUserContextId)GetRelatedReloadData(sc, "normalize_tcp"))) if (!(swap_set = (tSfPolicyUserContextId)GetRelatedReloadData(sc, "normalize_icmp4"))) swap_set = (tSfPolicyUserContextId)GetRelatedReloadData(sc, "normalize_icmp6"); if ( !swap_set ) { swap_set = sfPolicyConfigCreate(); *new_config = (void *)swap_set; } sc->normalizer_set = true; sfPolicyUserPolicySet(swap_set, policy_id); pc = sfPolicyUserDataGetCurrent(swap_set); if ( !pc ) { pc = (NormalizerContext* )SnortAlloc(sizeof(NormalizerContext)); sfPolicyUserDataSetCurrent(swap_set, pc); if( pc->regFunc != init) { AddFuncToPreprocList( sc, Preproc_Execute, PP_NORMALIZE_PRIORITY, PP_NORMALIZE, PROTO_BITS); pc->regFunc = reload; } session_api->enable_preproc_all_ports( sc, PP_NORMALIZE, PROTO_BITS ); } if ( sc->targeted_policies[policy_id]->nap_policy_mode == POLICY_MODE__INLINE_TEST ) pc->normMode = NORM_MODE_WOULDA; else pc->normMode = NORM_MODE_ON; return pc; } static void Reload_IP4 (struct _SnortConfig *sc, char* args, void **new_config) { NormalizerContext* pc = Reload_GetContext(sc, new_config); if ( pc ) Parse_IP4(sc, pc, args); else LogMessage(NOT_INLINE, "ip4"); } static void Reload_ICMP4 (struct _SnortConfig *sc, char* args, void **new_config) { NormalizerContext* pc = Reload_GetContext(sc, new_config); if ( pc ) Parse_ICMP4(pc, args); else LogMessage(NOT_INLINE, "icmp4"); } static void Reload_IP6 (struct _SnortConfig *sc, char* args, void **new_config) { NormalizerContext* pc = Reload_GetContext(sc, new_config); if ( pc ) Parse_IP6(sc, pc, args); else LogMessage(NOT_INLINE, "ip6"); } static void Reload_ICMP6 (struct _SnortConfig *sc, char* args, void **new_config) { NormalizerContext* pc = Reload_GetContext(sc, new_config); if ( pc ) Parse_ICMP6(pc, args); else LogMessage(NOT_INLINE, "icmp6"); } static void Reload_TCP (struct _SnortConfig *sc, char* args, void **new_config) { NormalizerContext* pc = Reload_GetContext(sc, new_config); if ( pc ) Parse_TCP(pc, args); else LogMessage(NOT_INLINE, "tcp"); } //------------------------------------------------------------------------- static int Reload_VerifyPolicy ( struct _SnortConfig *sc, tSfPolicyUserContextId set, tSfPolicyId pid, void* pv ) { //NormalizerContext* pc = (NormalizerContext*)pv; SnortPolicy* policy = sc->targeted_policies[pid]; if ( policy->new_ttl && policy->new_ttl < policy->min_ttl ) { policy->new_ttl = policy->min_ttl; } return 0; } static int Reload_Verify(struct _SnortConfig *sc, void *swap_config) { int rval; tSfPolicyUserContextId swap_set = (tSfPolicyUserContextId)swap_config; if ( !swap_set ) return -1; if ((rval = sfPolicyUserDataIterate (sc, swap_set, Reload_VerifyPolicy))) return rval; return 0; } //------------------------------------------------------------------------- static int Reload_SwapPolicy ( tSfPolicyUserContextId set, tSfPolicyId pid, void* pv) { NormalizerContext* pc = (NormalizerContext*)pv; sfPolicyUserDataClear(set, pid); Preproc_FreeContext(pc); return 0; } static void* Reload_Swap (struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId swap_set = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_set = base_set; if ( !swap_set ) return NULL; base_set = swap_set; sfPolicyUserDataIterate(sc, base_set, Preproc_PostInit); if ( old_set ) { sfPolicyUserDataFreeIterate(old_set, Reload_SwapPolicy); if ( !sfPolicyUserPolicyGetActive(old_set) ) return (void*)old_set; } return NULL; } //------------------------------------------------------------------------- static void Reload_Free (void* pv) { if ( !pv ) return; Preproc_FreeSet((tSfPolicyUserContextId)pv); } #endif //------------------------------------------------------------------------- // public methods //------------------------------------------------------------------------- NormMode Normalize_GetMode (const SnortConfig* sc, NormFlags nf) { tSfPolicyId pid; NormalizerContext* pc; if ( !base_set ) return NORM_MODE_OFF; if (!sc->normalizer_set) return NORM_MODE_OFF; pid = getNapRuntimePolicy(); pc = sfPolicyUserDataGet(base_set, pid); if ( !pc ) return NORM_MODE_OFF; if ( Norm_IsEnabled(pc, nf) ) return pc->normMode; return NORM_MODE_OFF; } #endif // NORMALIZER snort-2.9.20/src/preprocessors/spp_stream6.c0000644000175000017500000024263214241077124017213 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file spp_stream6.c * @author Martin Roesch * Steven Sturges * davis mcpherson * @date 19 Apr 2005 * * @brief You can never have too many stream reassemblers... */ /* I N C L U D E S ************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #ifndef WIN32 #include /* struct timeval */ #endif #include /* u_int*_t */ #include "snort.h" #include "snort_bounds.h" #include "util.h" #include "snort_debug.h" #include "plugbase.h" #include "session_api.h" #include "spp_stream6.h" #include "stream_api.h" #include "stream_paf.h" #include "stream_common.h" #include "session_common.h" #include "snort_stream_tcp.h" #include "snort_stream_udp.h" #include "snort_stream_icmp.h" #include "snort_stream_ip.h" #include "snort_session.h" #include "checksum.h" #include "mstring.h" #include "parser/IpAddrSet.h" #include "decode.h" #include "detect.h" #include "generators.h" #include "event_queue.h" #include "session_expect.h" #include "perf.h" #include "active.h" #include "sfdaq.h" #include "ipv6_port.h" #include "sfPolicy.h" #include "sp_flowbits.h" #include "stream5_ha.h" #include "memory_stats.h" #include "mempool.h" #include "control/sfcontrol_funcs.h" #ifdef TARGET_BASED #include "sftarget_protocol_reference.h" #include "sftarget_hostentry.h" #endif #include "profiler.h" #ifdef PERF_PROFILING PreprocStats s5PerfStats; extern PreprocStats s5TcpPerfStats; extern PreprocStats s5UdpPerfStats; extern PreprocStats s5IcmpPerfStats; extern PreprocStats s5IpPerfStats; #endif extern SessionCache *proto_session_caches[SESSION_PROTO_MAX]; extern MemPool sessionFlowMempool; extern OptTreeNode *otn_tmp; extern FlushConfig ignore_flush_policy[MAX_PORTS]; #ifdef TARGET_BASED extern FlushConfig ignore_flush_policy_protocol[MAX_PROTOCOL_ORDINAL]; #endif /* M A C R O S **************************************************/ #define PP_STREAM6_PRIORITY PRIORITY_CORE + PP_CORE_ORDER_STREAM /* G L O B A L S **************************************************/ tSfPolicyUserContextId stream_parsing_config = NULL; tSfPolicyUserContextId stream_online_config = NULL; SessionConfiguration *stream_session_config = NULL; StreamStats s5stats; uint32_t xtradata_func_count = 0; LogFunction xtradata_map[LOG_FUNC_MAX]; LogExtraData extra_data_log = NULL; void *extra_data_config = NULL; static bool old_config_freed = false; /* P R O T O T Y P E S ********************************************/ #ifdef MPLS static void updateMplsHeaders(Packet *, SessionControlBlock *); #endif int stream_print_mem_stats(FILE *, char *, PreprocMemInfo *); static void StreamPolicyInitTcp(struct _SnortConfig *, char *); static void StreamPolicyInitUdp(struct _SnortConfig *, char *); static void StreamPolicyInitIcmp(struct _SnortConfig *, char *); static void StreamPolicyInitIp(struct _SnortConfig *, char *); static void StreamCleanExit(int, void *); static void StreamReset(int, void *); static void StreamResetStats(int, void *); static int StreamVerifyConfig(struct _SnortConfig *); static void StreamPrintSessionConfig(SessionConfiguration *); static void StreamPrintStats(int); static void DisplayStreamStatistics (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); static void StreamProcess(Packet *p, void *context); static inline int IsEligible(Packet *p); #ifdef TARGET_BASED static void initServiceFilterStatus(struct _SnortConfig *sc); #endif #ifdef SNORT_RELOAD static void StreamTcpReload(struct _SnortConfig *, char *, void **); static void StreamUdpReload(struct _SnortConfig *, char *, void **); static void StreamIcmpReload(struct _SnortConfig *, char *, void **); static void StreamIpReload(struct _SnortConfig *, char *, void **); static int StreamReloadVerify(struct _SnortConfig *, void *); static void * StreamReloadSwap(struct _SnortConfig *, void *); static void StreamReloadSwapFree(void *); #endif /* S T R E A M A P I **********************************************/ static int StreamMidStreamDropAlert(void); static int StreamAlertFlushStream(Packet *p); static int StreamRequestFlushStream(Packet *p); static int StreamResponseFlushStream(Packet *p); static int StreamAddSessionAlert(void *ssnptr, Packet *p, uint32_t gid, uint32_t sid); static int StreamCheckSessionAlert(void *ssnptr, Packet *p, uint32_t gid, uint32_t sid); static int StreamUpdateSessionAlert(void *ssnptr, Packet *p, uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second); static char StreamSetReassembly(void *ssnptr, uint8_t flush_policy, char dir, char flags); static void StreamUpdateDirection( void * scbptr, char dir, sfaddr_t* ip, uint16_t port ); static char StreamGetReassemblyDirection(void *ssnptr); static char StreamGetReassemblyFlushPolicy(void *ssnptr, char dir); static char StreamIsStreamSequenced(void *ssnptr, char dir); static int StreamMissingInReassembled(void *ssnptr, char dir); static char StreamPacketsMissing(void *ssnptr, char dir); static void StreamDropPacket( Packet *p ); static int StreamGetRebuiltPackets( Packet *p, PacketIterator callback, void *userdata); static int StreamGetStreamSegments( Packet *p, StreamSegmentIterator callback, void *userdata); static uint32_t StreamGetFlushPoint(void *ssnptr, char dir); static void StreamSetFlushPoint(void *ssnptr, char dir, uint32_t flush_point); static uint8_t StreamRegisterPAFPort( struct _SnortConfig *, tSfPolicyId, uint16_t server_port, bool toServer, PAF_Callback, bool autoEnable); static uint8_t StreamRegisterPAFService( struct _SnortConfig *, tSfPolicyId, uint16_t service, bool toServer, PAF_Callback, bool autoEnable); static void** StreamGetPAFUserData(void* ssnptr, bool to_server, uint8_t id); static bool StreamIsPafActive(void* ssnptr, bool to_server); static bool StreamActivatePaf(void* ssnptr, int dir, int16_t service, uint8_t type); static uint32_t StreamRegisterXtraData(LogFunction ); static uint32_t StreamGetXtraDataMap(LogFunction **); static void StreamRegisterXtraDataLog(LogExtraData, void * ); static void StreamSetExtraData(void* ssn, Packet*, uint32_t); static void StreamClearExtraData(void* ssn, Packet*, uint32_t); static void s5SetPortFilterStatus( struct _SnortConfig *, IpProto protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing ); static void s5UnsetPortFilterStatus( struct _SnortConfig *, IpProto protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing ); #ifdef TARGET_BASED static void setServiceFilterStatus( struct _SnortConfig *sc, int service, int status, tSfPolicyId policyId, int parsing ); #endif static void StreamForceSessionExpiration(void *ssnptr); static void StreamForceDeleteSession(void *ssnptr ); static void registerReassemblyPort( char *network, uint16_t port, int reassembly_direction ); static void unregisterReassemblyPort( char *network, uint16_t port, int reassembly_direction ); static unsigned StreamRegisterHandler(Stream_Callback); static bool StreamSetHandler(void* ssnptr, unsigned id, Stream_Event); static uint32_t StreamGetPreprocFlags( void *ssnptr); static void StreamResetPolicy(void* ssnptr, int dir, uint16_t policy, uint16_t mss); static void StreamSetSessionDecrypted(void* ssnptr, bool enable); static bool StreamIsSessionDecrypted(void* ssnptr); struct _ExpectNode; static int StreamSetApplicationProtocolIdExpectedPreassignCallbackId( const Packet *ctrlPkt, sfaddr_t* srcIP, uint16_t srcPort, sfaddr_t* dstIP, uint16_t dstPort, uint8_t protocol, int16_t protoId, uint32_t preprocId, void *protoData, void (*protoDataFreeFn)(void*), unsigned cbId, Stream_Event se, struct _ExpectNode** packetExpectedNode); #if defined(FEAT_OPEN_APPID) static void SetApplicationId(void* ssnptr, int16_t serviceAppId, int16_t clientAppId, int16_t payloadAppId, int16_t miscAppId); static void GetApplicationId(void* ssnptr, int16_t *serviceAppId, int16_t *clientAppId, int16_t *payloadAppId, int16_t *miscAppId); static int RegisterHttpHeaderCallback (Http_Processor_Callback cb); #endif /* defined(FEAT_OPEN_APPID) */ static bool serviceEventPublish(unsigned int preprocId, void *ssnptr, ServiceEventType eventType, void * eventData); static bool serviceEventSubscribe(unsigned int preprocId, ServiceEventType eventType, ServiceEventNotifierFunc cb); static void StreamRegisterPAFFree(uint8_t id, PAF_Free_Callback cb); static Packet* getWirePacket(); static uint8_t getFlushPolicyDir(); static bool StreamIsSessionHttp2(void* ssnptr); static void StreamSetSessionHttp2(void* ssnptr); static bool StreamShowRebuiltPackets(); static bool StreamIsSessionHttp2Upg(void* ssnptr); static void StreamSetSessionHttp2Upg(void* ssnptr); static int RegisterFTPFlushCallback (FTP_Processor_Flush_Callback cb); static void setFtpFilePosition (void *scbptr,bool flush); #ifdef HAVE_DAQ_DECRYPTED_SSL static int StreamSimulateTcpAck(void *ssnptr, uint8_t dir, uint32_t len); #endif StreamAPI s5api = { /* .version = */ STREAM_API_VERSION5, /* .alert_inline_midstream_drops = */ StreamMidStreamDropAlert, /* .alert_flush_stream = */ StreamAlertFlushStream, /* .request_flush_stream = */ StreamRequestFlushStream, /* .response_flush_stream = */ StreamResponseFlushStream, /* .traverse_reassembled = */ StreamGetRebuiltPackets, /* .traverse_stream_segments = */ StreamGetStreamSegments, /* .add_session_alert = */ StreamAddSessionAlert, /* .check_session_alerted = */ StreamCheckSessionAlert, /* .update_session_alert = */ StreamUpdateSessionAlert, /* .set_reassembly = */ StreamSetReassembly, /* .update_direction = */ StreamUpdateDirection, /* .get_reassembly_direction = */ StreamGetReassemblyDirection, /* .get_reassembly_flush_policy = */ StreamGetReassemblyFlushPolicy, /* .is_stream_sequenced = */ StreamIsStreamSequenced, /* .missing_in_reassembled = */ StreamMissingInReassembled, /* .missed_packets = */ StreamPacketsMissing, /* .drop_packet = */ StreamDropPacket, /* .get_flush_point = */ StreamGetFlushPoint, /* .set_flush_point = */ StreamSetFlushPoint, /* .register_paf_port = */ StreamRegisterPAFPort, /* .get_paf_user_data = */ StreamGetPAFUserData, /* .is_paf_active = */ StreamIsPafActive, /* .activate_paf = */ StreamActivatePaf, /* .set_tcp_syn_session_status = */ s5TcpSetSynSessionStatus, /* .unset_tcp_syn_session_status = */ s5TcpUnsetSynSessionStatus, /* .reg_xtra_data_cb = */ StreamRegisterXtraData, /* .reg_xtra_data_log = */ StreamRegisterXtraDataLog, /* .get_xtra_data_map = */ StreamGetXtraDataMap, /* .register_paf_service = */ StreamRegisterPAFService, /* .set_extra_data = */ StreamSetExtraData, /* .clear_extra_data = */ StreamClearExtraData, // The methods below may move to Session /* .set_port_filter_status = */ s5SetPortFilterStatus, /* .unset_port_filter_status = */ s5UnsetPortFilterStatus, #ifdef TARGET_BASED /* .set_service_filter_status = */ setServiceFilterStatus, #endif /* .register_reassembly_port = */ registerReassemblyPort, /* .register_reassembly_port = */ unregisterReassemblyPort, /* .expire_session = */ StreamForceSessionExpiration, /* .force_delete_session = */ StreamForceDeleteSession, /* .register_event_handler = */ StreamRegisterHandler, /* .set_event_handler = */ StreamSetHandler, /* .set_reset_policy = */ StreamResetPolicy, /* .set_session_decrypted = */ StreamSetSessionDecrypted, /* .is_session_decrypted = */ StreamIsSessionDecrypted, /* .set_application_protocol_id_expected_preassign_callback = */ StreamSetApplicationProtocolIdExpectedPreassignCallbackId, /* .print_normalization_stats = */ Stream_PrintNormalizationStats, /* .reset_normalization_stats = */ Stream_ResetNormalizationStats, #if defined(FEAT_OPEN_APPID) /* .set_application_id = */ SetApplicationId, /* .get_application_id = */ GetApplicationId, /* .register_http_header_callback = */ RegisterHttpHeaderCallback, #endif /* defined(FEAT_OPEN_APPID) */ /* .service_event_publish */ serviceEventPublish, /* .service_event_subscribe */ serviceEventSubscribe, /* .register_paf_free */ StreamRegisterPAFFree, /* .get_wire_packet */ getWirePacket, /* .get_flush_policy_dir */ getFlushPolicyDir, /* .is_session_http2 */ StreamIsSessionHttp2, /* .set_session_http2 */ StreamSetSessionHttp2, /* .is_show_rebuilt_packets_enabled */ StreamShowRebuiltPackets, /* .is_session_http2_upg */ StreamIsSessionHttp2Upg, /* .set_session_http2_upg */ StreamSetSessionHttp2Upg, /* .get_preproc_flags */ StreamGetPreprocFlags, /* .register_ftp_flush_cb */ RegisterFTPFlushCallback, /* .set_ftp_file_position */ setFtpFilePosition #ifdef HAVE_DAQ_DECRYPTED_SSL , /* .simulate_tcp_ack_in_peer_stream_tracker = */ StreamSimulateTcpAck #endif }; void SetupStream6(void) { RegisterMemoryStatsFunction(PP_STREAM, stream_print_mem_stats); #ifndef SNORT_RELOAD RegisterPreprocessor("stream5_tcp", StreamPolicyInitTcp); RegisterPreprocessor("stream5_udp", StreamPolicyInitUdp); RegisterPreprocessor("stream5_icmp", StreamPolicyInitIcmp); RegisterPreprocessor("stream5_ip", StreamPolicyInitIp); #else RegisterPreprocessor("stream5_tcp", StreamPolicyInitTcp, StreamTcpReload, StreamReloadVerify, StreamReloadSwap, StreamReloadSwapFree); RegisterPreprocessor("stream5_udp", StreamPolicyInitUdp, StreamUdpReload, StreamReloadVerify, StreamReloadSwap, StreamReloadSwapFree); RegisterPreprocessor("stream5_icmp", StreamPolicyInitIcmp, StreamIcmpReload, StreamReloadVerify, StreamReloadSwap, StreamReloadSwapFree); RegisterPreprocessor("stream5_ip", StreamPolicyInitIp, StreamIpReload, StreamReloadVerify, StreamReloadSwap, StreamReloadSwapFree); #endif // init pointer to stream api dispatch table... stream_api = &s5api; /* Registering for SFR CLI */ ControlSocketRegisterHandler(CS_TYPE_STREAM_STATS, NULL, NULL, &DisplayStreamStatistics); DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Stream preprocessor setup complete.\n");); } // Initialize the configuration object for a stream preprocessor policy. If this is the first stream configuration // being parsed for this NAP policy then allocate the config context object that holds the config settings for all // the possible stream protocols. This function is called before each protocol specific configuration string is // processed for each NAP policy defined. static StreamConfig *initStreamPolicyConfig( struct _SnortConfig *sc, bool reload_config ) { tSfPolicyId policy_id = getParserPolicy( sc ); StreamConfig *pCurrentPolicyConfig = NULL; if( stream_parsing_config == NULL ) { // we are parsing the first stream conf file, create a context and do all stream // one time initialization functions. // stream_parsing_config = sfPolicyConfigCreate(); if( !reload_config ) { #ifdef PERF_PROFILING RegisterPreprocessorProfile( "s5", &s5PerfStats, 0, &totalPerfStats , NULL); RegisterPreprocessorProfile( "s5tcp", &s5TcpPerfStats, 1, &s5PerfStats , NULL); RegisterPreprocessorProfile( "s5udp", &s5UdpPerfStats, 1, &s5PerfStats , NULL); RegisterPreprocessorProfile( "s5icmp", &s5IcmpPerfStats, 1, &s5PerfStats , NULL); RegisterPreprocessorProfile( "s5ip", &s5IpPerfStats, 1, &s5PerfStats , NULL); #endif AddFuncToPreprocCleanExitList( StreamCleanExit, NULL, PP_STREAM6_PRIORITY, PP_STREAM ); AddFuncToPreprocResetList( StreamReset, NULL, PP_STREAM6_PRIORITY, PP_STREAM ); AddFuncToPreprocResetStatsList( StreamResetStats, NULL, PP_STREAM6_PRIORITY, PP_STREAM ); AddFuncToConfigCheckList( sc, StreamVerifyConfig ); RegisterPreprocStats( "stream5", StreamPrintStats ); } else old_config_freed = false; } // set this policy id as current and get pointer to the struct of pointers to the // protocol specific configuration pointers for this policy... // if this pointer is NULL then this is the first stream protocol conf file we are // parsing for this policy, so allocate required memory sfPolicyUserPolicySet( stream_parsing_config, policy_id ); pCurrentPolicyConfig = ( StreamConfig * ) sfPolicyUserDataGetCurrent( stream_parsing_config ); if( pCurrentPolicyConfig == NULL ) { pCurrentPolicyConfig = ( StreamConfig * ) SnortPreprocAlloc( 1, sizeof( StreamConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); sfPolicyUserDataSetCurrent( stream_parsing_config, pCurrentPolicyConfig ); // get pointer to the session configuration...if it's NULL bad news, session not // configured so Fatal Error... pCurrentPolicyConfig->session_config = getSessionConfiguration( reload_config ); if( pCurrentPolicyConfig->session_config == NULL ) { FatalError( "%s(%d) - Session Must Be Configured Before Stream!\n", file_name, file_line ); } // stream registers to run for all ports session_api->enable_preproc_all_ports( sc, PP_STREAM, PROTO_BIT__ALL ); pCurrentPolicyConfig->verified = false; pCurrentPolicyConfig->swapped = false; pCurrentPolicyConfig->reload_config = reload_config; StreamPrintSessionConfig( pCurrentPolicyConfig->session_config ); } return pCurrentPolicyConfig; } // return pointer to configuration context object for all stream polices. If parsing is // true return pointer to current parsing context object (NULL if parsing not in progress) // otherwise pointer to the current active config context object static inline tSfPolicyUserContextId getStreamConfigContext( bool parsing ) { if( parsing ) return stream_parsing_config; else return stream_online_config; } // return pointer to Stream configuration for the specified policy. If parsing is // true return pointer to config struct the policy is being parsed into, otherwise pointer // to the currently active config StreamConfig *getStreamPolicyConfig( tSfPolicyId policy_id, bool parsing ) { tSfPolicyUserContextId ctx; if( parsing ) ctx = ( stream_parsing_config != NULL ) ? stream_parsing_config : stream_online_config; else ctx = stream_online_config; if( ctx != NULL ) return ( StreamConfig * ) sfPolicyUserDataGet( ctx, policy_id ); else return NULL; } static void StreamPrintSessionConfig( SessionConfiguration *config ) { LogMessage("Stream global config:\n"); LogMessage(" Track TCP sessions: %s\n", config->track_tcp_sessions == STREAM_TRACK_YES ? "ACTIVE" : "INACTIVE"); if( config->track_tcp_sessions == STREAM_TRACK_YES ) { LogMessage(" Max TCP sessions: %u\n", config->max_tcp_sessions); LogMessage(" TCP cache pruning timeout: %u seconds\n", config->tcp_cache_pruning_timeout); LogMessage(" TCP cache nominal timeout: %u seconds\n", config->tcp_cache_nominal_timeout); } LogMessage(" Memcap (for reassembly packet storage): %d\n", config->memcap); LogMessage(" Track UDP sessions: %s\n", config->track_udp_sessions == STREAM_TRACK_YES ? "ACTIVE" : "INACTIVE"); if( config->track_udp_sessions == STREAM_TRACK_YES ) { LogMessage(" Max UDP sessions: %u\n", config->max_udp_sessions); LogMessage(" UDP cache pruning timeout: %u seconds\n", config->udp_cache_pruning_timeout); LogMessage(" UDP cache nominal timeout: %u seconds\n", config->udp_cache_nominal_timeout); } LogMessage(" Track ICMP sessions: %s\n", config->track_icmp_sessions == STREAM_TRACK_YES ? "ACTIVE" : "INACTIVE"); if( config->track_icmp_sessions == STREAM_TRACK_YES ) LogMessage(" Max ICMP sessions: %u\n", config->max_icmp_sessions); LogMessage(" Track IP sessions: %s\n", config->track_ip_sessions == STREAM_TRACK_YES ? "ACTIVE" : "INACTIVE"); if( config->track_ip_sessions == STREAM_TRACK_YES ) LogMessage(" Max IP sessions: %u\n", config->max_ip_sessions); if( config->prune_log_max ) LogMessage(" Log info if session memory consumption exceeds %d\n", config->prune_log_max); #ifdef ACTIVE_RESPONSE LogMessage(" Send up to %d active responses\n", config->max_active_responses); if( config->max_active_responses > 1 ) { LogMessage(" Wait at least %d seconds between responses\n", config->min_response_seconds); } #endif LogMessage(" Protocol Aware Flushing: %s\n", ScPafEnabled() ? "ACTIVE" : "INACTIVE"); LogMessage(" Maximum Flush Point: %u\n", ScPafMax()); #ifdef ENABLE_HA LogMessage(" High Availability: %s\n", config->enable_ha ? "ENABLED" : "DISABLED"); #endif #ifdef REG_TEST LogMessage(" Session Control Block Size: %lu\n", (long unsigned int)sizeof(SessionControlBlock)); #endif } static void StreamPolicyInitTcp( struct _SnortConfig *sc, char *args ) { StreamConfig *config = NULL; config = initStreamPolicyConfig( sc, false ); if ( !config->session_config->track_tcp_sessions ) return; if( config->tcp_config == NULL ) { config->tcp_config = ( StreamTcpConfig * ) SnortPreprocAlloc( 1, sizeof( StreamTcpConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); StreamInitTcp( ); StreamTcpInitFlushPoints( ); StreamTcpRegisterRuleOptions( sc ); AddFuncToPreprocPostConfigList( sc, StreamPostConfigTcp, config->tcp_config ); } /* Call the protocol specific initializer */ StreamTcpPolicyInit( sc, config->tcp_config, args ); } static void StreamPolicyInitUdp( struct _SnortConfig *sc, char *args ) { StreamConfig *config; config = initStreamPolicyConfig( sc, false ); if( !config->session_config->track_udp_sessions ) return; if( config->udp_config == NULL ) { config->udp_config = ( StreamUdpConfig * ) SnortPreprocAlloc(1, sizeof( StreamUdpConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG); StreamInitUdp( ); } /* Call the protocol specific initializer */ StreamUdpPolicyInit( config->udp_config, args ); } static void StreamPolicyInitIcmp( struct _SnortConfig *sc, char *args ) { StreamConfig *config; config = initStreamPolicyConfig( sc, false ); if( !config->session_config->track_icmp_sessions ) return; if( config->icmp_config == NULL ) { config->icmp_config = ( StreamIcmpConfig * ) SnortPreprocAlloc( 1, sizeof( StreamIcmpConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); StreamInitIcmp( ); } /* Call the protocol specific initializer */ StreamIcmpPolicyInit( config->icmp_config, args ); } static void StreamPolicyInitIp( struct _SnortConfig *sc, char *args ) { StreamConfig *config; config = initStreamPolicyConfig( sc, false ); if( !config->session_config->track_ip_sessions ) return; if( config->ip_config == NULL ) { config->ip_config = ( StreamIpConfig * ) SnortPreprocAlloc( 1, sizeof( StreamIpConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); StreamInitIp( ); } /* Call the protocol specific initializer */ StreamIpPolicyInit( config->ip_config, args ); } int StreamVerifyProtocolConfigs( struct _SnortConfig *sc, StreamConfig *s5c, tSfPolicyId policyId, int *proto_flags ) { int tcpNotConfigured = 0; int udpNotConfigured = 0; int icmpNotConfigured = 0; int ipNotConfigured = 0; if( s5c->tcp_config ) { tcpNotConfigured = StreamVerifyTcpConfig( sc, s5c->tcp_config, policyId ); if( tcpNotConfigured ) WarningMessage("WARNING: Stream TCP misconfigured.\n"); else *proto_flags |= PROTO_BIT__TCP; } if( s5c->udp_config ) { udpNotConfigured = StreamVerifyUdpConfig( sc, s5c->udp_config, policyId ); if( udpNotConfigured ) WarningMessage("WARNING: Stream UDP misconfigured.\n"); else *proto_flags |= PROTO_BIT__UDP; } if( s5c->icmp_config ) { icmpNotConfigured = StreamVerifyIcmpConfig( s5c->icmp_config, policyId ); if( icmpNotConfigured ) WarningMessage("WARNING: Stream ICMP misconfigured.\n"); else *proto_flags |= PROTO_BIT__ICMP; } if( s5c->ip_config ) { ipNotConfigured = StreamVerifyIpConfig( s5c->ip_config, policyId ); if( ipNotConfigured ) WarningMessage("WARNING: Stream IP misconfigured.\n"); else *proto_flags |= PROTO_BIT__IP; } return( tcpNotConfigured || udpNotConfigured || icmpNotConfigured || ipNotConfigured ); } static int StreamVerifyConfigPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { int configNotValid = 0; int proto_flags = 0; tSfPolicyId tmp_policy_id = getParserPolicy( sc ); StreamConfig *stream_conf = ( StreamConfig * ) pData; if( stream_conf->verified ) return 0; // verify that session is configured. if ( stream_conf->session_config == NULL ) { FatalError("%s(%d) No Stream session configuration...exiting.\n", __FILE__, __LINE__); } configNotValid = StreamVerifyProtocolConfigs( sc, stream_conf, policyId, &proto_flags ); if ( configNotValid ) { FatalError("%s(%d) Stream not properly configured... exiting\n", __FILE__, __LINE__); } stream_conf->verified = true; setParserPolicy( sc, policyId ); AddFuncToPreprocList( sc, StreamProcess, PP_STREAM6_PRIORITY, PP_STREAM, proto_flags ); setParserPolicy( sc, tmp_policy_id ); return 0; } static int StreamVerifyConfig(struct _SnortConfig *sc) { int rval = sfPolicyUserDataIterate( sc, stream_parsing_config, StreamVerifyConfigPolicy ); if( rval ) return rval; if (!stream_online_config) { stream_online_config = stream_parsing_config; stream_parsing_config = NULL; } #ifdef TARGET_BASED initServiceFilterStatus( sc ); #endif return 0; } static void StreamReset(int signal, void *foo) { if (stream_online_config == NULL) return; StreamResetTcp(); StreamResetUdp(); StreamResetIcmp(); StreamResetIp(); } static void StreamResetStats(int signal, void *foo) { memset(&s5stats, 0, sizeof(s5stats)); StreamResetTcpPrunes(); StreamResetUdpPrunes(); StreamResetIcmpPrunes(); StreamResetIpPrunes(); } static void StreamCleanExit(int signal, void *foo) { #ifdef ENABLE_QUICK_EXIT LogMessage("Snort quick exit enabled\n"); return; #else /* Protocol specific cleanup actions */ StreamCleanTcp(); StreamCleanUdp(); StreamCleanIcmp(); StreamCleanIp(); StreamFreeConfigs(stream_online_config); stream_online_config = NULL; #endif } static void DisplayStreamStatistics (uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f) { char buffer[CS_STATS_BUF_SIZE + 1]; int len = 0; int total_sessions = s5stats.total_tcp_sessions + s5stats.total_udp_sessions + s5stats.total_icmp_sessions + s5stats.total_ip_sessions; if (total_sessions) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "Stream statistics:\n" " Total sessions: %u\n" " TCP sessions: %u\n" " UDP sessions: %u\n" " ICMP sessions: %u\n" " IP sessions: %u\n" " TCP Prunes: %u\n" " UDP Prunes: %u\n" " ICMP Prunes: %u\n" " IP Prunes: %u\n" "TCP StreamTrackers Created: %u\n" "TCP StreamTrackers Deleted: %u\n" " TCP Timeouts: %u\n" " TCP Overlaps: %u\n" " TCP Segments Queued: %u\n" " TCP Segments Released: %u\n" " TCP Rebuilt Packets: %u\n" " TCP Segments Used: %u\n" " TCP Discards: %u\n" " TCP Gaps: %u\n" " UDP Sessions Created: %u\n" " UDP Sessions Deleted: %u\n" " UDP Timeouts: %u\n" " UDP Discards: %u\n" " Events: %u\n" " Internal Events: %u\n" " TCP Port Filter\n" " Filtered: %u\n" " Inspected: %u\n" " Tracked: %u\n" " UDP Port Filter\n" " Filtered: %u\n" " Inspected: %u\n" " Tracked: %u\n" , total_sessions , s5stats.total_tcp_sessions , s5stats.total_udp_sessions , s5stats.total_icmp_sessions , s5stats.total_ip_sessions , StreamGetTcpPrunes() , StreamGetUdpPrunes() , StreamGetIcmpPrunes() , StreamGetIpPrunes() , s5stats.tcp_streamtrackers_created , s5stats.tcp_streamtrackers_released , s5stats.tcp_timeouts , s5stats.tcp_overlaps , s5stats.tcp_streamsegs_created , s5stats.tcp_streamsegs_released , s5stats.tcp_rebuilt_packets , s5stats.tcp_rebuilt_seqs_used , s5stats.tcp_discards , s5stats.tcp_gaps , s5stats.udp_sessions_created , s5stats.udp_sessions_released , s5stats.udp_timeouts , s5stats.udp_discards , s5stats.events , s5stats.internalEvents , s5stats.tcp_port_filter.filtered , s5stats.tcp_port_filter.inspected , s5stats.tcp_port_filter.session_tracked , s5stats.udp_port_filter.filtered , s5stats.udp_port_filter.inspected , s5stats.udp_port_filter.session_tracked); } else { len = snprintf(buffer, CS_STATS_BUF_SIZE, "Stream statistics not available\n Total sessions: %u", total_sessions); } if (-1 == f(te, (const uint8_t *)buffer, len)) { LogMessage("Unable to send data to the frontend\n"); } } static void StreamPrintStats(int exiting) { LogMessage("Stream statistics:\n"); LogMessage(" Total sessions: %u\n", s5stats.total_tcp_sessions + s5stats.total_udp_sessions + s5stats.total_icmp_sessions + s5stats.total_ip_sessions); LogMessage(" TCP sessions: %u\n", s5stats.total_tcp_sessions); LogMessage(" Active TCP sessions: %u\n", s5stats.active_tcp_sessions); LogMessage(" Non mempool TCP sess mem: %u\n", session_mem_in_use); LogMessage(" TCP mempool used: %"PRIu64"\n", get_tcp_used_mempool()); LogMessage(" UDP sessions: %u\n", s5stats.total_udp_sessions); LogMessage(" Active UDP sessions: %u\n", s5stats.active_udp_sessions); LogMessage(" UDP mempool used: %"PRIu64"\n", get_udp_used_mempool()); LogMessage(" ICMP sessions: %u\n", s5stats.total_icmp_sessions); LogMessage(" Active ICMP sessions: %u\n", s5stats.active_icmp_sessions); LogMessage(" ICMP mempool used: %"PRIu64"\n", get_icmp_used_mempool()); LogMessage(" IP sessions: %u\n", s5stats.total_ip_sessions); LogMessage(" Active IP sessions: %u\n", s5stats.active_ip_sessions); LogMessage(" IP mempool used: %"PRIu64"\n", get_ip_used_mempool()); LogMessage(" TCP Prunes: %u\n", StreamGetTcpPrunes()); LogMessage(" UDP Prunes: %u\n", StreamGetUdpPrunes()); LogMessage(" ICMP Prunes: %u\n", StreamGetIcmpPrunes()); LogMessage(" IP Prunes: %u\n", StreamGetIpPrunes()); LogMessage("TCP StreamTrackers Created: %u\n", s5stats.tcp_streamtrackers_created); LogMessage("TCP StreamTrackers Deleted: %u\n", s5stats.tcp_streamtrackers_released); LogMessage(" TCP Timeouts: %u\n", s5stats.tcp_timeouts); LogMessage(" TCP Overlaps: %u\n", s5stats.tcp_overlaps); LogMessage(" TCP Segments Queued: %u\n", s5stats.tcp_streamsegs_created); LogMessage(" TCP Segments Released: %u\n", s5stats.tcp_streamsegs_released); LogMessage(" TCP Rebuilt Packets: %u\n", s5stats.tcp_rebuilt_packets); LogMessage(" TCP Segments Used: %u\n", s5stats.tcp_rebuilt_seqs_used); LogMessage(" TCP Discards: %u\n", s5stats.tcp_discards); LogMessage(" TCP Gaps: %u\n", s5stats.tcp_gaps); LogMessage(" UDP Sessions Created: %u\n", s5stats.udp_sessions_created); LogMessage(" UDP Sessions Deleted: %u\n", s5stats.udp_sessions_released); LogMessage(" UDP Timeouts: %u\n", s5stats.udp_timeouts); LogMessage(" UDP Discards: %u\n", s5stats.udp_discards); LogMessage(" ICMP Dest unreachable: %u\n", s5stats.icmp_unreachable); LogMessage(" ICMP Fragmentation needed: %u\n", s5stats.icmp_unreachable_code4); LogMessage(" Events: %u\n", s5stats.events); LogMessage(" Internal Events: %u\n", s5stats.internalEvents); LogMessage(" TCP Port Filter\n"); LogMessage(" Filtered: %u\n", s5stats.tcp_port_filter.filtered); LogMessage(" Inspected: %u\n", s5stats.tcp_port_filter.inspected); LogMessage(" Tracked: %u\n", s5stats.tcp_port_filter.session_tracked); LogMessage(" UDP Port Filter\n"); LogMessage(" Filtered: %u\n", s5stats.udp_port_filter.filtered); LogMessage(" Inspected: %u\n", s5stats.udp_port_filter.inspected); LogMessage(" Tracked: %u\n", s5stats.udp_port_filter.session_tracked); // TBD-EDM move to session will need to fix reg tests? #ifdef ENABLE_HA SessionPrintHAStats(); #endif } #define GET_MEMPOOL(protocol) \ proto_session_caches[protocol] ? \ proto_session_caches[protocol]->protocol_session_pool : NULL size_t stream_get_free_mempool(MemPool *mempool) { if (mempool) return mempool->max_memory - mempool->used_memory; return 0; } size_t stream_get_used_mempool(MemPool *mempool) { if (mempool) return mempool->used_memory; return 0; } size_t stream_get_max_mempool(MemPool *mempool) { if (mempool) return mempool->max_memory; return 0; } int stream_print_mem_stats(FILE *fd, char* buffer, PreprocMemInfo *meminfo) { time_t curr_time; int len = 0; uint32_t total_sessions = s5stats.total_tcp_sessions + s5stats.total_udp_sessions + s5stats.total_icmp_sessions + s5stats.total_ip_sessions; if (fd) { size_t total_heap_memory = meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory; size_t total_pool_memory = stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_TCP)) + stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_UDP)) + stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_ICMP)) + stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_IP)) + stream_get_max_mempool(&sessionFlowMempool); len = fprintf(fd, ",%u,%u,%u" ",%u,%u,%u" ",%u,%u,%u" ",%lu,%u,%u" ",%lu,%u,%u,%lu,%lu" , total_sessions , s5stats.total_tcp_sessions , s5stats.active_tcp_sessions , s5stats.total_udp_sessions , s5stats.active_udp_sessions , s5stats.total_icmp_sessions , s5stats.active_icmp_sessions , s5stats.total_ip_sessions , s5stats.active_ip_sessions , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , total_pool_memory , total_pool_memory + total_heap_memory); return len; } curr_time = time(NULL); if (buffer) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics of Stream on: %s\n" "Stream Session Statistics:\n" " Total sessions: %u\n" " TCP sessions: %u\n" " Active TCP sessions: %u\n" " UDP sessions: %u\n" " Active UDP sessions: %u\n" " ICMP sessions: %u\n" " Active ICMP sessions: %u\n" " IP sessions: %u\n" " Active IP sessions: %u\n" "\n TCP Memory Pool:\n" " Free Memory: %14zu bytes\n" " Used Memory: %14zu bytes\n" " Max Memory : %14zu bytes\n" "\n UDP Memory Pool:\n" " Free Memory: %14zu bytes\n" " Used Memory: %14zu bytes\n" " Max Memory : %14zu bytes\n" "\n ICMP Memory Pool:\n" " Free Memory: %14zu bytes\n" " Used Memory: %14zu bytes\n" " Max Memory : %14zu bytes\n" "\n IP Memory Pool:\n" " Free Memory: %14zu bytes\n" " Used Memory: %14zu bytes\n" " Max Memory : %14zu bytes\n" "\n Session Flow Memory Pool:\n" " Free Memory: %14zu bytes\n" " Used Memory: %14zu bytes\n" " Max Memory : %14zu bytes\n" , ctime(&curr_time) , total_sessions , s5stats.total_tcp_sessions , s5stats.active_tcp_sessions , s5stats.total_udp_sessions , s5stats.active_udp_sessions , s5stats.total_icmp_sessions , s5stats.active_icmp_sessions , s5stats.total_ip_sessions , s5stats.active_ip_sessions , stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_TCP)) , stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_TCP)) , stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_TCP)) , stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_UDP)) , stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_UDP)) , stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_UDP)) , stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_ICMP)) , stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_ICMP)) , stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_ICMP)) , stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_IP)) , stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_IP)) , stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_IP)) , stream_get_free_mempool(&sessionFlowMempool) , stream_get_used_mempool(&sessionFlowMempool) , stream_get_max_mempool(&sessionFlowMempool)); } else { LogMessage("\n"); LogMessage("Memory Statistics of Stream on: %s\n",ctime(&curr_time)); LogMessage("Stream Session Statistics:\n"); LogMessage(" Total sessions: %u\n", total_sessions); LogMessage(" TCP sessions: %u\n", s5stats.total_tcp_sessions); LogMessage(" Active TCP sessions: %u\n", s5stats.active_tcp_sessions); LogMessage(" UDP sessions: %u\n", s5stats.total_udp_sessions); LogMessage(" Active UDP sessions: %u\n", s5stats.active_udp_sessions); LogMessage(" ICMP sessions: %u\n", s5stats.total_icmp_sessions); LogMessage(" Active ICMP sessions: %u\n", s5stats.active_icmp_sessions); LogMessage(" IP sessions: %u\n", s5stats.total_ip_sessions); LogMessage(" Active IP sessions: %u\n", s5stats.active_ip_sessions); LogMessage(" TCP Memory Pool:\n"); LogMessage(" Free Memory: %14zu bytes\n", stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_TCP))); LogMessage(" Used Memory: %14zu bytes\n", stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_TCP))); LogMessage(" Max Memory : %14zu bytes\n", stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_TCP))); LogMessage(" UDP Memory Pool:\n"); LogMessage(" Free Memory: %14zu bytes\n", stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_UDP))); LogMessage(" Used Memory: %14zu bytes\n", stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_UDP))); LogMessage(" Max Memory : %14zu bytes\n", stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_UDP))); LogMessage(" ICMP Memory Pool:\n"); LogMessage(" Free Memory: %14zu bytes\n", stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_ICMP))); LogMessage(" Used Memory: %14zu bytes\n", stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_ICMP))); LogMessage(" Max Memory : %14zu bytes\n", stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_ICMP))); LogMessage(" IP Memory Pool:\n"); LogMessage(" Free Memory: %14zu bytes\n", stream_get_free_mempool(GET_MEMPOOL(SESSION_PROTO_IP))); LogMessage(" Used Memory: %14zu bytes\n", stream_get_used_mempool(GET_MEMPOOL(SESSION_PROTO_IP))); LogMessage(" Max Memory : %14zu bytes\n", stream_get_max_mempool(GET_MEMPOOL(SESSION_PROTO_IP))); LogMessage(" Session Flow Memory Pool:\n"); LogMessage(" Free Memory: %14zu bytes\n", stream_get_free_mempool(&sessionFlowMempool)); LogMessage(" Used Memory: %14zu bytes\n", stream_get_used_mempool(&sessionFlowMempool)); LogMessage(" Max Memory : %14zu bytes\n", stream_get_max_mempool(&sessionFlowMempool)); } return len; } static void checkOnewayStatus( uint32_t protocol, SessionControlBlock *scb ) { if( scb->in_oneway_list && scb->session_established ) session_api->remove_session_from_oneway_list( protocol, scb ); } static void updateMplsHeaders(Packet *p, SessionControlBlock *scb ) { uint8_t layerIndex; uint32_t direction = session_api->get_packet_direction(p); if(direction == PKT_FROM_CLIENT && !(scb->clientMplsHeader->start)) { for(layerIndex=0; layerIndex < p->next_layer; layerIndex++) { if( p->layers[layerIndex].proto == PROTO_MPLS && p->layers[layerIndex].start != NULL ) { scb->clientMplsHeader->length = p->layers[layerIndex].length; scb->clientMplsHeader->start = (uint8_t*)SnortPreprocAlloc(1, scb->clientMplsHeader->length, PP_STREAM, PP_MEM_CATEGORY_SESSION); memcpy(scb->clientMplsHeader->start,p->layers[layerIndex].start,scb->clientMplsHeader->length); break; } } } else if ( direction == PKT_FROM_SERVER && !(scb->serverMplsHeader->start)) { for(layerIndex=0; layerIndex < p->next_layer; layerIndex++) { if( p->layers[layerIndex].proto == PROTO_MPLS && p->layers[layerIndex].start != NULL ) { scb->serverMplsHeader->length = p->layers[layerIndex].length; scb->serverMplsHeader->start = (uint8_t*)SnortPreprocAlloc(1, scb->serverMplsHeader->length, PP_STREAM, 0); memcpy(scb->serverMplsHeader->start,p->layers[layerIndex].start, scb->serverMplsHeader->length); break; } } } else return; } /* * MAIN ENTRY POINT */ void StreamProcess(Packet *p, void *context) { SessionKey key; SessionControlBlock *scb; PROFILE_VARS; if (!firstPacketTime) firstPacketTime = p->pkth->ts.tv_sec; DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "++++++++++++++++++++++++++++++++++++++++++++++++++++++\n");); DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In Stream!\n");); // get the session and if NULL, bail we can't do anything with that... scb = p->ssnptr; if( scb == NULL ) { DEBUG_WRAP( DebugMessage( DEBUG_STREAM_STATE, "Stream Processing called with NULL pointer to session control block\n" ) ); return; } stream_session_config = scb->session_config; if( scb->stream_config_stale || scb->stream_config == NULL ) { scb->stream_config = sfPolicyUserDataGet( stream_online_config, getNapRuntimePolicy() ); if( scb->stream_config == NULL ) { ErrorMessage("Stream Configuration is NULL, Stream Packet Processing Terminated.\n"); return; } else { scb->proto_policy = NULL; scb->stream_config_stale = false; } } if(!IsEligible(p)) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Is not eligible!\n");); return; } #ifdef MPLS if(scb->clientMplsHeader != NULL && scb->serverMplsHeader != NULL ) { if(!(scb->clientMplsHeader->start) || !(scb->serverMplsHeader->start)) updateMplsHeaders(p,scb); } #endif PREPROC_PROFILE_START(s5PerfStats); /* Call individual TCP/UDP/ICMP/IP processing, per GET_IPH_PROTO(p) */ switch( GET_IPH_PROTO( p ) ) { case IPPROTO_TCP: if( session_api->protocol_tracking_enabled( SESSION_PROTO_TCP ) ) { StreamProcessTcp( p, scb, scb->proto_policy, &key ); checkOnewayStatus( SESSION_PROTO_TCP, scb ); } break; case IPPROTO_UDP: if (session_api->protocol_tracking_enabled( SESSION_PROTO_UDP ) ) { StreamProcessUdp(p, scb, scb->proto_policy, &key); checkOnewayStatus( SESSION_PROTO_UDP, scb ); } break; case IPPROTO_ICMP: if (session_api->protocol_tracking_enabled( SESSION_PROTO_ICMP ) ) { StreamProcessIcmp(p); checkOnewayStatus( SESSION_PROTO_ICMP, scb ); break; } // fall thru ... default: if (session_api->protocol_tracking_enabled( SESSION_PROTO_IP ) ) { StreamProcessIp(p, scb, &key); checkOnewayStatus( SESSION_PROTO_IP, scb ); } break; } PREPROC_PROFILE_END(s5PerfStats); } static inline int IsEligible(Packet *p) { if ((p->frag_flag) || (p->error_flags & PKT_ERR_CKSUM_IP)) return 0; if (p->packet_flags & PKT_REBUILT_STREAM) return 0; if (!IPH_IS_VALID(p)) return 0; switch(GET_IPH_PROTO(p)) { case IPPROTO_TCP: { if(p->tcph == NULL) return 0; if (p->error_flags & PKT_ERR_CKSUM_TCP) return 0; } break; case IPPROTO_UDP: { if(p->udph == NULL) return 0; if (p->error_flags & PKT_ERR_CKSUM_UDP) return 0; } break; case IPPROTO_ICMP: case IPPROTO_ICMPV6: { if(p->icmph == NULL) return 0; if (p->error_flags & PKT_ERR_CKSUM_ICMP) return 0; } break; default: if(p->iph == NULL) return 0; break; } return 1; } /*************************** API Implementations *******************/ static int StreamMidStreamDropAlert(void) { StreamConfig *config = sfPolicyUserDataGet(stream_online_config, getNapRuntimePolicy()); if (config == NULL) return 1; return (config->session_config->flags & STREAM_CONFIG_MIDSTREAM_DROP_NOALERT) ? 0 : 1; } static inline bool StreamOkToFlush(Packet *p) { SessionControlBlock *ssn; if ((p == NULL) || (p->ssnptr == NULL)) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Don't flush NULL packet or session\n");); return false; } ssn = p->ssnptr; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return false; if ((ssn->protocol != IPPROTO_TCP) || (p->packet_flags & PKT_REBUILT_STREAM)) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Don't flush on rebuilt packets\n");); return false; } return true; } static int StreamAlertFlushStream(Packet *p) { if (!StreamOkToFlush(p)) return 0; if (!(stream_session_config->flags & STREAM_CONFIG_FLUSH_ON_ALERT)) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Don't flush on alert from individual packet\n");); return 0; } /* Flush the listener queue -- this is the same side that * the packet gets inserted into */ StreamFlushListener(p, p->ssnptr); return 0; } static int StreamRequestFlushStream(Packet *p) { if (!StreamOkToFlush(p)) return 0; /* Flush the talker queue -- this is the opposite side that * the packet gets inserted into */ StreamFlushListener(p, p->ssnptr); return 0; } static int StreamResponseFlushStream(Packet *p) { if (!StreamOkToFlush(p)) return 0; /* Flush the talker queue -- this is the opposite side that * the packet gets inserted into */ StreamFlushTalker(p, p->ssnptr); return 0; } static int StreamAddSessionAlert( void *ssnptr, Packet *p, uint32_t gid, uint32_t sid) { SessionControlBlock *ssn; if ( !ssnptr ) return 0; ssn = (SessionControlBlock *)ssnptr; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 0; /* Don't need to do this for other protos because they don't do any reassembly. */ if ( GET_IPH_PROTO(p) != IPPROTO_TCP ) return 0; return StreamAddSessionAlertTcp(ssn, p, gid, sid); } /* return non-zero if gid/sid have already been seen */ static int StreamCheckSessionAlert( void *ssnptr, Packet *p, uint32_t gid, uint32_t sid) { SessionControlBlock *ssn; if ( !ssnptr ) return 0; ssn = (SessionControlBlock *)ssnptr; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 0; /* Don't need to do this for other protos because they don't do any reassembly. */ if ( GET_IPH_PROTO(p) != IPPROTO_TCP ) return 0; return StreamCheckSessionAlertTcp(ssn, p, gid, sid); } static int StreamUpdateSessionAlert( void *ssnptr, Packet *p, uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second) { SessionControlBlock *ssn; if ( !ssnptr ) return 0; ssn = (SessionControlBlock *)ssnptr; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 0; /* Don't need to do this for other protos because they don't do any reassembly. */ if ( GET_IPH_PROTO(p) != IPPROTO_TCP ) return 0; return StreamUpdateSessionAlertTcp(ssn, p, gid, sid, event_id, event_second); } static void StreamSetExtraData (void* pv, Packet* p, uint32_t flag) { SessionControlBlock* ssn = pv; if ( !ssn ) return; StreamSetExtraDataTcp(ssn, p, flag); } // FIXTHIS get pv/ssn from packet directly? static void StreamClearExtraData (void* pv, Packet* p, uint32_t flag) { SessionControlBlock* ssn = pv; if ( !ssn ) return; StreamClearExtraDataTcp(ssn, p, flag); } static int StreamGetRebuiltPackets( Packet *p, PacketIterator callback, void *userdata) { SessionControlBlock *ssn = (SessionControlBlock*)p->ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return 0; /* Only if this is a rebuilt packet */ if (!(p->packet_flags & PKT_REBUILT_STREAM)) return 0; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 0; return GetTcpRebuiltPackets(p, ssn, callback, userdata); } static int StreamGetStreamSegments( Packet *p, StreamSegmentIterator callback, void *userdata) { SessionControlBlock *ssn = (SessionControlBlock*)p->ssnptr; if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP)) return -1; /* Only if this is a rebuilt packet */ if (!(p->packet_flags & PKT_REBUILT_STREAM)) return -1; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return -1; return GetTcpStreamSegments(p, ssn, callback, userdata); } static void StreamUpdateDirection( void * scbptr, char dir, sfaddr_t* ip, uint16_t port ) { SessionControlBlock *scb = (SessionControlBlock *)scbptr; if (!scb) return; if (StreamSetRuntimeConfiguration(scb, scb->protocol) == -1) return; switch (scb->protocol) { case IPPROTO_TCP: TcpUpdateDirection(scb, dir, ip, port); break; case IPPROTO_UDP: UdpUpdateDirection(scb, dir, ip, port); break; case IPPROTO_ICMP: //IcmpUpdateDirection(scb, dir, ip, port); break; } } static char StreamGetReassemblyDirection(void *ssnptr) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return SSN_DIR_NONE; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return SSN_DIR_NONE; return StreamGetReassemblyDirectionTcp(ssn); } static uint32_t StreamGetFlushPoint(void *ssnptr, char dir) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP)) return 0; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 0; return StreamGetFlushPointTcp(ssn, dir); } static void StreamSetFlushPoint(void *ssnptr, char dir, uint32_t flush_point) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if ((ssn == NULL) || (ssn->protocol != IPPROTO_TCP)) return; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return; StreamSetFlushPointTcp(ssn, dir, flush_point); } static char StreamSetReassembly(void *ssnptr, uint8_t flush_policy, char dir, char flags) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return 0; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 0; return StreamSetReassemblyTcp(ssn, flush_policy, dir, flags); } #ifdef HAVE_DAQ_DECRYPTED_SSL static int StreamSimulateTcpAck(void *ssnptr, uint8_t dir, uint32_t len) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return -1; stream_session_config = ssn->session_config; return StreamSimulatePeerTcpAckp(ssn, dir, len); } #endif static char StreamGetReassemblyFlushPolicy(void *ssnptr, char dir) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return STREAM_FLPOLICY_NONE; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return STREAM_FLPOLICY_NONE; return StreamGetReassemblyFlushPolicyTcp(ssn, dir); } static char StreamIsStreamSequenced(void *ssnptr, char dir) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return 1; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 1; return StreamIsStreamSequencedTcp(ssn, dir); } static int StreamMissingInReassembled(void *ssnptr, char dir) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return SSN_MISSING_NONE; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return SSN_MISSING_NONE; return StreamMissingInReassembledTcp(ssn, dir); } static void StreamDropPacket( Packet *p ) { SessionControlBlock* scb = (SessionControlBlock*)p->ssnptr; if ( !scb ) return; switch (scb->protocol) { case IPPROTO_TCP: StreamTcpSessionClear(p); break; case IPPROTO_UDP: UdpSessionCleanup(scb); break; case IPPROTO_IP: IpSessionCleanup(scb); break; case IPPROTO_ICMP: IcmpSessionCleanup(scb); break; default: break; } if (!(p->packet_flags & PKT_STATELESS)) session_api->drop_traffic(p, p->ssnptr, SSN_DIR_BOTH); } static char StreamPacketsMissing(void *ssnptr, char dir) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return 1; if (StreamSetRuntimeConfiguration(ssn, ssn->protocol) == -1) return 1; return StreamPacketsMissingTcp(ssn, dir); } #ifdef TARGET_BASED static void initServiceFilterStatus( struct _SnortConfig *sc ) { SFGHASH_NODE *hashNode; tSfPolicyId policyId = 0; if( sc == NULL ) { FatalError("%s(%d) Snort config for parsing is NULL.\n", __FILE__, __LINE__); } for( hashNode = sfghash_findfirst( sc->otn_map ); hashNode; hashNode = sfghash_findnext( sc->otn_map ) ) { OptTreeNode *otn = ( OptTreeNode * ) hashNode->data; for( policyId = 0; policyId < otn->proto_node_num; policyId++ ) { RuleTreeNode *rtn = getRtnFromOtn( otn, policyId ); if( rtn && ( rtn->proto == IPPROTO_TCP ) ) { unsigned int svc_idx; for( svc_idx = 0; svc_idx < otn->sigInfo.num_services; svc_idx++ ) if( otn->sigInfo.services[svc_idx].service_ordinal ) setServiceFilterStatus( sc, otn->sigInfo.services[svc_idx].service_ordinal, PORT_MONITOR_SESSION, policyId, 1 ); } } } } static void setServiceFilterStatus( struct _SnortConfig *sc, int service, int status, tSfPolicyId policyId, int parsing ) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( config != NULL ) config->service_filter[ service ] = status; } static int getServiceFilterStatus( struct _SnortConfig *sc, int service, tSfPolicyId policyId, int parsing ) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( config != NULL ) return config->service_filter[ service ]; else return PORT_MONITOR_NONE; } #endif int isPacketFilterDiscard( Packet *p, int ignore_any_rules ) { uint8_t action = 0; tPortFilterStats *pPortFilterStats = NULL; tSfPolicyId policy_id = getNapRuntimePolicy(); #ifdef TARGET_BASED int protocolId = GetProtocolReference(p); #endif #ifdef TARGET_BASED if( ( protocolId > 0 ) && getServiceFilterStatus( NULL, protocolId, policy_id, 0 ) ) { return PORT_MONITOR_PACKET_PROCESS; } #endif switch( GET_IPH_PROTO( p ) ) { case IPPROTO_TCP: if( session_api->protocol_tracking_enabled( SESSION_PROTO_TCP ) ) { action = s5TcpGetPortFilterStatus( NULL, p->sp, policy_id, 0 ) | s5TcpGetPortFilterStatus( NULL, p->dp, policy_id, 0 ); } pPortFilterStats = &s5stats.tcp_port_filter; break; case IPPROTO_UDP: if( session_api->protocol_tracking_enabled( SESSION_PROTO_UDP ) ) { action = s5UdpGetPortFilterStatus( NULL, p->sp, policy_id, 0 ) | s5UdpGetPortFilterStatus( NULL, p->dp, policy_id, 0 ); } pPortFilterStats = &s5stats.udp_port_filter; break; default: return PORT_MONITOR_PACKET_PROCESS; } if( !( action & PORT_MONITOR_SESSION_BITS ) ) { if( !( action & PORT_MONITOR_INSPECT ) && ignore_any_rules ) { /* Ignore this TCP packet entirely */ DisableDetect( p ); //otn_tmp = NULL; pPortFilterStats->filtered++; } else { pPortFilterStats->inspected++; } return PORT_MONITOR_PACKET_DISCARD; } pPortFilterStats->session_tracked++; return PORT_MONITOR_PACKET_PROCESS; } int isPacketFilterDiscardUdp ( Packet *p, int ignore_any_rules ) { uint8_t action_ips = 0, action_nap = 0; tPortFilterStats *pPortFilterStats = NULL; SessionControlBlock *scb; tSfPolicyId policy_id_ips = getIpsRuntimePolicy(); tSfPolicyId policy_id_nap = getNapRuntimePolicy(); SnortPolicy *policy; PreprocEnableMask enabled_pps; bool nap_inspect = false; scb = p->ssnptr; if ( !scb ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Session control block of packet is NULL.\n");); return PORT_MONITOR_PACKET_DISCARD; } if ( session_api->protocol_tracking_enabled( SESSION_PROTO_UDP ) && ( snort_conf->udp_ips_port_filter_list ) ) { action_ips = s5UdpGetIPSPortFilterStatus (snort_conf, p->sp, p->dp, policy_id_ips); } pPortFilterStats = &s5stats.udp_port_filter; // Check if NAP has marked it as inspect/filter. action_nap = s5UdpGetPortFilterStatus (NULL, p->sp, policy_id_nap, 0) | s5UdpGetPortFilterStatus (NULL, p->dp, policy_id_nap, 0); if ( !( action_nap & PORT_MONITOR_SESSION_BITS ) && ( action_nap & PORT_MONITOR_INSPECT ) && ignore_any_rules ) { nap_inspect = true ; } if ( !( action_ips & PORT_MONITOR_SESSION_BITS ) ) { if ( !( action_ips & PORT_MONITOR_INSPECT ) && ignore_any_rules ) { // Port not present in IPS port list too, disable detection. DisableDetect( p ); } else { /* * If nap_inspect is true it implies NAP marked it for inspect, now IPS too marking for inspect, * so no change in counter. * If nap_inspect is false ie: NAP marked for filter, now IPS marks it to inspect undo the NAP counter. */ if ( !nap_inspect ) { sfBase.total_udp_filtered_packets--; pPortFilterStats->filtered--; pPortFilterStats->inspected++; } } return PORT_MONITOR_PACKET_DISCARD; } // Undo NAPs increment and enable detection if ( nap_inspect ) pPortFilterStats->inspected--; else pPortFilterStats->filtered--; pPortFilterStats->session_tracked++; policy = snort_conf->targeted_policies[ getNapRuntimePolicy() ]; enabled_pps = policy->pp_enabled[ p->dp ] | policy->pp_enabled[ p->sp ]; EnableContentPreprocDetection (p,enabled_pps); return PORT_MONITOR_PACKET_PROCESS; } static uint8_t StreamRegisterPAFPort( struct _SnortConfig *sc, tSfPolicyId id, uint16_t server_port, bool to_server, PAF_Callback cb, bool autoEnable) { return s5_paf_register_port( sc, id, server_port, to_server, cb, autoEnable ); } static uint8_t StreamRegisterPAFService( struct _SnortConfig *sc, tSfPolicyId id, uint16_t service, bool to_server, PAF_Callback cb, bool autoEnable) { return s5_paf_register_service( sc, id, service, to_server, cb, autoEnable ); } static uint32_t StreamRegisterXtraData( LogFunction f ) { uint32_t i = 0; while( i < xtradata_func_count ) { if( xtradata_map[i++] == f ) { return i; } } if( xtradata_func_count == LOG_FUNC_MAX ) return 0; xtradata_map[xtradata_func_count++] = f; return xtradata_func_count; } static uint32_t StreamGetXtraDataMap( LogFunction **f ) { if( f ) { *f = xtradata_map; return xtradata_func_count; } else return 0; } static void StreamRegisterXtraDataLog( LogExtraData f, void *config ) { extra_data_log = f; extra_data_config = config; } void** StreamGetPAFUserData( void* ssnptr, bool to_server, uint8_t id ) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return NULL; return StreamGetPAFUserDataTcp( ( SessionControlBlock * ) ssnptr, to_server, id ); } static bool StreamIsPafActive( void* ssnptr, bool to_server ) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return false; return StreamIsPafActiveTcp( ( SessionControlBlock * ) ssnptr, to_server ); } static bool StreamActivatePaf( void* ssnptr, int dir, int16_t service, uint8_t type ) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if (!ssn || ssn->protocol != IPPROTO_TCP) return false; return StreamActivatePafTcp( ( SessionControlBlock *) ssnptr, dir, service, type ); } static void StreamResetPolicy( void *ssnptr, int dir, uint16_t policy, uint16_t mss ) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if( ssn ) { if (ssn->protocol != IPPROTO_TCP ) return; StreamResetPolicyTcp( ssnptr, dir, policy, mss ); } return; } static void StreamSetSessionDecrypted( void *ssnptr, bool enable ) { SessionControlBlock *ssn = (SessionControlBlock *)ssnptr; if( ssn ) { if (ssn->protocol != IPPROTO_TCP ) return; StreamSetSessionDecryptedTcp( ssnptr, enable ); } return; } static bool StreamIsSessionDecrypted( void *ssnptr ) { SessionControlBlock *ssn; if(ssnptr) { ssn = (SessionControlBlock *)ssnptr; if (ssn->protocol == IPPROTO_TCP ) return StreamIsSessionDecryptedTcp( ssnptr ); else return false; } else return false; } static void s5SetPortFilterStatus( struct _SnortConfig *sc, IpProto protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing ) { switch( protocol ) { case IPPROTO_TCP: s5TcpSetPortFilterStatus( sc, port, status, policyId, parsing ); break; case IPPROTO_UDP: s5UdpSetPortFilterStatus( sc, port, status, policyId, parsing ); break; case IPPROTO_ICMP: break; default: break; } } static void s5UnsetPortFilterStatus( struct _SnortConfig *sc, IpProto protocol, uint16_t port, uint16_t status, tSfPolicyId policyId, int parsing ) { if( status <= PORT_MONITOR_SESSION ) return; switch( protocol ) { case IPPROTO_TCP: s5TcpUnsetPortFilterStatus( sc, port, status, policyId, parsing ); break; case IPPROTO_UDP: s5UdpUnsetPortFilterStatus( sc, port, status, policyId, parsing ); break; case IPPROTO_ICMP: break; default: break; } } static void StreamForceSessionExpiration( void *ssnptr ) { SessionControlBlock *scb = ( SessionControlBlock * ) ssnptr; if( StreamExpireSession( scb ) ) { #ifdef ENABLE_HA SessionHANotifyDeletion( scb ); #endif } } static void StreamForceDeleteSession(void *ssnptr ) { SessionControlBlock *scb = ( SessionControlBlock * ) ssnptr; if(scb) StreamDeleteSession( scb ); } static void registerReassemblyPort( char *network, uint16_t port, int reassembly_direction ) { registerPortForReassembly( network, port, reassembly_direction ); } static void unregisterReassemblyPort( char *network, uint16_t port, int reassembly_direction ) { unregisterPortForReassembly( network, port, reassembly_direction ); } #define CB_MAX 32 static Stream_Callback stream_cb[ CB_MAX ]; static unsigned stream_cb_idx = 1; static unsigned StreamRegisterHandler( Stream_Callback cb ) { unsigned id; for ( id = 1; id < stream_cb_idx; id++ ) { if ( stream_cb[id] == cb ) break; } if ( id == CB_MAX ) return 0; if ( id == stream_cb_idx ) stream_cb[stream_cb_idx++] = cb; return id; } static bool StreamSetHandler( void* ssnptr, unsigned id, Stream_Event se ) { SessionControlBlock *scb = ( SessionControlBlock * ) ssnptr; if ( se >= SE_MAX || scb->handler[ se ] ) return false; scb->handler[ se ] = id; return true; } static uint32_t StreamGetPreprocFlags( void *ssnptr) { SessionControlBlock *scb = ( SessionControlBlock * ) ssnptr; if( scb ) return StreamGetPreprocFlagsTcp(ssnptr); return 0; } #if defined(FEAT_OPEN_APPID) static void SetApplicationId(void* ssnptr, int16_t serviceAppId, int16_t clientAppId, int16_t payloadAppId, int16_t miscAppId) { SessionControlBlock *scb = (SessionControlBlock *)ssnptr; scb->app_protocol_id[APP_PROTOID_SERVICE] = serviceAppId; scb->app_protocol_id[APP_PROTOID_CLIENT] = clientAppId; scb->app_protocol_id[APP_PROTOID_PAYLOAD] = payloadAppId; scb->app_protocol_id[APP_PROTOID_MISC] = miscAppId; } static void GetApplicationId(void* ssnptr, int16_t *serviceAppId, int16_t *clientAppId, int16_t *payloadAppId, int16_t *miscAppId) { SessionControlBlock *scb = (SessionControlBlock *)ssnptr; *serviceAppId = scb->app_protocol_id[APP_PROTOID_SERVICE]; *clientAppId = scb->app_protocol_id[APP_PROTOID_CLIENT]; *payloadAppId = scb->app_protocol_id[APP_PROTOID_PAYLOAD]; *miscAppId = scb->app_protocol_id[APP_PROTOID_MISC]; } #define HTTP_HEADER_PROCESSOR_MAX 10 static Http_Processor_Callback http_header_processor_cb[HTTP_HEADER_PROCESSOR_MAX]; static unsigned http_header_processor_cb_idx = 1; static int RegisterHttpHeaderCallback(Http_Processor_Callback cb) { unsigned id; for ( id = 1; id < http_header_processor_cb_idx; id++ ) { if ( http_header_processor_cb[id] == cb ) break; } if ( id == HTTP_HEADER_PROCESSOR_MAX ) return -1; if ( id == http_header_processor_cb_idx ) http_header_processor_cb[http_header_processor_cb_idx++] = cb; return 0; } void CallHttpHeaderProcessors(Packet* p, HttpParsedHeaders * const headers) { unsigned id; for ( id = 1; id < http_header_processor_cb_idx; id++ ) { http_header_processor_cb[id](p, headers); } } bool IsAnybodyRegisteredForHttpHeader(void) { return ((http_header_processor_cb_idx - 1) > 0); } #endif /* defined(FEAT_OPEN_APPID) */ #define SERVICE_EVENT_SUBSCRIBER_MAX 10 #define SERVICE_EVENT_TYPE_MAX 10 static ServiceEventNotifierFunc serviceEventRegistry[PP_MAX][SERVICE_EVENT_TYPE_MAX][SERVICE_EVENT_SUBSCRIBER_MAX]; static bool serviceEventSubscribe(unsigned int preprocId, ServiceEventType eventType, ServiceEventNotifierFunc cb) { unsigned i; ServiceEventNotifierFunc *notifierPtr; if (preprocId >= PP_MAX || eventType >= SERVICE_EVENT_TYPE_MAX) return false; notifierPtr = serviceEventRegistry[preprocId][eventType]; for ( i = 0; i < SERVICE_EVENT_SUBSCRIBER_MAX; i++ , notifierPtr++) { if ( *notifierPtr == cb ) return true; if (!(*notifierPtr)) { *notifierPtr = cb; return true; } } return false; } static bool serviceEventPublish(unsigned int preprocId, void *ssnptr, ServiceEventType eventType, void * eventData) { unsigned i; ServiceEventNotifierFunc *notifierPtr; if (preprocId >= PP_MAX || eventType >= SERVICE_EVENT_TYPE_MAX) return false; notifierPtr = serviceEventRegistry[preprocId][eventType]; for ( i = 0; i < SERVICE_EVENT_SUBSCRIBER_MAX; i++ , notifierPtr++) { if (*notifierPtr) (*notifierPtr)(ssnptr, eventType, eventData); else break; } return true; } void StreamCallHandler( Packet* p, unsigned id ) { assert( id && id < stream_cb_idx && stream_cb[ id ] ); stream_cb[ id ]( p ); } static void setFtpFilePosition (void *scbptr,bool flush) { SetFTPFileLocation(scbptr,flush); return; } static int StreamSetApplicationProtocolIdExpectedPreassignCallbackId( const Packet *ctrlPkt, sfaddr_t* srcIP, uint16_t srcPort, sfaddr_t* dstIP, uint16_t dstPort, uint8_t protocol, int16_t protoId, uint32_t preprocId, void *protoData, void ( *protoDataFreeFn )( void * ), unsigned cbId, Stream_Event se, struct _ExpectNode** packetExpectedNode) { return StreamExpectAddChannelPreassignCallback(ctrlPkt, srcIP, srcPort, dstIP, dstPort, SSN_DIR_BOTH, 0, protocol, STREAM_EXPECTED_CHANNEL_TIMEOUT, protoId, preprocId, protoData, protoDataFreeFn, cbId, se, packetExpectedNode); } #define FTP_PROCESSOR_MAX 2 static FTP_Processor_Flush_Callback ftp_processor_flush_cb[FTP_PROCESSOR_MAX]; static unsigned ftp_processor_flush_cb_idx = 1; static int RegisterFTPFlushCallback(FTP_Processor_Flush_Callback cb) { unsigned id; for ( id = 1; id < ftp_processor_flush_cb_idx; id++ ) { if ( ftp_processor_flush_cb[id] == cb ) break; } if ( id == FTP_PROCESSOR_MAX) return -1; if ( id == ftp_processor_flush_cb_idx) ftp_processor_flush_cb[ftp_processor_flush_cb_idx++] = cb; return 0; } void CallFTPFlushProcessor(Packet* p) { unsigned id; for ( id = 1; id < ftp_processor_flush_cb_idx; id++ ) { ftp_processor_flush_cb[id](p); } } #ifdef SNORT_RELOAD static void StreamTcpReload( struct _SnortConfig *sc, char *args, void **new_config ) { StreamConfig *config; config = initStreamPolicyConfig( sc, true ); if( !config->session_config->track_tcp_sessions ) return; if( config->tcp_config == NULL ) { config->tcp_config = ( StreamTcpConfig * ) SnortPreprocAlloc( 1, sizeof( StreamTcpConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); StreamTcpInitFlushPoints(); StreamTcpRegisterRuleOptions( sc ); AddFuncToPreprocPostConfigList( sc, StreamPostConfigTcp, config->tcp_config ); } /* Call the protocol specific initializer */ StreamTcpPolicyInit( sc, config->tcp_config, args ); *new_config = getStreamConfigContext( true ); } static void StreamUdpReload(struct _SnortConfig *sc, char *args, void **new_config) { StreamConfig *config; config = initStreamPolicyConfig( sc, true ); if( !config->session_config->track_udp_sessions ) return; if( config->udp_config == NULL ) config->udp_config = ( StreamUdpConfig * ) SnortPreprocAlloc( 1, sizeof( StreamUdpConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); /* Call the protocol specific initializer */ StreamUdpPolicyInit( config->udp_config, args ); *new_config = getStreamConfigContext( true ); } static void StreamIcmpReload(struct _SnortConfig *sc, char *args, void **new_config) { StreamConfig *config; config = initStreamPolicyConfig( sc, true ); if( !config->session_config->track_icmp_sessions ) return; if( config->icmp_config == NULL ) config->icmp_config = ( StreamIcmpConfig * ) SnortPreprocAlloc(1, sizeof( StreamIcmpConfig ), PP_STREAM, PP_MEM_CATEGORY_CONFIG); /* Call the protocol specific initializer */ StreamIcmpPolicyInit( config->icmp_config, args ); *new_config = getStreamConfigContext( true ); } static void StreamIpReload(struct _SnortConfig *sc, char *args, void **new_config) { StreamConfig *config; config = initStreamPolicyConfig( sc, true ); if( !config->session_config->track_ip_sessions ) return; if( config->ip_config == NULL ) config->ip_config = ( StreamIpConfig * ) SnortPreprocAlloc( 1, sizeof( *config->ip_config ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); /* Call the protocol specific initializer */ StreamIpPolicyInit( config->ip_config, args ); *new_config = getStreamConfigContext( true ); } static int StreamReloadVerify( struct _SnortConfig *sc, void *swap_config ) { tSfPolicyUserContextId ssc = ( tSfPolicyUserContextId ) swap_config; if( ( ssc == NULL ) || ( stream_online_config == NULL ) ) return 0; if( sfPolicyUserDataIterate( sc, ssc, StreamVerifyConfigPolicy ) != 0 ) return -1; #ifdef TARGET_BASED initServiceFilterStatus( sc ); #endif return 0; } static void StreamFreeOldConfig( void *data ) { if( data == NULL ) return; StreamFreeConfigs( ( tSfPolicyUserContextId ) data ); } static int StreamReloadSwapPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void *pData ) { StreamConfig *stream_config = ( StreamConfig * ) pData; if( stream_config->session_config->policy_ref_count[ policyId ] == 0 ) { sfPolicyUserDataClear( config, policyId ); StreamFreeConfig( stream_config ); } else register_no_ref_policy_callback(stream_config->session_config, StreamFreeOldConfig, (void *)config); return 0; } static void *StreamReloadSwap( struct _SnortConfig *sc, void *swap_config ) { tSfPolicyUserContextId new_config = ( tSfPolicyUserContextId ) swap_config; tSfPolicyUserContextId old_config = stream_online_config; if( ( new_config == stream_online_config ) || new_config == NULL ) return NULL; stream_online_config = new_config; stream_parsing_config = NULL; // free memory for all configs with no refs sfPolicyUserDataIterate( sc, old_config, StreamReloadSwapPolicy ); if( sfPolicyUserPolicyGetActive( old_config ) == 0 ) return old_config; // still some active sessions with ref to old config... return NULL; } static void StreamReloadSwapFree( void *data ) { if( data == NULL ) return; if( !old_config_freed ) { StreamFreeConfigs( ( tSfPolicyUserContextId ) data ); old_config_freed = true; } } #endif static void StreamRegisterPAFFree(uint8_t id, PAF_Free_Callback cb) { s5_paf_register_free(id, cb); } static Packet* getWirePacket() { return getWirePacketTcp(); } static uint8_t getFlushPolicyDir() { return getFlushPolicyDirTcp(); } static bool StreamIsSessionHttp2( void *ssnptr ) { SessionControlBlock *ssn; if(ssnptr) { ssn = (SessionControlBlock *)ssnptr; if (ssn->protocol == IPPROTO_TCP ) return StreamIsSessionHttp2Tcp( ssnptr ); else return false; } else return false; } static void StreamSetSessionHttp2( void *ssnptr) { if( ssnptr ) StreamSetSessionHttp2Tcp( ssnptr ); return; } static bool StreamShowRebuiltPackets() { return (stream_session_config->flags & STREAM_CONFIG_SHOW_PACKETS); } static bool StreamIsSessionHttp2Upg( void *ssnptr ) { SessionControlBlock *ssn; if(ssnptr) { ssn = (SessionControlBlock *)ssnptr; if (ssn->protocol == IPPROTO_TCP ) return StreamIsSessionHttp2UpgTcp( ssnptr ); else return false; } else return false; } static void StreamSetSessionHttp2Upg( void *ssnptr) { if( ssnptr ) StreamSetSessionHttp2UpgTcp( ssnptr ); return; } snort-2.9.20/src/preprocessors/perf.h0000644000175000017500000001070614241077053015705 0ustar apoapo/* ** $Id$ ** ** perf.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Dan Roelker ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** ** DESCRIPTION ** These are the basic functions and structures that are needed to call ** performance functions. ** ** Dan Roelker ** ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef _PERF_H #define _PERF_H #include "perf-base.h" #include "perf-flow.h" #include "perf-event.h" #include "sf_types.h" #include "snort_debug.h" #include "decode.h" // Perf Flags #define SFPERF_BASE 0x00000001 #define SFPERF_FLOW 0x00000002 #define SFPERF_EVENT 0x00000004 #define SFPERF_BASE_MAX 0x00000008 #define SFPERF_CONSOLE 0x00000010 #define SFPERF_PKTCNT 0x00000020 #define SFPERF_FLOWIP 0x00000040 #define SFPERF_TIME_COUNT 0x00000080 #define SFPERF_MAX_BASE_STATS 0x00000100 #define SFPERF_SUMMARY_BASE 0x00001000 #define SFPERF_SUMMARY_FLOW 0x00002000 #define SFPERF_SUMMARY_FLOWIP 0x00004000 #define SFPERF_SUMMARY_EVENT 0x00008000 #define SFPERF_SUMMARY \ (SFPERF_SUMMARY_BASE|SFPERF_SUMMARY_FLOW|SFPERF_SUMMARY_FLOWIP|SFPERF_SUMMARY_EVENT) #define ROLLOVER_THRESH 512 #define MAX_PERF_FILE_SIZE INT32_MAX #define MIN_PERF_FILE_SIZE 4096 /* The perfmonitor configuration */ typedef struct _SFPERF { int perf_flags; uint32_t pkt_cnt; int sample_interval; char *file; FILE *fh; int base_reset; int flow_max_port_to_track; char *flow_file; FILE *flow_fh; uint32_t max_file_size; char *flowip_file; FILE *flowip_fh; uint32_t flowip_memcap; } SFPERF; extern SFBASE sfBase; extern SFFLOW sfFlow; extern SFEVENT sfEvent; extern SFPERF* perfmon_config; extern int perfmon_rotate_perf_file; #ifdef SNORT_RELOAD typedef enum { PERF_NOT_RELOADING, PERF_RELOAD, PERF_RELOAD_VERIFY, PERF_RELOAD_SWAP, PERF_RELOAD_SWAP_FREE, PERF_RELOAD_ADUST } PERFRELOAD_STATUS; extern PERFRELOAD_STATUS perfmon_reload_status; #endif void sfInitPerformanceStatistics(SFPERF *); FILE * sfOpenBaseStatsFile(const char *); void sfCloseBaseStatsFile(SFPERF *sfPerf); FILE * sfOpenFlowStatsFile(const char *); void sfCloseFlowStatsFile(SFPERF *sfPerf); FILE * sfOpenFlowIPStatsFile(const char *); void sfCloseFlowIPStatsFile(SFPERF *sfPerf); int sfRotateBaseStatsFile(SFPERF *sfPerf); int sfRotateFlowStatsFile(SFPERF *sfPerf); void sfPerformanceStats(SFPERF *, Packet *); void sfPerformanceStatsOOB(SFPERF *, time_t); void sfPerfStatsSummary(SFPERF *); void SetSampleTime(SFPERF *, Packet *); void InitPerfStats(SFPERF *sfPerf); void syncAllStats(SFPERF *sfPerf, SFPERF *); static inline void SetRotatePerfFileFlag(void) { perfmon_rotate_perf_file = 1; } static inline int IsSetRotatePerfFileFlag(void) { return perfmon_rotate_perf_file; } static inline void ClearRotatePerfFileFlag(void) { perfmon_rotate_perf_file = 0; } #endif snort-2.9.20/src/preprocessors/spp_perfmonitor.c0000644000175000017500000010206014241077114020163 0ustar apoapo/* $Id$ ** ** spp_perfmonitor.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Marc Norton ** Dan Roelker ** ** NOTES ** 6.4.02 - Initial Source Code. Norton/Roelker ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #include #include #include #ifndef WIN32 # include # include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "plugbase.h" #include "mstring.h" #include "util.h" #include "snort_debug.h" #include "parser.h" #include "sfdaq.h" #include "snort.h" #include "perf.h" #include "perf-base.h" #include "profiler.h" #include "session_api.h" #include "reload.h" #include "control/sfcontrol_funcs.h" #include "control/sfcontrol.h" #ifdef SNORT_RELOAD #ifdef REG_TEST #include "reg_test.h" #endif #endif // Performance statistic types //#define PERFMON_ARG__BASE "base" #define PERFMON_ARG__FLOW "flow" #define PERFMON_ARG__FLOW_IP "flow-ip" #define PERFMON_ARG__EVENTS "events" // Logging #define PERFMON_ARG__FILE "file" #define PERFMON_ARG__LOG_DIR_FILE "snortfile" #define PERFMON_ARG__FLOW_FILE "flow-file" #define PERFMON_ARG__FLOW_IP_FILE "flow-ip-file" #define PERFMON_ARG__CONSOLE "console" #define PERFMON_ARG__MAX_FILE_SIZE "max_file_size" // When to log #define PERFMON_ARG__TIME "time" #define PERFMON_ARG__PKT_COUNT "pktcnt" #define PERFMON_ARG__SUMMARY "atexitonly" #define PERFMON_SUMMARY_OPT__BASE "base-stats" #define PERFMON_SUMMARY_OPT__FLOW "flow-stats" #define PERFMON_SUMMARY_OPT__FLOW_IP "flow-ip-stats" #define PERFMON_SUMMARY_OPT__EVENTS "events-stats" // Misc #define PERFMON_ARG__FLOW_PORTS "flow-ports" #define PERFMON_ARG__ACCUMULATE "accumulate" #define PERFMON_ARG__RESET "reset" #define PERFMON_ARG__MAX_STATS "max" #define PERFMON_ARG__FLOW_IP_MEMCAP "flow-ip-memcap" #define PERFMON_ARG__FLOW_IP_MEMCAP_MIN 8200 SFPERF *perfmon_config = NULL; /* * Protype these forward references, and don't clutter up the name space */ static void PerfMonitorInit(struct _SnortConfig *, char *); static void ParsePerfMonitorArgs(struct _SnortConfig *, SFPERF *, char *); static void ProcessPerfMonitor(Packet *, void *); static void PerfMonitorCleanExit(int, void *); static void PerfMonitorReset(int, void *); static void PerfMonitorResetStats(int, void *); static int PerfMonitorVerifyConfig(struct _SnortConfig *sc); static void PerfMonitorFreeConfig(SFPERF *); static void PerfMonitorOpenLogFiles(struct _SnortConfig *, void *); #ifndef WIN32 static void PerfMonitorChangeLogFilesPermission(void); #endif #ifdef SNORT_RELOAD static void PerfMonitorReload(struct _SnortConfig *, char *, void **); static int PerfmonReloadVerify(struct _SnortConfig *, void *); static void * PerfMonitorReloadSwap(struct _SnortConfig *, void *); static void PerfMonitorReloadSwapFree(void *); static bool PerfmonitorReloadAdjustFunc(bool idle, tSfPolicyId raPolicyId, void* userData); #endif static int FlowIPStart(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char *statusBuf, int statusBuf_len); static int FlowIPStop(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char *statusBuf, int statusBuf_len); static void FlowIPShow(uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f); #ifdef PERF_PROFILING PreprocStats perfmonStats; #endif /* * Function: SetupPerfMonitor() * * Purpose: Registers the preprocessor keyword and initialization * function into the preprocessor list. This is the function that * gets called from InitPreprocessors() in plugbase.c. * * Arguments: None. * * Returns: void function * */ void SetupPerfMonitor(void) { /* link the preprocessor keyword to the init function in the preproc list */ #ifndef SNORT_RELOAD RegisterPreprocessor("PerfMonitor", PerfMonitorInit); #else RegisterPreprocessor("PerfMonitor", PerfMonitorInit, PerfMonitorReload, PerfmonReloadVerify, PerfMonitorReloadSwap, PerfMonitorReloadSwapFree); #endif ControlSocketRegisterHandler(CS_TYPE_FLOWIP_START, &FlowIPStart, NULL, NULL); ControlSocketRegisterHandler(CS_TYPE_FLOWIP_STOP, &FlowIPStop, NULL, NULL); ControlSocketRegisterHandler(CS_TYPE_FLOWIP_SHOW, NULL, NULL, &FlowIPShow); DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Preprocessor: PerfMonitor is setup...\n");); } /* * Function: PerfMonitorInit(char *) * * Purpose: Calls the argument parsing function, performs final setup on data * structs, links the preproc function into the function list. * * Arguments: args => ptr to argument string * * Returns: void function * */ static void PerfMonitorInit(struct _SnortConfig *sc, char *args) { DEBUG_WRAP(DebugMessage(DEBUG_PLUGIN,"Preprocessor: PerfMonitor Initialized\n");); //not policy specific. Perf monitor configuration should be in the default //configuration file. if( ( perfmon_config != NULL ) || getParserPolicy( sc ) != 0 ) { #if 0 // TBD-EDM ParseError("Perfmonitor can only be configured in default policy and only once.\n"); #else WarningMessage( "Perfmonitor can only be configured in default policy and only once.\n"); #endif return; } perfmon_config = (SFPERF *)SnortAlloc(sizeof(SFPERF)); /* parse the argument list from the rules file */ ParsePerfMonitorArgs(sc, perfmon_config, args); InitPerfStats(perfmon_config); #ifndef WIN32 PerfMonitorChangeLogFilesPermission(); #endif /* register callbacks */ AddFuncToPreprocCleanExitList(PerfMonitorCleanExit, NULL, PRIORITY_LAST, PP_PERFMONITOR); AddFuncToPreprocResetList(PerfMonitorReset, NULL, PRIORITY_LAST, PP_PERFMONITOR); AddFuncToPreprocResetStatsList(PerfMonitorResetStats, NULL, PRIORITY_LAST, PP_PERFMONITOR); AddFuncToConfigCheckList( sc, PerfMonitorVerifyConfig ); AddFuncToPreprocPostConfigList(sc, PerfMonitorOpenLogFiles, NULL); #ifdef PERF_PROFILING RegisterPreprocessorProfile("perfmon", &perfmonStats, 0, &totalPerfStats, NULL); #endif } /* * Function: ParsePerfMonitorArgs(struct _SnortConfig *, char *) * * Purpose: Process the preprocessor arguments from the rules file and * initialize the preprocessor's data struct. This function doesn't * have to exist if it makes sense to parse the args in the init * function. * * Arguments: args => argument list * * * perfmonitor: [ time 10 flow ] * * Returns: void function * */ static void ParsePerfMonitorArgs(struct _SnortConfig *sc, SFPERF *pconfig, char *args) { char **toks = NULL; int num_toks = 0; char *base_stats_file = NULL; int i; char *endptr; if (pconfig == NULL) return; if (sc == NULL) { FatalError("%s(%d) Snort config for parsing is NULL.\n", __FILE__, __LINE__); } // Initialize the performance system and set defaults sfInitPerformanceStatistics(pconfig); if (args != NULL) toks = mSplit(args, " \t", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { if (strcasecmp(toks[i], PERFMON_ARG__FLOW) == 0) { // This parameter turns on the flow statistics. // Flow statistics give you the traffic profile // that snort is processing. This helps in // troubleshooting and performance tuning. pconfig->perf_flags |= SFPERF_FLOW; } else if (strcasecmp(toks[i], PERFMON_ARG__FLOW_PORTS) == 0) { uint32_t value = 0; // Requires an integer argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing argument to \"%s\". The " "value must be a positive integer between 1 and %d.", PERFMON_ARG__FLOW_PORTS, SF_MAX_PORT); } if ((SnortStrToU32(toks[++i], &endptr, &value, 10) != 0) || (value == 0) || (value > SF_MAX_PORT) || *endptr || (errno == ERANGE)) { ParseError("Perfmonitor: Invalid argument to \"%s\". The " "value must be a positive integer between 1 and %d.", PERFMON_ARG__FLOW_PORTS, SF_MAX_PORT); } pconfig->flow_max_port_to_track = (int)value; pconfig->perf_flags |= SFPERF_FLOW; } else if (strcasecmp(toks[i], PERFMON_ARG__FLOW_FILE) == 0) { if (pconfig->flow_file != NULL) free(pconfig->flow_file); // Requires a file name/path argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing file name/path argument " "to \"%s\".", PERFMON_ARG__FLOW_FILE); } pconfig->perf_flags |= SFPERF_FLOW; pconfig->flow_file = ProcessFileOption(sc, toks[++i]); } else if (strcasecmp(toks[i], PERFMON_ARG__EVENTS) == 0) { // The events paramenter gives the total number // of qualified and non-qualified events during // the processing sample time. This allows // performance problems to be seen in a general // manner. pconfig->perf_flags |= SFPERF_EVENT; } else if (strcasecmp(toks[i], PERFMON_ARG__FLOW_IP) == 0) { pconfig->perf_flags |= SFPERF_FLOWIP; } else if (strcasecmp(toks[i], PERFMON_ARG__FLOW_IP_FILE) == 0) { if (pconfig->flowip_file != NULL) free(pconfig->flowip_file); // Requires a file name/path argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing file name/path argument " "to \"%s\".", PERFMON_ARG__FLOW_IP_FILE); } pconfig->perf_flags |= SFPERF_FLOWIP; pconfig->flowip_file = ProcessFileOption(sc, toks[++i]); } else if (strcasecmp(toks[i], PERFMON_ARG__FLOW_IP_MEMCAP) == 0) { uint32_t value = 0; // Requires an integer argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing argument to \"%s\". The " "value must be a positive integer.", PERFMON_ARG__FLOW_IP_MEMCAP); } if ((SnortStrToU32(toks[++i], &endptr, &value, 10) != 0) || (value < PERFMON_ARG__FLOW_IP_MEMCAP_MIN) || *endptr || (errno == ERANGE)) { ParseError("Perfmonitor: Invalid argument to \"%s\". The " "value must be a positive integer between %u and %u.", PERFMON_ARG__FLOW_IP_MEMCAP, PERFMON_ARG__FLOW_IP_MEMCAP_MIN, UINT32_MAX); } pconfig->flowip_memcap = value; pconfig->perf_flags |= SFPERF_FLOWIP; } else if (strcasecmp(toks[i], PERFMON_ARG__TIME) == 0) { uint32_t value = 0; // Requires an integer argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing argument to \"%s\". The " "value must be a positive integer.", PERFMON_ARG__TIME); } if ((SnortStrToU32(toks[++i], &endptr, &value, 10) != 0) || (value == 0) || (value > INT32_MAX) || *endptr || (errno == ERANGE)) { ParseError("Perfmonitor: Invalid argument to \"%s\". The " "value must be a positive integer between 1 and %d.", PERFMON_ARG__TIME, INT32_MAX); } pconfig->sample_interval = (int)value; } else if (strcasecmp(toks[i], PERFMON_ARG__PKT_COUNT) == 0) { uint32_t value = 0; // Requires an integer argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing argument to \"%s\". The " "value must be an integer.", PERFMON_ARG__PKT_COUNT); } if ((SnortStrToU32(toks[++i], &endptr, &value, 10) != 0) || *endptr || (errno == ERANGE)) { ParseError("Perfmonitor: Invalid argument to \"%s\". The " "value must be an integer between 0 and %u.", PERFMON_ARG__PKT_COUNT, UINT32_MAX); } pconfig->pkt_cnt = value; } else if (strcasecmp(toks[i], PERFMON_ARG__ACCUMULATE) == 0) { pconfig->base_reset = 0; } else if (strcasecmp(toks[i], PERFMON_ARG__RESET) == 0) { pconfig->base_reset = 1; } else if (strcasecmp(toks[i], PERFMON_ARG__MAX_STATS) == 0) { #ifndef LINUX_SMP pconfig->perf_flags |= SFPERF_MAX_BASE_STATS; #endif } else if (strcasecmp(toks[i], PERFMON_ARG__CONSOLE) == 0) { pconfig->perf_flags |= SFPERF_CONSOLE; } else if (strcasecmp(toks[i], PERFMON_ARG__FILE) == 0) { // Requires a file name/path argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing file name/path argument " "to \"%s\".", PERFMON_ARG__FILE); } if (base_stats_file != NULL) { ParseError("Perfmonitor: \"%s\" can only be specified once " "and cannot be used in combination with \"%s\".", PERFMON_ARG__FILE, PERFMON_ARG__LOG_DIR_FILE); } base_stats_file = SnortStrdup(toks[++i]); } else if (strcasecmp(toks[i], PERFMON_ARG__LOG_DIR_FILE) == 0) { // Requires a file name/path argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing file name/path argument " "to \"%s\".", PERFMON_ARG__LOG_DIR_FILE); } if (base_stats_file != NULL) { ParseError("Perfmonitor: \"%s\" can only be specified once " "and cannot be used in combination with \"%s\".", PERFMON_ARG__LOG_DIR_FILE, PERFMON_ARG__FILE); } base_stats_file = ProcessFileOption(sc, toks[++i]); } else if (strcasecmp(toks[i], PERFMON_ARG__MAX_FILE_SIZE) == 0) { uint32_t value = 0; // Requires an integer argument if (i == (num_toks - 1)) { ParseError("Perfmonitor: Missing argument to \"%s\". The " "value must be an integer between %d and %d.", PERFMON_ARG__MAX_FILE_SIZE, MIN_PERF_FILE_SIZE, MAX_PERF_FILE_SIZE); } if ((SnortStrToU32(toks[++i], &endptr, &value, 10) != 0) || (value < MIN_PERF_FILE_SIZE) || (value > MAX_PERF_FILE_SIZE) || *endptr || (errno == ERANGE)) { ParseError("Perfmonitor: Invalid argument to \"%s\". The " "value must be an integer between %d and %d.", PERFMON_ARG__MAX_FILE_SIZE, MIN_PERF_FILE_SIZE, MAX_PERF_FILE_SIZE); } // Scale it back by the threshold. pconfig->max_file_size = value - ROLLOVER_THRESH; } else if (strcasecmp(toks[i], PERFMON_ARG__SUMMARY) == 0) { int summary_flags = 0; while (i < num_toks) { i++; if (i == num_toks) { // No arguments left. Keep old behavior where no argument is // needed to atexitonly and apply to all stats. if (!summary_flags) summary_flags |= SFPERF_SUMMARY; } else if (strcasecmp(toks[i], PERFMON_SUMMARY_OPT__BASE) == 0) { summary_flags |= SFPERF_SUMMARY_BASE; } else if (strcasecmp(toks[i], PERFMON_SUMMARY_OPT__FLOW) == 0) { summary_flags |= SFPERF_SUMMARY_FLOW; } else if (strcasecmp(toks[i], PERFMON_SUMMARY_OPT__FLOW_IP) == 0) { summary_flags |= SFPERF_SUMMARY_FLOWIP; } else if (strcasecmp(toks[i], PERFMON_SUMMARY_OPT__EVENTS) == 0) { summary_flags |= SFPERF_SUMMARY_EVENT; } else { // Keep old behaviour of no argument to atexitonly and apply to // all stats. Decrement index so as to parse next argument out // of this context. if (!summary_flags) summary_flags |= SFPERF_SUMMARY; i--; break; } } if (summary_flags) pconfig->perf_flags |= summary_flags; } else { ParseError("Perfmonitor: Invalid parameter - \"%s\".", toks[i]); } } mSplitFree(&toks, num_toks); if ((base_stats_file != NULL) || (sc->perf_file != NULL)) { // Use command line override if applicable if (sc->perf_file != NULL) { pconfig->file = SnortStrdup(sc->perf_file); if (base_stats_file != NULL) free(base_stats_file); } else { pconfig->file = base_stats_file; } } if ((pconfig->perf_flags & SFPERF_SUMMARY) != SFPERF_SUMMARY) pconfig->perf_flags |= SFPERF_TIME_COUNT; LogMessage("PerfMonitor config:\n"); LogMessage(" Sample Time: %d seconds\n", pconfig->sample_interval); LogMessage(" Packet Count: %d\n", pconfig->pkt_cnt); LogMessage(" Max File Size: %u\n", pconfig->max_file_size); LogMessage(" Base Stats: %s%s\n", pconfig->perf_flags & SFPERF_BASE ? "ACTIVE" : "INACTIVE", pconfig->perf_flags & SFPERF_SUMMARY_BASE ? " (SUMMARY)" : ""); if (pconfig->perf_flags & SFPERF_BASE) { LogMessage(" Base Stats File: %s\n", (pconfig->file != NULL) ? pconfig->file : "INACTIVE"); LogMessage(" Max Perf Stats: %s\n", (pconfig->perf_flags & SFPERF_MAX_BASE_STATS) ? "ACTIVE" : "INACTIVE"); } LogMessage(" Flow Stats: %s%s\n", pconfig->perf_flags & SFPERF_FLOW ? "ACTIVE" : "INACTIVE", pconfig->perf_flags & SFPERF_SUMMARY_FLOW ? " (SUMMARY)" : ""); if (pconfig->perf_flags & SFPERF_FLOW) { LogMessage(" Max Flow Port: %u\n", pconfig->flow_max_port_to_track); LogMessage(" Flow File: %s\n", (pconfig->flow_file != NULL) ? pconfig->flow_file : "INACTIVE"); } LogMessage(" Event Stats: %s%s\n", pconfig->perf_flags & SFPERF_EVENT ? "ACTIVE" : "INACTIVE", pconfig->perf_flags & SFPERF_SUMMARY_EVENT ? " (SUMMARY)" : ""); LogMessage(" Flow IP Stats: %s%s\n", pconfig->perf_flags & SFPERF_FLOWIP ? "ACTIVE" : "INACTIVE", pconfig->perf_flags & SFPERF_SUMMARY_FLOWIP ? " (SUMMARY)" : ""); if (pconfig->perf_flags & SFPERF_FLOWIP) { LogMessage(" Flow IP Memcap: %u\n", pconfig->flowip_memcap); LogMessage(" Flow IP File: %s\n", (pconfig->flowip_file != NULL) ? pconfig->flowip_file : "INACTIVE"); } LogMessage(" Console Mode: %s\n", (pconfig->perf_flags & SFPERF_CONSOLE) ? "ACTIVE" : "INACTIVE"); } /* * Function: ProcessPerfMonitor(Packet *) * * Purpose: Perform the preprocessor's intended function. This can be * simple (statistics collection) or complex (IP defragmentation) * as you like. Try not to destroy the performance of the whole * system by trying to do too much.... * * Arguments: p => pointer to the current packet data struct * * Returns: void function * */ static void ProcessPerfMonitor(Packet *p, void *context) { static bool first = true; PROFILE_VARS; PREPROC_PROFILE_START(perfmonStats); if (first) { if (ScReadMode()) { sfBase.pkt_stats.pkts_recv = pc.total_from_daq; sfBase.pkt_stats.pkts_drop = 0; } else { const DAQ_Stats_t* ps = DAQ_GetStats(); sfBase.pkt_stats.pkts_recv = ps->hw_packets_received; sfBase.pkt_stats.pkts_drop = ps->hw_packets_dropped; } first = false; } if (IsSetRotatePerfFileFlag()) { sfRotateBaseStatsFile(perfmon_config); sfRotateFlowStatsFile(perfmon_config); ClearRotatePerfFileFlag(); } sfPerformanceStats(perfmon_config, p); PREPROC_PROFILE_END(perfmonStats); return; } /** * CleanExit func required by preprocessors */ static void PerfMonitorCleanExit(int signal, void *foo) { if (perfmon_config->perf_flags & SFPERF_SUMMARY) sfPerfStatsSummary(perfmon_config); /* Close the performance stats file */ sfCloseBaseStatsFile(perfmon_config); sfCloseFlowStatsFile(perfmon_config); sfCloseFlowIPStatsFile(perfmon_config); FreeFlowStats(&sfFlow); FreeFlowIPStats(&sfFlow); #ifdef LINUX_SMP FreeProcPidStats(&sfBase.sfProcPidStats); #endif PerfMonitorFreeConfig(perfmon_config); perfmon_config = NULL; } static void PerfMonitorFreeConfig(SFPERF *config) { if (config == NULL) return; if (config->file != NULL) free(config->file); if (config->flow_file != NULL) free(config->flow_file); if (config->flowip_file != NULL) free(config->flowip_file); free(config); } static void PerfMonitorReset(int signal, void *foo) { return; } static void PerfMonitorResetStats(int signal, void *foo) { if (perfmon_config == NULL) return; InitPerfStats(perfmon_config); } /* This function changes the perfmon log files permission if exists. It is done in the PerfMonitorInit() before Snort changed its user & group. */ #ifndef WIN32 static void PerfMonitorChangeLogFilesPermission(void) { struct stat pt; mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; if (perfmon_config == NULL) return; if (perfmon_config->file != NULL) { /*Check file before change permission*/ if (stat(perfmon_config->file, &pt) == 0) { /*Only change permission for file owned by root*/ if ((0 == pt.st_uid) || (0 == pt.st_gid)) { if (chmod(perfmon_config->file, mode) != 0) { ParseError("Perfmonitor: Unable to change mode of " "base stats file \"%s\" to mode:%d: %s.", perfmon_config->file, mode, strerror(errno)); } if (chown(perfmon_config->file, ScUid(), ScGid()) != 0) { ParseError("Perfmonitor: Unable to change permissions of " "base stats file \"%s\" to user:%d and group:%d: %s.", perfmon_config->file, ScUid(), ScGid(), strerror(errno)); } } } } if (perfmon_config->flow_file != NULL) { /*Check file before change permission*/ if (stat(perfmon_config->flow_file, &pt) == 0) { /*Only change permission for file owned by root*/ if ((0 == pt.st_uid) || (0 == pt.st_gid)) { if (chmod(perfmon_config->flow_file, mode) != 0) { ParseError("Perfmonitor: Unable to change mode of " "flow stats file \"%s\" to mode:%d: %s.", perfmon_config->flow_file, mode, strerror(errno)); } if (chown(perfmon_config->flow_file, ScUid(), ScGid()) != 0) { ParseError("Perfmonitor: Unable to change permissions of " "flow stats file \"%s\" to user:%d and group:%d: %s.", perfmon_config->flow_file, ScUid(), ScGid(), strerror(errno)); } } } } if (perfmon_config->flowip_file != NULL) { /*Check file before change permission*/ if (stat(perfmon_config->flowip_file, &pt) == 0) { /*Only change permission for file owned by root*/ if ((0 == pt.st_uid) || (0 == pt.st_gid)) { if (chmod(perfmon_config->flowip_file, mode) != 0) { ParseError("Perfmonitor: Unable to change mode of " "flow-ip stats file \"%s\" to mode:%d: %s.", perfmon_config->flowip_file, mode, strerror(errno)); } if (chown(perfmon_config->flowip_file, ScUid(), ScGid()) != 0) { ParseError("Perfmonitor: Unable to change permissions of " "flow-ip stats file \"%s\" to user:%d and group:%d: %s.", perfmon_config->flowip_file, ScUid(), ScGid(), strerror(errno)); } } } } } #endif static void initializePerfmonForDispatch( struct _SnortConfig *sc ) { AddFuncToPreprocListAllNapPolicies( sc, ProcessPerfMonitor, PRIORITY_SCANNER, PP_PERFMONITOR, PROTO_BIT__ALL ); session_api->enable_preproc_all_ports_all_policies( sc, PP_PERFMONITOR, PROTO_BIT__ALL ); } static int PerfMonitorVerifyConfig(struct _SnortConfig *sc) { if (perfmon_config == NULL) return 0; // register perfmon callback with policy and session initializePerfmonForDispatch( sc ); return 0; } /* This function opens the perfmon log files. The logic was moved out of PerfMonitorInit() to avoid creating files before Snort changed its user & group. */ static void PerfMonitorOpenLogFiles(struct _SnortConfig *sc, void *data) { if (perfmon_config == NULL) return; if ((perfmon_config->file != NULL) && ((perfmon_config->fh = sfOpenBaseStatsFile(perfmon_config->file)) == NULL)) { ParseError("Perfmonitor: Cannot open base stats file \"%s\".", perfmon_config->file); } if ((perfmon_config->flow_file != NULL) && ((perfmon_config->flow_fh = sfOpenFlowStatsFile(perfmon_config->flow_file)) == NULL)) { ParseError("Perfmonitor: Cannot open flow stats file \"%s\".", perfmon_config->flow_file); } if ((perfmon_config->flowip_file != NULL) && ((perfmon_config->flowip_fh = sfOpenFlowIPStatsFile(perfmon_config->flowip_file)) == NULL)) { ParseError("Perfmonitor: Cannot open flow-ip stats file \"%s\".", perfmon_config->flowip_file); } } #ifdef SNORT_RELOAD static void PerfMonitorReload(struct _SnortConfig *sc, char *args, void **new_config) { SFPERF *perfmon_swap_config = (SFPERF *)*new_config; // Perf monitor configuration must be defined in the default configuration file only. if( ( perfmon_swap_config != NULL ) || getParserPolicy( sc ) != 0 ) { #if 0 // TBD-EDM ParseError("Perfmonitor can only be configured in default policy and only once.\n"); #else WarningMessage( "Perfmonitor can only be configured in default policy and only once.\n"); #endif return; } perfmon_swap_config = (SFPERF *)SnortAlloc(sizeof(SFPERF)); *new_config = (void *)perfmon_swap_config; /* parse the argument list from the rules file */ ParsePerfMonitorArgs(sc, perfmon_swap_config, args); } static int PerfmonReloadVerify(struct _SnortConfig *sc, void *swap_config) { SFPERF *perfmon_swap_config = (SFPERF *)swap_config; if (perfmon_config == NULL && perfmon_swap_config == NULL) return 0; //start from scratch if (perfmon_config == NULL && perfmon_swap_config !=NULL) { initializePerfmonForDispatch( sc ); return 0; } //keep perfmon_config if (perfmon_config != NULL && perfmon_swap_config ==NULL) return 0; //the only way to prevent this would be to change the number of rows in the //sfFlow.ipMap hash table. That solution seems extreme, plus as it stands //snort wouldn't function on fresh start with a flowip_memcap that low becuase the hash table //rows are hard coded in InitFlowIPStats in preprocessors/perf-flow.c if (perfmon_config->perf_flags & SFPERF_FLOWIP && perfmon_swap_config->perf_flags & SFPERF_FLOWIP && sfxhash_overhead_bytes(sfFlow.ipMap) > perfmon_swap_config->flowip_memcap) { ErrorMessage("Perfmonitor Reload: flowip_memcap is too low.\n"); return -1; } perfmon_reload_status = PERF_RELOAD_VERIFY; syncAllStats(perfmon_config, perfmon_swap_config); perfmon_reload_status = PERF_NOT_RELOADING; // register perfmon callback with policy and session initializePerfmonForDispatch( sc ); return 0; } static void * PerfMonitorReloadSwap(struct _SnortConfig *sc, void *swap_config) { SFPERF *perfmon_swap_config = (SFPERF *)swap_config; SFPERF *old_config = perfmon_config; if (perfmon_swap_config == NULL) return NULL; //start from scratch if (perfmon_config == NULL) { InitPerfStats(perfmon_swap_config); perfmon_config = perfmon_swap_config; return NULL; } perfmon_reload_status = PERF_RELOAD_SWAP; syncAllStats(perfmon_config, perfmon_swap_config); perfmon_reload_status = PERF_NOT_RELOADING; if (perfmon_swap_config->perf_flags & SFPERF_FLOWIP && perfmon_config->perf_flags & SFPERF_FLOWIP) { //this is mainly intended for when FLOWIP is in summary mode and we may have to free objects //in use, but it's also used in other situations to decrase the size of an empty table tSfPolicyId policy_id = getParserPolicy(sc); ReloadAdjustRegister(sc,"perfmon", policy_id, &PerfmonitorReloadAdjustFunc, NULL, NULL); } perfmon_config = perfmon_swap_config; return (void *)old_config; } static void PerfMonitorReloadSwapFree(void *data) { if (data == NULL) return; perfmon_reload_status = PERF_RELOAD_SWAP_FREE; syncAllStats((SFPERF *) data, perfmon_config); perfmon_reload_status = PERF_NOT_RELOADING; PerfMonitorFreeConfig((SFPERF *)data); } static bool PerfmonitorReloadAdjustFunc(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned max_work = idle ? 512 : 32; int ret = sfxhash_change_memcap(sfFlow.ipMap, perfmon_config->flowip_memcap, &max_work); #ifdef REG_TEST if (REG_TEST_FLAG_PERFMON_RELOAD & getRegTestFlags()) { printf("memused:%lu\n",sfFlow.ipMap->mc.memused); printf("memcap:%lu\n",sfFlow.ipMap->mc.memcap); } #endif return ret == SFXHASH_OK; } #endif static int FlowIPStart(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char *statusBuf, int statusBuf_len) { char *args = (char*) data; ParsePerfMonitorArgs(snort_conf, perfmon_config, args); InitPerfStats(perfmon_config); #ifndef WIN32 PerfMonitorChangeLogFilesPermission(); #endif PerfMonitorOpenLogFiles(snort_conf,NULL); return 0; } static int FlowIPStop(uint16_t type, const uint8_t *data, uint32_t length, void **new_context, char *statusBuf, int statusBuf_len) { sfCloseFlowIPStatsFile(perfmon_config); FreeFlowIPStats(&sfFlow); if (perfmon_config->flowip_file != NULL) free(perfmon_config->flowip_file); perfmon_config->perf_flags &= ~SFPERF_FLOWIP; return 0; } static void FlowIPShow(uint16_t type, void *old_context, struct _THREAD_ELEMENT *te, ControlDataSendFunc f) { char buffer[CS_STATS_BUF_SIZE + 1]; int len = 0; if(perfmon_config->perf_flags & SFPERF_FLOWIP) len = snprintf(buffer, CS_STATS_BUF_SIZE, "\nFlow IP Profiling is enabled.\n\n"); else len = snprintf(buffer, CS_STATS_BUF_SIZE, "\nFlow IP Profiling is disabled.\n\n"); if(-1 == f(te, (const uint8_t *)buffer, len)) LogMessage("Unable to send data to the frontend\n"); } snort-2.9.20/src/preprocessors/Stream6/0000755000175000017500000000000014242725713016121 5ustar apoaposnort-2.9.20/src/preprocessors/Stream6/stream_common.c0000644000175000017500000004430214241077141021125 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "decode.h" #include "log.h" #include "util.h" #include "generators.h" #include "event_queue.h" #include "snort.h" #include "memory_stats.h" #include "session_api.h" #include "stream_common.h" #include "portscan.h" #include "sftarget_protocol_reference.h" #include "sp_dynamic.h" #include "snort_stream_tcp.h" #include "snort_stream_udp.h" #include "snort_stream_icmp.h" #include "snort_stream_ip.h" #include "parser.h" #include "active.h" #ifdef ENABLE_HA #include "stream5_ha.h" #endif static void printIgnoredRules( IgnoredRuleList *pIgnoredRuleList, int any_any_flow ); static void addRuleToIgnoreList( IgnoredRuleList **ppIgnoredRuleList, OptTreeNode *otn); /* M A C R O S **************************************************/ int StreamExpireSession(SessionControlBlock *scb) { #ifdef ENABLE_HA /* Sessions in standby cannot be locally expired. */ if (scb->ha_flags & HA_FLAG_STANDBY) return 0; #endif sfBase.iStreamTimeouts++; scb->ha_state.session_flags |= SSNFLAG_TIMEDOUT; scb->session_state |= STREAM_STATE_TIMEDOUT; switch (scb->protocol) { case IPPROTO_TCP: s5stats.tcp_timeouts++; break; case IPPROTO_UDP: s5stats.udp_timeouts++; break; case IPPROTO_ICMP: s5stats.icmp_timeouts++; break; } return 1; } void StreamDeleteSession(SessionControlBlock *scb) { sfBase.iStreamTimeouts++; scb->ha_state.session_flags |= SSNFLAG_TIMEDOUT; switch (scb->protocol) { case IPPROTO_TCP: s5stats.tcp_timeouts++; break; case IPPROTO_UDP: s5stats.udp_timeouts++; break; case IPPROTO_ICMP: s5stats.icmp_timeouts++; case IPPROTO_IP: s5stats.ip_timeouts++; break; } if ( session_api->delete_session_by_key(scb, "timeout") != SFXHASH_OK ) { LogMessage("WARNING: failed to delete session. \n"); } } int StreamExpire(Packet *p, SessionControlBlock *scb) { uint64_t pkttime = CalcJiffies(p); if (scb->expire_time == 0) { /* Not yet set, not expired */ return 0; } if((int)(pkttime - scb->expire_time) > 0) { /* Expiration time has passed. */ return StreamExpireSession(scb); } return 0; } #ifdef ACTIVE_RESPONSE int StreamGetExpire(Packet *p, SessionControlBlock *scb) { return ( CalcJiffies(p) > scb->expire_time ); } // *DROP* flags are set to mark the direction(s) for which traffic was // seen since last reset and then cleared after sending new attempt so // that we only send in the still active direction(s). void StreamActiveResponse(Packet* p, SessionControlBlock *scb) { StreamConfig *config = sfPolicyUserDataGet(stream_online_config, getNapRuntimePolicy()); uint8_t max = config->session_config->max_active_responses; if ( p->packet_flags & PKT_FROM_CLIENT ) scb->session_state |= STREAM_STATE_DROP_CLIENT; else scb->session_state |= STREAM_STATE_DROP_SERVER; if ( (scb->response_count < max) && session_api->get_expire_timer(p, scb) ) { uint32_t delay = config->session_config->min_response_seconds; EncodeFlags flags = ( (scb->session_state & STREAM_STATE_DROP_CLIENT) && (scb->session_state & STREAM_STATE_DROP_SERVER) ) ? ENC_FLAG_FWD : 0; // reverse dir is always true Active_KillSession(p, &flags); ++scb->response_count; #if defined(DAQ_CAPA_CST_TIMEOUT) if (!Daq_Capa_Timeout) #endif session_api->set_expire_timer(p, scb, delay); scb->session_state &= ~(STREAM_STATE_DROP_CLIENT|STREAM_STATE_DROP_SERVER); } } void SetTTL (SessionControlBlock* ssn, Packet* p, int client) { uint8_t inner_ttl = 0, outer_ttl = 0; if ( p->outer_iph_api ) outer_ttl = p->outer_iph_api->iph_ret_ttl(p); if ( p->iph_api ) inner_ttl = p->iph_api->iph_ret_ttl(p); if ( client ) { ssn->outer_client_ttl = outer_ttl; ssn->inner_client_ttl = inner_ttl; } else { ssn->outer_server_ttl = outer_ttl; ssn->inner_server_ttl = inner_ttl; } } #endif void MarkupPacketFlags(Packet *p, SessionControlBlock *scb) { if(!scb) return; if((scb->ha_state.session_flags & SSNFLAG_ESTABLISHED) != SSNFLAG_ESTABLISHED) { if((scb->ha_state.session_flags & (SSNFLAG_SEEN_SERVER|SSNFLAG_SEEN_CLIENT)) != (SSNFLAG_SEEN_SERVER|SSNFLAG_SEEN_CLIENT)) { p->packet_flags |= PKT_STREAM_UNEST_UNI; } } else { p->packet_flags |= PKT_STREAM_EST; if(p->packet_flags & PKT_STREAM_UNEST_UNI) { p->packet_flags ^= PKT_STREAM_UNEST_UNI; } } } #if 0 /** Get rule list for a specific protocol * * @param rule * @param ptocool protocol type * @returns RuleTreeNode* rule list for specific protocol */ static inline RuleTreeNode * protocolRuleList(RuleListNode *rule, IpProto protocol) { switch (protocol) { case IPPROTO_TCP: return rule->RuleList->TcpList; case IPPROTO_UDP: return rule->RuleList->UdpList; case IPPROTO_ICMP: break; default: break; } return NULL; } #endif static inline char * getProtocolName (IpProto protocol) { static char *protocolName[] = {"TCP", "UDP", "ICMP"}; switch (protocol) { case IPPROTO_TCP: return protocolName[0]; case IPPROTO_UDP: return protocolName[1]; case IPPROTO_ICMP: return protocolName[2]; break; default: break; } return NULL; } /**check whether a flow bit is set for an option node. * * @param otn Option Tree Node * @returns 0 - no flow bit is set, 1 otherwise */ int StreamOtnHasFlowOrFlowbit(OptTreeNode *otn) { if (otn->ds_list[PLUGIN_CLIENTSERVER] || DynamicHasFlow(otn) || DynamicHasFlowbit(otn) || otn->ds_list[PLUGIN_FLOWBIT]) { return 1; } return 0; } /**initialize given port list from the given ruleset, for a given policy * @param portList pointer to array of MAX_PORTS+1 uint8_t. This array content * is changed by walking through the rulesets. * @param protocol - protocol type */ void setPortFilterList( struct _SnortConfig *sc, uint16_t *portList, IpProto protocol, int ignoreAnyAnyRules, tSfPolicyId policyId ) { char *port_array = NULL; int num_ports = 0; int i; RuleTreeNode *rtn; OptTreeNode *otn; int inspectSrc, inspectDst; char any_any_flow = 0; IgnoredRuleList *pIgnoredRuleList = NULL; ///list of ignored rules char *protocolName; SFGHASH_NODE *hashNode; int flowBitIsSet = 0; if (sc == NULL) { FatalError("%s(%d) Snort conf for parsing is NULL.\n", __FILE__, __LINE__); } if (((protocol == IPPROTO_TCP) || (protocol == IPPROTO_UDP)) && (ignoreAnyAnyRules == 0)) { int j; for (j=0; jotn_map); hashNode; hashNode = sfghash_findnext(sc->otn_map)) { otn = (OptTreeNode *)hashNode->data; flowBitIsSet = StreamOtnHasFlowOrFlowbit(otn); rtn = getRtnFromOtn(otn, policyId); if (!rtn) { continue; } if (rtn->proto == protocol) { //do operation inspectSrc = inspectDst = 0; if (PortObjectHasAny(rtn->src_portobject)) { inspectSrc = -1; } else { port_array = PortObjectCharPortArray(port_array, rtn->src_portobject, &num_ports); if (port_array && num_ports != 0) { inspectSrc = 1; for (i=0;idst_portobject)) { inspectDst = -1; } else { port_array = PortObjectCharPortArray(port_array, rtn->dst_portobject, &num_ports); if (port_array && num_ports != 0) { inspectDst = 1; for (i=0;i any rule */ if (any_any_flow == 0) { any_any_flow = StreamAnyAnyFlow(portList, otn, rtn, any_any_flow, &pIgnoredRuleList, ignoreAnyAnyRules); } } } } /* If portscan is tracking TCP/UDP, need to create * sessions for all ports */ if (((protocol == IPPROTO_UDP) && (ps_get_protocols(sc, policyId) & PS_PROTO_UDP)) || ((protocol == IPPROTO_TCP) && (ps_get_protocols(sc, policyId) & PS_PROTO_TCP))) { int j; for (j=0; j any with flow/flowbits */ portList[i] |= PORT_MONITOR_SESSION; } return 1; } if (ignoreAnyAnyRules) { /* if not, then ignore the content/pcre/etc */ if (otn->ds_list[PLUGIN_PATTERN_MATCH] || otn->ds_list[PLUGIN_PATTERN_MATCH_OR] || otn->ds_list[PLUGIN_PATTERN_MATCH_URI] || DynamicHasContent(otn) || DynamicHasByteTest(otn) || DynamicHasPCRE(otn) || otn->ds_list[PLUGIN_BYTE_TEST] || otn->ds_list[PLUGIN_PCRE]) { /* Ignoring this rule.... */ addRuleToIgnoreList(ppIgnoredRuleList, otn); } } return 0; } /**add rule to the ignore rule list. */ static void addRuleToIgnoreList(IgnoredRuleList **ppIgnoredRuleList, OptTreeNode *otn) { IgnoredRuleList *ignored_rule; ignored_rule = SnortPreprocAlloc(1, sizeof(*ignored_rule), PP_STREAM, PP_MEM_CATEGORY_CONFIG); ignored_rule->otn = otn; ignored_rule->next = *ppIgnoredRuleList; *ppIgnoredRuleList = ignored_rule; } /**print the ignored rule list. */ static void printIgnoredRules( IgnoredRuleList *pIgnoredRuleList, int any_any_flow ) { char six_sids = 0; int sids_ignored = 0; char buf[STD_BUF]; IgnoredRuleList *ignored_rule; IgnoredRuleList *next_ignored_rule; buf[0] = '\0'; for (ignored_rule = pIgnoredRuleList; ignored_rule != NULL; ) { if (any_any_flow == 0) { if (six_sids == 1) { SnortSnprintfAppend(buf, STD_BUF-1, "\n"); LogMessage("%s", buf); six_sids = 0; } if (sids_ignored == 0) { SnortSnprintf(buf, STD_BUF-1, " %d:%d", ignored_rule->otn->sigInfo.generator, ignored_rule->otn->sigInfo.id); } else { SnortSnprintfAppend(buf, STD_BUF-1, ", %d:%d", ignored_rule->otn->sigInfo.generator, ignored_rule->otn->sigInfo.id); } sids_ignored++; if (sids_ignored %6 == 0) { /* Have it print next time through */ six_sids = 1; sids_ignored = 0; } } next_ignored_rule = ignored_rule->next; SnortPreprocFree(ignored_rule, sizeof(*ignored_rule), PP_STREAM, PP_MEM_CATEGORY_CONFIG); ignored_rule = next_ignored_rule; } if (sids_ignored || six_sids) { SnortSnprintfAppend(buf, STD_BUF-1, "\n"); LogMessage("%s", buf); } } static int StreamFreeConfigsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { StreamConfig *pPolicyConfig = (StreamConfig *)pData; //do any housekeeping before freeing StreamConfig sfPolicyUserDataClear (config, policyId); StreamFreeConfig(pPolicyConfig); return 0; } void StreamFreeConfigs(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate(config, StreamFreeConfigsPolicy); sfPolicyConfigDelete(config); } void StreamFreeConfig(StreamConfig *config) { if (config == NULL) return; if (config->tcp_config != NULL) { StreamTcpConfigFree(config->tcp_config); config->tcp_config = NULL; } if (config->udp_config != NULL) { StreamUdpConfigFree(config->udp_config); config->udp_config = NULL; } if (config->icmp_config != NULL) { StreamIcmpConfigFree(config->icmp_config); config->icmp_config = NULL; } if (config->ip_config != NULL) { StreamIpConfigFree(config->ip_config); config->ip_config = NULL; } SnortPreprocFree(config, sizeof(*config), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } int StreamSetRuntimeConfiguration( SessionControlBlock *scb, uint8_t protocol ) { StreamConfig *pPolicyConfig; pPolicyConfig = ( StreamConfig * ) sfPolicyUserDataGet( stream_online_config, getNapRuntimePolicy() ); if (pPolicyConfig == NULL) return -1; stream_session_config = pPolicyConfig->session_config; return 0; } bool getStreamIgnoreAnyConfig (struct _SnortConfig *sc, IpProto protocol) { StreamConfig *config; tSfPolicyId policyId; for (policyId = 0; policyId < sfPolicyNumAllocated(sc->policy_config); policyId++) { if ((config = getStreamPolicyConfig(policyId, 0))) { switch (protocol) { case IPPROTO_TCP : if ((config->tcp_config) && (config->tcp_config->default_policy->flags & STREAM_CONFIG_IGNORE_ANY)) return true; case IPPROTO_UDP : if ((config->udp_config) && (config->udp_config->default_policy->flags & STREAM_CONFIG_IGNORE_ANY)) return true; default: break; } } } return false; } snort-2.9.20/src/preprocessors/Stream6/stream_common.h0000644000175000017500000002763314241077143021144 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef STREAM_COMMON_H_ #define STREAM_COMMON_H_ #include #ifndef WIN32 #include #endif #include "sfutil/bitop_funcs.h" #include "sfutil/sfActionQueue.h" #include "parser/IpAddrSet.h" #include "session_common.h" #include "stream_api.h" #include "mempool.h" #include "sf_types.h" #ifdef TARGET_BASED #include "target-based/sftarget_hostentry.h" #endif #include "sfPolicy.h" #include "sfPolicyUserData.h" //#define STREAM_DEBUG_ENABLED DEBUG /* defaults and limits */ #define STREAM_MAX_MAX_WINDOW 0x3FFFc000 /* max window allowed by TCP */ /* 65535 << 14 (max wscale) */ #define STREAM_MIN_MAX_WINDOW 0 #define MAX_PORTS_TO_PRINT 20 #define STREAM_DEFAULT_MAX_QUEUED_BYTES 1048576 /* 1 MB */ #define STREAM_MIN_MAX_QUEUED_BYTES 1024 /* Don't let this go below 1024 */ #define STREAM_MAX_MAX_QUEUED_BYTES 0x40000000 /* 1 GB, most we could reach within * largest window scale */ #define AVG_PKT_SIZE 400 #define STREAM_DEFAULT_MAX_QUEUED_SEGS (STREAM_DEFAULT_MAX_QUEUED_BYTES/AVG_PKT_SIZE) #define STREAM_MIN_MAX_QUEUED_SEGS 2 /* Don't let this go below 2 */ #define STREAM_MAX_MAX_QUEUED_SEGS 0x40000000 /* 1 GB worth of one-byte segments */ #define STREAM_DEFAULT_MAX_SMALL_SEG_SIZE 0 /* disabled */ #define STREAM_MAX_MAX_SMALL_SEG_SIZE 2048 /* 2048 bytes in single packet, uh, not small */ #define STREAM_MIN_MAX_SMALL_SEG_SIZE 0 /* 0 means disabled */ #define STREAM_DEFAULT_CONSEC_SMALL_SEGS 0 /* disabled */ #define STREAM_MAX_CONSEC_SMALL_SEGS 2048 /* 2048 single byte packets without acks is alot */ #define STREAM_MIN_CONSEC_SMALL_SEGS 0 /* 0 means disabled */ #if defined(FEAT_OPEN_APPID) #define MAX_APP_PROTOCOL_ID 4 #endif /* defined(FEAT_OPEN_APPID) */ /* target-based policy types */ #define STREAM_POLICY_FIRST 1 #define STREAM_POLICY_LINUX 2 #define STREAM_POLICY_BSD 3 #define STREAM_POLICY_OLD_LINUX 4 #define STREAM_POLICY_LAST 5 #define STREAM_POLICY_WINDOWS 6 #define STREAM_POLICY_SOLARIS 7 #define STREAM_POLICY_HPUX11 8 #define STREAM_POLICY_IRIX 9 #define STREAM_POLICY_MACOS 10 #define STREAM_POLICY_HPUX10 11 #define STREAM_POLICY_VISTA 12 #define STREAM_POLICY_WINDOWS2K3 13 #define STREAM_POLICY_IPS 14 #define STREAM_POLICY_NOACK 15 #define STREAM_POLICY_DEFAULT STREAM_POLICY_BSD #define STREAM_CONFIG_STATEFUL_INSPECTION 0x00000001 #define STREAM_CONFIG_ENABLE_ALERTS 0x00000002 #define STREAM_CONFIG_LOG_STREAMS 0x00000004 #define STREAM_CONFIG_REASS_CLIENT 0x00000008 #define STREAM_CONFIG_REASS_SERVER 0x00000010 #define STREAM_CONFIG_ASYNC 0x00000020 #define STREAM_CONFIG_SHOW_PACKETS 0x00000040 #define STREAM_CONFIG_FLUSH_ON_ALERT 0x00000080 #define STREAM_CONFIG_REQUIRE_3WHS 0x00000100 #define STREAM_CONFIG_MIDSTREAM_DROP_NOALERT 0x00000200 #define STREAM_CONFIG_IGNORE_ANY 0x00000400 #define STREAM_CONFIG_PERFORMANCE 0x00000800 #define STREAM_CONFIG_STATIC_FLUSHPOINTS 0x00001000 #define STREAM_CONFIG_IPS 0x00002000 #define STREAM_CONFIG_CHECK_SESSION_HIJACKING 0x00004000 #define STREAM_CONFIG_NO_ASYNC_REASSEMBLY 0x00008000 /* traffic direction identification */ #define FROM_SERVER 0 #define FROM_RESPONDER 0 #define FROM_CLIENT 1 #define FROM_SENDER 1 #define STREAM_STATE_NONE 0x0000 #define STREAM_STATE_SYN 0x0001 #define STREAM_STATE_SYN_ACK 0x0002 #define STREAM_STATE_ACK 0x0004 #define STREAM_STATE_ESTABLISHED 0x0008 #define STREAM_STATE_DROP_CLIENT 0x0010 #define STREAM_STATE_DROP_SERVER 0x0020 #define STREAM_STATE_MIDSTREAM 0x0040 #define STREAM_STATE_TIMEDOUT 0x0080 #define STREAM_STATE_UNREACH 0x0100 #define STREAM_STATE_PORT_INSPECT 0x0200 #define STREAM_STATE_CLOSED 0x0800 /* D A T A S T R U C T U R E S **********************************/ typedef struct _FlushMgr { uint32_t flush_pt; uint16_t last_count; uint16_t last_size; uint8_t flush_policy; uint8_t flush_type; uint8_t auto_disable; bool flush; //uint8_t spare; } FlushMgr; typedef struct _FlushConfig { FlushMgr client; FlushMgr server; //SF_LIST *dynamic_policy; #ifdef TARGET_BASED uint8_t configured; #endif } FlushConfig; #ifndef DYNAMIC_RANDOM_FLUSH_POINTS typedef struct _FlushPointList { uint8_t current; uint8_t initialized; uint32_t flush_range; uint32_t flush_base; /* Set as value - range/2 */ /* flush_pt is split evently on either side of flush_value, within * the flush_range. flush_pt can be from: * (flush_value - flush_range/2) to (flush_value + flush_range/2) * * For example: * flush_value = 192 * flush_range = 128 * flush_pt will vary from 128 to 256 */ uint32_t *flush_points; } FlushPointList; #endif /**list of ignored rules. */ typedef struct _IgnoredRuleList { OptTreeNode *otn; struct _IgnoredRuleList *next; } IgnoredRuleList; typedef struct _StreamTcpPolicy { uint16_t policy; uint16_t reassembly_policy; uint16_t flags; uint16_t flush_factor; uint32_t session_timeout; uint32_t max_window; uint32_t overlap_limit; uint32_t hs_timeout; IpAddrSet *bound_addrs; FlushConfig flush_config[MAX_PORTS]; #ifdef TARGET_BASED FlushConfig flush_config_protocol[MAX_PROTOCOL_ORDINAL]; #endif #ifndef DYNAMIC_RANDOM_FLUSH_POINTS FlushPointList flush_point_list; #endif uint32_t max_queued_bytes; uint32_t max_queued_segs; uint32_t max_consec_small_segs; uint32_t max_consec_small_seg_size; char small_seg_ignore[MAX_PORTS/8]; bool log_asymmetric_traffic; } StreamTcpPolicy; typedef struct _StreamTcpConfig { StreamTcpPolicy *default_policy; StreamTcpPolicy **policy_list; void* paf_config; uint8_t num_policies; uint16_t session_on_syn; uint16_t port_filter[MAX_PORTS + 1]; } StreamTcpConfig; typedef struct _StreamUdpPolicy { uint32_t session_timeout; uint16_t flags; IpAddrSet *bound_addrs; } StreamUdpPolicy; typedef struct _StreamUdpConfig { StreamUdpPolicy *default_policy; StreamUdpPolicy **policy_list; uint8_t num_policies; uint8_t dummy; /* For alignment */ uint16_t port_filter[MAX_PORTS + 1]; } StreamUdpConfig; typedef struct _StreamIcmpPolicy { uint32_t session_timeout; //uint16_t flags; } StreamIcmpPolicy; typedef struct _StreamIcmpConfig { StreamIcmpPolicy default_policy; uint8_t num_policies; } StreamIcmpConfig; typedef struct _StreamIpPolicy { uint32_t session_timeout; } StreamIpPolicy; typedef struct _StreamIpConfig { StreamIpPolicy default_policy; } StreamIpConfig; typedef struct _StreamConfig { SessionConfiguration *session_config; StreamTcpConfig *tcp_config; StreamUdpConfig *udp_config; StreamIcmpConfig *icmp_config; StreamIpConfig *ip_config; #ifdef TARGET_BASED uint8_t service_filter[MAX_PROTOCOL_ORDINAL]; #endif bool verified; bool swapped; bool reload_config; } StreamConfig; typedef struct _StreamStats { uint32_t total_tcp_sessions; uint32_t total_udp_sessions; uint32_t total_icmp_sessions; uint32_t total_ip_sessions; uint32_t tcp_prunes; uint32_t udp_prunes; uint32_t icmp_prunes; uint32_t ip_prunes; uint32_t tcp_timeouts; uint32_t tcp_streamtrackers_created; uint32_t tcp_streamtrackers_released; uint32_t tcp_streamsegs_created; uint32_t tcp_streamsegs_released; uint32_t tcp_rebuilt_packets; uint32_t tcp_rebuilt_seqs_used; uint32_t tcp_overlaps; uint32_t tcp_discards; uint32_t tcp_gaps; uint32_t udp_timeouts; uint32_t udp_sessions_created; uint32_t udp_sessions_released; uint32_t udp_discards; uint32_t icmp_timeouts; uint32_t icmp_sessions_created; uint32_t icmp_sessions_released; uint32_t ip_timeouts; uint32_t events; uint32_t internalEvents; uint32_t active_tcp_sessions; uint64_t active_tcp_memory; uint32_t active_udp_sessions; uint32_t active_icmp_sessions; uint32_t active_ip_sessions; uint32_t icmp_unreachable; uint32_t icmp_unreachable_code4; tPortFilterStats tcp_port_filter; tPortFilterStats udp_port_filter; } StreamStats; /**Whether incoming packets should be ignored or processed. */ typedef enum { /**Ignore the packet. */ PORT_MONITOR_PACKET_PROCESS = 0, /**Process the packet. */ PORT_MONITOR_PACKET_DISCARD } PortMonitorPacketStates; void StreamDisableInspection(SessionControlBlock *scb, Packet *p); int StreamExpireSession(SessionControlBlock *scb); int StreamExpire(Packet *p, SessionControlBlock *scb); #ifdef ACTIVE_RESPONSE void StreamActiveResponse(Packet*, SessionControlBlock*); void SetTTL (SessionControlBlock*, Packet*, int client); #endif void MarkupPacketFlags(Packet *p, SessionControlBlock *ssn); #ifdef TARGET_BASED void setAppProtocolIdFromHostEntry(SessionControlBlock *scb, HostAttributeEntry *host_entry, int direction); #endif StreamConfig *getStreamPolicyConfig( tSfPolicyId policy_id, bool parsing ); void StreamFreeConfig(StreamConfig *); void StreamFreeConfigs(tSfPolicyUserContextId); void StreamCallHandler(Packet*, unsigned id); void CallFTPFlushProcessor(Packet *); static inline void StreamResetFlowBits( SessionControlBlock *scb ) { StreamFlowData *flowdata; if( ( scb == NULL ) || ( scb->flowdata == NULL ) ) return; flowdata = ( StreamFlowData * ) scb->flowdata->data; boResetBITOP( &( flowdata->boFlowbits ) ); } void setPortFilterList( struct _SnortConfig *sc, uint16_t *portList, IpProto protocol, int ignoreAnyAnyRules, tSfPolicyId policyId ); int StreamAnyAnyFlow( uint16_t *portList, OptTreeNode *otn, RuleTreeNode *rtn, int any_any_flow, IgnoredRuleList **ppIgnoredRuleList, int ignoreAnyAnyRules ); void s5PrintPortFilter( uint16_t portList[] ); int StreamSetRuntimeConfiguration( SessionControlBlock *scb, uint8_t protocol ); bool getStreamIgnoreAnyConfig (struct _SnortConfig *sc, IpProto protocol); // shared stream state extern StreamStats s5stats; extern uint32_t firstPacketTime; extern MemPool s5FlowMempool; extern uint32_t session_mem_in_use; extern SessionConfiguration *stream_session_config; extern tSfPolicyUserContextId stream_online_config; extern tSfPolicyUserContextId stream_parsing_config; extern tSfActionQueueId decoderActionQ; void StreamDeleteSession(SessionControlBlock *scb); #endif /* STREAM_COMMON_H_ */ snort-2.9.20/src/preprocessors/Stream6/stream_paf.c0000644000175000017500000005106214241077144020407 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ //-------------------------------------------------------------------- // s5 stuff // // @file stream5_paf.c // @author Russ Combs //-------------------------------------------------------------------- #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "sf_types.h" #include "snort_bounds.h" #include "snort_debug.h" #include "snort.h" #include "memory_stats.h" #include "sfPolicyUserData.h" #include "stream_common.h" #include "stream_paf.h" #include "sftarget_protocol_reference.h" #include "encode.h" //-------------------------------------------------------------------- // private state //-------------------------------------------------------------------- // uint16_t global_mask = 0; typedef enum { FT_NOP, // no flush FT_SFP, // abort paf FT_PAF, // flush to paf pt when len >= paf FT_LIMIT, // flush to paf. flags are not updated FT_MAX, // flush len when len >= mfp FT_DISC_S,// start of discard FT_DISC_E, // End of discard FT_PAF_HDR, FT_PSEUDO_FLUSH } FlushType; typedef struct { uint16_t cb_mask; uint8_t auto_on; } PAF_Map; typedef struct { uint32_t mfp; uint32_t prep_calls; uint32_t prep_bytes; PAF_Map port_map[MAXPORTS][2]; PAF_Map service_map[MAX_PROTOCOL_ORDINAL][2]; } PAF_Config; #define PAF_LIMIT_FUZZ ETHERNET_MTU // for cb registration #define MAX_CB 16 // depends on sizeof(PAF_Map.cb_mask) static PAF_Callback s5_cb[MAX_CB]; static uint8_t s5_cb_idx = 0; static PAF_Free_Callback s5_free_cb[MAX_CB]; // s5_len and s5_idx are used only during the // lifetime of s5_paf_check() static uint32_t s5_len; // total bytes queued static uint32_t s5_idx; // offset from start of queued bytes //-------------------------------------------------------------------- static uint32_t s5_paf_flush ( PAF_Config* pc, PAF_State* ps, FlushType ft, uint64_t* flags) { uint32_t at = 0; *flags &= ~(PKT_PDU_HEAD | PKT_PDU_TAIL); DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "%s: type=%d, fpt=%u, len=%u, tot=%u\n", __FUNCTION__, ft, ps->fpt, s5_len, ps->tot);) switch ( ft ) { case FT_PAF_HDR: at = ps->fpt_eoh; *flags |= PKT_PDU_TAIL; break; case FT_NOP: return 0; case FT_SFP: *flags = 0; return 0; case FT_PAF: at = ps->fpt; *flags |= PKT_PDU_TAIL; break; case FT_PSEUDO_FLUSH: at = s5_len; break; case FT_DISC_S: at = s5_len; ps->mode = FLUSH_MODE_DISCARD; if ( ps->fpt > s5_len ) ps->fpt -= s5_len; else ps->fpt = 0; break; case FT_DISC_E: at = ps->fpt; ps->mode = FLUSH_MODE_NORMAL; *flags |= PKT_IGNORE | PKT_PDU_TAIL; //overloading existing flag break; case FT_LIMIT: if (ps->fpt > s5_len) { at = s5_len; ps->fpt -= s5_len; } else { at = ps->fpt; ps->fpt = s5_len - ps->fpt; // number of characters scanned but not flushing } break; // use of s5_len is suboptimal here because the actual amount // flushed is determined later and can differ in certain cases // such as exceeding s5_pkt->max_dsize. the actual amount // flushed would ideally be applied to ps->fpt later. for // now we try to circumvent such cases so we track correctly. case FT_MAX: at = s5_len; if( ps->mode == FLUSH_MODE_DISCARD ) *flags |= PKT_IGNORE; if ( ps->fpt > s5_len ) ps->fpt -= s5_len; else ps->fpt = 0; break; } if ( !at || !s5_len ) return 0; // safety - prevent seq + at < seq if ( at > 0x7FFFFFFF ) at = 0x7FFFFFFF; if ( !ps->tot ) *flags |= PKT_PDU_HEAD; if (ft == FT_PSEUDO_FLUSH) return at; if ( *flags & PKT_PDU_TAIL ) ps->tot = 0; else ps->tot += at; if(!ps->fpt_eoh) { ps->pos += at; ps->seq = ps->pos; } return at; } //-------------------------------------------------------------------- static inline int8_t s5_paf_user_data_index(PAF_State* ps, uint8_t id) { int8_t i; //use id and iterate over ps.cb_id[]. compare. if matching return index for( i = 0; i < MAX_PAF_USER; i++ ) { // Check ch_id[i] against (id+1) if( ps->cb_id[i] == (id + 1) ) return i; } //if id doesnt match and user slot is available, this is new PAF call //use the free slot and return its index for( i = 0; i < MAX_PAF_USER; i++ ) { if(ps->user[i] == NULL) return i; } return -1; } //-------------------------------------------------------------------- int8_t s5_paf_get_user_data_index(PAF_State* ps, uint8_t id) { return s5_paf_user_data_index(ps, id-1); } //-------------------------------------------------------------------- static bool s5_paf_callback ( PAF_State* ps, void* ssn, const uint8_t* data, uint32_t len, uint64_t *flags) { PAF_Status paf = PAF_ABORT; uint16_t mask = ps->cb_mask; bool update = false; int i = 0; int8_t udata_idx; while ( mask ) { uint16_t bit = (1<cb_id[udata_idx] = ( i + 1 ) ; paf = s5_cb[i](ssn, &ps->user[udata_idx], data, len, flags, &ps->fpt, &ps->fpt_eoh); if ( paf == PAF_ABORT ) { // this one bailed out ps->cb_mask ^= bit; } else if (paf != PAF_SEARCH) { // this one selected if (!(*flags & PKT_UPGRADE_PROTO)) ps->cb_mask = bit; update = true; break; } mask ^= bit; } if ( ++i == MAX_CB ) break; } if ( !ps->cb_mask ) { ps->paf = PAF_ABORT; update = true; } else if ( paf != PAF_ABORT ) { ps->paf = paf; } if ( update ) { ps->fpt += s5_idx; if ( ps->fpt <= s5_len ) { s5_idx = ps->fpt; return true; } if(ps->fpt_eoh && (paf != PAF_SKIP)){ ps->fpt_eoh += s5_idx; return true; } } s5_idx = s5_len; return false; } //-------------------------------------------------------------------- static inline bool s5_paf_eval ( PAF_Config* pc, PAF_State* ps, void* ssn, uint16_t port, uint64_t *flags, uint32_t fuzz, const uint8_t* data, uint32_t len, FlushType* ft) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "%s: paf=%d, idx=%u, len=%u, fpt=%u\n", __FUNCTION__, ps->paf, s5_idx, s5_len, ps->fpt);) switch ( ps->paf ) { case PAF_SEARCH: if ( s5_len > s5_idx ) { return s5_paf_callback(ps, ssn, data, len, flags); } return false; case PAF_FLUSH: case PAF_PSEUDO_FLUSH_SEARCH: if ( s5_len >= ps->fpt ) { *ft = FT_PAF; ps->paf = PAF_SEARCH; return true; } if ( s5_len >= pc->mfp + fuzz ) { *ft = FT_MAX; return false; } if(ps->fpt_eoh){ *ft = FT_PAF_HDR; return true; } return false; case PAF_LIMIT: // if we are within PAF_LIMIT_FUZZ character of paf_max ... if ( s5_len + PAF_LIMIT_FUZZ >= pc->mfp + fuzz) { *ft = FT_LIMIT; ps->paf = PAF_PERFORMED_LMT_FLUSH; return false; } ps->paf = PAF_SEARCH; return false; case PAF_SKIP: case PAF_PSEUDO_FLUSH_SKIP: if ( s5_len > ps->fpt ) { bool ret; if ( ps->fpt > s5_idx ) { uint32_t delta = ps->fpt - s5_idx; if ( delta > len ) return false; data += delta; len -= delta; } s5_idx = ps->fpt; ps->paf = PAF_SKIP; ret = s5_paf_callback(ps, ssn, data, len, flags); if (ps->paf == PAF_PSEUDO_FLUSH_SKIP) { /* * If we have processed entire data, check if we * can prepare for pseudo flush. */ if (!ret && (s5_len < ps->fpt)) { *ft = FT_PSEUDO_FLUSH; } } return ret; } return false; case PAF_PERFORMED_LMT_FLUSH: // increment position by previously scanned bytes. set in s5_paf_flush ps->paf = PAF_SEARCH; s5_idx += ps->fpt; ps->fpt = 0; return true; case PAF_DISCARD_START: /* * We can flush at this point, * but this will result in splitting of segments * so we will try flushing at s5_len if PDU end * doesnt come before. */ if( s5_len == ps->fpt ) { *ft = FT_DISC_S; ps->paf = PAF_SEARCH; return true; } else if( s5_len > ps->fpt ) { ps->mode = FLUSH_MODE_PRE_DISCARD; ps->paf = PAF_SEARCH; s5_idx = ps->fpt; return true; } if ( s5_len >= pc->mfp + fuzz ) { *ft = FT_MAX; return false; } return false; case PAF_DISCARD_END: if( s5_len >= ps->fpt ) { if( ps->mode != FLUSH_MODE_DISCARD ) { ps->mode = FLUSH_MODE_NORMAL; *ft = FT_PAF; } else *ft = FT_DISC_E; ps->paf = PAF_SEARCH; return true; } if ( s5_len >= pc->mfp + fuzz ) { /* * If mode is set to PRE_DISCARD, * we will anyway flush at s5_len * so no need to set to FT_MAX */ if( ps->mode != FLUSH_MODE_PRE_DISCARD ) { *ft = FT_MAX; return false; } } return false; case PAF_IGNORE: { return false; } default: // PAF_ABORT || PAF_START ps->mode = FLUSH_MODE_NORMAL; break; } *ft = FT_SFP; return false; } //-------------------------------------------------------------------- // helper functions static PAF_Config* get_config (struct _SnortConfig *sc, tSfPolicyId pid) { StreamConfig *config; config = getStreamPolicyConfig( pid, true ); if ( ( config != NULL ) && ( config->tcp_config != NULL ) ) return config->tcp_config->paf_config; else return NULL; } static int install_callback (PAF_Callback cb) { int i; for ( i = 0; i < s5_cb_idx; i++ ) { if ( s5_cb[i] == cb ) break; } if ( i == MAX_CB ) return -1; if ( i == s5_cb_idx ) { s5_cb[i] = cb; s5_cb_idx++; } return i; } //-------------------------------------------------------------------- // public stuff //-------------------------------------------------------------------- void s5_paf_setup (PAF_State* ps, uint16_t mask) { // this is already cleared when instantiated //memset(ps, 0, sizeof(*ps)); ps->paf = PAF_START; ps->cb_mask = mask; ps->mode = FLUSH_MODE_NORMAL; } void s5_paf_clear (PAF_State* ps) { int i; for(i = 0; i < MAX_PAF_USER; i++) { if ( ps->user[i] ) { if (s5_free_cb[ps->cb_id[i]]) { s5_free_cb[ps->cb_id[i]](ps->user[i]); } else { free(ps->user[i]); } ps->user[i] = NULL; } } ps->paf = PAF_ABORT; } //-------------------------------------------------------------------- uint32_t s5_paf_check ( void* pv, PAF_State* ps, void* ssn, const uint8_t* data, uint32_t len, uint32_t total, uint32_t seq, uint16_t port, uint64_t* flags, uint32_t fuzz) { PAF_Config* pc = pv; DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "%s: len=%u, amt=%u, seq=%u, cur=%u, pos=%u, fpt=%u, tot=%u, paf=%d\n", __FUNCTION__, len, total, seq, ps->seq, ps->pos, ps->fpt, ps->tot, ps->paf);) if ( !s5_paf_initialized(ps) ) { ps->seq = ps->pos = seq; ps->paf = PAF_SEARCH; } else if (*flags & PKT_PSEUDO_FLUSH) { return s5_paf_flush(pc, ps, FT_PSEUDO_FLUSH, flags); } else if ( SEQ_GT(seq, ps->seq) ) { // if seq jumped we have a gap. Flush any queued data, then abort s5_len = total - len; if(s5_len) { ps->fpt = 0; return s5_paf_flush(pc, ps, FT_MAX, flags); } *flags = 0; return 0; } else if ( SEQ_LEQ(seq + len, ps->seq) ) { return 0; } else if ( SEQ_LT(seq, ps->seq) ) { uint32_t shift = ps->seq - seq; data += shift; len -= shift; } ps->fpt_eoh = 0; ps->seq += len; pc->prep_calls++; pc->prep_bytes += len; s5_idx = total - len; // if 'total' is greater than the maximum paf_max AND 'total' is greater // than paf_max bytes + fuzz (i.e. after we have finished analyzing the // current segment, total bytes analyzed will be greater than the // configured (fuzz + paf_max) == (pc->mfp + fuzz), we must ensure a flush // occurs at the paf_max byte. So, we manually set the data's length and // total queued bytes (s5_len) to guarantee that at most paf_max bytes will // be analyzed and flushed since the last flush point. It should also be // noted that we perform the check here rather in in s5_paf_flush() to // avoid scanning the same data twice. The first scan would analyze the // entire segment and the second scan would analyze this segments // unflushed data. if ( ps->mode == FLUSH_MODE_NORMAL && total >= MAXIMUM_PAF_MAX && total > pc->mfp + fuzz ) { s5_len = MAXIMUM_PAF_MAX + fuzz; len = (len > ( total - s5_len )) ? len + s5_len - total : 0; if( !len ) return 0; } else { s5_len = total; } do { FlushType ft = FT_NOP; uint32_t idx = s5_idx; uint32_t shift; bool cont = s5_paf_eval(pc, ps, ssn, port, flags, fuzz, data, len, &ft); if (ps->paf == PAF_IGNORE) break; if ( ft != FT_NOP ) return s5_paf_flush(pc, ps, ft, flags); if ( !cont ) break; if ( s5_idx > idx ) { shift = s5_idx - idx; if ( shift > len ) shift = len; data += shift; len -= shift; } } while ( 1 ); //Added for Http/2 if (ps->paf == PAF_IGNORE) { *flags |= PKT_PURGE; ps->paf = PAF_SEARCH; return 0; } //start discarding from now on if(ps->mode == FLUSH_MODE_PRE_DISCARD) return s5_paf_flush(pc, ps, FT_DISC_S, flags); else if ( (s5_len >= pc->mfp+fuzz) ) return s5_paf_flush(pc, ps, FT_MAX, flags); return 0; } //-------------------------------------------------------------------- // port registration foo uint8_t s5_paf_register_port (struct _SnortConfig *sc, tSfPolicyId pid, uint16_t port, bool c2s, PAF_Callback cb, bool auto_on) { PAF_Config* pc = get_config(sc, pid); int i, dir = c2s ? 1 : 0; if ( !pc ) return 0; i = install_callback(cb); if ( i < 0 ) return 0; pc->port_map[port][dir].cb_mask |= (1<port_map[port][dir].auto_on ) pc->port_map[port][dir].auto_on = (uint8_t)auto_on; return i+1; } uint16_t s5_paf_port_registration (void* pv, uint16_t port, bool c2s, bool flush) { PAF_Config* pc = pv; PAF_Map* pm; if ( !pc ) return false; pm = pc->port_map[port] + (c2s?1:0); if ( !pm->cb_mask ) return 0; if ( pm->auto_on || flush ) return pm->cb_mask; return 0; } uint16_t s5_paf_port_registration_all (void* pv, uint16_t port, bool c2s, bool flush) { PAF_Config* pc = pv; PAF_Map* pm; if ( !pc ) return false; pm = pc->port_map[port] + (c2s?1:0); if ( !pm->cb_mask ) pm->cb_mask = global_mask; if ( pm->auto_on || flush ) return pm->cb_mask; return 0; } //-------------------------------------------------------------------- // service registration foo uint8_t s5_paf_register_service (struct _SnortConfig *sc, tSfPolicyId pid, uint16_t service, bool c2s, PAF_Callback cb, bool auto_on) { PAF_Config* pc = get_config(sc, pid); int i, dir = c2s ? 1 : 0; if ( !pc ) return 0; i = install_callback(cb); if ( i < 0 ) return 0; pc->service_map[service][dir].cb_mask |= (1<service_map[service][dir].auto_on ) pc->service_map[service][dir].auto_on = (uint8_t)auto_on; return i+1; } /* cb_mask_cmp * * This api compares expected cb_mask w.r.t appid * and cb_mask populated in stream paf_state. * * Input : PAF_Config - Paf configurations. * Service - Service id like for HTTP id is 5. * c2s - True - This represents flow is client to server. * - False - This represents flow is server to client. * cb_mask - cb_mask populated in stream paf_state * * Return : -1 - If PAF_Config is not present. * 0 - If both the cb_mask matches. * 1 - If both the cb_mask does not match. */ int cb_mask_cmp(void* pv, uint16_t service, bool c2s, uint16_t cb_mask) { PAF_Config* pc = (PAF_Config*)pv; PAF_Map* pm; if ( !pc ) return -1; pm = pc->service_map[service] + (c2s?1:0); if (pm && pm->cb_mask && (pm->cb_mask != cb_mask)) return 1; return 0; } uint16_t s5_paf_service_registration (void* pv, uint16_t service, bool c2s, bool flush) { PAF_Config* pc = pv; PAF_Map* pm; if ( !pc ) return false; pm = pc->service_map[service] + (c2s?1:0); if ( !pm->cb_mask ) return 0; if ( pm->auto_on || flush ) return pm->cb_mask; return 0; } //-------------------------------------------------------------------- void* s5_paf_new (void) { PAF_Config* pc = SnortPreprocAlloc(1, sizeof(*pc), PP_STREAM, PP_MEM_CATEGORY_CONFIG); assert( pc ); pc->mfp = ScPafMax(); if ( !pc->mfp ) // this ensures max < IP_MAXPACKET pc->mfp = MAXIMUM_PAF_MAX; DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "%s: mfp=%u\n", __FUNCTION__, pc->mfp);) return pc; } void s5_paf_delete (void* pv) { PAF_Config* pc = (PAF_Config*)pv; DEBUG_WRAP(DebugMessage(DEBUG_STREAM_PAF, "%s: prep=%u/%u\n", __FUNCTION__, pc->prep_calls, pc->prep_bytes);) SnortPreprocFree(pc, sizeof(*pc), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } void s5_paf_register_free (uint8_t id, PAF_Free_Callback cb) { s5_free_cb[id] = cb; } snort-2.9.20/src/preprocessors/Stream6/snort_stream_ip.c0000644000175000017500000003514014241077133021473 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * * ***************************************************************************/ /* * @file snort_stream_ip.c * @author Russ Combs * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "active.h" #include "decode.h" #include "detect.h" #include "mstring.h" #include "parser.h" #include "profiler.h" #include "sfPolicy.h" #include "sfxhash.h" #include "sf_types.h" #include "snort_debug.h" #include "memory_stats.h" #include "spp_session.h" #include "session_api.h" #include "snort_session.h" #include "snort_stream_ip.h" #include "session_expect.h" #include "stream5_ha.h" #include "util.h" #include "reg_test.h" #ifdef PERF_PROFILING PreprocStats s5IpPerfStats; #endif static SessionCache* ip_lws_cache = NULL; //------------------------------------------------------------------------- // private methods //------------------------------------------------------------------------- static void StreamPrintIpConfig (StreamIpPolicy* policy) { LogMessage("Stream IP Policy config:\n"); LogMessage(" Timeout: %d seconds\n", policy->session_timeout); } static void StreamParseIpArgs (char* args, StreamIpPolicy* policy) { char* *toks; int num_toks; int i; policy->session_timeout = STREAM_DEFAULT_SSN_TIMEOUT; if ( !args || !*args ) return; toks = mSplit(args, ",", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { int s_toks; char* *stoks = mSplit(toks[i], " ", 2, &s_toks, 0); if (s_toks == 0) { ParseError("Missing parameter in Stream IP config.\n"); } if(!strcasecmp(stoks[0], "timeout")) { char* endPtr = NULL; if(stoks[1]) { policy->session_timeout = strtoul(stoks[1], &endPtr, 10); } if (!stoks[1] || (endPtr == &stoks[1][0])) { ParseError("Invalid timeout in config file. Integer parameter required.\n"); } if ((policy->session_timeout > STREAM_MAX_SSN_TIMEOUT) || (policy->session_timeout < STREAM_MIN_SSN_TIMEOUT)) { ParseError("Invalid timeout in config file. Must be between %d and %d\n", STREAM_MIN_SSN_TIMEOUT, STREAM_MAX_SSN_TIMEOUT); } if (s_toks > 2) { ParseError("Invalid Stream IP Policy option. Missing comma?\n"); } } else { ParseError("Invalid Stream IP policy option\n"); } mSplitFree(&stoks, s_toks); } mSplitFree(&toks, num_toks); } void IpSessionCleanup (void* ssn) { SessionControlBlock *scb = ( SessionControlBlock * ) ssn; if (scb->ha_state.session_flags & SSNFLAG_PRUNED) { CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED); } else if (scb->ha_state.session_flags & SSNFLAG_TIMEDOUT) { CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT); } else { CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY); } StreamResetFlowBits(scb); session_api->free_application_data(scb); scb->ha_state.session_flags = SSNFLAG_NONE; scb->session_state = STREAM_STATE_NONE; scb->expire_time = 0; scb->ha_state.ignore_direction = 0; s5stats.active_ip_sessions--; } #ifdef ENABLE_HA //------------------------------------------------------------------------- // ip ha stuff //------------------------------------------------------------------------- SessionControlBlock *GetLWIpSession (const SessionKey *key) { return session_api->get_session_by_key(ip_lws_cache, key); } static SessionControlBlock *StreamIPCreateSession (const SessionKey *key) { setNapRuntimePolicy(getDefaultPolicy()); SessionControlBlock *scb = session_api->create_session(ip_lws_cache, NULL, key); if (scb) s5stats.active_ip_sessions++; return scb; } static int StreamIPDeleteSession (const SessionKey *key) { SessionControlBlock *scb = session_api->get_session_by_key(ip_lws_cache, key); if( scb != NULL ) { if( StreamSetRuntimeConfiguration(scb, scb->protocol) == 0 ) { session_api->delete_session(ip_lws_cache, scb, "ha sync", false); s5stats.active_ip_sessions--; } else WarningMessage(" WARNING: Attempt to delete an IP Session when no valid runtime configuration\n" ); } return 0; } static HA_Api ha_ip_api = { /*.get_lws = */ GetLWIpSession, /*.create_session = */ StreamIPCreateSession, /*.deactivate_session = */ NULL, /*.delete_session = */ StreamIPDeleteSession, }; #endif //------------------------------------------------------------------------- // public methods //------------------------------------------------------------------------- void StreamInitIp( void ) { if(ip_lws_cache == NULL) { ip_lws_cache = session_api->init_session_cache( SESSION_PROTO_IP, 0, // NO session control blocks for IP IpSessionCleanup); } #ifdef ENABLE_HA ha_set_api(IPPROTO_IP, &ha_ip_api); #endif } void StreamResetIp (void) { session_api->purge_session_cache(ip_lws_cache); } void StreamCleanIp (void) { if ( ip_lws_cache ) s5stats.ip_prunes = session_api->get_session_prune_count( SESSION_PROTO_IP ); /* Clean up hash table -- delete all sessions */ session_api->delete_session_cache( SESSION_PROTO_IP ); ip_lws_cache = NULL; } //------------------------------------------------------------------------- // public config methods //------------------------------------------------------------------------- void StreamIpPolicyInit (StreamIpConfig* config, char* args) { if (config == NULL) return; StreamParseIpArgs(args, &config->default_policy); StreamPrintIpConfig(&config->default_policy); } void StreamIpConfigFree (StreamIpConfig* config) { if (config == NULL) return; SnortPreprocFree(config, sizeof(StreamIpConfig), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } int StreamVerifyIpConfig (StreamIpConfig* config, tSfPolicyId policy_id) { if (config == NULL) return -1; if (!ip_lws_cache) return -1; return 0; } //------------------------------------------------------------------------- // public access methods //------------------------------------------------------------------------- uint32_t StreamGetIpPrunes (void) { if( ip_lws_cache ) return session_api->get_session_prune_count( SESSION_PROTO_IP ); else return s5stats.ip_prunes; } void StreamResetIpPrunes (void) { session_api->reset_session_prune_count( SESSION_PROTO_IP ); } //------------------------------------------------------------------------- // private packet processing methods //------------------------------------------------------------------------- static inline void InitSession (Packet* p, SessionControlBlock *scb) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Stream IP session initialized!\n");); s5stats.total_ip_sessions++; s5stats.active_ip_sessions++; IP_COPY_VALUE(scb->client_ip, GET_SRC_IP(p)); IP_COPY_VALUE(scb->server_ip, GET_DST_IP(p)); } static inline int BlockedSession (Packet* p, SessionControlBlock *scb) { if ( !(scb->ha_state.session_flags & (SSNFLAG_DROP_CLIENT|SSNFLAG_DROP_SERVER)) ) return 0; if ( ((p->packet_flags & PKT_FROM_SERVER) && (scb->ha_state.session_flags & SSNFLAG_DROP_SERVER)) || ((p->packet_flags & PKT_FROM_CLIENT) && (scb->ha_state.session_flags & SSNFLAG_DROP_CLIENT)) ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Blocking %s packet as session was blocked\n", p->packet_flags & PKT_FROM_SERVER ? "server" : "client");); DisableDetect( p ); if ( scb->ha_state.session_flags & SSNFLAG_FORCE_BLOCK ) Active_ForceDropSessionWithoutReset(); else Active_DropSessionWithoutReset(p); #ifdef ACTIVE_RESPONSE StreamActiveResponse(p, scb); #endif if (pkt_trace_enabled) addPktTraceData(VERDICT_REASON_STREAM, snprintf(trace_line, MAX_TRACE_LINE, "Stream: session was already blocked, %s\n", getPktTraceActMsg())); else addPktTraceData(VERDICT_REASON_STREAM, 0); return 1; } return 0; } static inline int IgnoreSession (Packet* p, SessionControlBlock *scb) { if ( ((p->packet_flags & PKT_FROM_SERVER) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_CLIENT)) || ((p->packet_flags & PKT_FROM_CLIENT) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_SERVER)) ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream Ignoring packet from %d. Session marked as ignore\n", p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder");); session_api->disable_inspection(scb, p); return 1; } return 0; } #ifdef ENABLE_EXPECTED_IP static inline int CheckExpectedSession (Packet* p, SessionControlBlock *scb) { int ignore; ignore = StreamExpectCheck(p, scb); if (ignore) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Ignoring packet from %d. Marking session marked as ignore.\n", p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder");); scb->ha_state.ignore_direction = ignore; session_api->disable_inspection(scb, p); return 1; } return 0; } #endif static inline void UpdateSession (Packet* p, SessionControlBlock* scb) { MarkupPacketFlags(p, scb); if ( !(scb->ha_state.session_flags & SSNFLAG_ESTABLISHED) ) { if ( p->packet_flags & PKT_FROM_CLIENT ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from client\n");); scb->ha_state.session_flags |= SSNFLAG_SEEN_CLIENT; } else { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from server\n");); scb->ha_state.session_flags |= SSNFLAG_SEEN_SERVER; } if ( (scb->ha_state.session_flags & SSNFLAG_SEEN_CLIENT) && (scb->ha_state.session_flags & SSNFLAG_SEEN_SERVER) ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: session established!\n");); scb->ha_state.session_flags |= SSNFLAG_ESTABLISHED; #ifdef ACTIVE_RESPONSE SetTTL(scb, p, 0); #endif } } // Reset the session timeout. { StreamIpPolicy* policy; policy = (StreamIpPolicy*)scb->proto_policy; session_api->set_expire_timer(p, scb, policy->session_timeout); } } //------------------------------------------------------------------------- // public packet processing method //------------------------------------------------------------------------- int StreamProcessIp( Packet *p, SessionControlBlock *scb, SessionKey *skey ) { PROFILE_VARS; PREPROC_PROFILE_START( s5IpPerfStats ); if( scb->proto_policy == NULL ) { scb->proto_policy = ( ( StreamConfig * ) scb->stream_config )->ip_config; #if defined(DAQ_CAPA_CST_TIMEOUT) if (Daq_Capa_Timeout) { StreamIpPolicy* policy; uint64_t timeout; policy = (StreamIpPolicy*)scb->proto_policy; GetTimeout(p,&timeout); policy->session_timeout = timeout; } #endif } if( !scb->session_established ) { scb->session_established = true; InitSession(p, scb); #ifdef ENABLE_EXPECTED_IP if( CheckExpectedSession( p, scb ) ) { PREPROC_PROFILE_END( s5IpPerfStats ); return 0; } #endif } else { if( ( scb->session_state & STREAM_STATE_TIMEDOUT ) || StreamExpire( p, scb ) ) { scb->ha_state.session_flags |= SSNFLAG_TIMEDOUT; /* Session is timed out */ DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Stream IP session timeout!\n");); #ifdef ENABLE_HA /* Notify the HA peer of the session cleanup/reset by way of a deletion notification. */ PREPROC_PROFILE_TMPEND( s5IpPerfStats ); SessionHANotifyDeletion( scb ); scb->ha_flags = ( HA_FLAG_NEW | HA_FLAG_MODIFIED | HA_FLAG_MAJOR_CHANGE ); PREPROC_PROFILE_TMPSTART( s5IpPerfStats ); #endif /* Clean it up */ IpSessionCleanup( scb ); #ifdef ENABLE_EXPECTED_IP if( CheckExpectedSession( p, scb ) ) { PREPROC_PROFILE_END( s5IpPerfStats ); return 0; } #endif } } session_api->set_packet_direction_flag( p, scb ); p->ssnptr = scb; if( BlockedSession( p, scb ) || IgnoreSession( p, scb ) ) { PREPROC_PROFILE_END( s5IpPerfStats ); return 0; } UpdateSession( p, scb ); PREPROC_PROFILE_END( s5IpPerfStats ); return 0; } #ifdef SNORT_RELOAD void SessionIPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout) { SessionReload(ip_lws_cache, max_sessions, pruningTimeout, nominalTimeout #ifdef REG_TEST , "IP" #endif ); } unsigned SessionIPReloadAdjust(unsigned maxWork) { return SessionProtocolReloadAdjust(ip_lws_cache, session_configuration->max_ip_sessions, maxWork, 0 #ifdef REG_TEST , "IP" #endif ); } #endif size_t get_ip_used_mempool() { if (ip_lws_cache && ip_lws_cache->protocol_session_pool) return ip_lws_cache->protocol_session_pool->used_memory; return 0; } snort-2.9.20/src/preprocessors/Stream6/snort_stream_udp.c0000644000175000017500000007447314241077137021673 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "detect.h" #include "plugbase.h" #include "mstring.h" #include "sfxhash.h" #include "util.h" #include "decode.h" #include "memory_stats.h" #include "spp_session.h" #include "session_api.h" #include "snort_session.h" #include "stream_common.h" #include "stream_api.h" #include "session_expect.h" #include "snort_stream_udp.h" #include "plugin_enum.h" #include "rules.h" #include "treenodes.h" #include "snort.h" #include "active.h" #include "portscan.h" /* To know when to create sessions for all UDP */ #include "dynamic-plugins/sp_dynamic.h" #include "profiler.h" #include "sfPolicy.h" #include "stream5_ha.h" #include "reg_test.h" #ifdef PERF_PROFILING PreprocStats s5UdpPerfStats; #endif /* M A C R O S **************************************************/ /* actions */ #define ACTION_NOTHING 0x00000000 /* sender/responder ip/port dereference */ #define udp_sender_ip lwSsn->client_ip #define udp_sender_port lwSsn->client_port #define udp_responder_ip lwSsn->server_ip #define udp_responder_port lwSsn->server_port /* D A T A S T R U C T U R E S ***********************************/ typedef struct _UdpSession { SessionControlBlock *lwSsn; struct timeval ssn_time; //uint8_t c_ttl; //uint8_t s_ttl; } UdpSession; /* G L O B A L S **************************************************/ static SessionCache* udp_lws_cache = NULL; /* P R O T O T Y P E S ********************************************/ static void StreamParseUdpArgs(StreamUdpConfig *, char *, StreamUdpPolicy *); static void StreamPrintUdpConfig(StreamUdpPolicy *); static int ProcessUdp(SessionControlBlock *, Packet *, StreamUdpPolicy *, SFXHASH_NODE *); static int ProcessUdpCreate (Packet *); #ifdef ENABLE_HA //------------------------------------------------------------------------- // udp ha stuff // TBD there may be some refactoring possible once tcp, icmp, and udp // are complete static SessionControlBlock *StreamUDPCreateSession(const SessionKey *key) { setNapRuntimePolicy(getDefaultPolicy()); SessionControlBlock *scb = session_api->create_session(udp_lws_cache, NULL, key ); if (scb) s5stats.active_udp_sessions++; return scb; } static int StreamUDPDeleteSession(const SessionKey *key) { SessionControlBlock *scb = session_api->get_session_by_key(udp_lws_cache, key); if (scb) { if( StreamSetRuntimeConfiguration( scb, scb->protocol ) == 0 ) { session_api->delete_session( udp_lws_cache, scb, "ha sync", false ); s5stats.active_udp_sessions--; } else WarningMessage(" WARNING: Attempt to delete a UDP Session when no valid runtime configuration.\n" ); } return 0; } static HA_Api ha_udp_api = { /*.get_lws = */ GetLWUdpSession, /*.create_session = */ StreamUDPCreateSession, /*.deactivate_session = */ NULL, /*.delete_session = */ StreamUDPDeleteSession, }; #endif //------------------------------------------------------------------------- void StreamInitUdp( void ) { /* Now UDP */ if (udp_lws_cache == NULL) { udp_lws_cache = session_api->init_session_cache( SESSION_PROTO_UDP, sizeof( UdpSession ), &UdpSessionCleanup); } #ifdef ENABLE_HA ha_set_api(IPPROTO_UDP, &ha_udp_api); #endif } void setUdpDirectionAndPorts( Packet *p, SessionControlBlock *scb ) { scb->ha_state.direction = FROM_SENDER; IP_COPY_VALUE(scb->client_ip, GET_SRC_IP(p)); scb->client_port = p->udph->uh_sport; IP_COPY_VALUE(scb->server_ip, GET_DST_IP(p)); scb->server_port = p->udph->uh_dport; } void StreamUdpPolicyInit(StreamUdpConfig *config, char *args) { StreamUdpPolicy *s5UdpPolicy; if (config == NULL) return; s5UdpPolicy = (StreamUdpPolicy *)SnortPreprocAlloc(1, sizeof(StreamUdpPolicy), PP_STREAM, PP_MEM_CATEGORY_CONFIG); StreamParseUdpArgs(config, args, s5UdpPolicy); config->num_policies++; /* Now add this context to the internal list */ if (config->policy_list == NULL) { config->policy_list = (StreamUdpPolicy **)SnortPreprocAlloc(1, sizeof(StreamUdpPolicy *), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } else { StreamUdpPolicy **tmpPolicyList = (StreamUdpPolicy **)SnortPreprocAlloc(config->num_policies, sizeof(StreamUdpPolicy *), PP_STREAM, PP_MEM_CATEGORY_CONFIG); memcpy(tmpPolicyList, config->policy_list, sizeof(StreamUdpPolicy *) * (config->num_policies - 1)); SnortPreprocFree(config->policy_list, (config->num_policies - 1) * sizeof(StreamUdpPolicy *), PP_STREAM, PP_MEM_CATEGORY_CONFIG); config->policy_list = tmpPolicyList; } config->policy_list[config->num_policies - 1] = s5UdpPolicy; // register callback with Session that determines direction and client/server ports registerDirectionPortCallback( SESSION_PROTO_UDP, setUdpDirectionAndPorts ); StreamPrintUdpConfig(s5UdpPolicy); #ifdef REG_TEST LogMessage(" UDP Session Size: %lu\n", (long unsigned int)sizeof(UdpSession)); #endif } static void StreamParseUdpArgs(StreamUdpConfig *config, char *args, StreamUdpPolicy *s5UdpPolicy) { char **toks; int num_toks; int i; char *index; char **stoks = NULL; int s_toks; char *endPtr = NULL; if (s5UdpPolicy == NULL) return; s5UdpPolicy->session_timeout = STREAM_DEFAULT_SSN_TIMEOUT; s5UdpPolicy->flags = 0; if(args != NULL && strlen(args) != 0) { toks = mSplit(args, ",", 6, &num_toks, 0); i=0; while(i < num_toks) { index = toks[i]; while(isspace((int)*index)) index++; stoks = mSplit(index, " ", 3, &s_toks, 0); if (s_toks == 0) { FatalError("%s(%d) => Missing parameter in Stream UDP config.\n", file_name, file_line); } if(!strcasecmp(stoks[0], "timeout")) { if(stoks[1]) { s5UdpPolicy->session_timeout = strtoul(stoks[1], &endPtr, 10); } if (!stoks[1] || (endPtr == &stoks[1][0]) || *endPtr) { FatalError("%s(%d) => Invalid timeout in config file. Integer parameter required.\n", file_name, file_line); } if ((s5UdpPolicy->session_timeout > STREAM_MAX_SSN_TIMEOUT) || (s5UdpPolicy->session_timeout < STREAM_MIN_SSN_TIMEOUT)) { FatalError("%s(%d) => Invalid timeout in config file. " "Must be between %d and %d\n", file_name, file_line, STREAM_MIN_SSN_TIMEOUT, STREAM_MAX_SSN_TIMEOUT); } if (s_toks > 2) { FatalError("%s(%d) => Invalid Stream UDP Policy option. Missing comma?\n", file_name, file_line); } } else if (!strcasecmp(stoks[0], "ignore_any_rules")) { s5UdpPolicy->flags |= STREAM_CONFIG_IGNORE_ANY; if (s_toks > 1) { FatalError("%s(%d) => Invalid Stream UDP Policy option. Missing comma?\n", file_name, file_line); } } else { FatalError("%s(%d) => Invalid Stream UDP Policy option\n", file_name, file_line); } mSplitFree(&stoks, s_toks); i++; } mSplitFree(&toks, num_toks); } if (s5UdpPolicy->bound_addrs == NULL) { if (config->default_policy != NULL) { FatalError("%s(%d) => Default Stream UDP Policy already set. " "This policy must be bound to a specific host or " "network.\n", file_name, file_line); } config->default_policy = s5UdpPolicy; } else { if (s5UdpPolicy->flags & STREAM_CONFIG_IGNORE_ANY) { FatalError("%s(%d) => \"ignore_any_rules\" option can be used only" " with Default Stream UDP Policy\n", file_name, file_line); } } } static void StreamPrintUdpConfig(StreamUdpPolicy *s5UdpPolicy) { LogMessage("Stream UDP Policy config:\n"); LogMessage(" Timeout: %d seconds\n", s5UdpPolicy->session_timeout); if (s5UdpPolicy->flags) { LogMessage(" Options:\n"); if (s5UdpPolicy->flags & STREAM_CONFIG_IGNORE_ANY) { LogMessage(" Ignore Any -> Any Rules: YES\n"); } } //IpAddrSetPrint(" Bound Addresses:", s5UdpPolicy->bound_addrs); } int StreamVerifyUdpConfig(struct _SnortConfig *sc, StreamUdpConfig *config, tSfPolicyId policyId) { if (config == NULL) return -1; if (!udp_lws_cache) return -1; if (config->num_policies == 0) return -1; /* Post-process UDP rules to establish UDP ports to inspect. */ setPortFilterList(sc, config->port_filter, IPPROTO_UDP, (config->default_policy->flags & STREAM_CONFIG_IGNORE_ANY), policyId); //printf ("UDP Ports with Inspection/Monitoring\n"); //s5PrintPortFilter(config->port_filter); return 0; } #ifdef STREAM_DEBUG_ENABLED static void PrintUdpSession(UdpSession *us) { LogMessage("UdpSession:\n"); LogMessage(" ssn_time: %lu\n", us->ssn_time.tv_sec); LogMessage(" sender IP: 0x%08X\n", us->udp_sender_ip); LogMessage(" responder IP: 0x%08X\n", us->udp_responder_ip); LogMessage(" sender port: %d\n", us->udp_sender_port); LogMessage(" responder port: %d\n", us->udp_responder_port); LogMessage(" flags: 0x%X\n", us->lwSsn->session_flags); } #endif SessionControlBlock *GetLWUdpSession( const SessionKey *key ) { return session_api->get_session_by_key(udp_lws_cache, key); } void UdpSessionCleanup(void *ssn) { SessionControlBlock *scb = ( SessionControlBlock * ) ssn; UdpSession *udpssn = NULL; if (scb->ha_state.session_flags & SSNFLAG_PRUNED) { CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED); } else if (scb->ha_state.session_flags & SSNFLAG_TIMEDOUT) { CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT); } else { CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY); } if (scb->proto_specific_data) udpssn = (UdpSession *)scb->proto_specific_data->data; if (!udpssn) { /* Huh? */ return; } /* Cleanup the proto specific data */ session_api->free_protocol_session_pool( SESSION_PROTO_UDP, scb ); scb->proto_specific_data = NULL; scb->session_state = STREAM_STATE_NONE; scb->ha_state.session_flags = SSNFLAG_NONE; scb->expire_time = 0; scb->ha_state.ignore_direction = 0; StreamResetFlowBits(scb); session_api->free_application_data(scb); s5stats.udp_sessions_released++; s5stats.active_udp_sessions--; RemoveUDPSession(&sfBase); } uint32_t StreamGetUdpPrunes(void) { if( udp_lws_cache) return session_api->get_session_prune_count( SESSION_PROTO_UDP ); else return s5stats.udp_prunes; } void StreamResetUdpPrunes(void) { session_api->reset_session_prune_count( SESSION_PROTO_UDP ); } void StreamResetUdp(void) { session_api->purge_session_cache(udp_lws_cache); session_api->clean_protocol_session_pool( SESSION_PROTO_UDP ); } void StreamCleanUdp(void) { if ( udp_lws_cache ) s5stats.udp_prunes = session_api->get_session_prune_count( SESSION_PROTO_UDP ); /* Clean up session cache */ session_api->delete_session_cache( SESSION_PROTO_UDP ); udp_lws_cache = NULL; } static int NewUdpSession(Packet *p, SessionControlBlock *scb, StreamUdpPolicy *s5UdpPolicy) { UdpSession *tmp; MemBucket *tmpBucket; /****************************************************************** * create new sessions *****************************************************************/ tmpBucket = session_api->alloc_protocol_session( SESSION_PROTO_UDP ); if (tmpBucket == NULL) return -1; tmp = tmpBucket->data; DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker!\n");); tmp->ssn_time.tv_sec = p->pkth->ts.tv_sec; tmp->ssn_time.tv_usec = p->pkth->ts.tv_usec; scb->ha_state.session_flags |= SSNFLAG_SEEN_SENDER; DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "adding UdpSession to lightweight session\n");); scb->proto_specific_data = tmpBucket; scb->protocol = GET_IPH_PROTO(p); scb->ha_state.direction = FROM_SENDER; tmp->lwSsn = scb; #ifdef STREAM_DEBUG_ENABLED PrintUdpSession(tmp); #endif session_api->set_expire_timer(p, scb, s5UdpPolicy->session_timeout); s5stats.udp_sessions_created++; s5stats.active_udp_sessions++; AddUDPSession(&sfBase); if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP)) UpdateFlowIPState(&sfFlow, IP_ARG(scb->client_ip), IP_ARG(scb->server_ip), SFS_STATE_UDP_CREATED); return 0; } //------------------------------------------------------------------------- /* * Main entry point for UDP */ int StreamProcessUdp( Packet *p, SessionControlBlock *scb, StreamUdpPolicy *s5UdpPolicy, SessionKey *skey) { SFXHASH_NODE *hash_node = NULL; PROFILE_VARS; // XXX-IPv6 StreamProcessUDP debugging PREPROC_PROFILE_START(s5UdpPerfStats); if( s5UdpPolicy == NULL ) { int policyIndex; StreamUdpConfig *udp_config = ( ( StreamConfig * ) scb->stream_config )->udp_config; if( udp_config == NULL ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] Could not find Udp Policy context " "for IP %s\n", inet_ntoa(GET_DST_ADDR(p)));); PREPROC_PROFILE_END( s5UdpPerfStats ); return 0; } /* Find an Udp policy for this packet */ for( policyIndex = 0; policyIndex < udp_config->num_policies; policyIndex++ ) { s5UdpPolicy = udp_config->policy_list[ policyIndex ]; if( s5UdpPolicy->bound_addrs == NULL ) continue; /* * Does this policy handle packets to this IP address? */ if( sfvar_ip_in( s5UdpPolicy->bound_addrs, GET_DST_IP( p ) ) ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] Found udp policy in IpAddrSet\n");); break; } } if( policyIndex == udp_config->num_policies ) s5UdpPolicy = udp_config->default_policy; if( s5UdpPolicy == NULL ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] Could not find Udp Policy context " "for IP %s\n", inet_ntoa(GET_DST_ADDR(p)));); PREPROC_PROFILE_END(s5UdpPerfStats); return 0; } } /* UDP Sessions required */ if ( !scb->session_established ) { int rc; #if defined(DAQ_CAPA_CST_TIMEOUT) uint64_t timeout; if (Daq_Capa_Timeout) { GetTimeout(p,&timeout); s5UdpPolicy->session_timeout = timeout; } #endif scb->proto_policy = s5UdpPolicy; rc = isPacketFilterDiscard( p, s5UdpPolicy->flags & STREAM_CONFIG_IGNORE_ANY ); if( ( rc == PORT_MONITOR_PACKET_DISCARD ) && !StreamExpectIsExpected( p, &hash_node ) ) { //ignore the packet scb->session_state &= ~STREAM_STATE_PORT_INSPECT; UpdateFilteredPacketStats(&sfBase, IPPROTO_UDP); session_api->set_expire_timer(p, scb, s5UdpPolicy->session_timeout); PREPROC_PROFILE_END(s5UdpPerfStats); return 0; } scb->session_state |= STREAM_STATE_PORT_INSPECT; scb->session_established = true; s5stats.total_udp_sessions++; s5stats.active_udp_sessions++; } p->ssnptr = scb; /* * Check if the session is expired. * Should be done before we do something with the packet... * ie, Insert a packet, or handle state change SYN, FIN, RST, etc. */ if( ( scb->session_state & STREAM_STATE_TIMEDOUT ) || StreamExpire( p, scb ) ) { scb->ha_state.session_flags |= SSNFLAG_TIMEDOUT; /* Session is timed out */ DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream UDP session timedout!\n");); #ifdef ENABLE_HA /* Notify the HA peer of the session cleanup/reset by way of a deletion notification. */ PREPROC_PROFILE_TMPEND(s5UdpPerfStats); SessionHANotifyDeletion(scb); PREPROC_PROFILE_TMPSTART(s5UdpPerfStats); scb->ha_flags = (HA_FLAG_NEW | HA_FLAG_MODIFIED | HA_FLAG_MAJOR_CHANGE); #endif /* Clean it up */ UdpSessionCleanup(scb); ProcessUdp(scb, p, s5UdpPolicy, hash_node); } else { ProcessUdp(scb, p, s5UdpPolicy, hash_node); DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Finished Stream UDP cleanly!\n" "---------------------------------------------------\n");); } MarkupPacketFlags(p, scb); session_api->set_expire_timer(p, scb, s5UdpPolicy->session_timeout); PREPROC_PROFILE_END(s5UdpPerfStats); return 0; } static int ProcessUdp( SessionControlBlock *scb, Packet *p, StreamUdpPolicy *s5UdpPolicy, SFXHASH_NODE *hash_node ) { char ignore; UdpSession *udpssn = NULL; if (scb->protocol != IPPROTO_UDP) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Lightweight session not UDP on UDP packet\n");); return ACTION_NOTHING; } if (scb->ha_state.session_flags & (SSNFLAG_DROP_CLIENT|SSNFLAG_DROP_SERVER)) { /* Got a packet on a session that was dropped (by a rule). */ session_api->set_packet_direction_flag(p, scb); /* Drop this packet */ if (((p->packet_flags & PKT_FROM_SERVER) && (scb->ha_state.session_flags & SSNFLAG_DROP_SERVER)) || ((p->packet_flags & PKT_FROM_CLIENT) && (scb->ha_state.session_flags & SSNFLAG_DROP_CLIENT))) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Blocking %s packet as session was blocked\n", p->packet_flags & PKT_FROM_SERVER ? "server" : "client");); DisableDetect( p ); if ( scb->ha_state.session_flags & SSNFLAG_FORCE_BLOCK ) Active_ForceDropSessionWithoutReset(); else Active_DropSessionWithoutReset(p); #ifdef ACTIVE_RESPONSE StreamActiveResponse(p, scb); #endif if (pkt_trace_enabled) addPktTraceData(VERDICT_REASON_STREAM, snprintf(trace_line, MAX_TRACE_LINE, "Stream: session was already blocked, %s\n", getPktTraceActMsg())); else addPktTraceData(VERDICT_REASON_STREAM, 0); return ACTION_NOTHING; } } if (scb->proto_specific_data != NULL) udpssn = (UdpSession *)scb->proto_specific_data->data; if (udpssn == NULL) { if (NewUdpSession(p, scb, s5UdpPolicy) == -1) return ACTION_NOTHING; udpssn = (UdpSession *)scb->proto_specific_data->data; /* Check if the session is to be ignored */ if (hash_node) ignore = StreamExpectProcessNode(p, scb, hash_node); else ignore = StreamExpectCheck(p, scb); if (ignore) { /* Set the directions to ignore... */ scb->ha_state.ignore_direction = ignore; DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Ignoring packet from %d. " "Marking session marked as ignore.\n", p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder");); session_api->disable_inspection(scb, p); return ACTION_NOTHING; } } /* figure out direction of this packet */ session_api->set_packet_direction_flag(p, scb); if (((p->packet_flags & PKT_FROM_SERVER) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_CLIENT)) || ((p->packet_flags & PKT_FROM_CLIENT) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_SERVER))) { session_api->disable_inspection(scb, p); DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream Ignoring packet from %d. " "Session marked as ignore\n", p->packet_flags & PKT_FROM_CLIENT? "sender" : "responder");); return ACTION_NOTHING; } /* if both seen, mark established */ if(p->packet_flags & PKT_FROM_SERVER) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from responder\n");); scb->ha_state.session_flags |= SSNFLAG_SEEN_RESPONDER; #ifdef ACTIVE_RESPONSE SetTTL(scb, p, 0); #endif } else { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from client\n");); /* if we got here we had to see the SYN already... */ scb->ha_state.session_flags |= SSNFLAG_SEEN_SENDER; #ifdef ACTIVE_RESPONSE SetTTL(scb, p, 1); #endif } if (!(scb->ha_state.session_flags & SSNFLAG_ESTABLISHED)) { if ((scb->ha_state.session_flags & SSNFLAG_SEEN_SENDER) && (scb->ha_state.session_flags & SSNFLAG_SEEN_RESPONDER)) { scb->ha_state.session_flags |= SSNFLAG_ESTABLISHED; } } return ACTION_NOTHING; } int ProcessUdpCreate (Packet *p) { SFXHASH_NODE *hash_node = NULL; SessionControlBlock *scb; StreamUdpPolicy *s5UdpPolicy; PROFILE_VARS; scb = p->ssnptr; if (!scb) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] Could not find Udp session Control block ")); return 0; } s5UdpPolicy = scb->proto_policy; if (s5UdpPolicy == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] Could not find Udp Policy context " "for IP %s\n", inet_ntoa(GET_DST_ADDR(p)));); return 0; } PREPROC_PROFILE_START(s5UdpPerfStats); scb->session_established = true; s5stats.total_udp_sessions++; /* * Check if the session is expired. */ if (( scb->session_state & STREAM_STATE_TIMEDOUT ) || StreamExpire( p, scb )) { scb->ha_state.session_flags |= SSNFLAG_TIMEDOUT; /* Session is timed out */ DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream UDP session timedout!\n");); #ifdef ENABLE_HA /* Notify the HA peer of the session cleanup/reset by way of a deletion notification. */ PREPROC_PROFILE_TMPEND(s5UdpPerfStats); SessionHANotifyDeletion(scb); PREPROC_PROFILE_TMPSTART(s5UdpPerfStats); scb->ha_flags = (HA_FLAG_NEW | HA_FLAG_MODIFIED | HA_FLAG_MAJOR_CHANGE); #endif /* Clean it up */ UdpSessionCleanup(scb); ProcessUdp(scb, p, s5UdpPolicy, hash_node); } else { ProcessUdp(scb, p, s5UdpPolicy, hash_node); DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Finished Stream UDP cleanly!\n" "---------------------------------------------------\n");); } MarkupPacketFlags(p, scb); session_api->set_expire_timer(p, scb, s5UdpPolicy->session_timeout); PREPROC_PROFILE_END(s5UdpPerfStats); return 0; } void InspectPortFilterUdp (Packet *p) { int rc; SessionControlBlock *scb; StreamUdpPolicy *s5UdpPolicy; scb = p->ssnptr; if (!scb) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM,"[Stream] Sesssion control does not exist")); return; } s5UdpPolicy = scb->proto_policy; if (s5UdpPolicy == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] Could not find Udp Policy context " "for IP %s\n", inet_ntoa(GET_DST_ADDR(p)));); return; } // If NAP had set port to be filtered, now check IPS portlist. if (!(scb->session_state & STREAM_STATE_PORT_INSPECT)) { rc = isPacketFilterDiscardUdp(p, s5UdpPolicy->flags & STREAM_CONFIG_IGNORE_ANY); if (rc == PORT_MONITOR_PACKET_PROCESS) { // Port is not present in NAP, but present in IPS portlist, flow will be tracked. scb->session_state |= STREAM_STATE_PORT_INSPECT; // Complete UDP session/flow creation as it needs to tracked. ProcessUdpCreate(p); } /* * If return value from isPacketFilterDiscardUdp() was PORT_MONITOR_PACKET_DISCARD, * packet is marked either inspected/filtered, based NAP/IPS portlist flag evaluation. */ } return; } void UdpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port) { UdpSession *udpssn = (UdpSession *)ssn->proto_specific_data->data; sfaddr_t tmpIp; uint16_t tmpPort; if (IP_EQUALITY(&udpssn->udp_sender_ip, ip) && (udpssn->udp_sender_port == port)) { if ((dir == SSN_DIR_FROM_SENDER) && (ssn->ha_state.direction == SSN_DIR_FROM_SENDER)) { /* Direction already set as SENDER */ return; } } else if (IP_EQUALITY(&udpssn->udp_responder_ip, ip) && (udpssn->udp_responder_port == port)) { if ((dir == SSN_DIR_FROM_RESPONDER) && (ssn->ha_state.direction == SSN_DIR_FROM_RESPONDER)) { /* Direction already set as RESPONDER */ return; } } /* Swap them -- leave ssn->ha_state.direction the same */ tmpIp = udpssn->udp_sender_ip; tmpPort = udpssn->udp_sender_port; udpssn->udp_sender_ip = udpssn->udp_responder_ip; udpssn->udp_sender_port = udpssn->udp_responder_port; udpssn->udp_responder_ip = tmpIp; udpssn->udp_responder_port = tmpPort; } void s5UdpSetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->udp_config != NULL ) ) config->udp_config->port_filter[ port ] |= status; } void s5UdpUnsetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->udp_config != NULL ) ) config->udp_config->port_filter[ port ] &= ~status; } int s5UdpGetPortFilterStatus( struct _SnortConfig *sc, unsigned short port, tSfPolicyId policyId, int parsing) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->udp_config != NULL ) ) return ( int ) config->udp_config->port_filter[ port ]; else return PORT_MONITOR_NONE; } int s5UdpGetIPSPortFilterStatus(struct _SnortConfig *sc, unsigned short sport, unsigned short dport, tSfPolicyId policyId) { if ( sc->udp_ips_port_filter_list && sc->udp_ips_port_filter_list[policyId] ) return ( ((int) sc->udp_ips_port_filter_list[policyId]->port_filter[ sport ]) | ((int) sc->udp_ips_port_filter_list[policyId]->port_filter[ dport ]) ) ; else return PORT_MONITOR_NONE; } void StreamUdpConfigFree(StreamUdpConfig *config) { int i; if (config == NULL) return; /* Cleanup TCP Policies and the list */ for (i = 0; i < config->num_policies; i++) { StreamUdpPolicy *policy = config->policy_list[i]; if (policy->bound_addrs != NULL) sfvar_free(policy->bound_addrs); SnortPreprocFree(policy, sizeof(StreamUdpPolicy), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } SnortPreprocFree(config->policy_list, config->num_policies * sizeof(StreamUdpPolicy *), PP_STREAM, PP_MEM_CATEGORY_CONFIG); SnortPreprocFree(config, sizeof(StreamUdpConfig), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } #ifdef SNORT_RELOAD void SessionUDPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout) { SessionReload(udp_lws_cache, max_sessions, pruningTimeout, nominalTimeout #ifdef REG_TEST , "UDP" #endif ); } unsigned SessionUDPReloadAdjust(unsigned maxWork) { return SessionProtocolReloadAdjust(udp_lws_cache, session_configuration->max_udp_sessions, maxWork, 0 #ifdef REG_TEST , "UDP" #endif ); } #endif size_t get_udp_used_mempool() { if (udp_lws_cache && udp_lws_cache->protocol_session_pool) return udp_lws_cache->protocol_session_pool->used_memory; return 0; } snort-2.9.20/src/preprocessors/Stream6/snort_stream_udp.h0000644000175000017500000000527514241077140021664 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef STREAM_UDP_H_ #define STREAM_UDP_H_ #include "ipv6_port.h" #include "session_api.h" #include "stream_common.h" #include "sfPolicy.h" void StreamCleanUdp(void); void StreamResetUdp(void); void StreamInitUdp(void); void StreamUdpPolicyInit(StreamUdpConfig *, char *); int StreamVerifyUdpConfig(struct _SnortConfig *, StreamUdpConfig *, tSfPolicyId); int StreamProcessUdp(Packet *, SessionControlBlock *, StreamUdpPolicy *, SessionKey *); void UdpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port); SessionControlBlock *GetLWUdpSession(const SessionKey *key); void s5UdpSetPortFilterStatus( struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing ); void s5UdpUnsetPortFilterStatus( struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing ); int s5UdpGetPortFilterStatus( struct _SnortConfig *sc, unsigned short port, tSfPolicyId policyId, int parsing ); int s5UdpGetIPSPortFilterStatus( struct _SnortConfig *sc, unsigned short sport, unsigned short dport, tSfPolicyId policyId); void InspectPortFilterUdp (Packet *p); void StreamUdpConfigFree(StreamUdpConfig *); uint32_t StreamGetUdpPrunes(void); void StreamResetUdpPrunes(void); void UdpSessionCleanup(void *scb); void SessionUDPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout); unsigned SessionUDPReloadAdjust(unsigned maxWork); size_t get_udp_used_mempool(); #endif /* STREAM_UDP_H_ */ snort-2.9.20/src/preprocessors/Stream6/Makefile.in0000644000175000017500000004160114242725547020175 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/preprocessors/Stream6 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libstream6_a_AR = $(AR) $(ARFLAGS) libstream6_a_DEPENDENCIES = snort_stream_tcp.o snort_stream_udp.o \ snort_stream_icmp.o snort_stream_ip.o stream_paf.o \ stream_common.o am_libstream6_a_OBJECTS = snort_stream_tcp.$(OBJEXT) \ snort_stream_udp.$(OBJEXT) snort_stream_icmp.$(OBJEXT) \ snort_stream_ip.$(OBJEXT) stream_paf.$(OBJEXT) \ stream_common.$(OBJEXT) libstream6_a_OBJECTS = $(am_libstream6_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libstream6_a_SOURCES) DIST_SOURCES = $(libstream6_a_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies noinst_LIBRARIES = libstream6.a libstream6_a_SOURCES = \ snort_stream_tcp.c \ snort_stream_tcp.h \ snort_stream_udp.c \ snort_stream_udp.h \ snort_stream_icmp.c \ snort_stream_icmp.h \ snort_stream_ip.c \ snort_stream_ip.h \ stream_paf.c \ stream_paf.h \ stream_common.c \ stream_common.h libstream6_a_LIBADD = \ snort_stream_tcp.o \ snort_stream_udp.o \ snort_stream_icmp.o \ snort_stream_ip.o \ stream_paf.o \ stream_common.o all: all-am .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/preprocessors/Stream6/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/preprocessors/Stream6/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libstream6.a: $(libstream6_a_OBJECTS) $(libstream6_a_DEPENDENCIES) $(EXTRA_libstream6_a_DEPENDENCIES) $(AM_V_at)-rm -f libstream6.a $(AM_V_AR)$(libstream6_a_AR) libstream6.a $(libstream6_a_OBJECTS) $(libstream6_a_LIBADD) $(AM_V_at)$(RANLIB) libstream6.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-am CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-am cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done check-am: all-am check: check-am all-am: Makefile $(LIBRARIES) installdirs: install: install-am install-exec: install-exec-am install-data: install-data-am uninstall: uninstall-am install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-am install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-am clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-am -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-am dvi-am: html: html-am html-am: info: info-am info-am: install-data-am: install-dvi: install-dvi-am install-dvi-am: install-exec-am: install-html: install-html-am install-html-am: install-info: install-info-am install-info-am: install-man: install-pdf: install-pdf-am install-pdf-am: install-ps: install-ps-am install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-am mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-am pdf-am: ps: ps-am ps-am: uninstall-am: .MAKE: install-am install-strip .PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ clean-libtool clean-noinstLIBRARIES cscopelist-am ctags \ ctags-am distclean distclean-compile distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ install-exec-am install-html install-html-am install-info \ install-info-am install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ tags tags-am uninstall uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/preprocessors/Stream6/snort_stream_tcp.h0000644000175000017500000001356414241077136021667 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef _STREAM_TCP_H_ #define _STREAM_TCP_H_ #include "session_common.h" #include "stream_common.h" #include "sfPolicy.h" extern uint32_t xtradata_func_count; extern LogFunction xtradata_map[LOG_FUNC_MAX]; extern LogExtraData extra_data_log; extern void *extra_data_config; void StreamCleanTcp(void); void StreamResetTcp(void); void StreamInitTcp(void); void StreamTcpRegisterPreprocProfiles(void); void StreamTcpRegisterRuleOptions(struct _SnortConfig *); void StreamTcpInitFlushPoints(void); int StreamVerifyTcpConfig(struct _SnortConfig *, StreamTcpConfig *, tSfPolicyId); void StreamTcpPolicyInit(struct _SnortConfig *, StreamTcpConfig *, char *); int StreamProcessTcp(Packet *, SessionControlBlock *, StreamTcpPolicy *, SessionKey *); int StreamFlushListener(Packet *p, SessionControlBlock *scb); int StreamFlushTalker(Packet *p, SessionControlBlock *scb); int StreamFlushClient(Packet *p, SessionControlBlock *scb); int StreamFlushServer(Packet *p, SessionControlBlock *scb); void TcpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port); void StreamTcpSessionClear(Packet *p); SessionControlBlock *GetLWTcpSession(const SessionKey *key); int GetTcpRebuiltPackets(Packet *p, SessionControlBlock *ssn, PacketIterator callback, void *userdata); int GetTcpStreamSegments(Packet *p, SessionControlBlock *ssn, StreamSegmentIterator callback, void *userdata); int StreamAddSessionAlertTcp(SessionControlBlock *scb, Packet *p, uint32_t gid, uint32_t sid); int StreamCheckSessionAlertTcp(SessionControlBlock *scb, Packet *p, uint32_t gid, uint32_t sid); int StreamUpdateSessionAlertTcp(SessionControlBlock *scb, Packet *p, uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second); void StreamSetExtraDataTcp(SessionControlBlock*, Packet*, uint32_t flag); void StreamClearExtraDataTcp(SessionControlBlock*, Packet*, uint32_t flag); char StreamGetReassemblyDirectionTcp(SessionControlBlock *scb); uint32_t StreamGetFlushPointTcp(SessionControlBlock *scb, char dir); void StreamSetFlushPointTcp(SessionControlBlock *scb, char dir, uint32_t flush_point); char StreamSetReassemblyTcp(SessionControlBlock *scb, uint8_t flush_policy, char dir, char flags); char StreamGetReassemblyFlushPolicyTcp(SessionControlBlock *scb, char dir); char StreamIsStreamSequencedTcp(SessionControlBlock *scb, char dir); int StreamMissingInReassembledTcp(SessionControlBlock *scb, char dir); char StreamPacketsMissingTcp(SessionControlBlock *scb, char dir); void s5TcpSetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing ); void s5TcpUnsetPortFilterStatus( struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing ); int s5TcpGetPortFilterStatus( struct _SnortConfig *sc, unsigned short port, tSfPolicyId policyId, int parsing ); void s5TcpSetSynSessionStatus(struct _SnortConfig *sc, uint16_t status, tSfPolicyId policyId, int parsing); void s5TcpUnsetSynSessionStatus(struct _SnortConfig *sc, uint16_t status, tSfPolicyId policyId, int parsing); void StreamTcpConfigFree(StreamTcpConfig *); void **StreamGetPAFUserDataTcp(SessionControlBlock*, bool to_server, uint8_t id); bool StreamIsPafActiveTcp(SessionControlBlock*, bool to_server); bool StreamActivatePafTcp (SessionControlBlock *scb, int dir, int16_t service_port, uint8_t type); void StreamResetPolicyTcp(SessionControlBlock*, int dir, uint16_t policy, uint16_t mss); void StreamSetSessionDecryptedTcp( SessionControlBlock *scb, bool enable); bool StreamIsSessionDecryptedTcp( SessionControlBlock *scb ); uint32_t StreamGetTcpPrunes(void); void StreamResetTcpPrunes(void); void enableRegisteredPortsForReassembly( struct _SnortConfig *sc ); uint32_t StreamGetPreprocFlagsTcp(SessionControlBlock *scb); #ifdef NORMALIZER void Stream_PrintNormalizationStats(void); void Stream_ResetNormalizationStats(void); #endif void StreamPostConfigTcp(struct _SnortConfig *sc, void*); void registerPortForReassembly( char *network, uint16_t port, int reassembly_direction ); void unregisterPortForReassembly( char *network, uint16_t port, int reassembly_direction ); Packet* getWirePacketTcp(); uint8_t getFlushPolicyDirTcp(); bool StreamIsSessionHttp2Tcp( SessionControlBlock *scb ); void StreamSetSessionHttp2Tcp( SessionControlBlock *scb ); bool StreamIsSessionHttp2UpgTcp( SessionControlBlock *scb ); void StreamSetSessionHttp2UpgTcp( SessionControlBlock *scb ); void SessionTCPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout); unsigned SessionTCPReloadAdjust(unsigned maxWork); void SetFTPFileLocation(void *scbptr ,bool flush); void set_service_based_flush_policy(SessionControlBlock *scb); #ifdef HAVE_DAQ_DECRYPTED_SSL int StreamSimulatePeerTcpAckp( SessionControlBlock *scb, uint8_t dir, uint32_t tcp_payload_len ); #endif size_t get_tcp_used_mempool(); #endif /* STREAM_TCP_H_ */ snort-2.9.20/src/preprocessors/Stream6/snort_stream_tcp.c0000644000175000017500000153077014241077135021665 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** * @file snort_stream_tcp.c * @author Martin Roesch * @author Steven Sturges * */ /* * TODOs: * - midstream ssn pickup (done, SAS 10/14/2005) * - syn flood protection (done, SAS 9/27/2005) * * - review policy anomaly detection * + URG pointer (TODO) * + data on SYN (done, SAS 10/12/2005) * + data on FIN (done, SAS 10/12/2005) * + data after FIN (done, SAS 10/13/2005) * + window scaling/window size max (done, SAS 10/13/2005) * + PAWS, TCP Timestamps (done, SAS 10/12/2005) * * - session shutdown/Reset handling (done, SAS) * - flush policy for Window/Consumed * - limit on number of overlapping packets (done, SAS) */ #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "perf.h" #include "sf_types.h" #include "snort_debug.h" #include "detect.h" #include "plugbase.h" #include "mstring.h" #include "sfxhash.h" #include "util.h" #include "sflsq.h" #include "snort_bounds.h" #include "generators.h" #include "event_queue.h" #include "snort.h" #include "memory_stats.h" #include "parser/IpAddrSet.h" #include "decode.h" #include "encode.h" #include "log.h" #include "active.h" #include "spp_normalize.h" #include "sf_sdlist.h" #include "spp_session.h" #include "session_api.h" #include "snort_session.h" #include "stream_common.h" #include "snort_stream_tcp.h" #include "stream_api.h" #include "session_expect.h" #include "stream_paf.h" #include "stream5_ha.h" #ifdef TARGET_BASED #include "sftarget_protocol_reference.h" #include "sftarget_hostentry.h" #endif #include "profiler.h" #include "ipv6_port.h" #include "sf_iph.h" #include "sp_preprocopt.h" #include "sfPolicy.h" #include "sfActionQueue.h" #include "detection_util.h" #include "file_api.h" #ifdef REG_TEST #include "reg_test.h" #include #endif // port reassembly registration utils static void StreamCreateReassemblyPortList( void ); static void StreamDestoryReassemblyPortList( void ); #ifdef PERF_PROFILING PreprocStats s5TcpPerfStats; PreprocStats s5TcpNewSessPerfStats; PreprocStats s5TcpStatePerfStats; PreprocStats s5TcpDataPerfStats; PreprocStats s5TcpInsertPerfStats; PreprocStats s5TcpPAFPerfStats; PreprocStats s5TcpFlushPerfStats; PreprocStats s5TcpBuildPacketPerfStats; PreprocStats s5TcpProcessRebuiltPerfStats; PreprocStats streamSizePerfStats; PreprocStats streamReassembleRuleOptionPerfStats; extern PreprocStats preprocRuleOptionPerfStats; #endif /* M A C R O S **************************************************/ /* TCP flags */ #define TH_FIN 0x01 #define TH_SYN 0x02 #define TH_RST 0x04 #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 #define TH_ECE 0x40 #define TH_CWR 0x80 #define TH_NORESERVED (TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) /* TCP states */ #define TCP_STATE_NONE 0 #define TCP_STATE_LISTEN 1 #define TCP_STATE_SYN_RCVD 2 #define TCP_STATE_SYN_SENT 3 #define TCP_STATE_ESTABLISHED 4 #define TCP_STATE_CLOSE_WAIT 5 #define TCP_STATE_LAST_ACK 6 #define TCP_STATE_FIN_WAIT_1 7 #define TCP_STATE_CLOSING 8 #define TCP_STATE_FIN_WAIT_2 9 #define TCP_STATE_TIME_WAIT 10 #define TCP_STATE_CLOSED 11 #ifndef MIN # define MIN(a,b) (((a)<(b)) ? (a):(b)) #endif #ifndef MAX # define MAX(a,b) (((a)>(b)) ? (a):(b)) #endif #define PAWS_WINDOW 60 #define PAWS_24DAYS 2147483647 /* 2^ 31 -1 - approx 24 days in milli secs*/ /* for state transition queuing */ #define CHK_SEQ 0 #define NO_CHK_SEQ 1 #define STREAM_UNALIGNED 0 #define STREAM_ALIGNED 1 /* actions */ #define ACTION_NOTHING 0x00000000 #define ACTION_FLUSH_SENDER_STREAM 0x00000001 #define ACTION_FLUSH_RECEIVER_STREAM 0x00000002 #define ACTION_DROP_SESSION 0x00000004 #define ACTION_ACK_SENDER_DATA 0x00000008 #define ACTION_ACK_RECEIVER_DATA 0x00000010 #define ACTION_SET_SSN 0x00000040 #define ACTION_COMPLETE_TWH 0x00000080 #define ACTION_RST 0x00000100 #define ACTION_BAD_SEQ 0x00000200 #define ACTION_BAD_PKT 0x00000400 #define ACTION_LWSSN_CLOSED 0x00000800 #define ACTION_DISABLE_INSPECTION 0x00001000 /* events */ #define EVENT_SYN_ON_EST 0x00000001 #define EVENT_DATA_ON_SYN 0x00000002 #define EVENT_DATA_ON_CLOSED 0x00000004 #define EVENT_BAD_TIMESTAMP 0x00000008 #define EVENT_BAD_SEGMENT 0x00000010 #define EVENT_WINDOW_TOO_LARGE 0x00000020 #define EVENT_EXCESSIVE_TCP_OVERLAPS 0x00000040 #define EVENT_DATA_AFTER_RESET 0x00000080 #define EVENT_SESSION_HIJACK_CLIENT 0x00000100 #define EVENT_SESSION_HIJACK_SERVER 0x00000200 #define EVENT_DATA_WITHOUT_FLAGS 0x00000400 #define EVENT_4WHS 0x00000800 #define EVENT_NO_TIMESTAMP 0x00001000 #define EVENT_BAD_RST 0x00002000 #define EVENT_BAD_FIN 0x00004000 #define EVENT_BAD_ACK 0x00008000 #define EVENT_DATA_AFTER_RST_RCVD 0x00010000 #define EVENT_WINDOW_SLAM 0x00020000 #define EVENT_WIN_SZ_0_TCP_FIN_WAIT_1 0x00040000 #define TF_NONE 0x0000 #define TF_WSCALE 0x0001 #define TF_TSTAMP 0x0002 #define TF_TSTAMP_ZERO 0x0004 #define TF_MSS 0x0008 #define TF_FORCE_FLUSH 0x0010 #define TF_PKT_MISSED 0x0020 // sticky #define TF_MISSING_PKT 0x0040 // used internally #define TF_MISSING_PREV_PKT 0x0080 // reset for each reassembled #define TF_FIRST_PKT_MISSING 0x0100 #define TF_ALL 0xFFFF #define STREAM_INSERT_OK 0 #define STREAM_INSERT_ANOMALY 1 #define STREAM_INSERT_TIMEOUT 2 #define STREAM_INSERT_FAILED 3 #define STREAM_DEFAULT_TCP_PACKET_MEMCAP 8388608 /* 8MB */ #define STREAM_MIN_OVERLAP_LIMIT 0 #define STREAM_MAX_OVERLAP_LIMIT 255 #define STREAM_MAX_FLUSH_FACTOR 2048 #define REASSEMBLY_POLICY_FIRST 1 #define REASSEMBLY_POLICY_LINUX 2 #define REASSEMBLY_POLICY_BSD 3 #define REASSEMBLY_POLICY_OLD_LINUX 4 #define REASSEMBLY_POLICY_LAST 5 #define REASSEMBLY_POLICY_WINDOWS 6 #define REASSEMBLY_POLICY_SOLARIS 7 #define REASSEMBLY_POLICY_HPUX11 8 #define REASSEMBLY_POLICY_IRIX 9 #define REASSEMBLY_POLICY_MACOS 10 #define REASSEMBLY_POLICY_HPUX10 11 #define REASSEMBLY_POLICY_VISTA 12 #define REASSEMBLY_POLICY_WINDOWS2K3 13 #define REASSEMBLY_POLICY_NOACK 14 #define REASSEMBLY_POLICY_DEFAULT REASSEMBLY_POLICY_BSD #define SUB_SYN_SENT 0x01 #define SUB_ACK_SENT 0x02 #define SUB_SETUP_OK 0x03 #define SUB_RST_SENT 0x04 #define SUB_FIN_SENT 0x08 // flush types #define STREAM_FT_INTERNAL 0 // normal s5 "footprint" #define STREAM_FT_EXTERNAL 1 // set by other preprocessor #define STREAM_FT_PAF_MAX 2 // paf_max + footprint fp #define SLAM_MAX 4 /* Only track a maximum number of alerts per session */ #define MAX_SESSION_ALERTS 8 //#define STREAM_DEBUG_ENABLED #ifdef STREAM_DEBUG_ENABLED #define STREAM_DEBUG_WRAP(x) DEBUG_WRAP(x) #else #define STREAM_DEBUG_WRAP(x) #endif /* client/server ip/port dereference */ #define tcp_client_ip scb->client_ip #define tcp_client_port scb->client_port #define tcp_server_ip scb->server_ip #define tcp_server_port scb->server_port static uint32_t pkt_snaplen = 0; #define PKT_SNAPLEN 1514 /* D A T A S T R U C T U R E S ***********************************/ typedef struct _TcpDataBlock { uint32_t seq; uint32_t ack; uint32_t win; uint32_t end_seq; uint32_t ts; } TcpDataBlock; typedef struct _StateMgr { uint8_t state; uint8_t sub_state; uint8_t state_queue; uint8_t expected_flags; uint32_t transition_seq; uint32_t stq_get_seq; } StateMgr; #define RAND_FLUSH_POINTS 64 //------------------------------------------------------------------------- // extra, extra - read all about it! // -- u2 is the only output plugin that currently supports extra data // -- extra data may be captured before or after alerts // -- extra data may be per packet or persistent (saved on session) // // -- per packet extra data is logged iff we alert on the packet // containing the extra data - u2 drives this // -- an extra data mask is added to Packet to indicate when per packet // extra data is available // // -- persistent extra data must be logged exactly once for each alert // regardless of capture/alert ordering - s5 purge_alerts drives this // -- an extra data mask is added to the session trackers to indicate that // persistent extra data is available // // -- event id and second are added to the session alert trackers so that // the extra data can be correlated with events // -- event id and second are not available when StreamAddSessionAlertTcp // is called; u2 calls StreamUpdateSessionAlertTcp as events are logged // to set these fields //------------------------------------------------------------------------- typedef struct _StreamAlertInfo { /* For storing alerts that have already been seen on the session */ uint32_t sid; uint32_t gid; uint32_t seq; // if we log extra data, event_* is used to correlate with alert uint32_t event_id; uint32_t event_second; } StreamAlertInfo; //----------------------------------------------------------------- // we make a lot of StreamSegments, StreamTrackers, and TcpSessions // so they are organized by member size/alignment requirements to // minimize unused space in the structs. // ... however, use of padding below is critical, adjust if needed //----------------------------------------------------------------- typedef struct _StreamSegment { uint8_t *data; uint8_t *payload; struct _StreamSegment *prev; struct _StreamSegment *next; #ifdef DEBUG int ordinal; #endif struct timeval tv; uint32_t caplen; uint32_t pktlen; uint32_t ts; uint32_t seq; uint16_t orig_dsize; uint16_t size; uint16_t urg_offset; uint8_t buffered; // this sequence ensures 4-byte alignment of iph in pkt // (only significant if we call the grinder) uint8_t pad1; uint16_t pad2; uint8_t pkt[1]; // variable length } StreamSegment; typedef struct _StreamTracker { StateMgr s_mgr; /* state tracking goodies */ FlushMgr flush_mgr; /* please flush twice, it's a long way to * the bitbucket... */ // this is intended to be private to s5_paf but is included // directly to avoid the need for allocation; do not directly // manipulate within this module. PAF_State paf_state; // for tracking protocol aware flushing StreamAlertInfo alerts[MAX_SESSION_ALERTS]; /* history of alerts */ StreamTcpPolicy *tcp_policy; StreamSegment *seglist; /* first queued segment */ StreamSegment *seglist_tail; /* last queued segment */ // TBD move out of here since only used per packet? StreamSegment* seglist_next; /* next queued segment to flush */ StreamSegment *held_segment; /* blocked segment of http connection */ #ifdef DEBUG int segment_ordinal; #endif /* Local in the context of these variables means the local part * of the connection. For example, if this particular StreamTracker * was tracking the client side of a connection, the l_unackd value * would represent the client side of the connection's last unacked * sequence number */ uint32_t l_unackd; /* local unack'd seq number */ uint32_t l_nxt_seq; /* local next expected sequence */ uint32_t l_window; /* local receive window */ uint32_t r_nxt_ack; /* next expected ack from remote side */ uint32_t r_win_base; /* remote side window base sequence number * (i.e. the last ack we got) */ uint32_t isn; /* initial sequence number */ uint32_t ts_last; /* last timestamp (for PAWS) */ uint32_t ts_last_pkt; /* last packet timestamp we got */ uint32_t seglist_base_seq; /* seq of first queued segment */ uint32_t seg_count; /* number of current queued segments */ uint32_t seg_bytes_total; /* total bytes currently queued */ uint32_t seg_bytes_logical; /* logical bytes queued (total - overlaps) */ uint32_t total_bytes_queued; /* total bytes queued (life of session) */ uint32_t total_segs_queued; /* number of segments queued (life) */ uint32_t overlap_count; /* overlaps encountered */ uint32_t small_seg_count; uint32_t flush_count; /* number of flushed queued segments */ uint32_t xtradata_mask; /* extra data available to log */ uint16_t os_policy; uint16_t reassembly_policy; uint16_t wscale; /* window scale setting */ uint16_t mss; /* max segment size */ uint16_t flags; /* bitmap flags (TF_xxx) */ uint8_t mac_addr[6]; uint8_t alert_count; /* number alerts stored (up to MAX_SESSION_ALERTS) */ } StreamTracker; typedef struct _TcpSession { StreamTracker client; StreamTracker server; SessionControlBlock *scb; #ifdef DEBUG struct timeval ssn_time; #endif #ifdef HAVE_DAQ_ADDRESS_SPACE_ID int32_t ingress_index; /* Index of the inbound interface. */ int32_t egress_index; /* Index of the outbound interface. */ int32_t ingress_group; /* Index of the inbound group. */ int32_t egress_group; /* Index of the outbound group. */ uint32_t daq_flags; /* Flags for the packet (DAQ_PKT_FLAG_*) */ void *priv_ptr; /* private data pointer. used in pinhole */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t address_space_id_src; uint16_t address_space_id_dst; #else uint16_t address_space_id; #endif #ifdef HAVE_DAQ_FLOW_ID uint32_t daq_flow_id; #endif #endif uint8_t ecn; bool session_decrypted; uint8_t pp_flags; } TcpSession; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID TcpSession initialization ={ .ingress_index = DAQ_PKTHDR_UNKNOWN, .egress_index = DAQ_PKTHDR_UNKNOWN, .ingress_group = DAQ_PKTHDR_UNKNOWN, .egress_group = DAQ_PKTHDR_UNKNOWN }; #endif #define SL_BUF_FLUSHED 1 static inline int SetupOK (const StreamTracker* st) { return ( (st->s_mgr.sub_state & SUB_SETUP_OK) == SUB_SETUP_OK ); } static inline int Stream_NormGetMode(uint16_t reassembly_policy, const SnortConfig* sc, NormFlags nf) { if ( reassembly_policy == REASSEMBLY_POLICY_NOACK ) return NORM_MODE_OFF; return Normalize_GetMode(sc, nf); } static inline uint32_t SegsToFlush (const StreamTracker* st, unsigned max) { uint32_t n = st->seg_count - st->flush_count; StreamSegment* s; if ( !n || max == 1 ) return n; n = 0; s = st->seglist; while ( s ) { if ( !s->buffered && SEQ_LT(s->seq, st->r_win_base) ) n++; if ( max && n == max ) return n; s = s->next; } return n; } static inline bool DataToFlush (const StreamTracker* st) { if ( (st->flush_mgr.flush_policy == STREAM_FLPOLICY_PROTOCOL) #ifdef NORMALIZER || (st->flush_mgr.flush_policy == STREAM_FLPOLICY_PROTOCOL_IPS) #endif || st->flush_mgr.flush_policy == STREAM_FLPOLICY_PROTOCOL_NOACK || st->flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_NOACK ) return ( SegsToFlush(st, 1) > 0 ); #ifdef NORMALIZER if ((st->flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_IPS) || (st->flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_IPS_FTP)) return ( SegsToFlush(st, 1) > 1 ); #endif return ( SegsToFlush(st, 2) > 1 ); } #ifdef REG_TEST int default_ports[] = { 21, 23, 25, 42, 53, 80, 110, 111, 135, 136, 137, 139, 143, 445, 513, 514, 1433, 1521, 2401, 3306 }; #define DEFAULT_PORTS_SIZE (int)(sizeof(default_ports)/sizeof(int)) #else int *default_ports = 0; #define DEFAULT_PORTS_SIZE 0 #endif #ifdef TARGET_BASED char *default_protocols[] = { "ftp", "telnet", "smtp", "nameserver", "dns", "http", "pop3", "sunrpc", "dcerpc", "netbios-ssn", "imap", "login", "shell", "mssql", "oracle", "cvs", "mysql" }; #endif FlushConfig ignore_flush_policy[MAX_PORTS]; #ifdef TARGET_BASED FlushConfig ignore_flush_policy_protocol[MAX_PROTOCOL_ORDINAL]; #endif /* P R O T O T Y P E S ********************************************/ static void StreamParseTcpArgs(struct _SnortConfig *, StreamTcpConfig *, char *, StreamTcpPolicy *); static void StreamPrintTcpConfig(StreamTcpPolicy *); static void StreamInitPacket(); static inline void SetupTcpDataBlock(TcpDataBlock *, Packet *); static int ProcessTcp(SessionControlBlock *, Packet *, TcpDataBlock *, StreamTcpPolicy *, SFXHASH_NODE *); #if OLD_CODE_NOLONGER_USED_DEPENDS_ON_CURRENT_STATE static inline void QueueState(uint8_t, StreamTracker*, uint8_t, uint32_t, uint8_t); static inline int EvalStateQueue(StreamTracker *, uint8_t, uint32_t); #endif static inline int CheckFlushPolicyOnData( StreamTcpConfig *config, TcpSession *, StreamTracker *, StreamTracker *, TcpDataBlock *, Packet *); static inline int CheckFlushPolicyOnAck( StreamTcpConfig *config, TcpSession *, StreamTracker *, StreamTracker *, TcpDataBlock *, Packet *); static void StreamSeglistAddNode(StreamTracker *, StreamSegment *, StreamSegment *); static int StreamSeglistDeleteNode(StreamTracker *, StreamSegment *); static int StreamSeglistDeleteNodeTrim(StreamTracker*, StreamSegment*, uint32_t flush_seq); static int AddStreamNode(StreamTracker *st, Packet *p, TcpDataBlock*, TcpSession *tcpssn, uint16_t len, uint32_t slide, uint32_t trunc, uint32_t seq, StreamSegment *left, StreamSegment **retSeg); static int DupStreamNode( Packet*, StreamTracker*, StreamSegment* left, StreamSegment** retSeg); static uint32_t StreamGetWscale(Packet *, uint16_t *); static uint32_t StreamPacketHasWscale(Packet *); static uint32_t StreamGetMss(Packet *, uint16_t *); static uint32_t StreamGetTcpTimestamp(Packet *, uint32_t *, int strip); static int FlushStream( Packet*, StreamTracker *st, uint32_t toSeq, uint8_t *flushbuf, const uint8_t *flushbuf_end); static void TcpSessionCleanup(SessionControlBlock *ssn, int freeApplicationData); static void TcpSessionCleanupWithFreeApplicationData(void *ssn); static void FlushQueuedSegs(SessionControlBlock *ssn, TcpSession *tcpssn); int s5TcpStreamSizeInit(struct _SnortConfig *sc, char *name, char *parameters, void **dataPtr); int s5TcpStreamSizeEval(void *p, const uint8_t **cursor, void *dataPtr); void s5TcpStreamSizeCleanup(void *dataPtr); int s5TcpStreamReassembleRuleOptionInit(struct _SnortConfig *sc, char *name, char *parameters, void **dataPtr); int s5TcpStreamReassembleRuleOptionEval(void *p, const uint8_t **cursor, void *dataPtr); void s5TcpStreamReassembleRuleOptionCleanup(void *dataPtr); static inline void ResetFlushMgrs(void); static void targetPolicyIterate(void (*callback)(int)); static void policyDecoderFlagsSaveNClear(int policyId); static void policyDecoderFlagsRestore(int policyId); static void policyChecksumFlagsSaveNClear(int policyId); static void policyChecksumFlagsRestore(int policyId); static inline uint16_t GetTcpReassemblyPolicy(int os_policy); /* G L O B A L S **************************************************/ static SessionCache* tcp_lws_cache = NULL; static Packet *s5_pkt = NULL; static Packet *tcp_cleanup_pkt = NULL; static const uint8_t *s5_pkt_end = NULL; static char midstream_allowed = 0; static Packet *wire_packet = NULL; static uint8_t flush_policy_for_dir = 0; /* enum for policy names */ static char *reassembly_policy_names[] = { "no policy!", "FIRST", "LINUX", "BSD", "OLD LINUX", "LAST", "WINDOWS", "SOLARIS", "HPUX11", "IRIX", "MACOS", "HPUX10", "WINDOWS VISTA", "WINDOWS 2003" "IPS" }; #ifdef STREAM_DEBUG_ENABLED static char *state_names[] = { "NONE", "LISTEN", "SYN_RCVD", "SYN_SENT", "ESTABLISHED", "CLOSE_WAIT", "LAST_ACK", "FIN_WAIT_1", "CLOSING", "FIN_WAIT_2", "TIME_WAIT", "CLOSED" }; #endif static char *flush_policy_names[] = { "None", "Footprint", "Logical", "Response", "Sliding Window", #if 0 "Consumed", #endif "Ignore", "Protocol", "Footprint-IPS", "Protocol-IPS", "Footprint-NOACK", "Protocol-NOACK", "Footprint-IPS-FTP", "Disabled" }; static int s5_tcp_cleanup = 0; static uint32_t g_static_points[RAND_FLUSH_POINTS] = { 128, 217, 189, 130, 240, 221, 134, 129, 250, 232, 141, 131, 144, 177, 201, 130, 230, 190, 177, 142, 130, 200, 173, 129, 250, 244, 174, 151, 201, 190, 180, 198, 220, 201, 142, 185, 219, 129, 194, 140, 145, 191, 197, 183, 199, 220, 231, 245, 233, 135, 143, 158, 174, 194, 200, 180, 201, 142, 153, 187, 173, 199, 143, 201 }; /* F U N C T I O N S **********************************************/ static inline uint32_t GenerateFlushPoint(FlushPointList *flush_point_list) { return (rand() % flush_point_list->flush_range) + flush_point_list->flush_base; } static inline void InitFlushPointList(FlushPointList *flush_point_list, uint32_t value, uint32_t range, int use_static) { uint32_t i; uint32_t flush_range = range; uint32_t flush_base = value - range/2; if (!flush_point_list) return; if (!flush_point_list->initialized) { #ifdef REG_TEST const char* sfp = getenv("S5_FPT"); // no error checking required - atoi() is sufficient uint32_t cfp = sfp ? atoi(sfp) : 0; if ( cfp < 128 || cfp > 255 ) cfp = 192; #else const uint32_t cfp = 192; #endif flush_point_list->flush_range = flush_range; flush_point_list->flush_base = flush_base; #ifndef DYNAMIC_RANDOM_FLUSH_POINTS flush_point_list->current = 0; flush_point_list->flush_points = SnortPreprocAlloc(RAND_FLUSH_POINTS, sizeof(uint32_t), PP_STREAM, PP_MEM_CATEGORY_CONFIG); for (i=0;irun_flags & RUN_FLAG__STATIC_HASH) { if ( i == 0 ) LogMessage("WARNING: using constant flush point = %u!\n", cfp); flush_point_list->flush_points[i] = cfp; } else if (use_static) { if ( i == 0 ) LogMessage("WARNING: using static flush points.\n"); flush_point_list->flush_points[i] = g_static_points[i]; } else { flush_point_list->flush_points[i] = GenerateFlushPoint(flush_point_list); } } #endif flush_point_list->initialized = 1; } } static inline void UpdateFlushMgr( SnortConfig *sc, FlushMgr *mgr, FlushPointList *flush_point_list, uint32_t flags) { if ( mgr->flush_type == STREAM_FT_EXTERNAL ) return; switch (mgr->flush_policy) { case STREAM_FLPOLICY_FOOTPRINT: case STREAM_FLPOLICY_LOGICAL: break; case STREAM_FLPOLICY_PROTOCOL: if ( flags & TF_PKT_MISSED ) { mgr->flush_policy = STREAM_FLPOLICY_FOOTPRINT; mgr->flush_type = STREAM_FT_PAF_MAX; } break; #ifdef NORMALIZER case STREAM_FLPOLICY_FOOTPRINT_IPS: case STREAM_FLPOLICY_FOOTPRINT_IPS_FTP: break; case STREAM_FLPOLICY_PROTOCOL_IPS: if ( flags & TF_PKT_MISSED ) { mgr->flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS; mgr->flush_type = STREAM_FT_PAF_MAX; } break; #endif case STREAM_FLPOLICY_FOOTPRINT_NOACK: break; case STREAM_FLPOLICY_PROTOCOL_NOACK: if ( flags & TF_PKT_MISSED ) { mgr->flush_policy = STREAM_FLPOLICY_FOOTPRINT_NOACK; mgr->flush_type = STREAM_FT_PAF_MAX; } break; default: return; } /* Ideally, we would call rand() each time, but that * is a performance headache waiting to happen. */ #ifdef DYNAMIC_RANDOM_FLUSH_POINTS mgr->flush_pt = GenerateFlushPoint(); #else if (flush_point_list) { /* Handle case where it wasn't initialized... */ if (flush_point_list->initialized == 0) { InitFlushPointList(flush_point_list, 192, 128, 0); } mgr->flush_pt = flush_point_list->flush_points[flush_point_list->current]; flush_point_list->current = (flush_point_list->current+1) % RAND_FLUSH_POINTS; } #endif //Do not reset the last_size for FTP flush policy if(mgr->flush_policy != STREAM_FLPOLICY_FOOTPRINT_IPS_FTP) { mgr->last_size = 0; mgr->last_count = 0; } if ( mgr->flush_type == STREAM_FT_PAF_MAX ) mgr->flush_pt += ScPafMaxNewConf(sc); } static inline void InitFlushMgr( SnortConfig *sc, FlushMgr *mgr, FlushPointList *flush_point_list, uint8_t policy, uint8_t auto_disable) { // if policy is to disable reassembly just set policy in flush manager and // return if( policy == STREAM_FLPOLICY_DISABLED ) { mgr->flush_policy = policy; return; } else if ( mgr->flush_policy == STREAM_FLPOLICY_DISABLED ) { WarningMessage( "Stream policy has disabled reassembly on this port." ); return; } mgr->flush_policy = policy; mgr->flush_type = STREAM_FT_INTERNAL; mgr->auto_disable = auto_disable; UpdateFlushMgr(sc, mgr, flush_point_list, 0); #ifdef NORMALIZER if ( Normalize_GetMode(sc, NORM_TCP_IPS) == NORM_MODE_ON) { if ( policy == STREAM_FLPOLICY_FOOTPRINT ) mgr->flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS; else if ( policy == STREAM_FLPOLICY_PROTOCOL ) mgr->flush_policy = STREAM_FLPOLICY_PROTOCOL_IPS; } #endif } static inline void InitFlushMgrByPort ( SessionControlBlock* scb, StreamTracker* pst, uint16_t port, bool c2s, uint8_t flush_policy, bool all_services) { uint16_t registration, auto_disable = 0; bool flush = (flush_policy != STREAM_FLPOLICY_IGNORE); #if 0 // this check required if PAF doesn't abort if ( scb->session_state & STREAM_STATE_MIDSTREAM ) registration = 0; else #endif if(all_services) { registration = s5_paf_port_registration_all( ( ( StreamConfig * ) scb->stream_config )->tcp_config->paf_config, port, c2s, flush); } else { registration = s5_paf_port_registration( ( ( StreamConfig * ) scb->stream_config )->tcp_config->paf_config, port, c2s, flush); } if ( registration ) { if( flush_policy == STREAM_FLPOLICY_FOOTPRINT_NOACK ) flush_policy = STREAM_FLPOLICY_PROTOCOL_NOACK; else flush_policy = STREAM_FLPOLICY_PROTOCOL; s5_paf_setup(&pst->paf_state, registration); auto_disable = !flush; } InitFlushMgr(snort_conf, &pst->flush_mgr, &pst->tcp_policy->flush_point_list, flush_policy, auto_disable); } static inline void InitFlushMgrByService ( SessionControlBlock* scb, StreamTracker* pst, int16_t service, bool c2s, uint8_t flush_policy) { uint16_t registration, auto_disable = 0; bool flush = (flush_policy != STREAM_FLPOLICY_IGNORE); #if 0 // this check required if PAF doesn't abort if ( scb->session_state & STREAM_STATE_MIDSTREAM ) registration = 0; else #endif registration = s5_paf_service_registration( ( ( StreamConfig * ) scb->stream_config )->tcp_config->paf_config, service, c2s, flush); if ( registration ) { if( flush_policy == STREAM_FLPOLICY_FOOTPRINT_NOACK ) flush_policy = STREAM_FLPOLICY_PROTOCOL_NOACK; else flush_policy = STREAM_FLPOLICY_PROTOCOL; s5_paf_setup(&pst->paf_state, registration); auto_disable = !flush; } InitFlushMgr(snort_conf, &pst->flush_mgr, &pst->tcp_policy->flush_point_list, flush_policy, auto_disable); } static int ResetFlushMgrsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { int i; StreamConfig *pPolicyConfig = (StreamConfig *)pData; //do any housekeeping before freeing StreamConfig if (pPolicyConfig->tcp_config == NULL) return 0; for (i = 0; i < pPolicyConfig->tcp_config->num_policies; i++) { int j; StreamTcpPolicy *policy = pPolicyConfig->tcp_config->policy_list[i]; FlushPointList *fpl = &policy->flush_point_list; FlushMgr *client, *server; uint8_t flush_policy; fpl->current = 0; for (j = 0; j < MAX_PORTS; j++) { client = &policy->flush_config[j].client; flush_policy = policy->flush_config[j].client.flush_policy; InitFlushMgr(snort_conf, client, fpl, flush_policy, 0); server = &policy->flush_config[j].server; flush_policy = policy->flush_config[j].server.flush_policy; InitFlushMgr(snort_conf, server, fpl, flush_policy, 0); } #ifdef TARGET_BASED /* protocol 0 is the unknown case. skip it */ for (j = 1; j < MAX_PROTOCOL_ORDINAL; j++) { client = &policy->flush_config_protocol[j].client; flush_policy = policy->flush_config_protocol[j].client.flush_policy; InitFlushMgr(snort_conf, client, fpl, flush_policy, 0); server = &policy->flush_config_protocol[j].server; flush_policy = policy->flush_config_protocol[j].server.flush_policy; InitFlushMgr(snort_conf, server, fpl, flush_policy, 0); } #endif } return 0; } static inline void ResetFlushMgrs( void ) { if( stream_online_config == NULL ) return; sfPolicyUserDataFreeIterate( stream_online_config, ResetFlushMgrsPolicy ); } void **StreamGetPAFUserDataTcp( SessionControlBlock* scb, bool to_server, uint8_t id ) { TcpSession* tcpssn; PAF_State *ps; int8_t i; if(!id) return NULL; if( !scb->proto_specific_data ) return NULL; tcpssn = ( TcpSession * ) scb->proto_specific_data->data; if ( !tcpssn ) return NULL; ps = ( to_server ) ? &tcpssn->server.paf_state : &tcpssn->client.paf_state; i = s5_paf_get_user_data_index( ps, id ); if( i >= 0 ) return &ps->user[i]; else return NULL; } bool StreamIsPafActiveTcp( SessionControlBlock *scb, bool to_server ) { TcpSession *tcpssn; FlushMgr *fm; if( !scb->proto_specific_data ) return false; tcpssn = ( TcpSession * ) scb->proto_specific_data->data; if( !tcpssn ) return false; fm = ( to_server ) ? &tcpssn->server.flush_mgr : &tcpssn->client.flush_mgr; return ( ( fm->flush_policy == STREAM_FLPOLICY_PROTOCOL ) #ifdef NORMALIZER || ( fm->flush_policy == STREAM_FLPOLICY_PROTOCOL_IPS ) #endif || (fm->flush_policy == STREAM_FLPOLICY_PROTOCOL_NOACK) ); } bool StreamActivatePafTcp (SessionControlBlock *scb, int dir, int16_t service_port, uint8_t type) { TcpSession *tcpssn; StreamTracker *trk; FlushMgr *fm; uint8_t flush_policy; if( !scb->proto_specific_data ) return false; tcpssn = ( TcpSession * ) scb->proto_specific_data->data; if( !tcpssn || !ScPafEnabled() ) return false; // must have valid stream config for this session, may need to reset after a reload if( scb->stream_config == NULL ) { WarningMessage("Stream Configuration NULL For In Progress Session, Reinitializing.\n"); scb->stream_config = sfPolicyUserDataGet( stream_online_config, getNapRuntimePolicy() ); if( scb->stream_config == NULL ) { ErrorMessage("No Valid Stream Configurations, Stream Processing Terminated For This Packet.\n"); return false; } } switch ( dir ) { case SSN_DIR_TO_CLIENT: { trk = &tcpssn->client; fm = &tcpssn->client.flush_mgr; flush_policy = fm->flush_policy; if ( flush_policy == STREAM_FLPOLICY_IGNORE ) flush_policy = STREAM_FLPOLICY_PROTOCOL; if ( type == PAF_TYPE_SERVICE ) { InitFlushMgrByService(scb, trk, service_port, false, flush_policy); } else { InitFlushMgrByPort(scb, trk, service_port, false, flush_policy, true); } } break; case SSN_DIR_TO_SERVER: { trk = &tcpssn->server; fm = &tcpssn->server.flush_mgr; flush_policy = fm->flush_policy; if ( flush_policy == STREAM_FLPOLICY_IGNORE ) flush_policy = STREAM_FLPOLICY_PROTOCOL; if ( type == PAF_TYPE_SERVICE ) { InitFlushMgrByService(scb, trk, service_port, true, flush_policy); } else { InitFlushMgrByPort(scb, trk, service_port, true, flush_policy, true ); } } break; case SSN_DIR_BOTH: { trk = &tcpssn->server; fm = &tcpssn->server.flush_mgr; flush_policy = fm->flush_policy; if ( flush_policy == STREAM_FLPOLICY_IGNORE ) flush_policy = STREAM_FLPOLICY_PROTOCOL; if ( type == PAF_TYPE_SERVICE ) { InitFlushMgrByService(scb, trk, service_port, true, flush_policy); } else { InitFlushMgrByPort(scb, trk, service_port, true, flush_policy, true ); } } { trk = &tcpssn->client; fm = &tcpssn->client.flush_mgr; flush_policy = fm->flush_policy; if ( flush_policy == STREAM_FLPOLICY_IGNORE ) flush_policy = STREAM_FLPOLICY_PROTOCOL; if ( type == PAF_TYPE_SERVICE ) { InitFlushMgrByService(scb, trk, service_port, false, flush_policy); } else { InitFlushMgrByPort(scb, trk, service_port, false, flush_policy, true); } } break; default: return false; } return true; } static inline void StreamResetPolicyPerTrk( StreamTracker* trk, uint16_t policy, uint16_t mss ) { trk->tcp_policy->policy = trk->os_policy = policy; trk->reassembly_policy = GetTcpReassemblyPolicy( policy ); trk->mss = mss; } void StreamResetPolicyTcp ( SessionControlBlock *scb, int dir, uint16_t policy, uint16_t mss) { TcpSession *tcpssn; if( !scb->proto_specific_data ) return; tcpssn = ( TcpSession * ) scb->proto_specific_data->data; if( !tcpssn ) return; switch( dir ) { case SSN_DIR_TO_CLIENT: StreamResetPolicyPerTrk( &tcpssn->client, policy, mss ); break; case SSN_DIR_TO_SERVER: StreamResetPolicyPerTrk( &tcpssn->server, policy, mss ); break; case SSN_DIR_BOTH: StreamResetPolicyPerTrk( &tcpssn->server, policy, mss ); StreamResetPolicyPerTrk( &tcpssn->client, policy, mss ); break; default: break; } } void StreamSetSessionDecryptedTcp( SessionControlBlock *scb, bool enable ) { TcpSession *tcpssn; if( !scb->proto_specific_data ) return; tcpssn = ( TcpSession * ) scb->proto_specific_data->data; if ( !tcpssn ) return; if((SegsToFlush(&tcpssn->server, 1) > 0) || (SegsToFlush(&tcpssn->client, 1) > 0)) { FlushQueuedSegs(scb, tcpssn); Active_Reset(); } tcpssn->session_decrypted = enable; } bool StreamIsSessionDecryptedTcp( SessionControlBlock *scb ) { TcpSession *tcpssn; if( !scb->proto_specific_data ) return false; tcpssn = ( TcpSession * ) scb->proto_specific_data->data; if ( !tcpssn ) return false; return ( tcpssn->session_decrypted == true ); } uint32_t StreamGetPreprocFlagsTcp(SessionControlBlock *scb) { TcpSession *tcpssn; if( !scb->proto_specific_data ) return 0; tcpssn = ( TcpSession * ) scb->proto_specific_data->data; if ( !tcpssn ) return 0; return tcpssn->pp_flags; } //------------------------------------------------------------------------- // tcp ha stuff #ifdef ENABLE_HA static SessionControlBlock *StreamTCPCreateSession( const SessionKey *key ) { setNapRuntimePolicy( getDefaultPolicy() ); /* TODO: Set the TCP policy to something here? */ return session_api->create_session( tcp_lws_cache, NULL, key ); } static void StreamTCPDeactivateSession( SessionControlBlock *scb ) { if( scb->proto_specific_data ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Cleaning up the TCP session associated with the session being put into standby.\n");); TcpSessionCleanup( scb, 0 ); } scb->session_state &= ~( STREAM_STATE_SYN | STREAM_STATE_SYN_ACK | STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED ); scb->ha_state.session_flags &= ~( SSNFLAG_SEEN_CLIENT | SSNFLAG_SEEN_SERVER ); } static int StreamTCPDeleteSession( const SessionKey *key ) { SessionControlBlock *scb = session_api->get_session_by_key( tcp_lws_cache, key ); if( scb != NULL ) { if( StreamSetRuntimeConfiguration( scb, scb->protocol ) == 0 ) { if (!session_api->delete_session( tcp_lws_cache, scb, "ha sync", false )) s5stats.active_tcp_sessions--; } else WarningMessage(" WARNING: Attempt to delete a TCP Session when no valid runtime configuration.\n" ); } return 0; } #endif SessionControlBlock *GetLWTcpSession( const SessionKey *key ) { return session_api->get_session_by_key( tcp_lws_cache, key ); } #ifdef ENABLE_HA static HA_Api ha_tcp_api = { /*.get_lws = */ GetLWTcpSession, /*.create_session = */ StreamTCPCreateSession, /*.deactivate_session = */ StreamTCPDeactivateSession, /*.delete_session = */ StreamTCPDeleteSession, }; #endif void setTcpDirectionAndPorts( Packet *p, SessionControlBlock *scb ) { if( TCP_ISFLAGSET( p->tcph, TH_SYN ) && !TCP_ISFLAGSET( p->tcph, TH_ACK ) ) { STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "Stream SYN PACKET, establishing lightweight session direction.\n");); /* SYN packet from client */ scb->ha_state.direction = FROM_CLIENT; IP_COPY_VALUE( scb->client_ip, GET_SRC_IP( p ) ); scb->client_port = p->tcph->th_sport; IP_COPY_VALUE( scb->server_ip, GET_DST_IP( p ) ); scb->server_port = p->tcph->th_dport; } else if( TCP_ISFLAGSET( p->tcph, ( TH_SYN | TH_ACK ) ) ) { scb->ha_state.direction = FROM_SERVER; IP_COPY_VALUE( scb->client_ip, GET_DST_IP( p ) ); scb->client_port = p->tcph->th_dport; IP_COPY_VALUE( scb->server_ip, GET_SRC_IP( p ) ); scb->server_port = p->tcph->th_sport; } else { /* Assume from client, can update later */ if( p->sp > p->dp ) { scb->ha_state.direction = FROM_CLIENT; IP_COPY_VALUE( scb->client_ip, GET_SRC_IP( p ) ); scb->client_port = p->tcph->th_sport; IP_COPY_VALUE( scb->server_ip, GET_DST_IP( p ) ); scb->server_port = p->tcph->th_dport; } else { scb->ha_state.direction = FROM_SERVER; IP_COPY_VALUE( scb->client_ip, GET_DST_IP( p ) ); scb->client_port = p->tcph->th_dport; IP_COPY_VALUE( scb->server_ip, GET_SRC_IP( p ) ); scb->server_port = p->tcph->th_sport; } } } void StreamInitTcp( void ) { if( tcp_lws_cache == NULL ) { tcp_lws_cache = session_api->init_session_cache( SESSION_PROTO_TCP, sizeof( TcpSession ), &TcpSessionCleanupWithFreeApplicationData ); StreamTcpRegisterPreprocProfiles(); } #ifdef ENABLE_HA ha_set_api( IPPROTO_TCP, &ha_tcp_api ); #endif pkt_snaplen = (snort_conf->pkt_snaplen > 0) ? snort_conf->pkt_snaplen : PKT_SNAPLEN; } void StreamTcpRegisterPreprocProfiles( void ) { #ifdef PERF_PROFILING RegisterPreprocessorProfile( "s5TcpNewSess", &s5TcpNewSessPerfStats, 2, &s5TcpPerfStats , NULL); RegisterPreprocessorProfile( "s5TcpState", &s5TcpStatePerfStats, 2, &s5TcpPerfStats , NULL); RegisterPreprocessorProfile( "s5TcpData", &s5TcpDataPerfStats, 3, &s5TcpStatePerfStats , NULL); RegisterPreprocessorProfile( "s5TcpPktInsert", &s5TcpInsertPerfStats, 4, &s5TcpDataPerfStats , NULL); RegisterPreprocessorProfile( "s5TcpPAF", &s5TcpPAFPerfStats, 3, &s5TcpStatePerfStats , NULL); RegisterPreprocessorProfile( "s5TcpFlush", &s5TcpFlushPerfStats, 3, &s5TcpStatePerfStats , NULL); RegisterPreprocessorProfile( "s5TcpBuildPacket", &s5TcpBuildPacketPerfStats, 4, &s5TcpFlushPerfStats , NULL); RegisterPreprocessorProfile( "s5TcpProcessRebuilt", &s5TcpProcessRebuiltPerfStats, 4, &s5TcpFlushPerfStats, NULL ); #endif } void StreamTcpRegisterRuleOptions( struct _SnortConfig *sc ) { /* Register the 'stream_size' rule option */ RegisterPreprocessorRuleOption( sc, "stream_size", &s5TcpStreamSizeInit, &s5TcpStreamSizeEval, &s5TcpStreamSizeCleanup, NULL, NULL, NULL, NULL ); RegisterPreprocessorRuleOption( sc, "stream_reassemble", &s5TcpStreamReassembleRuleOptionInit, &s5TcpStreamReassembleRuleOptionEval, &s5TcpStreamReassembleRuleOptionCleanup, NULL, NULL, NULL, NULL ); #ifdef PERF_PROFILING RegisterPreprocessorProfile( "stream_size", &streamSizePerfStats, 4, &preprocRuleOptionPerfStats , NULL); RegisterPreprocessorProfile( "reassemble", &streamReassembleRuleOptionPerfStats, 4, &preprocRuleOptionPerfStats, NULL); #endif } void StreamTcpInitFlushPoints( void ) { int i; /* Seed the flushpoint random generator */ srand( ( unsigned int ) sizeof( default_ports ) + ( unsigned int ) time( NULL ) ); /* Default is to ignore, for all ports */ for( i = 0; i < MAX_PORTS; i++ ) { ignore_flush_policy[i].client.flush_policy = STREAM_FLPOLICY_IGNORE; ignore_flush_policy[i].server.flush_policy = STREAM_FLPOLICY_IGNORE; } #ifdef TARGET_BASED for( i = 0; i < MAX_PROTOCOL_ORDINAL; i++) { ignore_flush_policy_protocol[i].client.flush_policy = STREAM_FLPOLICY_IGNORE; ignore_flush_policy_protocol[i].server.flush_policy = STREAM_FLPOLICY_IGNORE; } #endif } static void addStreamTcpPolicyToList( StreamTcpConfig *config, StreamTcpPolicy *policy ) { config->num_policies++; /* Now add this context to the internal list */ if( config->policy_list == NULL ) { config->policy_list = ( StreamTcpPolicy ** ) SnortPreprocAlloc(1, sizeof( StreamTcpPolicy * ), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } else { uint32_t policyListSize = sizeof( StreamTcpPolicy * ) * ( config->num_policies ); StreamTcpPolicy **tmpPolicyList = ( StreamTcpPolicy ** ) SnortPreprocAlloc(1, policyListSize, PP_STREAM, PP_MEM_CATEGORY_CONFIG); // copy the existing policies to new list, free old list and point to new... memcpy( tmpPolicyList, config->policy_list, policyListSize - sizeof( StreamTcpPolicy * ) ); SnortPreprocFree( config->policy_list, sizeof( StreamTcpPolicy * ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); config->policy_list = tmpPolicyList; } // add new policy to end of the list config->policy_list[config->num_policies - 1] = policy; } StreamTcpPolicy *initTcpPolicyInstance( void ) { StreamTcpPolicy *tcp_policy = (StreamTcpPolicy *) SnortPreprocAlloc(1, sizeof(StreamTcpPolicy), PP_STREAM, PP_MEM_CATEGORY_CONFIG); /* Initialize flush policy to Ignore */ memcpy(&tcp_policy->flush_config, ignore_flush_policy, sizeof(FlushConfig) * MAX_PORTS); #ifdef TARGET_BASED memcpy(&tcp_policy->flush_config_protocol, ignore_flush_policy_protocol, sizeof(FlushConfig) * MAX_PROTOCOL_ORDINAL); #endif return tcp_policy; } StreamTcpPolicy *StreamTcpPolicyClone( StreamTcpPolicy *master, StreamConfig *config ) { StreamTcpPolicy *clone = initTcpPolicyInstance( ); clone->policy = master->policy; clone->reassembly_policy = master->reassembly_policy; clone->flags = master->flags; clone->flush_factor = master->flush_factor; clone->session_timeout = master->session_timeout; clone->max_window = master->max_window; clone->overlap_limit = master->overlap_limit; clone->hs_timeout = master->hs_timeout; clone->max_queued_bytes = master->max_queued_bytes; clone->max_queued_segs = master->max_queued_segs; clone->max_consec_small_segs = master->max_consec_small_segs; clone->max_consec_small_seg_size = master->max_consec_small_seg_size; memcpy(clone->small_seg_ignore, master->small_seg_ignore, sizeof(master->small_seg_ignore)); addStreamTcpPolicyToList( config->tcp_config, clone ); return clone; } void StreamTcpPolicyInit(struct _SnortConfig *sc, StreamTcpConfig *config, char *args) { StreamTcpPolicy *s5TcpPolicy; if (config == NULL) return; // create list for caching port reassembly requests... StreamCreateReassemblyPortList( ); s5TcpPolicy = initTcpPolicyInstance( ); StreamParseTcpArgs(sc, config, args, s5TcpPolicy); addStreamTcpPolicyToList( config, s5TcpPolicy ); if ( ScPafEnabledNewConf(sc) && !config->paf_config ) config->paf_config = s5_paf_new(); // register callback with Session that determines direction and client/server ports registerDirectionPortCallback( SESSION_PROTO_TCP, setTcpDirectionAndPorts ); StreamPrintTcpConfig(s5TcpPolicy); #ifdef REG_TEST LogMessage(" TCP Session Size: %lu\n", (long unsigned int)sizeof(TcpSession)); #endif } static inline uint16_t StreamPolicyIdFromName(char *name) { if (!name) { return STREAM_POLICY_DEFAULT; } if(!strcasecmp(name, "bsd")) { return STREAM_POLICY_BSD; } else if(!strcasecmp(name, "old-linux")) { return STREAM_POLICY_OLD_LINUX; } else if(!strcasecmp(name, "linux")) { return STREAM_POLICY_LINUX; } else if(!strcasecmp(name, "first")) { return STREAM_POLICY_FIRST; } else if(!strcasecmp(name, "noack")) { return STREAM_POLICY_NOACK; } else if(!strcasecmp(name, "last")) { return STREAM_POLICY_LAST; } else if(!strcasecmp(name, "windows")) { return STREAM_POLICY_WINDOWS; } else if(!strcasecmp(name, "solaris")) { return STREAM_POLICY_SOLARIS; } else if(!strcasecmp(name, "win2003") || !strcasecmp(name, "win2k3")) { return STREAM_POLICY_WINDOWS2K3; } else if(!strcasecmp(name, "vista")) { return STREAM_POLICY_VISTA; } else if(!strcasecmp(name, "hpux") || !strcasecmp(name, "hpux11")) { return STREAM_POLICY_HPUX11; } else if(!strcasecmp(name, "hpux10")) { return STREAM_POLICY_HPUX10; } else if(!strcasecmp(name, "irix")) { return STREAM_POLICY_IRIX; } else if(!strcasecmp(name, "macos") || !strcasecmp(name, "grannysmith")) { return STREAM_POLICY_MACOS; } return STREAM_POLICY_DEFAULT; /* BSD is the default */ } static inline uint16_t GetTcpReassemblyPolicy(int os_policy) { switch (os_policy) { case STREAM_POLICY_FIRST: return REASSEMBLY_POLICY_FIRST; case STREAM_POLICY_LINUX: return REASSEMBLY_POLICY_LINUX; case STREAM_POLICY_BSD: return REASSEMBLY_POLICY_BSD; case STREAM_POLICY_OLD_LINUX: return REASSEMBLY_POLICY_OLD_LINUX; case STREAM_POLICY_LAST: return REASSEMBLY_POLICY_LAST; case STREAM_POLICY_WINDOWS: return REASSEMBLY_POLICY_WINDOWS; case STREAM_POLICY_SOLARIS: return REASSEMBLY_POLICY_SOLARIS; case STREAM_POLICY_WINDOWS2K3: return REASSEMBLY_POLICY_WINDOWS2K3; case STREAM_POLICY_VISTA: return REASSEMBLY_POLICY_VISTA; case STREAM_POLICY_HPUX11: return REASSEMBLY_POLICY_HPUX11; case STREAM_POLICY_HPUX10: return REASSEMBLY_POLICY_HPUX10; case STREAM_POLICY_IRIX: return REASSEMBLY_POLICY_IRIX; case STREAM_POLICY_MACOS: return REASSEMBLY_POLICY_MACOS; case STREAM_POLICY_NOACK: return REASSEMBLY_POLICY_NOACK; default: return REASSEMBLY_POLICY_DEFAULT; } } #define STATIC_FP ((s5TcpPolicy->flags & STREAM_CONFIG_STATIC_FLUSHPOINTS)?1:0) static void StreamParseTcpArgs(struct _SnortConfig *sc, StreamTcpConfig *config, char *args, StreamTcpPolicy *s5TcpPolicy) { char **toks; int num_toks; int i; char **stoks = NULL; int s_toks; char *endPtr = NULL; char set_flush_policy = 0; #ifdef TARGET_BASED char set_target_flush_policy = 0; #endif int reassembly_direction = SSN_DIR_FROM_CLIENT; int32_t long_val = 0; s5TcpPolicy->policy = STREAM_POLICY_DEFAULT; s5TcpPolicy->reassembly_policy = REASSEMBLY_POLICY_DEFAULT; s5TcpPolicy->session_timeout = STREAM_DEFAULT_SSN_TIMEOUT; s5TcpPolicy->max_window = 0; s5TcpPolicy->flags = 0; s5TcpPolicy->max_queued_bytes = STREAM_DEFAULT_MAX_QUEUED_BYTES; s5TcpPolicy->max_queued_segs = STREAM_DEFAULT_MAX_QUEUED_SEGS; s5TcpPolicy->max_consec_small_segs = STREAM_DEFAULT_CONSEC_SMALL_SEGS; s5TcpPolicy->max_consec_small_seg_size = STREAM_DEFAULT_MAX_SMALL_SEG_SIZE; s5TcpPolicy->log_asymmetric_traffic = false; if(args != NULL && strlen(args) != 0) { toks = mSplit(args, ",", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { if(!strcasecmp(toks[i], "use_static_footprint_sizes")) s5TcpPolicy->flags |= STREAM_CONFIG_STATIC_FLUSHPOINTS; } for (i = 0; i < num_toks; i++) { int max_s_toks = 1; // set to 0 to disable check stoks = mSplit(toks[i], " ", 3, &s_toks, 0); if (s_toks == 0) { FatalError("%s(%d) => Missing parameter in Stream TCP config.\n", file_name, file_line); } if(!strcasecmp(stoks[0], "timeout")) { if(stoks[1]) { s5TcpPolicy->session_timeout = strtoul(stoks[1], &endPtr, 10); } if (!stoks[1] || (endPtr == &stoks[1][0]) || *endPtr) { FatalError("%s(%d) => Invalid timeout in config file. " "Integer parameter required.\n", file_name, file_line); } if ((s5TcpPolicy->session_timeout > STREAM_MAX_SSN_TIMEOUT) || (s5TcpPolicy->session_timeout < STREAM_MIN_SSN_TIMEOUT)) { FatalError("%s(%d) => Invalid timeout in config file. " "Must be between %d and %d\n", file_name, file_line, STREAM_MIN_SSN_TIMEOUT, STREAM_MAX_SSN_TIMEOUT); } max_s_toks = 2; } else if(!strcasecmp(stoks[0], "log_asymmetric_traffic")) { if(stoks[1]) { if(!strcasecmp(stoks[1], "no")) s5TcpPolicy->log_asymmetric_traffic = false; else if(!strcasecmp(stoks[1], "yes")) s5TcpPolicy->log_asymmetric_traffic = true; else FatalError("%s(%d) => invalid: value must be 'yes' or 'no'\n", file_name, file_line); } else { FatalError("%s(%d) => 'log_asymmetric_traffic' missing option\n", file_name, file_line); } max_s_toks = 2; } else if(!strcasecmp(stoks[0], "overlap_limit")) { if(stoks[1]) { long_val = SnortStrtol(stoks[1], &endPtr, 10); if (errno == ERANGE) { errno = 0; long_val = -1; } s5TcpPolicy->overlap_limit = (uint8_t)long_val; } if (!stoks[1] || (endPtr == &stoks[1][0])) { FatalError("%s(%d) => Invalid overlap limit in config file." "Integer parameter required\n", file_name, file_line); } if ((long_val > STREAM_MAX_OVERLAP_LIMIT) || (long_val < STREAM_MIN_OVERLAP_LIMIT)) { FatalError("%s(%d) => Invalid overlap limit in config file." " Must be between %d and %d\n", file_name, file_line, STREAM_MIN_OVERLAP_LIMIT, STREAM_MAX_OVERLAP_LIMIT); } max_s_toks = 2; } else if(!strcasecmp(stoks[0], "detect_anomalies")) { s5TcpPolicy->flags |= STREAM_CONFIG_ENABLE_ALERTS; } else if(!strcasecmp(stoks[0], "policy")) { if(s_toks != 2) ParseError("Invalid Stream TCP policy option"); s5TcpPolicy->policy = StreamPolicyIdFromName(stoks[1]); if ((s5TcpPolicy->policy == STREAM_POLICY_DEFAULT) && (strcasecmp(stoks[1], "bsd"))) { /* Default is BSD. If we don't have "bsd", its * the default and invalid. */ FatalError("%s(%d) => Bad policy name \"%s\"\n", file_name, file_line, stoks[1]); } s5TcpPolicy->reassembly_policy = GetTcpReassemblyPolicy(s5TcpPolicy->policy); max_s_toks = 2; } else if(!strcasecmp(stoks[0], "require_3whs")) { s5TcpPolicy->flags |= STREAM_CONFIG_REQUIRE_3WHS; if (s_toks > 1) { s5TcpPolicy->hs_timeout = SnortStrtoul(stoks[1], &endPtr, 10); if ((endPtr == &stoks[1][0]) || (*endPtr != '\0') || (errno == ERANGE)) { FatalError("%s(%d) => Invalid 3Way Handshake allowable. Integer parameter required.\n", file_name, file_line); } if (s5TcpPolicy->hs_timeout > STREAM_MAX_SSN_TIMEOUT) { FatalError("%s(%d) => Invalid handshake timeout in " "config file. Must be between %d and %d\n", file_name, file_line, STREAM_MIN_ALT_HS_TIMEOUT, STREAM_MAX_SSN_TIMEOUT); } } max_s_toks = 2; } else if(!strcasecmp(stoks[0], "bind_to")) { if (s_toks < 2) { FatalError("%s(%d) => Invalid Stream TCP Policy option - " "\"bind_to\" option requires an argument.\n", file_name, file_line); } if(strstr(stoks[1], "[")) { FatalError("%s(%d) => Invalid Stream TCP Policy option. IP lists are not allowed.\n", file_name, file_line); } s5TcpPolicy->bound_addrs = IpAddrSetParse(sc, stoks[1]); max_s_toks = 2; } else if(!strcasecmp(stoks[0], "max_window")) { if(stoks[1]) { long_val = SnortStrtol(stoks[1], &endPtr, 10); if (errno == ERANGE) { errno = 0; FatalError("%s(%d) => Invalid Max Window size. Integer parameter required.\n", file_name, file_line); } s5TcpPolicy->max_window = (uint32_t)long_val; } if (!stoks[1] || (endPtr == &stoks[1][0])) { FatalError("%s(%d) => Invalid Max Window size. Integer parameter required.\n", file_name, file_line); } if ((long_val > STREAM_MAX_MAX_WINDOW) || (long_val < STREAM_MIN_MAX_WINDOW)) { FatalError("%s(%d) => Invalid Max Window size." " Must be between %d and %d\n", file_name, file_line, STREAM_MIN_MAX_WINDOW, STREAM_MAX_MAX_WINDOW); } max_s_toks = 2; } else if(!strcasecmp(stoks[0], "use_static_footprint_sizes")) { // we already handled this one above } else if(!strcasecmp(stoks[0], "flush_factor")) { if (stoks[1]) { s5TcpPolicy->flush_factor = (uint16_t)SnortStrtoulRange( stoks[1], &endPtr, 10, 0, STREAM_MAX_FLUSH_FACTOR); } if ( (!stoks[1] || (endPtr == &stoks[1][0])) || (s5TcpPolicy->flush_factor > STREAM_MAX_FLUSH_FACTOR)) { FatalError("%s(%d) => 'flush_factor %d' invalid: " "value must be between 0 and %d segments.\n", file_name, file_line, s5TcpPolicy->flush_factor, STREAM_MAX_FLUSH_FACTOR); } max_s_toks = 2; } else if(!strcasecmp(stoks[0], "dont_store_large_packets")) { s5TcpPolicy->flags |= STREAM_CONFIG_PERFORMANCE; } else if(!strcasecmp(stoks[0], "check_session_hijacking")) { s5TcpPolicy->flags |= STREAM_CONFIG_CHECK_SESSION_HIJACKING; } else if(!strcasecmp(stoks[0], "ignore_any_rules")) { s5TcpPolicy->flags |= STREAM_CONFIG_IGNORE_ANY; } else if(!strcasecmp(stoks[0], "dont_reassemble_async")) { s5TcpPolicy->flags |= STREAM_CONFIG_NO_ASYNC_REASSEMBLY; } else if(!strcasecmp(stoks[0], "max_queued_bytes")) { if(stoks[1]) { long_val = SnortStrtol(stoks[1], &endPtr, 10); if (errno == ERANGE) { errno = 0; FatalError("%s(%d) => Invalid Max Queued Bytes. Integer parameter required.\n", file_name, file_line); } s5TcpPolicy->max_queued_bytes = (uint32_t)long_val; } if (!stoks[1] || (endPtr == &stoks[1][0])) { FatalError("%s(%d) => Invalid Max Queued Bytes. Integer parameter required.\n", file_name, file_line); } if (((long_val > STREAM_MAX_MAX_QUEUED_BYTES) || (long_val < STREAM_MIN_MAX_QUEUED_BYTES)) && (long_val != 0)) { FatalError("%s(%d) => Invalid Max Queued Bytes." " Must be 0 (disabled) or between %d and %d\n", file_name, file_line, STREAM_MIN_MAX_QUEUED_BYTES, STREAM_MAX_MAX_QUEUED_BYTES); } max_s_toks = 2; } else if(!strcasecmp(stoks[0], "max_queued_segs")) { if(stoks[1]) { long_val = SnortStrtol(stoks[1], &endPtr, 10); if (errno == ERANGE) { errno = 0; FatalError("%s(%d) => Invalid Max Queued Bytes. Integer parameter required.\n", file_name, file_line); } s5TcpPolicy->max_queued_segs = (uint32_t)long_val; } if (!stoks[1] || (endPtr == &stoks[1][0])) { FatalError("%s(%d) => Invalid Max Queued Bytes. Integer parameter required.\n", file_name, file_line); } if (((long_val > STREAM_MAX_MAX_QUEUED_SEGS) || (long_val < STREAM_MIN_MAX_QUEUED_SEGS)) && (long_val != 0)) { FatalError("%s(%d) => Invalid Max Queued Bytes." " Must be 0 (disabled) or between %d and %d\n", file_name, file_line, STREAM_MIN_MAX_QUEUED_SEGS, STREAM_MAX_MAX_QUEUED_SEGS); } max_s_toks = 2; } else if (!strcasecmp(stoks[0], "small_segments")) { char **ptoks; int num_ptoks; /* Small segments takes at least 3 parameters... */ if (s_toks < 3) { FatalError("%s(%d) => Insufficient parameters to small " "segments configuration. Syntax is: " " bytes ignore_ports p1 p2, " "with ignore_ports being an optional parameter\n", file_name, file_line); } /* first the number of consecutive segments */ long_val = SnortStrtol(stoks[1], &endPtr, 10); if (errno == ERANGE) { errno = 0; FatalError("%s(%d) => Invalid Small Segment number. Integer parameter required.\n", file_name, file_line); } s5TcpPolicy->max_consec_small_segs = (uint32_t)long_val; if ((long_val > STREAM_MAX_CONSEC_SMALL_SEGS) || (long_val < STREAM_MIN_CONSEC_SMALL_SEGS)) { FatalError("%s(%d) => Invalid Small Segments." " Must be integer between %d and %d, inclusive\n", file_name, file_line, STREAM_MIN_CONSEC_SMALL_SEGS, STREAM_MAX_CONSEC_SMALL_SEGS); } ptoks = mSplit(stoks[2], " ", MAX_PORTS + 3, &num_ptoks, 0); /* the bytes keyword */ if (strcasecmp(ptoks[0], "bytes") || (num_ptoks < 2)) { FatalError("%s(%d) => Insufficient parameters to small " "segments configuration. Syntax is: " " bytes ignore_ports p1 p2, " "with ignore_ports being an optional parameter\n", file_name, file_line); } /* the minimum bytes for a segment to be considered "small" */ long_val = SnortStrtol(ptoks[1], &endPtr, 10); if (errno == ERANGE) { errno = 0; FatalError("%s(%d) => Invalid Small Segment bytes. Integer parameter required.\n", file_name, file_line); } s5TcpPolicy->max_consec_small_seg_size = (uint32_t)long_val; if ((long_val > STREAM_MAX_MAX_SMALL_SEG_SIZE) || (long_val < STREAM_MIN_MAX_SMALL_SEG_SIZE)) { FatalError("%s(%d) => Invalid Small Segments bytes." " Must be integer between %d and %d, inclusive\n", file_name, file_line, STREAM_MIN_MAX_SMALL_SEG_SIZE, STREAM_MAX_MAX_SMALL_SEG_SIZE); } /* and the optional ignore_ports */ if (num_ptoks > 2) { int j; unsigned short port = 0; long long_port = 0; if (strcasecmp(ptoks[2], "ignore_ports") || (num_ptoks < 4)) { FatalError("%s(%d) => Insufficient parameters to small " "segments configuration. Syntax is: " " bytes ignore_ports p1 p2, " "with ignore_ports being an optional parameter\n", file_name, file_line); } for (j=3; j Invalid Port for small segments ignore_ports parameter. Integer parameter required.\n", file_name, file_line); } if ((long_port < 0) || (long_port > MAX_PORTS-1)) { FatalError( "%s(%d) => Invalid port %ld for small segments ignore_ports " "parameter, must be between 0 and %d, inclusive\n", file_name, file_line, long_port, MAX_PORTS-1); } port = (unsigned short)long_port; s5TcpPolicy->small_seg_ignore[port/8] |= (1 << (port %8)); } } max_s_toks = 0; // we already checked all tokens mSplitFree(&ptoks, num_ptoks); } else if (!strcasecmp(stoks[0], "ports")) { if (s_toks > 1) { if(!strcasecmp(stoks[1], "client")) { reassembly_direction = SSN_DIR_FROM_CLIENT; } else if(!strcasecmp(stoks[1], "server")) { reassembly_direction = SSN_DIR_FROM_SERVER; } else if(!strcasecmp(stoks[1], "both")) { reassembly_direction = SSN_DIR_BOTH; } else { FatalError( "%s(%d) => Invalid direction for reassembly " "configuration \"%s\". Valid values are 'client', " "'server', or 'both'.\n", file_name, file_line, stoks[1]); } } if (s_toks > 2) { char **ptoks; int num_ptoks; int j; unsigned short port = 0; long long_port = 0; /* Initialize it if not already... */ InitFlushPointList(&s5TcpPolicy->flush_point_list, 192, 128, STATIC_FP); if (!strcasecmp(stoks[2], "all")) { for (j=0; jflush_config[j].client; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config[j].server; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } } } else if (!strcasecmp(stoks[2], "none")) { for (j=0; jflush_config[j].client; flush_mgr->flush_policy = STREAM_FLPOLICY_IGNORE; } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config[j].server; flush_mgr->flush_policy = STREAM_FLPOLICY_IGNORE; } } } else { ptoks = mSplit(stoks[2], " ", MAX_PORTS, &num_ptoks, 0); for (j=0; j < num_ptoks; j++) { FlushPolicy flush_policy = STREAM_FLPOLICY_FOOTPRINT; if (ptoks[j]) { // check for '!' not port syntax used to disable reassembly on a port // even if requested later by a preproc... if ( ptoks[j][0] == '!' ) { flush_policy = STREAM_FLPOLICY_DISABLED; long_port = strtol(&ptoks[j][1], &endPtr, 10); } else { long_port = strtol(ptoks[j], &endPtr, 10); } } if (!ptoks[j] || (endPtr == &ptoks[j][0])) { FatalError("%s(%d) => Invalid Port list. Integer parameter required.\n", file_name, file_line); } if ((long_port < 0) || (long_port > MAX_PORTS-1)) { FatalError( "%s(%d) => Invalid port %ld, must be between 0 and %d, " "inclusive\n", file_name, file_line, long_port, MAX_PORTS-1); } port = (unsigned short)long_port; if (reassembly_direction & SSN_DIR_FROM_CLIENT) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config[port].client; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, flush_policy, 0); } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config[port].server; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, flush_policy, 0); } } mSplitFree(&ptoks, num_ptoks); } set_flush_policy = 1; } max_s_toks = 0; // we already checked all tokens } #ifdef TARGET_BASED else if (!strcasecmp(stoks[0], "protocol")) { if (s_toks > 1) { if(!strcasecmp(stoks[1], "client")) { reassembly_direction = SSN_DIR_FROM_CLIENT; } else if(!strcasecmp(stoks[1], "server")) { reassembly_direction = SSN_DIR_FROM_SERVER; } else if(!strcasecmp(stoks[1], "both")) { reassembly_direction = SSN_DIR_BOTH; } else { FatalError( "%s(%d) => Invalid direction for reassembly " "configuration \"%s\". Valid values are 'client', " "'server', or 'both'.\n", file_name, file_line, stoks[1]); } } if (s_toks > 2) { char **ptoks; int num_ptoks; int j; /* Initialize it if not already... */ InitFlushPointList(&s5TcpPolicy->flush_point_list, 192, 128, STATIC_FP); if (!strcasecmp(stoks[2], "all")) { for (j=1; jflush_config_protocol[j].client; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config_protocol[j].server; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } s5TcpPolicy->flush_config_protocol[j].configured = 1; } } else if (!strcasecmp(stoks[2], "none")) { for (j=1; jflush_config_protocol[j].client; flush_mgr->flush_policy = STREAM_FLPOLICY_IGNORE; } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config_protocol[j].server; flush_mgr->flush_policy = STREAM_FLPOLICY_IGNORE; } s5TcpPolicy->flush_config_protocol[j].configured = 1; } } else { ptoks = mSplit(stoks[2], " ", MAX_PROTOCOL_ORDINAL, &num_ptoks, 0); for (j=0;j Invalid Protocol Name. Protocol name must be specified.\n", file_name, file_line); } /* First look it up */ proto_ordinal = FindProtocolReference(ptoks[j]); if (proto_ordinal == SFTARGET_UNKNOWN_PROTOCOL) { /* Not known -- add it */ proto_ordinal = AddProtocolReference(ptoks[j]); if (proto_ordinal == SFTARGET_UNKNOWN_PROTOCOL) { FatalError("%s(%d) => Failed to find protocol reference for '%s'\n", file_name, file_line, ptoks[j]); } } if (reassembly_direction & SSN_DIR_FROM_CLIENT) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config_protocol[proto_ordinal].client; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config_protocol[proto_ordinal].server; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } s5TcpPolicy->flush_config_protocol[proto_ordinal].configured = 1; } mSplitFree(&ptoks, num_ptoks); } set_target_flush_policy = 1; } max_s_toks = 0; // we already checked all tokens } #endif else { FatalError("%s(%d) => Invalid Stream TCP policy option\n", file_name, file_line); } if ( max_s_toks && (s_toks > max_s_toks) ) { FatalError("%s(%d) => Invalid Stream TCP Policy option. Missing comma?\n", file_name, file_line); } mSplitFree(&stoks, s_toks); } mSplitFree(&toks, num_toks); } if (s5TcpPolicy->bound_addrs == NULL) { if (config->default_policy != NULL) { FatalError("%s(%d) => Default Stream TCP Policy already set. " "This policy must be bound to a specific host or " "network.\n", file_name, file_line); } config->default_policy = s5TcpPolicy; } else { if (s5TcpPolicy->flags & STREAM_CONFIG_IGNORE_ANY) { FatalError("%s(%d) => \"ignore_any_rules\" option can be used only" " with Default Stream TCP Policy\n", file_name, file_line); } } if (!set_flush_policy) { /* Initialize it if not already... */ InitFlushPointList(&s5TcpPolicy->flush_point_list, 192, 128, STATIC_FP); for (i=0; iflush_config[default_ports[i]].client; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config[default_ports[i]].server; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } } } #ifdef TARGET_BASED if (!set_target_flush_policy) { int app_id; /* Initialize it if not already... */ InitFlushPointList(&s5TcpPolicy->flush_point_list, 192, 128, STATIC_FP); for (i=0; i<(int)(sizeof(default_protocols)/sizeof(char *)); i++) { /* Look up the protocol by name. Add it if it doesn't exist. */ app_id = FindProtocolReference(default_protocols[i]); if (app_id == SFTARGET_UNKNOWN_PROTOCOL) { app_id = AddProtocolReference(default_protocols[i]); } /* While this should never happen, I don't feel guilty adding this * logic as it executes at parse time. */ if (app_id == SFTARGET_UNKNOWN_PROTOCOL) continue; /* Set flush managers. */ if (reassembly_direction & SSN_DIR_FROM_CLIENT) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config_protocol[app_id].client; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } if (reassembly_direction & SSN_DIR_FROM_SERVER) { FlushMgr *flush_mgr = &s5TcpPolicy->flush_config_protocol[app_id].server; FlushPointList *flush_point_list = &s5TcpPolicy->flush_point_list; InitFlushMgr(sc, flush_mgr, flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); } s5TcpPolicy->flush_config_protocol[app_id].configured = 1; } } #endif } static void StreamPrintTcpConfig(StreamTcpPolicy *s5TcpPolicy) { int i=0, j=0; LogMessage("Stream TCP Policy config:\n"); if (s5TcpPolicy->bound_addrs != NULL) { IpAddrSetPrint(" Bound Addresses: ", s5TcpPolicy->bound_addrs); } else { LogMessage(" Bound Address: default\n"); } LogMessage(" Reassembly Policy: %s\n", reassembly_policy_names[s5TcpPolicy->reassembly_policy]); LogMessage(" Timeout: %d seconds\n", s5TcpPolicy->session_timeout); if (s5TcpPolicy->max_window != 0) LogMessage(" Max TCP Window: %u\n", s5TcpPolicy->max_window); if (s5TcpPolicy->overlap_limit) LogMessage(" Limit on TCP Overlaps: %d\n", s5TcpPolicy->overlap_limit); if (s5TcpPolicy->max_queued_bytes != 0) { LogMessage(" Maximum number of bytes to queue per session: %d\n", s5TcpPolicy->max_queued_bytes); } if (s5TcpPolicy->max_queued_segs != 0) { LogMessage(" Maximum number of segs to queue per session: %d\n", s5TcpPolicy->max_queued_segs); } if (s5TcpPolicy->flags) { LogMessage(" Options:\n"); if (s5TcpPolicy->flags & STREAM_CONFIG_REQUIRE_3WHS) { LogMessage(" Require 3-Way Handshake: YES\n"); if (s5TcpPolicy->hs_timeout != 0) { LogMessage(" 3-Way Handshake Timeout: %d\n", s5TcpPolicy->hs_timeout); } } if (s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS) { LogMessage(" Detect Anomalies: YES\n"); } if (s5TcpPolicy->flags & STREAM_CONFIG_STATIC_FLUSHPOINTS) { LogMessage(" Static Flushpoint Sizes: YES\n"); } if (s5TcpPolicy->flags & STREAM_CONFIG_PERFORMANCE) { LogMessage(" Don't Queue Large Packets for Reassembly: YES\n"); } if (s5TcpPolicy->flags & STREAM_CONFIG_CHECK_SESSION_HIJACKING) { LogMessage(" Check for TCP Session Hijacking: YES\n"); } if (s5TcpPolicy->flags & STREAM_CONFIG_IGNORE_ANY) { LogMessage(" Ignore Any -> Any Rules: YES\n"); } if (s5TcpPolicy->flags & STREAM_CONFIG_NO_ASYNC_REASSEMBLY) { LogMessage(" Don't queue packets on one-sided sessions: YES\n"); } } LogMessage(" Reassembly Ports:\n"); for (i=0; iflush_config[i].client.flush_policy; int server_flushpolicy = s5TcpPolicy->flush_config[i].server.flush_policy; char client_policy_str[STD_BUF]; char server_policy_str[STD_BUF]; client_policy_str[0] = server_policy_str[0] = '\0'; if (client_flushpolicy != STREAM_FLPOLICY_IGNORE) { direction |= SSN_DIR_FROM_CLIENT; if (client_flushpolicy < STREAM_FLPOLICY_MAX) SnortSnprintf(client_policy_str, STD_BUF, "client (%s)", flush_policy_names[client_flushpolicy]); } if (server_flushpolicy != STREAM_FLPOLICY_IGNORE) { direction |= SSN_DIR_FROM_SERVER; if (server_flushpolicy < STREAM_FLPOLICY_MAX) SnortSnprintf(server_policy_str, STD_BUF, "server (%s)", flush_policy_names[server_flushpolicy]); } if (direction) { if (j MAX_PORTS_TO_PRINT) { LogMessage(" additional ports configured but not printed.\n"); } } #ifdef TARGET_BASED int StreamPolicyIdFromHostAttributeEntry(HostAttributeEntry *host_entry) { if (!host_entry) return 0; host_entry->hostInfo.streamPolicy = StreamPolicyIdFromName(host_entry->hostInfo.streamPolicyName); host_entry->hostInfo.streamPolicySet = 1; STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "STREAM INIT: %s(%d) for Entry %s\n", reassembly_policy_names[host_entry->hostInfo.streamPolicy], host_entry->hostInfo.streamPolicy, host_entry->hostInfo.streamPolicyName);); return 0; } #endif void StreamPostConfigTcp (struct _SnortConfig *sc, void* pv) { // enable all ports registered by preprocessors for reassembly enableRegisteredPortsForReassembly( sc ); } void s5TcpPrintPortFilter(); /** * StreamVerifyTcpConfig is is called after all preprocs (static & dynamic) * are inited. */ int StreamVerifyTcpConfig(struct _SnortConfig *sc, StreamTcpConfig *config, tSfPolicyId policy_id) { if (config == NULL) return -1; if (!tcp_lws_cache) { LogMessage("WARNING: Stream TCP Session Cache not initialized.\n"); return -1; } if (config->num_policies == 0) { LogMessage("WARNING: Stream TCP no policies specified in configuration.\n"); return -1; } if (config->default_policy == NULL) { LogMessage("WARNING: Stream TCP default policy not specified in configuration.\n"); return -1; } /* Do this now * verify config is called after all preprocs (static & dynamic) * are inited. Gives us the correct number of bits for * p->preprocessor_bits */ if (!s5_pkt) StreamInitPacket(); #ifdef TARGET_BASED SFAT_SetPolicyIds(StreamPolicyIdFromHostAttributeEntry, policy_id); #endif /* Post-process TCP rules to establish TCP ports to inspect. */ setPortFilterList(sc, config->port_filter, IPPROTO_TCP, (config->default_policy->flags & STREAM_CONFIG_IGNORE_ANY), policy_id); //printf ("TCP Ports with Inspection/Monitoring\n"); //s5PrintPortFilter(config->tcpPortFilter); return 0; } uint32_t StreamGetTcpPrunes(void) { if( tcp_lws_cache ) return session_api->get_session_prune_count( SESSION_PROTO_TCP ); else return s5stats.tcp_prunes; } void StreamResetTcpPrunes(void) { session_api->reset_session_prune_count( SESSION_PROTO_TCP ); } void StreamResetTcp(void) { if (snort_conf == NULL) { ErrorMessage("Snort configuration is NULL.\n"); return; } /* Unset decoder flags for the purge */ targetPolicyIterate(policyDecoderFlagsSaveNClear); s5_tcp_cleanup = 1; session_api->purge_session_cache(tcp_lws_cache); s5_tcp_cleanup = 0; session_api->clean_protocol_session_pool( SESSION_PROTO_TCP ); /* Set decoder flags back to original */ targetPolicyIterate(policyDecoderFlagsRestore); ResetFlushMgrs(); } void StreamCleanTcp(void) { if (snort_conf == NULL) { ErrorMessage("Snort configuration is NULL.\n"); return; } s5stats.tcp_prunes = session_api->get_session_prune_count( SESSION_PROTO_TCP ); /* Turn off decoder alerts since we're decoding stored * packets that we already alerted on. */ targetPolicyIterate(policyDecoderFlagsSaveNClear); /* Set s5_tcp_cleanup to force a flush of all queued data */ s5_tcp_cleanup = 1; /* Clean up session cache */ session_api->delete_session_cache( SESSION_PROTO_TCP ); tcp_lws_cache = NULL; /* Cleanup the rebuilt packet */ if (s5_pkt) { Encode_Delete(s5_pkt); s5_pkt = NULL; } /* Cleanup TCP session cleanup packet */ if (tcp_cleanup_pkt) { DeleteGrinderPkt(tcp_cleanup_pkt); tcp_cleanup_pkt = NULL; } /* cleanup is over... */ s5_tcp_cleanup = 0; /* And turn decoder alerts back on (or whatever they were set to) */ targetPolicyIterate(policyDecoderFlagsRestore); } void StreamTcpConfigFree(StreamTcpConfig *config) { int i; if (config == NULL) return; /* Cleanup TCP Policies and the list */ for (i = 0; i < config->num_policies; i++) { StreamTcpPolicy *policy = config->policy_list[i]; SnortPreprocFree(policy->flush_point_list.flush_points, sizeof(uint32_t) * RAND_FLUSH_POINTS, PP_STREAM, PP_MEM_CATEGORY_CONFIG); if (policy->bound_addrs != NULL) sfvar_free(policy->bound_addrs); SnortPreprocFree(policy, sizeof( StreamTcpPolicy * ), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } if ( config->paf_config ) s5_paf_delete(config->paf_config); SnortPreprocFree(config->policy_list, sizeof(StreamTcpPolicy *) * (config->num_policies), PP_STREAM, PP_MEM_CATEGORY_CONFIG); SnortPreprocFree(config, sizeof(StreamTcpConfig), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } #ifdef STREAM_DEBUG_ENABLED static void PrintStateMgr(StateMgr *s) { LogMessage("StateMgr:\n"); LogMessage(" state: %s\n", state_names[s->state]); LogMessage(" state_queue: %s\n", state_names[s->state_queue]); LogMessage(" expected_flags: 0x%X\n", s->expected_flags); LogMessage(" transition_seq: 0x%X\n", s->transition_seq); LogMessage(" stq_get_seq: %d\n", s->stq_get_seq); } static void PrintStreamTracker(StreamTracker *s) { LogMessage(" + StreamTracker +\n"); LogMessage(" isn: 0x%X\n", s->isn); LogMessage(" ts_last: %u\n", s->ts_last); LogMessage(" wscale: %u\n", s->wscale); LogMessage(" mss: 0x%08X\n", s->mss); LogMessage(" l_unackd: %X\n", s->l_unackd); LogMessage(" l_nxt_seq: %X\n", s->l_nxt_seq); LogMessage(" l_window: %u\n", s->l_window); LogMessage(" r_nxt_ack: %X\n", s->r_nxt_ack); LogMessage(" r_win_base: %X\n", s->r_win_base); LogMessage(" seglist_base_seq: %X\n", s->seglist_base_seq); LogMessage(" seglist: %p\n", (void*)s->seglist); LogMessage(" seglist_tail: %p\n", (void*)s->seglist_tail); LogMessage(" seg_count: %d\n", s->seg_count); LogMessage(" seg_bytes_total: %d\n", s->seg_bytes_total); LogMessage(" seg_bytes_logical: %d\n", s->seg_bytes_logical); PrintStateMgr(&s->s_mgr); } static void PrintTcpSession(TcpSession *ts) { char buf[64]; LogMessage("TcpSession:\n"); #ifdef DEBUG LogMessage(" ssn_time: %lu\n", ts->ssn_time.tv_sec); #endif sfip_ntop(&ts->tcp_server_ip, buf, sizeof(buf)); LogMessage(" server IP: %s\n", buf); sfip_ntop(&ts->tcp_client_ip, buf, sizeof(buf)); LogMessage(" client IP: %s\n", buf); LogMessage(" server port: %d\n", ts->tcp_server_port); LogMessage(" client port: %d\n", ts->tcp_client_port); LogMessage(" flags: 0x%X\n", ts->scb->ha_state.session_flags); LogMessage("Client Tracker:\n"); PrintStreamTracker(&ts->client); LogMessage("Server Tracker:\n"); PrintStreamTracker(&ts->server); } static void PrintTcpDataBlock(TcpDataBlock *tdb) { LogMessage("TcpDataBlock:\n"); LogMessage(" seq: 0x%08X\n", tdb->seq); LogMessage(" ack: 0x%08X\n", tdb->ack); LogMessage(" win: %d\n", tdb->win); LogMessage(" end: 0x%08X\n", tdb->end_seq); } #ifdef STREAM_DEBUG_ENABLED static void PrintFlushMgr(FlushMgr *fm) { if(fm == NULL) return; switch(fm->flush_policy) { case STREAM_FLPOLICY_NONE: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " NONE\n");); break; case STREAM_FLPOLICY_FOOTPRINT: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " FOOTPRINT %d\n", fm->flush_pt);); break; case STREAM_FLPOLICY_LOGICAL: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " LOGICAL %d\n", fm->flush_pt);); break; case STREAM_FLPOLICY_RESPONSE: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " RESPONSE\n");); break; case STREAM_FLPOLICY_SLIDING_WINDOW: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " SLIDING_WINDOW %d\n", fm->flush_pt);); break; #if 0 case STREAM_FLPOLICY_CONSUMED: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " CONSUMED %d\n", fm->flush_pt);); break; #endif case STREAM_FLPOLICY_IGNORE: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " IGNORE\n");); break; case STREAM_FLPOLICY_PROTOCOL: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " PROTOCOL\n");); break; } } #endif // DEBUG #endif // STREAM_DEBUG_ENABLED static inline void Discard () { s5stats.tcp_discards++; } static inline void EventSynOnEst(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_SYN_ON_EST, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_SYN_ON_EST_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventExcessiveOverlap(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_EXCESSIVE_TCP_OVERLAPS, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_EXCESSIVE_TCP_OVERLAPS_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventBadTimestamp(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_BAD_TIMESTAMP, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_BAD_TIMESTAMP_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventWindowTooLarge(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_WINDOW_TOO_LARGE, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_WINDOW_TOO_LARGE_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventDataOnSyn(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_DATA_ON_SYN, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_DATA_ON_SYN_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventDataOnClosed(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_DATA_ON_CLOSED, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_DATA_ON_CLOSED_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventDataAfterReset(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_DATA_AFTER_RESET, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_DATA_AFTER_RESET_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventBadSegment(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_BAD_SEGMENT, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_BAD_SEGMENT_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventSessionHijackedClient(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_SESSION_HIJACKED_CLIENT, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_SESSION_HIJACKED_CLIENT_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventSessionHijackedServer(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_SESSION_HIJACKED_SERVER, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_SESSION_HIJACKED_SERVER_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventDataWithoutFlags(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_DATA_WITHOUT_FLAGS, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_DATA_WITHOUT_FLAGS_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventMaxSmallSegsExceeded(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_SMALL_SEGMENT, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_SMALL_SEGMENT_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void Event4whs(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_4WAY_HANDSHAKE, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_4WAY_HANDSHAKE_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventNoTimestamp(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_NO_TIMESTAMP, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_NO_TIMESTAMP_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventBadReset(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_BAD_RST, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_BAD_RST_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventBadFin(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_BAD_FIN, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_BAD_FIN_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventBadAck(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_BAD_ACK, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_BAD_ACK_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventDataAfterRstRcvd(StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_DATA_AFTER_RST_RCVD, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_DATA_AFTER_RST_RCVD_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventInternal (uint32_t eventSid) { if ( !InternalEventIsEnabled(snort_conf->rate_filter_config, eventSid) ) return; s5stats.internalEvents++; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream raised internal event %d\n", eventSid);); SnortEventqAdd( GENERATOR_INTERNAL, /* GID */ eventSid, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_INTERNAL_EVENT_STR, /* event msg*/ NULL /* rule info ptr */ ); } static inline void EventWindowSlam (StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_WINDOW_SLAM, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_WINDOW_SLAM_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventWindowZeroAfterFinAck (StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_WIN_SZ_0_TCP_FIN_WAIT_1, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_WIN_SZ_0_TCP_FIN_WAIT_1_STR, /* event msg */ NULL); /* rule info ptr */ } static inline void EventNo3whs (StreamTcpPolicy *s5TcpPolicy) { if(!(s5TcpPolicy->flags & STREAM_CONFIG_ENABLE_ALERTS)) return; s5stats.events++; SnortEventqAdd(GENERATOR_SPP_STREAM, /* GID */ STREAM_NO_3WHS, /* SID */ 1, /* rev */ 0, /* class */ 3, /* priority */ STREAM_NO_3WHS_STR, /* event msg */ NULL); /* rule info ptr */ } /* * Utility functions for TCP stuff */ #ifdef NORMALIZER typedef enum { PC_TCP_ECN_SSN, PC_TCP_TS_NOP, PC_TCP_IPS_DATA, PC_TCP_BLOCK, PC_TCP_TRIM_SYN, PC_TCP_TRIM_RST, PC_TCP_TRIM_WIN, PC_TCP_TRIM_MSS, PC_MAX } PegCounts; static uint64_t normStats[PC_MAX][NORM_MODE_MAX]; static const char* pegName[PC_MAX] = { "tcp::ecn_ssn", "tcp::ts_nop", "tcp::ips_data", "tcp::block", "tcp::trim_syn", "tcp::trim_rst", "tcp::trim_win", "tcp::trim_mss", }; void Stream_PrintNormalizationStats (void) { int i; // header output by normalizer for ( i = 0; i < PC_MAX; i++ ) { // same alignment as in Norm_PrintStats() LogMessage("%23s: " STDu64 "\n", pegName[i], normStats[i][NORM_MODE_ON]); LogMessage("Would %17s: " STDu64 "\n", pegName[i], normStats[i][NORM_MODE_WOULDA]); } } void Stream_ResetNormalizationStats (void) { memset(normStats, 0, sizeof(normStats)); } //----------------------------------------------------------------------- // instead of centralizing all these normalizations so that // Normalize_GetMode() is called only once, the checks and // normalizations are localized. this should lead to many // fewer total checks. however, it is best to minimize // configuration checks on a per packet basis so there is // still room for improvement. static inline bool NormalDropPacket (Packet* p) { NormMode mode = Normalize_GetMode(snort_conf, NORM_TCP_BLOCK); if ( mode != NORM_MODE_OFF ) { normStats[PC_TCP_BLOCK][mode]++; sfBase.iPegs[PERF_COUNT_TCP_BLOCK][mode]++; if ( mode == NORM_MODE_ON ) { Active_NapDropPacket(p); if (pkt_trace_enabled) { addPktTraceData(VERDICT_REASON_STREAM, snprintf(trace_line, MAX_TRACE_LINE, "Stream: TCP normalization error in timestamp, window, seq, ack, fin, flags, or unexpected data, %s\n", getPktTraceActMsg())); } else addPktTraceData(VERDICT_REASON_STREAM, 0); return true; } } return false; } static inline bool NormalStripTimeStamp (Packet* p, int i) { uint8_t* opt; NormMode mode = Normalize_GetMode(snort_conf, NORM_TCP_OPT); if ( mode != NORM_MODE_OFF ) { if ( i < 0 ) { for ( i = 0; i < p->tcp_option_count; i++ ) if ( p->tcp_options[i].code == TCPOPT_TIMESTAMP ) break; if ( i == p->tcp_option_count ) return false; } normStats[PC_TCP_TS_NOP][mode]++; sfBase.iPegs[PERF_COUNT_TCP_TS_NOP][mode]++; if ( mode == NORM_MODE_ON ) { // first set raw option bytes to nops opt = (uint8_t*)p->tcp_options[i].data - 2; memset(opt, TCPOPT_NOP, TCPOLEN_TIMESTAMP); // then nop decoded option code only p->tcp_options[i].code = TCPOPT_NOP; p->packet_flags |= PKT_MODIFIED; return true; } } return false; } static inline void NormalTrimPayload (Packet* p, uint32_t max, TcpDataBlock* tdb) { uint16_t fat = p->dsize - max; p->dsize = max; p->packet_flags |= (PKT_MODIFIED|PKT_RESIZED); tdb->end_seq -= fat; } static inline bool NormalTrimPayloadIfSyn ( Packet *p, uint32_t max, TcpDataBlock *tdb ) { NormMode mode = Normalize_GetMode(snort_conf, NORM_TCP_TRIM_SYN); if ( mode != NORM_MODE_OFF && p->dsize > max ) { normStats[PC_TCP_TRIM_SYN][mode]++; sfBase.iPegs[PERF_COUNT_TCP_TRIM_SYN][mode]++; if( mode == NORM_MODE_ON ) { NormalTrimPayload(p, max, tdb); return true; } } return false; } static inline bool NormalTrimPayloadIfRst ( Packet *p, uint32_t max, TcpDataBlock *tdb ) { NormMode mode = Normalize_GetMode(snort_conf, NORM_TCP_TRIM_RST); if ( mode != NORM_MODE_OFF && p->dsize > max ) { normStats[PC_TCP_TRIM_RST][mode]++; sfBase.iPegs[PERF_COUNT_TCP_TRIM_RST][mode]++; if( mode == NORM_MODE_ON ) { NormalTrimPayload(p, max, tdb); return true; } } return false; } static inline bool NormalTrimPayloadIfWin ( Packet *p, uint32_t max, TcpDataBlock *tdb ) { NormMode mode = Normalize_GetMode(snort_conf, NORM_TCP_TRIM_WIN); if ( mode != NORM_MODE_OFF && p->dsize > max ) { normStats[PC_TCP_TRIM_WIN][mode]++; sfBase.iPegs[PERF_COUNT_TCP_TRIM_WIN][mode]++; if( mode == NORM_MODE_ON ) { NormalTrimPayload(p, max, tdb); return true; } } return false; } static inline bool NormalTrimPayloadIfMss ( Packet *p, uint32_t max, TcpDataBlock *tdb ) { NormMode mode = Normalize_GetMode(snort_conf, NORM_TCP_TRIM_MSS); if ( mode != NORM_MODE_OFF && p->dsize > max ) { normStats[PC_TCP_TRIM_MSS][mode]++; sfBase.iPegs[PERF_COUNT_TCP_TRIM_MSS][mode]++; if( mode == NORM_MODE_ON ) { NormalTrimPayload(p, max, tdb); return true; } } return false; } static inline void NormalTrackECN (TcpSession* s, const TCPHdr* tcph, int req3way) { if ( !s ) return; if ( TCP_ISFLAGSET(tcph, TH_SYN|TH_ACK) ) { if ( !req3way || s->ecn ) s->ecn = ((tcph->th_flags & (TH_ECE|TH_CWR)) == TH_ECE); } else if ( TCP_ISFLAGSET(tcph, TH_SYN) ) s->ecn = TCP_ISFLAGSET(tcph, (TH_ECE|TH_CWR)); } static inline void NormalCheckECN (TcpSession* s, Packet* p) { NormMode mode = Normalize_GetMode(snort_conf, NORM_TCP_ECN_STR); if ( mode != NORM_MODE_OFF ) { if ( !s->ecn && (p->tcph->th_flags & (TH_ECE|TH_CWR)) ) { normStats[PC_TCP_ECN_SSN][mode]++; sfBase.iPegs[PERF_COUNT_TCP_ECN_SSN][mode]++; if ( mode == NORM_MODE_ON ) { ((TCPHdr*)p->tcph)->th_flags &= ~(TH_ECE|TH_CWR); p->packet_flags |= PKT_MODIFIED; } } } } #else #define NormalDropPacket(p) #define NormalTrackECN(s, h, r) #endif void StreamUpdatePerfBaseState(SFBASE *sf_base, SessionControlBlock *scb, char newState) { if (!scb) { return; } switch (newState) { case TCP_STATE_SYN_SENT: if (!(scb->ha_state.session_flags & SSNFLAG_COUNTED_INITIALIZE)) { sf_base->iSessionsInitializing++; scb->ha_state.session_flags |= SSNFLAG_COUNTED_INITIALIZE; } break; case TCP_STATE_ESTABLISHED: if (!(scb->ha_state.session_flags & SSNFLAG_COUNTED_ESTABLISH)) { sf_base->iSessionsEstablished++; EventInternal(INTERNAL_EVENT_SESSION_ADD); if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP)) UpdateFlowIPState(&sfFlow, IP_ARG(scb->client_ip), IP_ARG(scb->server_ip), SFS_STATE_TCP_ESTABLISHED); scb->ha_state.session_flags |= SSNFLAG_COUNTED_ESTABLISH; if ((scb->ha_state.session_flags & SSNFLAG_COUNTED_INITIALIZE) && !(scb->ha_state.session_flags & SSNFLAG_COUNTED_CLOSING)) { assert(sf_base->iSessionsInitializing); sf_base->iSessionsInitializing--; } } break; case TCP_STATE_CLOSING: if (!(scb->ha_state.session_flags & SSNFLAG_COUNTED_CLOSING)) { sf_base->iSessionsClosing++; scb->ha_state.session_flags |= SSNFLAG_COUNTED_CLOSING; if (scb->ha_state.session_flags & SSNFLAG_COUNTED_ESTABLISH) { assert(sf_base->iSessionsEstablished); sf_base->iSessionsEstablished--; if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP)) UpdateFlowIPState(&sfFlow, IP_ARG(scb->client_ip), IP_ARG(scb->server_ip), SFS_STATE_TCP_CLOSED); } else if (scb->ha_state.session_flags & SSNFLAG_COUNTED_INITIALIZE) { assert(sf_base->iSessionsInitializing); sf_base->iSessionsInitializing--; } } break; case TCP_STATE_CLOSED: if (scb->ha_state.session_flags & SSNFLAG_COUNTED_CLOSING) { assert(sf_base->iSessionsClosing); sf_base->iSessionsClosing--; } else if (scb->ha_state.session_flags & SSNFLAG_COUNTED_ESTABLISH) { assert(sf_base->iSessionsEstablished); sf_base->iSessionsEstablished--; if (perfmon_config && (perfmon_config->perf_flags & SFPERF_FLOWIP)) UpdateFlowIPState(&sfFlow, IP_ARG(scb->client_ip), IP_ARG(scb->server_ip), SFS_STATE_TCP_CLOSED); } else if (scb->ha_state.session_flags & SSNFLAG_COUNTED_INITIALIZE) { assert(sf_base->iSessionsInitializing); sf_base->iSessionsInitializing--; } break; default: break; } sf_base->stream5_mem_in_use = session_mem_in_use; } //------------------------------------------------------------------------- // ssn ingress is client; ssn egress is server #ifdef HAVE_DAQ_ADDRESS_SPACE_ID static inline void SetPacketHeaderFoo (TcpSession* tcpssn, const Packet* p) { if ((( p->packet_flags & PKT_FROM_CLIENT ) || (p->pkth->egress_index == DAQ_PKTHDR_UNKNOWN)) && (p->pkth->egress_group == DAQ_PKTHDR_UNKNOWN )) { tcpssn->ingress_index = p->pkth->ingress_index; tcpssn->ingress_group = p->pkth->ingress_group; // ssn egress may be unknown, but will be correct tcpssn->egress_index = p->pkth->egress_index; tcpssn->egress_group = p->pkth->egress_group; } else { if ( p->pkth->egress_index != DAQ_PKTHDR_FLOOD ) { tcpssn->ingress_index = p->pkth->egress_index; tcpssn->ingress_group = p->pkth->egress_group; } tcpssn->egress_index = p->pkth->ingress_index; tcpssn->egress_group = p->pkth->ingress_group; tcpssn->ingress_index = p->pkth->egress_index; tcpssn->ingress_group = p->pkth->egress_group; } #ifdef HAVE_DAQ_FLOW_ID tcpssn->daq_flow_id = p->pkth->flow_id; #endif tcpssn->daq_flags = p->pkth->flags; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) tcpssn->address_space_id_dst = p->pkth->address_space_id_dst; tcpssn->address_space_id_src = p->pkth->address_space_id_src; #else tcpssn->address_space_id = p->pkth->address_space_id; #endif } static inline void GetPacketHeaderFoo ( const TcpSession* tcpssn, const DAQ_PktHdr_t* phdr, EncodeFlags fwd, DAQ_PktHdr_t* pkth, uint32_t dir) { if (((dir & PKT_FROM_CLIENT) || (tcpssn->egress_index == DAQ_PKTHDR_UNKNOWN)) && (tcpssn->egress_group == DAQ_PKTHDR_UNKNOWN)) { pkth->ingress_index = tcpssn->ingress_index; pkth->ingress_group = tcpssn->ingress_group; pkth->egress_index = tcpssn->egress_index; pkth->egress_group = tcpssn->egress_group; } else { pkth->ingress_index = tcpssn->egress_index; pkth->ingress_group = tcpssn->egress_group; pkth->egress_index = tcpssn->ingress_index; pkth->egress_group = tcpssn->ingress_group; } #ifdef HAVE_DAQ_FLOW_ID pkth->flow_id = tcpssn->daq_flow_id; #endif pkth->flags = tcpssn->daq_flags; pkth->priv_ptr = tcpssn->priv_ptr; #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) pkth->address_space_id_dst = tcpssn->address_space_id_dst; pkth->address_space_id_src = tcpssn->address_space_id_src; #else pkth->address_space_id = tcpssn->address_space_id; #endif #if defined(DAQ_VERSION) && DAQ_VERSION > 8 pkth->proto = phdr->proto; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) && defined(DAQ_VERSION) && DAQ_VERSION > 10 pkth->carrier_id = phdr->carrier_id; #endif #ifdef HAVE_DAQ_REAL_ADDRESSES if (phdr->flags & DAQ_PKT_FLAG_REAL_ADDRESSES) { pkth->flags &= ~(DAQ_PKT_FLAG_REAL_SIP_V6 | DAQ_PKT_FLAG_REAL_DIP_V6); if (fwd) { pkth->flags |= phdr->flags & (DAQ_PKT_FLAG_REAL_SIP_V6 | DAQ_PKT_FLAG_REAL_DIP_V6); pkth->n_real_sPort = phdr->n_real_sPort; pkth->n_real_dPort = phdr->n_real_dPort; pkth->real_sIP = phdr->real_sIP; pkth->real_dIP = phdr->real_dIP; } else { if (phdr->flags & DAQ_PKT_FLAG_REAL_SIP_V6) pkth->flags |= DAQ_PKT_FLAG_REAL_DIP_V6; if (phdr->flags & DAQ_PKT_FLAG_REAL_DIP_V6) pkth->flags |= DAQ_PKT_FLAG_REAL_SIP_V6; pkth->n_real_sPort = phdr->n_real_dPort; pkth->n_real_dPort = phdr->n_real_sPort; pkth->real_sIP = phdr->real_dIP; pkth->real_dIP = phdr->real_sIP; } } #endif } static inline void SwapPacketHeaderFoo (TcpSession* tcpssn) { if ( tcpssn->egress_index != DAQ_PKTHDR_UNKNOWN ) { int32_t ingress_index; int32_t ingress_group; ingress_index = tcpssn->ingress_index; ingress_group = tcpssn->ingress_group; tcpssn->ingress_index = tcpssn->egress_index; tcpssn->ingress_group = tcpssn->egress_group; tcpssn->egress_index = ingress_index; tcpssn->egress_group = ingress_group; } } #endif //------------------------------------------------------------------------- static inline int IsBetween(uint32_t low, uint32_t high, uint32_t cur) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "(%X, %X, %X) = (low, high, cur)\n", low,high,cur);); /* If we haven't seen anything, ie, low & high are 0, return true */ if ((low == 0) && (low == high)) return 1; return (SEQ_GEQ(cur, low) && SEQ_LEQ(cur, high)); } static inline bool TwoWayTraffic (SessionControlBlock *scb) { return ( (scb->ha_state.session_flags & SSNFLAG_SEEN_BOTH) == SSNFLAG_SEEN_BOTH ); } static inline uint32_t StreamGetWindow( SessionControlBlock *scb, StreamTracker* st, TcpDataBlock* tdb) { int32_t window; if ( st->l_window ) { // don't use the window if we may have missed scaling if ( !(scb->session_state & STREAM_STATE_MIDSTREAM) ) return st->l_window; } // one way zero window is unitialized // two way zero window is actually closed (regardless of scaling) else if ( TwoWayTraffic(scb) ) return st->l_window; // ensure the data is in the window window = tdb->end_seq - st->r_win_base; if(window < 0) window = 0; return (uint32_t) window; } // ack number must ack syn static inline int ValidRstSynSent(StreamTracker *st, TcpDataBlock *tdb) { return tdb->ack == st->l_unackd; } // per rfc 793 a rst is valid if the seq number is in window // for all states but syn-sent (handled above). however, we // validate here based on how various implementations actually // handle a rst. static inline int ValidRst( SessionControlBlock *scb, StreamTracker *st, TcpDataBlock *tdb) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Checking end_seq (%X) > r_win_base (%X) && " "seq (%X) < r_nxt_ack(%X)\n", tdb->end_seq, st->r_win_base, tdb->seq, st->r_nxt_ack+StreamGetWindow(scb, st, tdb));); switch (st->os_policy) { case STREAM_POLICY_HPUX11: if (SEQ_GEQ(tdb->seq, st->r_nxt_ack)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (>= next seq)!\n");); return 1; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (>= next seq)!\n");); return 0; break; case STREAM_POLICY_FIRST: case STREAM_POLICY_NOACK: case STREAM_POLICY_LAST: case STREAM_POLICY_MACOS: case STREAM_POLICY_WINDOWS: case STREAM_POLICY_VISTA: case STREAM_POLICY_WINDOWS2K3: case STREAM_POLICY_HPUX10: case STREAM_POLICY_IRIX: if (SEQ_EQ(tdb->seq, st->r_nxt_ack)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (next seq)!\n");); return 1; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (next seq)!\n");); return 0; break; case STREAM_POLICY_BSD: case STREAM_POLICY_LINUX: case STREAM_POLICY_OLD_LINUX: case STREAM_POLICY_SOLARIS: if(SEQ_GEQ(tdb->end_seq, st->r_win_base)) { // reset must be admitted when window closed if ( SEQ_LEQ(tdb->seq, st->r_win_base+StreamGetWindow(scb, st, tdb)) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "rst is valid seq (within window)!\n");); return 1; } } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "rst is not valid seq (within window)!\n");); return 0; break; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "rst is not valid!\n");); return 0; } static inline int ValidTimestamp(StreamTracker *talker, StreamTracker *listener, TcpDataBlock *tdb, Packet *p, int *eventcode, int *got_ts) { if((p->tcph->th_flags & TH_RST) || (listener->tcp_policy->policy == STREAM_POLICY_NOACK)) return ACTION_NOTHING; #ifdef NORMALIZER #if 0 if ( p->tcph->th_flags & TH_ACK && Normalize_GetMode(snort_conf, NORM_TCP_OPT) != NORM_MODE_OFF) { // FIXTHIS validate tsecr here (check that it was previously sent) // checking for the most recent ts is easy enough must check if // ts are up to date in retransmitted packets } #endif #endif /* * check PAWS */ if((talker->flags & TF_TSTAMP) && (listener->flags & TF_TSTAMP)) { char validate_timestamp = 1; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Checking timestamps for PAWS\n");); *got_ts = StreamGetTcpTimestamp(p, &tdb->ts, 0); if (*got_ts) { if (listener->tcp_policy->policy == STREAM_POLICY_HPUX11) { /* HPUX 11 ignores timestamps for out of order segments */ if ((listener->flags & TF_PKT_MISSED) || !SEQ_EQ(listener->r_nxt_ack, tdb->seq)) { validate_timestamp = 0; } } if (talker->flags & TF_TSTAMP_ZERO) { /* Handle the case where the 3whs used a 0 timestamp. Next packet * from that endpoint should have a valid timestamp... */ if ((listener->tcp_policy->policy == STREAM_POLICY_LINUX) || (listener->tcp_policy->policy == STREAM_POLICY_WINDOWS2K3)) { /* Linux, Win2k3 et al. do not support timestamps if * the 3whs used a 0 timestamp. */ talker->flags &= ~TF_TSTAMP; listener->flags &= ~TF_TSTAMP; validate_timestamp = 0; } else if ((listener->tcp_policy->policy == STREAM_POLICY_OLD_LINUX) || (listener->tcp_policy->policy == STREAM_POLICY_WINDOWS) || (listener->tcp_policy->policy == STREAM_POLICY_VISTA)) { /* Older Linux (2.2 kernel & earlier), Win32 (non 2K3) * allow the 3whs to use a 0 timestamp. */ talker->flags &= ~TF_TSTAMP_ZERO; if(SEQ_EQ(listener->r_nxt_ack, tdb->seq)) { talker->ts_last = tdb->ts; validate_timestamp = 0; /* Ignore the timestamp for this * first packet, next one will * checked. */ } } } if (validate_timestamp) { int result = 0; if (listener->tcp_policy->policy == STREAM_POLICY_LINUX) { /* Linux 2.6 accepts timestamp values that are off * by one. */ result = (int)((tdb->ts - talker->ts_last) + 1); } else { result = (int)(tdb->ts - talker->ts_last); } #ifdef DAQ_PKT_FLAG_RETRY_PACKET if(result < 0 && (p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET)) { // Retry packets can legitimately have old timestamps // in TCP options (if a re-transmit comes in before // the retry) so don't consider it an error. STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Retry packet had old timestamp. Reseting to last timestamp seen.\n");); tdb->ts = talker->ts_last; } else #endif if( (talker->ts_last != 0) && result < 0) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Packet outside PAWS window, dropping\n");); /* bail, we've got a packet outside the PAWS window! */ //Discard(); *eventcode |= EVENT_BAD_TIMESTAMP; NormalDropPacket(p); return ACTION_BAD_PKT; } else if ((talker->ts_last != 0) && ((uint32_t)p->pkth->ts.tv_sec > talker->ts_last_pkt+PAWS_24DAYS)) { /* this packet is from way too far into the future */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "packet PAWS timestamp way too far ahead of" "last packet %d %d...\n", p->pkth->ts.tv_sec, talker->ts_last_pkt);); //Discard(); *eventcode |= EVENT_BAD_TIMESTAMP; NormalDropPacket(p); return ACTION_BAD_PKT; } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "packet PAWS ok...\n");); } } } else { /* we've got a packet with no timestamp, but 3whs indicated talker * was doing timestamps. This breaks protocol, however, some servers * still ack the packet with the missing timestamp. Log an alert, * but continue to process the packet */ *eventcode |= EVENT_NO_TIMESTAMP; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "packet no timestamp, had one earlier from this side...ok for now...\n");); if (listener->tcp_policy->policy == STREAM_POLICY_SOLARIS) { /* Solaris stops using timestamps if it receives a packet * without a timestamp and there were timestamps in use. */ listener->flags &= ~TF_TSTAMP; } NormalDropPacket(p); } } else if ( TCP_ISFLAGSET(p->tcph, TH_SYN) && !TCP_ISFLAGSET(p->tcph, TH_ACK) ) { *got_ts = StreamGetTcpTimestamp(p, &tdb->ts, 0); if ( *got_ts ) { talker->flags |= TF_TSTAMP; // In case of SYN, there is no ts_last is 0 // set it to the current value, so that computation of // ts_last is correct for subsequent packets. talker->ts_last = tdb->ts; talker->ts_last_pkt = p->pkth->ts.tv_sec; } } else { #ifdef NORMALIZER // if we are not handling timestamps, and this isn't a syn // (only), and we have seen a valid 3way setup, then we strip // (nop) the timestamp option. this includes the cases where // we disable timestamp handling. int strip = ( SetupOK(talker) && SetupOK(listener) ); #else int strip = 0; #endif STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "listener not doing timestamps...\n");); *got_ts = StreamGetTcpTimestamp(p, &tdb->ts, strip); if (*got_ts) { if (!(talker->flags & TF_TSTAMP)) { /* Since we skipped the SYN, may have missed the talker's * timestamp there, so set it now. */ talker->flags |= TF_TSTAMP; if (tdb->ts == 0) { talker->flags |= TF_TSTAMP_ZERO; } } /* Only valid to test this if listener is using timestamps. * Otherwise, timestamp in this packet is not used, regardless * of its value. */ if ((tdb->ts == 0) && (listener->flags & TF_TSTAMP)) { switch (listener->os_policy) { case STREAM_POLICY_WINDOWS: case STREAM_POLICY_VISTA: case STREAM_POLICY_WINDOWS2K3: case STREAM_POLICY_OLD_LINUX: case STREAM_POLICY_SOLARIS: /* Old Linux & Windows allows a 0 timestamp value. */ break; default: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Packet with 0 timestamp, dropping\n");); //Discard(); /* bail */ *eventcode |= EVENT_BAD_TIMESTAMP; return ACTION_BAD_PKT; } } } } return ACTION_NOTHING; } #ifdef S5_PEDANTIC // From RFC 793: // // Segment Receive Test // Length Window // ------- ------- ------------------------------------------- // // 0 0 SEG.SEQ = RCV.NXT // // 0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND // // >0 0 not acceptable // // >0 >0 RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND // or RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND // static inline int ValidSeq( const Packet* p, SessionControlBlock *scb, StreamTracker *st, TcpDataBlock *tdb, bool *before_win_base) { uint32_t win = StreamGetWindow(scb, st, tdb); if ( !p->dsize ) { if ( !win ) { return ( tdb->seq == st->r_win_base ); } return SEQ_LEQ(st->r_win_base, tdb->seq) && SEQ_LT(tdb->seq, st->r_win_base+win); } if ( !win ) return 0; if ( SEQ_LEQ(st->r_win_base, tdb->seq) && SEQ_LT(tdb->seq, st->r_win_base+win) ) return 1; return SEQ_LEQ(st->r_win_base, tdb->end_seq) && SEQ_LT(tdb->end_seq, st->r_win_base+win); } #else static inline int ValidSeq( const Packet* p, SessionControlBlock *scb, StreamTracker *st, TcpDataBlock *tdb, bool *before_win_base) { int right_ok; uint32_t left_seq; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Checking end_seq (%X) > r_win_base (%X) && " "seq (%X) < r_nxt_ack(%X)\n", tdb->end_seq, st->r_win_base, tdb->seq, st->r_nxt_ack+StreamGetWindow(scb, st, tdb));); if ( SEQ_LT(st->r_nxt_ack, st->r_win_base) ) left_seq = st->r_nxt_ack; else left_seq = st->r_win_base; if ( p->dsize ) right_ok = SEQ_GT(tdb->end_seq, left_seq); else right_ok = SEQ_GEQ(tdb->end_seq, left_seq); if ( right_ok ) { uint32_t win = StreamGetWindow(scb, st, tdb); if( SEQ_LEQ(tdb->seq, st->r_win_base+win) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seq is within window!\n");); return 1; } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seq is past the end of the window!\n");); } } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "end_seq is before win_base\n");); *before_win_base = true; } return 0; } #endif static inline void UpdateSsn(Packet* p, StreamTracker *rcv, StreamTracker *snd, TcpDataBlock *tdb) { #if 0 #ifdef NORMALIZER if ( // FIXTHIS these checks are a hack to avoid off by one normalization // due to FIN ... if last segment filled a hole, r_nxt_ack is not at // end of data, FIN is ignored so sequence isn't bumped, and this // forces seq-- on ACK of FIN. :( rcv->s_mgr.state == TCP_STATE_ESTABLISHED && rcv->s_mgr.state_queue == TCP_STATE_NONE && Normalize_GetMode(snort_conf, NORM_TCP_IPS) ) { // walk the seglist until a gap or tdb->ack whichever is first // if a gap exists prior to ack, move ack back to start of gap StreamSegment* seg = snd->seglist; // FIXTHIS must check ack oob with empty seglist // FIXTHIS add lower gap bound to tracker for efficiency? while ( seg ) { uint32_t seq = seg->seq + seg->size; if ( SEQ_LEQ(tdb->ack, seq) ) break; seg = seg->next; if ( !seg || seg->seq > seq ) { // normalize here tdb->ack = seq; ((TCPHdr*)p->tcph)->th_ack = htonl(seq); p->packet_flags |= PKT_MODIFIED; break; } } } #endif #endif // ** if we don't see a segment, we can't track seq at ** below // so we update the seq by the ack if it is beyond next expected if(SEQ_GT(tdb->ack, rcv->l_unackd)) rcv->l_unackd = tdb->ack; // ** this is how we track the last seq number sent // as is l_unackd is the "last left" seq recvd snd->l_unackd = tdb->seq; if (SEQ_GT(tdb->end_seq, snd->l_nxt_seq)) snd->l_nxt_seq = tdb->end_seq; #ifdef S5_PEDANTIC if ( SEQ_GT(tdb->ack, snd->r_win_base) && SEQ_LEQ(tdb->ack, snd->r_nxt_ack) ) #else if ( SEQ_GT(tdb->ack, snd->r_win_base) ) #endif snd->r_win_base = tdb->ack; snd->l_window = tdb->win; } static void StreamInitPacket(void) { s5_pkt = Encode_New(); } static inline void SetupTcpDataBlock(TcpDataBlock *tdb, Packet *p) { tdb->seq = ntohl(p->tcph->th_seq); tdb->ack = ntohl(p->tcph->th_ack); tdb->win = ntohs(p->tcph->th_win); tdb->end_seq = tdb->seq + (uint32_t) p->dsize; tdb->ts = 0; if(p->tcph->th_flags & TH_SYN) { tdb->end_seq++; } // don't bump end_seq for fin here // we will bump if/when fin is processed return; } static void SegmentFree (StreamSegment *seg) { unsigned dropped = sizeof(StreamSegment); STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "Dumping segment at seq %X, size %d, caplen %d\n", seg->seq, seg->size, seg->caplen);); if ( seg->caplen > 0 ) dropped += seg->caplen - 1; // seg contains 1st byte session_mem_in_use -= dropped; SnortPreprocFree(seg, dropped, PP_STREAM, PP_MEM_CATEGORY_SESSION); s5stats.tcp_streamsegs_released++; STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "SegmentFree dropped %d bytes\n", dropped);); } static void DeleteSeglist(StreamSegment *listhead) { StreamSegment *idx = listhead; StreamSegment *dump_me; int i = 0; STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "In DeleteSeglist\n");); while(idx) { i++; dump_me = idx; idx = idx->next; SegmentFree(dump_me); } STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "Dropped %d segments\n", i);); } static inline int purge_alerts(StreamTracker *st, uint32_t flush_seq, void *ssnptr) { int i; int new_count = 0; for (i=0;ialert_count;i++) { StreamAlertInfo* ai = st->alerts + i; if (SEQ_LT(ai->seq, flush_seq) ) { if(st->xtradata_mask && extra_data_log) { extra_data_log( ssnptr, extra_data_config, xtradata_map, xtradata_func_count, st->xtradata_mask, ai->event_id, ai->event_second); } memset(ai, 0, sizeof(*ai)); } else { if (new_count != i) { st->alerts[new_count] = st->alerts[i]; } new_count++; } } st->alert_count = new_count; return new_count; } static inline int purge_to_seq(TcpSession *tcpssn, StreamTracker *st, uint32_t flush_seq) { StreamSegment *ss = NULL; StreamSegment *dump_me = NULL; int purged_bytes = 0; uint32_t last_ts = 0; if(st->seglist == NULL) { if ( SEQ_LT(st->seglist_base_seq, flush_seq) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "setting st->seglist_base_seq to 0x%X\n", flush_seq);); st->seglist_base_seq = flush_seq; } return 0; } ss = st->seglist; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In purge_to_seq, start seq = 0x%X end seq = 0x%X delta %d\n", ss->seq, flush_seq, flush_seq-ss->seq);); while(ss) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "s: %X sz: %d\n", ss->seq, ss->size);); dump_me = ss; ss = ss->next; if(SEQ_LT(dump_me->seq, flush_seq)) { if (dump_me->ts > last_ts) { last_ts = dump_me->ts; } purged_bytes += StreamSeglistDeleteNodeTrim(st, dump_me, flush_seq); } else break; } if ( SEQ_LT(st->seglist_base_seq, flush_seq) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "setting st->seglist_base_seq to 0x%X\n", flush_seq);); st->seglist_base_seq = flush_seq; } if ( SEQ_LT(st->r_nxt_ack, flush_seq) ) st->r_nxt_ack = flush_seq; purge_alerts(st, flush_seq, (void *)tcpssn->scb); if (st->seglist == NULL) { st->seglist_tail = NULL; } /* Update the "last" time stamp seen from the other side * to be the most recent timestamp (largest) that was removed * from the queue. This will ensure that as we go forward, * last timestamp is the highest one that we had stored and * purged and handle the case when packets arrive out of order, * such as: * P1: seq 10, length 10, timestamp 10 * P3: seq 30, length 10, timestamp 30 * P2: seq 20, length 10, timestamp 20 * * Without doing it this way, the timestamp would be 20. With * the next packet to arrive (P4, seq 40), the ts_last value * wouldn't be updated for the talker in ProcessTcp() since that * code specificially looks for the NEXT sequence number. */ if ( !last_ts ) return purged_bytes; if (st == &tcpssn->client) { int32_t delta = last_ts - tcpssn->server.ts_last; if (delta > 0) tcpssn->server.ts_last = last_ts; } else if (st == &tcpssn->server) { int32_t delta = last_ts - tcpssn->client.ts_last; if (delta > 0) tcpssn->client.ts_last = last_ts; } return purged_bytes; } static inline void purge_all (StreamTracker *st) { DeleteSeglist(st->seglist); st->seglist = st->seglist_tail = st->seglist_next = NULL; st->seg_count = st->flush_count = 0; st->seg_bytes_total = st->seg_bytes_logical = 0; } // purge_flushed_ackd(): // * must only purge flushed and acked bytes // * we may flush partial segments // * must adjust seq->seq and seg->size when a flush gets only the // initial part of a segment // * FIXTHIS need flag to mark any reassembled packets that have a gap // (if we reassemble such) static inline int purge_flushed_ackd (TcpSession *tcpssn, StreamTracker *st) { StreamSegment* seg = st->seglist; uint32_t seq; if ( !st->seglist ) return 0; seq = st->seglist->seq; while ( seg && seg->buffered ) { uint32_t end = seg->seq + seg->size; if ( SEQ_GT(end, st->r_win_base) ) { seq = st->r_win_base; break; } seq = end; seg = seg->next; } if ( seq != st->seglist->seq ) return purge_to_seq(tcpssn, st, seq); return 0; } static void ShowRebuiltPacket (Packet* p) { if(stream_session_config->flags & STREAM_CONFIG_SHOW_PACKETS) { //ClearDumpBuf(); printf("+++++++++++++++++++Stream Packet+++++++++++++++++++++\n"); PrintIPPkt(stdout, IPPROTO_TCP, p); printf("+++++++++++++++++++++++++++++++++++++++++++++++++++++\n"); //ClearDumpBuf(); } } static inline int _flush_to_seq_noack( TcpSession *tcpssn, StreamTracker *st, uint32_t bytes, Packet *p, sfaddr_t* sip, sfaddr_t* dip, uint16_t sp, uint16_t dp, uint32_t dir ) { uint32_t stop_seq; uint32_t footprint = 0; uint32_t bytes_processed = 0; PROFILE_VARS; PREPROC_PROFILE_START(s5TcpFlushPerfStats); if(p->dsize == 0) return bytes_processed; // if not specified, set bytes to flush to what was acked if ( !bytes && SEQ_GT(st->r_win_base, st->seglist_base_seq) ) bytes = st->r_win_base - st->seglist_base_seq; stop_seq = st->seglist_base_seq + bytes; { footprint = stop_seq - st->seglist_base_seq; if(footprint == 0) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Negative footprint, bailing %d (0x%X - 0x%X)\n", footprint, stop_seq, st->seglist_base_seq);); PREPROC_PROFILE_END(s5TcpFlushPerfStats); return bytes_processed; } #if 0 //Might not need this as we are not buffering the packets?? #ifdef DEBUG_STREAM if(footprint < st->seg_bytes_logical) { STREAM5_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Footprint less than queued bytes, " "win_base: 0x%X base_seq: 0x%X\n", stop_seq, st->seglist_base_seq);); } #endif #endif #if 0 //Won't need this as we are not buffering the packets if(footprint > p->max_dsize) { /* this is as much as we can pack into a stream buffer */ footprint = p->max_dsize; stop_seq = st->seglist_base_seq + footprint; } #endif STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Attempting to flush %lu bytes\n", footprint);); st->seglist_base_seq = stop_seq; st->seglist_next->buffered = SL_BUF_FLUSHED; st->flush_count++; p->packet_flags &= ~PKT_STREAM_INSERT; p->packet_flags |= (PKT_REBUILT_STREAM|PKT_STREAM_EST); bytes_processed = p->dsize; sfBase.iStreamFlushes++; ShowRebuiltPacket(p); s5stats.tcp_rebuilt_packets++; UpdateStreamReassStats(&sfBase, bytes_processed); } if ( st->tcp_policy ) UpdateFlushMgr(snort_conf, &st->flush_mgr, &st->tcp_policy->flush_point_list, st->flags); /* tell them how many bytes we processed */ PREPROC_PROFILE_END(s5TcpFlushPerfStats); return bytes_processed; } static inline unsigned int getSegmentFlushSize( StreamTracker* st, StreamSegment *ss, uint32_t to_seq, unsigned int flushBufSize ) { unsigned int flushSize = ss->size; //copy only till flush buffer gets full if ( flushSize > flushBufSize ) flushSize = flushBufSize; // copy only to flush point if ( s5_paf_active(&st->paf_state) && SEQ_GT(ss->seq + flushSize, to_seq) ) flushSize = to_seq - ss->seq; return flushSize; } static int PseudoFlushStream( Packet* p, StreamTracker *st, uint32_t toSeq, uint8_t *flushbuf, const uint8_t *flushbuf_end) { StreamSegment *ss = NULL, *seglist; uint16_t bytes_flushed = 0; uint32_t flushbuf_size, bytes_to_copy; int ret; PROFILE_VARS; if ( st->seglist == NULL || st->seglist_tail == NULL ) return -1; PREPROC_PROFILE_START(s5TcpBuildPacketPerfStats); // skip over previously pseudo flushed segments seglist = st->seglist_next; for(ss = seglist; ss && SEQ_LT(ss->seq, toSeq); ss = ss->next) { flushbuf_size = flushbuf_end - flushbuf; bytes_to_copy = getSegmentFlushSize(st, ss, toSeq, flushbuf_size); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Copying %u bytes for pseudo flushing from %X\n", bytes_to_copy, ss->seq);); ret = SafeMemcpy(flushbuf, ss->payload, bytes_to_copy, flushbuf, flushbuf_end); if (ret == SAFEMEM_ERROR) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "ERROR writing flushbuf while pseudo flushing." "Attempting to write flushbuf out of range!\n");); } else { flushbuf += bytes_to_copy; } bytes_flushed += bytes_to_copy; if (ss->next) { st->seglist_next = ss->next; } if ( flushbuf >= flushbuf_end ) break; if ( SEQ_EQ(ss->seq + bytes_to_copy, toSeq) ) break; } PREPROC_PROFILE_END(s5TcpBuildPacketPerfStats); return bytes_flushed; } static inline void pseudo_flush( TcpSession *tcpssn, StreamTracker *st, uint32_t bytes, Packet *p, sfaddr_t* sip, sfaddr_t* dip, uint16_t sp, uint16_t dp, uint32_t dir) { uint32_t start_seq; uint32_t stop_seq; uint32_t footprint = 0; uint32_t bytes_processed = 0; int32_t flushed_bytes; StreamSegment *ss = NULL; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID DAQ_PktHdr_t pkth; #endif EncodeFlags enc_flags = 0; PROFILE_VARS; if (!bytes) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "No bytes to pseudo flush\n");); return; } PREPROC_PROFILE_START(s5TcpFlushPerfStats); if ( !p->packet_flags || (dir & p->packet_flags) ) enc_flags = ENC_FLAG_FWD; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID GetPacketHeaderFoo(tcpssn, p->pkth, enc_flags, &pkth, dir); Encode_Format_With_DAQ_Info(enc_flags, p, s5_pkt, PSEUDO_PKT_TCP, &pkth, 0); #elif defined(HAVE_DAQ_ACQUIRE_WITH_META) Encode_Format_With_DAQ_Info(enc_flags, p, s5_pkt, PSEUDO_PKT_TCP, 0); #else Encode_Format(enc_flags, p, s5_pkt, PSEUDO_PKT_TCP); #endif s5_pkt_end = s5_pkt->data + s5_pkt->max_dsize; ss = st->seglist_next; stop_seq = st->seglist_next->seq + bytes; do { footprint = bytes; if(footprint > s5_pkt->max_dsize) { /* this is as much as we can pack into a stream buffer */ footprint = s5_pkt->max_dsize; stop_seq = st->seglist_next->seq + footprint; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Attempting to pseudo flush %lu bytes\n", footprint);); /* Capture the seq of the first octet before flush changes the sequence numbers */ start_seq = htonl(st->seglist_next->seq); /* setup the pseudopacket payload */ flushed_bytes = PseudoFlushStream(p, st, stop_seq, (uint8_t *)s5_pkt->data, s5_pkt_end); if(flushed_bytes == -1) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Failed to pseudo flush the stream\n");); return; } if (flushed_bytes == 0) { /* No more ACK'd data... bail */ break; } bytes_processed += flushed_bytes; ((TCPHdr *)s5_pkt->tcph)->th_seq = start_seq; s5_pkt->packet_flags |= (PKT_REBUILT_STREAM|PKT_STREAM_EST); s5_pkt->dsize = (uint16_t)flushed_bytes; s5_pkt->packet_flags |= PKT_PDU_TAIL; s5_pkt->packet_flags |= PKT_PSEUDO_FLUSH; Encode_Update(s5_pkt); if(IS_IP4(s5_pkt)) { s5_pkt->inner_ip4h.ip_len = s5_pkt->iph->ip_len; } else { IP6RawHdr* ip6h = (IP6RawHdr*)s5_pkt->raw_ip6h; if ( ip6h ) s5_pkt->inner_ip6h.len = ip6h->ip6plen; } ((DAQ_PktHdr_t*)s5_pkt->pkth)->ts.tv_sec = st->seglist_next->tv.tv_sec; ((DAQ_PktHdr_t*)s5_pkt->pkth)->ts.tv_usec = st->seglist_next->tv.tv_usec; s5_pkt->packet_flags |= dir; s5_pkt->ssnptr = (void *) tcpssn->scb; #ifdef TARGET_BASED s5_pkt->application_protocol_ordinal = p->application_protocol_ordinal; #endif PREPROC_PROFILE_TMPEND(s5TcpFlushPerfStats); { int tmp_do_detect, tmp_do_detect_content; PROFILE_VARS; PREPROC_PROFILE_START(s5TcpProcessRebuiltPerfStats); tmp_do_detect = do_detect; tmp_do_detect_content = do_detect_content; SnortEventqPush(); Preprocess(s5_pkt); SnortEventqPop(); DetectReset(s5_pkt->data, s5_pkt->dsize); do_detect = tmp_do_detect; do_detect_content = tmp_do_detect_content; PREPROC_PROFILE_END(s5TcpProcessRebuiltPerfStats); } PREPROC_PROFILE_TMPSTART(s5TcpFlushPerfStats); } while(bytes_processed < bytes); st->seglist_next = ss; PREPROC_PROFILE_END(s5TcpFlushPerfStats); } static inline int _flush_to_seq ( TcpSession *tcpssn, StreamTracker *st, uint32_t bytes, Packet *p, sfaddr_t* sip, sfaddr_t* dip, uint16_t sp, uint16_t dp, uint32_t dir) { uint32_t start_seq; uint32_t stop_seq; uint32_t footprint = 0; uint32_t bytes_processed = 0; int32_t flushed_bytes; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID DAQ_PktHdr_t pkth; #endif EncodeFlags enc_flags = 0; PROFILE_VARS; PREPROC_PROFILE_START(s5TcpFlushPerfStats); if(st->paf_state.fpt_eoh) tcpssn->pp_flags |= PP_HTTPINSPECT_PAF_FLUSH_POST_HDR; else tcpssn->pp_flags &= ~PP_HTTPINSPECT_PAF_FLUSH_POST_HDR; if ( !p->packet_flags || (dir & p->packet_flags) ) enc_flags = ENC_FLAG_FWD; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID GetPacketHeaderFoo(tcpssn, p->pkth, enc_flags, &pkth, dir); Encode_Format_With_DAQ_Info(enc_flags, p, s5_pkt, PSEUDO_PKT_TCP, &pkth, 0); #elif defined(HAVE_DAQ_ACQUIRE_WITH_META) Encode_Format_With_DAQ_Info(enc_flags, p, s5_pkt, PSEUDO_PKT_TCP, 0); #else Encode_Format(enc_flags, p, s5_pkt, PSEUDO_PKT_TCP); #endif s5_pkt_end = s5_pkt->data + s5_pkt->max_dsize; // TBD in ips mode, these should be coming from current packet (tdb) ((TCPHdr *)s5_pkt->tcph)->th_ack = htonl(st->l_unackd); ((TCPHdr *)s5_pkt->tcph)->th_win = htons((uint16_t)st->l_window); // if not specified, set bytes to flush to what was acked if ( !bytes && SEQ_GT(st->r_win_base, st->seglist_base_seq) ) bytes = st->r_win_base - st->seglist_base_seq; stop_seq = st->seglist_base_seq + bytes; do { footprint = stop_seq - st->seglist_base_seq; if(footprint == 0) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Negative footprint, bailing %d (0x%X - 0x%X)\n", footprint, stop_seq, st->seglist_base_seq);); PREPROC_PROFILE_END(s5TcpFlushPerfStats); return bytes_processed; } #ifdef STREAM_DEBUG_ENABLED if(footprint < st->seg_bytes_logical) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Footprint less than queued bytes, " "win_base: 0x%X base_seq: 0x%X\n", stop_seq, st->seglist_base_seq);); } #endif if(footprint > s5_pkt->max_dsize) { /* this is as much as we can pack into a stream buffer */ footprint = s5_pkt->max_dsize; stop_seq = st->seglist_base_seq + footprint; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Attempting to flush %lu bytes\n", footprint);); /* Capture the seq of the first octet before flush changes the sequence numbers */ start_seq = htonl(st->seglist_next->seq); /* setup the pseudopacket payload */ flushed_bytes = FlushStream(p, st, stop_seq, (uint8_t *)s5_pkt->data, s5_pkt_end); if(flushed_bytes == -1) { /* couldn't put a stream together for whatever reason * should probably clean the seglist and bail... */ if(st->seglist) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "dumping entire seglist!\n");); purge_all(st); } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "setting st->seglist_base_seq to 0x%X\n", stop_seq);); st->seglist_base_seq = stop_seq; PREPROC_PROFILE_END(s5TcpFlushPerfStats); return bytes_processed; } if (flushed_bytes == 0) { /* No more ACK'd data... bail */ break; } ((TCPHdr *)s5_pkt->tcph)->th_seq = start_seq; s5_pkt->packet_flags |= (PKT_REBUILT_STREAM|PKT_STREAM_EST); s5_pkt->dsize = (uint16_t)flushed_bytes; if ((p->packet_flags & PKT_PDU_TAIL)) s5_pkt->packet_flags |= PKT_PDU_TAIL; /* Rebuilt packet with only and complete http-post header is set with PKT_EVAL_DROP, * it will be used to evalute drop/allow packet by preprocs */ if (tcpssn->pp_flags & PP_HTTPINSPECT_PAF_FLUSH_POST_HDR) s5_pkt->packet_flags |= PKT_EVAL_DROP; Encode_Update(s5_pkt); if(IS_IP4(s5_pkt)) { s5_pkt->inner_ip4h.ip_len = s5_pkt->iph->ip_len; } else { IP6RawHdr* ip6h = (IP6RawHdr*)s5_pkt->raw_ip6h; if ( ip6h ) s5_pkt->inner_ip6h.len = ip6h->ip6plen; } ((DAQ_PktHdr_t*)s5_pkt->pkth)->ts.tv_sec = st->seglist_next->tv.tv_sec; ((DAQ_PktHdr_t*)s5_pkt->pkth)->ts.tv_usec = st->seglist_next->tv.tv_usec; sfBase.iStreamFlushes++; bytes_processed += s5_pkt->dsize; s5_pkt->packet_flags |= dir; s5_pkt->ssnptr = (void *) tcpssn->scb; #ifdef TARGET_BASED s5_pkt->application_protocol_ordinal = p->application_protocol_ordinal; #endif ShowRebuiltPacket(s5_pkt); s5stats.tcp_rebuilt_packets++; UpdateStreamReassStats(&sfBase, flushed_bytes); PREPROC_PROFILE_TMPEND(s5TcpFlushPerfStats); { int tmp_do_detect, tmp_do_detect_content; PROFILE_VARS; PREPROC_PROFILE_START(s5TcpProcessRebuiltPerfStats); tmp_do_detect = do_detect; tmp_do_detect_content = do_detect_content; SnortEventqPush(); Preprocess(s5_pkt); SnortEventqPop(); DetectReset(s5_pkt->data, s5_pkt->dsize); do_detect = tmp_do_detect; do_detect_content = tmp_do_detect_content; PREPROC_PROFILE_END(s5TcpProcessRebuiltPerfStats); } PREPROC_PROFILE_TMPSTART(s5TcpFlushPerfStats); // TBD abort should be by PAF callback only since // recovery may be possible in some cases if ( st->flags & TF_MISSING_PKT ) { st->flags |= TF_MISSING_PREV_PKT; st->flags |= TF_PKT_MISSED; st->flags &= ~TF_MISSING_PKT; s5stats.tcp_gaps++; } else { st->flags &= ~TF_MISSING_PREV_PKT; } } while ( DataToFlush(st) ); if ( st->tcp_policy ) UpdateFlushMgr(snort_conf, &st->flush_mgr, &st->tcp_policy->flush_point_list, st->flags); /* tell them how many bytes we processed */ PREPROC_PROFILE_END(s5TcpFlushPerfStats); return bytes_processed; } static inline int flush_to_seq_noack( TcpSession *tcpssn, StreamTracker *st, uint32_t bytes, Packet *p, sfaddr_t* sip, sfaddr_t* dip, uint16_t sp, uint16_t dp, uint32_t dir ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In flush_to_seq_noack()\n");); if ( !bytes ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bailing, no data\n");); return 0; } if ( !st->seglist_next ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bailing, bad seglist ptr\n");); return 0; } #if 0 //Might not need this. Check with Russ. if (!DataToFlush(st) && !(st->flags & TF_FORCE_FLUSH)) { STREAM5_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "only 1 packet in seglist no need to flush\n");); return 0; } #endif st->flags &= ~TF_MISSING_PKT; st->flags &= ~TF_MISSING_PREV_PKT; /* This will set this flag on the first reassembly * if reassembly for this direction was set midstream */ if ( SEQ_LT(st->seglist_base_seq, st->seglist_next->seq) ) { uint32_t missed = st->seglist_next->seq - st->seglist_base_seq; if ( missed <= bytes ) bytes -= missed; st->flags |= TF_MISSING_PREV_PKT; st->flags |= TF_PKT_MISSED; s5stats.tcp_gaps++; st->seglist_base_seq = st->seglist_next->seq; if ( !bytes ) return 0; } return _flush_to_seq_noack(tcpssn, st, bytes, p, sip, dip, sp, dp, dir); } /* * flush a seglist up to the given point, generate a pseudopacket, * and fire it thru the system. */ static inline int flush_to_seq( TcpSession *tcpssn, StreamTracker *st, uint32_t bytes, Packet *p, sfaddr_t* sip, sfaddr_t* dip, uint16_t sp, uint16_t dp, uint32_t dir) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In flush_to_seq()\n");); if ( !bytes ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bailing, no data\n");); return 0; } if ( !st->seglist_next ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bailing, bad seglist ptr\n");); return 0; } if (!DataToFlush(st) && !(st->flags & TF_FORCE_FLUSH)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "only 1 packet in seglist no need to flush\n");); return 0; } st->flags &= ~TF_MISSING_PREV_PKT; /* This will set this flag on the first reassembly * if reassembly for this direction was set midstream */ if ( SEQ_LT(st->seglist_base_seq, st->seglist_next->seq) && !(st->flags & TF_FIRST_PKT_MISSING) ) { uint32_t missed = st->seglist_next->seq - st->seglist_base_seq; if ( missed <= bytes ) bytes -= missed; st->flags |= TF_MISSING_PREV_PKT; st->flags |= TF_PKT_MISSED; s5stats.tcp_gaps++; st->seglist_base_seq = st->seglist_next->seq; if ( !bytes ) return 0; } st->flags &= ~TF_FIRST_PKT_MISSING; if (p->packet_flags & PKT_PSEUDO_FLUSH) { pseudo_flush(tcpssn, st, bytes, p, sip, dip, sp, dp, dir); return 0; } return _flush_to_seq(tcpssn, st, bytes, p, sip, dip, sp, dp, dir); } /* * get the footprint for the current seglist, the difference * between our base sequence and the last ack'd sequence we * received */ static inline uint32_t get_q_footprint(StreamTracker *st) { uint32_t fp; if (st == NULL) { return 0; } fp = st->r_win_base - st->seglist_base_seq; if(fp <= 0) return 0; st->seglist_next = st->seglist; return fp; } static bool two_way_traffic=true; // FIXTHIS get_q_sequenced() performance could possibly be // boosted by tracking sequenced bytes as seglist is updated // to avoid the while loop, etc. below. static inline uint32_t get_q_sequenced(StreamTracker *st) { uint32_t len; StreamSegment* seg = st ? st->seglist : NULL; StreamSegment* base = NULL; if ( !seg ) return 0; if ( two_way_traffic && SEQ_LT(st->r_win_base, seg->seq) ) return 0; while ( seg->next && (seg->next->seq == seg->seq + seg->size) ) { if ( !seg->buffered && !base ) base = seg; seg = seg->next; } if ( !seg->buffered && !base ) base = seg; if ( !base ) return 0; st->seglist_next = base; st->seglist_base_seq = base->seq; len = seg->seq + seg->size - base->seq; return ( len > 0 ) ? len : 0; } static inline int flush_ackd( TcpSession *tcpssn, StreamTracker *st, Packet *p, sfaddr_t* sip, sfaddr_t* dip, uint16_t sp, uint16_t dp, uint32_t dir) { uint32_t bytes = get_q_footprint(st); return flush_to_seq(tcpssn, st, bytes, p, sip, dip, sp, dp, dir); } // FIXTHIS flush_stream() calls should be replaced with calls to // CheckFlushPolicyOn*() with the exception that for the *OnAck() case, // any available ackd data must be flushed in both directions. static inline int flush_stream( TcpSession *tcpssn, StreamTracker *st, Packet *p, sfaddr_t* sip, sfaddr_t* dip, uint16_t sp, uint16_t dp, uint32_t dir) { FlushMgr* fm; #ifdef NORMALIZER if ( Stream_NormGetMode(st->reassembly_policy, snort_conf, NORM_TCP_IPS) == NORM_MODE_ON ) { uint32_t bytes = get_q_sequenced(st); return flush_to_seq(tcpssn, st, bytes, p, sip, dip, sp, dp, dir); } #endif fm = &tcpssn->server.flush_mgr; if(fm->flush_policy == STREAM_FLPOLICY_PROTOCOL_NOACK) { uint32_t bytes = get_q_sequenced(st); return flush_to_seq(tcpssn, st, bytes, p, sip, dip, sp, dp, dir); } else if (fm->flush_policy == STREAM_FLPOLICY_FOOTPRINT_NOACK) { uint32_t bytes = get_q_sequenced(st); return flush_to_seq_noack(tcpssn, st, bytes, p, sip, dip, sp, dp, dir); } return flush_ackd(tcpssn, st, p, sip, dip, sp, dp, dir); } static int FlushStream( Packet* p, StreamTracker *st, uint32_t toSeq, uint8_t *flushbuf, const uint8_t *flushbuf_end) { StreamSegment *ss = NULL, *seglist, *sr; uint16_t bytes_flushed = 0; uint16_t bytes_skipped = 0; uint32_t bytes_queued = st->seg_bytes_logical; uint32_t segs = 0; int ret; PROFILE_VARS; if ( st->seglist == NULL || st->seglist_tail == NULL ) return -1; PREPROC_PROFILE_START(s5TcpBuildPacketPerfStats); // skip over previously flushed segments seglist = st->seglist_next; for(ss = seglist; ss && SEQ_LT(ss->seq, toSeq); ss = ss->next) { unsigned int flushbuf_size = flushbuf_end - flushbuf; unsigned int bytes_to_copy = getSegmentFlushSize(st, ss, toSeq, flushbuf_size); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flushing %u bytes from %X\n", bytes_to_copy, ss->seq)); if(ss->urg_offset >= 1) { /* if urg_offset is set, seq + urg_offset is seq # of octet * in stream following the last urgent octet. all preceding * octets in segment are considered urgent. this code will * skip over the urgent data when flushing. */ unsigned int non_urgent_bytes = ss->urg_offset < bytes_to_copy ? (bytes_to_copy - ss->urg_offset) : 0; if ( non_urgent_bytes ) { ret = SafeMemcpy(flushbuf, ss->payload+ss->urg_offset, non_urgent_bytes, flushbuf, flushbuf_end); if (ret == SAFEMEM_ERROR) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "ERROR writing flushbuf attempting to " "write flushbuf out of range!\n");); } else flushbuf += non_urgent_bytes; bytes_skipped += ss->urg_offset; } else { ss->urg_offset = 0; } } else { ret = SafeMemcpy(flushbuf, ss->payload, bytes_to_copy, flushbuf, flushbuf_end); if (ret == SAFEMEM_ERROR) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "ERROR writing flushbuf attempting to " "write flushbuf out of range!\n");); } else flushbuf += bytes_to_copy; } if ( !st->paf_state.fpt_eoh && bytes_to_copy < ss->size && DupStreamNode(NULL, st, ss, &sr) == STREAM_INSERT_OK ) { ss->size = bytes_to_copy; sr->seq += bytes_to_copy; sr->size -= bytes_to_copy; sr->payload += bytes_to_copy + (ss->payload - ss->data); } bytes_flushed += bytes_to_copy; if(!st->paf_state.fpt_eoh){ ss->buffered = SL_BUF_FLUSHED; st->flush_count++; segs++; } if ( flushbuf >= flushbuf_end ) break; if ( SEQ_EQ(ss->seq + bytes_to_copy, toSeq) ) break; /* Check for a gap/missing packet */ // FIXTHIS PAF should account for missing data and resume // scanning at the start of next PDU instead of aborting. // FIXTHIS FIN may be in toSeq causing bogus gap counts. if ( ((ss->next && (ss->seq + ss->size != ss->next->seq)) || (!ss->next && (ss->seq + ss->size < toSeq))) && !(st->flags & TF_FIRST_PKT_MISSING) ) { if ( ss->next ) { toSeq = ss->next->seq; st->seglist_next = ss->next; } st->flags |= TF_MISSING_PKT; break; } } st->seglist_base_seq = toSeq; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "setting st->seglist_base_seq to 0x%X\n", st->seglist_base_seq);); bytes_queued -= bytes_flushed; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "flushed %d bytes / %d segs on stream, " "skipped %d bytes, %d still queued\n", bytes_flushed, segs, bytes_skipped, bytes_queued);); PREPROC_PROFILE_END(s5TcpBuildPacketPerfStats); return bytes_flushed - bytes_skipped; } int StreamFlushServer(Packet *p, SessionControlBlock *scb) { int flushed; TcpSession *tcpssn = NULL; StreamTracker *flushTracker = NULL; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return 0; flushTracker = &tcpssn->server; flushTracker->flags |= TF_FORCE_FLUSH; /* If this is a rebuilt packet, don't flush now because we'll * overwrite the packet being processed. */ if (p->packet_flags & PKT_REBUILT_STREAM) { /* We'll check & clear the TF_FORCE_FLUSH next time through */ return 0; } /* Need to convert the addresses to network order */ flushed = flush_stream(tcpssn, flushTracker, p, &tcpssn->tcp_server_ip, &tcpssn->tcp_client_ip, tcpssn->tcp_server_port, tcpssn->tcp_client_port, PKT_FROM_SERVER); if (flushed) purge_flushed_ackd(tcpssn, flushTracker); flushTracker->flags &= ~TF_FORCE_FLUSH; return flushed; } int StreamFlushClient(Packet *p, SessionControlBlock *scb) { int flushed; TcpSession *tcpssn = NULL; StreamTracker *flushTracker = NULL; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return 0; flushTracker = &tcpssn->client; flushTracker->flags |= TF_FORCE_FLUSH; /* If this is a rebuilt packet, don't flush now because we'll * overwrite the packet being processed. */ if (p->packet_flags & PKT_REBUILT_STREAM) { /* We'll check & clear the TF_FORCE_FLUSH next time through */ return 0; } /* Need to convert the addresses to network order */ flushed = flush_stream(tcpssn, flushTracker, p, &tcpssn->tcp_client_ip, &tcpssn->tcp_server_ip, tcpssn->tcp_client_port, tcpssn->tcp_server_port, PKT_FROM_CLIENT); if (flushed) purge_flushed_ackd(tcpssn, flushTracker); flushTracker->flags &= ~TF_FORCE_FLUSH; return flushed; } int StreamFlushListener(Packet *p, SessionControlBlock *scb) { TcpSession *tcpssn = NULL; StreamTracker *listener = NULL; int dir = 0; int flushed = 0; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return 0; /* figure out direction of this packet -- we should've already * looked at it, so the packet_flags are already set. */ if(p->packet_flags & PKT_FROM_SERVER) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flushing listener on packet from server\n");); listener = &tcpssn->client; /* dir of flush is the data from the opposite side */ dir = PKT_FROM_SERVER; } else if (p->packet_flags & PKT_FROM_CLIENT) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flushing listener on packet from client\n");); listener = &tcpssn->server; /* dir of flush is the data from the opposite side */ dir = PKT_FROM_CLIENT; } if (dir != 0) { listener->flags |= TF_FORCE_FLUSH; flushed = flush_stream(tcpssn, listener, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, dir); if (flushed) purge_flushed_ackd(tcpssn, listener); listener->flags &= ~TF_FORCE_FLUSH; } return flushed; } int StreamFlushTalker(Packet *p, SessionControlBlock *scb) { TcpSession *tcpssn = NULL; StreamTracker *talker = NULL; int dir = 0; int flushed = 0; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) { return 0; } /* figure out direction of this packet -- we should've already * looked at it, so the packet_flags are already set. */ if(p->packet_flags & PKT_FROM_SERVER) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flushing talker on packet from server\n");); talker = &tcpssn->server; /* dir of flush is the data from the opposite side */ dir = PKT_FROM_CLIENT; } else if (p->packet_flags & PKT_FROM_CLIENT) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flushing talker on packet from client\n");); talker = &tcpssn->client; /* dir of flush is the data from the opposite side */ dir = PKT_FROM_SERVER; } if (dir != 0) { talker->flags |= TF_FORCE_FLUSH; flushed = flush_stream(tcpssn, talker, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, dir); if (flushed) purge_flushed_ackd(tcpssn, talker); talker->flags &= ~TF_FORCE_FLUSH; } return flushed; } static void TcpSessionClear (SessionControlBlock* scb, TcpSession* tcpssn, int freeApplicationData) { STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "In TcpSessionClear, %lu bytes in use\n", session_mem_in_use);); STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "client has %d segs queued\n", tcpssn->client.seg_count);); STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "server has %d segs queued\n", tcpssn->server.seg_count);); // update stats s5stats.tcp_streamtrackers_released++; if (s5stats.active_tcp_sessions > 0 ) s5stats.active_tcp_sessions--; StreamUpdatePerfBaseState(&sfBase, tcpssn->scb, TCP_STATE_CLOSED); RemoveStreamSession(&sfBase); if (scb->ha_state.session_flags & SSNFLAG_PRUNED) CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED); else if (scb->ha_state.session_flags & SSNFLAG_TIMEDOUT) CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED | SESSION_CLOSED_TIMEDOUT); else CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY); // release external state if (freeApplicationData) session_api->free_application_data(scb); StreamResetFlowBits(scb); // release internal protocol specific state purge_all(&tcpssn->client); purge_all(&tcpssn->server); s5_paf_clear(&tcpssn->client.paf_state); s5_paf_clear(&tcpssn->server.paf_state); session_api->free_protocol_session_pool( SESSION_PROTO_TCP, scb ); scb->proto_specific_data = NULL; // update light-weight state scb->ha_state.session_flags = SSNFLAG_NONE; scb->session_state = STREAM_STATE_NONE; scb->expire_time = 0; scb->ha_state.ignore_direction = 0; // generate event for rate filtering EventInternal(INTERNAL_EVENT_SESSION_DEL); STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "After cleaning, %lu bytes in use\n", session_mem_in_use);); } static StreamTcpPolicy *StreamSearchTcpConfigForBoundPolicy(StreamTcpConfig *tcp_config, sfaddr_t *ip) { int policyIndex; StreamTcpPolicy *policy = NULL; for (policyIndex = 0; policyIndex < tcp_config->num_policies; policyIndex++) { policy = tcp_config->policy_list[policyIndex]; if (policy->bound_addrs == NULL) continue; /* * Does this policy handle packets to this IP address? */ if(sfvar_ip_in(policy->bound_addrs, ip)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] Found tcp policy in IpAddrSet\n");); break; } } if (policyIndex == tcp_config->num_policies) policy = tcp_config->default_policy; return policy; } static inline StreamTcpPolicy *StreamPolicyLookup( SessionControlBlock *scb, sfaddr_t *ip) { StreamTcpConfig *tcp_config = ( ( StreamConfig * ) scb->stream_config )->tcp_config; if( tcp_config != NULL ) return StreamSearchTcpConfigForBoundPolicy( tcp_config, ip ); else return NULL; } static void FlushQueuedSegs(SessionControlBlock *scb, TcpSession *tcpssn) { DAQ_PktHdr_t tmp_pcap_hdr; #ifdef REG_TEST if (getRegTestFlags() & REG_TEST_FLAG_STREAM_DECODE) printf("\nFlushQueuedSegs | "); #endif /* Flush ack'd data on both sides as necessary */ { Packet *p = NewGrinderPkt(tcp_cleanup_pkt, NULL, NULL); int flushed; tcp_cleanup_pkt = p; if (!s5_tcp_cleanup) { /* Turn off decoder alerts since we're decoding stored * packets that we already alerted on. */ policyDecoderFlagsSaveNClear(scb->napPolicyId); } policyChecksumFlagsSaveNClear(scb->napPolicyId); /* Flush the client */ if (tcpssn->client.seglist && !(scb->ha_state.ignore_direction & SSN_DIR_FROM_SERVER) ) { pc.s5tcp1++; /* Do each field individually because of size differences on 64bit OS */ memset(&tmp_pcap_hdr, 0, sizeof(tmp_pcap_hdr)); tmp_pcap_hdr.ts.tv_sec = tcpssn->client.seglist->tv.tv_sec; tmp_pcap_hdr.ts.tv_usec = tcpssn->client.seglist->tv.tv_usec; tmp_pcap_hdr.caplen = tcpssn->client.seglist->caplen; tmp_pcap_hdr.pktlen = tcpssn->client.seglist->pktlen; SnortEventqPush(); (*grinder)(p, &tmp_pcap_hdr, tcpssn->client.seglist->pkt); p->ssnptr = scb; //set policy id for this packet { #ifdef HAVE_DAQ_REAL_ADDRESSES sfaddr_t* srcIp = (p->iph)? GET_SRC_IP((p)) : NULL; sfaddr_t* dstIp = (p->iph)? GET_DST_IP((p)) : NULL; if (srcIp && dstIp) { if (srcIp->family == AF_INET6) { memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_sIP).s6_addr), &(srcIp)->ia32, sizeof(struct in6_addr)); memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_dIP).s6_addr), &(dstIp)->ia32, sizeof(struct in6_addr)); ((DAQ_PktHdr_t*)p->pkth)->flags |= (DAQ_PKT_FLAG_REAL_SIP_V6 | DAQ_PKT_FLAG_REAL_DIP_V6); } else { memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_sIP).s6_addr), &(srcIp)->ia32[3], sizeof(uint32_t)); memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_dIP).s6_addr), &(dstIp)->ia32[3], sizeof(uint32_t)); } ((DAQ_PktHdr_t*)p->pkth)->flags |= DAQ_PKT_FLAG_REAL_ADDRESSES; if( tcpssn->daq_flags & DAQ_PKT_FLAG_IGNORE_VLAN ) ((DAQ_PktHdr_t*)p->pkth)->flags |= DAQ_PKT_FLAG_IGNORE_VLAN; } #endif if( scb->session_config != session_configuration ) { getSessionPlugins()->select_session_nap(p, true ); scb->napPolicyId = getNapRuntimePolicy(); scb->ipsPolicyId = getDefaultPolicy(); p->configPolicyId = snort_conf->targeted_policies[scb->ipsPolicyId]->configPolicyId; scb->stream_config = sfPolicyUserDataGet( stream_online_config, scb->napPolicyId ); tcpssn->client.tcp_policy = StreamPolicyLookup(scb, GET_DST_IP(p)); tcpssn->server.tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); } else { setNapRuntimePolicy(scb->napPolicyId); setIpsRuntimePolicy(scb->ipsPolicyId); p->configPolicyId = snort_conf->targeted_policies[scb->ipsPolicyId]->configPolicyId; scb->stream_config = sfPolicyUserDataGet( stream_online_config, scb->napPolicyId ); } //actions are queued only for IDS case sfActionQueueExecAll(decoderActionQ); } SnortEventqPop(); tcpssn->client.flags |= TF_FORCE_FLUSH; if ( !p->tcph ) { flushed = 0; } else { flushed = flush_stream(tcpssn, &tcpssn->client, p, p->iph_api->iph_ret_src(p), p->iph_api->iph_ret_dst(p), p->tcph->th_sport, p->tcph->th_dport, PKT_FROM_SERVER); } if (flushed) purge_flushed_ackd(tcpssn, &tcpssn->client); else { SnortEventqPush(); SnortEventqLog(snort_conf->event_queue, p); SnortEventqReset(); SnortEventqPop(); } tcpssn->client.flags &= ~TF_FORCE_FLUSH; } /* Flush the server */ if (tcpssn->server.seglist && !(scb->ha_state.ignore_direction & SSN_DIR_FROM_CLIENT) ) { pc.s5tcp2++; /* Do each field individually because of size differences on 64bit OS */ memset(&tmp_pcap_hdr, 0, sizeof(tmp_pcap_hdr)); tmp_pcap_hdr.ts.tv_sec = tcpssn->server.seglist->tv.tv_sec; tmp_pcap_hdr.ts.tv_usec = tcpssn->server.seglist->tv.tv_usec; tmp_pcap_hdr.caplen = tcpssn->server.seglist->caplen; tmp_pcap_hdr.pktlen = tcpssn->server.seglist->pktlen; SnortEventqPush(); (*grinder)(p, &tmp_pcap_hdr, tcpssn->server.seglist->pkt); p->ssnptr = scb; //set policy id for this packet { #ifdef HAVE_DAQ_REAL_ADDRESSES sfaddr_t* srcIp = (p->iph)? GET_SRC_IP((p)) : NULL; sfaddr_t* dstIp = (p->iph)? GET_DST_IP((p)) : NULL; if (srcIp && dstIp) { if (srcIp->family == AF_INET6) { memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_sIP).s6_addr), &(srcIp)->ia32, sizeof(struct in6_addr)); memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_dIP).s6_addr), &(dstIp)->ia32, sizeof(struct in6_addr)); ((DAQ_PktHdr_t*)p->pkth)->flags |= (DAQ_PKT_FLAG_REAL_SIP_V6 | DAQ_PKT_FLAG_REAL_DIP_V6); } else { memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_sIP).s6_addr), &(srcIp)->ia32[3], sizeof(uint32_t)); memcpy(&((((DAQ_PktHdr_t*)p->pkth)->real_dIP).s6_addr), &(dstIp)->ia32[3], sizeof(uint32_t)); } ((DAQ_PktHdr_t*)p->pkth)->flags |= DAQ_PKT_FLAG_REAL_ADDRESSES; if( tcpssn->daq_flags & DAQ_PKT_FLAG_IGNORE_VLAN ) ((DAQ_PktHdr_t*)p->pkth)->flags |= DAQ_PKT_FLAG_IGNORE_VLAN; } #endif if( scb->session_config != session_configuration ) { getSessionPlugins()->select_session_nap(p, false ); scb->ipsPolicyId = getDefaultPolicy(); scb->napPolicyId = getNapRuntimePolicy(); p->configPolicyId = snort_conf->targeted_policies[scb->ipsPolicyId]->configPolicyId; scb->stream_config = sfPolicyUserDataGet( stream_online_config, scb->napPolicyId ); tcpssn->client.tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); tcpssn->server.tcp_policy = StreamPolicyLookup(scb, GET_DST_IP(p)); } else { setNapRuntimePolicy(scb->napPolicyId); setIpsRuntimePolicy(scb->ipsPolicyId); p->configPolicyId = snort_conf->targeted_policies[scb->ipsPolicyId]->configPolicyId; scb->stream_config = sfPolicyUserDataGet( stream_online_config, scb->napPolicyId ); } //actions are queued only for IDS case sfActionQueueExecAll(decoderActionQ); } SnortEventqPop(); tcpssn->server.flags |= TF_FORCE_FLUSH; if ( !p->tcph ) { flushed = 0; } else { flushed = flush_stream(tcpssn, &tcpssn->server, p, p->iph_api->iph_ret_src(p), p->iph_api->iph_ret_dst(p), p->tcph->th_sport, p->tcph->th_dport, PKT_FROM_CLIENT); } if (flushed) purge_flushed_ackd(tcpssn, &tcpssn->server); else { SnortEventqPush(); SnortEventqLog(snort_conf->event_queue, p); SnortEventqReset(); SnortEventqPop(); } tcpssn->server.flags &= ~TF_FORCE_FLUSH; } if (!s5_tcp_cleanup) { /* And turn decoder alerts back on (or whatever they were set to) */ policyDecoderFlagsRestore(scb->napPolicyId); } policyChecksumFlagsRestore(scb->napPolicyId); } } static void TcpSessionCleanup(SessionControlBlock *scb, int freeApplicationData) { TcpSession *tcpssn = NULL; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) { /* Huh? */ StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_CLOSED); return; } stream_session_config = scb->session_config; FlushQueuedSegs(scb, tcpssn); TcpSessionClear(scb, tcpssn, freeApplicationData); } static void TcpSessionCleanupWithFreeApplicationData(void *scb) { TcpSessionCleanup( ( SessionControlBlock * ) scb, 1); } struct session_state_cleanup_cache { uint32_t old_mem_in_use; sfaddr_t client_ip; sfaddr_t server_ip; uint16_t client_port; uint16_t server_port; uint16_t lw_session_state; uint32_t lw_session_flags; int16_t app_proto_id; }; static inline void cleanup_cache_session_state( SessionControlBlock *scb, struct session_state_cleanup_cache *sscc ) { sscc->old_mem_in_use = session_mem_in_use; sscc->client_port = scb->client_port; sscc->server_port = scb->server_port; sscc->lw_session_state = scb->session_state; sscc->lw_session_flags = scb->ha_state.session_flags; #ifdef TARGET_BASED sscc->app_proto_id = scb->ha_state.application_protocol; #else sscc->app_proto_id = 0; #endif sfip_set_ip(&sscc->client_ip, &scb->client_ip); sfip_set_ip(&sscc->server_ip, &scb->server_ip); } static inline void cleanup_log_session_state( char *delete_reason, struct session_state_cleanup_cache *sscc ) { if (stream_session_config->prune_log_max && (sscc->old_mem_in_use - session_mem_in_use) > stream_session_config->prune_log_max) { char *client_ip_str, *server_ip_str; client_ip_str = SnortStrdup(inet_ntoa(&sscc->client_ip)); server_ip_str = SnortStrdup(inet_ntoa(&sscc->server_ip)); LogMessage("S5: Pruned session from cache that was " "using %d bytes (%s). %s %d --> %s %d (%d) : " "LWstate 0x%x LWFlags 0x%x\n", sscc->old_mem_in_use - session_mem_in_use, delete_reason, client_ip_str, sscc->client_port, server_ip_str, sscc->server_port, sscc->app_proto_id, sscc->lw_session_state, sscc->lw_session_flags); free(client_ip_str); free(server_ip_str); } } #ifdef SEG_TEST static void CheckSegments (const StreamTracker* a) { StreamSegment* ss = a->seglist; uint32_t sx = ss ? ss->seq : 0; while ( ss ) { if ( SEQ_GT(sx, ss->seq) ) { const int SEGBORK = 0; assert(SEGBORK); } sx = ss->seq + ss->size; ss = ss->next; } } #endif #ifdef REG_TEST #define LCL(p, x) (p->x - p->isn) #define RMT(p, x, q) (p->x - (q ? q->isn : 0)) static int s5_trace_enabled = -1; static void TraceEvent ( const Packet* p, TcpDataBlock* tdb, uint32_t txd, uint32_t rxd ) { int i; char flags[7] = "UAPRSF"; const TCPHdr* h = p->tcph; const char* order = ""; if ( !h ) return; for ( i = 0; i < 6; i++) if ( !((1<<(5-i)) & h->th_flags) ) flags[i] = '-'; // force relative ack to zero if not conveyed if ( flags[1] != 'A' ) rxd = ntohl(h->th_ack); if ( p->packet_flags & PKT_STREAM_ORDER_OK ) order = " (ins)"; else if ( p->packet_flags & PKT_STREAM_ORDER_BAD ) order = " (oos)"; fprintf(stdout, "\n" FMTu64("-3") " %s=0x%02x Seq=%-4u Ack=%-4u Win=%-4u Len=%-4u%s\n", //"\n" FMTu64("-3") " %s=0x%02x Seq=%-4u Ack=%-4u Win=%-4u Len=%-4u End=%-4u%s\n", pc.total_from_daq, flags, h->th_flags, ntohl(h->th_seq)-txd, ntohl(h->th_ack)-rxd, ntohs(h->th_win), p->dsize, order //ntohs(h->th_win), p->dsize, tdb->end_seq-txd, order ); } static void TraceSession (const SessionControlBlock* lws) { fprintf(stdout, " LWS: ST=0x%x SF=0x%x CP=%u SP=%u\n", (unsigned)lws->session_state, lws->ha_state.session_flags, (unsigned)ntohs(lws->client_port), (unsigned)ntohs(lws->server_port)); } static const char* statext[] = { "NON", "LST", "SYR", "SYS", "EST", "CLW", "LAK", "FW1", "CLG", "FW2", "TWT", "CLD" }; static const char* flushxt[] = { "NON", "FPR", "LOG", "RSP", "SLW", #if 0 "CON", #endif "IGN", "PRO", #ifdef NORMALIZER "PRE", "PAF" #endif }; static void TraceSegments (const StreamTracker* a) { StreamSegment* ss = a->seglist; uint32_t sx = a->r_win_base; unsigned segs = 0, bytes = 0; while ( ss ) { if ( SEQ_LT(sx, ss->seq) ) fprintf(stdout, " +%u", ss->seq-sx); else if ( SEQ_GT(sx, ss->seq) ) fprintf(stdout, " -%u", sx-ss->seq); fprintf(stdout, " %u", ss->size); segs++; bytes += ss->size; sx = ss->seq + ss->size; ss = ss->next; } assert(a->seg_count == segs); assert(a->seg_bytes_logical == bytes); } static void TraceState (const StreamTracker* a, const StreamTracker* b, const char* s) { uint32_t why = a->l_nxt_seq ? LCL(a, l_nxt_seq) : 0; fprintf(stdout, " %s ST=%s:%02x UA=%-4u NS=%-4u LW=%-5u RN=%-4u RW=%-4u ", s, statext[a->s_mgr.state], a->s_mgr.sub_state, LCL(a, l_unackd), why, a->l_window, RMT(a, r_nxt_ack, b), RMT(a, r_win_base, b) ); if ( a->s_mgr.state_queue ) fprintf(stdout, "QS=%s QC=0x%02x QA=%-4u", statext[a->s_mgr.state_queue], a->s_mgr.expected_flags, RMT(a, s_mgr.transition_seq, b) ); fprintf(stdout, "\n"); fprintf(stdout, " FP=%s:%-4u SC=%-4u FL=%-4u SL=%-5u BS=%-4u", flushxt[a->flush_mgr.flush_policy], a->flush_mgr.flush_pt, a->seg_count, a->flush_count, a->seg_bytes_logical, a->seglist_base_seq - b->isn ); if ( s5_trace_enabled == 2 ) TraceSegments(a); fprintf(stdout, "\n"); } static void TraceTCP ( const Packet* p, const SessionControlBlock* lws, TcpDataBlock* tdb, int event ) { const TcpSession* ssn = (lws && lws->proto_specific_data) ? (TcpSession*)lws->proto_specific_data->data : NULL; const StreamTracker* srv = ssn ? &ssn->server : NULL; const StreamTracker* cli = ssn ? &ssn->client : NULL; const char* cdir = "?", *sdir = "?"; uint32_t txd = 0, rxd = 0; if ( p->packet_flags & PKT_FROM_SERVER ) { sdir = "SRV>"; cdir = "CLI<"; if ( srv ) txd = srv->isn; if ( cli ) rxd = cli->isn; } else if ( p->packet_flags & PKT_FROM_CLIENT ) { sdir = "SRV<"; cdir = "CLI>"; if ( cli ) txd = cli->isn; if ( srv ) rxd = srv->isn; } TraceEvent(p, tdb, txd, rxd); if ( lws && lws->session_established ) TraceSession(lws); if ( !event ) { if ( cli ) TraceState(cli, srv, cdir); if ( srv ) TraceState(srv, cli, sdir); } } static inline void S5TraceTCP ( const Packet* p, const SessionControlBlock* lws, TcpDataBlock* tdb, int event ) { if ( !s5_trace_enabled ) return; if ( s5_trace_enabled < 0 ) { const char* s5t = getenv("S5_TRACE"); if ( !s5t ) { s5_trace_enabled = 0; return; } // no error checking required - atoi() is sufficient s5_trace_enabled = atoi(s5t); } TraceTCP(p, lws, tdb, event); } #endif // REG_TEST /* * Main entry point for TCP */ int StreamProcessTcp(Packet *p, SessionControlBlock *scb, StreamTcpPolicy *s5TcpPolicy, SessionKey *skey) { TcpDataBlock tdb; SFXHASH_NODE *hash_node = NULL; int rc, status; PROFILE_VARS; #if defined(DAQ_CAPA_CST_TIMEOUT) uint64_t timeout; #endif STREAM_DEBUG_WRAP( char flagbuf[9]; CreateTCPFlagString(p, flagbuf); DebugMessage((DEBUG_STREAM|DEBUG_STREAM_STATE), "Got TCP Packet 0x%X:%d -> 0x%X:%d %s\nseq: 0x%X ack:0x%X " "dsize: %u\n" "active sessions: %u\n", GET_SRC_IP(p), p->sp, GET_DST_IP(p), p->dp, flagbuf, ntohl(p->tcph->th_seq), ntohl(p->tcph->th_ack), p->dsize, sfxhash_count(tcp_lws_cache->hashTable)); ); PREPROC_PROFILE_START(s5TcpPerfStats); if( scb->ha_state.session_flags & ( SSNFLAG_DROP_CLIENT | SSNFLAG_DROP_SERVER ) ) { /* Got a packet on a session that was dropped (by a rule). */ session_api->set_packet_direction_flag(p, scb); /* Drop this packet */ if( ( ( p->packet_flags & PKT_FROM_SERVER) && ( scb->ha_state.session_flags & SSNFLAG_DROP_SERVER ) ) || ( ( p->packet_flags & PKT_FROM_CLIENT ) && ( scb->ha_state.session_flags & SSNFLAG_DROP_CLIENT ) ) ) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Blocking %s packet as session was blocked\n", p->packet_flags & PKT_FROM_SERVER ? "server" : "client");); DisableAllDetect( p ); if( scb->ha_state.session_flags & SSNFLAG_FORCE_BLOCK ) Active_ForceDropSessionWithoutReset(); else Active_DropSessionWithoutReset(p); #ifdef ACTIVE_RESPONSE StreamActiveResponse(p, scb); #endif if (pkt_trace_enabled) addPktTraceData(VERDICT_REASON_STREAM, snprintf(trace_line, MAX_TRACE_LINE, "Stream: TCP session was already blocked, %s\n", getPktTraceActMsg())); else addPktTraceData(VERDICT_REASON_STREAM, 0); PREPROC_PROFILE_END(s5TcpPerfStats); return ACTION_NOTHING; } } if( s5TcpPolicy == NULL ) { /* Find an Tcp policy for this packet */ s5TcpPolicy = StreamPolicyLookup( scb, GET_DST_IP( p ) ); if( !s5TcpPolicy ) { STREAM_DEBUG_WRAP( DebugMessage( DEBUG_STREAM, "[Stream] Could not find Tcp Policy context " "for IP %s\n", inet_ntoa( GET_DST_ADDR( p ) ) ); ); PREPROC_PROFILE_END( s5TcpPerfStats ); return 0; } #if defined(DAQ_CAPA_CST_TIMEOUT) if (Daq_Capa_Timeout) { GetTimeout(p,&timeout); s5TcpPolicy->session_timeout = timeout + STREAM_DELAY_TIMEOUT_AFTER_CONNECTION_ENDED; } #endif } rc = isPacketFilterDiscard( p, ( ( ( StreamConfig * ) scb->stream_config )->tcp_config->default_policy->flags & STREAM_CONFIG_IGNORE_ANY ) ); if( rc == PORT_MONITOR_PACKET_DISCARD ) { //ignore the packet STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "[Stream] %s:%d -> %s:%d Packet discarded due to port filtering\n", inet_ntoa(GET_SRC_ADDR(p)),p->sp,inet_ntoa(GET_DST_ADDR(p)),p->dp);); UpdateFilteredPacketStats(&sfBase, IPPROTO_TCP); PREPROC_PROFILE_END(s5TcpPerfStats); return 0; } SetupTcpDataBlock( &tdb, p ); #ifdef STREAM_DEBUG_ENABLED PrintTcpDataBlock( &tdb ); #endif if( !scb->session_established ) { if( s5TcpPolicy->flags & STREAM_CONFIG_REQUIRE_3WHS ) { /* if require 3WHS, set session state to SYN rx'ed if we got one */ if( TCP_ISFLAGSET( p->tcph, TH_SYN ) && !TCP_ISFLAGSET( p->tcph, TH_ACK ) ) { /* SYN only */ scb->session_state = STREAM_STATE_SYN; scb->session_established = true; scb->proto_policy = s5TcpPolicy; s5stats.total_tcp_sessions++; s5stats.active_tcp_sessions++; } else { /* If we're within the "startup" window, try to handle * this packet as midstream pickup -- allows for * connections that already existed before snort started. */ if (p->pkth->ts.tv_sec - firstPacketTime < s5TcpPolicy->hs_timeout) { midstream_allowed = 1; goto midstream_pickup_allowed; } /* * Do nothing with this packet since we require a 3-way. * Wow that just sounds cool... Require a 3-way. Hehe. */ DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "Stream: Requiring 3-way " "Handshake, but failed to retrieve session object " "for non SYN packet.\n");); midstream_allowed = 0; EventNo3whs(s5TcpPolicy); PREPROC_PROFILE_END(s5TcpPerfStats); #ifdef REG_TEST S5TraceTCP(p, scb, &tdb, 1); #endif return 0; } } else { midstream_pickup_allowed: if (TCP_ISFLAGSET(p->tcph, (TH_SYN|TH_ACK))) { /* If we have a SYN/ACK */ scb->session_established = true; scb->proto_policy = s5TcpPolicy; s5stats.total_tcp_sessions++; s5stats.active_tcp_sessions++; } else if (p->dsize > 0) { /* If we have data -- missed the SYN/ACK * somehow -- maybe just an incomplete PCAP. * This handles data on SYN situations */ scb->session_established = true; scb->proto_policy = s5TcpPolicy; s5stats.total_tcp_sessions++; s5stats.active_tcp_sessions++; } else if( ( ( ( ( StreamConfig * ) scb->stream_config )->tcp_config->session_on_syn || ( StreamPacketHasWscale(p) & TF_WSCALE ) ) && TCP_ISFLAGSET(p->tcph, TH_SYN) ) || StreamExpectIsExpected( p, &hash_node ) ) { /* If we have a wscale option or this is an expected connection, need to save the * option if its the first SYN from client or continue to get the expected session * data. */ scb->session_state = STREAM_STATE_SYN; scb->session_established = true; scb->proto_policy = s5TcpPolicy; s5stats.total_tcp_sessions++; s5stats.active_tcp_sessions++; } else { /* No data, no need to create session yet */ /* This is done to handle SYN flood DoS attacks */ #ifdef STREAM_DEBUG_ENABLED if (TCP_ISFLAGSET(p->tcph, TH_SYN)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: no data in packet (SYN only), no need to" "create lightweight session.\n");); } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: no data in packet (non SYN/keep alive " "ACK?), no need to create lightweight session.\n");); } #endif if(TCP_ISFLAGSET(p->tcph, TH_SYN)) scb->session_state |= STREAM_STATE_SYN; PREPROC_PROFILE_END(s5TcpPerfStats); #ifdef REG_TEST S5TraceTCP(p, scb, &tdb, 1); #endif return 0; } } } p->ssnptr = scb; /* * Check if the session is expired. * Should be done before we do something with the packet... * ie, Insert a packet, or handle state change SYN, FIN, RST, etc. */ if( ( scb->session_state & STREAM_STATE_TIMEDOUT) || StreamExpire( p, scb ) ) { scb->ha_state.session_flags |= SSNFLAG_TIMEDOUT; #ifdef ENABLE_HA /* Notify the HA peer of the session cleanup/reset by way of a deletion notification. */ PREPROC_PROFILE_TMPEND(s5TcpPerfStats); SessionHANotifyDeletion(scb); PREPROC_PROFILE_TMPSTART(s5TcpPerfStats); scb->ha_flags = HA_FLAG_NEW | HA_FLAG_MODIFIED; #endif /* If this one has been reset, delete the TCP portion, and start a new. */ if( scb->ha_state.session_flags & SSNFLAG_RESET ) { struct session_state_cleanup_cache sscc; cleanup_cache_session_state( scb, &sscc ); TcpSessionCleanupWithFreeApplicationData(scb); cleanup_log_session_state( "new data/reset", &sscc ); status = ProcessTcp( scb, p, &tdb, s5TcpPolicy, hash_node ); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Finished Stream TCP cleanly!\n" "---------------------------------------------------\n");); } else { /* Not reset, simply time'd out. Clean it up */ struct session_state_cleanup_cache sscc; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream TCP session timedout!\n");); cleanup_cache_session_state( scb, &sscc ); TcpSessionCleanupWithFreeApplicationData( scb ); cleanup_log_session_state( "new data/timedout", &sscc ); status = ProcessTcp(scb, p, &tdb, s5TcpPolicy, hash_node); } } else { status = ProcessTcp( scb, p, &tdb, s5TcpPolicy, hash_node ); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Finished Stream TCP cleanly!\n" "---------------------------------------------------\n");); } if( !( status & ACTION_LWSSN_CLOSED ) ) { MarkupPacketFlags( p, scb ); /* Receiving valid RST for a ongoing/hanged tcp session, * override configured session timeout to non-configurable * lower timeout of 180 seconds to clean up the session. */ if((status & ACTION_RST) && (scb->expire_time - (((uint64_t)p->pkth->ts.tv_sec ) * TCP_HZ)) > (STREAM_SSN_RST_TIMEOUT * TCP_HZ)) { session_api->set_expire_timer(p, scb, STREAM_SSN_RST_TIMEOUT); } else { session_api->set_expire_timer( p, scb, s5TcpPolicy->session_timeout ); } } #ifdef ENABLE_HA else { /* TCP Session closed, so send an HA deletion event for the session. */ SessionHANotifyDeletion( scb ); } #endif if( status & ACTION_DISABLE_INSPECTION ) { session_api->disable_inspection( scb, p ); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream Ignoring packet from %d. Session marked as ignore\n", p->packet_flags & PKT_FROM_SERVER? "server" : "client");); } PREPROC_PROFILE_END(s5TcpPerfStats); #ifdef REG_TEST S5TraceTCP( p, scb, &tdb, 0 ); #endif return 0; } static uint32_t StreamGetTcpTimestamp( Packet *p, uint32_t *ts, int strip ) { unsigned int i = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Getting timestamp...\n");); while(i < p->tcp_option_count && i < TCP_OPTLENMAX) { if(p->tcp_options[i].code == TCPOPT_TIMESTAMP) { #ifdef NORMALIZER if ( strip && Normalize_GetMode(snort_conf, NORM_TCP_OPT) == NORM_MODE_ON ) { NormalStripTimeStamp(p, i); } else if ( !strip || !NormalStripTimeStamp(p, i) ) #endif { *ts = EXTRACT_32BITS(p->tcp_options[i].data); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Found timestamp %lu\n", *ts);); return TF_TSTAMP; } } i++; } *ts = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "No timestamp...\n");); return TF_NONE; } static uint32_t StreamGetMss(Packet *p, uint16_t *value) { unsigned int i = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Getting MSS...\n");); while(i < p->tcp_option_count && i < TCP_OPTLENMAX) { if(p->tcp_options[i].code == TCPOPT_MAXSEG) { *value = EXTRACT_16BITS(p->tcp_options[i].data); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Found MSS %u\n", *value);); return TF_MSS; } i++; } *value = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "No MSS...\n");); return TF_NONE; } static uint32_t StreamGetWscale(Packet *p, uint16_t *value) { unsigned int i = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Getting wscale...\n");); while(i < p->tcp_option_count && i < TCP_OPTLENMAX) { if(p->tcp_options[i].code == TCPOPT_WSCALE) { *value = (uint16_t) p->tcp_options[i].data[0]; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Found wscale %d\n", *value);); /* If scale specified in option is larger than 14, * use 14 because of limitation in the math of * shifting a 32bit value (max scaled window is 2^30th). * * See RFC 1323 for details. */ if (*value > 14) { *value = 14; } return TF_WSCALE; } i++; } *value = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "No wscale...\n");); return TF_NONE; } static uint32_t StreamPacketHasWscale(Packet *p) { uint16_t wscale; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Checking for wscale...\n");); return StreamGetWscale(p, &wscale); } static inline int IsWellFormed(Packet *p, StreamTracker *ts) { return ( !ts->mss || (p->dsize <= ts->mss) ); } static void FinishServerInit(Packet *p, TcpDataBlock *tdb, TcpSession *ssn) { StreamTracker *server; StreamTracker *client; if (!ssn) { return; } server = &ssn->server; client = &ssn->client; server->l_window = tdb->win; /* set initial server window */ server->l_unackd = tdb->end_seq; server->l_nxt_seq = server->l_unackd; server->isn = tdb->seq; client->r_nxt_ack = tdb->seq + 1; if ( p->tcph->th_flags & TH_FIN ) server->l_nxt_seq--; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", client->seglist_base_seq);); if (!(ssn->scb->session_state & STREAM_STATE_MIDSTREAM)) { server->s_mgr.state = TCP_STATE_SYN_RCVD; client->seglist_base_seq = server->l_unackd; if ( tdb->seq == tdb->end_seq ) client->r_win_base = tdb->end_seq; else client->r_win_base = tdb->seq + 1; } else { client->seglist_base_seq = tdb->seq; client->r_win_base = tdb->seq; } server->flags |= StreamGetTcpTimestamp(p, &server->ts_last, 0); if (server->ts_last == 0) server->flags |= TF_TSTAMP_ZERO; else server->ts_last_pkt = p->pkth->ts.tv_sec; server->flags |= StreamGetMss(p, &server->mss); server->flags |= StreamGetWscale(p, &server->wscale); #ifdef STREAM_DEBUG_ENABLED PrintTcpSession(ssn); #endif } #ifdef OLD_CODE_NOLONGER_USED_DEPENDS_ON_CURRENT_STATE static inline void QueueState(uint8_t transition, StreamTracker *st, uint8_t expected_flags, uint32_t seq_num, uint8_t get_seq) { StateMgr *smgr = &st->s_mgr; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "[^^] Queing transition to %s, flag 0x%X, seq: 0x%X\n", state_names[transition], expected_flags, seq_num);); smgr->state_queue = transition; smgr->expected_flags = expected_flags; smgr->stq_get_seq = get_seq; smgr->transition_seq = seq_num; #ifdef STREAM_DEBUG_ENABLED PrintStateMgr(smgr); #endif return; } static inline int EvalStateQueue(StreamTracker *sptr, uint8_t flags, uint32_t ack) { StateMgr *smgr = &sptr->s_mgr; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Evaluating state queue!\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "StreamTracker %p, flags 0x%X ack: 0x%X\n", sptr, flags, ack); PrintStateMgr(smgr);); if(smgr->expected_flags != 0) { if((flags & smgr->expected_flags) != 0) { if(smgr->stq_get_seq && (SEQ_GEQ(ack, smgr->transition_seq))) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "[^^] Accepting %s state transition\n", state_names[smgr->state_queue]);); smgr->state = smgr->state_queue; smgr->expected_flags = 0; smgr->transition_seq = 0; return 1; } else if(!smgr->stq_get_seq) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "[^^] Accepting %s state transition\n", state_names[smgr->state_queue]);); smgr->state = smgr->state_queue; smgr->expected_flags = 0; smgr->transition_seq = 0; return 1; } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "[!!] sptr->stq_get_seq: %d " "[ack: 0x%X expected: 0x%X]\n", smgr->stq_get_seq, ack, smgr->transition_seq);); } } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "[!!] flags: 0x%X expected: 0x%X, bitwise: 0x%X\n", flags, smgr->expected_flags, (flags & smgr->expected_flags));); } } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "No transition queued, returning\n");); } return 0; } #endif static inline int IgnoreLargePkt(StreamTracker *st, Packet *p, TcpDataBlock *tdb) { if((st->flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT) && (st->tcp_policy->flags & STREAM_CONFIG_PERFORMANCE)) { if ((p->dsize > st->flush_mgr.flush_pt * 2) && (st->seg_count == 0)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "WARNING: Data larger than twice flushpoint. Not " "inserting for reassembly: seq: %d, size %d!\n" "This is a tradeoff of performance versus the remote " "possibility of catching an exploit that spans two or " "more consecuvitve large packets.\n", tdb->seq, p->dsize);); return 1; } } return 0; } static void NewQueue(StreamTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn) { StreamSegment *ss = NULL; uint32_t overlap = 0; PROFILE_VARS; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In NewQueue\n");); PREPROC_PROFILE_START(s5TcpInsertPerfStats); if(st->flush_mgr.flush_policy != STREAM_FLPOLICY_IGNORE) { uint32_t seq = tdb->seq; /* Check if we should not insert a large packet */ if (IgnoreLargePkt(st, p, tdb)) { return; } if ( p->tcph->th_flags & TH_SYN ) seq++; /* new packet seq is below the last ack... */ if ( SEQ_GT(st->r_win_base, seq) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "segment overlaps ack'd data...\n");); overlap = st->r_win_base - tdb->seq; if (overlap >= p->dsize) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "full overlap on ack'd data, dropping segment\n");); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return; } } AddStreamNode(st, p, tdb, tcpssn, p->dsize, overlap, 0, tdb->seq+overlap, NULL, &ss); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Attached new queue to seglist, %d bytes queued, " "base_seq 0x%X\n", ss->size, st->seglist_base_seq);); } PREPROC_PROFILE_END(s5TcpInsertPerfStats); return; } #if 0 static inline StreamSegment *FindSegment(StreamTracker *st, uint32_t pkt_seq) { int32_t dist_head; int32_t dist_tail; StreamSegment *ss; if (!st->seglist) return NULL; dist_head = pkt_seq - st->seglist->seq; dist_tail = pkt_seq - st->seglist_tail->seq; if (dist_head <= dist_tail) { /* Start iterating at the head (left) */ for (ss = st->seglist; ss; ss = ss->next) { if (SEQ_EQ(ss->seq, pkt_seq)) return ss; if (SEQ_GEQ(ss->seq, pkt_seq)) break; } } else { /* Start iterating at the tail (right) */ for (ss = st->seglist_tail; ss; ss = ss->prev) { if (SEQ_EQ(ss->seq, pkt_seq)) return ss; if (SEQ_LT(ss->seq, pkt_seq)) break; } } return NULL; } #endif void StreamTcpSessionClear(Packet *p) { SessionControlBlock *scb; TcpSession *ssn; if ((!p) || (!p->ssnptr)) return; scb = (SessionControlBlock *)p->ssnptr; if (!scb->proto_specific_data) return; ssn = (TcpSession *)scb->proto_specific_data->data; if (!ssn) return; TcpSessionClear(scb, ssn, 1); } static inline int SegmentFastTrack(StreamSegment *tail, TcpDataBlock *tdb) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Checking seq for fast track: %X > %X\n", tdb->seq, tail->seq + tail->size);); if(SEQ_EQ(tdb->seq, tail->seq + tail->size)) return 1; return 0; } static inline StreamSegment* SegmentAlloc ( Packet* p, const struct timeval* tv, uint32_t caplen, uint32_t pktlen, const uint8_t* pkt) { StreamSegment* ss; unsigned size = sizeof(*ss); if ( caplen > 0 ) size += caplen - 1; // ss contains 1st byte session_mem_in_use += size; if (session_mem_in_use > stream_session_config->memcap) { pc.str_mem_faults++; sfBase.iStreamFaults++; if ( !p ) { session_mem_in_use -= size; return NULL; } /* Smack the older time'd out sessions */ if ( !session_api->prune_session_cache( tcp_lws_cache, p->pkth->ts.tv_sec, ( SessionControlBlock * ) p->ssnptr, 0 ) ) { /* Try the memcap - last parameter (1) specifies check * based on memory cap. */ session_api->prune_session_cache(tcp_lws_cache, 0, (SessionControlBlock*) p->ssnptr, 1); } } if (session_mem_in_use > stream_session_config->memcap) { session_mem_in_use -= size; return NULL; } ss = SnortPreprocAlloc(1, size, PP_STREAM, PP_MEM_CATEGORY_SESSION); ss->tv.tv_sec = tv->tv_sec; ss->tv.tv_usec = tv->tv_usec; ss->caplen = caplen; ss->pktlen = pktlen; memcpy(ss->pkt, pkt, caplen); return ss; } static int AddStreamNode(StreamTracker *st, Packet *p, TcpDataBlock* tdb, TcpSession *tcpssn, uint16_t len, uint32_t slide, uint32_t trunc, uint32_t seq, StreamSegment *left, StreamSegment **retSeg) { StreamSegment *ss = NULL; uint16_t reassembly_policy; int32_t newSize = len - slide - trunc; reassembly_policy = st->reassembly_policy; if(st->s_mgr.state_queue == TCP_STATE_CLOSE_WAIT) { //If FIN is already queued, when adding a segment, take target-based approach switch(reassembly_policy) { case REASSEMBLY_POLICY_LAST: st->s_mgr.state_queue = TCP_STATE_NONE; break; case REASSEMBLY_POLICY_FIRST: default: if(SEQ_GT(seq + newSize, st->s_mgr.transition_seq - 1)) { uint32_t delta = seq + newSize - (st->s_mgr.transition_seq - 1); newSize = newSize - delta; } break; } } if (newSize <= 0) { /* * zero size data because of trimming. Don't * insert it */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "zero size TCP data after left & right trimming " "(len: %d slide: %d trunc: %d)\n", len, slide, trunc);); Discard(); NormalTrimPayloadIfWin(p, 0, tdb); #ifdef STREAM_DEBUG_ENABLED { StreamSegment *idx = st->seglist; unsigned long i = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Dumping seglist, %d segments\n", st->seg_count);); while (idx) { i++; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "%d ptr: %p seq: 0x%X size: %d nxt: %p prv: %p\n", i, idx, idx->seq, idx->size, idx->next, idx->prev);); if(st->seg_count < i) FatalError("Circular list, WTF?\n"); idx = idx->next; } } #endif return STREAM_INSERT_ANOMALY; } ss = SegmentAlloc(p, &p->pkth->ts, p->pkth->caplen, p->pkth->pktlen, p->pkt); if (!ss) return STREAM_INSERT_ANOMALY; ss->data = ss->pkt + (p->data - p->pkt); ss->orig_dsize = p->dsize; ss->payload = ss->data + slide; ss->size = (uint16_t)newSize; ss->seq = seq; ss->ts = tdb->ts; /* handle the urg ptr */ if(p->tcph->th_flags & TH_URG) { if(ntohs(p->tcph->th_urp) < p->dsize) { switch(st->os_policy) { case STREAM_POLICY_LINUX: case STREAM_POLICY_OLD_LINUX: /* Linux, Old linux discard data from urgent pointer */ /* If urg pointer is 0, it's treated as a 1 */ ss->urg_offset = ntohs(p->tcph->th_urp); if (ss->urg_offset == 0) { ss->urg_offset = 1; } break; case STREAM_POLICY_FIRST: case STREAM_POLICY_NOACK: case STREAM_POLICY_LAST: case STREAM_POLICY_BSD: case STREAM_POLICY_MACOS: case STREAM_POLICY_SOLARIS: case STREAM_POLICY_WINDOWS: case STREAM_POLICY_WINDOWS2K3: case STREAM_POLICY_VISTA: case STREAM_POLICY_HPUX11: case STREAM_POLICY_HPUX10: case STREAM_POLICY_IRIX: /* Others discard data from urgent pointer */ /* If urg pointer is beyond this packet, it's treated as a 0 */ ss->urg_offset = ntohs(p->tcph->th_urp); if (ss->urg_offset > p->dsize) { ss->urg_offset = 0; } break; } } } StreamSeglistAddNode(st, left, ss); st->seg_bytes_logical += ss->size; st->seg_bytes_total += ss->caplen; /* Includes protocol headers and payload */ st->total_segs_queued++; st->total_bytes_queued += ss->size; p->packet_flags |= PKT_STREAM_INSERT; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "added %d bytes on segment list @ seq: 0x%X, total %lu, " "%d segments queued\n", ss->size, ss->seq, st->seg_bytes_logical, SegsToFlush(st, 0));); *retSeg = ss; #ifdef SEG_TEST CheckSegments(st); #endif return STREAM_INSERT_OK; } static int DupStreamNode(Packet *p, StreamTracker *st, StreamSegment *left, StreamSegment **retSeg) { StreamSegment* ss = SegmentAlloc(p, &left->tv, left->caplen, left->pktlen, left->pkt); if ( !ss ) return STREAM_INSERT_FAILED; ss->data = ss->pkt + (left->data - left->pkt); ss->orig_dsize = left->orig_dsize; /* twiddle the values for overlaps */ ss->payload = ss->data; ss->size = left->size; ss->seq = left->seq; StreamSeglistAddNode(st, left, ss); st->seg_bytes_total += ss->caplen; st->total_segs_queued++; //st->total_bytes_queued += ss->size; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "added %d bytes on segment list @ seq: 0x%X, total %lu, " "%d segments queued\n", ss->size, ss->seq, st->seg_bytes_logical, SegsToFlush(st, 0));); *retSeg = ss; return STREAM_INSERT_OK; } static inline bool IsRetransmit(StreamSegment *seg, const uint8_t *rdata, uint16_t rsize, uint32_t rseq, bool check_frt, bool *full_retransmit) { // If seg->orig_size == seg->size, then it's sequence number wasn't adjusted // so can just do a straight compare of the sequence numbers. // Don't want to count as a retransmit if segment's size/sequence number // has been adjusted. *full_retransmit = false; if (SEQ_EQ(seg->seq, rseq) && (seg->orig_dsize == seg->size)) { if (((seg->size <= rsize) && (memcmp(seg->data, rdata, seg->size) == 0)) || ((seg->size > rsize) && (memcmp(seg->data, rdata, rsize) == 0))) return true; } //Checking for a possible split of segment in which case //we compare complete data of the segment to find a retransmission else if(check_frt && SEQ_EQ(seg->seq, rseq) && seg->orig_dsize == rsize ) { if(memcmp(seg->data, rdata, rsize) == 0) { *full_retransmit = true; return true; } } return false; } static inline void RetransmitProcess(Packet *p, TcpSession *tcpssn) { // Data has already been analyzed so don't bother looking at it again. DisableDetect( p ); DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Allowing retransmitted data " "-- not blocked previously\n");); } static inline void RetransmitHandle(Packet *p, TcpSession *tcpssn) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Calling SE_REXMIT Handler\n");); if ( tcpssn->scb->handler[SE_REXMIT] ) StreamCallHandler(p, tcpssn->scb->handler[SE_REXMIT]); } static inline void EndOfFileHandle(Packet *p, TcpSession *tcpssn) { DEBUG_WRAP(DebugMessage(DEBUG_STREAM, "Calling SE_EOF Handler\n");); if ( tcpssn->scb->handler[SE_EOF] ) StreamCallHandler(p, tcpssn->scb->handler[SE_EOF]); } static inline bool IsRetransmitOfHeldSegment(Packet *p, TcpDataBlock *tdb, StreamTracker *listener) { StreamSegment *held_seg = listener->held_segment; if (held_seg == NULL) return FALSE; if (held_seg->seq == tdb->seq) return TRUE; return FALSE; } static bool IsTCPFastOpenPkt (Packet *p) { uint8_t opt_count = 0; while (opt_count < p->tcp_option_count) { if (p->tcp_options[opt_count].code == TCPOPT_TFO) { return TRUE; } opt_count++; } return FALSE; } static int StreamQueue(StreamTracker *st, Packet *p, TcpDataBlock *tdb, TcpSession *tcpssn) { StreamSegment *ss = NULL; StreamSegment *left = NULL; StreamSegment *right = NULL; StreamSegment *dump_me = NULL; uint32_t seq = tdb->seq; uint32_t seq_end = tdb->end_seq; uint16_t len = p->dsize; int trunc = 0; int overlap = 0; int slide = 0; int ret = STREAM_INSERT_OK; char done = 0; char addthis = 1; int32_t dist_head; int32_t dist_tail; uint16_t reassembly_policy; #ifdef NORMALIZER NormMode ips_data; #endif // To check for retransmitted data const uint8_t *rdata = p->data; uint16_t rsize = p->dsize; uint32_t rseq = tdb->seq; PROFILE_VARS; STREAM_DEBUG_WRAP( StreamSegment *lastptr = NULL; uint32_t base_seq = st->seglist_base_seq; int last = 0; ); #ifdef NORMALIZER ips_data = Stream_NormGetMode(st->reassembly_policy, snort_conf, NORM_TCP_IPS); if ( ips_data == NORM_MODE_ON ) reassembly_policy = REASSEMBLY_POLICY_FIRST; else #endif reassembly_policy = st->reassembly_policy; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Queuing %d bytes on stream!\n" "base_seq: %X seq: %X seq_end: %X\n", seq_end - seq, base_seq, seq, seq_end);); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "%d segments on seglist\n", SegsToFlush(st, 0));); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+\n");); PREPROC_PROFILE_START(s5TcpInsertPerfStats); /* Check if we should not insert a large packet */ if (IgnoreLargePkt(st, p, tdb)) { // NORM should ignore large pkt be disabled for stream normalization? // if not, how to normalize ignored large packets? return ret; } // NORM fast tracks are in sequence - no norms if(st->seglist_tail && SegmentFastTrack(st->seglist_tail, tdb)) { /* segment fit cleanly at the end of the segment list */ left = st->seglist_tail; right = NULL; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Fast tracking segment! (tail_seq %X size %d)\n", st->seglist_tail->seq, st->seglist_tail->size);); ret = AddStreamNode(st, p, tdb, tcpssn, len, slide /* 0 */, trunc /* 0 */, seq, left /* tail */, &ss); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return ret; } if (st->seglist && st->seglist_tail) { if (SEQ_GT(tdb->seq, st->seglist->seq)) { dist_head = tdb->seq - st->seglist->seq; } else { dist_head = st->seglist->seq - tdb->seq; } if (SEQ_GT(tdb->seq, st->seglist_tail->seq)) { dist_tail = tdb->seq - st->seglist_tail->seq; } else { dist_tail = st->seglist_tail->seq - tdb->seq; } } else { dist_head = dist_tail = 0; } if (SEQ_LEQ(dist_head, dist_tail)) { /* Start iterating at the head (left) */ for(ss = st->seglist; ss; ss = ss->next) { STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "ss: %p seq: 0x%X size: %lu delta: %d\n", ss, ss->seq, ss->size, (ss->seq-base_seq) - last); last = ss->seq-base_seq; lastptr = ss; DebugMessage(DEBUG_STREAM_STATE, " lastptr: %p ss->next: %p ss->prev: %p\n", lastptr, ss->next, ss->prev); ); right = ss; if(SEQ_GEQ(right->seq, seq)) break; left = right; } if(ss == NULL) right = NULL; } else { /* Start iterating at the tail (right) */ for(ss = st->seglist_tail; ss; ss = ss->prev) { STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "ss: %p seq: 0x%X size: %lu delta: %d\n", ss, ss->seq, ss->size, (ss->seq-base_seq) - last); last = ss->seq-base_seq; lastptr = ss; DebugMessage(DEBUG_STREAM_STATE, " lastptr: %p ss->next: %p ss->prev: %p\n", lastptr, ss->next, ss->prev); ); left = ss; if(SEQ_LT(left->seq, seq)) break; right = left; } if(ss == NULL) left = NULL; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+!+\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "left: %p:0x%X right: %p:0x%X\n", left, left?left->seq:0, right, right?right->seq:0);); /* * handle left overlaps */ if(left) { // NOTE that left->seq is always less than seq, otherwise it would // be a right based on the above determination of left and right /* check if the new segment overlaps on the left side */ overlap = left->seq + left->size - seq; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "left overlap %d\n", overlap);); if(overlap > 0) { // NOTE that overlap will always be less than left->size since // seq is always greater than left->seq s5stats.tcp_overlaps++; st->overlap_count++; switch(reassembly_policy) { case REASSEMBLY_POLICY_FIRST: case REASSEMBLY_POLICY_NOACK: case REASSEMBLY_POLICY_LINUX: case REASSEMBLY_POLICY_BSD: case REASSEMBLY_POLICY_WINDOWS: case REASSEMBLY_POLICY_WINDOWS2K3: case REASSEMBLY_POLICY_VISTA: case REASSEMBLY_POLICY_HPUX10: case REASSEMBLY_POLICY_IRIX: case REASSEMBLY_POLICY_OLD_LINUX: case REASSEMBLY_POLICY_MACOS: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "left overlap, honoring old data\n");); #ifdef NORMALIZER if ( ips_data != NORM_MODE_OFF ) { if (SEQ_LT(left->seq,tdb->seq) && SEQ_GT(left->seq + left->size, tdb->seq + p->dsize)) { if ( ips_data == NORM_MODE_ON ) { unsigned offset = tdb->seq - left->seq; memcpy((uint8_t*)p->data, left->payload+offset, p->dsize); p->packet_flags |= PKT_MODIFIED; } normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; } else if (SEQ_LT(left->seq, tdb->seq)) { if ( ips_data == NORM_MODE_ON ) { unsigned offset = tdb->seq - left->seq; unsigned length = left->seq + left->size - tdb->seq; memcpy((uint8_t*)p->data, left->payload+offset, length); p->packet_flags |= PKT_MODIFIED; } normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; } } #endif seq += overlap; slide = overlap; if(SEQ_LEQ(seq_end, seq)) { /* * houston, we have a problem */ /* flag an anomaly */ EventBadSegment(st->tcp_policy); Discard(); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return STREAM_INSERT_ANOMALY; } break; case REASSEMBLY_POLICY_SOLARIS: case REASSEMBLY_POLICY_HPUX11: if (SEQ_LT(left->seq, seq) && SEQ_GEQ(left->seq + left->size, seq + len)) { /* New packet is entirely overlapped by an * existing packet on both sides. Drop the * new data. */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "left overlap, honoring old data\n");); seq += overlap; slide = overlap; if(SEQ_LEQ(seq_end, seq)) { /* * houston, we have a problem */ /* flag an anomaly */ EventBadSegment(st->tcp_policy); Discard(); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return STREAM_INSERT_ANOMALY; } } /* Otherwise, trim the old data accordingly */ left->size -= (int16_t)overlap; st->seg_bytes_logical -= overlap; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "left overlap, honoring new data\n");); break; case REASSEMBLY_POLICY_LAST: /* True "Last" policy" */ if (SEQ_LT(left->seq, seq) && SEQ_GT(left->seq + left->size, seq + len)) { /* New data is overlapped on both sides by * existing data. Existing data needs to be * split and the new data inserted in the * middle. * * Need to duplicate left. Adjust that * seq by + (seq + len) and * size by - (seq + len - left->seq). */ ret = DupStreamNode(p, st, left, &right); if (ret != STREAM_INSERT_OK) { /* No warning, * its done in StreamSeglistAddNode */ PREPROC_PROFILE_END(s5TcpInsertPerfStats); return ret; } left->size -= (int16_t)overlap; st->seg_bytes_logical -= overlap; right->seq = seq + len; right->size -= (int16_t)(seq + len - left->seq); right->payload += (seq + len - left->seq); st->seg_bytes_logical -= (seq + len - left->seq); } else { left->size -= (int16_t)overlap; st->seg_bytes_logical -= overlap; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "left overlap, honoring new data\n");); break; } if(SEQ_LEQ(seq_end, seq)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seq_end < seq");); /* * houston, we have a problem */ /* flag an anomaly */ EventBadSegment(st->tcp_policy); Discard(); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return STREAM_INSERT_ANOMALY; } } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "No left overlap\n");); } } //(seq_end > right->seq) && (seq_end <= (right->seq+right->size)))) while(right && !done && SEQ_LT(right->seq, seq_end)) { bool full_retransmit = false; trunc = 0; overlap = (int)(seq_end - right->seq); //overlap = right->size - (right->seq - seq); //right->seq + right->size - seq_end; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "right overlap(%d): len: %d right->seq: 0x%X seq: 0x%X\n", overlap, len, right->seq, seq);); /* Treat sequence number overlap as a retransmission * Only check right side since left side happens rarely */ if((st->flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_IPS_FTP) && (overlap == right->size) && right->next && (right->next->size > 0)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "ftp-data daq retry or rexmit, do not call RetransmitHandle as \ data beyond this packet is already received\n");); } else { RetransmitHandle(p, tcpssn); } if(overlap < right->size) { if (IsRetransmit(right, rdata, rsize, rseq, false, &full_retransmit)) { #ifdef DAQ_PKT_FLAG_RETRY_PACKET /* If packet is identified as re-transmitted * and it is set as retry already then block the packet */ if (!(p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET)) p->packet_flags |= PKT_RETRANSMIT; #endif // All data was retransmitted if (IsRetransmitOfHeldSegment(p, tdb, st)) { st->held_segment = NULL; p->packet_flags |= PKT_PSEUDO_FLUSH; } else { RetransmitProcess(p, tcpssn); } addthis = 0; break; } s5stats.tcp_overlaps++; st->overlap_count++; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got partial right overlap\n");); switch(reassembly_policy) { /* truncate existing data */ case REASSEMBLY_POLICY_LAST: case REASSEMBLY_POLICY_LINUX: case REASSEMBLY_POLICY_OLD_LINUX: case REASSEMBLY_POLICY_BSD: case REASSEMBLY_POLICY_WINDOWS: case REASSEMBLY_POLICY_WINDOWS2K3: case REASSEMBLY_POLICY_IRIX: case REASSEMBLY_POLICY_HPUX10: case REASSEMBLY_POLICY_MACOS: if (SEQ_EQ(right->seq, seq) && (reassembly_policy != REASSEMBLY_POLICY_LAST)) { slide = (right->seq + right->size - seq); seq += slide; } else { /* partial overlap */ right->seq += overlap; right->payload += overlap; right->size -= (int16_t)overlap; st->seg_bytes_logical -= overlap; st->total_bytes_queued -= overlap; } // right->size always > 0 since overlap < right->size break; case REASSEMBLY_POLICY_FIRST: case REASSEMBLY_POLICY_VISTA: case REASSEMBLY_POLICY_SOLARIS: case REASSEMBLY_POLICY_HPUX11: #ifdef NORMALIZER if ( ips_data == NORM_MODE_ON ) { unsigned offset = right->seq - tdb->seq; unsigned length = tdb->seq + p->dsize - right->seq; memcpy((uint8_t*)p->data+offset, right->payload, length); p->packet_flags |= PKT_MODIFIED; } if ( ips_data != NORM_MODE_OFF ) { normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; } #endif trunc = overlap; break; case REASSEMBLY_POLICY_NOACK: // Don't normalize trunc = overlap; break; } /* all done, keep me out of the loop */ done = 1; } else // Full overlap { // Don't want to count retransmits as overlaps or do anything // else with them. Account for retransmits of multiple PDUs // in one segment. if (IsRetransmit(right, rdata, rsize, rseq, (rseq == tdb->seq), &full_retransmit)) { if( !full_retransmit ) { rdata += right->size; rsize -= right->size; rseq += right->size; seq += right->size; left = right; right = right->next; } else { rsize = 0; done = 1; } if (rsize == 0) { #ifdef DAQ_PKT_FLAG_RETRY_PACKET /* If packet is identified as re-transmitted * and it is set as retry already then block the packet */ if (!(p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET)) p->packet_flags |= PKT_RETRANSMIT; #endif // All data was retransmitted if (IsRetransmitOfHeldSegment(p, tdb, st)) { st->held_segment = NULL; p->packet_flags |= PKT_PSEUDO_FLUSH; } else { RetransmitProcess(p, tcpssn); } addthis = 0; } continue; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got full right overlap\n");); s5stats.tcp_overlaps++; st->overlap_count++; switch(reassembly_policy) { case REASSEMBLY_POLICY_BSD: case REASSEMBLY_POLICY_LINUX: case REASSEMBLY_POLICY_WINDOWS: case REASSEMBLY_POLICY_WINDOWS2K3: case REASSEMBLY_POLICY_HPUX10: case REASSEMBLY_POLICY_IRIX: case REASSEMBLY_POLICY_MACOS: if (SEQ_GEQ(seq_end, right->seq + right->size) && SEQ_LT(seq, right->seq)) { dump_me = right; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "retrans, dropping old data at seq %d, size %d\n", right->seq, right->size);); right = right->next; StreamSeglistDeleteNode(st, dump_me); if (left == dump_me) left = NULL; break; } else { switch (reassembly_policy) { case REASSEMBLY_POLICY_WINDOWS: case REASSEMBLY_POLICY_WINDOWS2K3: case REASSEMBLY_POLICY_BSD: case REASSEMBLY_POLICY_MACOS: /* BSD/MacOS & Windows follow a FIRST policy in the * case below... */ break; default: /* All others follow a LAST policy */ if (SEQ_GT(seq_end, right->seq + right->size) && SEQ_EQ(seq, right->seq)) { /* When existing data is fully overlapped by new * and sequence numbers are the same, most OSs * follow a LAST policy. */ goto right_overlap_last; } break; } } /* Fall through */ case REASSEMBLY_POLICY_FIRST: case REASSEMBLY_POLICY_VISTA: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got full right overlap, truncating new\n");); #ifdef NORMALIZER if ( ips_data == NORM_MODE_ON ) { unsigned offset = right->seq - tdb->seq; memcpy((uint8_t*)p->data+offset, right->payload, right->size); p->packet_flags |= PKT_MODIFIED; } if ( ips_data != NORM_MODE_OFF ) { normStats[PC_TCP_IPS_DATA][ips_data]++; sfBase.iPegs[PERF_COUNT_TCP_IPS_DATA][ips_data]++; } #endif if (SEQ_EQ(right->seq, seq)) { /* Overlap is greater than or equal to right->size * slide gets set before insertion */ seq += right->size; left = right; right = right->next; /* Adjusted seq is fully overlapped */ if (SEQ_EQ(seq, seq_end)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "StreamQueue got full right overlap with " "resulting seq too high, bad segment " "(seq: %X seq_end: %X overlap: %lu\n", seq, seq_end, overlap);); EventBadSegment(st->tcp_policy); Discard(); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return STREAM_INSERT_ANOMALY; } /* No data to add on the left of right, so continue * since some of the other non-first targets may have * fallen into this case */ continue; } /* seq is less than right->seq */ /* trunc is reset to 0 at beginning of loop */ trunc = overlap; /* insert this one, and see if we need to chunk it up */ /* Adjust slide so that is correct relative to orig seq */ slide = seq - tdb->seq; ret = AddStreamNode(st, p, tdb, tcpssn, len, slide, trunc, seq, left, &ss); if (ret != STREAM_INSERT_OK) { /* no warning, already done above */ PREPROC_PROFILE_END(s5TcpInsertPerfStats); return ret; } /* Set seq to end of right since overlap was greater than * or equal to right->size and inserted seq has been * truncated to beginning of right * And reset trunc to 0 since we may fall out of loop if * next right is NULL */ seq = right->seq + right->size; left = right; right = right->next; trunc = 0; /* Keep looping since in IPS we may need to copy old * data into packet */ break; case REASSEMBLY_POLICY_NOACK: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got full right overlap, truncating new\n");); if (SEQ_EQ(right->seq, seq)) { /* Overlap is greater than or equal to right->size * slide gets set before insertion */ seq += right->size; left = right; right = right->next; /* Adjusted seq is fully overlapped */ if (SEQ_EQ(seq, seq_end)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "StreamQueue got full right overlap with " "resulting seq too high, bad segment " "(seq: %X seq_end: %X overlap: %lu\n", seq, seq_end, overlap);); EventBadSegment(st->tcp_policy); s5stats.tcp_discards++; PREPROC_PROFILE_END(s5TcpInsertPerfStats); return STREAM_INSERT_ANOMALY; } /* No data to add on the left of right, so continue * since some of the other non-first targets may have * fallen into this case */ continue; } /* seq is less than right->seq */ /* trunc is reset to 0 at beginning of loop */ trunc = overlap; /* insert this one, and see if we need to chunk it up */ /* Adjust slide so that is correct relative to orig seq */ slide = seq - tdb->seq; ret = AddStreamNode(st, p, tdb, tcpssn, len, slide, trunc, seq, left, &ss); if (ret != STREAM_INSERT_OK) { /* no warning, already done above */ PREPROC_PROFILE_END(s5TcpInsertPerfStats); return ret; } /* Set seq to end of right since overlap was greater than * or equal to right->size and inserted seq has been * truncated to beginning of right * And reset trunc to 0 since we may fall out of loop if * next right is NULL */ seq = right->seq + right->size; left = right; right = right->next; trunc = 0; /* Keep looping since in IPS we may need to copy old * data into packet */ break; case REASSEMBLY_POLICY_HPUX11: case REASSEMBLY_POLICY_SOLARIS: /* If this packet is wholly overlapping and the same size * as a previous one and we have not received the one * immediately preceeding, we take the FIRST. */ if (SEQ_EQ(right->seq, seq) && (right->size == len) && (left && !SEQ_EQ(left->seq + left->size, seq))) { trunc += overlap; if(SEQ_LEQ((int)(seq_end - trunc), seq)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "StreamQueue got full right overlap with " "resulting seq too high, bad segment " "(seq: %X seq_end: %X overlap: %lu\n", seq, seq_end, overlap);); EventBadSegment(st->tcp_policy); Discard(); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return STREAM_INSERT_ANOMALY; } break; } /* Fall through */ case REASSEMBLY_POLICY_OLD_LINUX: case REASSEMBLY_POLICY_LAST: right_overlap_last: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got full right overlap of old, dropping old\n");); dump_me = right; right = right->next; StreamSeglistDeleteNode(st, dump_me); if (left == dump_me) left = NULL; break; } } } if (addthis) { /* Adjust slide so that is correct relative to orig seq */ slide = seq - tdb->seq; ret = AddStreamNode(st, p, tdb, tcpssn, len, slide, trunc, seq, left, &ss); } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Fully truncated right overlap\n");); } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "StreamQueue returning normally\n");); PREPROC_PROFILE_END(s5TcpInsertPerfStats); return ret; } static void ProcessTcpStream(StreamTracker *rcv, TcpSession *tcpssn, Packet *p, TcpDataBlock *tdb, StreamTcpPolicy *s5TcpPolicy) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In ProcessTcpStream(), %d bytes to queue\n", p->dsize);); if ( p->packet_flags & PKT_IGNORE ) return; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID SetPacketHeaderFoo(tcpssn, p); #endif if ((s5TcpPolicy->flags & STREAM_CONFIG_NO_ASYNC_REASSEMBLY) && !TwoWayTraffic(tcpssn->scb)) { return; } if ((s5TcpPolicy->max_consec_small_segs) && /* check ignore_ports */ !(s5TcpPolicy->small_seg_ignore[p->dp/8] & (1 << (p->dp %8)))) { if (p->dsize >= s5TcpPolicy->max_consec_small_seg_size) { rcv->small_seg_count = 0; } else { if (++rcv->small_seg_count == s5TcpPolicy->max_consec_small_segs) { /* Above threshold, log it... requires detect_anomalies be * on in this TCP policy, action controlled by preprocessor * rule. */ EventMaxSmallSegsExceeded(s5TcpPolicy); } } } if (s5TcpPolicy->max_queued_bytes && (rcv->seg_bytes_total > s5TcpPolicy->max_queued_bytes)) { if (stream_session_config->prune_log_max && (TwoWayTraffic(tcpssn->scb) || s5TcpPolicy->log_asymmetric_traffic) && !(tcpssn->scb->ha_state.session_flags & SSNFLAG_LOGGED_QUEUE_FULL)) { char bufc[INET6_ADDRSTRLEN], bufs[INET6_ADDRSTRLEN]; sfip_ntop(&tcpssn->scb->client_ip, bufc, sizeof(bufc)); sfip_ntop(&tcpssn->scb->server_ip, bufs, sizeof(bufs)); LogMessage("S5: Session exceeded configured max bytes to queue %d " "using %d bytes (%s). %s %d --> %s %d " #ifdef TARGET_BASED "(%d) " #endif ": LWstate 0x%x LWFlags 0x%x\n", s5TcpPolicy->max_queued_bytes, rcv->seg_bytes_total, (rcv == &tcpssn->client) ? "client queue" : "server queue", bufc, ntohs(tcpssn->scb->client_port), bufs, ntohs(tcpssn->scb->server_port), #ifdef TARGET_BASED tcpssn->scb->ha_state.application_protocol, #endif tcpssn->scb->session_state, tcpssn->scb->ha_state.session_flags); /* only log this one per session */ tcpssn->scb->ha_state.session_flags |= SSNFLAG_LOGGED_QUEUE_FULL; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Ignoring segment due to too many bytes queued\n");); return; } if (s5TcpPolicy->max_queued_segs && (rcv->seg_count+1 > s5TcpPolicy->max_queued_segs)) { if (stream_session_config->prune_log_max && (TwoWayTraffic(tcpssn->scb) || s5TcpPolicy->log_asymmetric_traffic) && !(tcpssn->scb->ha_state.session_flags & SSNFLAG_LOGGED_QUEUE_FULL)) { char bufc[INET6_ADDRSTRLEN], bufs[INET6_ADDRSTRLEN]; sfip_ntop(&tcpssn->scb->client_ip, bufc, sizeof(bufc)); sfip_ntop(&tcpssn->scb->server_ip, bufs, sizeof(bufs)); LogMessage("S5: Session exceeded configured max segs to queue %d " "using %d segs (%s). %s %d --> %s %d " #ifdef TARGET_BASED "(%d) " #endif ": LWstate 0x%x LWFlags 0x%x\n", s5TcpPolicy->max_queued_segs, rcv->seg_count, (rcv == &tcpssn->client) ? "client queue" : "server queue", bufc, ntohs(tcpssn->scb->client_port), bufs, ntohs(tcpssn->scb->server_port), #ifdef TARGET_BASED tcpssn->scb->ha_state.application_protocol, #endif tcpssn->scb->session_state, tcpssn->scb->ha_state.session_flags); /* only log this one per session */ tcpssn->scb->ha_state.session_flags |= SSNFLAG_LOGGED_QUEUE_FULL; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Ignoring segment due to too many bytes queued\n");); return; } if(rcv->seg_count != 0) { if(rcv->flush_mgr.flush_policy == STREAM_FLPOLICY_IGNORE) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Ignoring segment due to IGNORE flush_policy\n");); return; } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "queuing segment\n");); if ( SEQ_GT(rcv->r_win_base, tdb->seq) ) { uint32_t offset = rcv->r_win_base - tdb->seq; if ( offset < p->dsize ) { tdb->seq += offset; p->data += offset; p->dsize -= (uint16_t)offset; StreamQueue(rcv, p, tdb, tcpssn); p->dsize += (uint16_t)offset; p->data -= offset; tdb->seq -= offset; } } else StreamQueue(rcv, p, tdb, tcpssn); if ((rcv->tcp_policy->overlap_limit) && (rcv->overlap_count > rcv->tcp_policy->overlap_limit)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Reached the overlap limit. Flush the data " "and kill the session if configured\n");); if (p->packet_flags & PKT_FROM_CLIENT) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flushing data on packet from the client\n");); flush_stream(tcpssn, rcv, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, PKT_FROM_CLIENT); flush_stream(tcpssn, &tcpssn->server, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, PKT_FROM_SERVER); } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flushing data on packet from the server\n");); flush_stream(tcpssn, rcv, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, PKT_FROM_SERVER); flush_stream(tcpssn, &tcpssn->client, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, PKT_FROM_CLIENT); } purge_all(&tcpssn->client); purge_all(&tcpssn->server); /* Alert on overlap limit and reset counter */ EventExcessiveOverlap(rcv->tcp_policy); rcv->overlap_count = 0; } } } else { if(rcv->flush_mgr.flush_policy == STREAM_FLPOLICY_IGNORE) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Ignoring segment due to IGNORE flush_policy\n");); return; } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "queuing segment\n");); NewQueue(rcv, p, tdb, tcpssn); } } return; } static int ProcessTcpData(Packet *p, StreamTracker *listener, TcpSession *tcpssn, TcpDataBlock *tdb, StreamTcpPolicy *s5TcpPolicy) { PROFILE_VARS; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In ProcessTcpData()\n");); PREPROC_PROFILE_START(s5TcpDataPerfStats); if (!IsTCPFastOpenPkt(p) && (p->tcph->th_flags & TH_SYN) && (listener->os_policy != STREAM_POLICY_MACOS)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Bailing, data on SYN, not MAC Policy!\n");); NormalTrimPayloadIfSyn(p, 0, tdb); PREPROC_PROFILE_END(s5TcpDataPerfStats); return STREAM_UNALIGNED; } /* we're aligned, so that's nice anyway */ if((tdb->seq == listener->r_nxt_ack) || (p->tcph->th_flags & TH_SYN)) { if ( p->tcph->th_flags & TH_SYN ) ++tdb->seq; /* check if we're in the window */ if( s5TcpPolicy->policy != STREAM_POLICY_NOACK ) { if(StreamGetWindow(tcpssn->scb, listener, tdb) == 0) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Bailing, we're out of the window!\n");); NormalTrimPayloadIfWin(p, 0, tdb); PREPROC_PROFILE_END(s5TcpDataPerfStats); return STREAM_UNALIGNED; } } /* move the ack boundry up, this is the only way we'll accept data */ // update r_nxt_ack for IGNORE Flush policy here. Else update in StreamSeglistAddNode if((listener->s_mgr.state_queue == TCP_STATE_NONE) && (listener->flush_mgr.flush_policy == STREAM_FLPOLICY_IGNORE)) listener->r_nxt_ack = tdb->end_seq; //For IPS mode trim packet if it overwrites OOO FIN in state_queue if((listener->s_mgr.state_queue == TCP_STATE_CLOSE_WAIT) && (Stream_NormGetMode(listener->reassembly_policy, snort_conf, NORM_TCP_IPS) == NORM_MODE_ON)) { if(SEQ_GT(tdb->end_seq, listener->s_mgr.transition_seq - 1)) { uint32_t delta = tdb->end_seq - (listener->s_mgr.transition_seq - 1); if (p->dsize > delta) NormalTrimPayload(p, delta, tdb); } } if(p->dsize != 0) { if (listener->flags & TF_MISSING_PKT) tcpssn->scb->ha_state.session_flags |= SSNFLAG_STREAM_ORDER_BAD; if (tcpssn->scb->ha_state.session_flags & SSNFLAG_STREAM_ORDER_BAD) p->packet_flags |= PKT_STREAM_ORDER_BAD; else p->packet_flags |= PKT_STREAM_ORDER_OK; ProcessTcpStream(listener, tcpssn, p, tdb, s5TcpPolicy); if (SEQ_GT(listener->r_nxt_ack,tdb->end_seq)) { tcpssn->scb->ha_state.session_flags |= SSNFLAG_STREAM_ORDER_BAD; } PREPROC_PROFILE_END(s5TcpDataPerfStats); return STREAM_ALIGNED; } } else { /* pkt is out of order, do some target-based shizzle here */ /* NO, we don't want to simply bail. Some platforms * favor unack'd dup data over the original data. * Let the reassembly policy decide how to handle * the overlapping data. * * See HP, Solaris, et al. for those that favor * duplicate data over the original in some cases. */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "out of order segment (tdb->seq: 0x%X " "l->r_nxt_ack: 0x%X!\n", tdb->seq, listener->r_nxt_ack);); if (listener->s_mgr.state_queue == TCP_STATE_NONE) { /* check if we're in the window */ if( s5TcpPolicy->policy != STREAM_POLICY_NOACK ) { if(StreamGetWindow(tcpssn->scb, listener, tdb) == 0) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Bailing, we're out of the window!\n");); NormalTrimPayloadIfWin(p, 0, tdb); PREPROC_PROFILE_END(s5TcpDataPerfStats); return STREAM_UNALIGNED; } } if ((listener->s_mgr.state == TCP_STATE_ESTABLISHED) && (listener->flush_mgr.flush_policy == STREAM_FLPOLICY_IGNORE)) { if ( SEQ_GT(tdb->end_seq, listener->r_nxt_ack)) { /* set next ack so we are within the window going forward on * this side. */ // FIXTHIS for ips, must move all the way to first hole or right end listener->r_nxt_ack = tdb->end_seq; } } } //For IPS mode trim packet if it overwrites OOO FIN in state_queue else if((listener->s_mgr.state_queue == TCP_STATE_CLOSE_WAIT) && (Stream_NormGetMode(listener->reassembly_policy, snort_conf, NORM_TCP_IPS) == NORM_MODE_ON)) { if(SEQ_GT(tdb->end_seq, listener->s_mgr.transition_seq - 1)) { uint32_t delta = tdb->end_seq - (listener->s_mgr.transition_seq - 1); if (p->dsize > delta) NormalTrimPayload(p, delta, tdb); } } if(p->dsize != 0) { uint32_t r_nxt_ack_old = listener->r_nxt_ack; if (SEQ_GT(listener->r_win_base, listener->r_nxt_ack) || (listener->flags & TF_MISSING_PKT)) tcpssn->scb->ha_state.session_flags |= SSNFLAG_STREAM_ORDER_BAD; if (tcpssn->scb->ha_state.session_flags & SSNFLAG_STREAM_ORDER_BAD) p->packet_flags |= PKT_STREAM_ORDER_BAD; ProcessTcpStream(listener, tcpssn, p, tdb, s5TcpPolicy); /* If we have moved the expected seq, overlapped segment, set BAD flag*/ if (SEQ_GT(listener->r_nxt_ack, r_nxt_ack_old)) { tcpssn->scb->ha_state.session_flags |= SSNFLAG_STREAM_ORDER_BAD; p->packet_flags |= PKT_STREAM_ORDER_BAD; } } } PREPROC_PROFILE_END(s5TcpDataPerfStats); return STREAM_UNALIGNED; } uint16_t StreamGetPolicy(SessionControlBlock *scb, StreamTcpPolicy *s5TcpPolicy, int direction) { #ifdef TARGET_BASED uint16_t policy_id; /* Not caching this host_entry in the frag tracker so we can * swap the table out after processing this packet if we need * to. */ HostAttributeEntry *host_entry = NULL; int ssn_dir; if (!IsAdaptiveConfigured()) return s5TcpPolicy->policy; if (direction == FROM_CLIENT) { host_entry = SFAT_LookupHostEntryByIP(&scb->server_ip); ssn_dir = SSN_DIR_FROM_SERVER; } else { host_entry = SFAT_LookupHostEntryByIP(&scb->client_ip); ssn_dir = SSN_DIR_FROM_CLIENT; } if (host_entry && (isStreamPolicySet(host_entry) == POLICY_SET)) { policy_id = getStreamPolicy(host_entry); if (policy_id != SFAT_UNKNOWN_STREAM_POLICY) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "StreamGetPolicy: Policy Map Entry: %d(%s)\n", policy_id, reassembly_policy_names[policy_id]);); /* Since we've already done the lookup, try to get the * application protocol id with that host_entry. */ setAppProtocolIdFromHostEntry(scb, host_entry, ssn_dir); return policy_id; } } #endif STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "StreamGetPolicy: Using configured default %d(%s)\n", s5TcpPolicy->policy, reassembly_policy_names[s5TcpPolicy->policy]);); return s5TcpPolicy->policy; } void SetTcpReassemblyPolicy(StreamTracker *st) { st->reassembly_policy = GetTcpReassemblyPolicy(st->os_policy); } static void SetOSPolicy(TcpSession *tcpssn) { if (tcpssn->client.os_policy == 0) { tcpssn->client.os_policy = StreamGetPolicy(tcpssn->scb, tcpssn->client.tcp_policy, FROM_SERVER); SetTcpReassemblyPolicy(&tcpssn->client); } if (tcpssn->server.os_policy == 0) { tcpssn->server.os_policy = StreamGetPolicy(tcpssn->scb, tcpssn->server.tcp_policy, FROM_CLIENT); SetTcpReassemblyPolicy(&tcpssn->server); } } static inline int ValidMacAddress(StreamTracker *talker, StreamTracker *listener, Packet *p) { int i; int ret = 0; if (p->eh == NULL) return 0; /* Use a for loop and byte comparison, which has proven to be * faster on pipelined architectures compared to a memcmp (setup * for memcmp is slow). Not using a 4 byte and 2 byte long because * there is no guaranttee of memory alignment (and thus performance * issues similar to memcmp). */ for (i=0;i<6;i++) { if ((talker->mac_addr[i] != p->eh->ether_src[i])) { if (p->packet_flags & PKT_FROM_CLIENT) ret |= EVENT_SESSION_HIJACK_CLIENT; else ret |= EVENT_SESSION_HIJACK_SERVER; } if (listener->mac_addr[i] != p->eh->ether_dst[i]) { if (p->packet_flags & PKT_FROM_CLIENT) ret |= EVENT_SESSION_HIJACK_SERVER; else ret |= EVENT_SESSION_HIJACK_CLIENT; } } return ret; } static inline void CopyMacAddr(Packet *p, TcpSession *tcpssn, int dir) { int i; /* Not ethernet based, nothing to do */ if (p->eh == NULL) return; if (dir == FROM_CLIENT) { /* Client is SRC */ for (i=0;i<6;i++) { tcpssn->client.mac_addr[i] = p->eh->ether_src[i]; tcpssn->server.mac_addr[i] = p->eh->ether_dst[i]; } } else { /* Server is SRC */ for (i=0;i<6;i++) { tcpssn->server.mac_addr[i] = p->eh->ether_src[i]; tcpssn->client.mac_addr[i] = p->eh->ether_dst[i]; } } } static int NewTcpSession(Packet *p, SessionControlBlock *scb, TcpDataBlock *tdb, StreamTcpPolicy *dstPolicy) { MemBucket *tmpBucket = NULL; TcpSession *tmp = NULL; uint16_t server_port = 0; PROFILE_VARS; PREPROC_PROFILE_START(s5TcpNewSessPerfStats); if (TCP_ISFLAGSET(p->tcph, TH_SYN) && !TCP_ISFLAGSET(p->tcph, TH_ACK)) { /****************************************************************** * start new sessions on proper SYN packets *****************************************************************/ tmpBucket = session_api->alloc_protocol_session( SESSION_PROTO_TCP ); if(!tmpBucket) { PREPROC_PROFILE_END(s5TcpNewSessPerfStats); return -1; } tmp = tmpBucket->data; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on SYN!\n");); #ifdef DEBUG tmp->ssn_time.tv_sec = p->pkth->ts.tv_sec; tmp->ssn_time.tv_usec = p->pkth->ts.tv_usec; #endif scb->ha_state.session_flags |= SSNFLAG_SEEN_CLIENT; if((p->tcph->th_flags & (TH_CWR|TH_ECE)) == (TH_CWR|TH_ECE)) { scb->ha_state.session_flags |= SSNFLAG_ECN_CLIENT_QUERY; } /* setup the stream trackers */ tmp->client.s_mgr.state = TCP_STATE_SYN_SENT; tmp->client.isn = tdb->seq; tmp->client.l_unackd = tdb->seq + 1; tmp->client.l_nxt_seq = tmp->client.l_unackd; if ( tdb->seq != tdb->end_seq ) tmp->client.l_nxt_seq += (tdb->end_seq - tdb->seq - 1); tmp->client.l_window = tdb->win; tmp->client.ts_last_pkt = p->pkth->ts.tv_sec; tmp->server.seglist_base_seq = tmp->client.l_unackd; tmp->server.r_nxt_ack = tmp->client.l_unackd; tmp->server.r_win_base = tdb->seq+1; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->server.seglist_base_seq);); tmp->server.s_mgr.state = TCP_STATE_LISTEN; tmp->client.flags |= StreamGetTcpTimestamp(p, &tmp->client.ts_last, 0); if (tmp->client.ts_last == 0) tmp->client.flags |= TF_TSTAMP_ZERO; tmp->client.flags |= StreamGetMss(p, &tmp->client.mss); tmp->client.flags |= StreamGetWscale(p, &tmp->client.wscale); /* Set the StreamTcpPolicy for each direction (pkt from client) */ tmp->client.tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); tmp->server.tcp_policy = dstPolicy; /* Server is destination */ server_port = p->dp; CopyMacAddr(p, tmp, FROM_CLIENT); } else if (TCP_ISFLAGSET(p->tcph, (TH_SYN|TH_ACK))) { /****************************************************************** * start new sessions on SYN/ACK from server *****************************************************************/ tmpBucket = session_api->alloc_protocol_session( SESSION_PROTO_TCP ); if(!tmpBucket) { PREPROC_PROFILE_END(s5TcpNewSessPerfStats); return -1; } tmp = tmpBucket->data; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on SYN_ACK!\n");); #ifdef DEBUG tmp->ssn_time.tv_sec = p->pkth->ts.tv_sec; tmp->ssn_time.tv_usec = p->pkth->ts.tv_usec; #endif scb->ha_state.session_flags |= SSNFLAG_SEEN_SERVER; if((p->tcph->th_flags & (TH_CWR|TH_ECE)) == (TH_CWR|TH_ECE)) { scb->ha_state.session_flags |= SSNFLAG_ECN_SERVER_REPLY; } /* setup the stream trackers */ tmp->server.s_mgr.state = TCP_STATE_SYN_RCVD; tmp->server.isn = tdb->seq; tmp->server.l_unackd = tdb->seq + 1; tmp->server.l_nxt_seq = tmp->server.l_unackd; tmp->server.l_window = tdb->win; tmp->server.seglist_base_seq = tdb->ack; tmp->server.r_win_base = tdb->ack; tmp->server.r_nxt_ack = tdb->ack; tmp->server.ts_last_pkt = p->pkth->ts.tv_sec; tmp->client.seglist_base_seq = tmp->server.l_unackd; tmp->client.r_nxt_ack = tmp->server.l_unackd; tmp->client.r_win_base = tdb->seq+1; tmp->client.l_nxt_seq = tdb->ack; tmp->client.isn = tdb->ack-1; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->client.seglist_base_seq);); tmp->client.s_mgr.state = TCP_STATE_SYN_SENT; tmp->server.flags |= StreamGetTcpTimestamp(p, &tmp->server.ts_last, 0); if (tmp->server.ts_last == 0) tmp->server.flags |= TF_TSTAMP_ZERO; tmp->server.flags |= StreamGetMss(p, &tmp->server.mss); tmp->server.flags |= StreamGetWscale(p, &tmp->server.wscale); /* Set the StreamTcpPolicy for each direction (pkt from server) */ tmp->server.tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); tmp->client.tcp_policy = dstPolicy; scb->proto_policy = tmp->server.tcp_policy; /* Client is destination */ server_port = p->sp; CopyMacAddr(p, tmp, FROM_SERVER); } else if ((p->tcph->th_flags & TH_ACK) && !(p->tcph->th_flags & TH_RST) && (scb->session_state & STREAM_STATE_ESTABLISHED)) { /****************************************************************** * start new sessions on completion of 3-way (ACK only, no data) *****************************************************************/ tmpBucket = session_api->alloc_protocol_session( SESSION_PROTO_TCP ); if(!tmpBucket) { PREPROC_PROFILE_END(s5TcpNewSessPerfStats); return -1; } tmp = tmpBucket->data; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on ACK!\n");); #ifdef DEBUG tmp->ssn_time.tv_sec = p->pkth->ts.tv_sec; tmp->ssn_time.tv_usec = p->pkth->ts.tv_usec; #endif scb->ha_state.session_flags |= SSNFLAG_SEEN_CLIENT; if((p->tcph->th_flags & (TH_CWR|TH_ECE)) == (TH_CWR|TH_ECE)) { scb->ha_state.session_flags |= SSNFLAG_ECN_CLIENT_QUERY; } /* setup the stream trackers */ tmp->client.s_mgr.state = TCP_STATE_ESTABLISHED; tmp->client.isn = tdb->seq; tmp->client.l_unackd = tdb->seq + 1; tmp->client.l_nxt_seq = tmp->client.l_unackd; tmp->client.l_window = tdb->win; tmp->client.ts_last_pkt = p->pkth->ts.tv_sec; tmp->server.seglist_base_seq = tmp->client.l_unackd; tmp->server.r_nxt_ack = tmp->client.l_unackd; tmp->server.r_win_base = tdb->seq+1; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->server.seglist_base_seq);); tmp->server.s_mgr.state = TCP_STATE_ESTABLISHED; tmp->client.flags |= StreamGetTcpTimestamp(p, &tmp->client.ts_last, 0); if (tmp->client.ts_last == 0) tmp->client.flags |= TF_TSTAMP_ZERO; tmp->client.flags |= StreamGetMss(p, &tmp->client.mss); tmp->client.flags |= StreamGetWscale(p, &tmp->client.wscale); /* Set the StreamTcpPolicy for each direction (pkt from client) */ tmp->client.tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); tmp->server.tcp_policy = dstPolicy; /* Server is destination */ server_port = p->dp; CopyMacAddr(p, tmp, FROM_CLIENT); } else if (p->dsize != 0) { /****************************************************************** * start new sessions on data in packet *****************************************************************/ tmpBucket = session_api->alloc_protocol_session( SESSION_PROTO_TCP ); if(!tmpBucket) { PREPROC_PROFILE_END(s5TcpNewSessPerfStats); return -1; } tmp = tmpBucket->data; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Creating new session tracker on data packet (ACK|PSH)!\n");); #ifdef DEBUG tmp->ssn_time.tv_sec = p->pkth->ts.tv_sec; tmp->ssn_time.tv_usec = p->pkth->ts.tv_usec; #endif if (scb->ha_state.direction == FROM_CLIENT) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Session direction is FROM_CLIENT\n");); /* Sender is client (src port is higher) */ scb->ha_state.session_flags |= SSNFLAG_SEEN_CLIENT; if((p->tcph->th_flags & (TH_CWR|TH_ECE)) == (TH_CWR|TH_ECE)) { scb->ha_state.session_flags |= SSNFLAG_ECN_CLIENT_QUERY; } /* setup the stream trackers */ tmp->client.s_mgr.state = TCP_STATE_ESTABLISHED; tmp->client.isn = tdb->seq; tmp->client.l_unackd = tdb->seq; tmp->client.l_nxt_seq = tmp->client.l_unackd; tmp->client.l_window = tdb->win; tmp->client.r_nxt_ack = tdb->ack; tmp->client.r_win_base = tdb->ack; tmp->client.ts_last_pkt = p->pkth->ts.tv_sec; tmp->server.seglist_base_seq = tmp->client.l_unackd; tmp->server.r_nxt_ack = tmp->client.l_unackd; tmp->server.r_win_base = tdb->seq; tmp->server.l_window = 0; /* reset later */ /* Next server packet is what was ACKd */ tmp->server.l_nxt_seq = tdb->ack; tmp->server.l_unackd = tdb->ack - 1; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->server.seglist_base_seq);); tmp->server.s_mgr.state = TCP_STATE_ESTABLISHED; tmp->client.flags |= StreamGetTcpTimestamp(p, &tmp->client.ts_last, 0); if (tmp->client.ts_last == 0) tmp->client.flags |= TF_TSTAMP_ZERO; tmp->client.flags |= StreamGetMss(p, &tmp->client.mss); tmp->client.flags |= StreamGetWscale(p, &tmp->client.wscale); /* Set the StreamTcpPolicy for each direction (pkt from client) */ tmp->client.tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); tmp->server.tcp_policy = dstPolicy; /* Server is destination */ server_port = p->dp; CopyMacAddr(p, tmp, FROM_CLIENT); } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Session direction is FROM_SERVER\n");); /* Sender is server (src port is lower) */ scb->ha_state.session_flags |= SSNFLAG_SEEN_SERVER; /* setup the stream trackers */ tmp->server.s_mgr.state = TCP_STATE_ESTABLISHED; tmp->server.isn = tdb->seq; tmp->server.l_unackd = tdb->seq; tmp->server.l_nxt_seq = tmp->server.l_unackd; tmp->server.l_window = tdb->win; tmp->server.seglist_base_seq = tdb->ack; tmp->server.r_win_base = tdb->ack; tmp->server.r_nxt_ack = tdb->ack; tmp->server.ts_last_pkt = p->pkth->ts.tv_sec; tmp->client.seglist_base_seq = tmp->server.l_unackd; tmp->client.r_nxt_ack = tmp->server.l_unackd; tmp->client.r_win_base = tdb->seq; tmp->client.l_window = 0; /* reset later */ tmp->client.isn = tdb->ack-1; tmp->client.l_nxt_seq = tdb->ack; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seglist_base_seq = %X\n", tmp->client.seglist_base_seq);); tmp->client.s_mgr.state = TCP_STATE_ESTABLISHED; tmp->server.flags |= StreamGetTcpTimestamp(p, &tmp->server.ts_last, 0); if (tmp->server.ts_last == 0) tmp->server.flags |= TF_TSTAMP_ZERO; tmp->server.flags |= StreamGetMss(p, &tmp->server.mss); tmp->server.flags |= StreamGetWscale(p, &tmp->server.wscale); /* Set the StreamTcpPolicy for each direction (pkt from server) */ tmp->server.tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); tmp->client.tcp_policy = dstPolicy; scb->proto_policy = tmp->server.tcp_policy; /* Client is destination */ server_port = p->sp; CopyMacAddr(p, tmp, FROM_SERVER); } } if (tmp) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "adding TcpSession to lightweight session\n");); scb->proto_specific_data = tmpBucket; scb->protocol = GET_IPH_PROTO(p); tmp->scb = scb; /* New session, previous was marked as reset. Clear the * reset flag. */ if (scb->ha_state.session_flags & SSNFLAG_RESET) scb->ha_state.session_flags &= ~SSNFLAG_RESET; SetOSPolicy(tmp); if ( (scb->ha_state.session_flags & SSNFLAG_CLIENT_SWAP) && !(scb->ha_state.session_flags & SSNFLAG_CLIENT_SWAPPED) ) { StreamTracker trk = tmp->client; sfaddr_t ip = scb->client_ip; uint16_t port = scb->client_port; tmp->client = tmp->server; tmp->server = trk; scb->client_ip = scb->server_ip; scb->server_ip = ip; scb->client_port = scb->server_port; scb->server_port = port; if ( !TwoWayTraffic(scb) ) { if ( scb->ha_state.session_flags & SSNFLAG_SEEN_CLIENT ) { scb->ha_state.session_flags ^= SSNFLAG_SEEN_CLIENT; scb->ha_state.session_flags |= SSNFLAG_SEEN_SERVER; } else if ( scb->ha_state.session_flags & SSNFLAG_SEEN_SERVER ) { scb->ha_state.session_flags ^= SSNFLAG_SEEN_SERVER; scb->ha_state.session_flags |= SSNFLAG_SEEN_CLIENT; } } scb->ha_state.session_flags |= SSNFLAG_CLIENT_SWAPPED; } /* Set up the flush behaviour, based on the configured info * for the server and client ports. */ /* Yes, the server flush manager gets the info from the * policy's server port's the flush policy from the client * and visa-versa. * * For example, when policy said 'ports client 80', that means * reassemble packets from the client side (stored in the server's * flush buffer in the session) destined for port 80. Port 80 is * the server port and we're reassembling the client side. * That should make this almost as clear as opaque mud! */ #ifdef TARGET_BASED if (tmp->client.tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].configured == 1) { StreamTracker* pst = &tmp->server; uint8_t flush_policy = pst->tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].client.flush_policy; InitFlushMgrByService(scb, pst, scb->ha_state.application_protocol, true, flush_policy); } else #endif { StreamTracker* pst = &tmp->server; uint8_t flush_policy = pst->tcp_policy->flush_config[server_port].client.flush_policy; InitFlushMgrByPort(scb, pst, server_port, true, flush_policy, false); } #ifdef TARGET_BASED if (tmp->server.tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].configured == 1) { StreamTracker* pst = &tmp->client; uint8_t flush_policy = pst->tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].server.flush_policy; InitFlushMgrByService(scb, pst, scb->ha_state.application_protocol, false, flush_policy); } else #endif { StreamTracker* pst = &tmp->client; uint8_t flush_policy = pst->tcp_policy->flush_config[server_port].server.flush_policy; InitFlushMgrByPort(scb, pst, server_port, false, flush_policy, false); } #ifdef STREAM_DEBUG_ENABLED PrintTcpSession(tmp); #endif session_api->set_expire_timer(p, scb, dstPolicy->session_timeout); s5stats.tcp_streamtrackers_created++; AddStreamSession(&sfBase, scb->session_state & STREAM_STATE_MIDSTREAM ? SSNFLAG_MIDSTREAM : 0); StreamUpdatePerfBaseState(&sfBase, tmp->scb, TCP_STATE_SYN_SENT); #ifdef NORMALIZER tmp->ecn = 0; #endif tmp->session_decrypted = false; PREPROC_PROFILE_END(s5TcpNewSessPerfStats); return 1; } PREPROC_PROFILE_END(s5TcpNewSessPerfStats); return 0; } /* set_service_based_flush_policy * * Once appid detects the protocol, calling this api * to set the respective paf apis. * If paf function pointer already pointed to the * correct paf api, not going to set again. */ void set_service_based_flush_policy(SessionControlBlock *scb) { TcpSession *tcp_session = NULL; StreamTracker* pst; uint8_t flush_policy; int ret; #ifdef TARGET_BASED if (scb->proto_specific_data) tcp_session = scb->proto_specific_data->data; if (tcp_session == NULL) return; if (tcp_session->client.tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].configured == 1) { pst = &tcp_session->server; ret = cb_mask_cmp(((StreamConfig *)scb->stream_config)->tcp_config->paf_config, scb->ha_state.application_protocol, true, pst->paf_state.cb_mask); if (ret <= 0) { return; } flush_policy = pst->tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].client.flush_policy; InitFlushMgrByService(scb, pst, scb->ha_state.application_protocol, true, flush_policy); } if (tcp_session->server.tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].configured == 1) { pst = &tcp_session->client; ret = cb_mask_cmp(((StreamConfig *)scb->stream_config)->tcp_config->paf_config, scb->ha_state.application_protocol, false, pst->paf_state.cb_mask); if (ret <= 0) { return; } flush_policy = pst->tcp_policy->flush_config_protocol[scb->ha_state.application_protocol].server.flush_policy; InitFlushMgrByService(scb, pst, scb->ha_state.application_protocol, false, flush_policy); } #endif } static int RepeatedSyn( StreamTracker *listener, StreamTracker *talker, TcpDataBlock *tdb, TcpSession *tcpssn) { switch (listener->os_policy) { case STREAM_POLICY_WINDOWS: case STREAM_POLICY_WINDOWS2K3: case STREAM_POLICY_VISTA: /* Windows has some strange behaviour here. If the * sequence of the reset is the next expected sequence, * it Resets. Otherwise it ignores the 2nd SYN. */ if (SEQ_EQ(tdb->seq, listener->r_nxt_ack)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got syn on established windows ssn, which causes Reset," "bailing\n");); tcpssn->scb->ha_state.session_flags |= SSNFLAG_RESET; talker->s_mgr.state = TCP_STATE_CLOSED; return ACTION_RST; } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got syn on established windows ssn, not causing Reset," "bailing\n");); Discard(); return ACTION_NOTHING; } break; case STREAM_POLICY_MACOS: /* MACOS ignores a 2nd SYN, regardless of the sequence number. */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got syn on established macos ssn, not causing Reset," "bailing\n");); Discard(); return ACTION_NOTHING; break; case STREAM_POLICY_FIRST: case STREAM_POLICY_NOACK: case STREAM_POLICY_LAST: case STREAM_POLICY_LINUX: case STREAM_POLICY_OLD_LINUX: case STREAM_POLICY_BSD: case STREAM_POLICY_SOLARIS: case STREAM_POLICY_HPUX11: case STREAM_POLICY_HPUX10: case STREAM_POLICY_IRIX: /* If its not a retransmission of the actual SYN... RESET */ if(!SEQ_EQ(tdb->seq,talker->isn)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got syn on established ssn, which causes Reset, bailing\n");); tcpssn->scb->ha_state.session_flags |= SSNFLAG_RESET; talker->s_mgr.state = TCP_STATE_CLOSED; return ACTION_RST; } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got syn on established ssn, not causing Reset," "bailing\n");); Discard(); return ACTION_NOTHING; } break; } return ACTION_NOTHING; } static void LogTcpEvents(StreamTcpPolicy *s5TcpPolicy, int eventcode) { if ( !eventcode ) return; if (eventcode & EVENT_SYN_ON_EST) EventSynOnEst(s5TcpPolicy); if (eventcode & EVENT_DATA_ON_SYN) EventDataOnSyn(s5TcpPolicy); if (eventcode & EVENT_DATA_ON_CLOSED) EventDataOnClosed(s5TcpPolicy); if (eventcode & EVENT_BAD_TIMESTAMP) EventBadTimestamp(s5TcpPolicy); if (eventcode & EVENT_BAD_SEGMENT) EventBadSegment(s5TcpPolicy); if (eventcode & EVENT_WINDOW_TOO_LARGE) EventWindowTooLarge(s5TcpPolicy); if (eventcode & EVENT_EXCESSIVE_TCP_OVERLAPS) EventExcessiveOverlap(s5TcpPolicy); if (eventcode & EVENT_DATA_AFTER_RESET) EventDataAfterReset(s5TcpPolicy); if (eventcode & EVENT_SESSION_HIJACK_CLIENT) EventSessionHijackedClient(s5TcpPolicy); if (eventcode & EVENT_SESSION_HIJACK_SERVER) EventSessionHijackedServer(s5TcpPolicy); if (eventcode & EVENT_DATA_WITHOUT_FLAGS) EventDataWithoutFlags(s5TcpPolicy); if (eventcode & EVENT_4WHS) Event4whs(s5TcpPolicy); if (eventcode & EVENT_NO_TIMESTAMP) EventNoTimestamp(s5TcpPolicy); if (eventcode & EVENT_BAD_RST) EventBadReset(s5TcpPolicy); if (eventcode & EVENT_BAD_FIN) EventBadFin(s5TcpPolicy); if (eventcode & EVENT_BAD_ACK) EventBadAck(s5TcpPolicy); if (eventcode & EVENT_DATA_AFTER_RST_RCVD) EventDataAfterRstRcvd(s5TcpPolicy); if (eventcode & EVENT_WINDOW_SLAM) EventWindowSlam(s5TcpPolicy); if (eventcode & EVENT_WIN_SZ_0_TCP_FIN_WAIT_1) EventWindowZeroAfterFinAck(s5TcpPolicy); } static inline void DisableInspection (SessionControlBlock *scb, Packet* p, char ignore) { /* Set the directions to ignore... */ scb->ha_state.ignore_direction = ignore; StreamSetReassemblyTcp(scb, STREAM_FLPOLICY_IGNORE, ignore, STREAM_FLPOLICY_SET_ABSOLUTE); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Ignoring packet from %d. Marking session marked as ignore.\n", p->packet_flags & PKT_FROM_SERVER? "server" : "client");); session_api->disable_inspection(scb, p); } static inline bool checkFINTransitionStatus(const Packet* p, StreamTracker *listener) { if((p->dsize != 0) && (listener->s_mgr.state_queue == TCP_STATE_CLOSE_WAIT) && ((listener->s_mgr.transition_seq - 1) == listener->r_nxt_ack)) return true; return false; } static int ProcessTcp(SessionControlBlock *scb, Packet *p, TcpDataBlock *tdb, StreamTcpPolicy *s5TcpPolicy, SFXHASH_NODE *hash_node) { int retcode = ACTION_NOTHING; int eventcode = 0; char ignore; int got_ts = 0; int new_ssn = 0; int ts_action = ACTION_NOTHING; TcpSession *tcpssn = NULL; StreamTracker *talker = NULL; StreamTracker *listener = NULL; uint32_t require3Way = (s5TcpPolicy->flags & STREAM_CONFIG_REQUIRE_3WHS); bool process_fin = false; STREAM_DEBUG_WRAP(char *t = NULL; char *l = NULL;) PROFILE_VARS; if (scb->protocol != IPPROTO_TCP) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Lightweight session not TCP on TCP packet\n");); return retcode; } if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; PREPROC_PROFILE_START(s5TcpStatePerfStats); if (tcpssn == NULL) { if ( ScPafEnabled() ) { /* Check if the session is to be ignored */ if (hash_node) ignore = StreamExpectProcessNode(p, scb, hash_node); else ignore = StreamExpectCheck(p, scb); if (ignore) { DisableInspection(scb, p, ignore); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } } if (TCP_ISFLAGSET(p->tcph, TH_SYN) && !TCP_ISFLAGSET(p->tcph, TH_ACK)) { scb->session_state |= STREAM_STATE_SYN; if( require3Way || ( StreamPacketHasWscale( p ) & TF_WSCALE ) || ( ( p->dsize > 0 ) && ( StreamGetPolicy( scb, s5TcpPolicy, FROM_CLIENT ) == STREAM_POLICY_MACOS ) ) ) { /* Create TCP session if we * 1) require 3-WAY HS, OR * 2) client sent wscale option, OR * 3) have data and its a MAC OS policy -- MAC * is the only one that accepts data on SYN * (and thus requires a TCP session at this point) */ if(NewTcpSession(p, scb, tdb, s5TcpPolicy)== -1 ) { PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } tcpssn = (TcpSession *)scb->proto_specific_data->data; new_ssn = 1; NormalTrackECN(tcpssn, p->tcph, require3Way); } /* Nothing left todo here */ } else if( TCP_ISFLAGSET( p->tcph, ( TH_SYN | TH_ACK ) ) ) { scb->session_state |= STREAM_STATE_SYN_ACK; if (!require3Way || midstream_allowed) { if(NewTcpSession(p, scb, tdb, s5TcpPolicy)== -1 ) { PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } tcpssn = (TcpSession *)scb->proto_specific_data->data; new_ssn = 1; } NormalTrackECN(tcpssn, p->tcph, require3Way); /* Nothing left todo here */ } else if( TCP_ISFLAGSET( p->tcph, TH_ACK ) && !TCP_ISFLAGSET( p->tcph, TH_RST ) && ( scb->session_state & STREAM_STATE_SYN_ACK ) ) { /* TODO: do we need to verify the ACK field is >= the seq of the SYN-ACK? */ /* 3-way Handshake complete, create TCP session */ scb->session_state |= STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED; if(NewTcpSession(p, scb, tdb, s5TcpPolicy)== -1 ) { PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } tcpssn = (TcpSession *)scb->proto_specific_data->data; new_ssn = 1; NormalTrackECN(tcpssn, p->tcph, require3Way); StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_ESTABLISHED); } else if ((p->dsize > 0) && (!require3Way || midstream_allowed)) { // TBD-EDM not sure we need this here... // /* need to figure out direction, etc Assume from client, can update later */ if (p->sp > p->dp) { scb->ha_state.direction = FROM_CLIENT; IP_COPY_VALUE(scb->client_ip, GET_SRC_IP(p)); scb->client_port = p->tcph->th_sport; IP_COPY_VALUE(scb->server_ip, GET_DST_IP(p)); scb->server_port = p->tcph->th_dport; } else { scb->ha_state.direction = FROM_SERVER; IP_COPY_VALUE(scb->client_ip, GET_DST_IP(p)); scb->client_port = p->tcph->th_dport; IP_COPY_VALUE(scb->server_ip, GET_SRC_IP(p)); scb->server_port = p->tcph->th_sport; } scb->session_state |= STREAM_STATE_MIDSTREAM; scb->ha_state.session_flags |= SSNFLAG_MIDSTREAM; #ifdef STREAM_DEBUG_ENABLED if (ScReadMode()) { /* If we're in readback mode... may only have one packet. * That being packet with the exploit being tested, so * mark this session as established, so rule option * 'flow:established' works correctly. */ STREAM_DEBUG_WRAP( char timestamp[TIMEBUF_SIZE]; char src_addr[17]; char dst_addr[17]; memset((char *)timestamp, 0, TIMEBUF_SIZE); ts_print((struct timeval *) &p->pkth->ts, timestamp); SnortSnprintf(src_addr, 17, "%s", inet_ntoa(GET_SRC_ADDR(p))); SnortSnprintf(dst_addr, 17, "%s", inet_ntoa(GET_DST_ADDR(p))); DebugMessage(DEBUG_STREAM_STATE, "Session not established" "on midstream-pickup of data packet. Will be marked" "as established when other side is seen. Packet Info:" "Time: %s\tSrc: %s:%d\tDst: %s:%d\n", timestamp, src_addr, p->sp, dst_addr, p->dp); ); #ifdef MIMIC_STREAM4_MIDSTREAM_BEHAVIOUR scb->session_state |= STREAM_STATE_ESTABLISHED; scb->ha_state.session_flags |= SSNFLAG_ESTABLISHED; #endif } #endif if(NewTcpSession(p, scb, tdb, s5TcpPolicy)== -1 ) { PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } tcpssn = (TcpSession *)scb->proto_specific_data->data; new_ssn = 1; NormalTrackECN(tcpssn, p->tcph, require3Way); if (scb->session_state & STREAM_STATE_ESTABLISHED) StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_ESTABLISHED); } else if (p->dsize == 0) { /* Already have a scb, but no tcp session. * Probably just an ACK of already sent data (that * we missed). */ /* Do nothing. */ PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } /* This flag is set in session_expect when the event handler is set. * It is assumed that if we need to register the event handler before * analyzing the packet, we will also want to begin packet reassembly * immediately. If a preprocessor does not want to set reassembly in * both directory, it will call set_reassembly again with the correct * parameters. */ if (p->packet_flags & PKT_EARLY_REASSEMBLY) { p->packet_flags &= ~PKT_EARLY_REASSEMBLY; StreamSetReassemblyTcp(scb, STREAM_FLPOLICY_FOOTPRINT, SSN_DIR_BOTH, STREAM_FLPOLICY_SET_ABSOLUTE); } } else { /* If session is already marked as established */ if (!(scb->session_state & STREAM_STATE_ESTABLISHED)) { if (hash_node) ignore = StreamExpectProcessNode(p, scb, hash_node); else ignore = StreamExpectCheck(p, scb); if (p->packet_flags & PKT_EARLY_REASSEMBLY) { p->packet_flags &= ~PKT_EARLY_REASSEMBLY; StreamSetReassemblyTcp(scb, STREAM_FLPOLICY_FOOTPRINT, ~(ignore & SSN_DIR_BOTH) & SSN_DIR_BOTH, //Directions to not ignore STREAM_FLPOLICY_SET_ABSOLUTE); } if(!require3Way || midstream_allowed) { /* If not requiring 3-way Handshake... */ /* TCP session created on TH_SYN above, * or maybe on SYN-ACK, or anything else */ /* Need to update Lightweight session state */ if (TCP_ISFLAGSET(p->tcph, (TH_SYN|TH_ACK))) { /* SYN-ACK from server */ if (scb->session_state != STREAM_STATE_NONE) { scb->session_state |= STREAM_STATE_SYN_ACK; } } else if (TCP_ISFLAGSET(p->tcph, TH_ACK) && (scb->session_state & STREAM_STATE_SYN_ACK)) { scb->session_state |= STREAM_STATE_ACK | STREAM_STATE_ESTABLISHED; StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_ESTABLISHED); } } } #ifdef NORMALIZER if (TCP_ISFLAGSET(p->tcph, TH_SYN)) NormalTrackECN(tcpssn, p->tcph, require3Way); #endif } two_way_traffic = TwoWayTraffic(scb); /* figure out direction of this packet */ session_api->set_packet_direction_flag(p, scb); if(p->packet_flags & PKT_FROM_SERVER) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from server\n");); scb->ha_state.session_flags |= SSNFLAG_SEEN_SERVER; if (tcpssn) { talker = &tcpssn->server; listener = &tcpssn->client; } STREAM_DEBUG_WRAP( t = "Server"; l = "Client"); if ( talker && talker->s_mgr.state == TCP_STATE_LISTEN && ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) ) { eventcode |= EVENT_4WHS; } /* If we picked this guy up midstream, finish the initialization */ if ((scb->session_state & STREAM_STATE_MIDSTREAM) && !(scb->session_state & STREAM_STATE_ESTABLISHED)) { FinishServerInit(p, tdb, tcpssn); if((p->tcph->th_flags & TH_ECE) && scb->ha_state.session_flags & SSNFLAG_ECN_CLIENT_QUERY) { scb->ha_state.session_flags |= SSNFLAG_ECN_SERVER_REPLY; } if (scb->ha_state.session_flags & SSNFLAG_SEEN_CLIENT) { // should TCP state go to established too? scb->session_state |= STREAM_STATE_ESTABLISHED; scb->ha_state.session_flags |= SSNFLAG_ESTABLISHED; StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_ESTABLISHED); } } #ifdef ACTIVE_RESPONSE if ( !scb->inner_server_ttl ) SetTTL(scb, p, 0); #endif } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Updating on packet from client\n");); /* if we got here we had to see the SYN already... */ scb->ha_state.session_flags |= SSNFLAG_SEEN_CLIENT; if (tcpssn) { talker = &tcpssn->client; listener = &tcpssn->server; } STREAM_DEBUG_WRAP( t = "Client"; l = "Server";); if ((scb->session_state & STREAM_STATE_MIDSTREAM) && !(scb->session_state & STREAM_STATE_ESTABLISHED)) { /* Midstream and seen server. */ if (scb->ha_state.session_flags & SSNFLAG_SEEN_SERVER) { scb->session_state |= STREAM_STATE_ESTABLISHED; scb->ha_state.session_flags |= SSNFLAG_ESTABLISHED; } } #ifdef ACTIVE_RESPONSE if ( !scb->inner_client_ttl ) SetTTL(scb, p, 1); #endif } /* Check if there is a reload and policy is changed */ if( listener && talker && listener->tcp_policy != s5TcpPolicy && talker->tcp_policy != s5TcpPolicy) { listener->tcp_policy = s5TcpPolicy; talker->tcp_policy = StreamPolicyLookup(scb, GET_SRC_IP(p)); } /* * check for SYN on reset session */ if ((scb->ha_state.session_flags & SSNFLAG_RESET) && (p->tcph->th_flags & TH_SYN)) { if ((!tcpssn) || ((listener->s_mgr.state == TCP_STATE_CLOSED) || (talker->s_mgr.state == TCP_STATE_CLOSED))) { /* Listener previously issued a reset */ /* Talker is re-SYN-ing */ struct session_state_cleanup_cache sscc; cleanup_cache_session_state( scb, &sscc ); TcpSessionCleanupWithFreeApplicationData(scb); cleanup_log_session_state( "SYN on RST ssn", &sscc ); if (p->tcph->th_flags & TH_RST) { /* Got SYN/RST. We're done. */ NormalTrimPayloadIfSyn(p, 0, tdb); NormalTrimPayloadIfRst(p, 0, tdb); tcpssn = NULL; PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_RST; } else if (TCP_ISFLAGSET(p->tcph, TH_SYN) && !TCP_ISFLAGSET(p->tcph, TH_ACK)) { scb->ha_state.direction = FROM_CLIENT; IP_COPY_VALUE(scb->client_ip, GET_SRC_IP(p)); scb->client_port = p->tcph->th_sport; IP_COPY_VALUE(scb->server_ip, GET_DST_IP(p)); scb->server_port = p->tcph->th_dport; scb->session_state = STREAM_STATE_SYN; #ifdef ACTIVE_RESPONSE SetTTL(scb, p, 1); #endif if(NewTcpSession(p, scb, tdb, s5TcpPolicy)== -1 ) { PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } tcpssn = (TcpSession *)scb->proto_specific_data->data; new_ssn = 1; NormalTrackECN(tcpssn, p->tcph, require3Way); if (tcpssn) { listener = &tcpssn->server; talker = &tcpssn->client; } scb->ha_state.session_flags = SSNFLAG_SEEN_CLIENT; } else if (TCP_ISFLAGSET(p->tcph, (TH_SYN|TH_ACK))) { scb->ha_state.direction = FROM_SERVER; IP_COPY_VALUE(scb->client_ip, GET_DST_IP(p)); scb->client_port = p->tcph->th_dport; IP_COPY_VALUE(scb->server_ip, GET_SRC_IP(p)); scb->server_port = p->tcph->th_sport; scb->session_state = STREAM_STATE_SYN_ACK; #ifdef ACTIVE_RESPONSE SetTTL(scb, p, 0); #endif if(NewTcpSession(p, scb, tdb, s5TcpPolicy)== -1 ) { PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } tcpssn = (TcpSession *)scb->proto_specific_data->data; new_ssn = 1; NormalTrackECN(tcpssn, p->tcph, require3Way); if (tcpssn) { listener = &tcpssn->client; talker = &tcpssn->server; } scb->ha_state.session_flags = SSNFLAG_SEEN_SERVER; } } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got SYN pkt on reset ssn, re-SYN-ing\n");); } if (((p->packet_flags & PKT_FROM_SERVER) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_CLIENT)) || ((p->packet_flags & PKT_FROM_CLIENT) && (scb->ha_state.ignore_direction & SSN_DIR_FROM_SERVER))) { if (talker && (talker->flags & TF_FORCE_FLUSH)) { StreamFlushTalker(p, scb); talker->flags &= ~TF_FORCE_FLUSH; } if (listener && (listener->flags & TF_FORCE_FLUSH)) { StreamFlushListener(p, scb); listener->flags &= ~TF_FORCE_FLUSH; } p->packet_flags |= PKT_IGNORE; retcode |= ACTION_DISABLE_INSPECTION; } /* Check if the session is to be ignored */ if ( !ScPafEnabled() ) { if (hash_node) ignore = StreamExpectProcessNode(p, scb, hash_node); else ignore = StreamExpectCheck(p, scb); if (ignore) { DisableInspection(scb, p, ignore); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } } /* Handle data on SYN */ if (!IsTCPFastOpenPkt(p) && p->dsize && TCP_ISFLAGSET(p->tcph, TH_SYN)) { /* MacOS accepts data on SYN, so don't alert if policy is MACOS */ if (StreamGetPolicy(scb, s5TcpPolicy, FROM_CLIENT) != STREAM_POLICY_MACOS) { #ifdef NORMALIZER NormalTrimPayloadIfSyn(p, 0, tdb); // remove data on SYN if ( Normalize_GetMode(snort_conf, NORM_TCP_TRIM_SYN) == NORM_MODE_OFF) #endif { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got data on SYN packet, not processing it\n");); //EventDataOnSyn(s5TcpPolicy); eventcode |= EVENT_DATA_ON_SYN; retcode |= ACTION_BAD_PKT; } } } if (!tcpssn) { LogTcpEvents(s5TcpPolicy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " %s [talker] state: %s\n", t, state_names[talker->s_mgr.state]);); STREAM_DEBUG_WRAP(PrintFlushMgr(&talker->flush_mgr);); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " %s state: %s(%d)\n", l, state_names[listener->s_mgr.state], listener->s_mgr.state);); STREAM_DEBUG_WRAP(PrintFlushMgr(&listener->flush_mgr);); // may find better placement to eliminate redundant flag checks if(p->tcph->th_flags & TH_SYN) talker->s_mgr.sub_state |= SUB_SYN_SENT; if(p->tcph->th_flags & TH_ACK) talker->s_mgr.sub_state |= SUB_ACK_SENT; /* * process SYN ACK on unestablished sessions */ if( (TCP_STATE_SYN_SENT == listener->s_mgr.state) && (TCP_STATE_LISTEN == talker->s_mgr.state) ) { if(p->tcph->th_flags & TH_ACK) { /* * make sure we've got a valid segment */ if(!IsBetween(listener->l_unackd, listener->l_nxt_seq, tdb->ack)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Pkt ack is out of bounds, bailing!\n");); Discard(); NormalTrimPayloadIfWin(p, 0, tdb); LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } } talker->flags |= StreamGetTcpTimestamp(p, &tdb->ts, 0); if (tdb->ts == 0) talker->flags |= TF_TSTAMP_ZERO; /* * catch resets sent by server */ if(p->tcph->th_flags & TH_RST) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "got RST\n");); NormalTrimPayloadIfRst(p, 0, tdb); /* Reset is valid when in SYN_SENT if the * ack field ACKs the SYN. */ if(ValidRstSynSent(listener, tdb)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "got RST, closing talker\n");); /* Reset is valid */ /* Mark session as reset... Leave it around so that any * additional data sent from one side or the other isn't * processed (and is dropped in inline mode). */ scb->ha_state.session_flags |= SSNFLAG_RESET; talker->s_mgr.state = TCP_STATE_CLOSED; StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_CLOSING); /* Leave listener open, data may be in transit */ LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_RST; } /* Reset not valid. */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bad sequence number, bailing\n");); Discard(); eventcode |= EVENT_BAD_RST; NormalDropPacket(p); LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode; } /* * finish up server init */ if(p->tcph->th_flags & TH_SYN) { FinishServerInit(p, tdb, tcpssn); if (talker->flags & TF_TSTAMP) { talker->ts_last_pkt = p->pkth->ts.tv_sec; talker->ts_last = tdb->ts; } STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Finish server init got called!\n");); } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Finish server init didn't get called!\n");); } if((p->tcph->th_flags & TH_ECE) && scb->ha_state.session_flags & SSNFLAG_ECN_CLIENT_QUERY) { scb->ha_state.session_flags |= SSNFLAG_ECN_SERVER_REPLY; } /* * explicitly set the state */ listener->s_mgr.state = TCP_STATE_SYN_SENT; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Accepted SYN ACK\n");); } /* * scale the window. Only if BOTH client and server specified * wscale option as part of 3-way handshake. * This is per RFC 1323. */ if ((talker->flags & TF_WSCALE) && (listener->flags & TF_WSCALE)) { tdb->win <<= talker->wscale; } /* Check for session hijacking -- compare mac address to the ones * that were recorded at session startup. */ #ifdef DAQ_PKT_FLAG_PRE_ROUTING if (!(p->pkth->flags & DAQ_PKT_FLAG_PRE_ROUTING) && listener->tcp_policy->flags & STREAM_CONFIG_CHECK_SESSION_HIJACKING) #else if (listener->tcp_policy->flags & STREAM_CONFIG_CHECK_SESSION_HIJACKING) #endif { eventcode |= ValidMacAddress(talker, listener, p); } /* Check timestamps */ ts_action = ValidTimestamp(talker, listener, tdb, p, &eventcode, &got_ts); /* * check RST validity */ if(p->tcph->th_flags & TH_RST) { NormalTrimPayloadIfRst(p, 0, tdb); if(ValidRst(scb, listener, tdb)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got RST, bailing\n");); if ( listener->s_mgr.state == TCP_STATE_FIN_WAIT_1 || listener->s_mgr.state == TCP_STATE_FIN_WAIT_2 || listener->s_mgr.state == TCP_STATE_CLOSE_WAIT || listener->s_mgr.state == TCP_STATE_LAST_ACK || listener->s_mgr.state == TCP_STATE_CLOSING ) { StreamFlushTalker(p, scb); StreamFlushListener(p, scb); scb->ha_state.session_flags |= SSNFLAG_FREE_APP_DATA; } scb->ha_state.session_flags |= SSNFLAG_RESET; talker->s_mgr.state = TCP_STATE_CLOSED; talker->s_mgr.sub_state |= SUB_RST_SENT; StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_CLOSING); #ifdef NORMALIZER if ( Normalize_GetMode(snort_conf, NORM_TCP_IPS) == NORM_MODE_ON ) listener->s_mgr.state = TCP_STATE_CLOSED; /* else for ids: leave listener open, data may be in transit */ #endif LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_RST; } /* Reset not valid. */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bad sequence number, bailing\n");); Discard(); eventcode |= EVENT_BAD_RST; NormalDropPacket(p); LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ts_action; } else { /* check for valid seqeuence/retrans */ bool before_win_base = false; if( s5TcpPolicy->policy != STREAM_POLICY_NOACK ) { if ( (listener->s_mgr.state >= TCP_STATE_ESTABLISHED) && !ValidSeq(p, scb, listener, tdb, &before_win_base) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bad sequence number, bailing\n");); Discard(); if(before_win_base) DisableAppPreprocessors(p); NormalTrimPayloadIfWin(p, 0, tdb); LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ts_action; } } } if (ts_action != ACTION_NOTHING) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bad timestamp, bailing\n");); Discard(); // this packet was normalized elsewhere LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ts_action; } /* * update PAWS timestamps */ STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "PAWS update tdb->seq %lu > listener->r_win_base %lu\n", tdb->seq, listener->r_win_base);); if(got_ts && SEQ_EQ(listener->r_win_base, tdb->seq)) { if((int32_t)(tdb->ts - talker->ts_last) >= 0 || (uint32_t)p->pkth->ts.tv_sec >= talker->ts_last_pkt+PAWS_24DAYS) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "updating timestamps...\n");); talker->ts_last = tdb->ts; talker->ts_last_pkt = p->pkth->ts.tv_sec; } } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "not updating timestamps...\n");); } /* * check for repeat SYNs */ if ( !new_ssn && ((p->tcph->th_flags & (TH_SYN|TH_ACK)) == TH_SYN) ) { int action; #ifdef NORMALIZER if ( !SEQ_EQ(tdb->seq, talker->isn) && NormalDropPacket(p) ) action = ACTION_BAD_PKT; else #endif if ( talker->s_mgr.state >= TCP_STATE_ESTABLISHED ) action = RepeatedSyn(listener, talker, tdb, tcpssn); else action = ACTION_NOTHING; if (action != ACTION_NOTHING) { /* got a bad SYN on the session, alert! */ eventcode |= EVENT_SYN_ON_EST; LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | action; } } /* * Check that the window is within the limits */ if ( s5TcpPolicy->policy != STREAM_POLICY_NOACK ) { if (listener->tcp_policy->max_window && (tdb->win > listener->tcp_policy->max_window)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got window that was beyond the allowed policy value, bailing\n");); /* got a window too large, alert! */ eventcode |= EVENT_WINDOW_TOO_LARGE; Discard(); NormalDropPacket(p); LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } else if ((p->packet_flags & PKT_FROM_CLIENT) && (tdb->win <= SLAM_MAX) && (tdb->ack == listener->isn + 1) && !(p->tcph->th_flags & (TH_FIN|TH_RST)) && !(scb->ha_state.session_flags & SSNFLAG_MIDSTREAM)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Window slammed shut!\n");); /* got a window slam alert! */ eventcode |= EVENT_WINDOW_SLAM; Discard(); #ifdef NORMALIZER if ( NormalDropPacket(p) ) { LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } #endif } } if(talker->s_mgr.state_queue != TCP_STATE_NONE) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Found queued state transition on ack 0x%X, " "current 0x%X!\n", talker->s_mgr.transition_seq, tdb->ack);); if(tdb->ack == talker->s_mgr.transition_seq) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "accepting transition!\n");); talker->s_mgr.state = talker->s_mgr.state_queue; talker->s_mgr.state_queue = TCP_STATE_NONE; //If ACK is received for Queued FIN before previous data is received. Process FIN transition if(listener->s_mgr.state == TCP_STATE_ESTABLISHED) { listener->l_nxt_seq++; talker->r_nxt_ack = tdb->ack; listener->s_mgr.sub_state |= SUB_FIN_SENT; listener->s_mgr.state = TCP_STATE_FIN_WAIT_1; StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_CLOSING); } } } /* * process ACK flags */ if(p->tcph->th_flags & TH_ACK) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got an ACK...\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " %s [listener] state: %s\n", l, state_names[listener->s_mgr.state]);); switch(listener->s_mgr.state) { case TCP_STATE_SYN_SENT: if ( !require3Way || midstream_allowed ) break; // fall thru ... case TCP_STATE_SYN_RCVD: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "listener state is SYN_SENT...\n");); if(IsBetween(listener->l_unackd, listener->l_nxt_seq, tdb->ack) && ((!require3Way || midstream_allowed) || ((talker->s_mgr.sub_state == SUB_SETUP_OK) && (listener->s_mgr.sub_state == SUB_SETUP_OK)) )) { UpdateSsn(p, listener, talker, tdb); scb->ha_state.session_flags |= SSNFLAG_ESTABLISHED; scb->session_state |= STREAM_STATE_ESTABLISHED; listener->s_mgr.state = TCP_STATE_ESTABLISHED; talker->s_mgr.state = TCP_STATE_ESTABLISHED; StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_ESTABLISHED); /* Indicate this packet completes 3-way handshake */ p->packet_flags |= PKT_STREAM_TWH; } talker->flags |= got_ts; if(got_ts && SEQ_EQ(listener->r_nxt_ack, tdb->seq)) { talker->ts_last_pkt = p->pkth->ts.tv_sec; talker->ts_last = tdb->ts; } break; case TCP_STATE_ESTABLISHED: /* Handle out-of-order/OOO ACK */ if ((Normalize_GetMode(snort_conf, NORM_TCP_IPS) == NORM_MODE_ON) && (SEQ_GT(tdb->ack, listener->l_nxt_seq))) { NormalDropPacket(p); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } UpdateSsn(p, listener, talker, tdb); break; case TCP_STATE_CLOSE_WAIT: UpdateSsn(p, listener, talker, tdb); break; case TCP_STATE_FIN_WAIT_1: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "tdb->ack %X >= talker->r_nxt_ack %X\n", tdb->ack, talker->r_nxt_ack);); if ( SEQ_EQ(tdb->ack, listener->l_nxt_seq) ) { #ifdef NORMALIZER if ( (listener->os_policy == STREAM_POLICY_WINDOWS) && (tdb->win == 0) ) { eventcode |= EVENT_WIN_SZ_0_TCP_FIN_WAIT_1; } #endif UpdateSsn(p, listener, talker, tdb); listener->s_mgr.state = TCP_STATE_FIN_WAIT_2; if ( (p->tcph->th_flags & TH_FIN) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "seq ok, setting state!\n");); if (talker->s_mgr.state_queue == TCP_STATE_NONE) { talker->s_mgr.state = TCP_STATE_LAST_ACK; EndOfFileHandle(p, (TcpSession *)scb->proto_specific_data->data); } if ( scb->ha_state.session_flags & SSNFLAG_MIDSTREAM ) { // FIXTHIS this should be handled below in fin section // but midstream sessions fail the seq test listener->s_mgr.state_queue = TCP_STATE_TIME_WAIT; listener->s_mgr.transition_seq = tdb->end_seq; listener->s_mgr.expected_flags = TH_ACK; } } else if (listener->s_mgr.state_queue == TCP_STATE_CLOSING) { listener->s_mgr.state_queue = TCP_STATE_TIME_WAIT; listener->s_mgr.transition_seq = tdb->end_seq; listener->s_mgr.expected_flags = TH_ACK; } } else { UpdateSsn(p, listener, talker, tdb); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bad ack!\n");); } break; case TCP_STATE_FIN_WAIT_2: if ( SEQ_GT(tdb->ack, listener->l_nxt_seq) ) { eventcode |= EVENT_BAD_ACK; LogTcpEvents(talker->tcp_policy, eventcode); NormalDropPacket(p); PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_BAD_PKT; } UpdateSsn(p, listener, talker, tdb); break; case TCP_STATE_CLOSING: UpdateSsn(p, listener, talker, tdb); if(SEQ_GEQ(tdb->end_seq, listener->r_nxt_ack)) { listener->s_mgr.state = TCP_STATE_TIME_WAIT; } break; case TCP_STATE_LAST_ACK: UpdateSsn(p, listener, talker, tdb); if ( SEQ_EQ(tdb->ack, listener->l_nxt_seq) ) { listener->s_mgr.state = TCP_STATE_CLOSED; } break; default: // FIXTHIS safe to ignore when inline? break; } } #ifdef HAVE_DAQ_ADDRESS_SPACE_ID tcpssn->priv_ptr = p->pkth->priv_ptr; #endif /* * handle data in the segment */ if(p->dsize) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " %s state: %s(%d) getting data\n", l, state_names[listener->s_mgr.state], listener->s_mgr.state);); // FIN means only that sender is done talking, // other side may continue yapping. if(TCP_STATE_FIN_WAIT_2 == talker->s_mgr.state || TCP_STATE_TIME_WAIT == talker->s_mgr.state) { /* data on a segment when we're not accepting data any more */ /* alert! */ //EventDataOnClosed(talker->tcp_policy); eventcode |= EVENT_DATA_ON_CLOSED; retcode |= ACTION_BAD_PKT; NormalDropPacket(p); } else if (TCP_STATE_CLOSED == talker->s_mgr.state) { /* data on a segment when we're not accepting data any more */ /* alert! */ if (scb->ha_state.session_flags & SSNFLAG_RESET) { // no need to run detection PPs on this packet DisableDetect( p ); //EventDataAfterReset(listener->tcp_policy); if ( talker->s_mgr.sub_state & SUB_RST_SENT ) eventcode |= EVENT_DATA_AFTER_RESET; else eventcode |= EVENT_DATA_AFTER_RST_RCVD; } else { //EventDataOnClosed(listener->tcp_policy); eventcode |= EVENT_DATA_ON_CLOSED; } retcode |= ACTION_BAD_PKT; NormalDropPacket(p); } else if ((TCP_STATE_FIN_WAIT_1 == talker->s_mgr.state) && SEQ_GEQ(tdb->seq, (listener->s_mgr.transition_seq - 1))) { /* Alert! : Data on a segment when we're not accepting data any more */ eventcode |= EVENT_DATA_ON_CLOSED; retcode |= ACTION_BAD_PKT; NormalDropPacket(p); } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Queuing data on listener, t %s, l %s...\n", flush_policy_names[talker->flush_mgr.flush_policy], flush_policy_names[listener->flush_mgr.flush_policy]);); #ifdef NORMALIZER if( s5TcpPolicy->policy != STREAM_POLICY_NOACK ) { // these normalizations can't be done if we missed setup. and // window is zero in one direction until we've seen both sides. // Avoid this normalization for Asymmetric traffic if (( !(scb->ha_state.session_flags & SSNFLAG_MIDSTREAM) ) && TwoWayTraffic(scb)) { // sender of syn w/mss limits payloads from peer // since we store mss on sender side, use listener mss // same reasoning for window size StreamTracker* st = listener; // trim to fit in window and mss as needed NormalTrimPayloadIfWin(p, (st->r_win_base + st->l_window) - st->r_nxt_ack, tdb); if ( st->mss ) NormalTrimPayloadIfMss(p, st->mss, tdb); NormalCheckECN(tcpssn, p); } } #endif /* * dunno if this is RFC but fragroute testing expects it * for the record, I've seen FTP data sessions that send * data packets with no tcp flags set */ if ((p->tcph->th_flags != 0) || (s5TcpPolicy->policy == STREAM_POLICY_LINUX) || (s5TcpPolicy->policy == STREAM_POLICY_NOACK)) { ProcessTcpData(p, listener, tcpssn, tdb, s5TcpPolicy); //Check if all segments are received. Process FIN transition if(checkFINTransitionStatus(p, listener)) process_fin = true; } else { eventcode |= EVENT_DATA_WITHOUT_FLAGS; NormalDropPacket(p); } } } if((p->tcph->th_flags & TH_FIN) || process_fin) //FIN is received or process Queued FIN { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Got an FIN...\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " %s state: %s(%d)\n", l, state_names[talker->s_mgr.state], talker->s_mgr.state);); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "checking ack (0x%X) vs nxt_ack (0x%X)\n", tdb->end_seq, listener->r_win_base);); if(SEQ_LT(tdb->end_seq,listener->r_win_base)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "FIN inside r_win_base, bailing\n");); goto dupfin; } else { //FIN is in order or we need to process FIN from state_queue if((tdb->end_seq == listener->r_nxt_ack) || process_fin || (talker->s_mgr.state > TCP_STATE_ESTABLISHED) || (listener->flush_mgr.flush_policy == STREAM_FLPOLICY_IGNORE)) { // need substate since we don't change state immediately if ( (talker->s_mgr.state >= TCP_STATE_ESTABLISHED) && !(talker->s_mgr.sub_state & SUB_FIN_SENT) ) { talker->l_nxt_seq++; listener->r_nxt_ack++; talker->s_mgr.sub_state |= SUB_FIN_SENT; #ifdef NORMALIZER if ((listener->flush_mgr.flush_policy != STREAM_FLPOLICY_PROTOCOL) && (listener->flush_mgr.flush_policy != STREAM_FLPOLICY_PROTOCOL_IPS) && (listener->flush_mgr.flush_policy != STREAM_FLPOLICY_PROTOCOL_NOACK) && Normalize_GetMode(snort_conf, NORM_TCP_IPS) == NORM_MODE_ON) { p->packet_flags |= PKT_PDU_TAIL; } #endif } switch(talker->s_mgr.state) { case TCP_STATE_SYN_RCVD: case TCP_STATE_ESTABLISHED: if (talker->s_mgr.state_queue == TCP_STATE_CLOSE_WAIT) { talker->s_mgr.state_queue = TCP_STATE_CLOSING; } talker->s_mgr.state = TCP_STATE_FIN_WAIT_1; EndOfFileHandle(p, (TcpSession *) scb->proto_specific_data->data); #ifdef NORMALIZER if ( !p->dsize ) CheckFlushPolicyOnData( ( ( StreamConfig * ) scb->stream_config )->tcp_config, tcpssn, talker, listener, tdb, p); #endif StreamUpdatePerfBaseState(&sfBase, scb, TCP_STATE_CLOSING); break; case TCP_STATE_CLOSE_WAIT: talker->s_mgr.state = TCP_STATE_LAST_ACK; break; case TCP_STATE_FIN_WAIT_1: if (!p->dsize) RetransmitHandle(p, tcpssn); break; default: /* all other states stay where they are */ break; } if (!process_fin && ((talker->s_mgr.state == TCP_STATE_FIN_WAIT_1) || (talker->s_mgr.state == TCP_STATE_LAST_ACK))) { uint32_t end_seq = ( scb->ha_state.session_flags & SSNFLAG_MIDSTREAM ) ? tdb->end_seq-1 : tdb->end_seq; if ( (listener->s_mgr.expected_flags == TH_ACK) && SEQ_GEQ(end_seq, listener->s_mgr.transition_seq) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "FIN beyond previous, ignoring\n");); eventcode |= EVENT_BAD_FIN; LogTcpEvents(talker->tcp_policy, eventcode); NormalDropPacket(p); PREPROC_PROFILE_END(s5TcpStatePerfStats); #ifdef HAVE_DAQ_ADDRESS_SPACE_ID tcpssn->priv_ptr = NULL; #endif return retcode | ACTION_BAD_PKT; } } if(!process_fin) { switch ( listener->s_mgr.state ) { case TCP_STATE_ESTABLISHED: listener->s_mgr.state_queue = TCP_STATE_CLOSE_WAIT; listener->s_mgr.transition_seq = tdb->end_seq + 1; listener->s_mgr.expected_flags = TH_ACK; break; case TCP_STATE_FIN_WAIT_1: listener->s_mgr.state_queue = TCP_STATE_CLOSING; listener->s_mgr.transition_seq = tdb->end_seq + 1; listener->s_mgr.expected_flags = TH_ACK; break; case TCP_STATE_FIN_WAIT_2: listener->s_mgr.state_queue = TCP_STATE_TIME_WAIT; listener->s_mgr.transition_seq = tdb->end_seq + 1; listener->s_mgr.expected_flags = TH_ACK; break; case TCP_STATE_CLOSED: listener->s_mgr.transition_seq = tdb->end_seq + 1; break; } } } else { //OOO FIN received if((listener->s_mgr.state_queue == TCP_STATE_CLOSE_WAIT) && SEQ_LT(tdb->end_seq,listener->s_mgr.transition_seq)) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "FIN inside transition_seq, bailing\n");); goto dupfin; } if ((listener->s_mgr.state_queue == TCP_STATE_CLOSE_WAIT) || (talker->s_mgr.state == TCP_STATE_FIN_WAIT_1) || (talker->s_mgr.state == TCP_STATE_LAST_ACK)) { uint32_t end_seq = ( scb->ha_state.session_flags & SSNFLAG_MIDSTREAM ) ? tdb->end_seq-1 : tdb->end_seq; if ( (listener->s_mgr.expected_flags == TH_ACK) && SEQ_GEQ(end_seq, listener->s_mgr.transition_seq) ) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "FIN beyond previous, ignoring\n");); eventcode |= EVENT_BAD_FIN; LogTcpEvents(talker->tcp_policy, eventcode); NormalDropPacket(p); PREPROC_PROFILE_END(s5TcpStatePerfStats); #ifdef HAVE_DAQ_ADDRESS_SPACE_ID tcpssn->priv_ptr = NULL; #endif return retcode | ACTION_BAD_PKT; } } switch ( listener->s_mgr.state ) { case TCP_STATE_ESTABLISHED: listener->s_mgr.state_queue = TCP_STATE_CLOSE_WAIT; listener->s_mgr.transition_seq = tdb->end_seq + 1; listener->s_mgr.expected_flags = TH_ACK; break; case TCP_STATE_CLOSED: listener->s_mgr.transition_seq = tdb->end_seq + 1; break; } } } } dupfin: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " %s [talker] state: %s\n", t, state_names[talker->s_mgr.state]);); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, " %s state: %s(%d)\n", l, state_names[listener->s_mgr.state], listener->s_mgr.state);); /* * handle TIME_WAIT timer stuff */ /* Handle Asymmetric connection closing, we will see only one * direction pkts. FIN_ACK handling. * Do not set state to closed prematurely when handling retry packets * to avoid retry loop - CSCvc08844. */ if((!TwoWayTraffic(scb) && #ifdef DAQ_PKT_FLAG_RETRY_PACKET !(p->pkth->flags & DAQ_PKT_FLAG_RETRY_PACKET) && #endif (talker->s_mgr.state >= TCP_STATE_FIN_WAIT_1 || listener->s_mgr.state >= TCP_STATE_FIN_WAIT_1))) { if (TCP_ISFLAGSET(p->tcph, (TH_FIN|TH_ACK))) { if(talker->s_mgr.state >= TCP_STATE_FIN_WAIT_1) talker->s_mgr.state = TCP_STATE_CLOSED; if(listener->s_mgr.state >= TCP_STATE_FIN_WAIT_1) listener->s_mgr.state = TCP_STATE_CLOSED; listener->flags |= TF_FORCE_FLUSH; } } if((talker->s_mgr.state == TCP_STATE_TIME_WAIT && listener->s_mgr.state == TCP_STATE_CLOSED) || (listener->s_mgr.state == TCP_STATE_TIME_WAIT && talker->s_mgr.state == TCP_STATE_CLOSED) || (listener->s_mgr.state == TCP_STATE_TIME_WAIT && talker->s_mgr.state == TCP_STATE_TIME_WAIT)|| (!TwoWayTraffic(scb)&& (talker->s_mgr.state == TCP_STATE_CLOSED || listener->s_mgr.state == TCP_STATE_CLOSED))) { //dropssn: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Session terminating, flushing session buffers\n");); #ifdef HAVE_DAQ_ADDRESS_SPACE_ID tcpssn->priv_ptr = NULL; #endif if ( p->tcph->th_flags & TH_ACK ) CheckFlushPolicyOnAck( ( ( StreamConfig * ) scb->stream_config )->tcp_config, tcpssn, talker, listener, tdb, p); if(p->packet_flags & PKT_FROM_SERVER) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "flushing FROM_SERVER\n");); if(talker->seg_bytes_logical) { uint32_t flushed = flush_stream(tcpssn, talker, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, PKT_FROM_CLIENT); if(flushed) { // FIXTHIS - these calls redundant? purge_alerts(talker, talker->r_win_base, (void *)tcpssn->scb); purge_to_seq(tcpssn, talker, talker->seglist->seq + flushed); } } if(listener->seg_bytes_logical) { uint32_t flushed = flush_stream(tcpssn, listener, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, PKT_FROM_SERVER); if(flushed) { purge_alerts(listener, listener->r_win_base, (void *)tcpssn->scb); purge_to_seq(tcpssn, listener, listener->seglist->seq + flushed); } } } else { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "flushing FROM_CLIENT\n");); if(listener->seg_bytes_logical) { uint32_t flushed = flush_stream(tcpssn, listener, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, PKT_FROM_CLIENT); if(flushed) { purge_alerts(listener, listener->r_win_base, (void *)tcpssn->scb); purge_to_seq(tcpssn, listener, listener->seglist->seq + flushed); } } if(talker->seg_bytes_logical) { uint32_t flushed = flush_stream(tcpssn, talker, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, PKT_FROM_SERVER); if(flushed) { purge_alerts(talker, talker->r_win_base,(void *)tcpssn->scb); purge_to_seq(tcpssn, talker, talker->seglist->seq + flushed); } } } LogTcpEvents(listener->tcp_policy, eventcode); /* The last ACK is a part of the session. Delete the session after processing is complete. */ TcpSessionCleanup(scb, 0); scb->session_state |= STREAM_STATE_CLOSED; PREPROC_PROFILE_END(s5TcpStatePerfStats); return retcode | ACTION_LWSSN_CLOSED; } else if(listener->s_mgr.state == TCP_STATE_CLOSED && talker->s_mgr.state == TCP_STATE_SYN_SENT) { if(p->tcph->th_flags & TH_SYN && !(p->tcph->th_flags & TH_ACK) && !(p->tcph->th_flags & TH_RST)) { session_api->set_expire_timer(p, scb, s5TcpPolicy->session_timeout); } } if((listener->flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_NOACK) || (listener->flush_mgr.flush_policy == STREAM_FLPOLICY_PROTOCOL_NOACK)) { uint32_t flushed = 0; flushed = CheckFlushPolicyOnData( ( ( StreamConfig * ) scb->stream_config )->tcp_config, tcpssn, talker, listener, tdb, p); if (flushed) { if(listener->xtradata_mask && extra_data_log) purge_alerts(listener, listener->seglist->seq + flushed, (void *)tcpssn->scb); } } #ifdef NORMALIZER else if ( p->dsize > 0 ) { /* in case of Asymmetric traffic, enfore flush_policy from STREAM_FLPOLICY_FOOTPRINT to STREAM_FLPOLICY_FOOTPRINT_IPS * This will be applied on passive mode sessions, which has policy mode as STREAM_FLPOLICY_FOOTPRINT only */ uint32_t flushed = 0; if (!TwoWayTraffic(scb) && (scb->session_state & ( STREAM_STATE_SYN | STREAM_STATE_SYN_ACK)) && !TCP_ISFLAGSET(p->tcph, TH_SYN)) { if(listener->flush_mgr.flush_policy == STREAM_FLPOLICY_PROTOCOL) listener->flush_mgr.flush_policy = STREAM_FLPOLICY_PROTOCOL_IPS; else if(listener->flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT) listener->flush_mgr.flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS; } flushed = CheckFlushPolicyOnData( ( ( StreamConfig * ) scb->stream_config )->tcp_config, tcpssn, talker, listener, tdb, p); if (!TwoWayTraffic(scb) && flushed && (scb->session_state & ( STREAM_STATE_SYN | STREAM_STATE_SYN_ACK)) && !TCP_ISFLAGSET(p->tcph, TH_SYN)) { purge_to_seq(tcpssn, listener, listener->seglist->seq + flushed); } else if(flushed) { if(listener->xtradata_mask && extra_data_log) purge_alerts(listener, listener->seglist->seq + flushed, (void *)tcpssn->scb); } } #endif if ( p->tcph->th_flags & TH_ACK ) CheckFlushPolicyOnAck( ( ( StreamConfig * ) scb->stream_config )->tcp_config, tcpssn, talker, listener, tdb, p); LogTcpEvents(listener->tcp_policy, eventcode); PREPROC_PROFILE_END(s5TcpStatePerfStats); #ifdef HAVE_DAQ_ADDRESS_SPACE_ID tcpssn->priv_ptr = NULL; #endif return retcode; } // this is for post-ack flushing static inline uint32_t GetReverseDir (const Packet* p) { /* Remember, one side's packets are stored in the * other side's queue. So when talker ACKs data, * we need to check if we're ready to flush. * * If we do decide to flush, the flush IP & port info * is the opposite of the packet -- again because this * is the ACK from the talker and we're flushing packets * that actually came from the listener. */ if ( p->packet_flags & PKT_FROM_SERVER ) return PKT_FROM_CLIENT; else if ( p->packet_flags & PKT_FROM_CLIENT ) return PKT_FROM_SERVER; return 0; } #ifdef NORMALIZER static inline uint32_t GetForwardDir (const Packet* p) { if ( p->packet_flags & PKT_FROM_SERVER ) return PKT_FROM_SERVER; else if ( p->packet_flags & PKT_FROM_CLIENT ) return PKT_FROM_CLIENT; return 0; } static inline int CheckFlushCoercion ( Packet* p, FlushMgr* fm, uint16_t flush_factor ) { if ( !flush_factor ) return 0; if ( p->dsize && (p->dsize < fm->last_size) && (fm->last_count >= flush_factor) ) { fm->last_size = 0; fm->last_count = 0; return 1; } if ( p->dsize > fm->last_size ) fm->last_size = p->dsize; fm->last_count++; return 0; } static inline int CheckFTPFlushCoercion (Packet* p, FlushMgr* fm) { #ifdef HAVE_DAQ_DECRYPTED_SSL if (p->pkth->flags & DAQ_PKT_FLAG_DECRYPTED_SSL) { if (fm->flush) return 1; else return 0; } #endif if( p->dsize && p->dsize != fm->last_size ) { fm->last_size = p->dsize; return 1; } return 0; } #endif static inline bool AutoDisable (StreamTracker* a, StreamTracker* b) { if ( !a->flush_mgr.auto_disable ) return false; a->flush_mgr.flush_policy = STREAM_FLPOLICY_IGNORE; purge_all(a); if ( b->flush_mgr.auto_disable ) { b->flush_mgr.flush_policy = STREAM_FLPOLICY_IGNORE; purge_all(b); } return true; } #ifdef NORMALIZER /* * In case of Pre-ACK mode, check if we need to * block packet so that the payload deos not reach endpoint without * inspection. */ static inline bool check_to_hold_packet(Packet *pkt, PAF_Status paf_state, StreamTracker *trk) { if (((paf_state == PAF_PSEUDO_FLUSH_SEARCH) || (paf_state == PAF_PSEUDO_FLUSH_SKIP)) && !(pkt->packet_flags & PKT_PSEUDO_FLUSH)) { if(trk->held_segment != NULL) return FALSE; return TRUE; } return FALSE; } static inline void hold_packet(Packet *pkt, StreamTracker *trk, StreamSegment *seg) { trk->held_segment = seg; pkt->packet_flags |= PKT_FAST_BLOCK; Active_DropPacket(pkt); } // see flush_pdu_ackd() for details // the key difference is that we operate on forward moving data // because we don't wait until it is acknowledged static inline uint32_t flush_pdu_ips ( StreamTcpConfig *config, TcpSession *ssn, StreamTracker *trk, Packet *pkt, uint64_t *flags ) { bool to_srv = ( *flags == PKT_FROM_CLIENT ); uint16_t srv_port = ( to_srv ? pkt->dp : pkt->sp ); uint32_t total = 0, avail; StreamSegment* seg; PROFILE_VARS; PREPROC_PROFILE_START(s5TcpPAFPerfStats); avail = get_q_sequenced(trk); seg = trk->seglist_next; // * must stop if gap (checked in s5_paf_check) while ( seg && *flags && (total < avail) ) { uint32_t flush_pt; uint32_t size = seg->size; uint32_t end = seg->seq + seg->size; uint32_t pos = s5_paf_position(&trk->paf_state); total += size; if ( s5_paf_initialized(&trk->paf_state) && SEQ_LEQ(end, pos) ) { if (!seg->next && pkt->packet_flags & PKT_PSEUDO_FLUSH) { *flags |= PKT_PSEUDO_FLUSH; } else { seg = seg->next; continue; } } //Used by h2_paf wire_packet = pkt; flush_policy_for_dir = trk->flush_mgr.flush_policy; flush_pt = s5_paf_check( config->paf_config, &trk->paf_state, ssn->scb, seg->payload, size, total, seg->seq, srv_port, flags, trk->flush_mgr.flush_pt); if (check_to_hold_packet(pkt, trk->paf_state.paf, trk)) { hold_packet(pkt, trk, seg); } if (*flags & PKT_PURGE) { //For HTTP/2 case where stream doesnt flush the data //Set the seglist value to SL_BUF_FLUSHED STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "flush_pdu_ips and purge\n");); seg->buffered = SL_BUF_FLUSHED; return 0; } if ( flush_pt > 0 ) { PREPROC_PROFILE_END(s5TcpPAFPerfStats); return flush_pt; } seg = seg->next; } PREPROC_PROFILE_END(s5TcpPAFPerfStats); return 0; } static inline int CheckFlushPolicyOnData( StreamTcpConfig *config, TcpSession *tcpssn, StreamTracker *talker, StreamTracker *listener, TcpDataBlock *tdb, Packet *p) { uint32_t flushed = 0; uint32_t avail; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In CheckFlushPolicyOnData\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Talker flush policy: %s\n", flush_policy_names[talker->flush_mgr.flush_policy]);); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Listener flush policy: %s\n", flush_policy_names[listener->flush_mgr.flush_policy]);); switch(listener->flush_mgr.flush_policy) { case STREAM_FLPOLICY_IGNORE: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_IGNORE\n");); return 0; case STREAM_FLPOLICY_FOOTPRINT_IPS_FTP: { int coerce = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_FOOTPRINT-IPS_FTP\n");); #ifdef NORMALIZER if ( Normalize_GetMode(snort_conf, NORM_TCP_IPS) == NORM_MODE_ON ) { #ifdef DAQ_PKT_FLAG_SSL_DETECTED //Do not do FTP-Normalization for FTPS traffic if((p->pkth->flags & DAQ_PKT_FLAG_SSL_DETECTED) || (p->pkth->flags & DAQ_PKT_FLAG_SSL_SHELLO)) { tcpssn->pp_flags |= PP_FTPTELNET_FTPS; listener->flush_mgr.last_size = 0; } #endif if(!(tcpssn->pp_flags & PP_FTPTELNET_FTPS)) { if(!listener->flush_mgr.last_size) { //Rely on mss if it exists, else use snaplen if((tcpssn->client.mss > 0) && (tcpssn->server.mss > 0)) { uint32_t ftp_data_conn_mss = (tcpssn->client.mss < tcpssn->server.mss) ? tcpssn->client.mss : tcpssn->server.mss; listener->flush_mgr.last_size = ftp_data_conn_mss - p->tcp_options_len; } else listener->flush_mgr.last_size = pkt_snaplen - (p->data - p->pkt); } coerce = CheckFTPFlushCoercion(p, &listener->flush_mgr); if(coerce) CallFTPFlushProcessor(p); listener->flush_mgr.flush = false; talker->flush_mgr.flush = false; } if(!coerce) { if(tcpssn->pp_flags & PP_FTPTELNET_FTPS) coerce = CheckFlushCoercion(p, &listener->flush_mgr, listener->tcp_policy->flush_factor); avail = get_q_sequenced(listener); if ( (avail > 0) && (coerce || (avail >= listener->flush_mgr.flush_pt) || (avail && talker->s_mgr.state == TCP_STATE_FIN_WAIT_1)) ) { uint32_t dir = GetForwardDir(p); if ( talker->s_mgr.state == TCP_STATE_FIN_WAIT_1 ) listener->flags |= TF_FORCE_FLUSH; flushed = flush_to_seq( tcpssn, listener, avail, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, dir); } } } #endif } break; case STREAM_FLPOLICY_FOOTPRINT_IPS: { int coerce; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_FOOTPRINT-IPS\n");); avail = get_q_sequenced(listener); coerce = CheckFlushCoercion( p, &listener->flush_mgr, listener->tcp_policy->flush_factor); if ( (avail > 0) && (coerce || (avail >= listener->flush_mgr.flush_pt) || (avail && talker->s_mgr.state == TCP_STATE_FIN_WAIT_1)) ) { uint32_t dir = GetForwardDir(p); if ( talker->s_mgr.state == TCP_STATE_FIN_WAIT_1 ) listener->flags |= TF_FORCE_FLUSH; flushed = flush_to_seq( tcpssn, listener, avail, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, dir); } } break; case STREAM_FLPOLICY_FOOTPRINT_NOACK: { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_FOOTPRINT-NOACK\n");); avail = get_q_sequenced(listener); CheckFlushCoercion( p, &listener->flush_mgr, listener->tcp_policy->flush_factor); if ( avail > 0) { uint32_t dir = GetForwardDir(p); if ( talker->s_mgr.state == TCP_STATE_FIN_WAIT_1 ) listener->flags |= TF_FORCE_FLUSH; flushed = flush_to_seq_noack( tcpssn, listener, avail, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, dir); } } break; case STREAM_FLPOLICY_PROTOCOL_IPS: case STREAM_FLPOLICY_PROTOCOL_NOACK: { uint64_t flags = GetForwardDir(p); uint32_t flush_amt = flush_pdu_ips( config, tcpssn, listener, p, &flags); uint32_t this_flush; if (flush_amt == 0 && (flags & PKT_PURGE)) { listener->seglist_next->buffered = SL_BUF_FLUSHED; break; } while ( flush_amt > 0 ) { if( flags & PKT_IGNORE ) { StreamSegment *curseg = listener->seglist_next; uint32_t size_to_flush = 0; //set these ignored segments to FLUSHED //so that these gets purged on ack while ( curseg ) { if( !curseg->buffered ) { size_to_flush += curseg->size; if( size_to_flush == flush_amt ) { curseg->buffered = SL_BUF_FLUSHED; break; } else if( size_to_flush > flush_amt ) { unsigned int bytes_to_copy = curseg->size - ( size_to_flush - flush_amt); StreamSegment *newseg = NULL; if ( DupStreamNode(NULL, listener, curseg, &newseg) == STREAM_INSERT_OK ) { curseg->size = bytes_to_copy; newseg->seq += bytes_to_copy; newseg->size -= bytes_to_copy; newseg->payload += bytes_to_copy + (curseg->payload - curseg->data); curseg->buffered = SL_BUF_FLUSHED; } break; } curseg->buffered = SL_BUF_FLUSHED; } curseg = curseg->next; } this_flush = flush_amt; flags &= ~PKT_IGNORE; } // if this payload is exactly one pdu, don't // actually flush, just use the raw packet /* * For pseudo flush case, fall back to the regular * flush routine which takes care of handling * pseudo flush case. */ else if (!(p->packet_flags & PKT_PSEUDO_FLUSH) && listener->seglist_next && (tdb->seq == listener->seglist_next->seq) && (flush_amt == listener->seglist_next->size) && (flush_amt == p->dsize) ) { this_flush = flush_amt; if(!listener->paf_state.fpt_eoh){ listener->seglist_next->buffered = SL_BUF_FLUSHED; listener->flush_count++; } p->packet_flags |= flags & PKT_PDU_FULL; /* Raw packet with only and complete http-post header is set with PKT_EVAL_DROP, * it will be used to evalute drop/allow packet by preprocs */ if (listener->paf_state.fpt_eoh) { p->packet_flags |= PKT_EVAL_DROP; } ShowRebuiltPacket(p); } else { this_flush = flush_to_seq( tcpssn, listener, flush_amt, p, GET_SRC_IP(p), GET_DST_IP(p), p->tcph->th_sport, p->tcph->th_dport, flags); } // if we didn't flush as expected, bail // (we can flush less than max dsize) if ( !this_flush ) break; if(listener->paf_state.fpt_eoh) { listener->paf_state.fpt_eoh = 0; tcpssn->pp_flags &= ~(PP_HTTPINSPECT_PAF_FLUSH_POST_HDR); } else flushed += this_flush; flags = GetForwardDir(p); flush_amt = flush_pdu_ips( config, tcpssn, listener, p, &flags); } if ( !flags ) { if ( AutoDisable(listener, talker) ) return 0; if ( listener->flush_mgr.flush_policy == STREAM_FLPOLICY_PROTOCOL_NOACK ) listener->flush_mgr.flush_policy = STREAM_FLPOLICY_FOOTPRINT_NOACK; else listener->flush_mgr.flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS; listener->flush_mgr.flush_pt += ScPafMax(); listener->flush_mgr.flush_type = STREAM_FT_PAF_MAX; return CheckFlushPolicyOnData( config, tcpssn, talker, listener, tdb, p); } } break; } listener->paf_state.fpt_eoh = 0; tcpssn->pp_flags &= ~(PP_HTTPINSPECT_PAF_FLUSH_POST_HDR); return flushed; } #endif // iterate over seglist and scan all new acked bytes // - new means not yet scanned // - must use seglist data (not packet) since this packet may plug a // hole and enable paf scanning of following segments // - if we reach a flush point // - return bytes to flush if data available (must be acked) // - return zero if not yet received or received but not acked // - if we reach a skip point // - jump ahead and resume scanning any available data // - must stop if we reach a gap // - one segment may lead to multiple checks since // it may contain multiple encapsulated PDUs // - if we partially scan a segment we must save state so we // know where we left off and can resume scanning the remainder static inline uint32_t flush_pdu_ackd ( StreamTcpConfig *config, TcpSession* ssn, StreamTracker* trk, Packet* pkt, uint64_t* flags) { bool to_srv = ( *flags == PKT_FROM_CLIENT ); uint16_t srv_port = ( to_srv ? pkt->sp : pkt->dp ); uint32_t total = 0; StreamSegment* seg; PROFILE_VARS; PREPROC_PROFILE_START(s5TcpPAFPerfStats); seg = SEQ_LT(trk->seglist_base_seq, trk->r_win_base) ? trk->seglist : NULL; // * must stop if not acked // * must use adjusted size of seg if not fully acked // * must stop if gap (checked in s5_paf_check) while ( seg && *flags && SEQ_LT(seg->seq, trk->r_win_base) ) { uint32_t flush_pt; uint32_t size = seg->size; uint32_t end = seg->seq + seg->size; uint32_t pos = s5_paf_position(&trk->paf_state); if ( s5_paf_initialized(&trk->paf_state) && SEQ_LEQ(end, pos) ) { total += size; seg = seg->next; continue; } if ( SEQ_GT(end, trk->r_win_base) ) size = trk->r_win_base - seg->seq; total += size; wire_packet = pkt; flush_policy_for_dir = trk->flush_mgr.flush_policy; flush_pt = s5_paf_check( config->paf_config, &trk->paf_state, ssn->scb, seg->payload, size, total, seg->seq, srv_port, flags, trk->flush_mgr.flush_pt); if (*flags & PKT_PURGE) { //For HTTP 2.0 case where stream doesnt flush the data //Set the seglist value to SL_BUF_FLUSHED STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Flag PKT_PURGE is set");); seg->buffered = SL_BUF_FLUSHED; return 0; } if ( flush_pt > 0 ) { PREPROC_PROFILE_END(s5TcpPAFPerfStats); return flush_pt; } seg = seg->next; } PREPROC_PROFILE_END(s5TcpPAFPerfStats); return 0; } static int CheckFlushPolicyOnAck( StreamTcpConfig *config, TcpSession *tcpssn, StreamTracker *talker, StreamTracker *listener, TcpDataBlock *tdb, Packet *p) { uint32_t flushed = 0; STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "In CheckFlushPolicyOnAck\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Talker flush policy: %s\n", flush_policy_names[talker->flush_mgr.flush_policy]);); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Listener flush policy: %s\n", flush_policy_names[listener->flush_mgr.flush_policy]);); switch(talker->flush_mgr.flush_policy) { case STREAM_FLPOLICY_IGNORE: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_IGNORE\n");); return 0; case STREAM_FLPOLICY_FOOTPRINT: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_FOOTPRINT\n");); { if(get_q_footprint(talker) >= talker->flush_mgr.flush_pt) { uint32_t dir = GetReverseDir(p); flushed = flush_ackd(tcpssn, talker, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, dir); if(flushed) purge_flushed_ackd(tcpssn, talker); } } break; case STREAM_FLPOLICY_LOGICAL: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_LOGICAL\n");); if(talker->seg_bytes_logical > talker->flush_mgr.flush_pt) { uint32_t dir = GetReverseDir(p); flushed = flush_ackd(tcpssn, talker, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, dir); if(flushed) purge_flushed_ackd(tcpssn, talker); } break; case STREAM_FLPOLICY_RESPONSE: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Running FLPOLICY_RESPONSE\n");); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "checking l.r_win_base (0x%X) > " "t.seglist_base_seq (0x%X)\n", talker->r_win_base, talker->seglist_base_seq);); if(SEQ_GT(talker->r_win_base, talker->seglist_base_seq) && IsWellFormed(p, talker)) { uint32_t dir = GetReverseDir(p); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "flushing talker, t->sbl: %d\n", talker->seg_bytes_logical);); //PrintStreamTracker(talker); //PrintStreamTracker(talker); flushed = flush_ackd(tcpssn, talker, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, dir); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "bye bye data...\n");); if(flushed) purge_flushed_ackd(tcpssn, talker); } break; case STREAM_FLPOLICY_SLIDING_WINDOW: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_SLIDING_WINDOW\n");); if(get_q_footprint(talker) >= talker->flush_mgr.flush_pt) { uint32_t dir = GetReverseDir(p); flushed = flush_ackd(tcpssn, talker, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, dir); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Deleting head node for sliding window...\n");); /* Base sequence for next window'd flush is the end * of the first packet. */ talker->seglist_base_seq = talker->seglist->seq + talker->seglist->size; StreamSeglistDeleteNode(talker, talker->seglist); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "setting talker->seglist_base_seq to 0x%X\n", talker->seglist->seq);); } break; #if 0 case STREAM_FLPOLICY_CONSUMED: STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "STREAM_FLPOLICY_CONSUMED\n");); if(get_q_footprint(talker) >= talker->flush_mgr.flush_pt) { uint32_t dir = GetReverseDir(p); flushed = flush_ackd(tcpssn, talker, p, p->iph->ip_dst.s_addr, p->iph->ip_src.s_addr, p->tcph->th_dport, p->tcph->th_sport, dir); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Deleting head node for sliding window...\n");); talker->seglist_base_seq = talker->seglist->seq + talker->seglist->size; /* TODO: Delete up to the consumed bytes */ StreamSeglistDeleteNode(talker, talker->seglist); STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "setting talker->seglist_base_seq to 0x%X\n", talker->seglist->seq);); } break; #endif case STREAM_FLPOLICY_PROTOCOL: { uint64_t flags = GetReverseDir(p); uint32_t flush_amt = flush_pdu_ackd( config, tcpssn, talker, p, &flags); while (flush_amt == 0 && (flags & PKT_PURGE)) { purge_flushed_ackd(tcpssn, talker); // Need to take care of other acked segments flags = GetReverseDir(p); flush_amt = flush_pdu_ackd( config, tcpssn, talker, p, &flags); } while ( flush_amt > 0 ) { if( flags & PKT_IGNORE ) { flushed = flush_amt; flags &= ~PKT_IGNORE; } else if(talker->paf_state.fpt_eoh) { talker->paf_state.fpt_eoh = 0; tcpssn->pp_flags &= ~(PP_HTTPINSPECT_PAF_FLUSH_POST_HDR); flags = GetReverseDir(p); flush_amt = flush_pdu_ackd( config, tcpssn, talker, p, &flags); continue; } else { talker->seglist_next = talker->seglist; talker->seglist_base_seq = talker->seglist->seq; // for consistency with other cases, should return total // but that breaks flushing pipelined pdus flushed = flush_to_seq( tcpssn, talker, flush_amt, p, GET_DST_IP(p), GET_SRC_IP(p), p->tcph->th_dport, p->tcph->th_sport, flags); } // ideally we would purge just once after this loop // but that throws off base purge_to_seq(tcpssn, talker, talker->seglist->seq + flushed); // if we didn't flush as expected, bail // (we can flush less than max dsize) if ( !flushed ) break; flags = GetReverseDir(p); flush_amt = flush_pdu_ackd( config, tcpssn, talker, p, &flags); } if ( !flags ) { if ( AutoDisable(talker, listener) ) return 0; talker->flush_mgr.flush_policy = STREAM_FLPOLICY_FOOTPRINT; talker->flush_mgr.flush_pt += ScPafMax(); talker->flush_mgr.flush_type = STREAM_FT_PAF_MAX; return CheckFlushPolicyOnAck( config, tcpssn, talker, listener, tdb, p ); } } break; #ifdef NORMALIZER case STREAM_FLPOLICY_FOOTPRINT_IPS: case STREAM_FLPOLICY_FOOTPRINT_IPS_FTP: case STREAM_FLPOLICY_PROTOCOL_IPS: purge_flushed_ackd(tcpssn, talker); break; #endif case STREAM_FLPOLICY_FOOTPRINT_NOACK: case STREAM_FLPOLICY_PROTOCOL_NOACK: purge_flushed_ackd(tcpssn, talker); break; } return flushed; } static void StreamSeglistAddNode(StreamTracker *st, StreamSegment *prev, StreamSegment *new) { s5stats.tcp_streamsegs_created++; if(prev) { new->next = prev->next; new->prev = prev; prev->next = new; if (new->next) new->next->prev = new; else st->seglist_tail = new; } else { new->next = st->seglist; if(new->next) new->next->prev = new; else st->seglist_tail = new; st->seglist = new; } st->seg_count++; //Calculate r_nxt_ack by walking the seglist to reach first hole or right end if(SEQ_LEQ(new->seq, st->r_nxt_ack)) { while ( new->next && (new->next->seq == new->seq + new->size) ) { new = new->next; } if(SEQ_LT(st->r_nxt_ack, new->seq + new->size)) st->r_nxt_ack = new->seq + new->size; } #ifdef DEBUG new->ordinal = st->segment_ordinal++; if (new->next && (new->next->seq == new->seq)) { LogMessage("Same seq to right, check me\n"); } #endif } static int StreamSeglistDeleteNode (StreamTracker* st, StreamSegment* seg) { int ret; assert(st && seg); STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "Dropping segment at seq %X, len %d\n", seg->seq, seg->size);); if(seg->prev) seg->prev->next = seg->next; else st->seglist = seg->next; if(seg->next) seg->next->prev = seg->prev; else st->seglist_tail = seg->prev; st->seg_bytes_logical -= seg->size; st->seg_bytes_total -= seg->caplen; ret = seg->caplen; if (seg->buffered) { s5stats.tcp_rebuilt_seqs_used++; st->flush_count--; } if ( st->seglist_next == seg ) st->seglist_next = NULL; SegmentFree(seg); st->seg_count--; return ret; } static int StreamSeglistDeleteNodeTrim ( StreamTracker* st, StreamSegment* seg, uint32_t flush_seq) { assert(st && seg); /*urgent data is never sent flushed to preprocessors, hence avoid trimming segment having urg_offset >=1*/ if( (seg->seq + seg->size - seg->urg_offset) > flush_seq ) { /* If PAF is applicable and paf_state is set, we need to trim the segment when * we receive ACK for data less than the segment size. */ if( s5_paf_active(&st->paf_state) ) { uint32_t delta = flush_seq - seg->seq; if ( delta < seg->size ) { STREAM_DEBUG_WRAP( DebugMessage(DEBUG_STREAM_STATE, "Left-Trimming segment at seq %X, len %d, delta %u\n", seg->seq, seg->size, delta);); seg->seq = flush_seq; seg->size -= (uint16_t)delta; seg->payload += delta; st->seg_bytes_logical -= delta; return 0; } } /* If PAF is not applicable return without trim and delete when ACK is received for data * less than segment size. */ else { /* If paf_state is PAF_ABORT and we receive ACK for bytes less than the segment size, * do not trim and do not delete from queue till we recive the ACK for remaining bytes. * example - FTP Data Session is always in default state which is PAF_ABORT, because PAF * is not applicable for FTP DATA, in this case if we receive ACK for data less than the * segment size, we should not trim OR delete this node till we get the ACK for complete * segment. */ return 0; } } return StreamSeglistDeleteNode(st, seg); } void TcpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port) { TcpSession *tcpssn = (TcpSession *)ssn->proto_specific_data->data; sfaddr_t tmpIp; uint16_t tmpPort; StreamTracker tmpTracker; if (IP_EQUALITY(&tcpssn->tcp_client_ip, ip) && (tcpssn->tcp_client_port == port)) { if ((dir == SSN_DIR_FROM_CLIENT) && (ssn->ha_state.direction == SSN_DIR_FROM_CLIENT)) { /* Direction already set as client */ return; } } else if (IP_EQUALITY(&tcpssn->tcp_server_ip, ip) && (tcpssn->tcp_server_port == port)) { if ((dir == SSN_DIR_FROM_SERVER) && (ssn->ha_state.direction == SSN_DIR_FROM_SERVER)) { /* Direction already set as server */ return; } } /* Swap them -- leave ssn->ha_state.direction the same */ /* XXX: Gotta be a more efficient way to do this without the memcpy */ tmpIp = tcpssn->tcp_client_ip; tmpPort = tcpssn->tcp_client_port; tcpssn->tcp_client_ip = tcpssn->tcp_server_ip; tcpssn->tcp_client_port = tcpssn->tcp_server_port; tcpssn->tcp_server_ip = tmpIp; tcpssn->tcp_server_port = tmpPort; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID SwapPacketHeaderFoo(tcpssn); #endif memcpy(&tmpTracker, &tcpssn->client, sizeof(StreamTracker)); memcpy(&tcpssn->client, &tcpssn->server, sizeof(StreamTracker)); memcpy(&tcpssn->server, &tmpTracker, sizeof(StreamTracker)); } /* Iterates through the packets that were reassembled for * logging of tagged packets. */ int GetTcpRebuiltPackets(Packet *p, SessionControlBlock *ssn, PacketIterator callback, void *userdata) { int packets = 0; TcpSession *tcpssn = NULL; StreamTracker *st; StreamSegment *ss; uint32_t start_seq = ntohl(p->tcph->th_seq); uint32_t end_seq = start_seq + p->dsize; if (!ssn || !ssn->proto_specific_data || !ssn->proto_specific_data->data) { return packets; } tcpssn = (TcpSession *)ssn->proto_specific_data->data; /* StreamTracker is the opposite of the ip of the reassembled * packet --> it came out the queue for the other side */ if (IP_EQUALITY(GET_SRC_IP(p), &tcpssn->tcp_client_ip)) { st = &tcpssn->server; } else { st = &tcpssn->client; } // skip over segments not covered by this reassembled packet for (ss = st->seglist; ss && SEQ_LT(ss->seq, start_seq); ss = ss->next); // return flushed segments only // For HTTP Post request End-of-Header Flush, if alert is raised, return the segments even though they are not buffered. for (; ss && ((ss->buffered == SL_BUF_FLUSHED) || (tcpssn->pp_flags & PP_HTTPINSPECT_PAF_FLUSH_POST_HDR)); ss = ss->next) { if (SEQ_GEQ(ss->seq,start_seq) && SEQ_LT(ss->seq, end_seq)) { DAQ_PktHdr_t pkth; pkth.ts.tv_sec = ss->tv.tv_sec; pkth.ts.tv_usec = ss->tv.tv_usec; pkth.caplen = ss->caplen; pkth.pktlen = ss->pktlen; callback(&pkth, ss->pkt, userdata); packets++; } else break; } return packets; } /* Iterates through the packets that were reassembled for * logging of tagged packets. */ int GetTcpStreamSegments(Packet *p, SessionControlBlock *ssn, StreamSegmentIterator callback, void *userdata) { int packets = 0; TcpSession *tcpssn = NULL; StreamTracker *st; StreamSegment *ss; uint32_t start_seq = ntohl(p->tcph->th_seq); uint32_t end_seq = start_seq + p->dsize; if (!ssn || !ssn->proto_specific_data || !ssn->proto_specific_data->data) return -1; tcpssn = (TcpSession *)ssn->proto_specific_data->data; /* StreamTracker is the opposite of the ip of the reassembled * packet --> it came out the queue for the other side */ if (IP_EQUALITY(GET_SRC_IP(p), &tcpssn->tcp_client_ip)) st = &tcpssn->server; else st = &tcpssn->client; // skip over segments not covered by this reassembled packet for (ss = st->seglist; ss && SEQ_LT(ss->seq, start_seq); ss = ss->next); // return flushed segments only // For HTTP Post request End-of-Header Flush, if alert is raised, return the segments even though they are not buffered. for (; ss && ((ss->buffered == SL_BUF_FLUSHED) || (tcpssn->pp_flags & PP_HTTPINSPECT_PAF_FLUSH_POST_HDR)); ss = ss->next) { if (SEQ_GEQ(ss->seq,start_seq) && SEQ_LT(ss->seq, end_seq)) { DAQ_PktHdr_t pkth; uint32_t ajust_seq = ss->seq - (uint32_t)(ss->payload - ss->data); pkth.ts.tv_sec = ss->tv.tv_sec; pkth.ts.tv_usec = ss->tv.tv_usec; pkth.caplen = ss->caplen; pkth.pktlen = ss->pktlen; if (callback(&pkth, ss->pkt, ss->data, ajust_seq, userdata) != 0) return -1; packets++; } else break; } return packets; } int StreamAddSessionAlertTcp( SessionControlBlock* scb, Packet* p, uint32_t gid, uint32_t sid) { TcpSession *tcpssn = NULL; StreamTracker *st; StreamAlertInfo* ai; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) { return 0; } if (IP_EQUALITY(GET_SRC_IP(p),&tcpssn->tcp_client_ip)) { st = &tcpssn->server; } else { st = &tcpssn->client; } if (st->alert_count >= MAX_SESSION_ALERTS) return 0; ai = st->alerts + st->alert_count; ai->gid = gid; ai->sid = sid; ai->seq = GET_PKT_SEQ(p); if ( p->tcph->th_flags & TH_FIN ) ai->seq--; st->alert_count++; return 0; } int StreamCheckSessionAlertTcp(SessionControlBlock *scb, Packet *p, uint32_t gid, uint32_t sid) { TcpSession *tcpssn = NULL; StreamTracker *st; int i; int iRet = 0; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) { return 0; } /* If this is not a rebuilt packet, no need to check further */ if (!(p->packet_flags & PKT_REBUILT_STREAM)) { return 0; } if (IP_EQUALITY(GET_SRC_IP(p), &tcpssn->tcp_client_ip)) { st = &tcpssn->server; } else { st = &tcpssn->client; } for (i=0;ialert_count;i++) { /* This is a rebuilt packet and if we've seen this alert before, * return that we have previously alerted on original packet. */ if ( st->alerts[i].gid == gid && st->alerts[i].sid == sid ) { return -1; } } return iRet; } int StreamUpdateSessionAlertTcp(SessionControlBlock *scb, Packet *p, uint32_t gid, uint32_t sid, uint32_t event_id, uint32_t event_second) { TcpSession *tcpssn = NULL; StreamTracker *st; int i; uint32_t seq_num; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) { return 0; } if (IP_EQUALITY(GET_SRC_IP(p), &tcpssn->tcp_client_ip)) { st = &tcpssn->server; } else { st = &tcpssn->client; } seq_num = GET_PKT_SEQ(p); if ( p->tcph->th_flags & TH_FIN ) seq_num--; for (i=0;ialert_count;i++) { StreamAlertInfo* ai = st->alerts + i; if ( ai->gid == gid && ai->sid == sid && SEQ_EQ(ai->seq, seq_num)) { ai->event_id = event_id; ai->event_second = event_second; return 0; } } return -1; } void StreamSetExtraDataTcp (SessionControlBlock* scb, Packet* p, uint32_t xid) { TcpSession *tcpssn = NULL; StreamTracker *st; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return; if (IP_EQUALITY(GET_SRC_IP(p),&tcpssn->tcp_client_ip)) st = &tcpssn->server; else st = &tcpssn->client; st->xtradata_mask |= BIT(xid); p->xtradata_mask |= BIT(xid); } void StreamClearExtraDataTcp (SessionControlBlock* scb, Packet* p, uint32_t xid) { TcpSession *tcpssn = NULL; StreamTracker *st; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return; if (IP_EQUALITY(GET_SRC_IP(p),&tcpssn->tcp_client_ip)) st = &tcpssn->server; else st = &tcpssn->client; if ( xid ) st->xtradata_mask &= ~BIT(xid); else st->xtradata_mask = 0; } char StreamGetReassemblyDirectionTcp(SessionControlBlock *scb) { TcpSession *tcpssn = NULL; char dir = SSN_DIR_NONE; if (!scb) return SSN_DIR_NONE; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return SSN_DIR_NONE; if ((tcpssn->server.flush_mgr.flush_policy != STREAM_FLPOLICY_NONE) && (tcpssn->server.flush_mgr.flush_policy != STREAM_FLPOLICY_IGNORE)) { dir |= SSN_DIR_TO_SERVER; } if ((tcpssn->client.flush_mgr.flush_policy != STREAM_FLPOLICY_NONE) && (tcpssn->client.flush_mgr.flush_policy != STREAM_FLPOLICY_IGNORE)) { dir |= SSN_DIR_TO_CLIENT; } return dir; } Packet* getWirePacketTcp() { return wire_packet; } uint8_t getFlushPolicyDirTcp() { if (flush_policy_for_dir == STREAM_FLPOLICY_FOOTPRINT_IPS || flush_policy_for_dir == STREAM_FLPOLICY_FOOTPRINT_NOACK || flush_policy_for_dir == STREAM_FLPOLICY_PROTOCOL_IPS || flush_policy_for_dir == STREAM_FLPOLICY_PROTOCOL_NOACK) { return 1; } return 0; } uint32_t StreamGetFlushPointTcp(SessionControlBlock *scb, char dir) { TcpSession *tcpssn = NULL; if (scb == NULL) return 0; if (scb->proto_specific_data != NULL) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (tcpssn == NULL) return 0; if (dir & SSN_DIR_TO_SERVER) return tcpssn->server.flush_mgr.flush_pt; else if (dir & SSN_DIR_TO_CLIENT) return tcpssn->client.flush_mgr.flush_pt; return 0; } void StreamSetFlushPointTcp(SessionControlBlock *scb, char dir, uint32_t flush_point) { TcpSession *tcpssn = NULL; if (scb == NULL) return; if (scb->proto_specific_data != NULL) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (tcpssn == NULL) return; if (flush_point == 0) return; if (dir & SSN_DIR_TO_SERVER) { tcpssn->server.flush_mgr.flush_pt = flush_point; tcpssn->server.flush_mgr.last_size = 0; tcpssn->server.flush_mgr.last_count = 0; tcpssn->server.flush_mgr.flush_type = STREAM_FT_EXTERNAL; } else if (dir & SSN_DIR_TO_CLIENT) { tcpssn->client.flush_mgr.flush_pt = flush_point; tcpssn->client.flush_mgr.last_size = 0; tcpssn->client.flush_mgr.last_count = 0; tcpssn->client.flush_mgr.flush_type = STREAM_FT_EXTERNAL; } } char StreamSetReassemblyTcp(SessionControlBlock *scb, uint8_t flush_policy, char dir, char flags) { TcpSession *tcpssn = NULL; uint16_t tmp_flags; if (!scb) return SSN_DIR_NONE; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return SSN_DIR_NONE; tmp_flags = (flush_policy == STREAM_FLPOLICY_IGNORE)? 0: TF_FIRST_PKT_MISSING; if (flags & STREAM_FLPOLICY_SET_APPEND) { if (dir & SSN_DIR_TO_SERVER) { if (tcpssn->server.flush_mgr.flush_policy != STREAM_FLPOLICY_NONE) { /* Changing policy with APPEND, Bad */ DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Changing client flush policy using " "append is asking for trouble. Ignored\n");); } else { InitFlushMgr(snort_conf, &tcpssn->server.flush_mgr, &tcpssn->server.tcp_policy->flush_point_list, flush_policy, 0); tcpssn->server.flags |= tmp_flags; } } if (dir & SSN_DIR_TO_CLIENT) { if (tcpssn->client.flush_mgr.flush_policy != STREAM_FLPOLICY_NONE) { /* Changing policy with APPEND, Bad */ DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: Changing server flush policy using " "append is asking for trouble. Ignored\n");); } else { InitFlushMgr(snort_conf, &tcpssn->client.flush_mgr, &tcpssn->client.tcp_policy->flush_point_list, flush_policy, 0); tcpssn->client.flags |= tmp_flags; } } } else if (flags & STREAM_FLPOLICY_SET_ABSOLUTE) { if (dir & SSN_DIR_TO_SERVER) { //If the flush policy is already set to STREAM_FLPOLICY_FOOTPRINT_IPS_FTP, ssl preproc shouldn't reset the flush policy for FTPS. //Unset the pp_flags because SSL will send decrypted FTP traffic from here on if((tcpssn->pp_flags & PP_FTPTELNET_FTPS) && (tcpssn->server.flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_IPS_FTP)) { flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS_FTP; tcpssn->pp_flags &= ~PP_FTPTELNET_FTPS; } InitFlushMgr(snort_conf, &tcpssn->server.flush_mgr, &tcpssn->server.tcp_policy->flush_point_list, flush_policy, 0); tcpssn->server.flags |= tmp_flags; if(tcpssn->server.flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_IPS) { //set_application_data is done for ftp-data connection during TCPSession setup. Change the Flush policy for ftp-data only. if(session_api->get_application_data(scb, PP_FTPTELNET)) { tcpssn->server.flush_mgr.flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS_FTP; tcpssn->server.flush_mgr.last_size = 0; } } } if (dir & SSN_DIR_TO_CLIENT) { //If the flush policy is already set to STREAM_FLPOLICY_FOOTPRINT_IPS_FTP, ssl preproc shouldn't reset the flush policy for FTPS. //Unset the pp_flags because SSL will send decrypted FTP traffic from here on if((tcpssn->pp_flags & PP_FTPTELNET_FTPS) && (tcpssn->client.flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_IPS_FTP)) { flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS_FTP; tcpssn->pp_flags &= ~PP_FTPTELNET_FTPS; } InitFlushMgr(snort_conf, &tcpssn->client.flush_mgr, &tcpssn->client.tcp_policy->flush_point_list, flush_policy, 0); tcpssn->client.flags |= tmp_flags; if(tcpssn->client.flush_mgr.flush_policy == STREAM_FLPOLICY_FOOTPRINT_IPS) { //set_application_data is done for ftp-data connection during TCPSession setup. Change the Flush policy for ftp-data only. if(session_api->get_application_data(scb, PP_FTPTELNET)) { tcpssn->client.flush_mgr.flush_policy = STREAM_FLPOLICY_FOOTPRINT_IPS_FTP; tcpssn->client.flush_mgr.last_size = 0; } } } } return StreamGetReassemblyDirectionTcp(scb); } char StreamGetReassemblyFlushPolicyTcp(SessionControlBlock *scb, char dir) { TcpSession *tcpssn = NULL; if (!scb) return STREAM_FLPOLICY_NONE; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return STREAM_FLPOLICY_NONE; if (dir & SSN_DIR_TO_SERVER) { return (char)tcpssn->server.flush_mgr.flush_policy; } if (dir & SSN_DIR_TO_CLIENT) { return (char)tcpssn->client.flush_mgr.flush_policy; } return STREAM_FLPOLICY_NONE; } char StreamIsStreamSequencedTcp(SessionControlBlock *scb, char dir) { TcpSession *tcpssn = NULL; if (!scb) return 1; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return 1; if (dir & SSN_DIR_FROM_CLIENT) { if ( tcpssn->server.flags & TF_MISSING_PREV_PKT ) return 0; } if (dir & SSN_DIR_FROM_SERVER) { if ( tcpssn->client.flags & TF_MISSING_PREV_PKT ) return 0; } return 1; } /* This will falsly return SSN_MISSING_BEFORE on the first reassembed * packet if reassembly for this direction was set mid-session */ int StreamMissingInReassembledTcp(SessionControlBlock *scb, char dir) { TcpSession *tcpssn = NULL; if (!scb) return SSN_MISSING_NONE; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return SSN_MISSING_NONE; if (dir & SSN_DIR_FROM_CLIENT) { if (tcpssn->server.flags & TF_MISSING_PREV_PKT) return SSN_MISSING_BEFORE; } else if (dir & SSN_DIR_FROM_SERVER) { if (tcpssn->client.flags & TF_MISSING_PREV_PKT) return SSN_MISSING_BEFORE; } return SSN_MISSING_NONE; } char StreamPacketsMissingTcp(SessionControlBlock *scb, char dir) { TcpSession *tcpssn = NULL; if (!scb) return 0; if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) return 0; if (dir & SSN_DIR_FROM_CLIENT) { if (tcpssn->server.flags & TF_PKT_MISSED) return 1; } if (dir & SSN_DIR_FROM_SERVER) { if (tcpssn->client.flags & TF_PKT_MISSED) return 1; } return 0; } #define SSOD_LESS_THAN 1 #define SSOD_GREATER_THAN 2 #define SSOD_EQUALS 3 #define SSOD_LESS_THAN_OR_EQUALS 4 #define SSOD_GREATER_THAN_OR_EQUALS 5 #define SSOD_NOT_EQUALS 6 #define SSOD_MATCH 1 #define SSOD_NOMATCH 0 typedef struct _StreamSizeOptionData { char operator; uint32_t size; char direction; } StreamSizeOptionData; int s5TcpStreamSizeInit(struct _SnortConfig *sc, char *name, char *parameters, void **dataPtr) { char **toks; int num_toks; char *endp; StreamSizeOptionData *ssod = NULL; toks = mSplit(parameters, ",", 4, &num_toks, 0); if (num_toks != 3) { FatalError("%s(%d): Invalid parameters for %s option\n", file_name, file_line, name); } ssod = SnortPreprocAlloc(1, sizeof(StreamSizeOptionData), PP_STREAM, PP_MEM_CATEGORY_CONFIG); if (!ssod) { FatalError("%s(%d): Failed to allocate data for %s option\n", file_name, file_line, name); } /* Parse the direction. * Can be: client, server, both, either */ if (!strcasecmp(toks[0], "client")) { ssod->direction = SSN_DIR_FROM_CLIENT; } else if (!strcasecmp(toks[0], "server")) { ssod->direction = SSN_DIR_FROM_SERVER; } else if (!strcasecmp(toks[0], "both")) { ssod->direction = SSN_DIR_BOTH; } else if (!strcasecmp(toks[0], "either")) { ssod->direction = SSN_DIR_NONE; } else { FatalError("%s(%d): Invalid direction: %s for option %s\n", file_name, file_line, toks[0], name); } /* Parse the operator. * Can be: =, <, > , !=, <=, >= */ if (!strcasecmp(toks[1], "=")) { ssod->operator = SSOD_EQUALS; } else if (!strcasecmp(toks[1], "<")) { ssod->operator = SSOD_LESS_THAN; } else if (!strcasecmp(toks[1], ">")) { ssod->operator = SSOD_GREATER_THAN; } else if (!strcasecmp(toks[1], "!=")) { ssod->operator = SSOD_NOT_EQUALS; } else if (!strcasecmp(toks[1], "<=")) { ssod->operator = SSOD_LESS_THAN_OR_EQUALS; } else if (!strcasecmp(toks[1], ">=")) { ssod->operator = SSOD_GREATER_THAN_OR_EQUALS; } else { FatalError("%s(%d): Invalid operator: %s for option %s\n", file_name, file_line, toks[1], name); } ssod->size = SnortStrtoul(toks[2], &endp, 0); if ((endp == toks[2]) || (errno == ERANGE)) { FatalError("%s(%d): Invalid size: %s for option %s\n", file_name, file_line, toks[2], name); } *dataPtr = ssod; mSplitFree(&toks, num_toks); return 1; } static inline int s5TcpStreamSizeCompare(uint32_t size1, uint32_t size2, char operator) { int retval = 0; switch (operator) { case SSOD_EQUALS: if (size1 == size2) retval = 1; break; case SSOD_LESS_THAN: if (size1 < size2) retval = 1; break; case SSOD_GREATER_THAN: if (size1 > size2) retval = 1; break; case SSOD_NOT_EQUALS: if (size1 != size2) retval = 1; break; case SSOD_LESS_THAN_OR_EQUALS: if (size1 <= size2) retval = 1; break; case SSOD_GREATER_THAN_OR_EQUALS: if (size1 >= size2) retval = 1; break; default: break; } return retval; } int s5TcpStreamSizeEval(void *p, const uint8_t **cursor, void *dataPtr) { Packet *pkt = p; SessionControlBlock *scb = NULL; TcpSession *tcpssn = NULL; StreamSizeOptionData *ssod = (StreamSizeOptionData *)dataPtr; uint32_t client_size; uint32_t server_size; PROFILE_VARS; if (!pkt || !pkt->ssnptr || !ssod || !pkt->tcph) return DETECTION_OPTION_NO_MATCH; scb = pkt->ssnptr; if (!scb->proto_specific_data) return DETECTION_OPTION_NO_MATCH; PREPROC_PROFILE_START(streamSizePerfStats); tcpssn = (TcpSession *)scb->proto_specific_data->data; if (tcpssn->client.l_nxt_seq > tcpssn->client.isn) { /* the normal case... */ client_size = tcpssn->client.l_nxt_seq - tcpssn->client.isn; } else { /* the seq num wrapping case... */ client_size = tcpssn->client.isn - tcpssn->client.l_nxt_seq; } if (tcpssn->server.l_nxt_seq > tcpssn->server.isn) { /* the normal case... */ server_size = tcpssn->server.l_nxt_seq - tcpssn->server.isn; } else { /* the seq num wrapping case... */ server_size = tcpssn->server.isn - tcpssn->server.l_nxt_seq; } switch (ssod->direction) { case SSN_DIR_FROM_CLIENT: if (s5TcpStreamSizeCompare(client_size, ssod->size, ssod->operator) == SSOD_MATCH) { PREPROC_PROFILE_END(streamSizePerfStats); return DETECTION_OPTION_MATCH; } break; case SSN_DIR_FROM_SERVER: if (s5TcpStreamSizeCompare(server_size, ssod->size, ssod->operator) == SSOD_MATCH) { PREPROC_PROFILE_END(streamSizePerfStats); return DETECTION_OPTION_MATCH; } break; case SSN_DIR_NONE: /* overloaded. really, its an 'either' */ if ((s5TcpStreamSizeCompare(client_size, ssod->size, ssod->operator) == SSOD_MATCH) || (s5TcpStreamSizeCompare(server_size, ssod->size, ssod->operator) == SSOD_MATCH)) { PREPROC_PROFILE_END(streamSizePerfStats); return DETECTION_OPTION_MATCH; } break; case SSN_DIR_BOTH: if ((s5TcpStreamSizeCompare(client_size, ssod->size, ssod->operator) == SSOD_MATCH) && (s5TcpStreamSizeCompare(server_size, ssod->size, ssod->operator) == SSOD_MATCH)) { PREPROC_PROFILE_END(streamSizePerfStats); return DETECTION_OPTION_MATCH; } break; default: break; } PREPROC_PROFILE_END(streamSizePerfStats); return DETECTION_OPTION_NO_MATCH; } void s5TcpStreamSizeCleanup(void *dataPtr) { StreamSizeOptionData *ssod = dataPtr; if (ssod) { SnortPreprocFree(ssod, sizeof(StreamSizeOptionData), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } } typedef struct _StreamReassembleRuleOptionData { char enable; char alert; char direction; char fastpath; } StreamReassembleRuleOptionData; int s5TcpStreamReassembleRuleOptionInit(struct _SnortConfig *sc, char *name, char *parameters, void **dataPtr) { char **toks; int num_toks; StreamReassembleRuleOptionData *srod = NULL; toks = mSplit(parameters, ",", 4, &num_toks, 0); if (num_toks < 2) { FatalError("%s(%d): Invalid parameters for %s option\n", file_name, file_line, name); } srod = SnortPreprocAlloc(1, sizeof(StreamReassembleRuleOptionData), PP_STREAM, PP_MEM_CATEGORY_CONFIG); if (!srod) { FatalError("%s(%d): Failed to allocate data for %s option\n", file_name, file_line, name); } /* Parse the action. * Can be: enable or disable */ if (!strcasecmp(toks[0], "enable")) { srod->enable = 1; } else if (!strcasecmp(toks[0], "disable")) { srod->enable = 0; } else { FatalError("%s(%d): Invalid action: %s for option %s. Valid " "parameters are 'enable' or 'disable'\n", file_name, file_line, toks[0], name); } /* Parse the direction. * Can be: client, server, both */ /* Need to these around, so they match the ones specified via the stream5_tcp ports * option, ie, stream5_tcp: ports client enables reassembly on client-sourced traffic. */ if (!strcasecmp(toks[1], "client")) { srod->direction = SSN_DIR_FROM_SERVER; } else if (!strcasecmp(toks[1], "server")) { srod->direction = SSN_DIR_FROM_CLIENT; } else if (!strcasecmp(toks[1], "both")) { srod->direction = SSN_DIR_BOTH; } else { FatalError("%s(%d): Invalid direction: %s for option %s\n", file_name, file_line, toks[1], name); } /* Parse the optional parameters: * noalert flag, fastpath flag */ srod->alert = 1; if (num_toks > 2) { int i = 2; for (; i< num_toks; i++) { if (!strcasecmp(toks[i], "noalert")) { srod->alert = 0; } else if (!strcasecmp(toks[i], "fastpath")) { srod->fastpath = 1; if (srod->enable) { FatalError("%s(%d): Using 'fastpath' with 'enable' is " "not valid for %s\n", file_name, file_line, name); } } else { FatalError("%s(%d): Invalid optional parameter: %s for option %s\n", file_name, file_line, toks[i], name); } } } *dataPtr = srod; mSplitFree(&toks, num_toks); return 1; } int s5TcpStreamReassembleRuleOptionEval(void *p, const uint8_t **cursor, void *dataPtr) { Packet *pkt = p; SessionControlBlock *scb = NULL; StreamReassembleRuleOptionData *srod = (StreamReassembleRuleOptionData *)dataPtr; PROFILE_VARS; if (!pkt || !pkt->ssnptr || !srod || !pkt->tcph) return 0; PREPROC_PROFILE_START(streamReassembleRuleOptionPerfStats); scb = pkt->ssnptr; if (!srod->enable) /* Turn it off */ StreamSetReassemblyTcp(scb, STREAM_FLPOLICY_IGNORE, srod->direction, STREAM_FLPOLICY_SET_ABSOLUTE); else StreamSetReassemblyTcp(scb, STREAM_FLPOLICY_FOOTPRINT, srod->direction, STREAM_FLPOLICY_SET_ABSOLUTE); if (srod->fastpath) { /* Turn off inspection */ scb->ha_state.ignore_direction |= srod->direction; session_api->disable_inspection(scb, pkt); /* TBD: Set TF_FORCE_FLUSH ? */ } if (srod->alert) { PREPROC_PROFILE_END(streamReassembleRuleOptionPerfStats); return DETECTION_OPTION_MATCH; } PREPROC_PROFILE_END(streamReassembleRuleOptionPerfStats); return DETECTION_OPTION_NO_ALERT; } void s5TcpStreamReassembleRuleOptionCleanup(void *dataPtr) { StreamReassembleRuleOptionData *srod = dataPtr; if (srod) { SnortPreprocFree(srod, sizeof(StreamReassembleRuleOptionData), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } } void s5TcpSetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->tcp_config != NULL ) ) config->tcp_config->port_filter[ port ] |= status; } void s5TcpUnsetPortFilterStatus( struct _SnortConfig *sc, unsigned short port, uint16_t status, tSfPolicyId policyId, int parsing ) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->tcp_config != NULL ) ) config->tcp_config->port_filter[ port ] &= ~status; } int s5TcpGetPortFilterStatus(struct _SnortConfig *sc, unsigned short port, tSfPolicyId policyId, int parsing) { StreamConfig *config; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->tcp_config != NULL ) ) return ( int ) config->tcp_config->port_filter[ port ]; else return PORT_MONITOR_NONE; } void s5TcpSetSynSessionStatus(struct _SnortConfig *sc, uint16_t status, tSfPolicyId policyId, int parsing) { StreamConfig *config; if (status <= PORT_MONITOR_SESSION) return; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->tcp_config != NULL ) ) config->tcp_config->session_on_syn |= status; } void s5TcpUnsetSynSessionStatus(struct _SnortConfig *sc, uint16_t status, tSfPolicyId policyId, int parsing) { StreamConfig *config; if (status <= PORT_MONITOR_SESSION) return; config = getStreamPolicyConfig( policyId, parsing ); if ( ( config != NULL ) && ( config->tcp_config != NULL ) ) config->tcp_config->session_on_syn &= ~status; } static void targetPolicyIterate(void (*callback)(int)) { unsigned int i; for (i = 0; i < snort_conf->num_policies_allocated; i++) { if (snort_conf->targeted_policies[i] != NULL) { callback(i); } } } static void policyDecoderFlagsSaveNClear(int policyId) { SnortPolicy *pPolicy = snort_conf->targeted_policies[policyId]; if (pPolicy) { pPolicy->decoder_alert_flags_saved = pPolicy->decoder_alert_flags; pPolicy->decoder_drop_flags_saved = pPolicy->decoder_drop_flags; pPolicy->decoder_alert_flags = 0; pPolicy->decoder_drop_flags = 0; } } static void policyDecoderFlagsRestore(int policyId) { SnortPolicy *pPolicy = snort_conf->targeted_policies[policyId]; if (pPolicy) { pPolicy->decoder_alert_flags = pPolicy->decoder_alert_flags_saved; pPolicy->decoder_drop_flags = pPolicy->decoder_drop_flags_saved; pPolicy->decoder_alert_flags_saved = 0; pPolicy->decoder_drop_flags_saved = 0; } } static void policyChecksumFlagsSaveNClear(int policyId) { SnortPolicy *pPolicy = snort_conf->targeted_policies[policyId]; if (pPolicy) { pPolicy->checksum_flags_saved = pPolicy->checksum_flags; pPolicy->checksum_flags = 0; } } static void policyChecksumFlagsRestore(int policyId) { SnortPolicy *pPolicy = snort_conf->targeted_policies[policyId]; if (pPolicy) { pPolicy->checksum_flags = pPolicy->checksum_flags_saved; } } typedef struct network_reassembly_ports { sfcidr_t *ip; uint8_t s_ports[MAXPORTS_STORAGE]; uint8_t c_ports[MAXPORTS_STORAGE]; } NetworkReassemblyPorts; // single instance for saving ports registered with default policy... NetworkReassemblyPorts default_nrps; // List head for list of networks and ports within that preprocs have requested for port // reassembly sfSDList targeted_nrps; static void freeNrpNode( void *data ) { if( data ) { NetworkReassemblyPorts *nrp = ( NetworkReassemblyPorts * ) data; if( nrp->ip != NULL ) free( nrp->ip ); SnortPreprocFree( data, sizeof( NetworkReassemblyPorts ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); } } static void StreamCreateReassemblyPortList( void ) { //In SnortInit, we call ConfigurePreprocessors twice. The allocations done first need to be freed if(targeted_nrps.size > 0) StreamDestoryReassemblyPortList(); sf_sdlist_init( &targeted_nrps, &freeNrpNode ); } static void StreamDestoryReassemblyPortList( void ) { sf_sdlist_purge( &targeted_nrps ); } static NetworkReassemblyPorts *initNetworkPortReassembly( sfcidr_t *ip, unsigned short port, int ra_dir ) { NetworkReassemblyPorts *nrp = SnortPreprocAlloc( 1, sizeof( NetworkReassemblyPorts ), PP_STREAM, PP_MEM_CATEGORY_CONFIG ); if( nrp == NULL ) { ErrorMessage( " ERROR: Unable to allocate memory for initializing Network Reassembly Post List\n"); return NULL; } nrp->ip = ip; if( ra_dir & SSN_DIR_FROM_SERVER ) enablePort( nrp->s_ports, port ); if( ra_dir & SSN_DIR_FROM_CLIENT ) enablePort( nrp->c_ports, port ); return nrp; } void registerPortForReassembly( char *network, uint16_t port, int ra_dir ) { SFIP_RET status; SDListItem *net_node; // if network is NULL then this is a registration for the default policy if( network == NULL ) { if( ra_dir & SSN_DIR_FROM_SERVER ) enablePort( default_nrps.s_ports, port ); if( ra_dir & SSN_DIR_FROM_CLIENT ) enablePort( default_nrps.c_ports, port ); return; } else { sfcidr_t *ip = sfip_alloc( network, &status ); if ( status != SFIP_SUCCESS ) { WarningMessage( "Invalid network ( %s ) in reassembly registration request.", network ); // free ip struct allocated above... if( ip != NULL ) sfip_free( ip ); return; } net_node = sf_sdlist_head( &targeted_nrps ); while( net_node != NULL ) { SFIP_RET rc; NetworkReassemblyPorts *net_data = sf_sdlist_data( net_node ); rc = sfip_compare( &net_data->ip->addr, &ip->addr ); if( rc == SFIP_EQUAL ) { // already have this network in list, add the new port... if( ra_dir & SSN_DIR_FROM_SERVER ) enablePort( net_data->s_ports, port ); if( ra_dir & SSN_DIR_FROM_CLIENT ) enablePort( net_data->c_ports, port ); break; } else if( rc == SFIP_GREATER ) { // current network in list is 'greater' than network in registration request, // this means request network is not in the list, so we add it as node before // the current node... // ptr to ip struct allocated above is stored in net data so set to NULL so // we don't free it below... NetworkReassemblyPorts *new_net = initNetworkPortReassembly( ip, port, ra_dir); if( new_net != NULL) { sf_sdlist_ins_prev( &targeted_nrps, net_node, new_net ); ip = NULL; } break; } else { // current network in list is 'less' than network in registration request... // move to next entry net_node = sf_sdlist_next( net_node ); } } // finished searching list for matching network... if net_node is NULL then we did not // find a match AND all networks in the list are 'less than' or more general than the // request network... if( net_node == NULL ) { // at list end and found no match, add new network to end of list // ptr to ip struct allocated above is stored in net data so set to NULL so // we don't free it below... NetworkReassemblyPorts *new_net = initNetworkPortReassembly( ip, port, ra_dir); if( new_net != NULL) { sf_sdlist_ins_next( &targeted_nrps, sf_sdlist_tail( &targeted_nrps ), new_net ); ip = NULL; } } // free ip struct allocated above if not still in use... if( ip != NULL ) sfip_free( ip ); } } static void enablePortsForReassembly( NetworkReassemblyPorts *nrp_list, StreamTcpPolicy *policy ) { uint32_t port; for( port = 0; port < MAXPORTS; port++) { if( isPortEnabled( nrp_list->s_ports, port ) ) { #if 0 // TBD-EDM InitFlushMgr(&policy->flush_config[port].server, &policy->flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); #endif } if( isPortEnabled( nrp_list->c_ports, port ) ) { #if 0 // TBD-EDM InitFlushMgr(&policy->flush_config[port].client, &policy->flush_point_list, STREAM_FLPOLICY_FOOTPRINT, 0); #endif } } } void enableTargetedPolicyReassemblyPorts( StreamConfig *config ) { SDListItem *prev_node; SDListItem *net_node = sf_sdlist_head( &targeted_nrps ); while( net_node != NULL ) { NetworkReassemblyPorts *nrp_list = sf_sdlist_data( net_node ); StreamTcpPolicy *policy = StreamSearchTcpConfigForBoundPolicy( config->tcp_config, &nrp_list->ip->addr ); if( policy == config->tcp_config->default_policy ) { // no existing policy for this network...create a new one by cloning // the default policy policy = StreamTcpPolicyClone( config->tcp_config->default_policy, config ); } // init the ports registered specifically for this network... enablePortsForReassembly( nrp_list, policy ); // now look back up the list of networks and register ports from all networks // that contain the current network... prev_node = sf_sdlist_prev( net_node ); while( prev_node != NULL ) { NetworkReassemblyPorts *prev_nrp_list = sf_sdlist_data( prev_node ); if( sfip_contains( prev_nrp_list->ip, &nrp_list->ip->addr ) == SFIP_CONTAINS ) enablePortsForReassembly( prev_nrp_list, policy ); prev_node = sf_sdlist_prev( prev_node ); } net_node = sf_sdlist_next( net_node ); } } void enableRegisteredPortsForReassembly( struct _SnortConfig *sc ) { int idx; tSfPolicyUserContextId stream_cfg = (stream_parsing_config)? stream_parsing_config : stream_online_config; StreamConfig *config = (StreamConfig *) sfPolicyUserDataGet(stream_cfg, getParserPolicy(sc)); // Since stream configs are inherited from the default, // we should attempt to fetch the default policy config // if the non-default lookup failed if (!config && getParserPolicy(sc) != getDefaultPolicy()) config = (StreamConfig *) sfPolicyUserDataGet( stream_cfg, getDefaultPolicy() ); if (!config) { WarningMessage( "Stream TCP not enabled in default configuration.\n" ); return; } // iterate thru list of networks, init'ing all ports requested from reassembly for each enableTargetedPolicyReassemblyPorts( config ); // ... now init ports registered with the default policy, these ports also get // registered with all targeted policies... enablePortsForReassembly( &default_nrps, config->tcp_config->default_policy ); // enable ports in all target policies... for (idx = 0; idx < config->tcp_config->num_policies; idx++) enablePortsForReassembly( &default_nrps, config->tcp_config->policy_list[ idx ] ); // all ports registered for reassembly have been enabled in all appropriate networks, // free memory used to cache the requests... StreamDestoryReassemblyPortList( ); } void SetFTPFileLocation(void *scbptr ,bool flush) { SessionControlBlock *scb = ( SessionControlBlock * ) scbptr; TcpSession * tcpssn; if ( scb && scb->proto_specific_data && scb->proto_specific_data->data) { tcpssn = (TcpSession *)scb->proto_specific_data->data; tcpssn->client.flush_mgr.flush = flush; tcpssn->server.flush_mgr.flush = flush; } } void unregisterPortForReassembly( char *network, uint16_t port, int ra_dir ) { // TBD may not need this capability... WarningMessage( "Unregistering ports for reassembly not currently supported" ); return; } bool StreamIsSessionHttp2Tcp( SessionControlBlock *scb ) { if ( scb->ha_state.session_flags & SSNFLAG_HTTP_2) { return true; } return false; } void StreamSetSessionHttp2Tcp( SessionControlBlock *scb ) { scb->ha_state.session_flags |= SSNFLAG_HTTP_2; } bool StreamIsSessionHttp2UpgTcp( SessionControlBlock *scb ) { if ( scb->ha_state.session_flags & SSNFLAG_HTTP_2_UPG) { return true; } return false; } void StreamSetSessionHttp2UpgTcp( SessionControlBlock *scb ) { scb->ha_state.session_flags |= SSNFLAG_HTTP_2_UPG; } #ifdef SNORT_RELOAD void SessionTCPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout) { SessionReload(tcp_lws_cache, max_sessions, pruningTimeout, nominalTimeout #ifdef REG_TEST , "TCP" #endif ); } unsigned SessionTCPReloadAdjust(unsigned maxWork) { return SessionProtocolReloadAdjust(tcp_lws_cache, session_configuration->max_tcp_sessions, maxWork, session_configuration->memcap #ifdef REG_TEST , "TCP" #endif ); } #endif #ifdef HAVE_DAQ_DECRYPTED_SSL int StreamSimulatePeerTcpAckp( SessionControlBlock *scb, uint8_t dir, uint32_t tcp_payload_len ) { StreamTracker *talker = NULL; StreamTracker *listener = NULL; TcpSession *tcpssn = NULL; if ((tcp_payload_len == 0) || !scb) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: no SCB %p or zero length %d \n", scb, tcp_payload_len);); return -1; } if (scb->proto_specific_data) tcpssn = (TcpSession *)scb->proto_specific_data->data; if (!tcpssn) { STREAM_DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Stream: no tcpssn \n");); return -1; } if (dir == 0) { // Packet from Server. (p->packet_flags & PKT_FROM_SERVER) is TRUE. talker = &tcpssn->server; listener = &tcpssn->client; } else { talker = &tcpssn->client; listener = &tcpssn->server; } talker->l_unackd += tcp_payload_len; listener->r_win_base += tcp_payload_len; purge_flushed_ackd(tcpssn, listener); return 0; } #endif size_t get_tcp_used_mempool() { if (tcp_lws_cache && tcp_lws_cache->protocol_session_pool) return tcp_lws_cache->protocol_session_pool->used_memory; return 0; } snort-2.9.20/src/preprocessors/Stream6/Makefile.am0000444000175000017500000000076714230012554020152 0ustar apoapo## $Id AUTOMAKE_OPTIONS=foreign no-dependencies noinst_LIBRARIES = libstream6.a libstream6_a_SOURCES = \ snort_stream_tcp.c \ snort_stream_tcp.h \ snort_stream_udp.c \ snort_stream_udp.h \ snort_stream_icmp.c \ snort_stream_icmp.h \ snort_stream_ip.c \ snort_stream_ip.h \ stream_paf.c \ stream_paf.h \ stream_common.c \ stream_common.h libstream6_a_LIBADD = \ snort_stream_tcp.o \ snort_stream_udp.o \ snort_stream_icmp.o \ snort_stream_ip.o \ stream_paf.o \ stream_common.o INCLUDES = @INCLUDES@ snort-2.9.20/src/preprocessors/Stream6/stream_paf.h0000644000175000017500000000747714241077145020430 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ //-------------------------------------------------------------------- // s5 protocol aware flushing stuff // // @file stream5_paf.h // @author Russ Combs //-------------------------------------------------------------------- #ifndef __STREAM_PAF_H__ #define __STREAM_PAF_H__ #include "sf_types.h" #include "sfPolicy.h" #include "stream_api.h" #define MAX_PAF_USER 2 void* s5_paf_new(void); // create new paf config (per policy) void s5_paf_delete(void*); // free config uint8_t s5_paf_register_port( struct _SnortConfig *, // snort configuration tSfPolicyId, // applicable policy uint16_t port, // server port bool toServer, // direction of interest relative to server port PAF_Callback, // stateful byte scanning function bool autoEnable // enable PAF reassembly regardless of s5 ports config ); uint8_t s5_paf_register_service( struct _SnortConfig *, // snort configuration tSfPolicyId, // same as above uint16_t service, // service ordinal bool toServer, PAF_Callback, bool autoEnable ); void s5_paf_register_free( uint8_t id, PAF_Free_Callback ); // flush indicates s5 port config uint16_t s5_paf_port_registration (void* pv, uint16_t port, bool c2s, bool flush); uint16_t s5_paf_port_registration_all (void* pv, uint16_t port, bool c2s, bool flush); uint16_t s5_paf_service_registration (void* pv, uint16_t service, bool c2s, bool flush); int cb_mask_cmp(void* pv, uint16_t service, bool c2s, uint16_t cb_mask); typedef enum _FlushMode { FLUSH_MODE_NORMAL = 0, FLUSH_MODE_PRE_DISCARD, FLUSH_MODE_DISCARD }FlushMode; typedef struct { void* user[MAX_PAF_USER]; // arbitrary user data uint32_t seq; // stream cursor uint32_t pos; // last flush position uint32_t fpt; // current flush point uint32_t tot; // total bytes flushed PAF_Status paf; // current scan state uint8_t mode; uint16_t cb_mask; // callback mask uint8_t cb_id[MAX_PAF_USER]; uint32_t fpt_eoh; //Flush point at EOH } PAF_State; // per session direction // called at session start void s5_paf_setup(PAF_State*, uint16_t registration); void s5_paf_clear(PAF_State*); // called at session end static inline uint32_t s5_paf_position (PAF_State* ps) { return ps->seq; } static inline uint32_t s5_paf_initialized (PAF_State* ps) { return ( ps->paf != PAF_START ); } static inline uint32_t s5_paf_active (PAF_State* ps) { return ( ps->paf != PAF_ABORT ); } // called on each in order segment uint32_t s5_paf_check( void* paf_config, PAF_State*, void* ssn, const uint8_t* data, uint32_t len, uint32_t total, uint32_t seq, uint16_t port, uint64_t* flags, uint32_t fuzz); int8_t s5_paf_get_user_data_index( PAF_State *ps, uint8_t id); #endif snort-2.9.20/src/preprocessors/Stream6/snort_stream_ip.h0000644000175000017500000000351114241077134021476 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2011-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* * @file snort_stream_ip.h * @author Russ Combs * */ #ifndef __STREAM_IP_H__ #define __STREAM_IP_H__ #include "session_common.h" #include "stream_common.h" #include "sfPolicy.h" void StreamCleanIp(void); void StreamResetIp(void); void StreamInitIp(void); void StreamIpPolicyInit(StreamIpConfig*, char*); int StreamVerifyIpConfig(StreamIpConfig*, tSfPolicyId); void StreamIpConfigFree(StreamIpConfig*); int StreamProcessIp(Packet *p, SessionControlBlock *scb, SessionKey *skey); uint32_t StreamGetIpPrunes(void); void StreamResetIpPrunes(void); void IpSessionCleanup (void* lws); void SessionIPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout); unsigned SessionIPReloadAdjust(unsigned maxWork); size_t get_ip_used_mempool(); #endif snort-2.9.20/src/preprocessors/Stream6/snort_stream_icmp.c0000644000175000017500000002755014241077131022017 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort_debug.h" #include "decode.h" #include "mstring.h" #include "sfxhash.h" #include "util.h" #include "memory_stats.h" #include "spp_session.h" #include "session_api.h" #include "snort_session.h" #include "stream_common.h" #include "snort_stream_tcp.h" #include "snort_stream_udp.h" #include "snort_stream_icmp.h" #include "parser.h" #include "reg_test.h" #include "profiler.h" #include "sfPolicy.h" #ifdef PERF_PROFILING PreprocStats s5IcmpPerfStats; #endif /* client/server ip/port dereference */ #define icmp_sender_ip lwSsn->client_ip #define icmp_responder_ip lwSsn->server_ip /* D A T A S T R U C T U R E S ***********************************/ typedef struct _IcmpSession { SessionControlBlock *lwSsn; uint32_t echo_count; struct timeval ssn_time; } IcmpSession; /* G L O B A L S **************************************************/ static SessionCache* icmp_lws_cache = NULL; /* P R O T O T Y P E S ********************************************/ static void StreamParseIcmpArgs(char *, StreamIcmpPolicy *); static void StreamPrintIcmpConfig(StreamIcmpPolicy *); static int ProcessIcmpUnreach(Packet *p); static int ProcessIcmpEcho(Packet *p); void StreamInitIcmp( void ) { /* Finally ICMP */ if(icmp_lws_cache == NULL) { icmp_lws_cache = session_api->init_session_cache( SESSION_PROTO_ICMP, sizeof( IcmpSession ), NULL ); } } void StreamIcmpPolicyInit(StreamIcmpConfig *config, char *args) { if (config == NULL) return; config->num_policies++; StreamParseIcmpArgs(args, &config->default_policy); StreamPrintIcmpConfig(&config->default_policy); } static void StreamParseIcmpArgs(char *args, StreamIcmpPolicy *s5IcmpPolicy) { char **toks; int num_toks; int i; char **stoks = NULL; int s_toks; char *endPtr = NULL; s5IcmpPolicy->session_timeout = STREAM_DEFAULT_SSN_TIMEOUT; //s5IcmpPolicy->flags = 0; if(args != NULL && strlen(args) != 0) { toks = mSplit(args, ",", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { stoks = mSplit(toks[i], " ", 2, &s_toks, 0); if (s_toks == 0) { FatalError("%s(%d) => Missing parameter in Stream ICMP config.\n", file_name, file_line); } if(!strcasecmp(stoks[0], "timeout")) { if(stoks[1]) { s5IcmpPolicy->session_timeout = strtoul(stoks[1], &endPtr, 10); } if (!stoks[1] || (endPtr == &stoks[1][0]) || *endPtr) { FatalError("%s(%d) => Invalid timeout in config file. Integer parameter required.\n", file_name, file_line); } if ((s5IcmpPolicy->session_timeout > STREAM_MAX_SSN_TIMEOUT) || (s5IcmpPolicy->session_timeout < STREAM_MIN_SSN_TIMEOUT)) { FatalError("%s(%d) => Invalid timeout in config file. " "Must be between %d and %d\n", file_name, file_line, STREAM_MIN_SSN_TIMEOUT, STREAM_MAX_SSN_TIMEOUT); } if (s_toks > 2) { FatalError("%s(%d) => Invalid Stream ICMP Policy option. Missing comma?\n", file_name, file_line); } } else { FatalError("%s(%d) => Invalid Stream ICMP policy option\n", file_name, file_line); } mSplitFree(&stoks, s_toks); } mSplitFree(&toks, num_toks); } } static void StreamPrintIcmpConfig(StreamIcmpPolicy *s5IcmpPolicy) { LogMessage("Stream ICMP Policy config:\n"); LogMessage(" Timeout: %d seconds\n", s5IcmpPolicy->session_timeout); //LogMessage(" Flags: 0x%X\n", s5UdpPolicy->flags); //IpAddrSetPrint(" Bound Addresses:", s5UdpPolicy->bound_addrs); } void IcmpSessionCleanup(SessionControlBlock *ssn) { IcmpSession *icmpssn = NULL; if (ssn->ha_state.session_flags & SSNFLAG_PRUNED) { CloseStreamSession(&sfBase, SESSION_CLOSED_PRUNED); } else if (ssn->ha_state.session_flags & SSNFLAG_TIMEDOUT) { CloseStreamSession(&sfBase, SESSION_CLOSED_TIMEDOUT); } else { CloseStreamSession(&sfBase, SESSION_CLOSED_NORMALLY); } if (ssn->proto_specific_data) icmpssn = ssn->proto_specific_data->data; if (!icmpssn) { /* Huh? */ return; } /* Cleanup the proto specific data */ session_api->free_protocol_session_pool( SESSION_PROTO_ICMP, ssn ); ssn->proto_specific_data = NULL; StreamResetFlowBits(ssn); session_api->free_application_data(ssn); s5stats.icmp_sessions_released++; s5stats.active_icmp_sessions--; } uint32_t StreamGetIcmpPrunes(void) { if( icmp_lws_cache ) return session_api->get_session_prune_count( SESSION_PROTO_ICMP ); else return s5stats.icmp_prunes; } void StreamResetIcmpPrunes(void) { session_api->reset_session_prune_count( SESSION_PROTO_ICMP ); } void StreamResetIcmp(void) { session_api->purge_session_cache(icmp_lws_cache); session_api->clean_protocol_session_pool( SESSION_PROTO_ICMP ); } void StreamCleanIcmp(void) { if ( icmp_lws_cache ) s5stats.icmp_prunes = session_api->get_session_prune_count( SESSION_PROTO_ICMP ); /* Clean up session cache */ session_api->delete_session_cache( SESSION_PROTO_ICMP ); icmp_lws_cache = NULL; } void StreamIcmpConfigFree(StreamIcmpConfig *config) { if (config == NULL) return; SnortPreprocFree(config, sizeof(StreamIcmpConfig), PP_STREAM, PP_MEM_CATEGORY_CONFIG); } int StreamVerifyIcmpConfig(StreamIcmpConfig *config, tSfPolicyId policy_id) { if (config == NULL) return -1; if (!icmp_lws_cache) return -1; if (config->num_policies == 0) return -1; return 0; } int StreamProcessIcmp(Packet *p) { int status = 0; switch (p->icmph->type) { case ICMP_DEST_UNREACH: s5stats.icmp_unreachable++; if (p->icmph->code != ICMP_FRAG_NEEDED) { status = ProcessIcmpUnreach(p); } else { s5stats.icmp_unreachable_code4++; } break; case ICMP_ECHO: case ICMP_ECHOREPLY: status = ProcessIcmpEcho(p); break; default: /* We only handle the above ICMP messages with stream5 */ break; } return status; } static int ProcessIcmpUnreach(Packet *p) { /* Handle ICMP unreachable */ SessionKey skey; SessionControlBlock *ssn = NULL; uint16_t sport; uint16_t dport; sfaddr_t *src; sfaddr_t *dst; /* No "orig" IP Header */ if (!p->orig_iph) return 0; /* Get TCP/UDP/ICMP session from original protocol/port info * embedded in the ICMP Unreach message. This is already decoded * in p->orig_foo. TCP/UDP ports are decoded as p->orig_sp/dp. */ skey.protocol = GET_ORIG_IPH_PROTO(p); sport = p->orig_sp; dport = p->orig_dp; src = GET_ORIG_SRC(p); dst = GET_ORIG_DST(p); if (sfip_fast_lt6(src, dst)) { COPY4(skey.ip_l, sfaddr_get_ip6_ptr(src)); skey.port_l = sport; COPY4(skey.ip_h, sfaddr_get_ip6_ptr(dst)); skey.port_h = dport; } else if (IP_EQUALITY(src, dst)) { COPY4(skey.ip_l, sfaddr_get_ip6_ptr(src)); COPY4(skey.ip_h, skey.ip_l); if (sport < dport) { skey.port_l = sport; skey.port_h = dport; } else { skey.port_l = dport; skey.port_h = sport; } } else { COPY4(skey.ip_l, sfaddr_get_ip6_ptr(dst)); COPY4(skey.ip_h, sfaddr_get_ip6_ptr(src)); skey.port_l = dport; skey.port_h = sport; } if (p->vh) skey.vlan_tag = (uint16_t)VTH_VLAN(p->vh); else skey.vlan_tag = 0; switch (skey.protocol) { case IPPROTO_TCP: /* Lookup a TCP session */ ssn = GetLWTcpSession(&skey); break; case IPPROTO_UDP: /* Lookup a UDP session */ ssn = GetLWUdpSession(&skey); break; case IPPROTO_ICMP: /* Lookup a ICMP session */ ssn = session_api->get_session_by_key(icmp_lws_cache, &skey); break; } if (ssn) { /* Mark this session as dead. */ DEBUG_WRAP(DebugMessage(DEBUG_STREAM_STATE, "Marking session as dead, per ICMP Unreachable!\n");); ssn->ha_state.session_flags |= SSNFLAG_DROP_CLIENT; ssn->ha_state.session_flags |= SSNFLAG_DROP_SERVER; ssn->session_state |= STREAM_STATE_UNREACH; s5stats.active_icmp_sessions++; } return 0; } static int ProcessIcmpEcho(Packet *p) { //SessionKey skey; //SessionControlBlock *ssn = NULL; s5stats.active_icmp_sessions++; return 0; } void IcmpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port) { IcmpSession *icmpssn = ssn->proto_specific_data->data; sfaddr_t tmpIp; if (!icmpssn) { /* Huh? */ return; } if (IP_EQUALITY(&icmpssn->icmp_sender_ip, ip)) { if ((dir == SSN_DIR_FROM_SENDER) && (ssn->ha_state.direction == SSN_DIR_FROM_SENDER)) { /* Direction already set as SENDER */ return; } } else if (IP_EQUALITY(&icmpssn->icmp_responder_ip, ip)) { if ((dir == SSN_DIR_FROM_RESPONDER) && (ssn->ha_state.direction == SSN_DIR_FROM_RESPONDER)) { /* Direction already set as RESPONDER */ return; } } /* Swap them -- leave ssn->ha_state.direction the same */ tmpIp = icmpssn->icmp_sender_ip; icmpssn->icmp_sender_ip = icmpssn->icmp_responder_ip; icmpssn->icmp_responder_ip = tmpIp; } #ifdef SNORT_RELOAD void SessionICMPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout) { SessionReload(icmp_lws_cache, max_sessions, pruningTimeout, nominalTimeout #ifdef REG_TEST , "ICMP" #endif ); } unsigned SessionICMPReloadAdjust(unsigned maxWork) { return SessionProtocolReloadAdjust(icmp_lws_cache, session_configuration->max_icmp_sessions, maxWork, 0 #ifdef REG_TEST , "ICMP" #endif ); } #endif size_t get_icmp_used_mempool() { if (icmp_lws_cache && icmp_lws_cache->protocol_session_pool) return icmp_lws_cache->protocol_session_pool->used_memory; return 0; } snort-2.9.20/src/preprocessors/Stream6/snort_stream_icmp.h0000644000175000017500000000357214241077132022023 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef STREAM_ICMP_H_ #define STREAM_ICMP_H_ #include "session_common.h" #include "stream_common.h" #include "sfPolicy.h" void StreamCleanIcmp(void); void StreamResetIcmp(void); void StreamInitIcmp(void); void StreamIcmpPolicyInit(StreamIcmpConfig *, char *); int StreamVerifyIcmpConfig(StreamIcmpConfig *, tSfPolicyId); int StreamProcessIcmp(Packet *p); void IcmpUpdateDirection(SessionControlBlock *ssn, char dir, sfaddr_t* ip, uint16_t port); void StreamIcmpConfigFree(StreamIcmpConfig *); uint32_t StreamGetIcmpPrunes(void); void StreamResetIcmpPrunes(void); void IcmpSessionCleanup(SessionControlBlock *ssn); void SessionICMPReload(uint32_t max_sessions, uint16_t pruningTimeout, uint16_t nominalTimeout); unsigned SessionICMPReloadAdjust(unsigned maxWork); size_t get_icmp_used_mempool(); #endif /* STREAM_ICMP_H_ */ snort-2.9.20/src/preprocessors/perf-flow.h0000644000175000017500000000641314241077051016650 0ustar apoapo/* $Id$ */ /* ** perf-flow.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Marc Norton ** Dan Roelker ** ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef _PERF_FLOW_H #define _PERF_FLOW_H #include "sf_types.h" #include "sfxhash.h" #include "ipv6_port.h" #include "decode.h" #define SF_MAX_PKT_LEN 9000 #define SF_MAX_PORT UINT16_MAX typedef enum { SFS_TYPE_TCP = 0, SFS_TYPE_UDP = 1, SFS_TYPE_OTHER = 2, SFS_TYPE_MAX = 3 } SFSType; typedef enum { SFS_STATE_TCP_ESTABLISHED = 0, SFS_STATE_TCP_CLOSED = 1, SFS_STATE_UDP_CREATED = 2, SFS_STATE_MAX = 3 } SFSState; typedef struct _portflow { double totperc[SF_MAX_PORT+1]; double sport_rate[SF_MAX_PORT+1]; double dport_rate[SF_MAX_PORT+1]; } PORTFLOW; typedef struct _icmpflow { double totperc[256]; int display[256]; } ICMPFLOW; typedef struct _sfflow { time_t time; uint64_t *pktLenCnt; uint64_t pktTotal; uint64_t byteTotal; uint64_t *pktLenPercent; uint64_t *portTcpSrc; uint64_t *portTcpDst; uint64_t *portUdpSrc; uint64_t *portUdpDst; uint64_t *typeIcmp; uint64_t portTcpHigh; uint64_t portTcpTotal; uint64_t portUdpHigh; uint64_t portUdpTotal; uint64_t typeIcmpTotal; SFXHASH *ipMap; } SFFLOW; typedef struct _sfflow_stats { time_t time; double pktLenPercent[SF_MAX_PKT_LEN + 2]; int pktLenPercentCount; double trafficTCP; double trafficUDP; double trafficICMP; double trafficOTHER; PORTFLOW portflowTCP; double portflowHighTCP; int portflowTCPCount; PORTFLOW portflowUDP; double portflowHighUDP; int portflowUDPCount; ICMPFLOW flowICMP; int flowICMPCount; } SFFLOW_STATS; /* ** Functions for the performance functions to call */ int InitFlowStats (SFFLOW *sfFlow); int InitFlowIPStats (SFFLOW *sfFlow); void UpdateFlowStats(SFFLOW *, Packet *); void ProcessFlowStats(SFFLOW *sfFlow, FILE *fh, int console); void ProcessFlowIPStats(SFFLOW *sfFlow, FILE *fh, int console); int UpdateFlowIPStats(SFFLOW *, sfaddr_t* src_addr, sfaddr_t* dst_addr, int len, SFSType type); int UpdateFlowIPState(SFFLOW *, sfaddr_t* src_addr, sfaddr_t* dst_addr, SFSState state); void FreeFlowStats(SFFLOW *sfFlow); void FreeFlowIPStats(SFFLOW *sfFlow); void LogFlowPerfHeader(FILE *); #endif snort-2.9.20/src/preprocessors/normalize.h0000644000175000017500000000570014241077043016746 0ustar apoapo/* $Id$ */ /**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __NORMALIZE_H__ #define __NORMALIZE_H__ #ifdef NORMALIZER #include "decode.h" #include "snort.h" #include "spp_normalize.h" // TBD encapsulate implementation within normalize.c! struct _NormalizerContext; // all normalizers look like this: // the return is 1 if packet was changed, else 0 typedef int (*Normalizer)( struct _NormalizerContext*, Packet*, uint8_t layer, int changes); typedef enum registerFunc { unIntialized = 0, init, reload } registerFunc; typedef struct _NormalizerContext { uint32_t normalizer_flags; uint8_t normalizer_options[32]; NormMode normMode; // these must be in the same order PROTO_IDs are defined! // if entry is NULL, proto doesn't have normalization or it is disabled Normalizer normalizers[PROTO_MAX]; registerFunc regFunc; } NormalizerContext; int Norm_SetConfig(NormalizerContext*); int Norm_Packet(NormalizerContext*, Packet*); static inline void Norm_Enable(NormalizerContext* nc, NormFlags nf) { nc->normalizer_flags |= nf; } static inline void Norm_Disable(NormalizerContext* nc, NormFlags nf) { nc->normalizer_flags &= ~nf; } static inline int Norm_IsEnabled(const NormalizerContext* nc, NormFlags nf) { return ( (nc->normalizer_flags & nf) != 0 ); } static inline void Norm_TcpPassOption(NormalizerContext* nc, uint8_t opt) { uint8_t byte = (opt >> 3), bit = (1 << (opt & 0x07)); nc->normalizer_options[byte] |= bit; } static inline void Norm_TcpDropOption(NormalizerContext* nc, uint8_t opt) { uint8_t byte = (opt >> 3), bit = (1 << (opt & 0x07)); nc->normalizer_options[byte] &= ~bit; } static inline int Norm_TcpIsOptional(const NormalizerContext* nc, uint8_t opt) { uint8_t byte = (opt >> 3), bit = (1 << (opt & 0x07)); return ( (nc->normalizer_options[byte] & bit) != 0 ); } void Norm_PrintStats(void); void Norm_ResetStats(void); #endif // NORMALIZER #endif // __NORMAL_H__ snort-2.9.20/src/preprocessors/perf-base.c0000644000175000017500000016520414241077044016614 0ustar apoapo/* ** $Id$ ** ** perf-base.c ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Dan Roelker ** Marc Norton ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** ** DESCRIPTION ** The following subroutines are concerned with getting ** basic stats on packet bytes and times that an app ** takes in processing packets. The times measured are ** kernel and user time for the process. Real-time ** (wall clock) is also measured to show when processing ** has reached capacity and to measure the true processing ** that the app is currently doing. ** ** NOTES ** 4.8.02 : Initial Code (DJR,MAN) ** 4.22.02 : Added Comments (DJR) ** 7.10.02 : Added sfprocpidstats code for SMP linux (DJR) ** 8.8.02 : Added stream4 instrumentation (cmg) ** 9.1.04 : Removed NO_PKTS, ACCUMULATE/RESET #defines, now we use SFBASE->iReset ** and the permonitor command has 'reset' and 'accrue' commands instead.(MAN) ** 10.4.06 : Added UDP Session Stats (SAS) ** 4.3.07 : Added stats for TCP sessions (SAS) */ #include #ifndef WIN32 #include #include #endif #include #include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "sf_types.h" #include "snort.h" #include "util.h" #include "mpse.h" #include "sfdaq.h" #include "session_api.h" #include "stream_api.h" #include "sf_types.h" #include "snort_bounds.h" static void GetPktDropStats(SFBASE *, SFBASE_STATS *); static void DisplayBasePerfStatsConsole(SFBASE_STATS *, int); static int CalculateBasePerfStats(SFBASE *, SFBASE_STATS *, int); static void LogBasePerfStats(SFBASE_STATS *, FILE *); static void GetPacketsPerSecond(SFBASE *, SFBASE_STATS *, SYSTIMES *, int); static void GetMbitsPerSecond(SFBASE *, SFBASE_STATS *, SYSTIMES *, int); static int GetProcessingTime(SYSTIMES *, SFBASE *); static void GetEventsPerSecond(SFBASE *, SFBASE_STATS *, SYSTIMES *); static void GetuSecondsPerPacket(SFBASE *, SFBASE_STATS *, SYSTIMES *); static void GetCPUTime(SFBASE *, SFBASE_STATS *, SYSTIMES *); //We should never output NaN or Infitity static inline double zeroFpException(double in) { //FIXME WIN32 -vc++6 does NOT provide the function |isnan() | nor |isinf()| - VJR 7/22/15 // (see also:http://stackoverflow.com/questions/2249110/how-do-i-make-a-portable-isnan-isinf-function) #ifndef WIN32 if (isnan(in) || isinf(in)) return 0.0; #endif return in; } /* ** NAME ** InitBaseStats ** DESCRIPTION ** Initializes structs and variables for the next performance ** sample. ** ** FORMAL INPUTS ** SFBASE * -- pointer to structure to initialize ** ** FORMAL OUTPUTS ** int -- 0 is successful */ int InitBaseStats(SFBASE *sfBase) { int todRet = -1; struct timeval tvTime; #ifndef WIN32 #ifndef LINUX_SMP struct rusage rusage; int rusageRet = -1; #endif #ifdef LINUX_SMP todRet = gettimeofday(&tvTime, NULL); #else rusageRet = getrusage(RUSAGE_SELF, &rusage); todRet = gettimeofday(&tvTime, NULL); if (rusageRet >= 0) { sfBase->usertime_sec = (double)rusage.ru_utime.tv_sec + ((double)rusage.ru_utime.tv_usec * 1.0e-6); sfBase->systemtime_sec = (double)rusage.ru_stime.tv_sec + ((double)rusage.ru_stime.tv_usec * 1.0e-6); } else { sfBase->usertime_sec = 0; sfBase->systemtime_sec = 0; } #endif /* !LINUX_SMP */ #else sfBase->usertime_sec = 0; sfBase->systemtime_sec = 0; todRet = gettimeofday(&tvTime, NULL); #endif /* !WIN32 */ if(todRet >= 0) { sfBase->realtime_sec = (double)tvTime.tv_sec + ((double)tvTime.tv_usec * 1.0e-6); } else { sfBase->realtime_sec = 0; } sfBase->total_blocked_packets = 0; sfBase->total_injected_packets = 0; sfBase->total_wire_packets = 0; sfBase->total_ipfragmented_packets = 0; sfBase->total_ipreassembled_packets = 0; sfBase->total_packets = 0; sfBase->total_rebuilt_packets = 0; sfBase->total_wire_bytes = 0; sfBase->total_ipfragmented_bytes = 0; sfBase->total_ipreassembled_bytes = 0; sfBase->total_bytes = 0; sfBase->total_rebuilt_bytes = 0; sfBase->total_blocked_bytes = 0; sfBase->iNewSessions = 0; sfBase->iDeletedSessions = 0; sfBase->iStreamFlushes = 0; sfBase->iStreamFaults = 0; sfBase->iStreamTimeouts = 0; //sfBase->iMaxSessions = 0; //sfBase->iMaxSessionsInterval = 0; //sfBase->iMidStreamSessions = 0; //sfBase->iClosedSessions = 0; //sfBase->iPrunedSessions = 0; //sfBase->iDroppedAsyncSessions = 0; //sfBase->iSessionsInitializing = 0; //sfBase->iSessionsEstablished = 0; //sfBase->iSessionsClosing = 0; sfBase->iFragCreates = 0; sfBase->iFragCompletes = 0; sfBase->iFragInserts = 0; sfBase->iFragDeletes = 0; sfBase->iFragAutoFrees = 0; sfBase->iFragFlushes = 0; sfBase->iFragTimeouts = 0; sfBase->iFragFaults = 0; #ifdef NORMALIZER { int i = 0; for ( i = 0; i < PERF_COUNT_MAX; i++ ) { sfBase->iPegs[i][NORM_MODE_ON] = 0; sfBase->iPegs[i][NORM_MODE_WOULDA] = 0; } } #endif sfBase->iNewUDPSessions = 0; sfBase->iDeletedUDPSessions = 0; //sfBase->iAttributeHosts = 0; //sfBase->iAttributeReloads = 0; sfBase->total_mpls_packets = 0; sfBase->total_mpls_bytes = 0; sfBase->total_blocked_mpls_packets = 0; sfBase->total_blocked_mpls_bytes = 0; sfBase->total_tcp_filtered_packets = 0; sfBase->total_udp_filtered_packets = 0; sfBase->frag3_mem_in_use = 0; sfBase->stream5_mem_in_use = 0; sfBase->total_iAlerts = 0; return 0; } /* ** NAME ** UpdateBaseStats ** ** DESCRIPTION ** Simple update of stats. ** ** FORMAL INPUTS ** SFBASE * - structure to update ** int - length of packet payload in bytes ** ** FORMAL OUTPUTS ** int - 0 is successful ** ** Add in Ethernet Overhead - assume a standerd Ethernet service ** ** Ethernet Frame ** --------------- ** | <----------- PCAP Packet --------> | ** Preamble Dest Mac Src Mac Type Payload CRC IFG ** | 8 bytes | 6 Bytes | 6 Bytes | 2-Bytes | 46-1500 | 4 Bytes | 12 | ** ** Len = PCAP Packet + 4 bytes for CRC ** Overhead = 20 bytes ** Min on the wire == 84 bytes ** Min Size of PCAP packet = 60 bytes (84 - 20 overhead - 4 CRC) ** ** Len is the amount of user data being sent. This will be less then ** actual wire-speed, because of the interframe gap (96 bits) and preamble ** (8 bytes). ** ** A 60 byte minimum packet uses 672 bits (60 bytes + 4 CRC), this limits a ** 1000 Mbit network to 1.488 Million packets with a bandwidth of 760 ** Mbits. The lost 240 Mbits is due to interframe gap (96 bits) and preamble ** (8 bytes). ** ** Even if the actual data is only 40 bytes per packet (ie, an empty ** TCP ACK), wire data is still 64 bytes per packet, even though actual ** packet size is 40 bytes. Bandwith drops to 480 Mbits. ** ** This explains why when a network goes over 50% capactiy you are closer to ** the edge than you realize, depending on the traffic profile. At 75% you ** are at the limit of your network, if you can get there. ** ** bool rebuilt determines whether the packet is rebuilt or not. We keep ** separate statistics between wire pkts and rebuilt pkts. ** */ void UpdateBaseStats(SFBASE *sfBase, Packet *p, bool rebuilt) { uint32_t len = p->pkth->caplen; if (!rebuilt) { // For SYN to SYN/ACK counts to help determine if traffic is asynchronous if ((p->tcph != NULL) && (p->tcph->th_flags & TH_SYN)) { if (p->tcph->th_flags & TH_ACK) sfBase->iSynAcks++; else sfBase->iSyns++; } len += 4; /* for the CRC */ } /* Includes wire, IP reassembled & TCP rebuilt packets * that make it to the application layer. */ sfBase->total_packets++; sfBase->total_bytes += len; } /* ** NAME ** UpdateWireStats ** ** DESCRIPTION ** Simple update of stats for "on the wire". ** ** FORMAL INPUTS ** SFBASE * - structure to update ** int - length of packet payload in bytes ** ** FORMAL OUTPUTS ** none */ void UpdateWireStats(SFBASE *sfBase, int len, int dropped, int inject) { sfBase->total_wire_packets++; len += 4; /* for the CRC */ sfBase->total_wire_bytes += len; if( dropped ) { sfBase->total_blocked_packets++; sfBase->total_blocked_bytes += len; } if ( inject ) sfBase->total_injected_packets++; } void UpdateMPLSStats(SFBASE *sfBase, int len, int dropped) { #ifdef MPLS sfBase->total_mpls_packets++; len += 4; /* for the CRC */ sfBase->total_mpls_bytes += len; if( dropped ) { sfBase->total_blocked_mpls_packets++; sfBase->total_blocked_mpls_bytes += len; } #endif } /* ** NAME ** UpdateIPFragStats ** ** DESCRIPTION ** Simple update of stats for IP fragmented packets ** ** FORMAL INPUTS ** SFBASE * - structure to update ** int - length of packet payload in bytes ** ** FORMAL OUTPUTS ** none */ void UpdateIPFragStats(SFBASE *sfBase, int len) { sfBase->total_ipfragmented_packets++; len += 4; /* for the CRC */ sfBase->total_ipfragmented_bytes += len; } /* ** NAME ** UpdateIPReassStats ** ** DESCRIPTION ** Simple update of stats for IP reassembled packets ** ** FORMAL INPUTS ** SFBASE * - structure to update ** int - length of packet payload in bytes ** ** FORMAL OUTPUTS ** none */ void UpdateIPReassStats(SFBASE *sfBase, int len) { sfBase->total_ipreassembled_bytes += len; sfBase->total_ipreassembled_packets++; } void UpdateStreamReassStats(SFBASE *sfBase, int len) { sfBase->total_rebuilt_bytes += len; sfBase->total_rebuilt_packets++; } /**API to update stats for packets discarded due to * TCP/UDP port/service based filtering. * * @param sfBase - pointer to accumulated stats */ void UpdateFilteredPacketStats(SFBASE *sfBase, IpProto proto) { switch (proto) { case IPPROTO_TCP: sfBase->total_tcp_filtered_packets++; break; case IPPROTO_UDP: sfBase->total_udp_filtered_packets++; break; default: //coding error ; } } /* ** NAME ** AddStreamSession ** ** DESCRIPTION ** Add a session count ** ** FORMAL INPUTS ** SFBASE * - ptr to update. ** ** FORMAL OUTPUTS ** int - 0 is successful */ int AddStreamSession(SFBASE *sfBase, uint32_t flags) { sfBase->iTotalSessions++; sfBase->iNewSessions++; if (flags & SSNFLAG_MIDSTREAM) sfBase->iMidStreamSessions++; if(sfBase->iTotalSessions > sfBase->iMaxSessions) sfBase->iMaxSessions = sfBase->iTotalSessions; if(sfBase->iTotalSessions > sfBase->iMaxSessionsInterval) sfBase->iMaxSessionsInterval = sfBase->iTotalSessions; return 0; } /* ** NAME ** CloseStreamSession ** ** DESCRIPTION ** Add a session count ** ** FORMAL INPUTS ** SFBASE * - ptr to update. ** ** FORMAL OUTPUTS ** int - 0 is successful */ int CloseStreamSession(SFBASE *sfBase, char flags) { if (flags & SESSION_CLOSED_NORMALLY) sfBase->iClosedSessions++; else{ if (flags & SESSION_CLOSED_TIMEDOUT) sfBase->iStreamTimeouts++; if (flags & SESSION_CLOSED_PRUNED) sfBase->iPrunedSessions++; if (flags & SESSION_CLOSED_ASYNC) sfBase->iDroppedAsyncSessions++; } return 0; } /* ** NAME ** RemoveStreamSession ** ** DESCRIPTION ** Add a session count ** ** FORMAL INPUTS ** SFBASE * - ptr to update. ** ** FORMAL OUTPUTS ** int - 0 is successful */ int RemoveStreamSession(SFBASE *sfBase) { sfBase->iTotalSessions--; sfBase->iDeletedSessions++; return 0; } /* ** NAME ** AddUDPSession ** ** DESCRIPTION ** Add a session count ** ** FORMAL INPUTS ** SFBASE * - ptr to update. ** ** FORMAL OUTPUTS ** int - 0 is successful */ int AddUDPSession(SFBASE *sfBase) { sfBase->iTotalUDPSessions++; sfBase->iNewUDPSessions++; if(sfBase->iTotalUDPSessions > sfBase->iMaxUDPSessions) sfBase->iMaxUDPSessions = sfBase->iTotalUDPSessions; return 0; } /* ** NAME ** RemoveUDPSession ** ** DESCRIPTION ** Add a session count ** ** FORMAL INPUTS ** SFBASE * - ptr to update. ** ** FORMAL OUTPUTS ** int - 0 is successful */ int RemoveUDPSession(SFBASE *sfBase) { sfBase->iTotalUDPSessions--; sfBase->iDeletedUDPSessions++; return 0; } /* ** NAME ** ProcessBaseStats ** ** DESCRIPTION ** Main function to process Base Stats. ** ** FORMAL INPUTS ** SFBASE * - ptr to update. ** ** FORMAL OUTPUTS ** int - 0 is successful */ void ProcessBaseStats(SFBASE *sfBase, FILE *fh, int console, int max_stats) { SFBASE_STATS sfBaseStats; if (fh || console) { if (CalculateBasePerfStats(sfBase, &sfBaseStats, max_stats)) return; if (console) DisplayBasePerfStatsConsole(&sfBaseStats, max_stats); if (fh) LogBasePerfStats(&sfBaseStats, fh); } } static int GetProcessingTime(SYSTIMES *Systimes, SFBASE *sfBase) { int todRet = -1; struct timeval tvTime; #ifdef LINUX_SMP if(sfProcessProcPidStats(&(sfBase->sfProcPidStats))) return -1; todRet = gettimeofday(&tvTime, NULL); #else struct rusage rusage; int rusageRet; #ifndef WIN32 rusageRet = getrusage(RUSAGE_SELF, &rusage); #else rusageRet = -1; #endif /* !WIN32 */ todRet = gettimeofday(&tvTime, NULL); if (rusageRet < 0) { rusage.ru_utime.tv_sec = 0; rusage.ru_utime.tv_usec = 0; rusage.ru_stime.tv_sec = 0; rusage.ru_stime.tv_usec = 0; } Systimes->usertime = ((double)rusage.ru_utime.tv_sec + ((double)rusage.ru_utime.tv_usec * 1.0e-6)) - sfBase->usertime_sec; Systimes->systemtime = ((double)rusage.ru_stime.tv_sec + ((double)rusage.ru_stime.tv_usec * 1.0e-6)) - sfBase->systemtime_sec; Systimes->totaltime = Systimes->usertime + Systimes->systemtime; #endif /* LINUX_SMP */ if (todRet < 0) { return todRet; } Systimes->realtime = ((double)tvTime.tv_sec + ((double)tvTime.tv_usec * 1.0e-6)) - sfBase->realtime_sec; return 0; } static void GetEventsPerSecond(SFBASE *sfBase, SFBASE_STATS *sfBaseStats, SYSTIMES *Systimes) { sfBaseStats->alerts_per_second = (double)(pc.alert_pkts - sfBase->iAlerts) / Systimes->realtime; sfBase->iAlerts = pc.alert_pkts; sfBaseStats->total_alerts_per_second = (double)(pc.total_alert_pkts - sfBase->total_iAlerts) / Systimes->realtime; sfBase->total_iAlerts = pc.total_alert_pkts; sfBaseStats->total_sessions = sfBase->iTotalSessions; sfBaseStats->max_sessions = sfBase->iMaxSessions; sfBaseStats->syns_per_second = (double)(sfBase->iSyns) / Systimes->realtime; sfBaseStats->synacks_per_second = (double)(sfBase->iSynAcks) / Systimes->realtime; sfBaseStats->deleted_sessions_per_second = (double)(sfBase->iDeletedSessions) / Systimes->realtime; sfBaseStats->new_sessions_per_second = (double)(sfBase->iNewSessions) / Systimes->realtime; sfBaseStats->tcp_sessions_midstream_per_second = (double)(sfBase->iMidStreamSessions) / Systimes->realtime; sfBaseStats->tcp_sessions_closed_per_second = (double)(sfBase->iClosedSessions) / Systimes->realtime; sfBaseStats->tcp_sessions_timedout_per_second = (double)(sfBase->iStreamTimeouts) / Systimes->realtime; sfBaseStats->tcp_sessions_pruned_per_second = (double)(sfBase->iPrunedSessions) / Systimes->realtime; sfBaseStats->tcp_sessions_dropped_async_per_second = (double)(sfBase->iDroppedAsyncSessions) / Systimes->realtime; sfBaseStats->max_tcp_sessions_interval = sfBase->iMaxSessionsInterval; sfBaseStats->stream_flushes_per_second = (double)sfBase->iStreamFlushes / Systimes->realtime; sfBaseStats->stream_faults = sfBase->iStreamFaults; sfBaseStats->stream_timeouts = sfBase->iStreamTimeouts; sfBaseStats->curr_tcp_sessions_initializing = sfBase->iSessionsInitializing; sfBaseStats->curr_tcp_sessions_established = sfBase->iSessionsEstablished; sfBaseStats->curr_tcp_sessions_closing = sfBase->iSessionsClosing; sfBaseStats->frag_creates_per_second = (double)sfBase->iFragCreates / Systimes->realtime; sfBaseStats->frag_completes_per_second = (double)sfBase->iFragCompletes / Systimes->realtime; sfBaseStats->frag_inserts_per_second = (double)sfBase->iFragInserts / Systimes->realtime; sfBaseStats->frag_deletes_per_second = (double)sfBase->iFragDeletes / Systimes->realtime; sfBaseStats->frag_autofrees_per_second = (double)sfBase->iFragAutoFrees / Systimes->realtime; sfBaseStats->frag_flushes_per_second = (double)sfBase->iFragFlushes / Systimes->realtime; sfBaseStats->max_frags = sfBase->iMaxFrags; sfBaseStats->current_frags = sfBase->iCurrentFrags; sfBaseStats->frag_timeouts = sfBase->iFragTimeouts; sfBaseStats->frag_faults = sfBase->iFragFaults; sfBase->iSyns = 0; sfBase->iSynAcks = 0; sfBase->iNewSessions = 0; sfBase->iDeletedSessions = 0; sfBase->iStreamFlushes = 0; sfBase->iStreamFaults = 0; sfBase->iStreamTimeouts = 0; sfBase->iFragCreates = 0; sfBase->iFragCompletes = 0; sfBase->iFragInserts = 0; sfBase->iFragDeletes = 0; sfBase->iFragAutoFrees = 0; sfBase->iFragFlushes = 0; sfBase->iFragTimeouts = 0; sfBase->iFragFaults = 0; #ifdef NORMALIZER { int i = 0; for ( i = 0; i < PERF_COUNT_MAX; i++ ) { sfBase->iPegs[i][NORM_MODE_ON] = 0; sfBase->iPegs[i][NORM_MODE_WOULDA] = 0; } } #endif sfBaseStats->total_udp_sessions = sfBase->iTotalUDPSessions; sfBaseStats->max_udp_sessions = sfBase->iMaxUDPSessions; sfBaseStats->deleted_udp_sessions_per_second = (double)(sfBase->iDeletedUDPSessions) / Systimes->realtime; sfBaseStats->new_udp_sessions_per_second = (double)(sfBase->iNewUDPSessions) / Systimes->realtime; sfBase->iNewUDPSessions = 0; sfBase->iDeletedUDPSessions = 0; sfBase->iMaxSessionsInterval = sfBase->iTotalSessions; sfBase->iMidStreamSessions = 0; sfBase->iClosedSessions = 0; sfBase->iPrunedSessions = 0; sfBase->iDroppedAsyncSessions = 0; } static void GetPacketsPerSecond(SFBASE *sfBase, SFBASE_STATS *sfBaseStats, SYSTIMES *Systimes, int max_stats) { sfBaseStats->kpackets_per_sec.realtime = (double)((double)sfBase->total_packets / 1000) / Systimes->realtime; if (max_stats) { sfBaseStats->kpackets_per_sec.usertime = (double)((double)sfBase->total_packets / 1000) / Systimes->usertime; sfBaseStats->kpackets_per_sec.systemtime = (double)((double)sfBase->total_packets / 1000) / Systimes->systemtime; sfBaseStats->kpackets_per_sec.totaltime = (double)((double)sfBase->total_packets / 1000) / Systimes->totaltime; } sfBaseStats->kpackets_wire_per_sec.realtime = (double)((double)sfBase->total_wire_packets / 1000) / Systimes->realtime; if (max_stats) { sfBaseStats->kpackets_wire_per_sec.usertime = (double)((double)sfBase->total_wire_packets / 1000) / Systimes->usertime; sfBaseStats->kpackets_wire_per_sec.systemtime = (double)((double)sfBase->total_wire_packets / 1000) / Systimes->systemtime; sfBaseStats->kpackets_wire_per_sec.totaltime = (double)((double)sfBase->total_wire_packets / 1000) / Systimes->totaltime; } sfBaseStats->kpackets_ipfrag_per_sec.realtime = (double)((double)sfBase->total_ipfragmented_packets / 1000) / Systimes->realtime; if (max_stats) { sfBaseStats->kpackets_ipfrag_per_sec.usertime = (double)((double)sfBase->total_ipfragmented_packets / 1000) / Systimes->usertime; sfBaseStats->kpackets_ipfrag_per_sec.systemtime = (double)((double)sfBase->total_ipfragmented_packets / 1000) / Systimes->systemtime; sfBaseStats->kpackets_ipfrag_per_sec.totaltime = (double)((double)sfBase->total_ipfragmented_packets / 1000) / Systimes->totaltime; } sfBaseStats->kpackets_ipreass_per_sec.realtime = (double)((double)sfBase->total_ipreassembled_packets / 1000) / Systimes->realtime; if (max_stats) { sfBaseStats->kpackets_ipreass_per_sec.usertime = (double)((double)sfBase->total_ipreassembled_packets / 1000) / Systimes->usertime; sfBaseStats->kpackets_ipreass_per_sec.systemtime = (double)((double)sfBase->total_ipreassembled_packets / 1000) / Systimes->systemtime; sfBaseStats->kpackets_ipreass_per_sec.totaltime = (double)((double)sfBase->total_ipreassembled_packets / 1000) / Systimes->totaltime; } sfBaseStats->kpackets_rebuilt_per_sec.realtime = (double)((double)sfBase->total_rebuilt_packets / 1000) / Systimes->realtime; if (max_stats) { sfBaseStats->kpackets_rebuilt_per_sec.usertime = (double)((double)sfBase->total_rebuilt_packets / 1000) / Systimes->usertime; sfBaseStats->kpackets_rebuilt_per_sec.systemtime = (double)((double)sfBase->total_rebuilt_packets / 1000) / Systimes->systemtime; sfBaseStats->kpackets_rebuilt_per_sec.totaltime = (double)((double)sfBase->total_rebuilt_packets / 1000) / Systimes->totaltime; } sfBaseStats->kpackets_per_sec_mpls.realtime = (double)((double)sfBase->total_mpls_packets / 1000) / Systimes->realtime; if (max_stats) { sfBaseStats->kpackets_per_sec_mpls.usertime = (double)((double)sfBase->total_mpls_packets / 1000) / Systimes->usertime; sfBaseStats->kpackets_per_sec_mpls.systemtime = (double)((double)sfBase->total_mpls_packets / 1000) / Systimes->systemtime; sfBaseStats->kpackets_per_sec_mpls.totaltime = (double)((double)sfBase->total_mpls_packets / 1000) / Systimes->totaltime; } } static void GetuSecondsPerPacket(SFBASE *sfBase, SFBASE_STATS *sfBaseStats, SYSTIMES *Systimes) { if(sfBase->total_packets) { sfBaseStats->usecs_per_packet.usertime = (Systimes->usertime * 1.0e6) / (double)sfBase->total_packets; sfBaseStats->usecs_per_packet.systemtime = (Systimes->systemtime * 1.0e6) / (double)sfBase->total_packets; sfBaseStats->usecs_per_packet.totaltime = (Systimes->totaltime * 1.0e6) / (double)sfBase->total_packets; sfBaseStats->usecs_per_packet.realtime = (Systimes->realtime * 1.0e6) / (double)sfBase->total_packets; } } static void GetMbitsPerSecond(SFBASE *sfBase, SFBASE_STATS *sfBaseStats, SYSTIMES *Systimes, int max_stats) { /* ** These Mbits stats are for the Snort Maximum Performance stats ** that can't reliably be gotten from Linux SMP kernels. So ** we don't do them. */ if (max_stats) { sfBaseStats->mbits_per_sec.usertime = ((double) (sfBase->total_bytes<<3) * 1.0e-6) / Systimes->usertime; sfBaseStats->mbits_per_sec.systemtime = ((double) (sfBase->total_bytes<<3) * 1.0e-6) / Systimes->systemtime; sfBaseStats->mbits_per_sec.totaltime = ((double) (sfBase->total_bytes<<3) * 1.0e-6) / Systimes->totaltime; } sfBaseStats->mbits_per_sec.realtime = ((double)(sfBase->total_bytes<<3) * 1.0e-6) / Systimes->realtime; sfBaseStats->wire_mbits_per_sec.realtime = ((double)(sfBase->total_wire_bytes<<3) * 1.0e-6) / Systimes->realtime; sfBaseStats->rebuilt_mbits_per_sec.realtime = ((double)(sfBase->total_rebuilt_bytes<<3) * 1.0e-6) / Systimes->realtime; sfBaseStats->ipfrag_mbits_per_sec.realtime = ((double)(sfBase->total_ipfragmented_bytes<<3) * 1.0e-6) / Systimes->realtime; sfBaseStats->ipreass_mbits_per_sec.realtime = ((double)(sfBase->total_ipreassembled_bytes<<3) * 1.0e-6) / Systimes->realtime; sfBaseStats->mpls_mbits_per_sec.realtime = ((double)(sfBase->total_mpls_bytes<<3) * 1.0e-6) / Systimes->realtime; } static void GetCPUTime(SFBASE *sfBase, SFBASE_STATS *sfBaseStats, SYSTIMES *Systimes) { #ifndef LINUX_SMP unsigned char needToNormalize = 0; sfBaseStats->user_cpu_time = (Systimes->usertime / Systimes->realtime) * 100; sfBaseStats->system_cpu_time = (Systimes->systemtime / Systimes->realtime) * 100; sfBaseStats->idle_cpu_time = ((Systimes->realtime - Systimes->totaltime) / Systimes->realtime) * 100; /* percentages can be < 0 because of a small variance between * when the snapshot is taken of the CPU times and snapshot of * the real time. So these are just a safe-guard to normalize * the data so we see positive values. */ if (sfBaseStats->user_cpu_time < 0) { sfBaseStats->user_cpu_time = 0; needToNormalize = 1; } if (sfBaseStats->system_cpu_time < 0) { sfBaseStats->system_cpu_time = 0; needToNormalize = 1; } if (sfBaseStats->idle_cpu_time < 0) { sfBaseStats->idle_cpu_time = 0; needToNormalize = 1; } if (needToNormalize) { double totalPercent = sfBaseStats->user_cpu_time + sfBaseStats->system_cpu_time + sfBaseStats->idle_cpu_time; sfBaseStats->user_cpu_time = (sfBaseStats->user_cpu_time / totalPercent) * 100; sfBaseStats->system_cpu_time = ( sfBaseStats->system_cpu_time / totalPercent) * 100; sfBaseStats->idle_cpu_time = ( sfBaseStats->idle_cpu_time / totalPercent) * 100; } #endif } /* ** NAME ** CalculateBasePerfStats ** ** DESCRIPTION ** This is the main function that calculates the stats. Stats ** that we caculate are: ** *uSecs per Packet ** *Packets per Second ** *Mbits per Second ** *Average bytes per Packet ** *CPU Time ** *Dropped Packets ** These statistics are processed and then stored in the ** SFBASE_STATS structure. This allows output functions to ** be easily formed and inserted. ** NOTE: We can break up these statistics into functions for easier ** reading. ** ** FORMAL INPUTS ** SFBASE * - ptr to performance struct ** SFBASE_STATS * - ptr to struct to fill in performance stats ** int - do max stats ** ** FORMAL OUTPUTS ** int - 0 is successful */ static int CalculateBasePerfStats(SFBASE *sfBase, SFBASE_STATS *sfBaseStats, int max_stats) { SYSTIMES Systimes; time_t clock; #ifdef LINUX_SMP /* ** We also give sfBaseStats access to the CPU usage ** contained in sfProcPidStats. This way we don't need ** to complicate sfBaseStats further. */ sfBaseStats->sfProcPidStats = &(sfBase->sfProcPidStats); #endif if(GetProcessingTime(&Systimes, sfBase)) return -1; sfBaseStats->total_blocked_packets = sfBase->total_blocked_packets; sfBaseStats->total_injected_packets = sfBase->total_injected_packets; sfBaseStats->total_mpls_packets = sfBase->total_mpls_packets; sfBaseStats->total_mpls_bytes = sfBase->total_mpls_bytes; sfBaseStats->total_blocked_mpls_packets = sfBase->total_blocked_mpls_packets; sfBaseStats->total_blocked_mpls_bytes = sfBase->total_blocked_mpls_bytes; sfBaseStats->total_tcp_filtered_packets = sfBase->total_tcp_filtered_packets; sfBaseStats->total_udp_filtered_packets = sfBase->total_udp_filtered_packets; #ifdef NORMALIZER { int iCtr; for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ ) { sfBaseStats->pegs[iCtr][NORM_MODE_ON] = sfBase->iPegs[iCtr][NORM_MODE_ON]; sfBaseStats->pegs[iCtr][NORM_MODE_WOULDA] = sfBase->iPegs[iCtr][NORM_MODE_WOULDA]; } } #endif /* ** Avg. bytes per Packet */ if (sfBase->total_packets > 0) sfBaseStats->avg_bytes_per_packet = zeroFpException( (int)((double)(sfBase->total_bytes) / (double)(sfBase->total_packets))); else sfBaseStats->avg_bytes_per_packet = 0; if (sfBase->total_wire_packets > 0) sfBaseStats->avg_bytes_per_wire_packet = zeroFpException( (int)((double)(sfBase->total_wire_bytes) / (double)(sfBase->total_wire_packets))); else sfBaseStats->avg_bytes_per_wire_packet = 0; if (sfBase->total_ipfragmented_packets > 0) sfBaseStats->avg_bytes_per_ipfrag_packet = zeroFpException( (int)((double)(sfBase->total_ipfragmented_bytes) / (double)(sfBase->total_ipfragmented_packets))); else sfBaseStats->avg_bytes_per_ipfrag_packet = 0; if (sfBase->total_ipreassembled_packets > 0) sfBaseStats->avg_bytes_per_ipreass_packet = zeroFpException( (int)((double)(sfBase->total_ipreassembled_bytes) / (double)(sfBase->total_ipreassembled_packets))); else sfBaseStats->avg_bytes_per_ipreass_packet = 0; if (sfBase->total_rebuilt_packets > 0) sfBaseStats->avg_bytes_per_rebuilt_packet = zeroFpException( (int)((double)(sfBase->total_rebuilt_bytes) / (double)(sfBase->total_rebuilt_packets))); else sfBaseStats->avg_bytes_per_rebuilt_packet = 0; if (sfBase->total_mpls_packets > 0) sfBaseStats->avg_bytes_per_mpls_packet = zeroFpException( (int)((double)(sfBase->total_mpls_bytes) / (double)(sfBase->total_mpls_packets))); else sfBaseStats->avg_bytes_per_mpls_packet = 0; /* ** CPU time */ GetCPUTime(sfBase, sfBaseStats, &Systimes); /* ** Get Dropped Packets */ GetPktDropStats(sfBase, sfBaseStats); /* ** Total packets */ sfBaseStats->total_packets = sfBase->total_wire_packets; /* * Pattern Matching Performance in Real and User time */ sfBaseStats->patmatch_percent = zeroFpException(100.0 * mpseGetPatByteCount() / sfBase->total_wire_bytes); mpseResetByteCount(); if (max_stats) { /* ** uSeconds per Packet ** user, system, total time */ GetuSecondsPerPacket(sfBase, sfBaseStats, &Systimes); } /* ** Mbits per sec ** user, system, total time */ GetMbitsPerSecond(sfBase, sfBaseStats, &Systimes, max_stats); /* ** EventsPerSecond ** We get the information from the global variable ** PacketCount. */ GetEventsPerSecond(sfBase, sfBaseStats, &Systimes); /* ** Packets per seconds ** user, system, total time */ GetPacketsPerSecond(sfBase, sfBaseStats, &Systimes, max_stats); /* ** Attribute Table counters ** */ sfBaseStats->current_attribute_hosts = sfBase->iAttributeHosts; sfBaseStats->attribute_table_reloads = sfBase->iAttributeReloads; sfBaseStats->frag3_mem_in_use = sfBase->frag3_mem_in_use; sfBaseStats->stream5_mem_in_use = sfBase->stream5_mem_in_use; /* ** Set the date string for print out */ if (sfBase->time) { clock = sfBase->time; } else { time(&clock); } sfBaseStats->time = clock; return 0; } /* ** NAME ** GetPktDropStats ** ** DESCRIPTION ** Gets the packet drop statisitics from DAQ. ** ** FORMAL INPUT ** SFBASE * - ptr to struct ** SFBASE_STATS * - ptr to struct to fill in with perf stats ** ** FORMAL OUTPUT ** void return */ static void GetPktDropStats(SFBASE *sfBase, SFBASE_STATS *sfBaseStats) { uint64_t recv, drop, sum; if (ScReadMode()) { recv = pc.total_from_daq; drop = 0; } else { const DAQ_Stats_t* ps = DAQ_GetStats(); recv = ps->hw_packets_received; drop = ps->hw_packets_dropped; if (perfmon_config->base_reset) { if (recv < sfBase->pkt_stats.pkts_recv) sfBase->pkt_stats.pkts_recv = 0; if (drop < sfBase->pkt_stats.pkts_drop) sfBase->pkt_stats.pkts_drop = 0; } } if (perfmon_config->base_reset) { sfBaseStats->pkt_stats.pkts_recv = recv - sfBase->pkt_stats.pkts_recv; sfBaseStats->pkt_stats.pkts_drop = drop - sfBase->pkt_stats.pkts_drop; } else { sfBaseStats->pkt_stats.pkts_recv = recv; sfBaseStats->pkt_stats.pkts_drop = drop; } sum = sfBaseStats->pkt_stats.pkts_recv + sfBaseStats->pkt_stats.pkts_drop; if ( !sum ) sfBaseStats->pkt_drop_percent = 0.0; else sfBaseStats->pkt_drop_percent = zeroFpException( ((double)sfBaseStats->pkt_stats.pkts_drop / (double)sum) * 100.0); /* ** Reset sfBase stats for next go round. */ sfBase->pkt_stats.pkts_recv = recv; sfBase->pkt_stats.pkts_drop = drop; } /* * * Log Base Per Stats to File * * unixtime(in secs since epoch) * %pkts dropped * mbits/sec (wire) * alerts/sec * K-Packets/Sec (wire) * Avg Bytes/Pkt (wire) * %bytes pattern matched * syns/sec * synacks/sec * new-sessions/sec (tcp stream cache) * del-sessions/sec (tcp stream cache) * total-sessions open (tcp stream cache) * max-sessions, lifetime (tcp stream cache) * streamflushes/sec * streamfaults/sec * streamtimeouts * fragcreates/sec * fragcompletes/sec * fraginserts/sec * fragdeletes/sec * fragflushes/sec * current-frags open (frag cache) * max-frags (frag cache) * fragtimeouts * fragfaults * num cpus (following triple is repeated for each CPU) * %user-cpu usage * %sys-cpu usage * %idle-cpu usage * mbits/sec (wire) * mbits/sec (ip fragmented) * mbits/sec (ip reassembled) * mbits/sec (tcp stream rebuilt) * mbits/sec (app layer) * Avg Bytes/Pkt (wire) * Avg Bytes/Pkt (ip fragmented) * Avg Bytes/Pkt (ip reassembled) * Avg Bytes/Pkt (tcp stream rebuilt) * Avg Bytes/Pkt (app layer) * K-Packets/Sec (wire) * K-Packets/Sec (ip fragmented) * K-Packets/Sec (ip reassembled) * K-Packets/Sec (tcp stream rebuilt) * K-Packets/Sec (app layer) * Pkts recieved * Pkts dropped * Blocked-KPackets (wire) * udp-sessions * max-udp-sessions * del-udp-sessions/sec (udp stream cache) * new-udp-sessions/sec (udp stream cache) * max-sessions, interval (tcp stream cache) * curr-tcp-sessions-initializing (tcp stream cache, of total-sessions open) * curr-tcp-sessions-established (tcp stream cache, of total-sessions open) * curr-tcp-sessions-closing (tcp stream cache, of total-sessions open) * tcp-sessions-mistream/sec (tcp stream cache, of new-sessions/sec) * tcp-sessions-closed/sec (tcp stream cache, of del-sessions/sec) * tcp-sessions-timedout/sec (tcp stream cache, of del-sessions/sec) * tcp-sessions-pruned/sec (tcp stream cache, of del-sessions/sec) * tcp-sessions-dropped_async/sec (tcp stream cache, of del-sessions/sec) * hosts in attribute table * attribute table reloads * */ // IMPORTANT - whatever changes you make here, please be sure // they are reflected in the LogBasePerfHeader() below! static void LogBasePerfStats(SFBASE_STATS *sfBaseStats, FILE * fh ) { double sys=0.0,usr=0.0,idle=0.0; int iCtr = 0; long start, size = 0; size_t wrote; // Oversized buffer; For perspective, the column header is only 1905 + 1 ('\n') characters long. static char buff[4096]; if (fh == NULL) return; if ( (start = ftell(fh)) < 0 ) return; memset(buff, 0, sizeof(buff)); size = SafeSnprintf(buff, sizeof(buff), "%lu,%.3f,%.3f,%.3f,%.3f,%d,%.3f,", (unsigned long)sfBaseStats->time, sfBaseStats->pkt_drop_percent, sfBaseStats->wire_mbits_per_sec.realtime, sfBaseStats->alerts_per_second, sfBaseStats->kpackets_wire_per_sec.realtime, sfBaseStats->avg_bytes_per_wire_packet, sfBaseStats->patmatch_percent); /* Session estimation statistics */ size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f,%.3f,%.3f,%.3f," CSVu64 CSVu64, sfBaseStats->syns_per_second, sfBaseStats->synacks_per_second, sfBaseStats->new_sessions_per_second, sfBaseStats->deleted_sessions_per_second, sfBaseStats->total_sessions, sfBaseStats->max_sessions); size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f," CSVu64 CSVu64, sfBaseStats->stream_flushes_per_second, sfBaseStats->stream_faults, sfBaseStats->stream_timeouts); size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f,%.3f,%.3f,%.3f,%.3f,%.3f," CSVu64 CSVu64 CSVu64 CSVu64, sfBaseStats->frag_creates_per_second, sfBaseStats->frag_completes_per_second, sfBaseStats->frag_inserts_per_second, sfBaseStats->frag_deletes_per_second, sfBaseStats->frag_autofrees_per_second, sfBaseStats->frag_flushes_per_second, sfBaseStats->current_frags, sfBaseStats->max_frags, sfBaseStats->frag_timeouts, sfBaseStats->frag_faults); /* CPU STATS - at the end of output record */ #ifdef LINUX_SMP /* First the number of CPUs */ size += SafeSnprintf(buff + size, sizeof(buff) - size, "%d,", sfBaseStats->sfProcPidStats->iCPUs); /* Next, stats for each CPU (a triple) */ for(iCtr = 0; iCtr < sfBaseStats->sfProcPidStats->iCPUs; iCtr++) { usr= sfBaseStats->sfProcPidStats->SysCPUs[iCtr].user; sys= sfBaseStats->sfProcPidStats->SysCPUs[iCtr].sys; idle= sfBaseStats->sfProcPidStats->SysCPUs[iCtr].idle; size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f,%.3f,%.3f,",usr,sys,idle); } #else usr=sfBaseStats->user_cpu_time; sys=sfBaseStats->system_cpu_time; idle=sfBaseStats->idle_cpu_time; /* 1 CPU hardcoded */ size += SafeSnprintf(buff + size, sizeof(buff) - size, "1,%.3f,%.3f,%.3f,",usr,sys,idle); #endif /* Status for MBits/s, Bytes/Pkt, KPkts/s for each of * wire, IP Fragmented, IP Reassembled, Stream Reassembled, * App Layer (data that reaches protocol decoders). */ size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f,%.3f,%.3f,%.3f,%.3f,", sfBaseStats->wire_mbits_per_sec.realtime, sfBaseStats->ipfrag_mbits_per_sec.realtime, sfBaseStats->ipreass_mbits_per_sec.realtime, sfBaseStats->rebuilt_mbits_per_sec.realtime, sfBaseStats->mbits_per_sec.realtime); size += SafeSnprintf(buff + size, sizeof(buff) - size, "%d,%d,%d,%d,%d,", sfBaseStats->avg_bytes_per_wire_packet, sfBaseStats->avg_bytes_per_ipfrag_packet, sfBaseStats->avg_bytes_per_ipreass_packet, sfBaseStats->avg_bytes_per_rebuilt_packet, sfBaseStats->avg_bytes_per_packet); size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f,%.3f,%.3f,%.3f,%.3f,", sfBaseStats->kpackets_wire_per_sec.realtime, sfBaseStats->kpackets_ipfrag_per_sec.realtime, sfBaseStats->kpackets_ipreass_per_sec.realtime, sfBaseStats->kpackets_rebuilt_per_sec.realtime, sfBaseStats->kpackets_per_sec.realtime); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64,sfBaseStats->pkt_stats.pkts_recv); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64, sfBaseStats->pkt_stats.pkts_drop); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64, sfBaseStats->total_blocked_packets); size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f,%.3f," CSVu64 CSVu64, sfBaseStats->new_udp_sessions_per_second, sfBaseStats->deleted_udp_sessions_per_second, sfBaseStats->total_udp_sessions, sfBaseStats->max_udp_sessions); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64 CSVu64 CSVu64 CSVu64 "%.3f,%.3f,%.3f,%.3f,%.3f,", sfBaseStats->max_tcp_sessions_interval, sfBaseStats->curr_tcp_sessions_initializing, sfBaseStats->curr_tcp_sessions_established, sfBaseStats->curr_tcp_sessions_closing, sfBaseStats->tcp_sessions_midstream_per_second, sfBaseStats->tcp_sessions_closed_per_second, sfBaseStats->tcp_sessions_timedout_per_second, sfBaseStats->tcp_sessions_pruned_per_second, sfBaseStats->tcp_sessions_dropped_async_per_second); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64 CSVu64, sfBaseStats->current_attribute_hosts, sfBaseStats->attribute_table_reloads); size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f,%d,%.3f,", sfBaseStats->mpls_mbits_per_sec.realtime, sfBaseStats->avg_bytes_per_mpls_packet, sfBaseStats->kpackets_per_sec_mpls.realtime); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64 CSVu64, sfBaseStats->total_tcp_filtered_packets, sfBaseStats->total_udp_filtered_packets); #ifdef NORMALIZER size += SafeSnprintf(buff + size, sizeof(buff) - size, "%d,", PERF_COUNT_MAX); for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ ) size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64, sfBaseStats->pegs[iCtr][NORM_MODE_ON]); for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ ) size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64, sfBaseStats->pegs[iCtr][NORM_MODE_WOULDA]); #endif size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64, sfBaseStats->total_injected_packets); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64, sfBaseStats->frag3_mem_in_use); size += SafeSnprintf(buff + size, sizeof(buff) - size, CSVu64, sfBaseStats->stream5_mem_in_use); size += SafeSnprintf(buff + size, sizeof(buff) - size, "%.3f", sfBaseStats->total_alerts_per_second); size += SafeSnprintf(buff + size, sizeof(buff) - size, "\n"); // Write to file. On error, reset the file position and inform the user. wrote = fwrite(buff, size, 1, fh); if (wrote != 1) { WarningMessage("%s: Failed to write stats\n", __FUNCTION__); // fseek to adjust offset; ftruncate doesn't do that for us. if (fseek(fh, start, SEEK_SET)) { WarningMessage("%s: Failed to adjust offset\n", __FUNCTION__); } ftruncate(fileno(fh), start); } fflush(fh); } #ifdef NORMALIZER static const char* iNames[PERF_COUNT_MAX] = { "ip4::trim", "ip4::tos", "ip4::df", "ip4::rf", "ip4::ttl", "ip4::opts", "icmp4::echo", "ip6::ttl", "ip6::opts", "icmp6::echo", "tcp::syn_opt", "tcp::opt", "tcp::pad", "tcp::rsv", "tcp::ns", "tcp::urp", "tcp::ecn_pkt", "tcp::ecn_ssn", "tcp::ts_ecr", "tcp::ts_nop", "tcp::ips_data", "tcp::block", "tcp::req_urg", "tcp::req_pay", "tcp::req_urp", "tcp::trim_syn", "tcp::trim_rst", "tcp::trim_win", "tcp::trim_mss", }; #endif // IMPORTANT - whatever changes you make here, please be sure // they correspond to the LogBasePerfStats() above! void LogBasePerfHeader (FILE* fh) { int iCtr, iCPUs; if( !fh ) return; fprintf(fh, "#%s,%s,%s,%s,%s,%s,%s", "time", "pkt_drop_percent", "wire_mbits_per_sec.realtime", "alerts_per_second", "kpackets_wire_per_sec.realtime", "avg_bytes_per_wire_packet", "patmatch_percent"); /* Session estimation statistics */ fprintf(fh, ",%s,%s,%s,%s,%s,%s", "syns_per_second", "synacks_per_second", "new_sessions_per_second", "deleted_sessions_per_second", "total_sessions", "max_sessions"); fprintf(fh, ",%s,%s,%s", "stream_flushes_per_second", "stream_faults", "stream_timeouts"); fprintf(fh, ",%s,%s,%s,%s,%s,%s,%s,%s,%s,%s", "frag_creates_per_second", "frag_completes_per_second", "frag_inserts_per_second", "frag_deletes_per_second", "frag_autofrees_per_second", "frag_flushes_per_second", "current_frags", "max_frags", "frag_timeouts", "frag_faults"); /* First the number of CPUs */ fprintf(fh, ",%s", "iCPUs"); #ifdef LINUX_SMP iCPUs = sfBase.sfProcPidStats.iCPUs; #else iCPUs = 1; #endif /* Next, stats for each CPU (a triple) */ for ( iCtr = 0; iCtr < iCPUs; iCtr++ ) { fprintf(fh, ",%s[%d],%s[%d],%s[%d]", "usr",iCtr,"sys",iCtr,"idle",iCtr); } /* Status for MBits/s, Bytes/Pkt, KPkts/s for each of * wire, IP Fragmented, IP Reassembled, Stream Reassembled, * App Layer (data that reaches protocol decoders). */ fprintf(fh, ",%s,%s,%s,%s,%s", "wire_mbits_per_sec.realtime", "ipfrag_mbits_per_sec.realtime", "ipreass_mbits_per_sec.realtime", "rebuilt_mbits_per_sec.realtime", "mbits_per_sec.realtime"); fprintf(fh, ",%s,%s,%s,%s,%s", "avg_bytes_per_wire_packet", "avg_bytes_per_ipfrag_packet", "avg_bytes_per_ipreass_packet", "avg_bytes_per_rebuilt_packet", "avg_bytes_per_packet"); fprintf(fh, ",%s,%s,%s,%s,%s", "kpackets_wire_per_sec.realtime", "kpackets_ipfrag_per_sec.realtime", "kpackets_ipreass_per_sec.realtime", "kpackets_rebuilt_per_sec.realtime", "kpackets_per_sec.realtime"); fprintf(fh, ",%s,%s,%s", "pkt_stats.pkts_recv", "pkt_stats.pkts_drop", "total_blocked_verdicts"); fprintf(fh, ",%s,%s,%s,%s", "new_udp_sessions_per_second", "deleted_udp_sessions_per_second", "total_udp_sessions", "max_udp_sessions"); fprintf(fh, ",%s,%s,%s,%s,%s,%s,%s,%s,%s", "max_tcp_sessions_interval", "curr_tcp_sessions_initializing", "curr_tcp_sessions_established", "curr_tcp_sessions_closing", "tcp_sessions_midstream_per_second", "tcp_sessions_closed_per_second", "tcp_sessions_timedout_per_second", "tcp_sessions_pruned_per_second", "tcp_sessions_dropped_async_per_second"); fprintf(fh, ",%s,%s", "current_attribute_hosts", "attribute_table_reloads"); fprintf(fh, ",%s,%s,%s", "mpls_mbits_per_sec.realtime", "avg_bytes_per_mpls_packet", "kpackets_per_sec_mpls.realtime"); fprintf(fh, ",%s,%s", "total_tcp_filtered_packets", "total_udp_filtered_packets"); #ifdef NORMALIZER fprintf(fh, ",num_normalizations"); for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ ) fprintf(fh, ",%s", iNames[iCtr]); for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ ) fprintf(fh, ",would_%s", iNames[iCtr]); #endif fprintf(fh, ",%s,%s,%s", "total_injected_packets", "frag3_mem_in_use", "stream5_mem_in_use"); fprintf(fh, ",%s", "total_alerts_per_second"); fprintf(fh,"\n"); fflush(fh); } /* ** NAME ** DisplayBasePerfStats ** ** DESCRIPTION ** Output Function. We can easily code multiple output buffers ** because all that is received is a SFBASE_STATS struct which ** holds all the information to output. This current output ** function just prints to stdout. ** ** FORMAL INPUTS ** SFBASE_STATS * - struct with perf information ** int - flags for output ** ** FORMAL OUTPUTS ** void return */ static void DisplayBasePerfStatsConsole(SFBASE_STATS *sfBaseStats, int max_stats) { int iCtr = 0; LogMessage("\n"); LogMessage("\n"); LogMessage("Snort Realtime Performance : %s--------------------------\n", ctime(&sfBaseStats->time)); LogMessage("Pkts Recv: " STDu64 "\n", sfBaseStats->pkt_stats.pkts_recv); LogMessage("Pkts Drop: " STDu64 "\n", sfBaseStats->pkt_stats.pkts_drop); LogMessage("%% Dropped: %.3f%%\n", sfBaseStats->pkt_drop_percent); LogMessage("Block Verdict: " STDu64 "\n", sfBaseStats->total_blocked_packets); LogMessage("Injected: " STDu64 "\n", sfBaseStats->total_injected_packets); LogMessage("Pkts Filtered TCP: " STDu64 "\n", sfBaseStats->total_tcp_filtered_packets); LogMessage("Pkts Filtered UDP: " STDu64 "\n\n", sfBaseStats->total_udp_filtered_packets); LogMessage("Mbits/Sec: %.3f (wire)\n", sfBaseStats->wire_mbits_per_sec.realtime); #ifdef MPLS LogMessage("Mbits/Sec: %.3f (mpls)\n", sfBaseStats->mpls_mbits_per_sec.realtime); #endif LogMessage("Mbits/Sec: %.3f (ip fragmented)\n", sfBaseStats->ipfrag_mbits_per_sec.realtime); LogMessage("Mbits/Sec: %.3f (ip reassembled)\n", sfBaseStats->ipreass_mbits_per_sec.realtime); LogMessage("Mbits/Sec: %.3f (tcp rebuilt)\n", sfBaseStats->rebuilt_mbits_per_sec.realtime); LogMessage("Mbits/Sec: %.3f (app layer)\n\n", sfBaseStats->mbits_per_sec.realtime); LogMessage("Bytes/Pkt: %d (wire)\n", sfBaseStats->avg_bytes_per_wire_packet); #ifdef MPLS LogMessage("Bytes/Pkt: %d (mpls)\n", sfBaseStats->avg_bytes_per_mpls_packet); #endif LogMessage("Bytes/Pkt: %d (ip fragmented)\n", sfBaseStats->avg_bytes_per_ipfrag_packet); LogMessage("Bytes/Pkt: %d (ip reassembled)\n", sfBaseStats->avg_bytes_per_ipreass_packet); LogMessage("Bytes/Pkt: %d (tcp rebuilt)\n", sfBaseStats->avg_bytes_per_rebuilt_packet); LogMessage("Bytes/Pkt: %d (app layer)\n\n", sfBaseStats->avg_bytes_per_packet); LogMessage("KPkts/Sec: %.3f (wire)\n", sfBaseStats->kpackets_wire_per_sec.realtime); #ifdef MPLS LogMessage("KPkts/Sec: %.3f (mpls)\n", sfBaseStats->kpackets_per_sec_mpls.realtime); #endif LogMessage("KPkts/Sec: %.3f (ip fragmented)\n", sfBaseStats->kpackets_ipfrag_per_sec.realtime); LogMessage("KPkts/Sec: %.3f (ip reassembled)\n", sfBaseStats->kpackets_ipreass_per_sec.realtime); LogMessage("KPkts/Sec: %.3f (tcp rebuilt)\n", sfBaseStats->kpackets_rebuilt_per_sec.realtime); LogMessage("KPkts/Sec: %.3f (app layer)\n\n", sfBaseStats->kpackets_per_sec.realtime); LogMessage("PatMatch: %.3f%%\n\n", sfBaseStats->patmatch_percent); /* ** The following ifdefs are for CPU stats dealing with multiple ** CPUs in Linux. Snort will show user, system and idle time for ** each CPU. The methods of calculating this are different though, ** since getrusage is broken for multiple CPUs in Linux. We get the ** CPU stats instead from the proc filesystem on Linux. */ #ifdef LINUX_SMP for(iCtr = 0; iCtr < sfBaseStats->sfProcPidStats->iCPUs; iCtr++) { LogMessage("CPU%d Usage: %.3f%% (user) %.3f%% (sys) %.3f%% (idle)\n", iCtr, sfBaseStats->sfProcPidStats->SysCPUs[iCtr].user, sfBaseStats->sfProcPidStats->SysCPUs[iCtr].sys, sfBaseStats->sfProcPidStats->SysCPUs[iCtr].idle); } printf("\n"); #else LogMessage("CPU Usage: %.3f%% (user) %.3f%% (sys) %.3f%% (idle)\n\n", sfBaseStats->user_cpu_time, sfBaseStats->system_cpu_time, sfBaseStats->idle_cpu_time); #endif /* ** Shows the number of snort alerts per second. */ LogMessage("Alerts/Sec : %.3f\n", sfBaseStats->alerts_per_second); /* Session estimation statistics */ LogMessage("Syns/Sec : %.3f\n", sfBaseStats->syns_per_second); LogMessage("Syn-Acks/Sec : %.3f\n", sfBaseStats->synacks_per_second); LogMessage("New Cached Sessions/Sec: %.3f\n", sfBaseStats->new_sessions_per_second); LogMessage("Midstream Sessions/Sec : %.3f\n", sfBaseStats->tcp_sessions_midstream_per_second); LogMessage("Cached Sessions Del/Sec: %.3f\n", sfBaseStats->deleted_sessions_per_second); LogMessage("Closed Sessions/Sec : %.3f\n", sfBaseStats->tcp_sessions_closed_per_second); LogMessage("TimedOut Sessions/Sec : %.3f\n", sfBaseStats->tcp_sessions_timedout_per_second); LogMessage("Pruned Sessions/Sec : %.3f\n", sfBaseStats->tcp_sessions_pruned_per_second); LogMessage("Dropped Async Ssns/Sec : %.3f\n", sfBaseStats->tcp_sessions_dropped_async_per_second); LogMessage("Current Cached Sessions: " STDu64 "\n", sfBaseStats->total_sessions); LogMessage("Sessions Initializing : " STDu64 "\n", sfBaseStats->curr_tcp_sessions_initializing); LogMessage("Sessions Established : " STDu64 "\n", sfBaseStats->curr_tcp_sessions_established); LogMessage("Sessions Closing : " STDu64 "\n", sfBaseStats->curr_tcp_sessions_closing); LogMessage("Max Cached Sessions : " STDu64 "\n", sfBaseStats->max_sessions); LogMessage("Max Sessions (interval): " STDu64 "\n", sfBaseStats->max_tcp_sessions_interval); /* more instrumentation for stream4/frag2 */ LogMessage("Stream Flushes/Sec : %.3f\n", sfBaseStats->stream_flushes_per_second); LogMessage("Stream Cache Faults/Sec: " STDu64 "\n", sfBaseStats->stream_faults); LogMessage("Stream Cache Timeouts : " STDu64 "\n", sfBaseStats->stream_timeouts); LogMessage("Frag Creates()s/Sec : %.3f\n", sfBaseStats->frag_creates_per_second); LogMessage("Frag Completes()s/Sec : %.3f\n", sfBaseStats->frag_completes_per_second); LogMessage("Frag Inserts()s/Sec : %.3f\n", sfBaseStats->frag_inserts_per_second); LogMessage("Frag Deletes/Sec : %.3f\n", sfBaseStats->frag_deletes_per_second); LogMessage("Frag AutoFrees/Sec : %.3f\n", sfBaseStats->frag_autofrees_per_second); LogMessage("Frag Flushes/Sec : %.3f\n", sfBaseStats->frag_flushes_per_second); LogMessage("Current Cached Frags : " STDu64 "\n", sfBaseStats->current_frags); LogMessage("Max Cached Frags : " STDu64 "\n", sfBaseStats->max_frags); LogMessage("Frag Timeouts : " STDu64 "\n", sfBaseStats->frag_timeouts); LogMessage("Frag Faults : " STDu64 "\n\n", sfBaseStats->frag_faults); LogMessage("New Cached UDP Ssns/Sec: %.3f\n", sfBaseStats->new_udp_sessions_per_second); LogMessage("Cached UDP Ssns Del/Sec: %.3f\n", sfBaseStats->deleted_udp_sessions_per_second); LogMessage("Current Cached UDP Ssns: " STDu64 "\n", sfBaseStats->total_udp_sessions); LogMessage("Max Cached UDP Ssns : " STDu64 "\n\n", sfBaseStats->max_udp_sessions); #ifdef TARGET_BASED LogMessage("Attribute Table Hosts : " STDu64 "\n", sfBaseStats->current_attribute_hosts); LogMessage("Attribute Table Reloads: " STDu64 "\n\n", sfBaseStats->attribute_table_reloads); #endif #ifdef NORMALIZER LogMessage("Number of Normalizations : %d\n", PERF_COUNT_MAX); for ( iCtr = 0; iCtr < PERF_COUNT_MAX; iCtr++ ) { LogMessage("%-26s: " STDu64 "\n", iNames[iCtr], sfBaseStats->pegs[iCtr][NORM_MODE_ON]); LogMessage("Would %-20s: " STDu64 "\n", iNames[iCtr], sfBaseStats->pegs[iCtr][NORM_MODE_WOULDA]); } #endif LogMessage("\n"); /* ** Snort Maximum Performance Statistics ** These statistics calculate the maximum performance that ** snort could attain by using the getrusage numbers. We've ** seen in testing that these numbers come close to the actual ** throughput for Mbits/Sec and Pkt/Sec. But note that these ** are not hard numbers and rigorous testing is necessary to ** establish snort performance on any hardware setting. */ if (max_stats) { LogMessage("Snort Maximum Performance\n"); LogMessage("-------------------------\n\n"); LogMessage("Mbits/Second\n"); LogMessage("----------------\n"); LogMessage("Snort: %.3f\n",sfBaseStats->mbits_per_sec.usertime); LogMessage("Sniffing: %.3f\n",sfBaseStats->mbits_per_sec.systemtime); LogMessage("Combined: %.3f\n\n",sfBaseStats->mbits_per_sec.totaltime); LogMessage("uSeconds/Pkt\n"); LogMessage("----------------\n"); LogMessage("Snort: %.3f\n",sfBaseStats->usecs_per_packet.usertime); LogMessage("Sniffing: %.3f\n",sfBaseStats->usecs_per_packet.systemtime); LogMessage("Combined: %.3f\n\n",sfBaseStats->usecs_per_packet.totaltime); LogMessage("KPkts/Second\n"); LogMessage("------------------\n"); LogMessage("Snort: %.3f\n",sfBaseStats->kpackets_per_sec.usertime); LogMessage("Sniffing: %.3f\n",sfBaseStats->kpackets_per_sec.systemtime); LogMessage("Combined: %.3f\n\n",sfBaseStats->kpackets_per_sec.totaltime); } } snort-2.9.20/src/preprocessors/session_api.h0000644000175000017500000007316714241077072017300 0ustar apoapo/* $Id$ */ /* * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * ** AUTHOR: d mcpherson * ** * ** This program is free software; you can redistribute it and/or modify * ** it under the terms of the GNU General Public License Version 2 as * ** published by the Free Software Foundation. You may not use, modify or * ** distribute this program under any other version of the GNU General * ** Public License. * ** * ** 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. * */ /* session_api.h * * Purpose: Definition of the SessionAPI. To be used as a common interface * for other preprocessors and detection plugins that require a * session context for execution. * * Arguments: * * Effect: * * Comments: * * Any comments? * */ #ifndef _SESSION_API_H_ #define _SESSION_API_H_ #include #include "ipv6_port.h" #include "preprocids.h" /* IDs are used when setting preproc specific data */ #include "bitop.h" #include "decode.h" #include "sfPolicy.h" /* default limits */ #define STREAM_DEFAULT_PRUNE_QUANTA 30 /* seconds to timeout a session */ #define STREAM_DEFAULT_MEMCAP 8388608 /* 8MB */ #define STREAM_DEFAULT_PRUNE_LOG_MAX 1048576 /* 1MB */ #define STREAM_RIDICULOUS_HI_MEMCAP ( 1024 * 1024 * 1024 ) /* 1GB */ #define STREAM_RIDICULOUS_LOW_MEMCAP 32768 /* 32k*/ #define STREAM_RIDICULOUS_MAX_SESSIONS ( 1024 * 1024 ) /* 1 million sessions */ #define STREAM_DEFAULT_MAX_TCP_SESSIONS 262144 /* 256k TCP sessions by default */ #define STREAM_DEFAULT_MAX_UDP_SESSIONS 131072 /* 128k UDP sessions by default */ #define STREAM_DEFAULT_MAX_ICMP_SESSIONS 65536 /* 64k ICMP sessions by default */ #define STREAM_DEFAULT_MAX_IP_SESSIONS 16384 /* 16k IP sessions by default */ #define STREAM_DEFAULT_TCP_CACHE_PRUNING_TIMEOUT 30 /* 30 seconds */ #define STREAM_DEFAULT_TCP_CACHE_NOMINAL_TIMEOUT ( 60 * 60 ) /* 60 minutes */ #define STREAM_DEFAULT_UDP_CACHE_PRUNING_TIMEOUT 30 /* 30 seconds */ #define STREAM_DEFAULT_UDP_CACHE_NOMINAL_TIMEOUT ( 3 * 60 ) /* 3 minutes */ #define STREAM_MAX_CACHE_TIMEOUT ( 12 * 60 * 60 ) /* 12 hours */ #define STREAM_MIN_PRUNE_LOG_MAX 1024 /* 1k packet data stored */ #define STREAM_MAX_PRUNE_LOG_MAX STREAM_RIDICULOUS_HI_MEMCAP /* 1GB packet data stored */ #define STREAM_DELAY_SESSION_DELETION true /* set if session deletion to be delayed */ #define STREAM_DELAY_TIMEOUT_AFTER_CONNECTION_ENDED (3 * 60) /* 3 minutes */ #define STREAM_DELAY_SCB_DELETION 1 /* 1 second */ #define STREAM_EXPECTED_CHANNEL_TIMEOUT 300 #ifdef ACTIVE_RESPONSE #define STREAM_DEFAULT_MAX_ACTIVE_RESPONSES 0 /* default to no responses */ #define STREAM_DEFAULT_MIN_RESPONSE_SECONDS 1 /* wait at least 1 second between resps */ #define STREAM_MAX_ACTIVE_RESPONSES_MAX 25 /* banging your head against the wall */ #define STREAM_MIN_RESPONSE_SECONDS_MAX 300 /* we want to stop the flow soonest */ #endif #define EXPECT_FLAG_ALWAYS 0x01 #define SSN_MISSING_NONE 0x00 #define SSN_MISSING_BEFORE 0x01 #define SSN_MISSING_AFTER 0x02 #define SSN_MISSING_BOTH (SSN_MISSING_BEFORE | SSN_MISSING_AFTER) #define SSN_DIR_NONE 0x0 #define SSN_DIR_FROM_CLIENT 0x1 #define SSN_DIR_FROM_SENDER 0x1 #define SSN_DIR_TO_SERVER 0x1 #define SSN_DIR_FROM_SERVER 0x2 #define SSN_DIR_FROM_RESPONDER 0x2 #define SSN_DIR_TO_CLIENT 0x2 #define SSN_DIR_BOTH 0x3 #define SSNFLAG_SEEN_CLIENT 0x00000001 #define SSNFLAG_SEEN_SENDER 0x00000001 #define SSNFLAG_SEEN_SERVER 0x00000002 #define SSNFLAG_SEEN_RESPONDER 0x00000002 #define SSNFLAG_SEEN_BOTH (SSNFLAG_SEEN_SERVER | SSNFLAG_SEEN_CLIENT) /* used to check asymetric traffic */ #define SSNFLAG_ESTABLISHED 0x00000004 #define SSNFLAG_NMAP 0x00000008 #define SSNFLAG_ECN_CLIENT_QUERY 0x00000010 #define SSNFLAG_ECN_SERVER_REPLY 0x00000020 #define SSNFLAG_HTTP_1_1 0x00000040 /* has stream seen HTTP 1.1? */ #define SSNFLAG_SEEN_PMATCH 0x00000080 /* seen pattern match? */ #define SSNFLAG_MIDSTREAM 0x00000100 /* picked up midstream */ #define SSNFLAG_CLIENT_FIN 0x00000200 /* server sent fin */ #define SSNFLAG_SERVER_FIN 0x00000400 /* client sent fin */ #define SSNFLAG_CLIENT_PKT 0x00000800 /* packet is from the client */ #define SSNFLAG_SERVER_PKT 0x00001000 /* packet is from the server */ #define SSNFLAG_COUNTED_INITIALIZE 0x00002000 #define SSNFLAG_COUNTED_ESTABLISH 0x00004000 #define SSNFLAG_COUNTED_CLOSING 0x00008000 #define SSNFLAG_TIMEDOUT 0x00010000 #define SSNFLAG_PRUNED 0x00020000 #define SSNFLAG_RESET 0x00040000 #define SSNFLAG_DROP_CLIENT 0x00080000 #define SSNFLAG_DROP_SERVER 0x00100000 #define SSNFLAG_LOGGED_QUEUE_FULL 0x00200000 #define SSNFLAG_STREAM_ORDER_BAD 0x00400000 #define SSNFLAG_FORCE_BLOCK 0x00800000 #define SSNFLAG_CLIENT_SWAP 0x01000000 #define SSNFLAG_CLIENT_SWAPPED 0x02000000 #define SSNFLAG_DETECTION_DISABLED 0x04000000 #define SSNFLAG_HTTP_2 0x08000000 #define SSNFLAG_HTTP_2_UPG 0x10000000 #define SSNFLAG_FREE_APP_DATA 0x20000000 #define SSNFLAG_ALL 0xFFFFFFFF /* all that and a bag of chips */ #define SSNFLAG_NONE 0x00000000 /* nothing, an MT bag of chips */ // HA Session flags helper macros #define HA_IGNORED_SESSION_FLAGS ( SSNFLAG_COUNTED_INITIALIZE | SSNFLAG_COUNTED_ESTABLISH | \ SSNFLAG_COUNTED_CLOSING | SSNFLAG_LOGGED_QUEUE_FULL) #define HA_TCP_MAJOR_SESSION_FLAGS ( SSNFLAG_ESTABLISHED ) #define UNKNOWN_PORT 0 #define TCP_HZ 100 #define SESSION_API_VERSION1 1 /* NOTE: The XFF_BUILTING_NAMES value must match the code in snort_httpinspect.c that adds the builtin names to the list. */ #define HTTP_XFF_FIELD_X_FORWARDED_FOR "X-Forwarded-For" #define HTTP_XFF_FIELD_TRUE_CLIENT_IP "True-Client-IP" #define HTTP_XFF_BUILTIN_NAMES (2) #define HTTP_MAX_XFF_FIELDS 8 typedef struct _StreamSessionKey { /* XXX If this data structure changes size, HashKeyCmp must be updated! */ uint32_t ip_l[4]; /* Low IP */ uint32_t ip_h[4]; /* High IP */ uint16_t port_l; /* Low Port - 0 if ICMP */ uint16_t port_h; /* High Port - 0 if ICMP */ uint16_t vlan_tag; uint8_t protocol; char pad; uint32_t mplsLabel; /* MPLS label */ #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t addressSpaceId_l; /* Low ASID */ uint16_t addressSpaceId_h; /* Higher ASID */ #else uint16_t addressSpaceId; uint16_t addressSpaceIdPad1; #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t carrierId; #endif /* XXX If this data structure changes size, HashKeyCmp must be updated! */ } StreamSessionKey; typedef StreamSessionKey SessionKey; typedef void ( *StreamAppDataFree )( void * ); typedef struct _StreamAppData { uint32_t protocol; void *dataPointer; struct _StreamAppData *next; struct _StreamAppData *prev; StreamAppDataFree freeFunc; } StreamAppData; typedef struct _StreamFlowData { BITOP boFlowbits; unsigned char flowb[1]; } StreamFlowData; typedef struct _StreamSessionLimits { uint32_t tcp_session_limit; uint32_t udp_session_limit; uint32_t icmp_session_limit; uint32_t ip_session_limit; } StreamSessionLimits; typedef struct _StreamHAState { uint32_t session_flags; #ifdef TARGET_BASED int16_t ipprotocol; int16_t application_protocol; #endif char direction; char ignore_direction; /* flag to ignore traffic on this session */ } StreamHAState; typedef enum { SE_REXMIT, SE_EOF, SE_MAX } Stream_Event; //typedef void (*LogExtraData)(void *ssnptr, void *config, LogFunction *funcs, uint32_t max_count, // uint32_t xtradata_mask, uint32_t id, uint32_t sec); #ifdef ENABLE_HA typedef uint32_t ( *StreamHAProducerFunc )( void *ssnptr, uint8_t *buf ); typedef int ( *StreamHAConsumerFunc )( void *ssnptr, const uint8_t *data, uint8_t length ); #endif extern uint32_t HA_CRITICAL_SESSION_FLAGS; // Protocol types for creating session cache #define SESSION_PROTO_TCP 0x00 #define SESSION_PROTO_UDP 0x01 #define SESSION_PROTO_ICMP 0x02 #define SESSION_PROTO_IP 0x03 #define SESSION_PROTO_MAX 0x04 // Snort Policy Types #define SNORT_NAP_POLICY 0x00 #define SNORT_IPS_POLICY 0x01 struct _SnortConfig; struct _ExpectNode; typedef void( *SessionCleanup )( void *ssn ); typedef void ( *nap_selector )( Packet *p, bool client_packet ); typedef void (*MandatoryEarlySessionCreatorFn)(void *ssn, struct _ExpectNode*); typedef char** (*GetHttpXffPrecedenceFunc)(void* ssn, uint32_t flags, int* nFields); struct _SessionCache; typedef struct _session_api { int version; /* Create a protocol specific cache for session control blocks * * Parameters: * Session procotol type * Protocol Session Control Block Size * Cleanup callback function */ struct _SessionCache* (*init_session_cache)(uint32_t, uint32_t, SessionCleanup); /* Lookup and return pointer to Session Control Block * * Parameters * Session Cache * Packet * Session Key */ void *(*get_session)(struct _SessionCache*, Packet *, SessionKey *); /* Populate a session key from the Packet * * Parameters * Packet * Stream session key pointer */ void (*populate_session_key)(Packet *, StreamSessionKey *); /* Lookup session by IP and Port from packet and return pointer to Session Control Block * * Parameters * Source IP * Source Port * Destination IP * Destination Port * Protocol * VLAN * MPLS ID * Address Space ID * Session Key */ int (*get_session_key_by_ip_port)(sfaddr_t*, uint16_t, sfaddr_t*, uint16_t, char, uint16_t, uint32_t, #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t, uint16_t, #else uint16_t, #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t, #endif SessionKey *); /* Lookup by session key and return Session Control Block * * Parameters * Session Cache (protocol specific) * Session Key * */ void *(*get_session_by_key)(struct _SessionCache*, const SessionKey *); /* Lookup by session key and return Session Control Block - relys on the SessionKey to determine which cache * * Parameters * Session Key * */ void *(*get_session_handle)(const SessionKey *); /* Create a new session * * Parameters * Session Cache (protocol specific) * Packet * Session Key * */ void *(*create_session)(struct _SessionCache*, Packet *, const SessionKey *); /* Is session verified by protocol * * Parameters * Session Control Block */ bool (*is_session_verified)( void * ); /* remove session from oneway list * * Parameters * protocol * Session Control Block */ void (*remove_session_from_oneway_list)( uint32_t, void * ); /* Delete a session * * Parameters * Session cache (protocol specific) * Session Control Block * Reason * Delete sycnhronous */ int (*delete_session)(struct _SessionCache*, void *, char *, bool); /* Delete a session but without providing the session cache. * * Parameters * Session Control Block * Reason */ int (*delete_session_by_key)(void *, char *); /* Print session cache * * Parameters * Session cache (protocol specific) * */ void (*print_session_cache)(struct _SessionCache*); /* Delete session cache * * Parameters * protocol * */ int (*delete_session_cache)( uint32_t protocol ); /* Purge session cache * * Parameters * Session cache (protocol specific) * */ int (*purge_session_cache)(struct _SessionCache*); /* Prune session cache * * Parameters * Session cache (protocol specific) * Time * Session Control Block * Mem Check * */ int (*prune_session_cache)(struct _SessionCache*, uint32_t, void *, int); /* Clean memory pool for protocol sessions by protocol * * Parameters * protocol * */ void (*clean_protocol_session_pool)( uint32_t ); /* Free protocol session memory by protocol * * Parameters * protocol * Session Pointer */ void (*free_protocol_session_pool)( uint32_t, void * ); /* Allocate session from protocol session pool * * Parameters * protocol */ void *(*alloc_protocol_session)( uint32_t ); /* Get session count * * Parameters * Session cache (protocol specific) * */ int (*get_session_count)(struct _SessionCache*); /* Get prune count by protocol * * Parameters * protocol */ uint32_t (*get_session_prune_count)( uint32_t protocol ); /* Reset prune count by protocol * * Parameters * protocol */ void (*reset_session_prune_count)( uint32_t protocol ); /* Check session timeout * * Parameters * Flow count * Current time */ void (*check_session_timeout)( uint32_t, time_t ); /* Return status of protocol tracking for specified protocol * * Parameters * proto * */ int (*protocol_tracking_enabled)( IpProto proto ); /* Set packet direction flag * * Parameters * Packet * Session Control Block * */ void (*set_packet_direction_flag)(Packet *, void *); /* Free session application data * * Parameters * Session Control Block * */ void (*free_application_data)(void *); /* Get direction of packet * * Parameters: * Packet */ uint32_t (*get_packet_direction)(Packet *); /* Disable inspection for a sesion. * * Parameters * Session Ptr * Packet */ void (*disable_inspection)(void *, Packet *); /* Stop inspection for session, up to count bytes (-1 to ignore * for life or until resume). * * If response flag is set, automatically resume inspection up to * count bytes when a data packet in the other direction is seen. * * Also marks the packet to be ignored * * Parameters * Session Ptr * Packet * Direction * Bytes * Response Flag */ void (*stop_inspection)(void *, Packet *, char, int32_t, int); /* Turn off inspection for potential session. * Adds session identifiers to a hash table. * TCP only. * * Parameters * IP addr #1 * Port #1 * IP addr #2 * Port #2 * Protocol * Preprocessor ID * Direction * Flags (permanent) * * Returns * 0 on success * -1 on failure */ int (*ignore_session)(const Packet *, sfaddr_t*, uint16_t, sfaddr_t*, uint16_t, uint8_t, uint32_t, char, char, struct _ExpectNode**); /* Get direction that data is being ignored. * * Parameters * Session Ptr */ int (*get_ignore_direction)(void *); /* Resume inspection for session. * * Parameters * Session Ptr * Direction */ void (*resume_inspection)(void *, char); /* Drop traffic arriving on session. * * Parameters * Session Ptr * Direction */ void (*drop_traffic)(Packet *, void *, char); /* Set a reference to application data for a session * * Parameters * Session Ptr * Application Protocol * Application Data reference (pointer) * Application Data free function * * Returns * 0 on success * -1 on failure */ int (*set_application_data)(void *, uint32_t, void *, StreamAppDataFree); /* Set a reference to application data for a session * * Parameters * Session Ptr * Application Protocol * * Returns * Application Data reference (pointer) */ void *(*get_application_data)(void *, uint32_t); /* * Set Expiration Timeout * * Parameters * Packet * Session Ptr * timeout */ void (*set_expire_timer)( Packet *, void *, uint32_t ); /* Get Expriration Timeou * * Parameters * Packet * Session Ptr * */ int (*get_expire_timer)( Packet *, void *); /* Sets the flags for a session * This ORs the supplied flags with the previous values * * Parameters * Session Ptr * Flags * * Returns * New Flags */ uint32_t (*set_session_flags)(void *, uint32_t); /* Gets the flags for a session * * Parameters * Session Ptr */ uint32_t (*get_session_flags)(void *); /* Get the runtime policy index for policy type * specified * * Parameters * Session Ptr * Policy Type: NAP or IPS */ tSfPolicyId (*get_runtime_policy)(void *, int); /* Set the runtime policy index for policy type * specified * * Parameters * Session Ptr * Policy Type: NAP or IPS * Index for this policy */ void (*set_runtime_policy)(void *, int, tSfPolicyId); /* Get Flowbits data * * Parameters * Packet * * Returns * Ptr to Flowbits Data */ StreamFlowData *(*get_flow_data)(Packet *p); /* Set if Session Deletion to be delayed * * Parameters * Session Ptr * bool to set/unset delay_session_deletion_flag * */ void (*set_session_deletion_delayed)(void *, bool); /* Returns if SessionDeletion to be delayed or not * * Parameters * Session Ptr * * Returns * bool value denoting if sessionDeletion Delayed or not * */ bool (*is_session_deletion_delayed)(void *); #ifdef TARGET_BASED /* Register preproc handler for the specifed application id * * Parameters * Preprocessor Id * Application ID */ void (*register_service_handler)(uint32_t, int16_t); /* Get the protocol identifier from a stream * * Parameters * Session Ptr * * Returns * integer protocol identifier */ int16_t (*get_application_protocol_id)(void *); /* Set the protocol identifier for a stream * * Parameters * Session Ptr * ID * * Returns * integer protocol identifier */ int16_t (*set_application_protocol_id)(void *, int16_t); /* Get server IP address. This could be used either during packet processing or when * a session is being closed. Caller should make a deep copy if return value is needed * for later use. * * Arguments * void * - session pointer * uint32_t - direction. Valid values are SSN_DIR_SERVER or SSN_DIR_CLIENT * * Returns * IP address. Contents at the buffer should not be changed. The */ sfaddr_t* (*get_session_ip_address)(void *, uint32_t); /* Get server/client ports. * * Arguments * void * - session pointer * uint16_t *client_port - client port pointer * uint16_t *server_port - server port pointer * * Returns * Ports. */ void (*get_session_ports)(void *, uint16_t *client_port, uint16_t *server_port); #endif /** Get an independent bit to allow an entity to enable and * disable port session tracking and syn session creation * without affecting the status of set by other entities. * Returns a bitmask (with the bit range 3-15) or 0, if no bits * are available. */ uint16_t (*get_preprocessor_status_bit)(void); #ifdef ACTIVE_RESPONSE // initialize response count and expiration time void (*init_active_response)(Packet *, void *); #endif // Get the TTL value used at session setup // outer=0 to get inner ip ttl for ip in ip; else outer=1 uint8_t (*get_session_ttl)(void *ssnptr, char direction, int outer); /* Turn off inspection for potential session. * Adds session identifiers to a hash table. * TCP only. * * Parameters * Control Channel Packet * IP addr #1 * Port #1 * IP addr #2 * Port #2 * Protocol * ID, * Preprocessor ID calling this function, * Preprocessor specific data, * Preprocessor data free function. If NULL, then static buffer is assumed. * * Returns * 0 on success * -1 on failure */ int (*set_application_protocol_id_expected)(const Packet *, sfaddr_t*, uint16_t, sfaddr_t*, uint16_t, uint8_t, int16_t, uint32_t, void*, void (*)(void*), struct _ExpectNode**); #ifdef ENABLE_HA /* Register a high availability producer and consumer function pair for a * particular preprocessor ID and subcode combination. * * Parameters * Processor ID * Subcode * Maximum Message Size * Message Producer Function * Message Consumer Function * * Returns * >= 0 on success * The returned value is the bit number in the HA pending bitmask and * should be stored for future calls to set_ha_pending_bit(). * < 0 on failure */ int (*register_ha_funcs)(uint32_t preproc_id, uint8_t subcode, uint8_t size, StreamHAProducerFunc produce, StreamHAConsumerFunc consume); /* Unregister a high availability producer and consumer function pair for a * particular preprocessor ID and subcode combination. * * Parameters * Processor ID * Subcode */ void (*unregister_ha_funcs)(uint32_t preproc_id, uint8_t subcode); /* Indicate a pending high availability update for a given session. * * Parameters * Session Ptr * HA Pending Update Bit */ void (*set_ha_pending_bit)(void *, int bit); /* Attempt to process any pending HA events for the given session * * Parameters * Session Ptr * DAQ Packet Header for the packet being processed (Could be NULL) */ void (*process_ha)(void *, const DAQ_PktHdr_t *); #endif //Retrieve the maximum session limits for the given policy void (*get_max_session_limits)(tSfPolicyId, StreamSessionLimits*); /* Set direction that data is being ignored. * * Parameters * Session Ptr */ int (*set_ignore_direction)(void *, int); /** Retrieve stream session pointer based on the lookup tuples for * cases where Snort does not have an active packet that is * relevant. * * Parameters * IP addr #1 * Port #1 (0 for non TCP/UDP) * IP addr #2 * Port #2 (0 for non TCP/UDP) * Protocol * VLAN ID * MPLS ID * Address Space ID * * Returns * Stream session pointer */ void *(*get_session_ptr_from_ip_port)(sfaddr_t*, uint16_t, sfaddr_t*, uint16_t, char, uint16_t, uint32_t, #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t, uint16_t #else uint16_t #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) , uint32_t #endif ); /** Retrieve the session key given a stream session pointer. * * Parameters * Session Ptr * * Returns * Stream session key */ const StreamSessionKey *(*get_key_from_session_ptr)(const void *); /* Delete the session if it is in the closed session state. * * Parameters * Packet */ void (*check_session_closed)(Packet *); /* Create a session key from the Packet * * Parameters * Packet */ StreamSessionKey *(*get_session_key)(Packet *); /* Get the application data from the session key * * Parameters * SessionKey * * Application Protocol */ void *(*get_application_data_from_key)(const StreamSessionKey *, uint32_t); /** Retrieve application session data based on the lookup tuples for * cases where Snort does not have an active packet that is * relevant. * * Parameters * IP addr #1 * Port #1 (0 for non TCP/UDP) * IP addr #2 * Port #2 (0 for non TCP/UDP) * Protocol * VLAN ID * MPLS ID * Address Space ID * Preprocessor ID * * Returns * Application Data reference (pointer) */ void *(*get_application_data_from_ip_port)(sfaddr_t*, uint16_t, sfaddr_t*, uint16_t, #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t, uint16_t, #else uint16_t, #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t, #endif char, uint16_t, uint32_t, uint32_t); void (*disable_preproc_for_session)( void *, uint32_t ); void (*enable_preproc_for_port)( struct _SnortConfig *, uint32_t, uint32_t, uint16_t ); void (*enable_preproc_all_ports)( struct _SnortConfig *, uint32_t, uint32_t ); void (*enable_preproc_all_ports_all_policies)( struct _SnortConfig *, uint32_t, uint32_t ); bool (*is_preproc_enabled_for_port)( uint32_t, uint16_t ); void (*register_nap_selector)( nap_selector ); void (*register_mandatory_early_session_creator)(struct _SnortConfig *, MandatoryEarlySessionCreatorFn callback); void* (*get_application_data_from_expected_node)(struct _ExpectNode*, uint32_t); int (*add_application_data_to_expected_node)(struct _ExpectNode*, uint32_t, void*, void (*)(void*)); void (*register_get_http_xff_precedence)(GetHttpXffPrecedenceFunc ); char** (*get_http_xff_precedence)(void* ssn, uint32_t flags, int* nFields); struct _ExpectNode* (*get_next_expected_node)(struct _ExpectNode*); void (*set_reputation_update_counter) (void *,uint8_t); } SessionAPI; /* To be set by Session */ extern SessionAPI *session_api; /**Port Inspection States. Port can be either ignored, * or inspected or session tracked. The values are bitmasks. */ typedef enum { /**Dont monitor the port. */ PORT_MONITOR_NONE = 0x00, /**Inspect the port. */ PORT_MONITOR_INSPECT = 0x01, /**perform session tracking on the port. */ PORT_MONITOR_SESSION = 0x02 } PortMonitorStates; #define PORT_MONITOR_SESSION_BITS 0xFFFE #define PP_SESSION_PRIORITY PRIORITY_CORE + PP_CORE_ORDER_SESSION // Utility functions // /********************************************************************* * Function: isPortEnabled * * Checks to see if a port is enabled in the port array mask * passed in. * * Arguments: * uint8_t * * Pointer to a port array mask. * const uint16_t * The port to check for in the mask. * * Returns: * bool * true if the port is set. * false if the port is not set. * *********************************************************************/ static inline bool isPortEnabled( const uint8_t *port_array, const uint16_t port ) { return port_array[ ( port / 8 ) ] & ( 1 << ( port % 8 ) ); } /********************************************************************* * Function: enablePort() * * Enable a port in the port array mask passed in. * * Arguments: * uint8_t * * Pointer to a port array mask. * const uint16_t * The port to set in the port array mask. * * Returns: None * *********************************************************************/ static inline void enablePort( uint8_t *port_array, const uint16_t port ) { port_array[ ( port / 8 ) ] |= ( 1 << ( port % 8 ) ); } /********************************************************************* * Function: disablePort() * * Disable a port in the port array mask passed in. * * Arguments: * uint8_t * * Pointer to a port array mask. * const uint16_t * The port to set in the port array mask. * * Returns: None * *********************************************************************/ static inline void disablePort( uint8_t *port_array, const uint16_t port ) { port_array[ ( port / 8 ) ] &= ~( 1 << ( port % 8 ) ); } #endif /* SESSION_API_H_ */ snort-2.9.20/src/preprocessors/spp_perfmonitor.h0000644000175000017500000000225014241077115020171 0ustar apoapo/* $Id$ ** ** spp_perfmonitor.h ** ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Marc Norton ** Dan Roelker ** ** NOTES ** 6.4.02 - Initial Source Code. Norton/Roelker ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. ** */ #ifndef _SPP_PERFMONITOR_H_ #define _SPP_PERFMONITOR_H_ void SetupPerfMonitor(void); #endif snort-2.9.20/src/preprocessors/str_search.h0000644000175000017500000000771014241077130017103 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2005-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __STR_SEARCH_H__ #define __STR_SEARCH_H__ #include "mpse_methods.h" /*search pattern case sensitivity */ #define STR_SEARCH_CASE_SENSITIVE 0 #define STR_SEARCH_CASE_INSENSITIVE 1 /* Function prototypes */ typedef int (*MatchFunction)(void *, void *, int, void *, void *); int SearchInit(unsigned int num); int SearchGetHandle(void); int SearchPutHandle(unsigned int id); int SearchReInit(unsigned int i); void SearchFree(void); void SearchFreeId(unsigned id); void SearchAdd(unsigned int mpse_id, const char *pat, unsigned int pat_len, int id); void SearchPrepPatterns(unsigned int mpse_id); int SearchFindString(unsigned int mpse_id, const char *str, unsigned int str_len, int confine, MatchFunction); void * SearchInstanceNew( void ); void * SearchInstanceNewEx( unsigned method ); void SearchInstanceFree( void * insance ); void SearchInstanceAdd( void * instance, const char *pat, unsigned int pat_len, int id); void SearchInstanceAddEx( void * instance, const char *pat, unsigned int pat_len, void* id, unsigned nocase); void SearchInstancePrepPatterns( void * instance ); int SearchInstanceFindString( void * instance, const char *str, unsigned int str_len, int confine, MatchFunction); int SearchInstanceFindStringAll( void * instance, const char *str, unsigned int str_len, int confine, MatchFunction, void *userData); int SearchInstanceSFindString( void * instance, const char *str, unsigned int str_len, int confine, MatchFunction, int *state); typedef struct _search_api { int (*search_init)(unsigned int); int (*search_reinit)(unsigned int); void (*search_free)(void); void (*search_add)(unsigned int, const char *, unsigned int, int); void (*search_prep)(unsigned int); int (*search_find)(unsigned int, const char *, unsigned int, int, MatchFunction); /* 6/1/06*/ void (*search_free_id)(unsigned id); int (*search_get_handle)(void); int (*search_put_handle)(unsigned int); void * (*search_instance_new)(void); void * (*search_instance_new_ex)(unsigned method); void (*search_instance_free)(void * instance); void (*search_instance_add) (void * instance, const char *s, unsigned int s_len, int s_id); void (*search_instance_add_ex) (void * instance, const char *s, unsigned int s_len, void* s_id, unsigned nocase); void (*search_instance_prep)(void * instance ); int (*search_instance_find)(void * instance, const char *s, unsigned int s_len, int confine, MatchFunction); int (*search_instance_find_all)(void * instance, const char *s, unsigned int s_len, int confine, MatchFunction, void *userData); char * (*search_instance_find_end)(char *match_ptr, int buflen, char *search_str, int search_len); int (*stateful_search_instance_find)(void * instance, const char *s, unsigned int s_len, int confine, MatchFunction, int *state); } SearchAPI; extern SearchAPI *search_api; #endif /* __STR_SEARCH_H__ */ snort-2.9.20/src/preprocessors/spp_sfportscan.c0000644000175000017500000014145614241077122020014 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2004-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /* ** @file spp_sfportscan.c ** ** @author Daniel Roelker ** ** @brief Portscan detection ** ** NOTES ** - User Configuration: The following is a list of parameters that can ** be configured through the user interface: ** ** proto { tcp udp icmp ip all } ** scan_type { portscan portsweep decoy_portscan distributed_portscan all } ** sense_level { high } # high, medium, low ** watch_ip { } # list of IPs, CIDR blocks ** ignore_scanners { } # list of IPs, CIDR blocks ** ignore_scanned { } # list of IPs, CIDR blocks ** memcap { 10000000 } # number of max bytes to allocate ** logfile { /tmp/ps.log } # file to log detailed portscan info */ #include #include #include #ifndef WIN32 # include # include # include #endif /* !WIN32 */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "decode.h" #include "encode.h" #include "plugbase.h" #include "generators.h" #include "event_wrapper.h" #include "util.h" #include "ipobj.h" #include "checksum.h" #include "packet_time.h" #include "snort.h" #include "sfthreshold.h" #include "sfsnprintfappend.h" #include "sf_iph.h" #include "session_api.h" #include "sfdaq.h" #include "portscan.h" #include "profiler.h" #include "reload.h" #ifdef REG_TEST #include "reg_test.h" #endif #define DELIMITERS " \t\n" #define TOKEN_ARG_BEGIN "{" #define TOKEN_ARG_END "}" #define PROTO_BUFFER_SIZE 256 extern char *file_name; extern int file_line; tSfPolicyUserContextId portscan_config = NULL; PortscanConfig *portscan_eval_config = NULL; static Packet *g_tmp_pkt = NULL; static FILE *g_logfile = NULL; #ifdef PERF_PROFILING PreprocStats sfpsPerfStats; #endif static void PortscanResetFunction(int signal, void *foo); static void PortscanResetStatsFunction(int signal, void *foo); static void ParsePortscan(struct _SnortConfig *, PortscanConfig *, char *); static void PortscanFreeConfigs(tSfPolicyUserContextId ); static void PortscanFreeConfig(PortscanConfig *); static void PortscanOpenLogFile(struct _SnortConfig *, void *); static int PortscanGetProtoBits(int); #ifdef SNORT_RELOAD static void PortscanReload(struct _SnortConfig *, char *, void **); static int PortscanReloadVerify(struct _SnortConfig *, void *); static void * PortscanReloadSwap(struct _SnortConfig *, void *); static void PortscanReloadSwapFree(void *); #endif /* ** NAME ** PortscanPacketInit:: */ /** ** Initialize the Packet structure buffer so we can generate our ** alert packets for portscan. We initialize the various fields in ** the Packet structure and set the hardware layer for easy identification ** by user interfaces. ** ** @return int ** ** @retval !0 initialization failed ** @retval 0 success */ #define IPPROTO_PS 0xFF static int PortscanPacketInit(void) { g_tmp_pkt = Encode_New(); return 0; } void PortscanCleanExitFunction(int signal, void *foo) { ps_cleanup(); Encode_Delete(g_tmp_pkt); g_tmp_pkt = NULL; PortscanFreeConfigs(portscan_config); portscan_config = NULL; } static void PortscanResetFunction(int signal, void *foo) { ps_reset(); } static void PortscanResetStatsFunction(int signal, void *foo) { return; } /* ** NAME ** MakeProtoInfo:: */ /** ** This routine makes the portscan payload for the events. The listed ** info is: ** - priority count (number of error transmissions RST/ICMP UNREACH) ** - connection count (number of protocol connections SYN) ** - ip count (number of IPs that communicated with host) ** - ip range (low to high range of IPs) ** - port count (number of port changes that occurred on host) ** - port range (low to high range of ports connected too) ** ** @return integer ** ** @retval -1 buffer not large enough ** @retval 0 successful */ static int MakeProtoInfo(PS_PROTO *proto, u_char *buffer, u_int *total_size) { int dsize; sfaddr_t *ip1, *ip2; if(!total_size || !buffer) return -1; dsize = (g_tmp_pkt->max_dsize - *total_size); if(dsize < PROTO_BUFFER_SIZE) return -1; ip1 = &proto->low_ip; ip2 = &proto->high_ip; if(proto->alerts == PS_ALERT_PORTSWEEP || proto->alerts == PS_ALERT_PORTSWEEP_FILTERED) { SnortSnprintf((char *)buffer, PROTO_BUFFER_SIZE, "Priority Count: %d\n" "Connection Count: %d\n" "IP Count: %d\n" "Scanned IP Range: %s:", proto->priority_count, proto->connection_count, proto->u_ip_count, inet_ntoa(ip1)); /* Now print the high ip into the buffer. This saves us * from having to copy the results of inet_ntoa (which is * a static buffer) to avoid the reuse of that buffer when * more than one use of inet_ntoa is within the same printf. */ SnortSnprintfAppend((char *)buffer, PROTO_BUFFER_SIZE, "%s\n" "Port/Proto Count: %d\n" "Port/Proto Range: %d:%d\n", inet_ntoa(ip2), proto->u_port_count, proto->low_p, proto->high_p); } else { SnortSnprintf((char *)buffer, PROTO_BUFFER_SIZE, "Priority Count: %d\n" "Connection Count: %d\n" "IP Count: %d\n" "Scanner IP Range: %s:", proto->priority_count, proto->connection_count, proto->u_ip_count, inet_ntoa(ip1) ); /* Now print the high ip into the buffer. This saves us * from having to copy the results of inet_ntoa (which is * a static buffer) to avoid the reuse of that buffer when * more than one use of inet_ntoa is within the same printf. */ SnortSnprintfAppend((char *)buffer, PROTO_BUFFER_SIZE, "%s\n" "Port/Proto Count: %d\n" "Port/Proto Range: %d:%d\n", inet_ntoa(ip2), proto->u_port_count, proto->low_p, proto->high_p); } dsize = SnortStrnlen((const char *)buffer, PROTO_BUFFER_SIZE); *total_size += dsize; /* ** Set the payload size. This is protocol independent. */ g_tmp_pkt->dsize = dsize; return 0; } static int LogPortscanAlert(Packet *p, const char *msg, uint32_t event_id, uint32_t event_ref, uint32_t gen_id, uint32_t sig_id) { char timebuf[TIMEBUF_SIZE]; sfaddr_t* src_addr; sfaddr_t* dst_addr; if(!p->iph_api) return -1; /* Do not log if being suppressed */ src_addr = GET_SRC_IP(p); dst_addr = GET_DST_IP(p); if( sfthreshold_test(gen_id, sig_id, src_addr, dst_addr, p->pkth->ts.tv_sec) ) { return 0; } ts_print((struct timeval *)&p->pkth->ts, timebuf); fprintf(g_logfile, "Time: %s\n", timebuf); if(event_id) fprintf(g_logfile, "event_id: %u\n", event_id); else fprintf(g_logfile, "event_ref: %u\n", event_ref); fprintf(g_logfile, "%s ", inet_ntoa(GET_SRC_ADDR(p))); fprintf(g_logfile, "-> %s %s\n", inet_ntoa(GET_DST_ADDR(p)), msg); fprintf(g_logfile, "%.*s\n", p->dsize, p->data); fflush(g_logfile); return 0; } static int GeneratePSSnortEvent(Packet *p,uint32_t gen_id,uint32_t sig_id, uint32_t sig_rev, uint32_t class, uint32_t priority, const char *msg) { unsigned int event_id; event_id = GenerateSnortEvent(p,gen_id,sig_id,sig_rev,class,priority,msg); if(g_logfile) LogPortscanAlert(p, msg, event_id, 0, gen_id, sig_id); return event_id; } /* ** NAME ** GenerateOpenPortEvent:: */ /** ** We have to generate open port events differently because we tag these ** to the original portscan event. ** ** @return int ** ** @retval 0 success */ static int GenerateOpenPortEvent(Packet *p, uint32_t gen_id, uint32_t sig_id, uint32_t sig_rev, uint32_t class, uint32_t pri, uint32_t event_ref, struct timeval *event_time, const char *msg) { Event event; /* ** This means that we logged an open port, but we don't have a event ** reference for it, so we don't log a snort event. We still keep ** track of it though. */ if(!event_ref) return 0; /* reset the thresholding subsystem checks for this packet */ sfthreshold_reset(); #if !defined(FEAT_OPEN_APPID) SetEvent(&event, gen_id, sig_id, sig_rev, class, pri, event_ref); #else /* defined(FEAT_OPEN_APPID) */ SetEvent(&event, gen_id, sig_id, sig_rev, class, pri, event_ref, NULL); #endif /* defined(FEAT_OPEN_APPID) */ //CallAlertFuncs(p,msg,NULL,&event); event.ref_time.tv_sec = event_time->tv_sec; event.ref_time.tv_usec = event_time->tv_usec; if(p) { /* * Do threshold test for suppression and thresholding. We have to do it * here since these are tagged packets, which aren't subject to thresholding, * but we want to do it for open port events. */ if( sfthreshold_test(gen_id, sig_id, GET_SRC_IP(p), GET_DST_IP(p), p->pkth->ts.tv_sec) ) { return 0; } CallLogFuncs(p,msg,NULL,&event); } else { return -1; } if(g_logfile) LogPortscanAlert(p, msg, 0, event_ref, gen_id, sig_id); return event.event_id; } /* ** NAME ** MakeOpenPortInfo:: */ /** ** Write out the open ports info for open port alerts. ** ** @return integer */ static int MakeOpenPortInfo(PS_PROTO *proto, u_char *buffer, u_int *total_size, void *user) { int dsize; if(!total_size || !buffer) return -1; dsize = (g_tmp_pkt->max_dsize - *total_size); if(dsize < PROTO_BUFFER_SIZE) return -1; SnortSnprintf((char *)buffer, PROTO_BUFFER_SIZE, "Open Port: %u\n", *((unsigned short *)user)); dsize = SnortStrnlen((const char *)buffer, PROTO_BUFFER_SIZE); *total_size += dsize; /* ** Set the payload size. This is protocol independent. */ g_tmp_pkt->dsize = dsize; return 0; } /* ** NAME ** MakePortscanPkt:: */ /* ** We have to create this fake packet so portscan data can be passed ** through the unified output. ** ** We want to copy the network and transport layer headers into our ** fake packet. ** */ static int MakePortscanPkt(PS_PKT *ps_pkt, PS_PROTO *proto, int proto_type, void *user) { unsigned int ip_size = 0; Packet* p = (Packet *)ps_pkt->pkt; EncodeFlags flags = ENC_FLAG_NET; if (!IsIP(p)) return -1; if ( !ps_pkt->reverse_pkt ) flags |= ENC_FLAG_FWD; if (p != g_tmp_pkt) { #if defined(HAVE_DAQ_ADDRESS_SPACE_ID) && defined(DAQ_VERSION) && DAQ_VERSION > 6 DAQ_PktHdr_t phdr; memcpy(&phdr, &p->pkth, sizeof(*p->pkth)); if (p->pkth->flags & DAQ_PKT_FLAG_REAL_ADDRESSES) { phdr.flags &= ~(DAQ_PKT_FLAG_REAL_SIP_V6 | DAQ_PKT_FLAG_REAL_DIP_V6); if (flags & ENC_FLAG_FWD) { phdr.flags |= phdr.flags & (DAQ_PKT_FLAG_REAL_SIP_V6 | DAQ_PKT_FLAG_REAL_DIP_V6); phdr.real_sIP = p->pkth->real_sIP; phdr.real_dIP = p->pkth->real_dIP; } else { if (p->pkth->flags & DAQ_PKT_FLAG_REAL_SIP_V6) phdr.flags |= DAQ_PKT_FLAG_REAL_DIP_V6; if (p->pkth->flags & DAQ_PKT_FLAG_REAL_DIP_V6) phdr.flags |= DAQ_PKT_FLAG_REAL_SIP_V6; phdr.real_sIP = p->pkth->real_dIP; phdr.real_dIP = p->pkth->real_sIP; } } Encode_Format_With_DAQ_Info(flags, p, g_tmp_pkt, PSEUDO_PKT_PS, &phdr, 0); #elif defined(HAVE_DAQ_ACQUIRE_WITH_META) && defined(DAQ_VERSION) && DAQ_VERSION > 6 Encode_Format_With_DAQ_Info(flags, p, g_tmp_pkt, PSEUDO_PKT_PS, 0); #else Encode_Format(flags, p, g_tmp_pkt, PSEUDO_PKT_PS); #endif } switch (proto_type) { case PS_PROTO_TCP: g_tmp_pkt->ps_proto = IPPROTO_TCP; break; case PS_PROTO_UDP: g_tmp_pkt->ps_proto = IPPROTO_UDP; break; case PS_PROTO_ICMP: g_tmp_pkt->ps_proto = IPPROTO_ICMP; break; case PS_PROTO_IP: g_tmp_pkt->ps_proto = IPPROTO_IP; break; case PS_PROTO_OPEN_PORT: g_tmp_pkt->ps_proto = GET_IPH_PROTO(p); break; default: return -1; } if(IS_IP4(p)) { ((IPHdr*)g_tmp_pkt->iph)->ip_proto = IPPROTO_PS; g_tmp_pkt->inner_ip4h.ip_proto = IPPROTO_PS; } else { if ( g_tmp_pkt->raw_ip6h ) ((IP6RawHdr*)g_tmp_pkt->raw_ip6h)->ip6nxt = IPPROTO_PS; g_tmp_pkt->inner_ip6h.next = IPPROTO_PS; g_tmp_pkt->ip6h = &g_tmp_pkt->inner_ip6h; } switch(proto_type) { case PS_PROTO_TCP: case PS_PROTO_UDP: case PS_PROTO_ICMP: case PS_PROTO_IP: if(MakeProtoInfo(proto, (u_char*)g_tmp_pkt->data, &ip_size)) return -1; break; case PS_PROTO_OPEN_PORT: if(MakeOpenPortInfo(proto, (u_char*)g_tmp_pkt->data, &ip_size, user)) return -1; break; default: return -1; } /* ** Let's finish up the IP header and checksum. */ Encode_Update(g_tmp_pkt); if(IS_IP4(g_tmp_pkt)) { g_tmp_pkt->inner_ip4h.ip_len = ((IPHdr *)g_tmp_pkt->iph)->ip_len; } else if (IS_IP6(g_tmp_pkt)) { g_tmp_pkt->inner_ip6h.len = htons((uint16_t)ip_size); } return 0; } static int PortscanAlertTcp(Packet *p, PS_PROTO *proto, int proto_type) { int iCtr; unsigned int event_ref; int portsweep = 0; if(!proto) return -1; switch(proto->alerts) { case PS_ALERT_ONE_TO_ONE: event_ref = GeneratePSSnortEvent(p, GENERATOR_PSNG, PSNG_TCP_PORTSCAN, 0, 0, 3, PSNG_TCP_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_DECOY: event_ref = GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_TCP_DECOY_PORTSCAN,0,0,3,PSNG_TCP_DECOY_PORTSCAN_STR); break; case PS_ALERT_PORTSWEEP: event_ref = GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_TCP_PORTSWEEP, 0, 0, 3, PSNG_TCP_PORTSWEEP_STR); portsweep = 1; break; case PS_ALERT_DISTRIBUTED: event_ref = GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_TCP_DISTRIBUTED_PORTSCAN, 0, 0, 3, PSNG_TCP_DISTRIBUTED_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_FILTERED: event_ref = GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_TCP_FILTERED_PORTSCAN,0,0,3, PSNG_TCP_FILTERED_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_DECOY_FILTERED: event_ref = GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_TCP_FILTERED_DECOY_PORTSCAN, 0,0,3, PSNG_TCP_FILTERED_DECOY_PORTSCAN_STR); break; case PS_ALERT_PORTSWEEP_FILTERED: event_ref = GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_TCP_PORTSWEEP_FILTERED,0,0,3, PSNG_TCP_PORTSWEEP_FILTERED_STR); portsweep = 1; return 0; case PS_ALERT_DISTRIBUTED_FILTERED: event_ref = GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_TCP_FILTERED_DISTRIBUTED_PORTSCAN, 0, 0, 3, PSNG_TCP_FILTERED_DISTRIBUTED_PORTSCAN_STR); break; default: return 0; } /* ** Set the current event reference information for any open ports. */ proto->event_ref = event_ref; proto->event_time.tv_sec = p->pkth->ts.tv_sec; proto->event_time.tv_usec = p->pkth->ts.tv_usec; /* ** Only log open ports for portsweeps after the alert has been ** generated. */ if(proto->open_ports_cnt && !portsweep) { for(iCtr = 0; iCtr < proto->open_ports_cnt; iCtr++) { DAQ_PktHdr_t *pkth = (DAQ_PktHdr_t *)g_tmp_pkt->pkth; PS_PKT ps_pkt; memset(&ps_pkt, 0x00, sizeof(PS_PKT)); ps_pkt.pkt = (void *)p; if(MakePortscanPkt(&ps_pkt, proto, PS_PROTO_OPEN_PORT, (void *)&proto->open_ports[iCtr])) return -1; pkth->ts.tv_usec += 1; GenerateOpenPortEvent(g_tmp_pkt,GENERATOR_PSNG,PSNG_OPEN_PORT, 0,0,3, proto->event_ref, &proto->event_time, PSNG_OPEN_PORT_STR); } } return 0; } static int PortscanAlertUdp(Packet *p, PS_PROTO *proto, int proto_type) { if(!proto) return -1; switch(proto->alerts) { case PS_ALERT_ONE_TO_ONE: GeneratePSSnortEvent(p, GENERATOR_PSNG, PSNG_UDP_PORTSCAN, 0, 0, 3, PSNG_UDP_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_DECOY: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_UDP_DECOY_PORTSCAN, 0, 0, 3, PSNG_UDP_DECOY_PORTSCAN_STR); break; case PS_ALERT_PORTSWEEP: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_UDP_PORTSWEEP, 0, 0, 3, PSNG_UDP_PORTSWEEP_STR); break; case PS_ALERT_DISTRIBUTED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_UDP_DISTRIBUTED_PORTSCAN, 0, 0, 3, PSNG_UDP_DISTRIBUTED_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_UDP_FILTERED_PORTSCAN,0,0,3, PSNG_UDP_FILTERED_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_DECOY_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_UDP_FILTERED_DECOY_PORTSCAN, 0,0,3, PSNG_UDP_FILTERED_DECOY_PORTSCAN_STR); break; case PS_ALERT_PORTSWEEP_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_UDP_PORTSWEEP_FILTERED,0,0,3, PSNG_UDP_PORTSWEEP_FILTERED_STR); break; case PS_ALERT_DISTRIBUTED_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN, 0, 0, 3, PSNG_UDP_FILTERED_DISTRIBUTED_PORTSCAN_STR); break; default: break; } return 0; } static int PortscanAlertIp(Packet *p, PS_PROTO *proto, int proto_type) { if(!proto) return -1; switch(proto->alerts) { case PS_ALERT_ONE_TO_ONE: GeneratePSSnortEvent(p, GENERATOR_PSNG, PSNG_IP_PORTSCAN, 0, 0, 3, PSNG_IP_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_DECOY: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_IP_DECOY_PORTSCAN, 0, 0, 3, PSNG_IP_DECOY_PORTSCAN_STR); break; case PS_ALERT_PORTSWEEP: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_IP_PORTSWEEP, 0, 0, 3, PSNG_IP_PORTSWEEP_STR); break; case PS_ALERT_DISTRIBUTED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_IP_DISTRIBUTED_PORTSCAN, 0, 0, 3, PSNG_IP_DISTRIBUTED_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_IP_FILTERED_PORTSCAN,0,0,3, PSNG_IP_FILTERED_PORTSCAN_STR); break; case PS_ALERT_ONE_TO_ONE_DECOY_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_IP_FILTERED_DECOY_PORTSCAN, 0,0,3, PSNG_IP_FILTERED_DECOY_PORTSCAN_STR); break; case PS_ALERT_PORTSWEEP_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_IP_PORTSWEEP_FILTERED,0,0,3, PSNG_IP_PORTSWEEP_FILTERED_STR); break; case PS_ALERT_DISTRIBUTED_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG, PSNG_IP_FILTERED_DISTRIBUTED_PORTSCAN, 0, 0, 3, PSNG_IP_FILTERED_DISTRIBUTED_PORTSCAN_STR); break; default: break; } return 0; } static int PortscanAlertIcmp(Packet *p, PS_PROTO *proto, int proto_type) { if(!proto) return -1; switch(proto->alerts) { case PS_ALERT_PORTSWEEP: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_ICMP_PORTSWEEP, 0, 0, 3, PSNG_ICMP_PORTSWEEP_STR); break; case PS_ALERT_PORTSWEEP_FILTERED: GeneratePSSnortEvent(p,GENERATOR_PSNG,PSNG_ICMP_PORTSWEEP_FILTERED,0,0,3, PSNG_ICMP_PORTSWEEP_FILTERED_STR); break; default: break; } return 0; } static int PortscanAlert(PS_PKT *ps_pkt, PS_PROTO *proto, int proto_type) { Packet *p; if(!ps_pkt || !ps_pkt->pkt) return -1; p = (Packet *)ps_pkt->pkt; if(proto->alerts == PS_ALERT_OPEN_PORT) { if(MakePortscanPkt(ps_pkt, proto, PS_PROTO_OPEN_PORT, (void *)&p->sp)) return -1; GenerateOpenPortEvent(g_tmp_pkt,GENERATOR_PSNG,PSNG_OPEN_PORT,0,0,3, proto->event_ref, &proto->event_time, PSNG_OPEN_PORT_STR); } else { if(MakePortscanPkt(ps_pkt, proto, proto_type, NULL)) return -1; switch(proto_type) { case PS_PROTO_TCP: PortscanAlertTcp(g_tmp_pkt, proto, proto_type); break; case PS_PROTO_UDP: PortscanAlertUdp(g_tmp_pkt, proto, proto_type); break; case PS_PROTO_ICMP: PortscanAlertIcmp(g_tmp_pkt, proto, proto_type); break; case PS_PROTO_IP: PortscanAlertIp(g_tmp_pkt, proto, proto_type); break; } } sfthreshold_reset(); return 0; } static void PortscanDetect(Packet *p, void *context) { PS_PKT ps_pkt; tSfPolicyId policy_id = getNapRuntimePolicy(); PortscanConfig *pPolicyConfig = NULL; PROFILE_VARS; assert(IPH_IS_VALID(p)); if ( p->packet_flags & PKT_REBUILT_STREAM ) return; sfPolicyUserPolicySet (portscan_config, policy_id); pPolicyConfig = (PortscanConfig *)sfPolicyUserDataGetCurrent(portscan_config); if ( pPolicyConfig == NULL ) return; portscan_eval_config = pPolicyConfig; PREPROC_PROFILE_START(sfpsPerfStats); memset(&ps_pkt, 0x00, sizeof(PS_PKT)); ps_pkt.pkt = (void *)p; /* See if there is already an exisiting node in the hash table */ ps_detect(&ps_pkt); if (ps_pkt.scanner && ps_pkt.scanner->proto.alerts && (ps_pkt.scanner->proto.alerts != PS_ALERT_GENERATED)) { PortscanAlert(&ps_pkt, &ps_pkt.scanner->proto, ps_pkt.proto); } if (ps_pkt.scanned && ps_pkt.scanned->proto.alerts && (ps_pkt.scanned->proto.alerts != PS_ALERT_GENERATED)) { PortscanAlert(&ps_pkt, &ps_pkt.scanned->proto, ps_pkt.proto); } PREPROC_PROFILE_END(sfpsPerfStats); } NORETURN static void FatalErrorNoOption(u_char *option) { FatalError("%s(%d) => No argument to '%s' config option.\n", file_name, file_line, option); } NORETURN static void FatalErrorNoEnd(char *option) { FatalError("%s(%d) => No ending brace to '%s' config option.\n", file_name, file_line, option); } NORETURN static void FatalErrorInvalidArg(char *option) { FatalError("%s(%d) => Invalid argument to '%s' config option.\n", file_name, file_line, option); } NORETURN static void FatalErrorInvalidOption(char *option) { FatalError("%s(%d) => Invalid option '%s' to portscan preprocessor.\n", file_name, file_line, option); } static void ParseProtos(int *protos, char **savptr) { char *pcTok; if(!protos) return; *protos = 0; pcTok = strtok_r(NULL, DELIMITERS, savptr); while(pcTok) { if(!strcasecmp(pcTok, "tcp")) *protos |= PS_PROTO_TCP; else if(!strcasecmp(pcTok, "udp")) *protos |= PS_PROTO_UDP; else if(!strcasecmp(pcTok, "icmp")) *protos |= PS_PROTO_ICMP; else if(!strcasecmp(pcTok, "ip")) *protos |= PS_PROTO_IP; else if(!strcasecmp(pcTok, "all")) *protos = PS_PROTO_ALL; else if(!strcasecmp(pcTok, TOKEN_ARG_END)) return; else FatalErrorInvalidArg("proto"); pcTok = strtok_r(NULL, DELIMITERS, savptr); } if(!pcTok) FatalErrorNoEnd("proto"); return; } static void ParseScanType(int *scan_types, char **savptr) { char *pcTok; if(!scan_types) return; *scan_types = 0; pcTok = strtok_r(NULL, DELIMITERS, savptr); while(pcTok) { if(!strcasecmp(pcTok, "portscan")) *scan_types |= PS_TYPE_PORTSCAN; else if(!strcasecmp(pcTok, "portsweep")) *scan_types |= PS_TYPE_PORTSWEEP; else if(!strcasecmp(pcTok, "decoy_portscan")) *scan_types |= PS_TYPE_DECOYSCAN; else if(!strcasecmp(pcTok, "distributed_portscan")) *scan_types |= PS_TYPE_DISTPORTSCAN; else if(!strcasecmp(pcTok, "all")) *scan_types = PS_TYPE_ALL; else if(!strcasecmp(pcTok, TOKEN_ARG_END)) return; else FatalErrorInvalidArg("scan_type"); pcTok = strtok_r(NULL, DELIMITERS, savptr); } if(!pcTok) FatalErrorNoEnd("scan_type"); return; } static void ParseSenseLevel(int *sense_level, char **savptr) { char *pcTok; if(!sense_level) return; *sense_level = 0; pcTok = strtok_r(NULL, DELIMITERS, savptr); while(pcTok) { if(!strcasecmp(pcTok, "low")) *sense_level = PS_SENSE_LOW; else if(!strcasecmp(pcTok, "medium")) *sense_level = PS_SENSE_MEDIUM; else if(!strcasecmp(pcTok, "high")) *sense_level = PS_SENSE_HIGH; else if(!strcmp(pcTok, TOKEN_ARG_END)) return; else FatalErrorInvalidArg("sense_level"); pcTok = strtok_r(NULL, DELIMITERS, savptr); } if(!pcTok) FatalErrorNoEnd("sense_level"); return; } static void ParseIpList(IPSET **ip_list, char *option, char **savptr) { char *pcTok; if(!ip_list) return; pcTok = strtok_r(NULL, TOKEN_ARG_END, savptr); if(!pcTok) FatalErrorInvalidArg(option); *ip_list = ipset_new(); if(!*ip_list) FatalError("Failed to initialize ip_list in portscan preprocessor.\n"); if(ipset_parse(*ip_list, pcTok)) FatalError("%s(%d) => Invalid ip_list to '%s' option.\n", file_name, file_line, option); return; } static void ParseMemcap(unsigned long *memcap, char **savptr) { char *pcTok; char *p; if(!memcap) return; *memcap = 0; pcTok = strtok_r(NULL, DELIMITERS, savptr); if(!pcTok) FatalErrorNoEnd("memcap"); *memcap = strtoul(pcTok, &p, 10); if(!*pcTok || *pcTok == '-' || *p) FatalErrorInvalidArg("memcap"); pcTok = strtok_r(NULL, DELIMITERS, savptr); if(!pcTok) FatalErrorNoEnd("memcap"); if(strcmp(pcTok, TOKEN_ARG_END)) FatalErrorInvalidArg("memcap"); return; } static void PrintIPPortSet(IP_PORT *p) { char ip_str[80], output_str[80]; PORTRANGE *pr; SnortSnprintf(ip_str, sizeof(ip_str), "%s", sfip_to_str(&p->ip.addr)); if(p->notflag) SnortSnprintf(output_str, sizeof(output_str), " !%s", ip_str); else SnortSnprintf(output_str, sizeof(output_str), " %s", ip_str); if (p->ip.bits != 128) SnortSnprintfAppend(output_str, sizeof(output_str), "/%d", (sfaddr_family(&p->ip.addr) == AF_INET) ? ((p->ip.bits >= 96) ? p->ip.bits - 96 : -1) : p->ip.bits); pr=(PORTRANGE*)sflist_first(&p->portset.port_list); if ( pr && pr->port_lo != 0 ) SnortSnprintfAppend(output_str, sizeof(output_str), " : "); for( ; pr != 0; pr=(PORTRANGE*)sflist_next(&p->portset.port_list) ) { if ( pr->port_lo != 0) { SnortSnprintfAppend(output_str, sizeof(output_str), "%d", pr->port_lo); if ( pr->port_hi != pr->port_lo ) { SnortSnprintfAppend(output_str, sizeof(output_str), "-%d", pr->port_hi); } SnortSnprintfAppend(output_str, sizeof(output_str), " "); } } LogMessage("%s\n", output_str); } static void PrintPortscanConf(int detect_scans, int detect_scan_type, int sense_level, IPSET *scanner, IPSET *scanned, IPSET *watch, unsigned long memcap, char *logpath, int disabled) { char buf[STD_BUF + 1]; int proto_cnt = 0; IP_PORT *p; LogMessage("Portscan Detection Config:\n"); if(disabled) { LogMessage(" Portscan Detection: INACTIVE\n"); } memset(buf, 0, STD_BUF + 1); if (!disabled) { SnortSnprintf(buf, STD_BUF + 1, " Detect Protocols: "); if(detect_scans & PS_PROTO_TCP) { sfsnprintfappend(buf, STD_BUF, "TCP "); proto_cnt++; } if(detect_scans & PS_PROTO_UDP) { sfsnprintfappend(buf, STD_BUF, "UDP "); proto_cnt++; } if(detect_scans & PS_PROTO_ICMP) { sfsnprintfappend(buf, STD_BUF, "ICMP "); proto_cnt++; } if(detect_scans & PS_PROTO_IP) { sfsnprintfappend(buf, STD_BUF, "IP"); proto_cnt++; } LogMessage("%s\n", buf); } if (!disabled) { memset(buf, 0, STD_BUF + 1); SnortSnprintf(buf, STD_BUF + 1, " Detect Scan Type: "); if(detect_scan_type & PS_TYPE_PORTSCAN) sfsnprintfappend(buf, STD_BUF, "portscan "); if(detect_scan_type & PS_TYPE_PORTSWEEP) sfsnprintfappend(buf, STD_BUF, "portsweep "); if(detect_scan_type & PS_TYPE_DECOYSCAN) sfsnprintfappend(buf, STD_BUF, "decoy_portscan "); if(detect_scan_type & PS_TYPE_DISTPORTSCAN) sfsnprintfappend(buf, STD_BUF, "distributed_portscan"); LogMessage("%s\n", buf); } if (!disabled) { memset(buf, 0, STD_BUF + 1); SnortSnprintf(buf, STD_BUF + 1, " Sensitivity Level: "); if(sense_level == PS_SENSE_HIGH) sfsnprintfappend(buf, STD_BUF, "High/Experimental"); if(sense_level == PS_SENSE_MEDIUM) sfsnprintfappend(buf, STD_BUF, "Medium"); if(sense_level == PS_SENSE_LOW) sfsnprintfappend(buf, STD_BUF, "Low"); LogMessage("%s\n", buf); } LogMessage(" Memcap (in bytes): %lu\n", memcap); if (!disabled) { LogMessage(" Number of Nodes: %ld\n", memcap / (sizeof(PS_PROTO)*proto_cnt-1)); if (logpath != NULL) LogMessage(" Logfile: %s\n", logpath); if(scanner) { LogMessage(" Ignore Scanner IP List:\n"); for(p = (IP_PORT*)sflist_first(&scanner->ip_list); p; p = (IP_PORT*)sflist_next(&scanner->ip_list)) { PrintIPPortSet(p); } } if(scanned) { LogMessage(" Ignore Scanned IP List:\n"); for(p = (IP_PORT*)sflist_first(&scanned->ip_list); p; p = (IP_PORT*)sflist_next(&scanned->ip_list)) { PrintIPPortSet(p); } } if(watch) { LogMessage(" Watch IP List:\n"); for(p = (IP_PORT*)sflist_first(&watch->ip_list); p; p = (IP_PORT*)sflist_next(&watch->ip_list)) { PrintIPPortSet(p); } } } } static void ParseLogFile(struct _SnortConfig *sc, PortscanConfig *config, char **savptr) { char *pcTok; if (config == NULL) return; pcTok = strtok_r(NULL, DELIMITERS, savptr); if (pcTok == NULL) { FatalError("%s(%d) => No ending brace to '%s' config option.\n", file_name, file_line, "logfile"); } config->logfile = ProcessFileOption(sc, pcTok); pcTok = strtok_r(NULL, DELIMITERS, savptr); if (pcTok == NULL) { FatalError("%s(%d) => No ending brace to '%s' config option.\n", file_name, file_line, "logfile"); } if (strcmp(pcTok, TOKEN_ARG_END) != 0) { FatalError("%s(%d) => Invalid argument to '%s' config option.\n", file_name, file_line, "logfile"); } } #ifdef REG_TEST static inline void PrintPORTSCANSize(void) { LogMessage("\nPORTSCAN Session Size: %lu\n", (long unsigned int)sizeof(PS_TRACKER)); } #endif static void PortscanInit(struct _SnortConfig *sc, char *args) { tSfPolicyId policy_id = getParserPolicy(sc); PortscanConfig *pPolicyConfig = NULL; #ifdef REG_TEST PrintPORTSCANSize(); #endif if (portscan_config == NULL) { portscan_config = sfPolicyConfigCreate(); PortscanPacketInit(); AddFuncToPreprocCleanExitList(PortscanCleanExitFunction, NULL, PRIORITY_SCANNER, PP_SFPORTSCAN); AddFuncToPreprocResetList(PortscanResetFunction, NULL, PRIORITY_SCANNER, PP_SFPORTSCAN); AddFuncToPreprocResetStatsList(PortscanResetStatsFunction, NULL, PRIORITY_SCANNER, PP_SFPORTSCAN); AddFuncToPreprocPostConfigList(sc, PortscanOpenLogFile, NULL); #ifdef PERF_PROFILING RegisterPreprocessorProfile("sfportscan", &sfpsPerfStats, 0, &totalPerfStats, NULL); #endif } if ((policy_id != 0) && (((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config)) == NULL)) { ParseError("Portscan: Must configure default policy if other " "policies are going to be configured."); } sfPolicyUserPolicySet (portscan_config, policy_id); pPolicyConfig = (PortscanConfig *)sfPolicyUserDataGetCurrent(portscan_config); if (pPolicyConfig) { ParseError("Can only configure sfportscan once.\n"); } pPolicyConfig = (PortscanConfig *)SnortAlloc(sizeof(PortscanConfig)); if (!pPolicyConfig) { ParseError("SFPORTSCAN preprocessor: memory allocate failed.\n"); } sfPolicyUserDataSetCurrent(portscan_config, pPolicyConfig); ParsePortscan(sc, pPolicyConfig, args); if (policy_id == 0) { ps_init_hash(pPolicyConfig->memcap); } else { pPolicyConfig->memcap = ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config))->memcap; if (pPolicyConfig->logfile != NULL) { ParseError("Portscan: logfile can only be configured in " "default policy.\n"); } } if ( !pPolicyConfig->disabled ) { AddFuncToPreprocList(sc, PortscanDetect, PRIORITY_SCANNER, PP_SFPORTSCAN, PortscanGetProtoBits(pPolicyConfig->detect_scans)); session_api->enable_preproc_all_ports( sc, PP_SFPORTSCAN, PortscanGetProtoBits(pPolicyConfig->detect_scans) ); } } void SetupSfPortscan(void) { #ifndef SNORT_RELOAD RegisterPreprocessor("sfportscan", PortscanInit); #else RegisterPreprocessor("sfportscan", PortscanInit, PortscanReload, PortscanReloadVerify, PortscanReloadSwap, PortscanReloadSwapFree); #endif } static void ParsePortscan(struct _SnortConfig *sc, PortscanConfig *config, char *args) { int sense_level = PS_SENSE_LOW; int protos = (PS_PROTO_TCP | PS_PROTO_UDP); int scan_types = PS_TYPE_ALL; unsigned long memcap = 1048576; IPSET *ignore_scanners = NULL; IPSET *ignore_scanned = NULL; IPSET *watch_ip = NULL; char *pcTok, *savpcTok = NULL; int iRet; if (args != NULL) { pcTok = strtok_r(args, DELIMITERS, &savpcTok); //pcTok = strtok(args, DELIMITERS); while(pcTok) { if(!strcasecmp(pcTok, "proto")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); //pcTok = strtok(NULL, DELIMITERS); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"proto"); ParseProtos(&protos, &savpcTok); } else if(!strcasecmp(pcTok, "scan_type")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"scan_type"); ParseScanType(&scan_types, &savpcTok); } else if(!strcasecmp(pcTok, "sense_level")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"sense_level"); ParseSenseLevel(&sense_level, &savpcTok); } else if(!strcasecmp(pcTok, "ignore_scanners")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"ignore_scanners"); ParseIpList(&ignore_scanners, "ignore_scanners", &savpcTok); } else if(!strcasecmp(pcTok, "ignore_scanned")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"ignore_scanned"); ParseIpList(&ignore_scanned, "ignore_scanned", &savpcTok); } else if(!strcasecmp(pcTok, "watch_ip")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"watch_ip"); ParseIpList(&watch_ip, "watch_ip", &savpcTok); } else if(!strcasecmp(pcTok, "print_tracker")) { config->print_tracker = 1; } else if(!strcasecmp(pcTok, "memcap")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"memcap"); ParseMemcap(&memcap, &savpcTok); } else if(!strcasecmp(pcTok, "logfile")) { pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); if(!pcTok || strcmp(pcTok, TOKEN_ARG_BEGIN)) FatalErrorNoOption((u_char *)"logfile"); ParseLogFile(sc, config, &savpcTok); } else if(!strcasecmp(pcTok, "include_midstream")) { /* Do not ignore packets in sessions picked up mid-stream */ config->include_midstream = 1; } else if(!strcasecmp(pcTok, "detect_ack_scans")) { /* * We will only see ack scan packets if we are looking at sessions that the * have been flagged as being picked up mid-stream */ config->include_midstream = 1; } else if(!strcasecmp(pcTok, "disabled")) { config->disabled = 1; } else { FatalErrorInvalidOption(pcTok); } pcTok = strtok_r(NULL, DELIMITERS, &savpcTok); } } iRet = ps_init(sc, config, protos, scan_types, sense_level, ignore_scanners, ignore_scanned, watch_ip, memcap); if (iRet) { if(iRet == -2) { FatalError("%s(%d) => 'memcap' limit not sufficient to run " "sfportscan preprocessor. Please increase this " "value or keep the default memory usage.\n", file_name, file_line); } FatalError("Failed to initialize the sfportscan detection module. " "Please check your configuration before submitting a " "bug.\n"); } PrintPortscanConf(protos, scan_types, sense_level, ignore_scanners, ignore_scanned, watch_ip, memcap, config->logfile, config->disabled); } static void PortscanOpenLogFile(struct _SnortConfig *sc, void *data) { PortscanConfig *pPolicyConfig = (PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config) ; if (( pPolicyConfig== NULL) || (pPolicyConfig->logfile == NULL)) { return; } g_logfile = fopen(pPolicyConfig->logfile, "a+"); if (g_logfile == NULL) { FatalError("Portscan log file '%s' could not be opened: %s.\n", pPolicyConfig->logfile, strerror(errno)); } } static int PortscanFreeConfigPolicy(tSfPolicyUserContextId config,tSfPolicyId policyId, void* pData ) { PortscanConfig *pPolicyConfig = (PortscanConfig *)pData; sfPolicyUserDataClear (config, policyId); PortscanFreeConfig(pPolicyConfig); return 0; } static void PortscanFreeConfigs(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, PortscanFreeConfigPolicy); sfPolicyConfigDelete(config); } static void PortscanFreeConfig(PortscanConfig *config) { if (config == NULL) return; if (config->logfile) free(config->logfile); if (config->ignore_scanners != NULL) ipset_free(config->ignore_scanners); if (config->ignore_scanned != NULL) ipset_free(config->ignore_scanned); if (config->watch_ip != NULL) ipset_free(config->watch_ip); free(config); } static int PortscanGetProtoBits(int detect_scans) { int proto_bits = PROTO_BIT__IP; if (detect_scans & PS_PROTO_IP) { proto_bits |= PROTO_BIT__ICMP; } if (detect_scans & PS_PROTO_UDP) { proto_bits |= PROTO_BIT__ICMP; proto_bits |= PROTO_BIT__UDP; } if (detect_scans & PS_PROTO_ICMP) proto_bits |= PROTO_BIT__ICMP; if (detect_scans & PS_PROTO_TCP) { proto_bits |= PROTO_BIT__ICMP; proto_bits |= PROTO_BIT__TCP; } return proto_bits; } #ifdef SNORT_RELOAD static void PortscanReload(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId portscan_swap_config = (tSfPolicyUserContextId)*new_config; tSfPolicyId policy_id = getParserPolicy(sc); PortscanConfig *pPolicyConfig = NULL; if (!portscan_swap_config) { portscan_swap_config = sfPolicyConfigCreate(); *new_config = (void *)portscan_swap_config; } if ((policy_id != 0) && (((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config)) == NULL)) { ParseError("Portscan: Must configure default policy if other " "policies are going to be configured."); } sfPolicyUserPolicySet (portscan_swap_config, policy_id); pPolicyConfig = (PortscanConfig *)sfPolicyUserDataGetCurrent(portscan_swap_config); if (pPolicyConfig) { ParseError("Can only configure sfportscan once.\n"); } pPolicyConfig = (PortscanConfig *)SnortAlloc(sizeof(PortscanConfig)); if (!pPolicyConfig) { ParseError("SFPORTSCAN preprocessor: memory allocate failed.\n"); } sfPolicyUserDataSetCurrent(portscan_swap_config, pPolicyConfig); ParsePortscan(sc, pPolicyConfig, args); if (policy_id != 0) { pPolicyConfig->memcap = ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config))->memcap; if (pPolicyConfig->logfile != NULL) { ParseError("Portscan: logfile can only be configured in " "default policy.\n"); } } if ( !pPolicyConfig->disabled ) { AddFuncToPreprocList(sc, PortscanDetect, PRIORITY_SCANNER, PP_SFPORTSCAN, PortscanGetProtoBits(pPolicyConfig->detect_scans)); session_api->enable_preproc_all_ports( sc, PP_SFPORTSCAN, PortscanGetProtoBits(pPolicyConfig->detect_scans) ); } } static bool PortscanReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned max_work = idle ? 512 : 32; unsigned long memcap = *(unsigned long *)userData; return ps_reload_adjust(memcap, max_work); } static int PortscanReloadVerify(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId portscan_swap_config = (tSfPolicyUserContextId)swap_config; static unsigned long new_memcap; unsigned long old_memcap = ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config))->memcap; new_memcap = ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config))->memcap; tSfPolicyId policy_id = getParserPolicy(sc); if ((portscan_swap_config == NULL) || (((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config)) == NULL) || (portscan_config == NULL) || (((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config)) == NULL)) { return 0; } if (old_memcap != new_memcap) { #ifdef REG_TEST if (REG_TEST_FLAG_PORTSCAN_RELOAD & getRegTestFlags()) { printf("portscan memcap old conf : %lu new conf : %lu\n",old_memcap,new_memcap); } #endif /* If memcap is less than hash overhead bytes, restart is needed */ if( new_memcap > ps_hash_overhead_bytes()) { ReloadAdjustRegister(sc, "PortscanReload", policy_id, &PortscanReloadAdjust, &new_memcap, NULL); } else { ErrorMessage("Portscan Reload: New memcap %lu s lower than minimum memory needed for hash table %u, and it requires a restart.\n", new_memcap, ps_hash_overhead_bytes()); return -1; } } return 0; } static void * PortscanReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId portscan_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = portscan_config; bool log_file_swap = false; if (portscan_swap_config == NULL) return NULL; if ((((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config))->logfile != NULL) && (((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config))->logfile != NULL)) { if (strcasecmp(((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config))->logfile, ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config))->logfile) != 0) { log_file_swap = true; } } else if (((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config))->logfile != ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config))->logfile) { log_file_swap = true; } if(log_file_swap) { char *new_logfile = ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_swap_config))->logfile; #ifdef REG_TEST char *old_logfile = ((PortscanConfig *)sfPolicyUserDataGetDefault(portscan_config))->logfile; if (REG_TEST_FLAG_PORTSCAN_RELOAD & getRegTestFlags()) { printf("portscan Logfile old: %s , new: %s \n", old_logfile?old_logfile:"NULL", new_logfile?new_logfile:"NULL"); } #endif if(g_logfile) { fclose(g_logfile); g_logfile = NULL; } if(new_logfile) { g_logfile = fopen(new_logfile, "a"); if (g_logfile == NULL) { FatalError("Portscan log file '%s' could not be opened: %s.\n", new_logfile, strerror(errno)); } } } portscan_config = portscan_swap_config; return (void *)old_config; } static void PortscanReloadSwapFree(void *data) { if (data == NULL) return; PortscanFreeConfigs((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/preprocessors/spp_rpc_decode.h0000644000175000017500000000211514241077117017716 0ustar apoapo/* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2002-2013 Sourcefire, Inc. ** Copyright (C) 1998-2002 Martin Roesch ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* $Id$ */ #ifndef __SPP_RPC_DECODE_H__ #define __SPP_RPC_DECODE_H__ void SetupRpcDecode(void); #endif /* __SPP_RPC_DECODE_H__ */ snort-2.9.20/src/preprocessors/sip_common.h0000644000175000017500000000703714241077076017124 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * ** Copyright (C) 2005-2013 Sourcefire, Inc. * ** AUTHOR: Steven Sturges * ** * ** This program is free software; you can redistribute it and/or modify * ** it under the terms of the GNU General Public License Version 2 as * ** published by the Free Software Foundation. You may not use, modify or * ** distribute this program under any other version of the GNU General * ** Public License. * ** * ** 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. * */ /* sip_common.h * * Purpose: Common SIP Definition shared between preprocessors. * * Arguments: * * Effect: * * Comments: * * Any comments? * */ #ifndef SIP_COMMON_H_ #define SIP_COMMON_H_ typedef enum _SIP_method { SIP_METHOD_NULL = 0, //0x0000, SIP_METHOD_INVITE = 1, //0x0001, SIP_METHOD_CANCEL = 2, //0x0002, SIP_METHOD_ACK = 3, //0x0004, SIP_METHOD_BYE = 4, //0x0008, SIP_METHOD_REGISTER = 5, //0x0010, SIP_METHOD_OPTIONS = 6, //0x0020, SIP_METHOD_REFER = 7, //0x0040, SIP_METHOD_SUBSCRIBE = 8, //0x0080, SIP_METHOD_UPDATE = 9, //0x0100, SIP_METHOD_JOIN = 10,//0x0200, SIP_METHOD_INFO = 11,//0x0400, SIP_METHOD_MESSAGE = 12,//0x0800, SIP_METHOD_NOTIFY = 13,//0x1000, SIP_METHOD_PRACK = 14,//0x2000, SIP_METHOD_USER_DEFINE = 15,//0x4000, SIP_METHOD_USER_DEFINE_MAX = 32//0x80000000, } SIPMethodsFlag; typedef struct _SipHeaders { const char *callid; const char *from; const char *userAgent; const char *server; const char *userName; uint16_t callidLen; uint16_t fromLen; uint16_t userAgentLen; uint16_t serverLen; uint16_t userNameLen; SIPMethodsFlag methodFlag; } SipHeaders; typedef enum _SIP_DialogState { SIP_DLG_CREATE = 1, //1 SIP_DLG_INVITING, //2 SIP_DLG_EARLY, //3 SIP_DLG_AUTHENCATING, //4 SIP_DLG_ESTABLISHED, //5 SIP_DLG_REINVITING, //6 SIP_DLG_TERMINATING, //7 SIP_DLG_TERMINATED //8 } SIP_DialogState; typedef struct _SIP_MediaData { sfaddr_t maddress; // media IP uint16_t mport; // media port uint8_t numPort; // number of media ports struct _SIP_MediaData *nextM; } SIP_MediaData; typedef SIP_MediaData* SIP_MediaDataList; typedef struct _SIP_MediaSession { uint32_t sessionID; // a hash value of the session int savedFlag; // whether this data has been saved by a dialog, // if savedFlag = 1, this session will be deleted after sip message is processed. sfaddr_t maddress_default; //Default media IP SIP_MediaDataList medias; //Media list in the session struct _SIP_MediaSession *nextS; // Next media session } SIP_MediaSession; typedef SIP_MediaSession* SIP_MediaList; typedef struct _SipDialog { SIP_DialogState state; bool mediaUpdated; SIP_MediaList mediaSessions; } SipDialog; typedef struct _SipEventData { SFSnortPacket *packet; const SipHeaders *headers; const SipDialog *dialog; } SipEventData; typedef enum _SipEventType { SIP_EVENT_TYPE_SIP_DIALOG } SipEventType; #endif /* SIP_COMMON_H_ */ snort-2.9.20/src/preprocessors/Makefile.in0000644000175000017500000005466614242725547016673 0ustar apoapo# Makefile.in generated by automake 1.16.1 from Makefile.am. # @configure_input@ # Copyright (C) 1994-2018 Free Software Foundation, Inc. # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY, to the extent permitted by law; without # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. @SET_MAKE@ VPATH = @srcdir@ am__is_gnu_make = { \ if test -z '$(MAKELEVEL)'; then \ false; \ elif test -n '$(MAKE_HOST)'; then \ true; \ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ true; \ else \ false; \ fi; \ } am__make_running_with_option = \ case $${target_option-} in \ ?) ;; \ *) echo "am__make_running_with_option: internal error: invalid" \ "target option '$${target_option-}' specified" >&2; \ exit 1;; \ esac; \ has_opt=no; \ sane_makeflags=$$MAKEFLAGS; \ if $(am__is_gnu_make); then \ sane_makeflags=$$MFLAGS; \ else \ case $$MAKEFLAGS in \ *\\[\ \ ]*) \ bs=\\; \ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ esac; \ fi; \ skip_next=no; \ strip_trailopt () \ { \ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ }; \ for flg in $$sane_makeflags; do \ test $$skip_next = yes && { skip_next=no; continue; }; \ case $$flg in \ *=*|--*) continue;; \ -*I) strip_trailopt 'I'; skip_next=yes;; \ -*I?*) strip_trailopt 'I';; \ -*O) strip_trailopt 'O'; skip_next=yes;; \ -*O?*) strip_trailopt 'O';; \ -*l) strip_trailopt 'l'; skip_next=yes;; \ -*l?*) strip_trailopt 'l';; \ -[dEDm]) skip_next=yes;; \ -[JT]) skip_next=yes;; \ esac; \ case $$flg in \ *$$target_option*) has_opt=yes; break;; \ esac; \ done; \ test $$has_opt = yes am__make_dryrun = (target_option=n; $(am__make_running_with_option)) am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ pkglibexecdir = $(libexecdir)/@PACKAGE@ am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd install_sh_DATA = $(install_sh) -c -m 644 install_sh_PROGRAM = $(install_sh) -c install_sh_SCRIPT = $(install_sh) -c INSTALL_HEADER = $(INSTALL_DATA) transform = $(program_transform_name) NORMAL_INSTALL = : PRE_INSTALL = : POST_INSTALL = : NORMAL_UNINSTALL = : PRE_UNINSTALL = : POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ subdir = src/preprocessors ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/configure.in am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = LIBRARIES = $(noinst_LIBRARIES) ARFLAGS = cru AM_V_AR = $(am__v_AR_@AM_V@) am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) am__v_AR_0 = @echo " AR " $@; am__v_AR_1 = libspp_a_AR = $(AR) $(ARFLAGS) libspp_a_LIBADD = am__libspp_a_SOURCES_DIST = spp_arpspoof.c spp_arpspoof.h spp_bo.c \ spp_bo.h spp_rpc_decode.c spp_rpc_decode.h spp_perfmonitor.c \ spp_perfmonitor.h perf.c perf.h perf-base.c perf-base.h \ perf-flow.c perf-flow.h perf-event.c perf-event.h \ perf_indicators.c perf_indicators.h sfprocpidstats.c \ sfprocpidstats.h spp_httpinspect.c spp_httpinspect.h \ snort_httpinspect.c snort_httpinspect.h portscan.c portscan.h \ spp_sfportscan.c spp_sfportscan.h spp_frag3.c spp_frag3.h \ str_search.c str_search.h spp_stream6.c spp_stream6.h \ spp_session.c spp_session.h session_api.c session_api.h \ stream_api.c stream_api.h spp_normalize.c spp_normalize.h \ normalize.c normalize.h sip_common.h cip_common.h @BUILD_PROCPIDSTATS_TRUE@am__objects_1 = sfprocpidstats.$(OBJEXT) am_libspp_a_OBJECTS = spp_arpspoof.$(OBJEXT) spp_bo.$(OBJEXT) \ spp_rpc_decode.$(OBJEXT) spp_perfmonitor.$(OBJEXT) \ perf.$(OBJEXT) perf-base.$(OBJEXT) perf-flow.$(OBJEXT) \ perf-event.$(OBJEXT) perf_indicators.$(OBJEXT) \ $(am__objects_1) spp_httpinspect.$(OBJEXT) \ snort_httpinspect.$(OBJEXT) portscan.$(OBJEXT) \ spp_sfportscan.$(OBJEXT) spp_frag3.$(OBJEXT) \ str_search.$(OBJEXT) spp_stream6.$(OBJEXT) \ spp_session.$(OBJEXT) session_api.$(OBJEXT) \ stream_api.$(OBJEXT) spp_normalize.$(OBJEXT) \ normalize.$(OBJEXT) libspp_a_OBJECTS = $(am_libspp_a_OBJECTS) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false am__v_P_1 = : AM_V_GEN = $(am__v_GEN_@AM_V@) am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) am__v_GEN_0 = @echo " GEN " $@; am__v_GEN_1 = AM_V_at = $(am__v_at_@AM_V@) am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) am__v_at_0 = @ am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) depcomp = am__maybe_remake_depfiles = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_lt = $(am__v_lt_@AM_V@) am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) am__v_lt_0 = --silent am__v_lt_1 = LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) am__v_CC_0 = @echo " CC " $@; am__v_CC_1 = CCLD = $(CC) LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(AM_LDFLAGS) $(LDFLAGS) -o $@ AM_V_CCLD = $(am__v_CCLD_@AM_V@) am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) am__v_CCLD_0 = @echo " CCLD " $@; am__v_CCLD_1 = SOURCES = $(libspp_a_SOURCES) DIST_SOURCES = $(am__libspp_a_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ install-exec-recursive install-html-recursive \ install-info-recursive install-pdf-recursive \ install-ps-recursive install-recursive installcheck-recursive \ installdirs-recursive pdf-recursive ps-recursive \ tags-recursive uninstall-recursive am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ *) (install-info --version) >/dev/null 2>&1;; \ esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive am__recursive_targets = \ $(RECURSIVE_TARGETS) \ $(RECURSIVE_CLEAN_TARGETS) \ $(am__extra_recursive_targets) AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ distdir distdir-am am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) # Read a list of newline-separated strings from the standard input, # and print each of them once, without duplicates. Input order is # *not* preserved. am__uniquify_input = $(AWK) '\ BEGIN { nonempty = 0; } \ { items[$$0] = 1; nonempty = 1; } \ END { if (nonempty) { for (i in items) print i; }; } \ ' # Make sure the list of sources is unique. This is necessary because, # e.g., the same source file might be shared among _SOURCES variables # for different programs/libraries. am__define_uniq_tagged_files = \ list='$(am__tagged_files)'; \ unique=`for i in $$list; do \ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) am__DIST_COMMON = $(srcdir)/Makefile.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ sed_first='s,^\([^/]*\)/.*$$,\1,'; \ sed_rest='s,^[^/]*/*,,'; \ sed_last='s,^.*/\([^/]*\)$$,\1,'; \ sed_butlast='s,/*[^/]*$$,,'; \ while test -n "$$dir1"; do \ first=`echo "$$dir1" | sed -e "$$sed_first"`; \ if test "$$first" != "."; then \ if test "$$first" = ".."; then \ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ else \ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ if test "$$first2" = "$$first"; then \ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ else \ dir2="../$$dir2"; \ fi; \ dir0="$$dir0"/"$$first"; \ fi; \ fi; \ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ done; \ reldir="$$dir2" ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ AWK = @AWK@ CC = @CC@ CCDEPMODE = @CCDEPMODE@ CCONFIGFLAGS = @CCONFIGFLAGS@ CFLAGS = @CFLAGS@ CONFIGFLAGS = @CONFIGFLAGS@ CPP = @CPP@ CPPFLAGS = @CPPFLAGS@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ GREP = @GREP@ ICONFIGFLAGS = @ICONFIGFLAGS@ INCLUDES = @INCLUDES@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ INSTALL_SCRIPT = @INSTALL_SCRIPT@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ LD = @LD@ LDFLAGS = @LDFLAGS@ LEX = @LEX@ LIBOBJS = @LIBOBJS@ LIBS = @LIBS@ LIBTOOL = @LIBTOOL@ LIPO = @LIPO@ LN_S = @LN_S@ LTLIBOBJS = @LTLIBOBJS@ LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ LUA_CFLAGS = @LUA_CFLAGS@ LUA_LIBS = @LUA_LIBS@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NM = @NM@ NMEDIT = @NMEDIT@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTOOL = @OTOOL@ OTOOL64 = @OTOOL64@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ RANLIB = @RANLIB@ SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ SIGNAL_SNORT_DUMP_STATS = @SIGNAL_SNORT_DUMP_STATS@ SIGNAL_SNORT_READ_ATTR_TBL = @SIGNAL_SNORT_READ_ATTR_TBL@ SIGNAL_SNORT_RELOAD = @SIGNAL_SNORT_RELOAD@ SIGNAL_SNORT_ROTATE_STATS = @SIGNAL_SNORT_ROTATE_STATS@ STRIP = @STRIP@ VERSION = @VERSION@ XCCFLAGS = @XCCFLAGS@ YACC = @YACC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ build_cpu = @build_cpu@ build_os = @build_os@ build_vendor = @build_vendor@ builddir = @builddir@ datadir = @datadir@ datarootdir = @datarootdir@ docdir = @docdir@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ extra_incl = @extra_incl@ host = @host@ host_alias = @host_alias@ host_cpu = @host_cpu@ host_os = @host_os@ host_vendor = @host_vendor@ htmldir = @htmldir@ includedir = @includedir@ infodir = @infodir@ install_sh = @install_sh@ libdir = @libdir@ libexecdir = @libexecdir@ localedir = @localedir@ localstatedir = @localstatedir@ luajit_CFLAGS = @luajit_CFLAGS@ luajit_LIBS = @luajit_LIBS@ mandir = @mandir@ mkdir_p = @mkdir_p@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ AUTOMAKE_OPTIONS = foreign no-dependencies noinst_LIBRARIES = libspp.a SUBDIRS = HttpInspect Stream6 Session @BUILD_PROCPIDSTATS_TRUE@PROCPIDSTATS_SOURCE = sfprocpidstats.c sfprocpidstats.h libspp_a_SOURCES = spp_arpspoof.c spp_arpspoof.h spp_bo.c spp_bo.h \ spp_rpc_decode.c spp_rpc_decode.h \ spp_perfmonitor.c spp_perfmonitor.h \ perf.c perf.h \ perf-base.c perf-base.h \ perf-flow.c perf-flow.h \ perf-event.c perf-event.h \ perf_indicators.c perf_indicators.h \ $(PROCPIDSTATS_SOURCE) \ spp_httpinspect.c spp_httpinspect.h \ snort_httpinspect.c snort_httpinspect.h \ portscan.c portscan.h \ spp_sfportscan.c spp_sfportscan.h \ spp_frag3.c spp_frag3.h \ str_search.c str_search.h \ spp_stream6.c spp_stream6.h \ spp_session.c spp_session.h \ session_api.c session_api.h \ stream_api.c stream_api.h \ spp_normalize.c spp_normalize.h \ normalize.c normalize.h \ sip_common.h cip_common.h all: all-recursive .SUFFIXES: .SUFFIXES: .c .lo .o .obj $(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ && { if test -f $@; then exit 0; else break; fi; }; \ exit 1;; \ esac; \ done; \ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/preprocessors/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --foreign src/preprocessors/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(top_srcdir)/configure: $(am__configure_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(ACLOCAL_M4): $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): clean-noinstLIBRARIES: -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) libspp.a: $(libspp_a_OBJECTS) $(libspp_a_DEPENDENCIES) $(EXTRA_libspp_a_DEPENDENCIES) $(AM_V_at)-rm -f libspp.a $(AM_V_AR)$(libspp_a_AR) libspp.a $(libspp_a_OBJECTS) $(libspp_a_LIBADD) $(AM_V_at)$(RANLIB) libspp.a mostlyclean-compile: -rm -f *.$(OBJEXT) distclean-compile: -rm -f *.tab.c .c.o: $(AM_V_CC)$(COMPILE) -c -o $@ $< .c.obj: $(AM_V_CC)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: $(AM_V_CC)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd # into them and run 'make' without going through this Makefile. # To change the values of 'make' variables: instead of editing Makefiles, # (1) if the variable is set in 'config.status', edit 'config.status' # (which will cause the Makefiles to be regenerated when you run 'make'); # (2) otherwise, pass the desired values on the 'make' command line. $(am__recursive_targets): @fail=; \ if $(am__make_keepgoing); then \ failcom='fail=yes'; \ else \ failcom='exit 1'; \ fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ case "$@" in \ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ *) list='$(SUBDIRS)' ;; \ esac; \ for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ local_target="$$target-am"; \ else \ local_target="$$target"; \ fi; \ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ || eval $$failcom; \ done; \ if test "$$dot_seen" = "no"; then \ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-recursive TAGS: tags tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ include_option=--etags-include; \ empty_fix=.; \ else \ include_option=--include; \ empty_fix=; \ fi; \ list='$(SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ test ! -f $$subdir/TAGS || \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ if test $$# -gt 0; then \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ "$$@" $$unique; \ else \ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ $$unique; \ fi; \ fi ctags: ctags-recursive CTAGS: ctags ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" cscopelist: cscopelist-recursive cscopelist-am: $(am__tagged_files) list='$(am__tagged_files)'; \ case "$(srcdir)" in \ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ *) sdir=$(subdir)/$(srcdir) ;; \ esac; \ for i in $$list; do \ if test -f "$$i"; then \ echo "$(subdir)/$$i"; \ else \ echo "$$sdir/$$i"; \ fi; \ done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ dist_files=`for file in $$list; do echo $$file; done | \ sed -e "s|^$$srcdirstrip/||;t" \ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ case $$dist_files in \ */*) $(MKDIR_P) `echo "$$dist_files" | \ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ sort -u` ;; \ esac; \ for file in $$dist_files; do \ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ if test -d $$d/$$file; then \ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ if test -d "$(distdir)/$$file"; then \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ fi; \ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ else \ test -f "$(distdir)/$$file" \ || cp -p $$d/$$file "$(distdir)/$$file" \ || exit 1; \ fi; \ done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ $(am__make_dryrun) \ || test -d "$(distdir)/$$subdir" \ || $(MKDIR_P) "$(distdir)/$$subdir" \ || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ dir1=$$subdir; dir2="$(top_distdir)"; \ $(am__relativize); \ new_top_distdir=$$reldir; \ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ ($(am__cd) $$subdir && \ $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$$new_top_distdir" \ distdir="$$new_distdir" \ am__remove_distdir=: \ am__skip_length_check=: \ am__skip_mode_fix=: \ distdir) \ || exit 1; \ fi; \ done check-am: all-am check: check-recursive all-am: Makefile $(LIBRARIES) installdirs: installdirs-recursive installdirs-am: install: install-recursive install-exec: install-exec-recursive install-data: install-data-recursive uninstall: uninstall-recursive install-am: all-am @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am installcheck: installcheck-recursive install-strip: if test -z '$(STRIP)'; then \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ install; \ else \ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ fi mostlyclean-generic: clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." clean: clean-recursive clean-am: clean-generic clean-libtool clean-noinstLIBRARIES \ mostlyclean-am distclean: distclean-recursive -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags dvi: dvi-recursive dvi-am: html: html-recursive html-am: info: info-recursive info-am: install-data-am: install-dvi: install-dvi-recursive install-dvi-am: install-exec-am: install-html: install-html-recursive install-html-am: install-info: install-info-recursive install-info-am: install-man: install-pdf: install-pdf-recursive install-pdf-am: install-ps: install-ps-recursive install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-recursive -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic mostlyclean: mostlyclean-recursive mostlyclean-am: mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf: pdf-recursive pdf-am: ps: ps-recursive ps-am: uninstall-am: .MAKE: $(am__recursive_targets) install-am install-strip .PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ check-am clean clean-generic clean-libtool \ clean-noinstLIBRARIES cscopelist-am ctags ctags-am distclean \ distclean-compile distclean-generic distclean-libtool \ distclean-tags distdir dvi dvi-am html html-am info info-am \ install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ installdirs-am maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-compile mostlyclean-generic \ mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ uninstall-am .PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: snort-2.9.20/src/preprocessors/spp_arpspoof.h0000644000175000017500000000224014241077102017451 0ustar apoapo/* $Id$ */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2003-2013 Sourcefire, Inc. ** Copyright (C) 2001-2003 Jeff Nathan ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* Snort ARPspoof Preprocessor Plugin * by Jeff Nathan * Version 0.1.3 */ #ifndef __SPP_ARPSPOOF_H__ #define __SPP_ARPSPOOF_H__ void SetupARPspoof(void); #endif /* __SPP_ARPSPOOF_H__ */ snort-2.9.20/src/preprocessors/spp_frag3.c0000644000175000017500000050163514241077105016634 0ustar apoapo/* $Id$ */ /** * @file spp_frag3.c * @author Martin Roesch * @date Thu Sep 30 14:12:37 EDT 2004 * * @brief Frag3: IP defragmentation preprocessor for Snort. */ /* ** Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. ** Copyright (C) 2004-2013 Sourcefire, Inc. ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License Version 2 as ** published by the Free Software Foundation. You may not use, modify or ** distribute this program under any other version of the GNU General ** Public License. ** ** 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. */ /* * Notes: * Frag3 sports the following improvements over frag2: * - Target-based IP defragmentation, harder to evade * - 8 Anomaly detection event types * - Two separate memory management strategies to tailor * performance for specific environments * - Up to 250% faster than frag2. * * The mechanism for processing frags is based on the Linux IP stack * implementation of IP defragmentation with proper amounts of paranoia * and an IDS perspective applied. Some of this code was derived from * frag2 originally, but it's basically unrecognizeable if you compare * it to frag2 IMO. * * I switched from using the UBI libs to using sfxhash and linked lists for * fragment management because I suspected that the management code was * the cause of performance issues that we were observing at Sourcefire * in certain customer situations. Splay trees are cool and really hard * to screw with from an attack perspective, but they also incur a lot * of overhead for managing the tree and lose the order of the fragments in * the FragTracker's fraglist, so I dropped them. Originally the * frag3 code was just supposed to migrate away from the splay tree system * that I was using in frag2, but I figured since I was doing the work to * pull out the splay trees I may as well solve some of the other problems * we were seeing. * * Initial performance testing that I've done shows that frag3 can be as much * as 250% faster than frag2, but we still need to do more testing and * optimization, we may be able to squeeze out some more performance. * * Frag3 is also capable of performing "Target-based" IP defragmentation. * What this means practically is that frag3 can model the IP stack of a * target on the network to avoid Ptacek-Newsham evasions of the IDS through * sensor/target desynchronization. In terms of implentation, this is * reflected by passing a "context" into the defragmentation engine that has * a specific configuration for a specific target type. Windows can put * fragments back together differently than Linux/BSD/etc, so we model that * inside frag3 so we can't be evaded. * * Configuration of frag3 is pretty straight forward, there's a global config * that contains data about how the hash tables will be structured, what type * of memory management to use and whether or not to generate alerts, then * specific target-contexts are setup and bound to IP address sets. Check * the README file for specifics! */ /* I N C L U D E S ************************************************/ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "spp_frag3.h" #include "snort_bounds.h" #include "generators.h" #include "log.h" #include "detect.h" #include "decode.h" #include "encode.h" #include "event.h" #include "util.h" #include "snort_debug.h" #include "plugbase.h" #include "parser.h" #include "mstring.h" #include "checksum.h" #include "perf.h" #include "event_queue.h" #include "timersub.h" #include "fpcreate.h" #include "sfutil/sflsq.h" #include "sfutil/sfxhash.h" #include "snort.h" #include "memory_stats.h" #include "profiler.h" #include "active.h" #include "session_api.h" #include "spp_normalize.h" #include "reload.h" #ifdef REG_TEST #include "reg_test.h" #endif #ifdef TARGET_BASED #include "sftarget_hostentry.h" #include "sftarget_protocol_reference.h" #endif #include "sfPolicy.h" extern OptTreeNode *otn_tmp; /* D E F I N E S **************************************************/ #define PP_FRAG3_PRIORITY PRIORITY_CORE + PP_CORE_ORDER_FRAG3 /* flags for the FragTracker->frag_flags field */ #define FRAG_GOT_FIRST 0x00000001 #define FRAG_GOT_LAST 0x00000002 #define FRAG_REBUILT 0x00000004 #define FRAG_BAD 0x00000008 #define FRAG_NO_BSD_VULN 0x00000010 #define FRAG_DROP_FRAGMENTS 0x00000020 /* default frag timeout, 90-120 might be better values, can we do * target-based quanta? */ #define FRAG_PRUNE_QUANTA 60 /* default 4MB memcap */ #define FRAG_MEMCAP 4194304 /* min acceptable ttl (should be 1?) */ #define FRAG3_MIN_TTL 1 /* target-based defragmentation policy enums */ #define FRAG_POLICY_FIRST 1 #define FRAG_POLICY_LINUX 2 #define FRAG_POLICY_BSD 3 #define FRAG_POLICY_BSD_RIGHT 4 #define FRAG_POLICY_LAST 5 /* Combo of FIRST & LAST, depending on overlap situation. */ #define FRAG_POLICY_WINDOWS 6 /* Combo of FIRST & LAST, depending on overlap situation. */ #define FRAG_POLICY_SOLARIS 7 #define FRAG_POLICY_DEFAULT FRAG_POLICY_BSD /* max packet size */ #define DATASIZE (ETHERNET_HEADER_LEN+IP_MAXPACKET) /* max frags in a single frag tracker */ #define DEFAULT_MAX_FRAGS 8192 /*max preallocated frags */ #define MAX_PREALLOC_FRAGS 50000 #define MIN_FRAG_MEMCAP 16384 /* return values for CheckTimeout() */ #define FRAG_TIME_OK 0 #define FRAG_TIMEOUT 1 /* return values for Frag3Insert() */ #define FRAG_INSERT_OK 0 #define FRAG_INSERT_FAILED 1 #define FRAG_INSERT_REJECTED 2 #define FRAG_INSERT_TIMEOUT 3 #define FRAG_INSERT_ATTACK 4 #define FRAG_INSERT_ANOMALY 5 #define FRAG_INSERT_TTL 6 #define FRAG_INSERT_OVERLAP_LIMIT 7 /* return values for Frag3CheckFirstLast() */ #define FRAG_FIRSTLAST_OK 0 #define FRAG_LAST_DUPLICATE 1 /* return values for Frag3Expire() */ #define FRAG_OK 0 #define FRAG_TRACKER_TIMEOUT 1 #define FRAG_LAST_OFFSET_ADJUST 2 /* flag for detecting attacks/alerting */ #define FRAG3_DETECT_ANOMALIES 0x01 /* D A T A S T R U C T U R E S **********************************/ /* runtime context for a specific instance of an engine */ typedef struct _Frag3Context { uint16_t frag_policy; /* policy to use for target-based reassembly */ uint32_t frag_timeout; /* timeout for frags in this policy */ uint8_t min_ttl; /* Minimum TTL to accept */ char frag3_alerts; /* Whether or not frag3 alerts are enabled */ IpAddrSet *bound_addrs; /* addresses bound to this context */ /**limit on number of fragments before excessive fragmentation event is generated. */ uint32_t overlap_limit; /**Fragment that is too small to be legal */ uint32_t min_fragment_length; } Frag3Context; /* struct to manage an individual fragment */ typedef struct _Frag3Frag { uint8_t *data; /* ptr to adjusted start position */ uint16_t size; /* adjusted frag size */ uint16_t offset; /* adjusted offset position */ uint8_t *fptr; /* free pointer */ uint16_t flen; /* free len, unneeded? */ struct _Frag3Frag *prev; struct _Frag3Frag *next; int ord; char last; } Frag3Frag; typedef struct _fragkey { uint32_t sip[4]; uint32_t dip[4]; uint32_t id; uint16_t vlan_tag; uint8_t proto; /* IP protocol, unused for IPv6 */ uint8_t ipver; /* Version */ #ifdef MPLS uint32_t mlabel; /* For 64 bit alignment since this is allocated in front of a FragTracker * and the structures are laid on top of that allocated memory */ #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t address_space_id_src; uint16_t address_space_id_dst; #else uint16_t addressSpaceId; uint16_t addressSpaceIdPad1; #endif #else uint32_t mpad; #endif #else #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) uint16_t address_space_id_src; uint16_t address_space_id_dst; #else uint16_t addressSpaceId; uint16_t addressSpaceIdPad1; #endif uint32_t addressSpaceIdPad2; #endif #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) uint32_t carrierId; #endif } FRAGKEY; /* Only track a certain number of alerts per session */ #define MAX_FRAG_ALERTS 8 /* global configuration data struct for this preprocessor */ typedef struct _Frag3Config { int disabled; uint32_t max_frags; /* max frags to track */ unsigned long memcap; /* memcap for frag3 */ int ten_percent; /* holder for self preservation data */ uint32_t static_frags; /* static frag nodes to keep around */ uint8_t use_prealloc; /* flag to indicate prealloc nodes in use */ uint8_t use_prealloc_frags; /* flag to indicate prealloc nodes in use */ Frag3Context *default_context; Frag3Context **frag3ContextList; /* List of Frag3 Contexts configured */ uint8_t numFrag3Contexts; uint32_t ref_count; } Frag3Config; /* tracker for a fragmented packet set */ typedef struct _FragTracker { uint32_t sip[4]; uint32_t dip[4]; uint32_t id; /* IP ID */ uint8_t protocol; /* IP protocol */ uint8_t ipver; /* Version */ uint8_t ttl; /* ttl used to detect evasions */ uint8_t alerted; uint32_t frag_flags; /* bit field */ uint32_t frag_bytes; /* number of fragment bytes stored, based * on aligned fragment offsets/sizes */ uint32_t calculated_size; /* calculated size of reassembled pkt, based on * last frag offset */ uint32_t frag_pkts; /* nummber of frag pkts stored under this tracker */ struct timeval frag_time; /* time we started tracking this frag */ Frag3Frag *fraglist; /* list of fragments */ Frag3Frag *fraglist_tail; /* tail ptr for easy appending */ int fraglist_count; /* handy dandy counter */ uint32_t alert_gid[MAX_FRAG_ALERTS]; /* flag alerts seen in a frag list */ uint32_t alert_sid[MAX_FRAG_ALERTS]; /* flag alerts seen in a frag list */ uint8_t alert_count; /* count alerts seen in a frag list */ uint32_t ip_options_len; /* length of ip options for this set of frags */ uint32_t ip_option_count; /* number of ip options for this set of frags */ uint8_t *ip_options_data; /* ip options from offset 0 packet */ uint32_t copied_ip_options_len; /* length of 'copied' ip options */ uint32_t copied_ip_option_count; /* number of 'copied' ip options */ Frag3Context *context; int ordinal; #ifdef TARGET_BASED int ipprotocol; int application_protocol; #endif uint32_t frag_policy; /**Count of IP fragment overlap for each packet id. */ uint32_t overlap_count; /* Configuration in use when this tracker was created */ tSfPolicyId policy_id; tSfPolicyUserContextId config; } FragTracker; /* statistics tracking struct */ typedef struct _Frag3Stats { uint32_t total; uint32_t overlaps; uint32_t reassembles; uint32_t prunes; uint32_t timeouts; uint32_t fragtrackers_created; uint32_t fragtrackers_released; uint32_t fragtrackers_autoreleased; uint32_t fragnodes_created; uint32_t fragnodes_released; uint32_t discards; uint32_t anomalies; uint32_t alerts; uint32_t drops; } Frag3Stats; /* G L O B A L S **************************************************/ static tSfPolicyUserContextId frag3_config = NULL; /* current configuration */ /* Config to use to evaluate * If a frag tracker is found in the hash table, the configuration under * which it was created will be used */ static Frag3Config *frag3_eval_config = NULL; static SFXHASH *f_cache = NULL; /* fragment hash table */ static Frag3Frag *prealloc_frag_list = NULL; /* head for prealloc queue */ static unsigned long frag3_mem_in_use = 0; /* memory in use, used for self pres */ static uint32_t prealloc_nodes_in_use; /* counter for debug */ static Frag3Stats f3stats; /* stats struct */ static Packet* defrag_pkt = NULL; #ifdef GRE static Packet* encap_defrag_pkt = NULL; #endif static uint32_t pkt_snaplen = 0; static uint32_t old_static_frags; static unsigned long hashTableSize; static unsigned long fcache_new_memcap; /* enum for policy names */ static char *frag_policy_names[] = { "no policy!", "FIRST", "LINUX", "BSD", "BSD_RIGHT", "LAST", "WINDOWS", "SOLARIS"}; #ifdef PERF_PROFILING PreprocStats frag3PerfStats; PreprocStats frag3InsertPerfStats; PreprocStats frag3RebuildPerfStats; #endif /* * external globals for startup */ extern char *file_name; extern int file_line; /* P R O T O T Y P E S ********************************************/ static void Frag3ParseGlobalArgs(Frag3Config *, char *); static void Frag3ParseArgs(struct _SnortConfig *, char *, Frag3Context *); static inline int Frag3Expire(Packet *, FragTracker *, Frag3Context *); static FragTracker *Frag3GetTracker(Packet *, FRAGKEY *); static int Frag3NewTracker(Packet *p, FRAGKEY *fkey, Frag3Context *); static int Frag3Insert(Packet *, FragTracker *, FRAGKEY *, Frag3Context *); static void Frag3Rebuild(FragTracker *, Packet *); static inline int Frag3IsComplete(FragTracker *); static int Frag3HandleIPOptions(FragTracker *, Packet *); static void Frag3PrintStats(int); static void Frag3FreeConfig(Frag3Config *); static void Frag3FreeConfigs(tSfPolicyUserContextId); #ifdef SNORT_RELOAD static void Frag3ReloadGlobal(struct _SnortConfig *, char *, void **); static void Frag3ReloadEngine(struct _SnortConfig *, char *, void **); static int Frag3ReloadVerify(struct _SnortConfig *, void *); static void * Frag3ReloadSwap(struct _SnortConfig *, void *); static void Frag3ReloadSwapFree(void *); static uint32_t Frag3MemReloadAdjust(unsigned); static bool Frag3ReloadAdjust(bool, tSfPolicyId, void *); #endif /* deletion funcs */ static int Frag3Prune(FragTracker *); static struct timeval *pkttime; /* packet timestamp */ static void Frag3DeleteFrag(Frag3Frag *); static void Frag3RemoveTracker(void *, void *); static void Frag3DeleteTracker(FragTracker *); static int Frag3AutoFree(void *, void *); static int Frag3UserFree(void *, void *); /* fraglist handler funcs */ static inline void Frag3FraglistAddNode(FragTracker *, Frag3Frag *, Frag3Frag *); static inline void Frag3FraglistDeleteNode(FragTracker *, Frag3Frag *); /* prealloc queue handler funcs */ static inline Frag3Frag *Frag3PreallocPop(); static inline void Frag3PreallocPush(Frag3Frag *); /* main preprocessor functions */ static void Frag3Defrag(Packet *, void *); static void Frag3CleanExit(int, void *); static void Frag3Reset(int, void *); static void Frag3ResetStats(int, void *); static void Frag3Init(struct _SnortConfig *, char *); static void Frag3GlobalInit(struct _SnortConfig *, char *); static int Frag3VerifyConfig(struct _SnortConfig *); static void Frag3PostConfigInit(struct _SnortConfig *, void *); char *FragIPToStr(uint32_t ip[4], uint8_t proto) { char *ret_str; sfaddr_t srcip; sfip_set_raw(&srcip, ip, proto == 4 ? AF_INET : AF_INET6); ret_str = sfip_to_str(&srcip); return ret_str; } #ifdef DEBUG_FRAG3 /** * Print out a FragTracker structure * * @param ft Pointer to the FragTracker to print * * @return none */ static void PrintFragTracker(FragTracker *ft) { LogMessage("FragTracker %p\n", ft); if(ft) { LogMessage(" sip: %s\n", FragIPToStr(ft->sip, ft->ipver)); LogMessage(" dip: %s\n", FragIPToStr(ft->dip, ft->ipver)); LogMessage(" id: %d\n", ft->id); LogMessage(" proto: 0x%X\n", ft->protocol); LogMessage(" ipver: 0x%X\n", ft->ipver); LogMessage(" ttl: %d\n", ft->ttl); LogMessage(" alerted: %d\n", ft->alerted); LogMessage(" frag_flags: 0x%X\n", ft->frag_flags); LogMessage(" frag_bytes: %d\n", ft->frag_bytes); LogMessage(" calc_size: %d\n", ft->calculated_size); LogMessage(" frag_pkts: %d\n", ft->frag_pkts); LogMessage(" frag_time: %lu %lu\n", ft->frag_time.tv_sec, ft->frag_time.tv_usec); LogMessage(" fraglist: %p\n", ft->fraglist); LogMessage(" fl_tail: %p\n", ft->fraglist_tail); LogMessage("fraglst cnt: %d\n", ft->fraglist_count); } } /** * Print out a FragKey structure * * @param fkey Pointer to the FragKey to print * * @return none */ static void PrintFragKey(FRAGKEY *fkey) { LogMessage("FragKey %p\n", fkey); if(fkey) { LogMessage(" sip: %s\n", FragIPToStr(fkey->sip, fkey->ipver)); LogMessage(" dip: %s\n", FragIPToStr(fkey->dip, fkey->ipver)); LogMessage(" id: %d\n", fkey->id); LogMessage(" proto: 0x%X\n", fkey->proto); #ifdef MPLS LogMessage(" mlabel: 0x%08X\n", fkey->mlabel); #endif #ifdef HAVE_DAQ_ADDRESS_SPACE_ID LogMessage(" addr id: %d\n", fkey->addressSpaceId); #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) LogMessage(" carrier id: %u\n", fkey->carrierId); #endif } } /** * Print out a Frag3Frag structure * * @param f Pointer to the Frag3Frag to print * * @return none */ static void PrintFrag3Frag(Frag3Frag *f) { LogMessage("Frag3Frag: %p\n", f); if(f) { LogMessage(" data: %p\n", f->data); LogMessage(" size: %d\n", f->size); LogMessage(" offset: %d\n", f->offset); LogMessage(" fptr: %p\n", f->fptr); LogMessage(" flen: %d\n", f->flen); LogMessage(" prev: %p\n", f->prev); LogMessage(" next: %p\n", f->next); } } #endif /* DEBUG_FRAG3 */ /** * Print out the global runtime configuration * * @param None * * @return none */ static void Frag3PrintGlobalConfig(Frag3Config *gconfig) { if (gconfig == NULL) return; LogMessage("Frag3 global config:\n"); if(gconfig->disabled) { LogMessage(" Frag3: INACTIVE\n"); } LogMessage(" Max frags: %d\n", gconfig->max_frags); if(!gconfig->use_prealloc) LogMessage(" Fragment memory cap: %lu bytes\n", gconfig->memcap); else { if (gconfig->static_frags) LogMessage(" Preallocated frag nodes: %u\n", gconfig->static_frags); if (!gconfig->use_prealloc_frags) LogMessage(" Memory cap used to determine preallocated frag nodes: %lu\n", gconfig->memcap); } } /** * Print out a defrag engine runtime context * * @param context Pointer to the context structure to print * * @return none */ static void Frag3PrintEngineConfig(Frag3Context *context) { LogMessage("Frag3 engine config:\n"); if (context->bound_addrs != NULL) { IpAddrSetPrint(" Bound Addresses: ", context->bound_addrs); } else { LogMessage(" Bound Address: default\n"); } LogMessage(" Target-based policy: %s\n", frag_policy_names[context->frag_policy]); LogMessage(" Fragment timeout: %d seconds\n", context->frag_timeout); LogMessage(" Fragment min_ttl: %d\n", context->min_ttl); LogMessage(" Fragment Anomalies: %s\n", context->frag3_alerts ? "Alert" : "No Alert"); LogMessage(" Overlap Limit: %d\n", context->overlap_limit); LogMessage(" Min fragment Length: %d\n", context->min_fragment_length); } /** * Generate an event due to IP options being detected in a frag packet * * @param context Current run context * * @return none */ static inline void EventAnomIpOpts(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3, /* GID */ FRAG3_IPOPTIONS, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_IPOPTIONS_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; } /** * Generate an event due to a Teardrop-style attack detected in a frag packet * * @param context Current run context * * @return none */ static inline void EventAttackTeardrop(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3, /* GID */ FRAG3_TEARDROP, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_TEARDROP_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; } /** * Generate an event for very small fragment * * @param context Current run context * * @return none */ static inline void EventTinyFragments(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3, /* GID */ FRAG3_TINY_FRAGMENT, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_TINY_FRAGMENT_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; } /** * Generate an event due to excessive fragment overlap detected in a frag packet * * @param context Current run context * * @return none */ static inline void EventExcessiveOverlap(Frag3Context *context) { //@TBD dschahal do I need this if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3, /* GID */ FRAG3_EXCESSIVE_OVERLAP, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_EXCESSIVE_OVERLAP_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; } /** * Generate an event due to a fragment being too short, typcially based * on a non-last fragment that doesn't properly end on an 8-byte boundary * * @param context Current run context * * @return none */ static inline void EventAnomShortFrag(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3, /* GID */ FRAG3_SHORT_FRAG, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_SHORT_FRAG_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; f3stats.anomalies++; } /** * This fragment's size will end after the already calculated reassembled * fragment end, as in a Bonk/Boink/etc attack. * * @param context Current run context * * @return none */ static inline void EventAnomOversize(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3,/* GID */ FRAG3_ANOMALY_OVERSIZE, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_ANOM_OVERSIZE_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; f3stats.anomalies++; } /** * The current fragment will be inserted with a size of 0 bytes, that's * an anomaly if I've ever seen one. * * @param context Current run context * * @return none */ static inline void EventAnomZeroFrag(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3,/* GID */ FRAG3_ANOMALY_ZERO, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_ANOM_ZERO_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; f3stats.anomalies++; } /** * The reassembled packet will be bigger than 64k, generate an event. * * @param context Current run context * * @return none */ static inline void EventAnomBadsizeLg(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3,/* GID */ FRAG3_ANOMALY_BADSIZE_LG, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_ANOM_BADSIZE_LG_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; f3stats.anomalies++; } /** * Fragment size is negative after insertion (end < offset). * * @param context Current run context * * @return none */ static inline void EventAnomBadsizeSm(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3,/* GID */ FRAG3_ANOMALY_BADSIZE_SM, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_ANOM_BADSIZE_SM_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; f3stats.anomalies++; } /** * There is an overlap with this fragment, someone is probably being naughty. * * @param context Current run context * * @return none */ static inline void EventAnomOverlap(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3,/* GID */ FRAG3_ANOMALY_OVLP, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_ANOM_OVLP_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; f3stats.anomalies++; } /** * Generate an event due to TTL below the configured minimum * * @param context Current run context * * @return none */ static inline void EventAnomScMinTTL(Frag3Context *context) { if(!(context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) return; SnortEventqAdd(GENERATOR_SPP_FRAG3, /* GID */ FRAG3_MIN_TTL_EVASION, /* SID */ 1, /* rev */ 0, /* classification enum */ 3, /* priority (low) */ FRAG3_MIN_TTL_EVASION_STR, /* event message */ NULL); /* rule info ptr */ f3stats.alerts++; } int frag3_print_mem_stats(FILE *fd, char* buffer, PreprocMemInfo *meminfo) { int len = 0; time_t curr_time; if (fd) { len = fprintf(fd, ",%lu,%u" ",%lu,%u,%u" ",%lu,%u,%u,%lu" , frag3_mem_in_use , prealloc_nodes_in_use , meminfo[PP_MEM_CATEGORY_SESSION].used_memory , meminfo[PP_MEM_CATEGORY_SESSION].num_of_alloc , meminfo[PP_MEM_CATEGORY_SESSION].num_of_free , meminfo[PP_MEM_CATEGORY_CONFIG].used_memory , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_alloc , meminfo[PP_MEM_CATEGORY_CONFIG].num_of_free , meminfo[PP_MEM_CATEGORY_SESSION].used_memory + meminfo[PP_MEM_CATEGORY_CONFIG].used_memory); return len; } curr_time = time(NULL); if (buffer) { len = snprintf(buffer, CS_STATS_BUF_SIZE, "\n\nMemory Statistics of Frag3 on: %s\n" " Memory in use : %lu\n" " prealloc nodes in use : %u\n\n" , ctime(&curr_time) , frag3_mem_in_use , prealloc_nodes_in_use); } else { LogMessage("\n"); LogMessage("Memory Statistics of Frag3 on: %s\n", ctime(&curr_time)); LogMessage(" Memory in use : %lu\n", frag3_mem_in_use); LogMessage(" prealloc nodes in use : %u\n\n", prealloc_nodes_in_use); } return len; } /** * Main setup function to register frag3 with the rest of Snort. * * @param none * * @return none */ void SetupFrag3(void) { RegisterMemoryStatsFunction(PP_FRAG3, frag3_print_mem_stats); #ifndef SNORT_RELOAD RegisterPreprocessor("frag3_global", Frag3GlobalInit); RegisterPreprocessor("frag3_engine", Frag3Init); #else RegisterPreprocessor("frag3_global", Frag3GlobalInit, Frag3ReloadGlobal, Frag3ReloadVerify, Frag3ReloadSwap, Frag3ReloadSwapFree); RegisterPreprocessor("frag3_engine", Frag3Init, Frag3ReloadEngine, NULL, NULL, NULL); #endif DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Preprocessor: frag3 is setup...\n");); } uint32_t Frag3KeyHashFunc(SFHASHFCN *p, unsigned char *d, int n) { uint32_t a,b,c; uint32_t offset = 0; #ifdef MPLS uint32_t tmp = 0; #endif #ifdef HAVE_DAQ_ADDRESS_SPACE_ID uint32_t tmp2 = 0; #endif a = *(uint32_t *)d; /* IPv6 sip[0] */ b = *(uint32_t *)(d+4); /* IPv6 sip[1] */ c = *(uint32_t *)(d+8); /* IPv6 sip[2] */ mix(a,b,c); a += *(uint32_t *)(d+12); /* IPv6 sip[3] */ b += *(uint32_t *)(d+16); /* IPv6 dip[0] */ c += *(uint32_t *)(d+20); /* IPv6 dip[1] */ mix(a,b,c); a += *(uint32_t *)(d+24); /* IPv6 dip[2] */ b += *(uint32_t *)(d+28); /* IPv6 dip[3] */ c += *(uint32_t *)(d+32); /* IPv6 id */ mix(a,b,c); offset = 36; a += *(uint32_t *)(d+offset); /* vlan, proto, ipver */ #ifdef MPLS tmp = *(uint32_t*)(d+offset+4); if( tmp ) { b += tmp; /* mpls label */ } offset += 8; /* skip past vlan/proto/ipver & mpls label */ #else offset += 4; /* skip past vlan/proto/ipver */ #endif #ifdef HAVE_DAQ_ADDRESS_SPACE_ID tmp2 = *(uint32_t*)(d+offset); /* after offset that has been moved */ c += tmp2; /* address space id and 16bits of zero'd pad */ #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) mix(a,b,c); a += *(uint32_t*)(d+offset+4); #endif final(a,b,c); return c; } int Frag3KeyCmpFunc(const void *s1, const void *s2, size_t n) { #ifndef SPARCV9 /* ie, everything else, use 64bit comparisons */ uint64_t *a, *b; a = (uint64_t*)s1; b = (uint64_t*)s2; if (*a - *b) return 1; /* Compares IPv4 sip/dip */ /* Compares IPv6 sip[0,1] */ a++; b++; if (*a - *b) return 1; /* Compares IPv6 sip[2,3] */ a++; b++; if (*a - *b) return 1; /* Compares IPv6 dip[0,1] */ a++; b++; if (*a - *b) return 1; /* Compares IPv6 dip[2,3] */ a++; b++; if (*a - *b) return 1; /* Compares IPv4 id/pad, vlan/proto/ipver */ /* Compares IPv6 id, vlan/proto/ipver */ #ifdef MPLS a++; b++; #ifdef HAVE_DAQ_ADDRESS_SPACE_ID if (*a - *b) return 1; /* Compares MPLS label, AddressSpace ID and 16bit pad */ #else { uint32_t *x, *y; x = (uint32_t *)a; y = (uint32_t *)b; //x++; //y++; if (*x - *y) return 1; /* Compares mpls label, no pad */ } #endif #else #ifdef HAVE_DAQ_ADDRESS_SPACE_ID a++; b++; { uint16_t *x, *y; x = (uint16_t *)a; y = (uint16_t *)b; //x++; //y++; if (*x - *y) return 1; /* Compares addressSpaceID, no pad */ } #endif #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) a++; b++; { uint32_t *x, *y; x = (uint32_t *)a; y = (uint32_t *)b; if (*x - *y) return 1; /* Compares carrierID */ } #endif #else /* SPARCV9 */ uint32_t *a,*b; a = (uint32_t*)s1; b = (uint32_t*)s2; if ((*a - *b) || (*(a+1) - *(b+1))) return 1; /* Compares IPv4 sip/dip */ /* Compares IPv6 sip[0,1] */ a+=2; b+=2; if ((*a - *b) || (*(a+1) - *(b+1))) return 1; /* Compares IPv6 sip[2,3] */ a+=2; b+=2; if ((*a - *b) || (*(a+1) - *(b+1))) return 1; /* Compares IPv6 dip[0,1] */ a+=2; b+=2; if ((*a - *b) || (*(a+1) - *(b+1))) return 1; /* Compares IPv6 dip[2,3] */ a+=2; b+=2; if ((*a - *b) || (*(a+1) - *(b+1))) return 1; /* Compares IPv4 id/pad, vlan/proto/ipver */ /* Compares IPv6 id, vlan/proto/ipver */ #ifdef MPLS a+=2; b+=2; { uint32_t *x, *y; x = (uint32_t *)a; y = (uint32_t *)b; //x++; //y++; if (*x - *y) return 1; /* Compares mpls label */ } #endif #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #ifdef MPLS a++; b++; #else a+=2; b+=2; #endif { uint16_t *x, *y; x = (uint16_t *)a; y = (uint16_t *)b; //x++; //y++; if (*x - *y) return 1; /* Compares addressSpaceID, no pad */ } #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) a++; b++; { uint32_t *x, *y; x = (uint32_t *)a; y = (uint32_t *)b; if (*x - *y) return 1; /* Compares carrierID */ } #endif #endif /* SPARCV9 */ return 0; } /** * Global init function, handles setting up the runtime hash table and * memory management mode. Global configuration applies only to default configuration, * which is in vlanGroup 0 * * @param args argument string to process for config data * * @return none */ static void Frag3GlobalInit(struct _SnortConfig *sc, char *args) { Frag3Config *pCurrentPolicyConfig = NULL; Frag3Config *pDefaultPolicyConfig = NULL; tSfPolicyId policy_id = getParserPolicy(sc); if (frag3_config == NULL) { //create a context frag3_config = sfPolicyConfigCreate(); defrag_pkt = Encode_New(); #ifdef GRE encap_defrag_pkt = Encode_New(); #endif #ifdef PERF_PROFILING RegisterPreprocessorProfile("frag3", &frag3PerfStats, 0, &totalPerfStats, NULL); RegisterPreprocessorProfile("frag3insert", &frag3InsertPerfStats, 1, &frag3PerfStats, NULL); RegisterPreprocessorProfile("frag3rebuild", &frag3RebuildPerfStats, 1, &frag3PerfStats, NULL); #endif AddFuncToPreprocCleanExitList(Frag3CleanExit, NULL, PP_FRAG3_PRIORITY, PP_FRAG3); AddFuncToPreprocResetList(Frag3Reset, NULL, PP_FRAG3_PRIORITY, PP_FRAG3); AddFuncToPreprocResetStatsList(Frag3ResetStats, NULL, PP_FRAG3_PRIORITY, PP_FRAG3); AddFuncToConfigCheckList(sc, Frag3VerifyConfig); AddFuncToPreprocPostConfigList(sc, Frag3PostConfigInit, NULL); RegisterPreprocStats("frag3", Frag3PrintStats); } sfPolicyUserPolicySet (frag3_config, policy_id); pCurrentPolicyConfig = (Frag3Config *)sfPolicyUserDataGetCurrent(frag3_config); pDefaultPolicyConfig = (Frag3Config *)sfPolicyUserDataGetDefault(frag3_config); if ((policy_id != getDefaultPolicy()) && (pDefaultPolicyConfig == NULL)) { ParseError("Frag3: Must configure default policy if other policies " "are going to be used.\n"); } if (pCurrentPolicyConfig != NULL) { FatalError("%s(%d) The frag3 global configuration can only be " "configured once.\n", file_name, file_line); } pCurrentPolicyConfig = (Frag3Config *)SnortPreprocAlloc(1, sizeof(Frag3Config), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); sfPolicyUserDataSetCurrent(frag3_config, pCurrentPolicyConfig); /* setup default values */ pCurrentPolicyConfig->max_frags = DEFAULT_MAX_FRAGS; pCurrentPolicyConfig->memcap = FRAG_MEMCAP; pCurrentPolicyConfig->static_frags = 0; pCurrentPolicyConfig->use_prealloc = 0; pCurrentPolicyConfig->use_prealloc_frags = 0; Frag3ParseGlobalArgs(pCurrentPolicyConfig, args); if (policy_id != getDefaultPolicy()) { /* Can't set these in alternate policies */ pCurrentPolicyConfig->memcap = pDefaultPolicyConfig->memcap; pCurrentPolicyConfig->max_frags = pDefaultPolicyConfig->max_frags; pCurrentPolicyConfig->use_prealloc = pDefaultPolicyConfig->use_prealloc; pCurrentPolicyConfig->use_prealloc_frags = pDefaultPolicyConfig->use_prealloc_frags; pCurrentPolicyConfig->static_frags = pDefaultPolicyConfig->static_frags; } /* * we really only need one frag cache no matter how many different * contexts we have loaded */ if(f_cache == NULL) { /* we keep FragTrackers in the hash table.. */ hashTableSize = (unsigned long) (pCurrentPolicyConfig->max_frags * 1.4); unsigned long maxFragMem = pCurrentPolicyConfig->max_frags * ( sizeof(FragTracker) + sizeof(SFXHASH_NODE) + sizeof (FRAGKEY) + sizeof(SFXHASH_NODE *)); unsigned long tableMem = (hashTableSize + 1) * sizeof(SFXHASH_NODE *); unsigned long maxMem = maxFragMem + tableMem; f_cache = sfxhash_new( hashTableSize, /* number of hash buckets */ sizeof(FRAGKEY), /* size of the key we're going to use */ sizeof(FragTracker), /* size of the storage node */ maxMem, /* memcap for frag trackers */ 1, /* use auto node recovery */ Frag3AutoFree, /* anr free function */ Frag3UserFree, /* user free function */ 1); /* recycle node flag */ /* can't proceed if we can't get a fragment cache */ if(!f_cache) { LogMessage("WARNING: Unable to generate new sfxhash for frag3, " "defragmentation disabled.\n"); return; } sfxhash_set_keyops(f_cache, Frag3KeyHashFunc, Frag3KeyCmpFunc); } /* display the global config for the user */ Frag3PrintGlobalConfig(pCurrentPolicyConfig); #ifdef REG_TEST LogMessage("\n"); LogMessage(" FragTracker Size: %lu\n",(unsigned long)sizeof(FragTracker)); LogMessage("\n"); #endif /* register the preprocessor func node */ if ( !pCurrentPolicyConfig->disabled ) { AddFuncToPreprocList(sc, Frag3Defrag, PP_FRAG3_PRIORITY, PP_FRAG3, PROTO_BIT__IP); session_api->enable_preproc_all_ports( sc, PP_FRAG3, PROTO_BIT__IP ); } } /** * Setup a frag3 engine context * * @param args list of configuration arguments * * @return none */ static void Frag3Init(struct _SnortConfig *sc, char *args) { Frag3Context *context; /* context pointer */ tSfPolicyId policy_id = getParserPolicy(sc); Frag3Config *config = NULL; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Initializing frag3\n");); config = (Frag3Config *)sfPolicyUserDataGet(frag3_config, policy_id); if (config == NULL) { FatalError("[!] Unable to configure frag3 engine!\n" "Frag3 global config has not been established, " "please issue a \"preprocessor frag3_global\" directive\n"); return; } /* * setup default context config. Thinking maybe we should go with * FRAG_POLICY_FIRST or FRAG_POLICY_LINUX as the default instead of * BSD since Win32/Linux have a higher incidence of occurrence. Anyone * with an opinion on the matter feel free to email me... */ context = (Frag3Context *) SnortPreprocAlloc(1, sizeof(Frag3Context), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); context->frag_policy = FRAG_POLICY_DEFAULT; context->frag_timeout = FRAG_PRUNE_QUANTA; /* 60 seconds */ context->min_ttl = FRAG3_MIN_TTL; context->frag3_alerts = 0; /* parse the configuration for this engine */ Frag3ParseArgs(sc, args, context); if (context->bound_addrs == NULL) { if (config->default_context != NULL) FatalError("Frag3 => only one non-bound engine can be specified.\n"); config->default_context = context; } /* Now add this context to the internal list */ if (config->frag3ContextList == NULL) { config->numFrag3Contexts = 1; config->frag3ContextList = (Frag3Context **)SnortPreprocAlloc(1, sizeof (Frag3Context *), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); } else { Frag3Context **tmpContextList; config->numFrag3Contexts++; tmpContextList = (Frag3Context **) SnortPreprocAlloc(config->numFrag3Contexts, sizeof (Frag3Context *), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); memcpy(tmpContextList, config->frag3ContextList, sizeof(Frag3Context *) * (config->numFrag3Contexts - 1)); SnortPreprocFree(config->frag3ContextList, (config->numFrag3Contexts-1) * sizeof (Frag3Context *), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); config->frag3ContextList = tmpContextList; } config->frag3ContextList[config->numFrag3Contexts - 1] = context; /* print this engine config */ Frag3PrintEngineConfig(context); } static int FragPolicyIdFromName(char *name) { if (!name) { return FRAG_POLICY_DEFAULT; } if(!strcasecmp(name, "bsd")) { return FRAG_POLICY_BSD; } else if(!strcasecmp(name, "bsd-right")) { return FRAG_POLICY_BSD_RIGHT; } else if(!strcasecmp(name, "linux")) { return FRAG_POLICY_LINUX; } else if(!strcasecmp(name, "first")) { return FRAG_POLICY_FIRST; } else if(!strcasecmp(name, "windows")) { return FRAG_POLICY_WINDOWS; } else if(!strcasecmp(name, "solaris")) { return FRAG_POLICY_SOLARIS; } else if(!strcasecmp(name, "last")) { return FRAG_POLICY_LAST; } return FRAG_POLICY_DEFAULT; } #ifdef TARGET_BASED int FragPolicyIdFromHostAttributeEntry(HostAttributeEntry *host_entry) { if (!host_entry) return 0; host_entry->hostInfo.fragPolicy = FragPolicyIdFromName(host_entry->hostInfo.fragPolicyName); host_entry->hostInfo.fragPolicySet = 1; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3 INIT: %s(%d) for Entry %s\n", frag_policy_names[host_entry->hostInfo.fragPolicy], host_entry->hostInfo.fragPolicy, host_entry->hostInfo.fragPolicyName);); return 0; } #endif /** * Verify frag3 setup is complete * * @param args list of configuration arguments * * @return none */ static int Frag3VerifyConfigPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { Frag3Config *pPolicyConfig = (Frag3Config *)pData; if ( pPolicyConfig->disabled ) return 0; //do any housekeeping before processingFrag3Config if ((policyId != getDefaultPolicy()) && (pPolicyConfig->numFrag3Contexts == 0)) { WarningMessage("Frag3VerifyConfig: PolicyId %d, policy engine required " "but not configured.\n", policyId); return -1; } #ifdef TARGET_BASED SFAT_SetPolicyIds(FragPolicyIdFromHostAttributeEntry, policyId); #endif return 0; } static int Frag3VerifyConfig(struct _SnortConfig *sc) { if (sfPolicyUserDataIterate (sc, frag3_config, Frag3VerifyConfigPolicy)) return -1; return 0; } /** * Handle the preallocation of frags * * @param int unused * void *arg unused inputs * (these aren't used, just need to match function prototype) * * @return none */ static void Frag3PostConfigInit(struct _SnortConfig *sc, void *arg) { Frag3Frag *tmp; /* for initializing the prealloc queue */ unsigned int i; /* counter */ Frag3Config *config = NULL; config = sfPolicyUserDataGetDefault(frag3_config); if (config == NULL) return; pkt_snaplen = DAQ_GetSnapLen(); /* * user has decided to prealloc the node structs for performance */ if(config->use_prealloc) { if (config->static_frags == 0) { config->static_frags = (uint32_t)(config->memcap / (sizeof(Frag3Frag) + sizeof(uint8_t) * pkt_snaplen) + 1); config->ten_percent = config->static_frags >> 5; } for (i = 0; i < config->static_frags; i++) { tmp = (Frag3Frag *) SnortPreprocAlloc(1, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); tmp->fptr = (uint8_t *) SnortPreprocAlloc(pkt_snaplen, sizeof(uint8_t), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); Frag3PreallocPush(tmp); } prealloc_nodes_in_use = 0; } } /** * Config parser for global config. * * @param args List of configuration parameters * * @return none */ static void Frag3ParseGlobalArgs(Frag3Config *gconfig, char *args) { char **toks; int num_toks; int i = 0; char *index; char **stoks = NULL; int s_toks; char *endPtr; long ivalue; unsigned long value; if ((args == NULL) || (gconfig == NULL)) return; toks = mSplit(args, ",", 0, &num_toks, 0); for (i = 0; i < num_toks; i++) { index = toks[i]; stoks = mSplit(index, " ", 0, &s_toks, 0); if(!strcasecmp(stoks[0], "max_frags")) { if (s_toks != 2) { FatalError("%s(%d) => Missing argument to max_frags in " "config file.\n", file_name, file_line); } gconfig->max_frags = ivalue = strtol(stoks[1], &endPtr, 10); if ((endPtr == &stoks[1][0]) || (ivalue <= 0)) { FatalError("%s(%d) => Invalid max_frags in config file. " "Integer parameter required.\n", file_name, file_line); } } else if(!strcasecmp(stoks[0], "memcap")) { if (s_toks != 2) { FatalError("%s(%d) => Missing argument to memcap in " "config file.\n", file_name, file_line); } gconfig->memcap = value = strtoul(stoks[1], &endPtr, 10); if (!*stoks[1] || *stoks[1] == '-' || *endPtr) { FatalError("%s(%d) => Invalid memcap in config file. " "Integer parameter required.\n", file_name, file_line); } if (gconfig->memcap < MIN_FRAG_MEMCAP) { LogMessage("WARNING: %s(%d) => Ludicrous (<16k) memcap " "size, setting to default (%d bytes)\n", file_name, file_line, FRAG_MEMCAP); gconfig->memcap = FRAG_MEMCAP; } /* ok ok, it's really 9.375%, sue me */ gconfig->ten_percent = ((gconfig->memcap >> 5) + (gconfig->memcap >> 6)); } else if(!strcasecmp(stoks[0], "prealloc_memcap")) { /* Use memcap to calculate prealloc_frag value */ unsigned long memcap = FRAG_MEMCAP; if (s_toks != 2) { FatalError("%s(%d) => Missing argument to prealloc_memcap in " "config file.\n", file_name, file_line); } memcap = value = strtoul(stoks[1], &endPtr, 10); if (!*stoks[1] || *stoks[1] == '-' || *endPtr) { FatalError("%s(%d) => Invalid prealloc_memcap in config file. " "Integer parameter required.\n", file_name, file_line); } if(memcap Ludicrous (<16k) prealloc_memcap " "size, setting to default (%d bytes)\n", file_name, file_line, FRAG_MEMCAP); memcap = FRAG_MEMCAP; } gconfig->use_prealloc = 1; gconfig->memcap = memcap; } else if(!strcasecmp(stoks[0], "prealloc_frags")) { if (s_toks != 2) { FatalError("%s(%d) => Missing argument to prealloc_frags " "in config file.\n", file_name, file_line); } gconfig->static_frags = value = strtoul(stoks[1], &endPtr, 10); gconfig->use_prealloc_frags = gconfig->use_prealloc = 1; if (!*stoks[1] || *stoks[1] == '-' || *endPtr || value > MAX_PREALLOC_FRAGS) { FatalError("%s(%d) => Invalid prealloc_frags in config file. Entered value is not allowed. " "Integer parameter (in the range of 0 to %d) required.\n", file_name, file_line,MAX_PREALLOC_FRAGS); } } else if(!strcasecmp(stoks[0], "disabled")) { gconfig->disabled = 1; } else { FatalError("%s(%d) => Invalid Frag3 global option (%s)\n", file_name, file_line, index); } mSplitFree(&stoks, s_toks); } mSplitFree(&toks, num_toks); } /** * Config parser for engine context config. * * @param args List of configuration parameters * * @return none */ static void Frag3ParseArgs(struct _SnortConfig *sc, char *args, Frag3Context *context) { char **toks; int num_toks; int i = 0; toks = mSplit(args, " ", 13, &num_toks, 0); while(i < num_toks) { int error = 0; int increment = 1; char *index = toks[i]; char *arg = NULL; char *endptr; int32_t value = 0; /* In case an option takes an argument */ if ((i + 1) < num_toks) arg = toks[i + 1]; if(!strcasecmp(index, "timeout")) { if (arg == NULL) { error = 1; } else { value = SnortStrtol(arg, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (value < 0)) error = 1; } if (error) { ParseError("Bad timeout in frag3 config. Positive integer " "parameter required."); } increment = 2; context->frag_timeout = (uint32_t)value; } else if(!strcasecmp(index, "min_ttl")) { if (arg == NULL) { error = 1; } else { value = SnortStrtol(arg, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (value < 0) || (value > UINT8_MAX)) { error = 1; } } if (error) { ParseError("Bad min_ttl in frag3 config. Positive integer " "less than 256 required."); } increment = 2; context->min_ttl = (uint8_t)value; } else if(!strcasecmp(index, "detect_anomalies")) { context->frag3_alerts |= FRAG3_DETECT_ANOMALIES; } else if(!strcasecmp(index, "policy")) { if (arg == NULL) { ParseError("Frag3 policy requires a policy " "identifier argument."); } increment = 2; context->frag_policy = FragPolicyIdFromName(arg); if ((context->frag_policy == FRAG_POLICY_DEFAULT) && (strcasecmp(arg, "bsd"))) { ParseError("Bad policy name \"%s\" in frag3 config.", arg); } } else if(!strcasecmp(index, "bind_to")) { if (arg == NULL) { ParseError("Frag3 bind_to requires an IP list or " "CIDR block argument."); } /* Fatals on bad ip address */ context->bound_addrs = IpAddrSetParse(sc, arg); increment = 2; } else if(!strcasecmp(index, "min_fragment_length")) { if (arg == NULL) { error = 1; } else { value = SnortStrtol(arg, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (value < 0)) error = 1; } if (error) { ParseError("Bad min_fragment_length in frag3 config. Positive " "integer parameter required."); } increment = 2; context->min_fragment_length = (uint32_t)value; } else if(!strcasecmp(index, "overlap_limit")) { if (arg == NULL) { error = 1; } else { value = SnortStrtol(arg, &endptr, 10); if ((errno == ERANGE) || (*endptr != '\0') || (value < 0)) error = 1; } if (error) { ParseError("Bad overlap_limit in frag3 config. Positive " "integer parameter required."); } increment = 2; context->overlap_limit = (uint32_t)value; } else { ParseError("Invalid Frag3 engine option (%s).", index); } i += increment; } mSplitFree(&toks, num_toks); } /** * Main runtime entry point for Frag3 * * @param p Current packet to process. * @param context Context for this defrag engine * * @return none */ static void Frag3Defrag(Packet *p, void *context) { FRAGKEY fkey; /* fragkey for this packet */ FragTracker *ft; /* FragTracker to process the packet on */ Frag3Context *f3context = NULL; /* engine context */ int engineIndex; int insert_return = 0; /* return value from the insert function */ tSfPolicyId policy_id = getNapRuntimePolicy(); PROFILE_VARS; // preconditions - what we registered for assert(IPH_IS_VALID(p) && !(p->error_flags & PKT_ERR_CKSUM_IP)); /* check to make sure this preprocessor should run */ if ( !p->frag_flag ) return; frag3_eval_config = (Frag3Config *)sfPolicyUserDataGet(frag3_config, policy_id); memset(&fkey, 0, sizeof(FRAGKEY)); ft = Frag3GetTracker(p, &fkey); if (ft != NULL) { f3context = ft->context; frag3_eval_config = (Frag3Config *)sfPolicyUserDataGet(ft->config, ft->policy_id); } if (frag3_eval_config == NULL) return; if (ft == NULL) { /* Find an engine context for this packet */ for (engineIndex = 0; engineIndex < frag3_eval_config->numFrag3Contexts; engineIndex++) { f3context = frag3_eval_config->frag3ContextList[engineIndex]; if (f3context->bound_addrs == NULL) continue; /* Does this engine context handle fragments to this IP address? */ if(sfvar_ip_in(f3context->bound_addrs, GET_DST_ADDR(p))) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[FRAG3] Found engine context in IpAddrSet\n");); break; } } if (engineIndex == frag3_eval_config->numFrag3Contexts) f3context = frag3_eval_config->default_context; if (!f3context) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[FRAG3] Could not find Frag3 engine context " "for IP %s\n", inet_ntoa(GET_SRC_ADDR(p)));); return; } } /* * First case: if frag offset is 0 & UDP, let that packet go * through the rest of the system. Ugly HACK to detect DNS * attack on 0 offset UDP. * * Second case: If frag offset is 0 & !more frags, this is a * full-frame "fragment", let the packet go through the rest * of the system. * * In other words: * a = frag_offset != 0 * b = !UDP * c = More Fragments * * if (a | (b & c)) * Disable Inspection since we'll look at the payload in * a rebuilt packet later. So don't process it further. */ if ((p->frag_offset != 0) || ((GET_IPH_PROTO(p) != IPPROTO_UDP) && (p->mf))) { DisableDetect( p ); otn_tmp = NULL; } /* * pkt's not going to make it to the target, bail */ if(GET_IPH_TTL(p) < f3context->min_ttl) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[FRAG3] Fragment discarded due to low TTL " "[0x%X->0x%X], TTL: %d " "Offset: %d Length: %d\n", ntohl(p->iph->ip_src.s_addr), ntohl(p->iph->ip_dst.s_addr), GET_IPH_TTL(p), p->frag_offset, p->dsize);); EventAnomScMinTTL(f3context); f3stats.discards++; return; } f3stats.total++; UpdateIPFragStats(&sfBase, p->pkth->caplen); PREPROC_PROFILE_START(frag3PerfStats); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "\n++++++++++++++++++++++++++++++++++++++++++++++\n");); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[**] [FRAG3] Inspecting fragment...\n");); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[FRAG3] Got frag packet (mem use: %ld frag " "trackers: %d p->pkt_flags: 0x%X " "prealloc nodes in use: %lu/%lu)\n", frag3_mem_in_use, sfxhash_count(f_cache), p->packet_flags, prealloc_nodes_in_use, frag3_eval_config->static_frags);); pkttime = (struct timeval *) &p->pkth->ts; /* * try to get the tracker that this frag should go with */ if (ft == NULL) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Adding New FragTracker...\n");); /* * first frag for this packet, start a new tracker */ Frag3NewTracker(p, &fkey, f3context); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[FRAG3] mem use: %ld frag " "trackers: %d prealloc " "nodes in use: %lu/%lu\n", frag3_mem_in_use, sfxhash_count(f_cache), prealloc_nodes_in_use, frag3_eval_config->static_frags);); /* * all done, return control to Snort */ PREPROC_PROFILE_END(frag3PerfStats); return; } else if (Frag3Expire(p, ft, f3context) == FRAG_TRACKER_TIMEOUT) { /* Time'd out FragTrackers are just purged of their packets. * Reset the timestamp per this packet. * And reset the rest of the tracker as if this is the * first packet on the tracker, and continue. */ /* This fixes an issue raised on bugtraq relating to * timeout frags not getting purged correctly when * the entire set of frags show up later. */ ft->ttl = GET_IPH_TTL(p); /* store the first ttl we got */ ft->calculated_size = 0; ft->alerted = 0; ft->frag_flags = 0; ft->frag_bytes = 0; ft->frag_pkts = 0; ft->alert_count = 0; ft->ip_options_len = 0; ft->ip_option_count = 0; ft->ip_options_data = NULL; ft->copied_ip_options_len = 0; ft->copied_ip_option_count = 0; ft->context = f3context; ft->ordinal = 0; } // Update frag time when we get a frag associated with this tracker ft->frag_time.tv_sec = p->pkth->ts.tv_sec; ft->frag_time.tv_usec = p->pkth->ts.tv_usec; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Found frag tracker\n");); //dont forward fragments to target if some previous fragment was dropped if ( ft->frag_flags & FRAG_DROP_FRAGMENTS ) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Blocking fragments due to earlier fragment drop\n");); DisableDetect( p ); Active_DAQDropPacket( p ); if (pkt_trace_enabled) { addPktTraceData(VERDICT_REASON_DEFRAG, snprintf(trace_line, MAX_TRACE_LINE, "Defragmentation: earlier fragment was already blocked, %s\n", getPktTraceActMsg())); } else addPktTraceData(VERDICT_REASON_DEFRAG, 0); f3stats.drops++; } /* * insert the fragment into the FragTracker */ if((insert_return = Frag3Insert(p, ft, &fkey, f3context)) != FRAG_INSERT_OK) { /* * we can pad this switch out for a variety of entertaining behaviors * later if we're so inclined */ switch(insert_return) { case FRAG_INSERT_FAILED: #ifdef DEBUG LogMessage("WARNING: Insert into Fraglist failed, " "(offset: %u).\n", p->frag_offset); #endif PREPROC_PROFILE_END(frag3PerfStats); return; case FRAG_INSERT_TTL: DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[FRAG3] Fragment discarded due to large TTL Delta " "[0x%X->0x%X], TTL: %d orig TTL: %d " "Offset: %d Length: %d\n", ntohl(p->iph->ip_src.s_addr), ntohl(p->iph->ip_dst.s_addr), GET_IPH_TTL(p), ft->ttl, p->frag_offset, p->dsize);); f3stats.discards++; PREPROC_PROFILE_END(frag3PerfStats); return; case FRAG_INSERT_ATTACK: case FRAG_INSERT_ANOMALY: f3stats.discards++; PREPROC_PROFILE_END(frag3PerfStats); return; case FRAG_INSERT_TIMEOUT: #ifdef DEBUG LogMessage("WARNING: Insert into Fraglist failed due to timeout, " "(offset: %u).\n", p->frag_offset); #endif PREPROC_PROFILE_END(frag3PerfStats); return; case FRAG_INSERT_OVERLAP_LIMIT: #ifdef DEBUG LogMessage("WARNING: Excessive IP fragment overlap, " "(More: %u, offset: %u, offsetSize: %u).\n", p->mf, (p->frag_offset<<3), p->ip_frag_len); #endif f3stats.discards++; PREPROC_PROFILE_END(frag3PerfStats); return; default: break; } } p->fragtracker = (void *)ft; /* * check to see if it's reassembly time */ if(Frag3IsComplete(ft)) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[*] Fragment is complete, rebuilding!\n");); /* * if the frag completes but it's bad we're just going to drop it * instead of wasting time on putting it back together */ if(!(ft->frag_flags & FRAG_BAD)) { Frag3Rebuild(ft, p); if (p->frag_offset != 0 || (GET_IPH_PROTO(p) != IPPROTO_UDP && ft->frag_flags & FRAG_REBUILT)) { /* Need to reset some things here because the * rebuilt packet will have reset the do_detect * flag when it hits Preprocess. */ do_detect_content = do_detect = 0; otn_tmp = NULL; } } if (Active_PacketWasDropped()) { Frag3DeleteTracker(ft); ft->frag_flags |= FRAG_DROP_FRAGMENTS; } else { Frag3RemoveTracker(&fkey, ft); p->fragtracker = NULL; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[FRAG3] Dumped fragtracker (mem use: %ld frag " "trackers: %d prealloc nodes in use: %lu/%lu)\n", frag3_mem_in_use, sfxhash_count(f_cache), prealloc_nodes_in_use, frag3_eval_config->static_frags);); } } PREPROC_PROFILE_END(frag3PerfStats); return; } /** * Check to see if a FragTracker has timed out * * @param current_time Time at this moment * @param start_time Time to compare current_time to * @param f3context Engine context * * @return status * @retval FRAG_TIMEOUT Current time diff is greater than the current * context's timeout value * @retval FRAG_TIME_OK Current time diff is within the context's prune * window */ static inline int CheckTimeout(struct timeval *current_time, struct timeval *start_time, Frag3Context *f3context) { struct timeval tv_diff; /* storage struct for the difference between current_time and start_time */ TIMERSUB(current_time, start_time, &tv_diff); if(tv_diff.tv_sec >= (int)f3context->frag_timeout) { return FRAG_TIMEOUT; } return FRAG_TIME_OK; } /** * Time-related expiration of fragments from the system. Checks the current * FragTracker for timeout, then walks up the LRU list looking to see if * anyone should have timed out. * * @param p Current packet (contains pointer to the current timestamp) * @param ft FragTracker to check for a timeout * @param fkey FragKey of the current FragTracker for sfxhash lookup * @param f3context Context of the defrag engine, contains the timeout value * * @return status * @retval FRAG_TRACKER_TIMEOUT The current FragTracker has timed out * @retval FRAG_OK The current FragTracker has not timed out */ static inline int Frag3Expire(Packet *p, FragTracker *ft, Frag3Context *f3context) { /* * Check the FragTracker that was passed in first */ if(CheckTimeout( pkttime, &(ft)->frag_time, f3context) == FRAG_TIMEOUT) { /* * Oops, we've timed out, whack the FragTracker */ #if defined(DEBUG_FRAG3) && defined(DEBUG) if (DEBUG_FRAG & GetDebugLevel()) { char *src_str = SnortStrdup(FragIPToStr(ft->sip, ft->ipver)); LogMessage("(spp_frag3) Current Fragment dropped due to timeout! " "[%s->%s ID: %d]\n", src_str, FragIPToStr(ft->dip, ft->ipver), ft->id); free(src_str); } #endif /* * Don't remove the tracker. * Remove all of the packets that are stored therein. * * If the existing tracker times out because of a delay * relative to the timeout */ //Frag3RemoveTracker(fkey, ft); Frag3DeleteTracker(ft); f3stats.timeouts++; sfBase.iFragTimeouts++; return FRAG_TRACKER_TIMEOUT; } return FRAG_OK; } /** * Check to see if we've got the first or last fragment on a FragTracker and * set the appropriate frag_flags * * @param p Packet to get the info from * @param ft FragTracker to set the flags on * * @return none */ static inline int Frag3CheckFirstLast(Packet *p, FragTracker *ft) { uint16_t fragLength; int retVal = FRAG_FIRSTLAST_OK; uint16_t endOfThisFrag; /* set the frag flag if this is the first fragment */ if(p->mf && p->frag_offset == 0) { ft->frag_flags |= FRAG_GOT_FIRST; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Got first frag\n");); } else if((!p->mf) && (p->frag_offset > 0)) /* set for last frag too */ { /* Use the actual length here, because packet may have been * truncated. Don't want to try to copy more than we actually * captured. */ //fragLength = p->actual_ip_len - GET_IPH_HLEN(p) * 4; fragLength = p->ip_frag_len; endOfThisFrag = (p->frag_offset << 3) + fragLength; if (ft->frag_flags & FRAG_GOT_LAST) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Got last frag again!\n");); switch (ft->frag_policy) { case FRAG_POLICY_BSD: case FRAG_POLICY_LINUX: case FRAG_POLICY_BSD_RIGHT: case FRAG_POLICY_LAST: case FRAG_POLICY_WINDOWS: case FRAG_POLICY_FIRST: if (ft->calculated_size > endOfThisFrag) { /* Already have a 'last frag' with a higher * end point. Leave it as is. * * Some OS's do not respond at all -- we'll * still try to rebuild anyway in that case, * because there is really something wrong * and we should look at it. */ retVal = FRAG_LAST_DUPLICATE; } break; case FRAG_POLICY_SOLARIS: if (ft->calculated_size > endOfThisFrag) { /* Already have a 'last frag' with a higher * end point. Leave it as is. * * Some OS's do not respond at all -- we'll * still try to rebuild anyway in that case, * because there is really something wrong * and we should look at it. */ retVal = FRAG_LAST_DUPLICATE; } else { /* Solaris does some weird stuff here... */ /* Usually, Solaris takes the higher end point. * But in one strange case (when it hasn't seen * any frags beyond the existing last frag), it * actually appends that new last frag to the * end of the previous last frag, regardless of * the offset. Effectively, it adjusts the * offset of the new last frag to immediately * after the existing last frag. */ /* XXX: how to handle that case? punt? */ retVal = FRAG_LAST_OFFSET_ADJUST; } break; } } ft->frag_flags |= FRAG_GOT_LAST; /* * If this is the last frag (and we don't have a frag that already * extends beyond this one), set the size that we're expecting. */ if ((ft->calculated_size < endOfThisFrag) && (retVal != FRAG_LAST_OFFSET_ADJUST)) { ft->calculated_size = endOfThisFrag; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Got last frag, Bytes: %d, " "Calculated size: %d\n", ft->frag_bytes, ft->calculated_size);); } } if (p->frag_offset != 0) { ft->frag_flags |= FRAG_NO_BSD_VULN; } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag Status: %s:%s\n", ft->frag_flags&FRAG_GOT_FIRST?"FIRST":"No FIRST", ft->frag_flags&FRAG_GOT_LAST?"LAST":"No LAST");); return retVal; } /** * Lookup a FragTracker in the f_cache sfxhash table based on an input key * * @param p The current packet to get the key info from * @param fkey Pointer to a container for the FragKey * * @return Pointer to the FragTracker in the hash bucket or NULL if there is * no fragment in the hash bucket */ static FragTracker *Frag3GetTracker(Packet *p, FRAGKEY *fkey) { FragTracker *returned; /* FragTracker ptr returned by the lookup */ /* * we have to setup the key first, downstream functions depend on * it being setup here */ if (IS_IP4(p)) { COPY4(fkey->sip, sfaddr_get_ip6_ptr(&p->ip4h->ip_addrs->ip_src)); COPY4(fkey->dip, sfaddr_get_ip6_ptr(&p->ip4h->ip_addrs->ip_dst)); fkey->id = GET_IPH_ID(p); fkey->ipver = 4; fkey->proto = GET_IPH_PROTO(p); } else { IP6Frag *fragHdr; COPY4(fkey->sip, sfaddr_get_ip6_ptr(&p->ip6h->ip_addrs->ip_src)); COPY4(fkey->dip, sfaddr_get_ip6_ptr(&p->ip6h->ip_addrs->ip_dst)); fkey->ipver = 6; /* Data points to the offset, and does not include the next hdr * and reserved. Offset it by -2 to get there */ fragHdr = (IP6Frag *)p->ip6_extensions[p->ip6_frag_index].data; /* Can't rely on the next header. Only the 0 offset packet * is required to have it in the frag header */ //fkey->proto = fragHdr->ip6f_nxt; fkey->proto = 0; fkey->id = fragHdr->ip6f_ident; } if (p->vh && !ScVlanAgnostic()) fkey->vlan_tag = (uint16_t)VTH_VLAN(p->vh); else fkey->vlan_tag = 0; #ifdef MPLS if(ScMplsOverlappingIp() && p->mpls) fkey->mlabel = p->mplsHdr.label; else fkey->mlabel = 0; #endif #ifdef HAVE_DAQ_ADDRESS_SPACE_ID #if !defined(SFLINUX) && defined(DAQ_CAPA_VRF) fkey->address_space_id_dst = DAQ_GetDestinationAddressSpaceID(p->pkth); fkey->address_space_id_src = DAQ_GetSourceAddressSpaceID(p->pkth); #else if (!ScAddressSpaceAgnostic()) fkey->addressSpaceId = DAQ_GetAddressSpaceID(p->pkth); else fkey->addressSpaceId = 0; #endif #endif #if !defined(SFLINUX) && defined(DAQ_CAPA_CARRIER_ID) fkey->carrierId = GET_OUTER_IPH_PROTOID(p, pkth); #endif /* * if the hash table is empty we're done */ if(sfxhash_count(f_cache) == 0) return NULL; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[*] Looking up FragTracker using key:\n");); #ifdef DEBUG_FRAG3 PrintFragKey(fkey); #endif returned = (FragTracker *) sfxhash_find(f_cache, fkey); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3GetTracker returning %p for\n", returned);); return returned; } /** * Handle IP Options in fragmented packets. * * @param ft Current frag tracker for this packet * @param p Current packet to check for options * @param context In case we get an anomaly * * @return status * @retval 0 on an error * @retval 1 on success */ static int Frag3HandleIPOptions(FragTracker *ft, Packet *p) { unsigned int i = 0; /* counter */ if(p->frag_offset == 0) { /* * This is the first packet. If it has IP options, * save them off, so we can set them on the reassembled packet. */ if (p->ip_options_len) { if (ft->ip_options_data) { /* Already seen 0 offset packet and copied some IP options */ if ((ft->frag_flags & FRAG_GOT_FIRST) && (ft->ip_option_count != p->ip_option_count)) { EventAnomIpOpts(ft->context); } } else { /* Allocate and copy in the options */ ft->ip_options_data = SnortPreprocAlloc(1, p->ip_options_len, PP_FRAG3, PP_MEM_CATEGORY_SESSION); memcpy(ft->ip_options_data, p->ip_options_data, p->ip_options_len); ft->ip_options_len = p->ip_options_len; ft->ip_option_count = p->ip_option_count; } } } else { /* check that options match those from other non-offset 0 packets */ /* XXX: could check each individual option here, but that * would be performance ugly. So, we'll just check that the * option counts match. Alert if invalid, but still include in * reassembly. */ if (ft->copied_ip_option_count) { if (ft->copied_ip_option_count != p->ip_option_count) { EventAnomIpOpts(ft->context); } } else { ft->copied_ip_option_count = p->ip_option_count; for (i = 0;i< p->ip_option_count && i < IP_OPTMAX; i++) { /* Is the high bit set? If not, weird anomaly. */ if (!(p->ip_options[i].code & 0x80)) EventAnomIpOpts(ft->context); } } } return 1; } int FragGetPolicy(Packet *p, Frag3Context *f3context) { #ifdef TARGET_BASED int frag_policy; /* Not caching this host_entry in the frag tracker so we can * swap the table out after processing this packet if we need * to. */ HostAttributeEntry *host_entry; if (!IsAdaptiveConfigured()) return f3context->frag_policy; host_entry = SFAT_LookupHostEntryByDst(p); if (host_entry && (isFragPolicySet(host_entry) == POLICY_SET)) { frag_policy = getFragPolicy(host_entry); if (frag_policy != SFAT_UNKNOWN_FRAG_POLICY) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "FragGetPolicy: Policy Map Entry: %d(%s)\n", frag_policy, frag_policy_names[frag_policy]);); return frag_policy; } } #endif DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "FragGetPolicy: Using configured default %d(%s)\n", f3context->frag_policy, frag_policy_names[f3context->frag_policy]);); return f3context->frag_policy; } /** * Didn't find a FragTracker in the hash table, create a new one and put it * into the f_cache * * @param p Current packet to fill in FragTracker fields * @param fkey FragKey struct to use for table insertion * * @return status * @retval 0 on an error * @retval 1 on success */ static int Frag3NewTracker(Packet *p, FRAGKEY *fkey, Frag3Context *f3context) { FragTracker *tmp; Frag3Frag *f = NULL; //int ret = 0; const uint8_t *fragStart; uint16_t fragLength; uint16_t frag_end; SFXHASH_NODE *hnode; tSfPolicyId policy_id = getNapRuntimePolicy(); fragStart = p->ip_frag_start; //fragStart = (uint8_t *)p->iph + GET_IPH_HLEN(p) * 4; /* Use the actual length here, because packet may have been * truncated. Don't want to try to copy more than we actually * captured. */ //fragLength = p->actual_ip_len - GET_IPH_HLEN(p) * 4; fragLength = p->ip_frag_len; #ifdef DEBUG_MSGS if (p->actual_ip_len != ntohs(GET_IPH_LEN(p))) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "IP Actual Length (%d) != specified length (%d), " "truncated packet (%d)?\n", p->actual_ip_len, ntohs(GET_IPH_LEN(p)), pkt_snaplen);); } #endif /* Just to double check */ if (fragLength > pkt_snaplen) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Overly large fragment %d 0x%x 0x%x %d\n", fragLength, GET_IPH_LEN(p), GET_IPH_OFF(p), p->frag_offset << 3);); /* Ah, crap. Return that tracker. */ return 0; } // Try to get a new one if (!(hnode = sfxhash_get_node(f_cache, fkey)) || !hnode->data) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3NewTracker: sfxhash_get_node() failed\n");); return 0; } tmp = (FragTracker *)hnode->data; memset(tmp, 0, sizeof(FragTracker)); /* * setup the frag tracker */ COPY4(tmp->sip,fkey->sip); COPY4(tmp->dip,fkey->dip); tmp->id = fkey->id; if (IS_IP4(p)) { tmp->protocol = fkey->proto; tmp->ipver = 4; } else /* IPv6 */ { if (p->frag_offset == 0) { IP6Frag *fragHdr = (IP6Frag *)p->ip6_extensions[p->ip6_frag_index].data; tmp->protocol = fragHdr->ip6f_nxt; } tmp->ipver = 6; } tmp->ttl = GET_IPH_TTL(p); /* store the first ttl we got */ tmp->calculated_size = 0; tmp->alerted = 0; tmp->frag_flags = 0; tmp->frag_bytes = 0; tmp->frag_pkts = 0; tmp->frag_time.tv_sec = p->pkth->ts.tv_sec; tmp->frag_time.tv_usec = p->pkth->ts.tv_usec; tmp->alert_count = 0; tmp->ip_options_len = 0; tmp->ip_option_count = 0; tmp->ip_options_data = NULL; tmp->copied_ip_options_len = 0; tmp->copied_ip_option_count = 0; tmp->ordinal = 0; tmp->frag_policy = FragGetPolicy(p, f3context); tmp->context = f3context; tmp->policy_id = policy_id; tmp->config = frag3_config; ((Frag3Config *)sfPolicyUserDataGet(tmp->config, tmp->policy_id))->ref_count++; /* * get our first fragment storage struct */ if(!frag3_eval_config->use_prealloc) { if(frag3_mem_in_use > frag3_eval_config->memcap) { if (Frag3Prune(tmp) == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3NewTracker: Pruning failed\n");); return 0; } } f = (Frag3Frag *) SnortPreprocAlloc(1, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use += sizeof(Frag3Frag); f->fptr = (uint8_t *) SnortPreprocAlloc(1, fragLength, PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use += fragLength; sfBase.frag3_mem_in_use = frag3_mem_in_use; } else { while((f = Frag3PreallocPop()) == NULL) { if (Frag3Prune(tmp) == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3NewTracker: Pruning failed\n");); return 0; } } } f3stats.fragnodes_created++; sfBase.iFragCreates++; sfBase.iCurrentFrags++; if (sfBase.iCurrentFrags > sfBase.iMaxFrags) sfBase.iMaxFrags = sfBase.iCurrentFrags; /* initialize the fragment list */ tmp->fraglist = NULL; /* * setup the Frag3Frag struct with the current packet's data */ memcpy(f->fptr, fragStart, fragLength); f->size = f->flen = fragLength; f->offset = p->frag_offset << 3; frag_end = f->offset + fragLength; f->ord = tmp->ordinal++; f->data = f->fptr; /* ptr to adjusted start position */ if (!p->mf) { f->last = 1; } else { /* * all non-last frags are supposed to end on 8-byte boundries */ if(frag_end & 7) { /* * bonk/boink/jolt/etc attack... */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[..] Short frag (Bonk, etc) attack!\n");); EventAnomShortFrag(f3context); /* don't return, might still be interesting... */ } /* can't have non-full fragments... */ frag_end &= ~7; /* Adjust len to take into account the jolting/non-full fragment. */ f->size = frag_end - f->offset; } /* insert the fragment into the frag list */ tmp->fraglist = f; tmp->fraglist_tail = f; tmp->fraglist_count = 1; /* XXX: Are these duplciates? */ tmp->frag_pkts = 1; /* * mark the FragTracker if this is the first/last frag */ Frag3CheckFirstLast(p, tmp); tmp->frag_bytes += fragLength; Frag3HandleIPOptions(tmp, p); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[#] accumulated bytes on FragTracker: %d\n", tmp->frag_bytes);); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Initial fragment for tracker, ptr %p, offset %d, " "size %d\n", f, f->offset, f->size);); #ifdef DEBUG_FRAG3 PrintFragKey(fkey); #endif DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Calling sfxhash(add), overhead at %lu\n", f_cache->overhead_bytes);); f3stats.fragtrackers_created++; pc.frag_trackers++; p->fragtracker = (void *)tmp; return 1; } /** * Handle the creation of the new frag node and list insertion. * Separating this from actually calculating the values. * * @param ft FragTracker to hold the packet * @param fragStart Pointer to start of the packet data * @param fragLength Length of packet data * @param len Length of this fragment * @param slide Adjustment to make to left side of data (for left overlaps) * @param trunc Adjustment to maek to right side of data (for right overlaps) * @param frag_offset Offset for this fragment * @prarm left FragNode prior to this one * @param retFrag this one after its inserted (returned) * * @return status * @retval FRAG_INSERT_FAILED Memory problem, insertion failed * @retval FRAG_INSERT_OK All okay */ static int AddFragNode(FragTracker *ft, Packet *p, Frag3Context *f3context, const uint8_t *fragStart, int16_t fragLength, char lastfrag, int16_t len, uint16_t slide, uint16_t trunc, uint16_t frag_offset, Frag3Frag *left, Frag3Frag **retFrag) { Frag3Frag *newfrag = NULL; /* new frag container */ int16_t newSize = len - slide - trunc; if (newSize <= 0) { /* * zero size frag */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "zero size frag after left & right trimming " "(len: %d slide: %d trunc: %d)\n", len, slide, trunc);); f3stats.discards++; #ifdef DEBUG_MSGS newfrag = ft->fraglist; while (newfrag) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Size: %d, offset: %d, len %d, " "Prev: 0x%x, Next: 0x%x, This: 0x%x, Ord: %d, %s\n", newfrag->size, newfrag->offset, newfrag->flen, newfrag->prev, newfrag->next, newfrag, newfrag->ord, newfrag->last ? "Last":"");); newfrag = newfrag->next; } #endif return FRAG_INSERT_ANOMALY; } /* * grab/generate a new frag node */ if(!frag3_eval_config->use_prealloc) { if(frag3_mem_in_use > frag3_eval_config->memcap) { if (Frag3Prune(ft) == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3Insert: Pruning failed\n");); return FRAG_INSERT_FAILED; } } /* * build a frag struct to track this particular fragment */ newfrag = (Frag3Frag *) SnortPreprocAlloc(1, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use += sizeof(Frag3Frag); /* * allocate some space to hold the actual data */ newfrag->fptr = (uint8_t*)SnortPreprocAlloc(1, fragLength, PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use += fragLength; sfBase.frag3_mem_in_use = frag3_mem_in_use; } else { /* * fragments are preallocated, grab one from the list */ while((newfrag = Frag3PreallocPop()) == NULL) { if (Frag3Prune(ft) == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3Insert: Pruning failed\n");); return FRAG_INSERT_FAILED; } } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "got newfrag (%p) from prealloc\n", newfrag);); } f3stats.fragnodes_created++; newfrag->flen = fragLength; memcpy(newfrag->fptr, fragStart, fragLength); newfrag->ord = ft->ordinal++; /* * twiddle the frag values for overlaps */ newfrag->data = newfrag->fptr + slide; newfrag->size = newSize; newfrag->offset = frag_offset; newfrag->last = lastfrag; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[+] Adding new frag, offset %d, size %d\n" " nf->data = nf->fptr(%p) + slide (%d)\n" " nf->size = len(%d) - slide(%d) - trunc(%d)\n", newfrag->offset, newfrag->size, newfrag->fptr, slide, fragLength, slide, trunc);); /* * insert the new frag into the list */ Frag3FraglistAddNode(ft, left, newfrag); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[*] Inserted new frag %d@%d ptr %p data %p prv %p nxt %p\n", newfrag->size, newfrag->offset, newfrag, newfrag->data, newfrag->prev, newfrag->next);); /* * record the current size of the data in the fraglist */ ft->frag_bytes += newfrag->size; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[#] accumulated bytes on FragTracker %d, count" " %d\n", ft->frag_bytes, ft->fraglist_count);); *retFrag = newfrag; return FRAG_INSERT_OK; } /** * Duplicate a frag node and insert it into the list. * * @param ft FragTracker to hold the packet * @prarm left FragNode prior to this one (to be dup'd) * @param retFrag this one after its inserted (returned) * * @return status * @retval FRAG_INSERT_FAILED Memory problem, insertion failed * @retval FRAG_INSERT_OK All okay */ static int DupFragNode(FragTracker *ft, Frag3Frag *left, Frag3Frag **retFrag) { Frag3Frag *newfrag = NULL; /* new frag container */ /* * grab/generate a new frag node */ if(!frag3_eval_config->use_prealloc) { if(frag3_mem_in_use > frag3_eval_config->memcap) { if (Frag3Prune(ft) == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3Insert: Pruning failed\n");); return FRAG_INSERT_FAILED; } } /* * build a frag struct to track this particular fragment */ newfrag = (Frag3Frag *) SnortPreprocAlloc(1, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use += sizeof(Frag3Frag); /* * allocate some space to hold the actual data */ newfrag->fptr = (uint8_t*)SnortPreprocAlloc(1, left->flen, PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use += left->flen; sfBase.frag3_mem_in_use = frag3_mem_in_use; } else { /* * fragments are preallocated, grab one from the list */ while((newfrag = Frag3PreallocPop()) == NULL) { if (Frag3Prune(ft) == 0) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3Insert: Pruning failed\n");); return FRAG_INSERT_FAILED; } } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "got newfrag (%p) from prealloc\n", newfrag);); } f3stats.fragnodes_created++; newfrag->ord = ft->ordinal++; /* * twiddle the frag values for overlaps */ newfrag->flen = left->flen; memcpy(newfrag->fptr, left->fptr, newfrag->flen); newfrag->data = newfrag->fptr + (left->data - left->fptr); newfrag->size = left->size; newfrag->offset = left->offset; newfrag->last = left->last; /* * insert the new frag into the list */ Frag3FraglistAddNode(ft, left, newfrag); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[*] Inserted new frag %d@%d ptr %p data %p prv %p nxt %p\n", newfrag->size, newfrag->offset, newfrag, newfrag->data, newfrag->prev, newfrag->next);); /* * record the current size of the data in the fraglist */ ft->frag_bytes += newfrag->size; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[#] accumulated bytes on FragTracker %d, count" " %d\n", ft->frag_bytes, ft->fraglist_count);); *retFrag = newfrag; return FRAG_INSERT_OK; } /** checks for tiny fragments and raises appropriate alarm * * @param p Current packet to insert * @param ft FragTracker to hold the packet * @param fkey FragKey with the current FragTracker's key info * @param f3context context of the current engine for target-based defrag info * * @returns 1 if tiny fragment was detected, 0 otherwise */ static inline int checkTinyFragments( Frag3Context *f3context, Packet *p, unsigned int trimmedLength ) { //Snort may need to raise a separate event if //only trimmed length is tiny. if(p->mf) { ///detect tiny fragments before processing overlaps. if (f3context->min_fragment_length) { if (p->ip_frag_len <= f3context->min_fragment_length) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3: Received fragment size(%d) is not more than configured min_fragment_length (%d)\n", p->ip_frag_len, f3context->min_fragment_length);); EventTinyFragments(f3context); return 1; } ///detect tiny fragments after processing overlaps. if (trimmedLength <= f3context->min_fragment_length) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3: # of New octets in Received fragment(%d) is not more than configured min_fragment_length (%d)\n", trimmedLength, f3context->min_fragment_length);); EventTinyFragments(f3context); return 1; } } } return 0; } int frag3DropAllFragments( Packet *p ) { FragTracker *ft = (FragTracker *)p->fragtracker; //drop this and all following fragments if (ft && !(ft->frag_flags & FRAG_DROP_FRAGMENTS)) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3: Will drop all fragments on this packet\n");); ft->frag_flags |= FRAG_DROP_FRAGMENTS; } return 0; } /** * This is where the rubber hits the road. Insert the new fragment's data * into the current FragTracker's fraglist, doing anomaly detection and * handling overlaps in a target-based manner. * * @param p Current packet to insert * @param ft FragTracker to hold the packet * @param fkey FragKey with the current FragTracker's key info * @param f3context context of the current engine for target-based defrag info * * @return status * @retval FRAG_INSERT_TIMEOUT FragTracker has timed out and been dropped * @retval FRAG_INSERT_ATTACK Attack detected during insertion * @retval FRAG_INSERT_ANOMALY Anomaly detected during insertion * @retval FRAG_INSERT_TTL Delta of TTL values beyond configured value * @retval FRAG_INSERT_OK Fragment has been inserted successfully */ static int Frag3Insert(Packet *p, FragTracker *ft, FRAGKEY *fkey, Frag3Context *f3context) { uint16_t orig_offset; /* offset specified in this fragment header */ uint16_t frag_offset; /* calculated offset for this fragment */ uint32_t frag_end; /* calculated end point for this fragment */ int16_t trunc = 0; /* we truncate off the tail */ int32_t overlap = 0; /* we overlap on either end of the frag */ int16_t len = 0; /* calculated size of the fragment */ int16_t slide = 0; /* slide up the front of the current frag */ int done = 0; /* flag for right-side overlap handling loop */ int addthis = 1; /* flag for right-side overlap handling loop */ int i = 0; /* counter */ int firstLastOk; int ret = FRAG_INSERT_OK; unsigned char lastfrag = 0; /* Set to 1 when this is the 'last' frag */ unsigned char alerted_overlap = 0; /* Set to 1 when alerted */ Frag3Frag *right = NULL; /* frag ptr for right-side overlap loop */ Frag3Frag *newfrag = NULL; /* new frag container */ Frag3Frag *left = NULL; /* left-side overlap fragment ptr */ Frag3Frag *idx = NULL; /* indexing fragment pointer for loops */ Frag3Frag *dump_me = NULL; /* frag ptr for complete overlaps to dump */ const uint8_t *fragStart; int16_t fragLength; uint32_t reassembled_pkt_size; PROFILE_VARS; sfBase.iFragInserts++; PREPROC_PROFILE_START(frag3InsertPerfStats); if (IS_IP6(p) && (p->frag_offset == 0)) { IP6Frag *fragHdr = (IP6Frag *)p->ip6_extensions[p->ip6_frag_index].data; if (ft->protocol != fragHdr->ip6f_nxt) { ft->protocol = fragHdr->ip6f_nxt; } } /* * Check to see if this fragment is the first or last one and * set the appropriate flags and values in the FragTracker */ firstLastOk = Frag3CheckFirstLast(p, ft); fragStart = p->ip_frag_start; //fragStart = (uint8_t *)p->iph + GET_IPH_HLEN(p) * 4; /* Use the actual length here, because packet may have been * truncated. Don't want to try to copy more than we actually * captured. */ //len = fragLength = p->actual_ip_len - GET_IPH_HLEN(p) * 4; len = fragLength = p->ip_frag_len; #ifdef DEBUG_MSGS if (p->actual_ip_len != ntohs(GET_IPH_LEN(p))) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "IP Actual Length (%d) != specified length (%d), " "truncated packet (%d)?\n", p->actual_ip_len, ntohs(GET_IPH_LEN(p)), pkt_snaplen);); } #endif /* * setup local variables for tracking this frag */ orig_offset = frag_offset = p->frag_offset << 3; /* Reset the offset to handle the weird Solaris case */ if (firstLastOk == FRAG_LAST_OFFSET_ADJUST) frag_offset = (uint16_t)ft->calculated_size; frag_end = frag_offset + fragLength; /* * Copy the calculated size of the reassembled * packet in a local variable. */ reassembled_pkt_size = ft->calculated_size; /* * might have last frag... */ if(!p->mf) { if ((frag_end > ft->calculated_size) && (firstLastOk == FRAG_LAST_OFFSET_ADJUST)) { ft->calculated_size = frag_end; } // ft->frag_flags |= FRAG_GOT_LAST; // ft->calculated_size = (p->frag_offset << 3) + fragLength; lastfrag = 1; } else { uint16_t oldfrag_end; /* * all non-last frags are supposed to end on 8-byte boundries */ if(frag_end & 7) { /* * bonk/boink/jolt/etc attack... */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[..] Short frag (Bonk, etc) attack!\n");); EventAnomShortFrag(f3context); /* don't return, might still be interesting... */ } /* can't have non-full fragments... */ oldfrag_end = frag_end; frag_end &= ~7; /* Adjust len to take into account the jolting/non-full fragment. */ len -= (oldfrag_end - frag_end); /* * if the end of this frag is greater than the max frag size we have a * problem */ if(frag_end > ft->calculated_size) { if(ft->frag_flags & FRAG_GOT_LAST) { /* oversize frag attack */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[..] Oversize frag pkt!\n");); EventAnomOversize(f3context); PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ANOMALY; } ft->calculated_size = frag_end; } } if(frag_end == frag_offset) { /* * zero size frag... */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[..] Zero size frag!\n");); if(f3context->frag3_alerts & FRAG3_DETECT_ANOMALIES) { EventAnomZeroFrag(f3context); } PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ANOMALY; } if(frag_end > IP_MAXPACKET) { /* * oversize pkt... */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[..] Oversize frag!\n");); EventAnomBadsizeLg(f3context); ft->frag_flags |= FRAG_BAD; /* * Restore the value of ft->calculated_size */ ft->calculated_size = reassembled_pkt_size; PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ANOMALY; } /* * This may alert on bad options, but we still want to * insert the packet */ Frag3HandleIPOptions(ft, p); ft->frag_pkts++; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Walking frag list (%d nodes), new frag %d@%d\n", ft->fraglist_count, fragLength, frag_offset);); /* * Need to figure out where in the frag list this frag should go * and who its neighbors are */ for(idx = ft->fraglist; idx; idx = idx->next) { i++; right = idx; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "%d right o %d s %d ptr %p prv %p nxt %p\n", i, right->offset, right->size, right, right->prev, right->next);); if(right->offset >= frag_offset) { break; } left = right; } /* * null things out if we walk to the end of the list */ if(idx == NULL) right = NULL; /* * handle forward (left-side) overlaps... */ if(left) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Dealing with previous (left) frag %d@%d\n", left->size, left->offset);); /* * generate the overlap of the current packet fragment * over this left-side fragment */ /* NOTE: If frag_offset is really large, overlap can be * negative because its stored as a 32bit int. */ overlap = left->offset + left->size - frag_offset; if(overlap > 0) { f3stats.overlaps++; ft->overlap_count++; if(frag_end < ft->calculated_size || ((ft->frag_flags & FRAG_GOT_LAST) && frag_end != ft->calculated_size)) { if (!p->mf) { /* * teardrop attack... */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[..] Teardrop attack!\n");); EventAttackTeardrop(f3context); ft->frag_flags |= FRAG_BAD; PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ATTACK; } } /* * Ok, we've got an overlap so we need to handle it. * * The target-based modes here match the data generated by * Paxson's Active Mapping paper as do the policy types. */ switch(ft->frag_policy) { /* * new frag gets moved around */ case FRAG_POLICY_LINUX: case FRAG_POLICY_FIRST: case FRAG_POLICY_WINDOWS: case FRAG_POLICY_SOLARIS: case FRAG_POLICY_BSD: frag_offset += (int16_t)overlap; slide = (int16_t)overlap; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "left overlap, new frag moves: %d bytes, " "slide: %d\n", overlap, slide);); if(frag_end <= frag_offset) { /* * zero size frag */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "zero size frag\n");); EventAnomZeroFrag(f3context); PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ANOMALY; } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "left overlap, " "truncating new pkt (slide: %d)\n", slide);); break; /* * new frag stays where it is, overlapee (existing frag) * gets whacked */ case FRAG_POLICY_BSD_RIGHT: if (left->offset + left->size >= frag_offset + len) { /* BSD-right (HP Printers) favor new fragments with * lower/equal offset, EXCEPT when the existing * fragment ends with at a higher/equal offset. */ frag_offset += (int16_t)overlap; slide = (int16_t)overlap; goto left_overlap_last; } /* fall through */ case FRAG_POLICY_LAST: if ((left->offset < frag_offset) && (left->offset + left->size > frag_offset + len)) { /* The new frag is overlapped on both sides by an * existing frag -- existing frag needs to be split * and the new frag inserted in the middle. * * Need to duplciate left. Adjust that guys * offset by + (frag_offset + len) and * size by - (frag_offset + len - left->offset). */ ret = DupFragNode(ft, left, &right); if (ret != FRAG_INSERT_OK) { /* Some warning here, * no, its done in AddFragNode */ PREPROC_PROFILE_END(frag3InsertPerfStats); return ret; } left->size -= (int16_t)overlap; ft->frag_bytes -= (int16_t)overlap; right->offset = frag_offset + len; right->size -= (frag_offset + len - left->offset); right->data += (frag_offset + len - left->offset); ft->frag_bytes -= (frag_offset + len - left->offset); } else { left->size -= (int16_t)overlap; ft->frag_bytes -= (int16_t)overlap; } left_overlap_last: DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[!!] left overlap, " "truncating old pkt (offset: %d overlap: %d)\n", left->offset, overlap);); if (left->size <= 0) { dump_me = left; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "retrans, " "dumping old frag (offset: %d overlap: %d)\n", dump_me->offset, overlap);); left = left->prev; Frag3FraglistDeleteNode(ft, dump_me); } break; } /* * frag can't end before it begins... */ if(frag_end < frag_offset) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "frag_end < frag_offset!");); if(f3context->frag3_alerts & FRAG3_DETECT_ANOMALIES) { EventAnomBadsizeSm(f3context); } PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ANOMALY; } } else { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "No left overlap!\n");); } } if ((uint16_t)fragLength > pkt_snaplen) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Overly large fragment %d 0x%x 0x%x %d\n", fragLength, GET_IPH_LEN(p), GET_IPH_OFF(p), p->frag_offset << 3);); PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_FAILED; } /* * handle tail (right-side) overlaps * * We have to walk thru all the right side frags until the offset of the * existing frag is greater than the end of the new frag */ while(right && (right->offset < frag_end) && !done) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Next (right)fragment %d@%d\n", right->size, right->offset);); #ifdef DEBUG_FRAG3 PrintFrag3Frag(right); #endif trunc = 0; overlap = frag_end - right->offset; if (overlap) { if(frag_end < ft->calculated_size || ((ft->frag_flags & FRAG_GOT_LAST) && frag_end != ft->calculated_size)) { if (!p->mf) { /* * teardrop attack... */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[..] Teardrop attack!\n");); EventAttackTeardrop(f3context); ft->frag_flags |= FRAG_BAD; PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ATTACK; } } } /* * partial right-side overlap, this will be the last frag to check */ if(overlap < right->size) { f3stats.overlaps++; ft->overlap_count++; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Right-side overlap %d bytes\n", overlap);); /* * once again, target-based policy processing */ switch(ft->frag_policy) { /* * existing fragment gets truncated */ case FRAG_POLICY_LAST: case FRAG_POLICY_LINUX: case FRAG_POLICY_BSD: if ((ft->frag_policy == FRAG_POLICY_BSD) && (right->offset == frag_offset)) { slide = (int16_t)(right->offset + right->size - frag_offset); frag_offset += (int16_t)slide; } else { right->offset += (int16_t)overlap; right->data += (int16_t)overlap; right->size -= (int16_t)overlap; ft->frag_bytes -= (int16_t)overlap; } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[!!] right overlap, " "truncating old frag (offset: %d, " "overlap: %d)\n", right->offset, overlap); DebugMessage(DEBUG_FRAG, "Exiting right overlap loop...\n");); if (right->size <= 0) { dump_me = right; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "retrans, " "dumping old frag (offset: %d overlap: %d)\n", dump_me->offset, overlap);); right = right->next; Frag3FraglistDeleteNode(ft, dump_me); } break; /* * new frag gets truncated */ case FRAG_POLICY_FIRST: case FRAG_POLICY_WINDOWS: case FRAG_POLICY_SOLARIS: case FRAG_POLICY_BSD_RIGHT: trunc = (int16_t)overlap; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[!!] right overlap, " "truncating new frag (offset: %d " "overlap: %d)\n", right->offset, overlap); DebugMessage(DEBUG_FRAG, "Exiting right overlap loop...\n");); break; } /* * all done, bail */ done = 1; } else { /* * we've got a full overlap */ if(!alerted_overlap && (f3context->frag3_alerts & FRAG3_DETECT_ANOMALIES)) { /* * retrans/full overlap */ EventAnomOverlap(f3context); alerted_overlap = 1; f3stats.overlaps++; ft->overlap_count++; } /* * handle the overlap in a target-based manner */ switch(ft->frag_policy) { /* * overlap is treated differently if there is more * data beyond the overlapped packet. */ case FRAG_POLICY_WINDOWS: case FRAG_POLICY_SOLARIS: case FRAG_POLICY_BSD: /* * Old packet is overlapped on both sides... * Drop the old packet. This follows a * POLICY_LAST model. */ if ((frag_end > right->offset + right->size) && (frag_offset < right->offset)) { dump_me = right; ft->frag_bytes -= right->size; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "retrans, " "dumping old frag (offset: %d overlap: %d)\n", dump_me->offset, overlap);); right = right->next; Frag3FraglistDeleteNode(ft, dump_me); break; } else { if ((ft->frag_policy == FRAG_POLICY_SOLARIS) || (ft->frag_policy == FRAG_POLICY_BSD)) { /* SOLARIS & BSD only */ if ((frag_end == right->offset + right->size) && (frag_offset < right->offset)) { /* If the frag overlaps an entire frag to the * right side of that frag, the old frag if * dumped -- this is a "policy last". */ goto right_overlap_last; } } } /* Otherwise, treat it as a POLICY_FIRST, * and trim accordingly. */ /* ie, fall through to the next case */ /* * overlap is rejected */ case FRAG_POLICY_FIRST: /* fix for bug 17823 */ if (right->offset == frag_offset) { slide = (int16_t)(right->offset + right->size - frag_offset); frag_offset += (int16_t)slide; left = right; right = right->next; } else { trunc = (int16_t)overlap; } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "right overlap, " "rejecting new overlap data (overlap: %d, " "trunc: %d)\n", overlap, trunc);); if (frag_end - trunc <= frag_offset) { /* * zero size frag */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "zero size frag (len: %d overlap: %d)\n", fragLength, overlap);); f3stats.discards++; PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_ANOMALY; } { uint16_t curr_end; /* Full overlapping an already received packet * and there are more packets beyond that fully * overlapped one. * Arrgh. Need to insert this guy in chunks. */ checkTinyFragments(f3context, p, len-slide-trunc); ret = AddFragNode(ft, p, f3context, fragStart, fragLength, 0, len, slide, trunc, frag_offset, left, &newfrag); if (ret != FRAG_INSERT_OK) { /* Some warning here, * no, its done in AddFragNode */ PREPROC_PROFILE_END(frag3InsertPerfStats); return ret; } curr_end = newfrag->offset + newfrag->size; /* Find the next gap that this one might fill in */ while (right && (curr_end == right->offset) && (right->offset < frag_end)) { curr_end = right->offset + right->size; left = right; right = right->next; } if (right && (right->offset < frag_end)) { /* Adjust offset to end of 'left' */ if (left) frag_offset = left->offset + left->size; else frag_offset = orig_offset; /* Overlapping to the left by a good deal now */ slide = frag_offset - orig_offset; /* * Reset trunc, in case the next one kicks us * out of the loop. This packet will become the * right-most entry so far. Don't truncate any * further. */ trunc = 0; if (right) continue; } if (curr_end < frag_end) { /* Insert this guy in his proper spot, * adjust offset to the right-most endpoint * we saw. */ slide = left->offset + left->size - frag_offset; frag_offset = curr_end; trunc = 0; } else { addthis = 0; } } break; /* * retrans accepted, dump old frag */ right_overlap_last: case FRAG_POLICY_BSD_RIGHT: case FRAG_POLICY_LAST: case FRAG_POLICY_LINUX: dump_me = right; ft->frag_bytes -= right->size; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "retrans, " "dumping old frag (offset: %d overlap: %d)\n", dump_me->offset, overlap);); right = right->next; Frag3FraglistDeleteNode(ft, dump_me); break; } } } ///detect tiny fragments but continue processing checkTinyFragments(f3context, p, len-slide-trunc); if ((f3context->overlap_limit) && (ft->overlap_count >= f3context->overlap_limit)) { //overlap limit exceeded. Raise event on all subsequent fragments DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Reached overlap limit.\n");); EventExcessiveOverlap(f3context); PREPROC_PROFILE_END(frag3InsertPerfStats); return FRAG_INSERT_OVERLAP_LIMIT; } if (addthis) { ret = AddFragNode(ft, p, f3context, fragStart, fragLength, lastfrag, len, slide, trunc, frag_offset, left, &newfrag); } else { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Fully truncated right overlap\n");); } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3Insert(): returning normally\n");); PREPROC_PROFILE_END(frag3InsertPerfStats); return ret; } /** * Check to see if a FragTracker has met all of its completion criteria * * @param ft FragTracker to check * * @return status * @retval 1 If the FragTracker is ready to be rebuilt * @retval 0 If the FragTracker hasn't fulfilled its completion criteria */ static inline int Frag3IsComplete(FragTracker *ft) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[$] Checking completion criteria\n");); /* * check to see if the first and last frags have arrived */ if((ft->frag_flags & FRAG_GOT_FIRST) && (ft->frag_flags & FRAG_GOT_LAST)) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, " Got First and Last frags\n");); /* * if we've accumulated enough data to match the calculated size * of the defragg'd packet, return 1 */ if(ft->frag_bytes == ft->calculated_size) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, " [!] frag_bytes = calculated_size!\n");); sfBase.iFragCompletes++; return 1; } if (ft->frag_bytes > ft->calculated_size) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, " [!] frag_bytes > calculated_size!\n");); sfBase.iFragCompletes++; return 1; } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, " Calc size (%d) != frag bytes (%d)\n", ft->calculated_size, ft->frag_bytes);); /* * no dice */ return 0; } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, " Missing First or Last frags (frag_flags: 0x%X)\n", ft->frag_flags);); return 0; } /** * Reassemble the packet from the data in the FragTracker and reinject into * Snort's packet analysis system * * @param ft FragTracker to rebuild * @param p Packet to fill in pseudopacket IP structs * * @return none */ static void Frag3Rebuild(FragTracker *ft, Packet *p) { uint8_t *rebuild_ptr = NULL; /* ptr to the start of the reassembly buffer */ const uint8_t *rebuild_end; /* ptr to the end of the reassembly buffer */ Frag3Frag *frag; /* frag pointer for managing fragments */ int ret = 0; Packet* dpkt; PROFILE_VARS; // XXX NOT YET IMPLEMENTED - debugging PREPROC_PROFILE_START(frag3RebuildPerfStats); #ifdef GRE if ( p->encapsulated ) dpkt = encap_defrag_pkt; else #endif dpkt = defrag_pkt; Encode_Format(ENC_FLAG_DEF|ENC_FLAG_FWD, p, dpkt, PSEUDO_PKT_IP); /* * set the pointer to the end of the rebuild packet */ rebuild_ptr = (uint8_t*)dpkt->data; // the encoder ensures enough space for a maximum datagram rebuild_end = (uint8_t*)dpkt->data + IP_MAXPACKET; if (IS_IP4(p)) { /* * if there are IP options, copy those in as well * these are for the inner IP... */ if (ft->ip_options_data && ft->ip_options_len) { /* Adjust the IP header size in pseudo packet for the new length */ uint8_t new_ip_hlen = sizeof(*dpkt->iph) + ft->ip_options_len; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Adjusting IP Header to %d bytes\n", new_ip_hlen);); SET_IP_HLEN((IPHdr *)dpkt->iph, new_ip_hlen>>2); ret = SafeMemcpy(rebuild_ptr, ft->ip_options_data, ft->ip_options_len, rebuild_ptr, rebuild_end); if (ret == SAFEMEM_ERROR) { /*XXX: Log message, failed to copy */ ft->frag_flags = ft->frag_flags | FRAG_REBUILT; return; } rebuild_ptr += ft->ip_options_len; } else if (ft->copied_ip_options_len) { /* XXX: should we log a warning here? there were IP options * copied across all fragments, EXCEPT the offset 0 fragment. */ } /* * clear the packet fragment fields */ ((IPHdr *)dpkt->iph)->ip_off = 0x0000; dpkt->frag_flag = 0; DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "[^^] Walking fraglist:\n");); } /* * walk the fragment list and rebuild the packet */ for(frag = ft->fraglist; frag; frag = frag->next) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, " frag: %p\n" " frag->data: %p\n" " frag->offset: %d\n" " frag->size: %d\n" " frag->prev: %p\n" " frag->next: %p\n", frag, frag->data, frag->offset, frag->size, frag->prev, frag->next);); /* * We somehow got a frag that had data beyond the calculated * end. Don't want to include it. */ if ((frag->offset + frag->size) > (uint16_t)ft->calculated_size) continue; /* * try to avoid buffer overflows... */ if (frag->size) { ret = SafeMemcpy(rebuild_ptr+frag->offset, frag->data, frag->size, rebuild_ptr, rebuild_end); if (ret == SAFEMEM_ERROR) { /*XXX: Log message, failed to copy */ ft->frag_flags = ft->frag_flags | FRAG_REBUILT; return; } } } if (IS_IP4(p)) { /* * tell the rest of the system that this is a rebuilt fragment */ dpkt->packet_flags |= PKT_REBUILT_FRAG; dpkt->frag_flag = 0; dpkt->dsize = (uint16_t)ft->calculated_size; Encode_Update(dpkt); } else /* Inner/only is IP6 */ { IP6RawHdr* rawHdr = (IP6RawHdr*)dpkt->raw_ip6h; if ( !rawHdr ) { /*XXX: Log message, failed to copy */ ft->frag_flags = ft->frag_flags | FRAG_REBUILT; return; } /* IPv6 Header is already copied over, as are all of the extensions * that were not part of the fragmented piece. */ /* Set the 'next' protocol */ if (p->ip6_frag_index > 0) { // FIXTHIS use of last_extension works but is ugly IP6Extension *last_extension = (IP6Extension *) (dpkt->pkt + (p->ip6_extensions[p->ip6_frag_index -1].data - p->pkt)); last_extension->ip6e_nxt = ft->protocol; } else { rawHdr->ip6nxt = ft->protocol; } dpkt->dsize = (uint16_t)ft->calculated_size; Encode_Update(dpkt); } pc.rebuilt_frags++; sfBase.iFragFlushes++; /* Rebuild is complete */ PREPROC_PROFILE_END(frag3RebuildPerfStats); /* * process the packet through the detection engine */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Processing rebuilt packet:\n");); f3stats.reassembles++; UpdateIPReassStats(&sfBase, dpkt->pkth->caplen); #if defined(DEBUG_FRAG3) && defined(DEBUG) /* * Note, that this won't print out the IP Options or any other * data that is established when the packet is decoded. */ if (DEBUG_FRAG & GetDebugLevel()) { //ClearDumpBuf(); printf("++++++++++++++++++Frag3 DEFRAG'd PACKET++++++++++++++\n"); PrintIPPkt(stdout, dpkt->iph->ip_proto, &dpkt); printf("++++++++++++++++++Frag3 DEFRAG'd PACKET++++++++++++++\n"); //ClearDumpBuf(); } #endif SnortEventqPush(); ProcessPacket(dpkt, dpkt->pkth, dpkt->pkt, ft); SnortEventqPop(); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Done with rebuilt packet, marking rebuilt...\n");); ft->frag_flags = ft->frag_flags | FRAG_REBUILT; } /** * Delete a Frag3Frag struct * * @param frag Fragment to delete * * @return none */ static void Frag3DeleteFrag(Frag3Frag *frag) { /* * delete the fragment either in prealloc or dynamic mode */ if(!frag3_eval_config->use_prealloc) { SnortPreprocFree(frag->fptr, frag->flen, PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use -= frag->flen; SnortPreprocFree(frag, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_SESSION); frag3_mem_in_use -= sizeof(Frag3Frag); sfBase.frag3_mem_in_use = frag3_mem_in_use; } else { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "o %d s %d ptr %p prv %p nxt %p\n", frag->offset, frag->size, frag, frag->prev, frag->next);); Frag3PreallocPush(frag); } f3stats.fragnodes_released++; } /** * Delete the contents of a FragTracker, in this instance that just means to * dump the fraglist. The sfxhash system deletes the actual FragTracker mem. * * @param ft FragTracker to delete * * @return none */ static void Frag3DeleteTracker(FragTracker *ft) { Frag3Frag *idx = ft->fraglist; /* pointer to the fraglist to delete */ Frag3Frag *dump_me = NULL; /* ptr to the Frag3Frag element to drop */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3DeleteTracker %d nodes to dump\n", ft->fraglist_count);); /* * delete all the nodes in a fraglist */ while(idx) { dump_me = idx; idx = idx->next; Frag3DeleteFrag(dump_me); } ft->fraglist = NULL; if (ft->ip_options_data) { SnortPreprocFree(ft->ip_options_data, ft->ip_options_len, PP_FRAG3, PP_MEM_CATEGORY_SESSION); ft->ip_options_data = NULL; } return; } /** * Remove a FragTracker from the f_cache hash table * * @param key FragKey of the FragTracker to be removed * @param data unused in this function * * @return none */ static void Frag3RemoveTracker(void *key, void *data) { /* * sfxhash maintains its own self preservation stuff/node freeing stuff */ if(sfxhash_remove(f_cache, key) != SFXHASH_OK) { ErrorMessage("sfxhash_remove() failed in frag3!\n"); } return; } /** * This is the auto-node-release function that gets handed to the sfxhash table * at initialization. Handles deletion of sfxhash table data members. * * @param key FragKey of the element to be freed * @param data unused in this implementation * * Now Returns 0 because we want to say, yes, delete that hash entry!!! */ static int Frag3AutoFree(void *key, void *data) { FragTracker *ft = (FragTracker *)data; tSfPolicyUserContextId config; tSfPolicyId policy_id; Frag3Config *pPolicyConfig = NULL; if (ft == NULL) return 0; config = ft->config; policy_id = ft->policy_id; pPolicyConfig = (Frag3Config *)sfPolicyUserDataGet(config, policy_id); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Calling Frag3DeleteTracker()\n");); Frag3DeleteTracker(ft); sfBase.iFragDeletes++; sfBase.iFragAutoFrees++; sfBase.iCurrentFrags--; f3stats.fragtrackers_autoreleased++; if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (config != frag3_config)) { Frag3FreeConfig(pPolicyConfig); sfPolicyUserDataClear (config, policy_id); /* No more outstanding policies for this config */ if (sfPolicyUserPolicyGetActive(config) == 0) Frag3FreeConfigs(config); } } return 0; } /** * This is the user free function that gets handed to the sfxhash table * at initialization. Handles deletion of sfxhash table data members. * * @param key FragKey of the element to be freed * @param data unused in this implementation * * Now Returns 0 because we want to say, yes, delete that hash entry!!! */ static int Frag3UserFree(void *key, void *data) { FragTracker *ft = (FragTracker *)data; tSfPolicyUserContextId config; tSfPolicyId policy_id; Frag3Config *pPolicyConfig = NULL; if (ft == NULL) return 0; config = ft->config; policy_id = ft->policy_id; pPolicyConfig = (Frag3Config *)sfPolicyUserDataGet(config, policy_id); DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Calling Frag3DeleteTracker()\n");); Frag3DeleteTracker(ft); sfBase.iFragDeletes++; sfBase.iCurrentFrags--; f3stats.fragtrackers_released++; if (pPolicyConfig != NULL) { pPolicyConfig->ref_count--; if ((pPolicyConfig->ref_count == 0) && (config != frag3_config)) { Frag3FreeConfig(pPolicyConfig); sfPolicyUserDataClear (config, policy_id); /* No more outstanding policies for this config */ if (sfPolicyUserPolicyGetActive(config) == 0) Frag3FreeConfigs(config); } } return 0; } /** * This function gets called either when we run out of prealloc nodes or when * the memcap is exceeded. Its job is to free memory up in frag3 by deleting * old/stale data. Currently implemented using a simple LRU pruning * technique, could probably benefit from having some sort of tail selection * randomization added to it. Additonally, right now when we hit the wall we * try to drop at least enough memory to satisfy the "ten_percent" value. * Hopefully that's not too aggressive, salt to taste! * * @param none * * @return none */ static int Frag3Prune(FragTracker *not_me) { SFXHASH_NODE *hnode; int found_this = 0; int pruned = 0; #ifdef DEBUG /* Use these to print out whether the frag tracker has * expired or not. */ FragTracker *ft; struct timeval *fttime; /* FragTracker timestamp */ #endif sfBase.iFragFaults++; f3stats.prunes++; if(!frag3_eval_config->use_prealloc) { //while(frag3_mem_in_use > (frag3_eval_config->memcap-globa_config->ten_percent)) DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "(spp_frag3) Frag3Prune: Pruning by memcap! ");); while((frag3_mem_in_use > frag3_eval_config->memcap) || (f_cache->count > (frag3_eval_config->max_frags - 5))) { hnode = sfxhash_lru_node(f_cache); if(!hnode) { break; } if (hnode && hnode->data == not_me) { if (found_this) { /* Uh, problem... we've gone through the entire list */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "(spp_frag3) Frag3Prune: Pruning by memcap - empty list! ");); return pruned; } sfxhash_gmovetofront(f_cache, hnode); found_this = 1; continue; } #ifdef DEBUG ft = hnode->data; fttime = &(ft->frag_time); if (CheckTimeout(pkttime,fttime,ft->context)==FRAG_TIMEOUT) { char *src_str = SnortStrdup(FragIPToStr(ft->sip, ft->ipver)); LogMessage("(spp_frag3) Frag3Prune: Fragment dropped (timeout)! " "[%s->%s ID: %d Count: %d]\n", src_str, FragIPToStr(ft->dip, ft->ipver), ft->id, ft->fraglist_count); free(src_str); f3stats.timeouts++; sfBase.iFragTimeouts++; } else { char *src_str = SnortStrdup(FragIPToStr(ft->sip, ft->ipver)); LogMessage("(spp_frag3) Frag3Prune: Fragment dropped (memory)! " "[%s->%s ID: %d Count: %d]\n", src_str, FragIPToStr(ft->dip, ft->ipver), ft->id, ft->fraglist_count); free(src_str); } #endif Frag3RemoveTracker(hnode->key, hnode->data); //sfBase.iFragDeletes++; //f3stats.fragtrackers_released++; pruned++; } } else { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "(spp_frag3) Frag3Prune: Pruning by prealloc! ");); while (prealloc_nodes_in_use > (frag3_eval_config->static_frags - frag3_eval_config->ten_percent)) { hnode = sfxhash_lru_node(f_cache); if(!hnode) { break; } if (hnode && hnode->data == not_me) { if (found_this) { /* Uh, problem... we've gone through the entire list */ DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "(spp_frag3) Frag3Prune: Pruning by prealloc - empty list! ");); return pruned; } sfxhash_gmovetofront(f_cache, hnode); found_this = 1; continue; } #ifdef DEBUG ft = hnode->data; fttime = &(ft->frag_time); if (CheckTimeout(pkttime,fttime,ft->context)==FRAG_TIMEOUT) { char *src_str = SnortStrdup(FragIPToStr(ft->sip, ft->ipver)); LogMessage("(spp_frag3) Frag3Prune: Fragment dropped (timeout)! " "[%s->%s ID: %d Count: %d]\n", src_str, FragIPToStr(ft->dip, ft->ipver), ft->id, ft->fraglist_count); free(src_str); f3stats.timeouts++; sfBase.iFragTimeouts++; } else { char *src_str = SnortStrdup(FragIPToStr(ft->sip, ft->ipver)); LogMessage("(spp_frag3) Frag3Prune: Fragment dropped (memory)! " "[%s->%s ID: %d Count: %d]\n", src_str, FragIPToStr(ft->dip, ft->ipver), ft->id, ft->fraglist_count); free(src_str); } #endif Frag3RemoveTracker(hnode->key, hnode->data); //sfBase.iFragDeletes++; //f3stats.fragtrackers_released++; pruned++; } } DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "(spp_frag3) Frag3Prune: Pruned %d nodes\n", pruned);); return pruned; } /** * Print out the frag stats from this run * * @param none * * @return none */ static void Frag3PrintStats(int exiting) { LogMessage("Frag3 statistics:\n"); LogMessage(" Total Fragments: %u\n", f3stats.total); LogMessage(" Frags Reassembled: %u\n", f3stats.reassembles); LogMessage(" Discards: %u\n", f3stats.discards); LogMessage(" Memory Faults: %u\n", f3stats.prunes); LogMessage(" Timeouts: %u\n", f3stats.timeouts); LogMessage(" Overlaps: %u\n", f3stats.overlaps); LogMessage(" Anomalies: %u\n", f3stats.anomalies); LogMessage(" Alerts: %u\n", f3stats.alerts); LogMessage(" Drops: %u\n", f3stats.drops); LogMessage(" FragTrackers Added: %u\n", f3stats.fragtrackers_created); LogMessage(" FragTrackers Dumped: %u\n", f3stats.fragtrackers_released); LogMessage("FragTrackers Auto Freed: %u\n", f3stats.fragtrackers_autoreleased); LogMessage(" Frag Nodes Inserted: %u\n", f3stats.fragnodes_created); LogMessage(" Frag Nodes Deleted: %u\n", f3stats.fragnodes_released); } static int Frag3FreeConfigsPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { Frag3Config *pPolicyConfig = (Frag3Config *)pData; //do any housekeeping before freeing Frag3Config sfPolicyUserDataClear (config, policyId); Frag3FreeConfig(pPolicyConfig); return 0; } static void Frag3FreeConfigs(tSfPolicyUserContextId config) { if (config == NULL) return; sfPolicyUserDataFreeIterate (config, Frag3FreeConfigsPolicy); sfPolicyConfigDelete(config); } static void Frag3FreeConfig(Frag3Config *config) { int engineIndex; Frag3Context *f3context; if (config == NULL) return; /* Cleanup the list of Frag3 engine contexts */ for (engineIndex = 0; engineIndex < config->numFrag3Contexts; engineIndex++) { f3context = config->frag3ContextList[engineIndex]; if (f3context->bound_addrs != NULL) { sfvar_free(f3context->bound_addrs); } SnortPreprocFree(f3context, sizeof(Frag3Context), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); } if (config->frag3ContextList != NULL) SnortPreprocFree(config->frag3ContextList, config->numFrag3Contexts * sizeof (Frag3Context *), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); SnortPreprocFree(config, sizeof(Frag3Config), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); } /** * CleanExit func required by preprocessors */ static void Frag3CleanExit(int signal, void *foo) { Frag3Frag *tmp; Frag3Config *pDefaultPolicyConfig = NULL; sfxhash_delete(f_cache); f_cache = NULL; pDefaultPolicyConfig = (Frag3Config *)sfPolicyUserDataGetDefault(frag3_config); /* Cleanup the preallocated frag nodes */ if(pDefaultPolicyConfig->use_prealloc) { tmp = Frag3PreallocPop(); while (tmp) { SnortPreprocFree(tmp->fptr, pkt_snaplen * sizeof(uint8_t), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); SnortPreprocFree(tmp, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); tmp = Frag3PreallocPop(); } } Frag3FreeConfigs(frag3_config); Encode_Delete(defrag_pkt); defrag_pkt = NULL; #ifdef GRE Encode_Delete(encap_defrag_pkt); encap_defrag_pkt = NULL; #endif } static void Frag3Reset(int signal, void *foo) { if (f_cache != NULL) sfxhash_make_empty(f_cache); } static void Frag3ResetStats(int signal, void *foo) { memset(&f3stats, 0, sizeof(f3stats)); } /** * Get a node from the prealloc_list * * @return pointer to a Frag3Frag preallocated structure or NULL if the list * is empty */ static inline Frag3Frag *Frag3PreallocPop(void) { Frag3Frag *node; if(prealloc_frag_list) { node = prealloc_frag_list; prealloc_frag_list = prealloc_frag_list->next; if (prealloc_frag_list) { prealloc_frag_list->prev = NULL; } else { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Using last prealloc frag node\n");); } node->next = NULL; node->prev = NULL; node->offset = 0; node->size = 0; node->flen = 0; node->last = 0; } else { return NULL; } if (!node->fptr) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3Frag fptr is NULL!\n");); } prealloc_nodes_in_use++; return node; } /** * Put a prealloc node back into the prealloc_cache pool * * @param node Prealloc node to place back in the pool * * @return none */ static inline void Frag3PreallocPush(Frag3Frag *node) { if (!prealloc_frag_list) { node->next = NULL; node->prev = NULL; } else { node->next = prealloc_frag_list; node->prev = NULL; prealloc_frag_list->prev = node; } prealloc_frag_list = node; node->data = NULL; if (!node->fptr) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Frag3Frag fptr is NULL!\n");); } prealloc_nodes_in_use--; return; } /** * Plug a Frag3Frag into the fraglist of a FragTracker * * @param ft FragTracker to put the new node into * @param prev ptr to preceeding Frag3Frag in fraglist * @param next ptr to following Frag3Frag in fraglist * @param node ptr to node to put in list * * @return none */ static inline void Frag3FraglistAddNode(FragTracker *ft, Frag3Frag *prev, Frag3Frag *node) { if(prev) { node->next = prev->next; node->prev = prev; prev->next = node; if (node->next) node->next->prev = node; else ft->fraglist_tail = node; } else { node->next = ft->fraglist; if (node->next) node->next->prev = node; else ft->fraglist_tail = node; ft->fraglist = node; } ft->fraglist_count++; return; } /** * Delete a Frag3Frag from a fraglist * * @param ft FragTracker to delete the frag from * @param node node to be deleted * * @return none */ static inline void Frag3FraglistDeleteNode(FragTracker *ft, Frag3Frag *node) { DEBUG_WRAP(DebugMessage(DEBUG_FRAG, "Deleting list node %p (p %p n %p)\n", node, node->prev, node->next);); if(node->prev) { node->prev->next = node->next; } else { ft->fraglist = node->next; } if(node->next) { node->next->prev = node->prev; } else { ft->fraglist_tail = node->prev; } Frag3DeleteFrag(node); ft->fraglist_count--; } /* ** ** NAME ** fpAddFragAlert:: ** ** DESCRIPTION ** This function flags an alert per frag tracker. ** ** FORMAL INPUTS ** Packet * - the packet to inspect ** OptTreeNode * - the rule that generated the alert ** ** FORMAL OUTPUTS ** int - 0 if not flagged ** 1 if flagged ** */ int fpAddFragAlert(Packet *p, OptTreeNode *otn) { FragTracker *ft = p->fragtracker; if ( !ft ) return 0; if ( !otn ) return 0; /* Only track a certain number of alerts per session */ if ( ft->alert_count >= MAX_FRAG_ALERTS ) return 0; ft->alert_gid[ft->alert_count] = otn->sigInfo.generator; ft->alert_sid[ft->alert_count] = otn->sigInfo.id; ft->alert_count++; return 1; } /* ** ** NAME ** fpFragAlerted:: ** ** DESCRIPTION ** This function indicates whether or not an alert has been generated previously ** in this session, but only if this is a rebuilt packet. ** ** FORMAL INPUTS ** Packet * - the packet to inspect ** OptTreeNode * - the rule that generated the alert ** ** FORMAL OUTPUTS ** int - 0 if alert NOT previously generated ** 1 if alert previously generated ** */ int fpFragAlerted(Packet *p, OptTreeNode *otn) { FragTracker *ft = p->fragtracker; SigInfo *si = &otn->sigInfo; int i; if ( !ft ) return 0; for ( i = 0; i < ft->alert_count; i++ ) { /* If this is a rebuilt packet and we've seen this alert before, return * that we have previously alerted on a non-rebuilt packet. */ if ( (p->packet_flags & PKT_REBUILT_FRAG) && ft->alert_gid[i] == si->generator && ft->alert_sid[i] == si->id ) { return 1; } } return 0; } #ifdef TARGET_BASED int fragGetApplicationProtocolId(Packet *p) { FragTracker *ft; /* Not caching this host_entry in the frag tracker so we can * swap the table out after processing this packet if we need * to. */ HostAttributeEntry *host_entry = NULL; uint16_t src_port = 0; uint16_t dst_port = 0; if (!p || !p->fragtracker) { return 0; } /* Must be a rebuilt frag... */ if (!(p->packet_flags & PKT_REBUILT_FRAG)) { return 0; } ft = (FragTracker *)p->fragtracker; if (ft->application_protocol != 0) { return ft->application_protocol; } switch (GET_IPH_PROTO(p)) { case IPPROTO_TCP: ft->ipprotocol = protocolReferenceTCP; src_port = p->sp; dst_port = p->dp; break; case IPPROTO_UDP: ft->ipprotocol = protocolReferenceUDP; src_port = p->sp; dst_port = p->dp; break; case IPPROTO_ICMP: ft->ipprotocol = protocolReferenceICMP; break; } host_entry = SFAT_LookupHostEntryBySrc(p); if (host_entry) { ft->application_protocol = getApplicationProtocolId(host_entry, ft->ipprotocol, src_port, SFAT_SERVICE); if (ft->application_protocol != 0) { return ft->application_protocol; } } host_entry = SFAT_LookupHostEntryByDst(p); if (host_entry) { ft->application_protocol = getApplicationProtocolId(host_entry, ft->ipprotocol, dst_port, SFAT_SERVICE); if (ft->application_protocol != 0) { return ft->application_protocol; } } return ft->application_protocol; } #endif #ifdef SNORT_RELOAD static void Frag3ReloadGlobal(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId frag3_swap_config = (tSfPolicyUserContextId)*new_config; Frag3Config *pDefaultPolicyConfig = NULL; Frag3Config *pCurrentPolicyConfig = NULL; tSfPolicyId policy_id = getParserPolicy(sc); if (!frag3_swap_config) { frag3_swap_config = sfPolicyConfigCreate(); *new_config = (void *)frag3_swap_config; } sfPolicyUserPolicySet (frag3_swap_config, policy_id); pDefaultPolicyConfig = (Frag3Config *)sfPolicyUserDataGetDefault(frag3_swap_config); pCurrentPolicyConfig = (Frag3Config *)sfPolicyUserDataGetCurrent(frag3_swap_config); if ((policy_id != getDefaultPolicy()) && (pDefaultPolicyConfig == NULL)) { ParseError("Frag3: Must configure default policy if other policies " "are going to be used.\n"); } if (pCurrentPolicyConfig != NULL) { FatalError("%s(%d) The frag3 global configuration can only be " "configured once.\n", file_name, file_line); } pCurrentPolicyConfig = (Frag3Config *)SnortPreprocAlloc(1, sizeof(Frag3Config), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); sfPolicyUserDataSetCurrent(frag3_swap_config, pCurrentPolicyConfig); /* setup default values */ pCurrentPolicyConfig->max_frags = DEFAULT_MAX_FRAGS; pCurrentPolicyConfig->memcap = FRAG_MEMCAP; pCurrentPolicyConfig->static_frags = 0; pCurrentPolicyConfig->use_prealloc = 0; Frag3ParseGlobalArgs(pCurrentPolicyConfig, args); if (policy_id != getDefaultPolicy()) { /* Can't set these in alternate policies */ pCurrentPolicyConfig->memcap = pDefaultPolicyConfig->memcap; pCurrentPolicyConfig->max_frags = pDefaultPolicyConfig->max_frags; pCurrentPolicyConfig->use_prealloc = pDefaultPolicyConfig->use_prealloc; pCurrentPolicyConfig->static_frags = pDefaultPolicyConfig->static_frags; } else if (pCurrentPolicyConfig->use_prealloc && (pCurrentPolicyConfig->static_frags == 0)) { pCurrentPolicyConfig->static_frags = (uint32_t)pCurrentPolicyConfig->memcap / (sizeof(Frag3Frag) + sizeof(uint8_t) * pkt_snaplen) + 1; pCurrentPolicyConfig->ten_percent = pCurrentPolicyConfig->static_frags >> 5; #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("static frags is zero and memcap : %lu\n",pCurrentPolicyConfig->memcap); printf("updated static_frags count : %u\n",pCurrentPolicyConfig->static_frags); } #endif } Frag3PrintGlobalConfig(pCurrentPolicyConfig); if ( !pCurrentPolicyConfig->disabled ) { AddFuncToPreprocList(sc, Frag3Defrag, PP_FRAG3_PRIORITY, PP_FRAG3, PROTO_BIT__IP); session_api->enable_preproc_all_ports( sc, PP_FRAG3, PROTO_BIT__IP ); } } static void Frag3ReloadEngine(struct _SnortConfig *sc, char *args, void **new_config) { tSfPolicyUserContextId frag3_swap_config; Frag3Context *context; /* context pointer */ tSfPolicyId policy_id = getParserPolicy(sc); Frag3Config *config = NULL; frag3_swap_config = (tSfPolicyUserContextId)GetRelatedReloadData(sc, "frag3_global"); config = (Frag3Config *)sfPolicyUserDataGet(frag3_swap_config, policy_id); if (config == NULL) { FatalError("[!] Unable to configure frag3 engine!\n" "Frag3 global config has not been established, " "please issue a \"preprocessor frag3_global\" directive\n"); } context = (Frag3Context *) SnortPreprocAlloc(1, sizeof(Frag3Context), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); context->frag_policy = FRAG_POLICY_DEFAULT; context->frag_timeout = FRAG_PRUNE_QUANTA; /* 60 seconds */ context->min_ttl = FRAG3_MIN_TTL; context->frag3_alerts = 0; Frag3ParseArgs(sc, args, context); if (context->bound_addrs == NULL) { if (config->default_context != NULL) FatalError("Frag3 => only one non-bound context can be specified.\n"); config->default_context = context; } if (config->frag3ContextList == NULL) { config->numFrag3Contexts = 1; config->frag3ContextList = (Frag3Context **)SnortPreprocAlloc(1, sizeof (Frag3Context *), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); } else { Frag3Context **tmpContextList; config->numFrag3Contexts++; tmpContextList = (Frag3Context **) SnortPreprocAlloc(config->numFrag3Contexts, sizeof (Frag3Context *), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); memcpy(tmpContextList, config->frag3ContextList, sizeof(Frag3Context *) * (config->numFrag3Contexts - 1)); SnortPreprocFree(config->frag3ContextList, (config->numFrag3Contexts - 1) * sizeof (Frag3Context *), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); config->frag3ContextList = tmpContextList; } config->frag3ContextList[config->numFrag3Contexts - 1] = context; Frag3PrintEngineConfig(context); } static int Frag3ReloadVerifyPolicy( struct _SnortConfig *sc, tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { Frag3Config *pPolicyConfig = (Frag3Config *)pData; if ( pPolicyConfig->disabled ) return 0; //do any housekeeping before freeing Frag3Config if ((policyId != getDefaultPolicy()) && (pPolicyConfig->numFrag3Contexts == 0)) { WarningMessage("Frag3VerifyConfig() policy engine required " "but not configured.\n"); return -1; } return 0; } static uint32_t Frag3MemReloadAdjust(unsigned maxWork) { Frag3Frag *tmp; SFXHASH_NODE *hnode; #ifdef REG_TEST static uint32_t addstaticfrag_cnt, delstaticfrag_cnt; #endif Frag3Config *newConfig = (Frag3Config *)sfPolicyUserDataGetCurrent(frag3_config); pkt_snaplen = DAQ_GetSnapLen(); if(newConfig->static_frags == old_static_frags) return maxWork; else if(newConfig->static_frags > old_static_frags) { if(newConfig->use_prealloc) { for(;maxWork && (newConfig->static_frags > old_static_frags); maxWork--,old_static_frags++) { tmp = (Frag3Frag *) SnortPreprocAlloc(1, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); tmp->fptr = (uint8_t *) SnortPreprocAlloc(pkt_snaplen, sizeof(uint8_t), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); Frag3PreallocPush(tmp); prealloc_nodes_in_use++; #ifdef REG_TEST addstaticfrag_cnt++; #endif } } } else { for(;maxWork && (newConfig->static_frags < old_static_frags); maxWork--,old_static_frags--) { tmp = Frag3PreallocPop(); if (tmp) { SnortPreprocFree(tmp->fptr, pkt_snaplen * sizeof(uint8_t), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); SnortPreprocFree(tmp, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); prealloc_nodes_in_use--; #ifdef REG_TEST delstaticfrag_cnt++; #endif } else { /*exhausted prealloc frags */ break; } } if(maxWork && (newConfig->static_frags < old_static_frags)) { for (;maxWork && (newConfig->static_frags < old_static_frags); maxWork--,old_static_frags--) { hnode = sfxhash_lru_node(f_cache); if (hnode) { Frag3RemoveTracker(hnode->key, hnode->data); tmp = Frag3PreallocPop(); if (tmp) { SnortPreprocFree(tmp->fptr, pkt_snaplen * sizeof(uint8_t), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); SnortPreprocFree(tmp, sizeof(Frag3Frag), PP_FRAG3, PP_MEM_CATEGORY_CONFIG); prealloc_nodes_in_use--; } #ifdef REG_TEST delstaticfrag_cnt++; #endif } } } } #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { if(newConfig->static_frags == old_static_frags) { if(addstaticfrag_cnt) printf("Total prealloc frag nodes added : %u\n",addstaticfrag_cnt); if(delstaticfrag_cnt) printf("Total prealloc frag nodes deleted : %u\n",delstaticfrag_cnt); } } #endif return maxWork; } static bool Frag3ReloadAdjust(bool idle, tSfPolicyId raPolicyId, void* userData) { unsigned initialMaxWork = idle ? 2048 : 5; unsigned maxWork; int iRet = -1; maxWork = Frag3MemReloadAdjust(initialMaxWork); if(maxWork) { iRet = sfxhash_change_memcap(f_cache, fcache_new_memcap, &maxWork); #ifdef REG_TEST if(iRet == SFXHASH_OK && maxWork) { if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("Successfully updated Frag cache memcap :%lu\n",f_cache->mc.memcap); } } #endif } return (maxWork != 0) ? true : false; } static int Frag3ReloadVerify(struct _SnortConfig *sc, void *swap_config) { int rval; tSfPolicyUserContextId frag3_swap_config = (tSfPolicyUserContextId)swap_config; Frag3Config *pCurrDefaultPolicyConfig = NULL; Frag3Config *pSwapDefaultPolicyConfig = NULL; tSfPolicyId policy_id = 0; pCurrDefaultPolicyConfig = (Frag3Config *)sfPolicyUserDataGetDefault(frag3_config); pSwapDefaultPolicyConfig = (Frag3Config *)sfPolicyUserDataGetDefault(frag3_swap_config); if ((frag3_swap_config == NULL) || (frag3_config == NULL)) return 0; if ((rval = sfPolicyUserDataIterate (sc, frag3_swap_config, Frag3ReloadVerifyPolicy))) return rval; policy_id = getParserPolicy(sc); if ((pSwapDefaultPolicyConfig->static_frags != pCurrDefaultPolicyConfig->static_frags) || (pSwapDefaultPolicyConfig->max_frags != pCurrDefaultPolicyConfig->max_frags)) { unsigned long max_frag_mem, table_mem; old_static_frags = pCurrDefaultPolicyConfig->static_frags; max_frag_mem = pSwapDefaultPolicyConfig->max_frags * ( sizeof(FragTracker) + sizeof(SFXHASH_NODE) + sizeof (FRAGKEY) + sizeof(SFXHASH_NODE *)); table_mem = (hashTableSize + 1) * sizeof(SFXHASH_NODE *); fcache_new_memcap = max_frag_mem + table_mem; #ifdef REG_TEST if (REG_TEST_FLAG_RELOAD & getRegTestFlags()) { printf("prealloc static frags old conf : %d new conf : %d\n",old_static_frags,pSwapDefaultPolicyConfig->static_frags); if (pSwapDefaultPolicyConfig->use_prealloc) printf("use_prealloc is enabled!\n"); else printf("use_prealloc is disabled!\n"); printf("Frag cache new memcap value: %lu\n",fcache_new_memcap); } #endif ReloadAdjustRegister(sc, "Frag3Reload", policy_id, &Frag3ReloadAdjust, NULL, NULL); } return 0; } static int Frag3ReloadSwapPolicy( tSfPolicyUserContextId config, tSfPolicyId policyId, void* pData ) { Frag3Config *pPolicyConfig = (Frag3Config *)pData; //do any housekeeping before freeing Frag3Config if (pPolicyConfig->ref_count == 0) { sfPolicyUserDataClear (config, policyId); Frag3FreeConfig(pPolicyConfig); } return 0; } static void * Frag3ReloadSwap(struct _SnortConfig *sc, void *swap_config) { tSfPolicyUserContextId frag3_swap_config = (tSfPolicyUserContextId)swap_config; tSfPolicyUserContextId old_config = frag3_config; if (frag3_swap_config == NULL) return NULL; frag3_config = frag3_swap_config; sfPolicyUserDataFreeIterate (old_config, Frag3ReloadSwapPolicy); if (sfPolicyUserPolicyGetActive(old_config) == 0) return (void *)old_config; return NULL; } static void Frag3ReloadSwapFree(void *data) { if (data == NULL) return; Frag3FreeConfigs((tSfPolicyUserContextId)data); } #endif snort-2.9.20/src/preprocessors/snort_httpinspect.h0000644000175000017500000002322214241077100020531 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ #ifndef __SNORT_HTTPINSPECT_H__ #define __SNORT_HTTPINSPECT_H__ #include "decode.h" #include "session_api.h" #include "stream_api.h" #include "hi_ui_config.h" #include "util_utf.h" #include "detection_util.h" #include "mempool.h" #include "str_search.h" #include "util_jsnorm.h" #include "memory_stats.h" #include extern MemPool *http_mempool; extern MemPool *mime_decode_mempool; extern MemPool *mime_log_mempool; extern DataBuffer HttpDecodeBuf; #ifdef PERF_PROFILING extern PreprocStats hi2PerfStats; extern PreprocStats hi2InitPerfStats; extern PreprocStats hi2PayloadPerfStats; extern PreprocStats hi2PseudoPerfStats; #endif /** ** The definition of the configuration separators in the snort.conf ** configure line. */ #define CONF_SEPARATORS " \t\n\r" #define MAX_METHOD_LEN 256 /* ** These are the definitions of the parser section delimiting ** keywords to configure HttpInspect. When one of these keywords ** are seen, we begin a new section. */ #define GLOBAL "global" #define GLOBAL_SERVER "global_server" #define SERVER "server" #define CLIENT "client" #define DEFAULT_HTTP_MEMCAP 150994944 /* 144 MB */ #define MIN_HTTP_MEMCAP 2304 #define MAX_HTTP_MEMCAP 603979776 /* 576 MB */ #define MAX_URI_EXTRACTED 2048 #define MAX_HOSTNAME 256 #define DEFAULT_MAX_GZIP_MEM 838860 #define GZIP_MEM_MIN 3276 #define MAX_GZIP_DEPTH 65535 #define DEFAULT_COMP_DEPTH 1460 #define DEFAULT_DECOMP_DEPTH 2920 #define DEFLATE_RAW_WBITS -15 #define DEFLATE_WBITS 15 #define GZIP_WBITS 31 #define XFF_MAX_PIPELINE_REQ 255 #define CONTENT_NONE 0x00 #define PARTIAL_CONTENT 0x01 #define FULL_CONTENT 0x02 #define GET_REQ_WITH_RANGE 0x04 typedef enum _HttpRespCompressType { HTTP_RESP_COMPRESS_TYPE__GZIP = 0x00000001, HTTP_RESP_COMPRESS_TYPE__DEFLATE = 0x00000002 } _HttpRespCompressType; typedef enum _DecompressStage { HTTP_DECOMP_START, HTTP_DECOMP_MID, HTTP_DECOMP_FIN } DecompressStage; typedef struct s_DECOMPRESS_STATE { uint8_t inflate_init; uint16_t compress_fmt; uint8_t decompress_data; int compr_bytes_read; int decompr_bytes_read; int compr_depth; int decompr_depth; z_stream d_stream; MemBucket *bkt; bool deflate_initialized; DecompressStage stage; } DECOMPRESS_STATE; typedef enum _ChunkLenState { CHUNK_LEN_DEFAULT = 0, CHUNK_LEN_INCOMPLETE } ChunkLenState; typedef struct s_HTTP_RESP_STATE { uint8_t inspect_body; uint8_t inspect_reassembled; uint8_t last_pkt_contlen; uint8_t last_pkt_chunked; uint32_t next_seq; uint32_t chunk_remainder; int data_extracted; uint32_t max_seq; bool flow_depth_excd; bool eoh_found; uint8_t look_for_partial_content; uint8_t chunk_len_state; }HTTP_RESP_STATE; typedef struct s_HTTP_LOG_STATE { uint32_t uri_bytes; uint32_t hostname_bytes; MemBucket *log_bucket; uint8_t *uri_extracted; uint8_t *hostname_extracted; }HTTP_LOG_STATE; typedef struct _Transaction { uint8_t tID; sfaddr_t *true_ip; struct _Transaction *next; }Transaction; typedef struct _HttpSessionData { uint64_t event_flags; HTTP_RESP_STATE resp_state; DECOMPRESS_STATE *decomp_state; HTTP_LOG_STATE *log_state; decode_utf_state_t utf_state; uint8_t log_flags; uint8_t cli_small_chunk_count; uint8_t srv_small_chunk_count; uint8_t http_req_id; uint8_t http_resp_id; uint8_t is_response; uint8_t tList_count; MimeState *mime_ssn; fd_session_p_t fd_state; Transaction *tList_start; Transaction *tList_end; } HttpSessionData; typedef struct _HISearch { char *name; int name_len; } HISearch; typedef struct _HiSearchToken { char *name; int name_len; int search_id; } HiSearchToken; typedef struct _HISearchInfo { int id; int index; int length; } HISearchInfo; #define COPY_URI 1 #define COPY_HOSTNAME 2 #define HTTP_LOG_URI 0x0001 #define HTTP_LOG_HOSTNAME 0x0002 #define HTTP_LOG_GZIP_DATA 0x0004 #define HTTP_LOG_JSNORM_DATA 0x0008 typedef enum _HiSearchIdEnum { HI_JAVASCRIPT = 0, HI_LAST } HiSearchId; typedef enum _HtmlSearchIdEnum { HTML_JS = 0, HTML_EMA, HTML_VB, HTML_LAST } HtmlSearchId; extern void *hi_javascript_search_mpse; extern void *hi_htmltype_search_mpse; extern HISearch hi_js_search[HI_LAST]; extern HISearch hi_html_search[HTML_LAST]; extern HISearch *hi_current_search; extern HISearchInfo hi_search_info; void ApplyFlowDepth(HTTPINSPECT_CONF *, Packet *, HttpSessionData *, int, int, uint32_t); int SnortHttpInspect(HTTPINSPECT_GLOBAL_CONF *GlobalConf, Packet *p); int ProcessGlobalConf(HTTPINSPECT_GLOBAL_CONF *, char *, int, char **saveptr); int PrintGlobalConf(HTTPINSPECT_GLOBAL_CONF *); int ProcessUniqueServerConf(struct _SnortConfig *, HTTPINSPECT_GLOBAL_CONF *, char *, int, char **); int HttpInspectInitializeGlobalConfig(HTTPINSPECT_GLOBAL_CONF *, char *, int); HttpSessionData * SetNewHttpSessionData(Packet *, void *); void FreeHttpSessionData(void *data); int GetHttpTrueIP(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int GetHttpGzipData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int GetHttpJSNormData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int GetHttpUriData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); int GetHttpHostnameData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type); void HI_SearchInit(void); void HI_SearchFree(void); int HI_SearchStrFound(void *, void *, int , void *, void *); int GetHttpFlowDepth(void *, uint32_t); uint8_t isHttpRespPartialCont(void *data); bool GetHttpFastBlockingStatus(); static inline HttpSessionData * GetHttpSessionData(Packet *p) { if (p->ssnptr == NULL) return NULL; return (HttpSessionData *)session_api->get_application_data(p->ssnptr, PP_HTTPINSPECT); } static inline void freeTransactionNode(Transaction *tPtr) { if(tPtr->true_ip) sfaddr_free(tPtr->true_ip); SnortPreprocFree(tPtr, sizeof (Transaction), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION); hi_stats.mem_used -= sizeof(Transaction); } static inline void deleteNode_tList(HttpSessionData *hsd) { Transaction *tmp = hsd->tList_start; hsd->tList_start = hsd->tList_start->next; if( hsd->tList_start == NULL ) hsd->tList_end = NULL; freeTransactionNode(tmp); } static inline sfaddr_t *GetTrueIPForSession(void *data) { HttpSessionData *hsd = NULL; if (data == NULL) return NULL; hsd = (HttpSessionData *)session_api->get_application_data(data, PP_HTTPINSPECT); if(hsd == NULL) return NULL; if( hsd->tList_start != NULL ) { if ((hsd->is_response == 0) && ( hsd->http_req_id == hsd->tList_end->tID ) ) return hsd->tList_end->true_ip; else if ( (hsd->is_response == 1) && (hsd->http_resp_id == hsd->tList_start->tID ) ) return hsd->tList_start->true_ip; } return NULL; } static inline void ResetGzipState(DECOMPRESS_STATE *ds) { if (ds == NULL) return; inflateEnd(&(ds->d_stream)); ds->inflate_init = 0; ds->deflate_initialized = false; ds->compr_bytes_read = 0; ds->decompr_bytes_read = 0; ds->compress_fmt = 0; ds->decompress_data = 0; ds->stage = HTTP_DECOMP_START; } static inline void ResetRespState(HTTP_RESP_STATE *ds) { if (ds == NULL) return; ds->inspect_body = 0; ds->last_pkt_contlen = 0; ds->last_pkt_chunked = 0; ds->inspect_reassembled = 0; ds->next_seq = 0; ds->chunk_remainder = 0; ds->data_extracted = 0; ds->max_seq = 0; } static inline int SetLogBuffers(HttpSessionData *hsd, void* scbPtr) { int iRet = 0; if (hsd->log_state == NULL) { MemBucket *bkt = mempool_alloc(http_mempool); if (bkt != NULL) { hsd->log_state = (HTTP_LOG_STATE *)SnortPreprocAlloc(1, sizeof(HTTP_LOG_STATE), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION); if( hsd->log_state != NULL ) { bkt->scbPtr = scbPtr; hsd->log_state->log_bucket = bkt; hsd->log_state->uri_bytes = 0; hsd->log_state->hostname_bytes = 0; hsd->log_state->uri_extracted = (uint8_t *)bkt->data; hsd->log_state->hostname_extracted = (uint8_t *)bkt->data + MAX_URI_EXTRACTED; } else { mempool_free(http_mempool, bkt); iRet = -1; } } else iRet = -1; } return iRet; } static inline void SetHttpDecode(uint16_t altLen) { HttpDecodeBuf.len = altLen; } #endif snort-2.9.20/src/preprocessors/snort_httpinspect.c0000644000175000017500000051644514241077077020557 0ustar apoapo/**************************************************************************** * * Copyright (C) 2014-2022 Cisco and/or its affiliates. All rights reserved. * Copyright (C) 2003-2013 Sourcefire, Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License Version 2 as * published by the Free Software Foundation. You may not use, modify or * distribute this program under any other version of the GNU General * Public License. * * 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. * ****************************************************************************/ /** ** @file snort_httpinspect.c ** ** @author Daniel Roelker ** ** @brief This file wraps the HttpInspect functionality for Snort ** and starts the HttpInspect flow. ** ** ** The file takes a Packet structure from the Snort IDS to start the ** HttpInspect flow. This also uses the Stream Interface Module which ** is also Snort-centric. Mainly, just a wrapper to HttpInspect ** functionality, but a key part to starting the basic flow. ** ** The main bulk of this file is taken up with user configuration and ** parsing. The reason this is so large is because HttpInspect takes ** very detailed configuration parameters for each specified server. ** Hopefully every web server that is out there can be emulated ** with these configuration options. ** ** The main functions of note are: ** - HttpInspectSnortConf::this is the configuration portion ** - SnortHttpInspect::this is the actual inspection flow ** - LogEvents:this is where we log the HttpInspect events ** ** NOTES: ** ** - 2.11.03: Initial Development. DJR ** - 2.4.05: Added tab_uri_delimiter config option. AJM. */ #include #include #include #include #include #ifndef WIN32 #include #include #include #endif #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "snort.h" #include "detect.h" #include "decode.h" #include "log.h" #include "event.h" #include "generators.h" #include "snort_debug.h" #include "plugbase.h" #include "util.h" #include "event_queue.h" #include "session_common.h" #include "session_api.h" #include "stream_api.h" #include "sfsnprintfappend.h" #include "hi_return_codes.h" #include "hi_ui_config.h" #include "hi_ui_iis_unicode_map.h" #include "hi_si.h" #include "hi_mi.h" #include "hi_norm.h" #include "hi_client.h" #include "snort_httpinspect.h" #include "detection_util.h" #include "profiler.h" #include "hi_cmd_lookup.h" #include "Unified2_common.h" #include "mempool.h" #include "file_mail_common.h" #include "file_api.h" #include "sf_email_attach_decode.h" #include "file_decomp.h" #include "hi_eo_log.h" #include "memory_stats.h" #ifdef DUMP_BUFFER #include "hi_buffer_dump.h" #endif #ifdef PERF_PROFILING extern PreprocStats hiDetectPerfStats; extern int hiDetectCalled; #endif extern char *snort_conf_dir; extern MemPool *hi_gzip_mempool; extern tSfPolicyUserContextId hi_config; extern char** xffFields; /* Stats tracking for HTTP Inspect */ HIStats hi_stats; DataBuffer HttpDecodeBuf; const HiSearchToken hi_patterns[] = { {"0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ /* static int ProcessGlobalAlert(HTTPINSPECT_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen) { GlobalConf->no_alerts = 1; return 0; } */ static int ProcessIISUnicodeMap(uint8_t **iis_unicode_map, char **iis_unicode_map_filename, int *iis_unicode_map_codepage, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int iRet; char filename[MAX_FILENAME]; char *pcEnd; int iCodeMap; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", IIS_UNICODE_MAP); return -1; } /* ** If an absolute path is specified, then use that. */ #ifndef WIN32 if(pcToken[0] == '/') { iRet = SnortSnprintf(filename, sizeof(filename), "%s", pcToken); } else { /* ** Set up the file name directory */ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '/') { iRet = SnortSnprintf(filename, sizeof(filename), "%s%s", snort_conf_dir, pcToken); } else { iRet = SnortSnprintf(filename, sizeof(filename), "%s/%s", snort_conf_dir, pcToken); } } #else if(strlen(pcToken)>3 && pcToken[1]==':' && pcToken[2]=='\\') { iRet = SnortSnprintf(filename, sizeof(filename), "%s", pcToken); } else { /* ** Set up the file name directory */ if (snort_conf_dir[strlen(snort_conf_dir) - 1] == '\\' || snort_conf_dir[strlen(snort_conf_dir) - 1] == '/' ) { iRet = SnortSnprintf(filename, sizeof(filename), "%s%s", snort_conf_dir, pcToken); } else { iRet = SnortSnprintf(filename, sizeof(filename), "%s\\%s", snort_conf_dir, pcToken); } } #endif if(iRet != SNORT_SNPRINTF_SUCCESS) { SnortSnprintf(ErrorString, ErrStrLen, "Filename too long for token '%s'.", IIS_UNICODE_MAP); return -1; } /* ** Set the filename */ *iis_unicode_map_filename = strdup(filename); if(*iis_unicode_map_filename == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "Could not strdup() '%s' filename.", IIS_UNICODE_MAP); return -1; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No codemap to select from IIS Unicode Map file."); return -1; } /* ** Grab the unicode codemap to use */ iCodeMap = strtol(pcToken, &pcEnd, 10); if(*pcEnd || iCodeMap < 0) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid IIS codemap argument."); return -1; } /* ** Set the codepage */ *iis_unicode_map_codepage = iCodeMap; /* ** Assume that the pcToken we now have is the filename of the map ** table. */ iRet = hi_ui_parse_iis_unicode_map(iis_unicode_map, filename, iCodeMap); if (iRet) { if(iRet == HI_INVALID_FILE) { SnortSnprintf(ErrorString, ErrStrLen, "Unable to open the IIS Unicode Map file '%s'.", filename); } else if(iRet == HI_FATAL_ERR) { SnortSnprintf(ErrorString, ErrStrLen, "Did not find specified IIS Unicode codemap in " "the specified IIS Unicode Map file."); } else { SnortSnprintf(ErrorString, ErrStrLen, "There was an error while parsing the IIS Unicode Map file."); } return -1; } return 0; } static int ProcessOversizeDir(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int iDirLen; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", OVERSIZE_DIR); return -1; } /* ** Grab the oversize directory length */ iDirLen = strtol(pcToken, &pcEnd, 10); if(*pcEnd || iDirLen < 0) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'.", OVERSIZE_DIR); return -1; } ServerConf->long_dir = iDirLen; return 0; } static int ProcessHttpMemcap(HTTPINSPECT_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken, *pcEnd; int memcap; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", HTTP_MEMCAP); return -1; } memcap = SnortStrtolRange(pcToken, &pcEnd, 10, 0 , INT_MAX); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", HTTP_MEMCAP); return -1; } if(memcap < MIN_HTTP_MEMCAP || memcap > MAX_HTTP_MEMCAP) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Must be between %d and " "%d.", HTTP_MEMCAP, MIN_HTTP_MEMCAP, MAX_HTTP_MEMCAP); return -1; } GlobalConf->memcap = memcap; return 0; } static int ProcessMaxGzipMem(HTTPINSPECT_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken, *pcEnd; int max_gzip_mem; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", MAX_GZIP_MEM); return -1; } max_gzip_mem = SnortStrtolRange(pcToken, &pcEnd, 10, 0, INT_MAX); if ((pcEnd == pcToken) || *pcEnd || (errno == ERANGE)) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", MAX_GZIP_MEM); return -1; } if(max_gzip_mem < GZIP_MEM_MIN) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", MAX_GZIP_MEM); return -1; } GlobalConf->max_gzip_mem = (unsigned int)max_gzip_mem; return 0; } static int ProcessCompressDepth(HTTPINSPECT_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int compress_depth; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", COMPRESS_DEPTH); return -1; } compress_depth = SnortStrtol(pcToken, &pcEnd, 10); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", COMPRESS_DEPTH); return -1; } if(compress_depth <= 0 || compress_depth > MAX_GZIP_DEPTH) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Must be between 1 and " "%d.", COMPRESS_DEPTH, MAX_GZIP_DEPTH); return -1; } GlobalConf->compr_depth = compress_depth; return 0; } static int ProcessDecompressDepth(HTTPINSPECT_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int decompress_depth; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", DECOMPRESS_DEPTH); return -1; } decompress_depth = SnortStrtol(pcToken, &pcEnd, 10); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", DECOMPRESS_DEPTH); return -1; } if(decompress_depth <= 0 || decompress_depth > MAX_GZIP_DEPTH) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Must be between 1 and " "%d.", DECOMPRESS_DEPTH, MAX_GZIP_DEPTH); return -1; } GlobalConf->decompr_depth = decompress_depth; return 0; } /* ** NAME ** ProcessGlobalConf:: */ /** ** This is where we process the global configuration for HttpInspect. ** ** We set the values of the global configuraiton here. Any errors that ** are encountered are specified in the error string and the type of ** error is returned through the return code, i.e. fatal, non-fatal. ** ** The configuration options that are dealt with here are: ** - global_alert ** This tells us whether to do any internal alerts or not, on ** a global scale. ** - max_pipeline ** Tells HttpInspect how many pipeline requests to buffer looking ** for a response before inspection. ** - inspection_type ** What type of inspection for HttpInspect to do, stateless or ** stateful. ** ** @param GlobalConf pointer to the global configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ int ProcessGlobalConf(HTTPINSPECT_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen, char **saveptr) { int iRet; char *pcToken; int iTokens = 0; while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { /* ** Show that we at least got one token */ iTokens = 1; if(!strcmp(IIS_UNICODE_MAP, pcToken)) { iRet = ProcessIISUnicodeMap(&GlobalConf->iis_unicode_map, &GlobalConf->iis_unicode_map_filename, &GlobalConf->iis_unicode_codepage, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(ANOMALOUS_SERVERS, pcToken)) { /* ** This is easy to configure since we just look for the token ** and turn on the option. */ GlobalConf->anomalous_servers = 1; } else if(!strcmp(PROXY_ALERT, pcToken)) { GlobalConf->proxy_alert = 1; } else if (!strcmp(MAX_GZIP_MEM, pcToken)) { iRet = ProcessMaxGzipMem(GlobalConf, ErrorString, ErrStrLen, saveptr); if(iRet) return iRet; } else if (!strcmp(COMPRESS_DEPTH, pcToken)) { iRet = ProcessCompressDepth(GlobalConf, ErrorString, ErrStrLen, saveptr); if (iRet) return iRet; } else if (!strcmp(DECOMPRESS_DEPTH, pcToken)) { iRet = ProcessDecompressDepth(GlobalConf, ErrorString, ErrStrLen, saveptr); if(iRet) return iRet; } else if (!strcmp(OPT_DISABLED, pcToken)) { GlobalConf->disabled = 1; return 0; } else if (!strcmp(NORMALIZE_NULLS, pcToken)) { GlobalConf->normalize_nulls = TRUE; } else if (!strcmp(FAST_BLOCKING, pcToken)) { GlobalConf->fast_blocking = TRUE; } else if (!strcmp(HTTP_MEMCAP, pcToken)) { iRet = ProcessHttpMemcap(GlobalConf, ErrorString, ErrStrLen, saveptr); if(iRet) return iRet; } else if (!file_api->parse_mime_decode_args(&(GlobalConf->decode_conf), pcToken, "HTTP", saveptr)) { continue; } else { SnortSnprintf(ErrorString, ErrStrLen, "Invalid keyword '%s' for '%s' configuration.", pcToken, GLOBAL); return -1; } } /* ** If there are not any tokens to the configuration, then ** we let the user know and log the error. return non-fatal ** error. */ if(!iTokens) { SnortSnprintf(ErrorString, ErrStrLen, "No tokens to '%s' configuration.", GLOBAL); return -1; } /* ** Let's check to make sure that we get a default IIS Unicode Codemap */ if(!GlobalConf->iis_unicode_map) { SnortSnprintf(ErrorString, ErrStrLen, "Global configuration must contain an IIS Unicode Map " "configuration. Use token '%s'.", IIS_UNICODE_MAP); return -1; } return 0; } /* ** NAME ** ProcessProfile:: */ /** Returns error messages for failed hi_ui_config_set_profile calls. ** ** Called exclusively by ProcessProfile. */ static inline int _ProcessProfileErr(int iRet, char* ErrorString, int ErrStrLen, char *token) { if(iRet == HI_MEM_ALLOC_FAIL) { SnortSnprintf(ErrorString, ErrStrLen, "Memory allocation failed while setting the '%s' " "profile.", token); return -1; } else { SnortSnprintf(ErrorString, ErrStrLen, "Undefined error code for set_profile_%s.", token); return -1; } } /* ** NAME ** ProcessProfile:: */ /** ** Process the PROFILE configuration. ** ** This function verifies that the argument to the profile configuration ** is valid. We also check to make sure there is no additional ** configuration after the PROFILE. This is no allowed, so we ** alert on that fact. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessProfile(HTTPINSPECT_GLOBAL_CONF *GlobalConf, HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int iRet; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s'.", PROFILE_STRING); return -1; } /* ** Load the specific type of profile */ if(!strcmp(APACHE, pcToken)) { iRet = hi_ui_config_set_profile_apache(ServerConf); if (iRet) { /* returns -1 */ return _ProcessProfileErr(iRet, ErrorString, ErrStrLen, pcToken); } ServerConf->profile = HI_APACHE; } else if(!strcmp(IIS, pcToken)) { iRet = hi_ui_config_set_profile_iis(ServerConf, GlobalConf->iis_unicode_map); if (iRet) { /* returns -1 */ return _ProcessProfileErr(iRet, ErrorString, ErrStrLen, pcToken); } ServerConf->profile = HI_IIS; } else if(!strcmp(IIS4_0, pcToken) || !strcmp(IIS5_0, pcToken)) { iRet = hi_ui_config_set_profile_iis_4or5(ServerConf, GlobalConf->iis_unicode_map); if (iRet) { /* returns -1 */ return _ProcessProfileErr(iRet, ErrorString, ErrStrLen, pcToken); } ServerConf->profile = (pcToken[3]=='4'?HI_IIS4:HI_IIS5); } else if(!strcmp(ALL, pcToken)) { iRet = hi_ui_config_set_profile_all(ServerConf, GlobalConf->iis_unicode_map); if (iRet) { /* returns -1 */ return _ProcessProfileErr(iRet, ErrorString, ErrStrLen, pcToken); } ServerConf->profile = HI_ALL; } else { SnortSnprintf(ErrorString, ErrStrLen, "Invalid profile argument '%s'.", pcToken); return -1; } return 0; } /* ** NAME ** ProcessPorts:: */ /** ** Process the port list for the server configuration. ** ** This configuration is a list of valid ports and is ended by a ** delimiter. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessPorts(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int iPort; int iEndPorts = 0; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid port list format."); return -1; } if(strcmp(START_PORT_LIST, pcToken)) { SnortSnprintf(ErrorString, ErrStrLen, "Must start a port list with the '%s' token.", START_PORT_LIST); return -1; } memset(ServerConf->ports, 0, MAXPORTS_STORAGE); while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndPorts = 1; break; } iPort = strtol(pcToken, &pcEnd, 10); /* ** Validity check for port */ if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid port number."); return -1; } if(iPort < 0 || iPort > MAXPORTS-1) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid port number. Must be between 0 and 65535."); return -1; } enablePort( ServerConf->ports, iPort ); if(ServerConf->port_count < MAXPORTS) ServerConf->port_count++; } if(!iEndPorts) { SnortSnprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", PORTS, END_PORT_LIST); return -1; } return 0; } /* ** NAME ** ProcessFlowDepth:: */ /** ** Configure the flow depth for a server. ** ** Check that the value for flow depth is within bounds ** and is a valid number. ** ** @param ServerConf pointer to the server configuration ** @param ServerOrClient which flowdepth is being set ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessFlowDepth(HTTPINSPECT_CONF *ServerConf, int ServerOrClient, char *ErrorString, int ErrStrLen, char **saveptr, char *pToken, int maxDepth) { char *pcToken; int iFlowDepth; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", pToken); return -1; } iFlowDepth = SnortStrtol(pcToken, &pcEnd, 10); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", pToken); return -1; } /* -1 here is okay, which means ignore ALL server side traffic */ if(iFlowDepth < -1 || iFlowDepth > maxDepth) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Must be between -1 and %d.", pToken, maxDepth); return -1; } if (ServerOrClient == HI_SI_CLIENT_MODE) ServerConf->client_flow_depth = iFlowDepth; else ServerConf->server_flow_depth = iFlowDepth; return 0; } /* ** NAME ** ProcessPostDepth:: */ /** ** Configure the post depth for client requests ** ** Checks that the value for flow depth is within bounds ** and is a valid number. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessPostDepth(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int post_depth; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", POST_DEPTH); return -1; } post_depth = SnortStrtol(pcToken, &pcEnd, 10); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", POST_DEPTH); return -1; } /* 0 means 'any depth' */ if(post_depth < -1 || post_depth > ( IP_MAXPACKET - (IP_HEADER_LEN + TCP_HEADER_LEN) ) ) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Must be between -1 and " "%d.", POST_DEPTH,( IP_MAXPACKET - (IP_HEADER_LEN + TCP_HEADER_LEN) )); return -1; } ServerConf->post_depth = post_depth; return 0; } /* ** NAME ** ProcessChunkLength:: */ /** ** Process and verify the chunk length for the server configuration. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessChunkLength(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int iChunkLength; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", CHUNK_LENGTH); return -1; } iChunkLength = SnortStrtolRange(pcToken, &pcEnd, 10, 0, INT_MAX); if ((pcEnd == pcToken) || *pcEnd || (errno == ERANGE)) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", CHUNK_LENGTH); return -1; } ServerConf->chunk_length = iChunkLength; return 0; } /* ** NAME ** ProcessSmallChunkLength:: */ /** ** Process and verify the small chunk length for the server configuration. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessSmallChunkLength(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int num_toks = 0; bool got_param_end = 0; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if (!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid cmd list format."); return -1; } if (strcmp(START_PORT_LIST, pcToken)) { SnortSnprintf(ErrorString, ErrStrLen, "Must start small chunk length parameters with the '%s' token.", START_PORT_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if (!strcmp(END_PORT_LIST, pcToken)) { got_param_end = 1; break; } num_toks++; if (num_toks > 2) { SnortSnprintf(ErrorString, ErrStrLen, "Too many arguments to '%s'.", SMALL_CHUNK_LENGTH); return -1; } if (num_toks == 1) { uint32_t chunk_length = (uint32_t)SnortStrtoulRange(pcToken, &pcEnd, 10, 0, UINT8_MAX); if ((pcEnd == pcToken) || *pcEnd || (errno == ERANGE)) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to chunk length param of '%s'. " "Must be between 0 and %u.\n", SMALL_CHUNK_LENGTH, UINT8_MAX); return -1; } ServerConf->small_chunk_length.size = (uint8_t)chunk_length; } else { uint32_t num_chunks_threshold = (uint32_t)SnortStrtoulRange(pcToken, &pcEnd, 10, 0, UINT8_MAX); if ((pcEnd == pcToken) || *pcEnd || (errno == ERANGE)) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to number of consecutive chunks " "threshold of '%s'. Must be between 0 and %u.\n", SMALL_CHUNK_LENGTH, UINT8_MAX); return -1; } ServerConf->small_chunk_length.num = (uint8_t)num_chunks_threshold; } } if (num_toks != 2) { SnortSnprintf(ErrorString, ErrStrLen, "Not enough arguments to '%s'.", SMALL_CHUNK_LENGTH); return -1; } if (!got_param_end) { SnortSnprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", SMALL_CHUNK_LENGTH, END_PORT_LIST); return -1; } return 0; } /* ** NAME ** ProcessMaxHeaders:: */ /** ** Process and verify the maximum allowed number of headers for the ** server configuration. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessMaxHeaders(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int length; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", MAX_HEADERS); return -1; } length = strtol(pcToken, &pcEnd, 10); if(*pcEnd || pcEnd == pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", MAX_HEADERS); return -1; } if(length < 0) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Valid range is 0 to 1024.", MAX_HEADERS); return -1; } if(length > 1024) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Valid range is 0 to 1024.", MAX_HEADERS); return -1; } ServerConf->max_headers = length; return 0; } /* ** NAME ** ProcessMaxHdrLen:: */ /** ** Process and verify the maximum allowed header length for the ** server configuration. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessMaxHdrLen(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int length; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", MAX_HDR_LENGTH); return -1; } length = strtol(pcToken, &pcEnd, 10); if(*pcEnd || pcEnd == pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", MAX_HDR_LENGTH); return -1; } if(length < 0) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Valid range is 0 to 65535.", MAX_HDR_LENGTH); return -1; } if(length > 65535) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Valid range is 0 to 65535.", MAX_HDR_LENGTH); return -1; } ServerConf->max_hdr_len = length; return 0; } /* ** NAME ** ProcessConfOpt:: */ /** ** Set the CONF_OPT on and alert fields. ** ** We check to make sure of valid parameters and then ** set the appropriate fields. Not much more to it, than ** that. ** ** @param ConfOpt pointer to the configuration option ** @param Option character pointer to the option being configured ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessConfOpt(HTTPINSPECT_CONF_OPT *ConfOpt, char *Option, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to token '%s'.", Option); return -1; } /* ** Check for the alert value */ if(!strcmp(BOOL_YES, pcToken)) { ConfOpt->alert = 1; } else if(!strcmp(BOOL_NO, pcToken)) { ConfOpt->alert = 0; } else { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to token '%s'.", Option); return -1; } ConfOpt->on = 1; return 0; } /* ** NAME ** ProcessNonRfcChar:: */ /*** ** Configure any characters that the user wants alerted on in the ** URI. ** ** This function allocates the memory for CONF_OPT per character and ** configures the alert option. ** ** @param ConfOpt pointer to the configuration option ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessNonRfcChar(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int iChar; int iEndChar = 0; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid '%s' list format.", NON_RFC_CHAR); return -1; } if(strcmp(START_PORT_LIST, pcToken)) { SnortSnprintf(ErrorString, ErrStrLen, "Must start a '%s' list with the '%s' token.", NON_RFC_CHAR, START_PORT_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndChar = 1; break; } iChar = strtol(pcToken, &pcEnd, 16); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Must be a single character.", NON_RFC_CHAR); return -1; } if(iChar < 0 || iChar > 255) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid character value to '%s'. Must be a single " "character no greater than 255.", NON_RFC_CHAR); return -1; } ServerConf->non_rfc_chars[iChar] = 1; } if(!iEndChar) { SnortSnprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", NON_RFC_CHAR, END_PORT_LIST); return -1; } return 0; } /* ** NAME ** ProcessWhitespaceChars:: */ /*** ** Configure any characters that the user wants to be treated as ** whitespace characters before and after a URI. ** ** ** @param ServerConf pointer to the server configuration structure ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessWhitespaceChars(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pcEnd; int iChar; int iEndChar = 0; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid '%s' list format.", WHITESPACE); return -1; } if(strcmp(START_PORT_LIST, pcToken)) { SnortSnprintf(ErrorString, ErrStrLen, "Must start a '%s' list with the '%s' token.", WHITESPACE, START_PORT_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndChar = 1; break; } iChar = strtol(pcToken, &pcEnd, 16); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Must be a single character.", WHITESPACE); return -1; } if(iChar < 0 || iChar > 255) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid character value to '%s'. Must be a single " "character no greater than 255.", WHITESPACE); return -1; } ServerConf->whitespace[iChar] = HI_UI_CONFIG_WS_BEFORE_URI; } if(!iEndChar) { SnortSnprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", WHITESPACE, END_PORT_LIST); return -1; } return 0; } static bool Is_Field_Prec_Unique( uint8_t *Name_Array[], uint8_t Prec_Array[], uint8_t *Name, uint8_t Precedence, char *ErrorString, int ErrStrLen ) { int i; for( i=0; i=Start; i-- ) { Name_Array[i] = Name_Array[i-1]; Length_Array[i] = Length_Array[i-1]; Prec_Array[i] = Prec_Array[i-1]; } } /* The caller of Add_XFF_Field is responsible for determining that at least one additional entry is availble in both the field name array and the precedence value array. */ static int Add_XFF_Field( HTTPINSPECT_CONF *ServerConf, uint8_t *Prec_Array, uint8_t *Field_Name, uint8_t Precedence, char *ErrorString, int ErrStrLen ) { uint8_t **Fields = ServerConf->xff_headers; uint8_t *Lengths = ServerConf->xff_header_lengths; size_t Length = 0; int i; char **Builtin_Fields; unsigned char *cp; const char *Special_Chars = { "_-" }; bool fieldAdded = false; if(!Field_Name ) { SnortSnprintf(ErrorString, ErrStrLen, "Field name is null" ); return -1; } if( (Length = strlen( (char *)Field_Name )) > UINT8_MAX ) { SnortSnprintf(ErrorString, ErrStrLen, "Field name limited to %u characters", UINT8_MAX); return -1; } cp = (unsigned char*)Field_Name; while( *cp != '\0' ) { *cp = (uint8_t)toupper(*cp); // Fold to upper case for runtime comparisons if( (isalnum( *cp ) == 0) && (strchr( Special_Chars, (int)(*cp) ) == NULL) ) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid xff field name: %s ", Field_Name); return( -1 ); } cp += 1; } Builtin_Fields = hi_client_get_field_names(); for( i=0; Builtin_Fields[i]!=NULL; i++ ) { if( strcasecmp(Builtin_Fields[i], HTTP_XFF_FIELD_X_FORWARDED_FOR) == 0 ) continue; if( strcasecmp(Builtin_Fields[i], HTTP_XFF_FIELD_TRUE_CLIENT_IP) == 0 ) continue; if( strcasecmp(Builtin_Fields[i], (char *)Field_Name) == 0 ) { SnortSnprintf(ErrorString, ErrStrLen, "Cannot use: '%s' as an xff header.", Field_Name); return -1; } } if( !Is_Field_Prec_Unique( Fields, Prec_Array, Field_Name, Precedence, ErrorString, ErrStrLen ) ) return( -1 ); if( Precedence == 0 ) { if( (i = Find_Open_Field( Fields )) >= 0 ) { Fields[i] = Field_Name; Lengths[i] = (uint8_t)Length; Prec_Array[i] = 0; fieldAdded = true; } else return( -1 ); } else { for( i=0; ixff_headers array contains all NULL's due to the structure allocation process. */ pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid XFF list format."); return -1; } for( i=0; i XFF_MAX_PREC) || (Precedence < XFF_MIN_PREC) ) { SnortSnprintf(ErrorString, ErrStrLen, "illegal precendence value: %u", Precedence); if( Field_Name ) free(Field_Name); return( -1 ); } if( strcasecmp( (char *)Field_Name, HTTP_XFF_FIELD_X_FORWARDED_FOR) == 0 ) { Max_XFF += 1; Have_XFF = true; } else if( strcasecmp( (char *)Field_Name, HTTP_XFF_FIELD_TRUE_CLIENT_IP) == 0 ) { Max_XFF += 1; Have_TCI = true; } else Count += 1; if( Count > Max_XFF ) { SnortSnprintf(ErrorString, ErrStrLen, "too many xff field names"); if( Field_Name ) free(Field_Name); return( -1 ); } if( Add_XFF_Field( ServerConf, Prec_List, Field_Name, (uint8_t)Precedence, ErrorString, ErrStrLen ) != 0 ) { SnortSnprintf(ErrorString, ErrStrLen, "Error adding xff header: %s", Field_Name); if( Field_Name ) free(Field_Name); return( -1 ); } addXffFieldName = 1; Parse_State = XFF_STATE_CLOSE; break; } case( XFF_STATE_CLOSE ): { if( strcmp( END_XFF_HEADER_ENTRY, pcToken ) != 0 ) { SnortSnprintf(ErrorString, ErrStrLen, "Must end an xff header entry with the '%s' token.", END_XFF_HEADER_ENTRY); if( Field_Name ) free(Field_Name); return( -1 ); } Parse_State = XFF_STATE_OPEN; break; } default: { SnortSnprintf(ErrorString, ErrStrLen, "xff header parsing error"); if( Field_Name ) free(Field_Name); return( -1 ); } } } while( Keep_Parsing && ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) ); if( Field_Name && !addXffFieldName ) { free(Field_Name); Field_Name = NULL; } if( Parse_State != XFF_STATE_END ) { SnortSnprintf(ErrorString, ErrStrLen, "xff header parsing error"); return( -1 ); } /* NOTE: The number of fields added here MUST be represented in HTTP_XFF_BUILTIN_NAMES value to assure that we reserve room for them on the list. */ if( !Have_XFF ) { Field_Name = (uint8_t *)SnortStrdup( HTTP_XFF_FIELD_X_FORWARDED_FOR ); if( Add_XFF_Field( ServerConf, Prec_List, Field_Name, 0, ErrorString, ErrStrLen ) != 0 ) { SnortSnprintf(ErrorString, ErrStrLen, "problem adding builtin xff field - too many fields?"); if( Field_Name ) free(Field_Name); return( -1 ); } } if( !Have_TCI ) { Field_Name = (uint8_t *)SnortStrdup( HTTP_XFF_FIELD_TRUE_CLIENT_IP ); if( Add_XFF_Field( ServerConf, Prec_List, Field_Name, 0, ErrorString, ErrStrLen ) != 0 ) { SnortSnprintf(ErrorString, ErrStrLen, "problem adding builtin xff field - too many fields?"); if( Field_Name ) free(Field_Name); return( -1 ); } } return 0; } static char** getHttpXffFields(int* nFields) { if (!xffFields[0]) { if (nFields) *nFields = 0; return NULL; } if (nFields) { for ((*nFields) = 0; ((*nFields) < HTTP_MAX_XFF_FIELDS) && xffFields[*nFields]; (*nFields)++) ; } return xffFields; } static int ProcessHttpMethodList(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { HTTP_CMD_CONF *HTTPMethods = NULL; char *pcToken; char *cmd; int iEndCmds = 0; int iRet; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid cmd list format."); return -1; } if(strcmp(START_PORT_LIST, pcToken)) { SnortSnprintf(ErrorString, ErrStrLen, "Must start a http request method list with the '%s' token.", START_PORT_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndCmds = 1; break; } cmd = pcToken; /* Max method length cannot exceed 256, as this is the size of the key array used in KMapAdd function */ if(strlen(pcToken) > MAX_METHOD_LEN) { snprintf(ErrorString, ErrStrLen, "Length of the http request method should not exceed the max request method length of '%d'.", MAX_METHOD_LEN); return -1; } HTTPMethods = http_cmd_lookup_find(ServerConf->cmd_lookup, cmd, strlen(cmd), &iRet); if (HTTPMethods == NULL) { /* Add it to the list */ // note that struct includes 1 byte for null, so just add len HTTPMethods = (HTTP_CMD_CONF *)calloc(1, sizeof(HTTP_CMD_CONF)+strlen(cmd)); if (HTTPMethods == NULL) { FatalError("%s(%d) Could not allocate memory for HTTP Method configuration.\n", __FILE__, __LINE__); } strcpy(HTTPMethods->cmd_name, cmd); http_cmd_lookup_add(ServerConf->cmd_lookup, cmd, strlen(cmd), HTTPMethods); } } if(!iEndCmds) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", HTTP_METHODS, END_PORT_LIST); return -1; } return 0; } static int ProcessDecompressionTypeList(HTTPINSPECT_CONF *ServerConf, char *ConfigType, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *cmd; int iEndCmds = 0; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid algorithm list format."); return -1; } if(strcmp(START_PORT_LIST, pcToken)) { SnortSnprintf(ErrorString, ErrStrLen, "Must start an algotithm list with the '%s' token.", START_PORT_LIST); return -1; } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL) { if(!strcmp(END_PORT_LIST, pcToken)) { iEndCmds = 1; break; } cmd = pcToken; /* FIX THIS: For now, the decompression types are limited to SWF/LZMA, SWF/Deflate, and PDF/Deflate. Hardcode the comparison logic and storage in the HTTPINSPECT_CONF struc. */ if( 0 == strcmp(ConfigType, INSPECT_SWF)) { if( 0 == strcmp(cmd, DECOMPRESS_DEFLATE) ) { ServerConf->file_decomp_modes |= (FILE_SWF_ZLIB_BIT | FILE_REVERT_BIT); } #ifdef LZMA else if( 0 == strcmp(cmd, DECOMPRESS_LZMA) ) { ServerConf->file_decomp_modes |= (FILE_SWF_LZMA_BIT | FILE_REVERT_BIT); } #endif else { snprintf(ErrorString, ErrStrLen, "Bad cmd element passed to ProcessDecompressionTypeList(): %s", cmd); return( -1 ); } } else if( 0 == strcmp(ConfigType, INSPECT_PDF) ) { if( 0 == strcmp(cmd, DECOMPRESS_DEFLATE) ) { ServerConf->file_decomp_modes |= (FILE_PDF_DEFL_BIT | FILE_REVERT_BIT); } else { snprintf(ErrorString, ErrStrLen, "Bad cmd passed to ProcessDecompressionTypeList(): %s", cmd); return( -1 ); } } else { snprintf(ErrorString, ErrStrLen, "Bad ConfigType passed to ProcessDecompressionTypeList(): %s", ConfigType); return( -1 ); } } if(!iEndCmds) { snprintf(ErrorString, ErrStrLen, "Must end '%s' configuration with '%s'.", ConfigType, END_PORT_LIST); return -1; } return 0; } /* ** NAME ** ProcessMaxSpaces:: */ /** ** Process and verify the maximum allowed spaces for the ** server configuration. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessMaxSpaces(HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr, char *configOption, SpaceType type) { char *pcToken; int num_spaces; char *pcEnd; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No argument to '%s' token.", configOption); return -1; } num_spaces = SnortStrtolRange(pcToken, &pcEnd, 10, 0 , INT_MAX); if(*pcEnd) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'.", configOption); return -1; } if(num_spaces < 0) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Valid range is 0 to 65535.", configOption); return -1; } if(num_spaces > 65535) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid argument to '%s'. Valid range is 0 to 65535.", configOption); return -1; } switch(type) { case CONFIG_MAX_SPACES: ServerConf->max_spaces = num_spaces; break; case CONFIG_MAX_JS_WS: ServerConf->max_js_ws = num_spaces; break; default: break; } return 0; } /* ** NAME ** ProcessServerConf:: */ /** ** Process the global server configuration. ** ** Take the configuration and translate into the global server ** configuration. We also check for any configuration errors and ** invalid keywords. ** ** @param ServerConf pointer to the server configuration ** @param ErrorString error string buffer ** @param ErrStrLen the length of the error string buffer ** @param saveptr the strtok_r saved state ** ** @return an error code integer ** (0 = success, >0 = non-fatal error, <0 = fatal error) ** ** @retval 0 successs ** @retval -1 generic fatal error ** @retval 1 generic non-fatal error */ static int ProcessServerConf(HTTPINSPECT_GLOBAL_CONF *GlobalConf, HTTPINSPECT_CONF *ServerConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; int iRet; int iPorts = 0; HTTPINSPECT_CONF_OPT *ConfOpt; /* ** Check for profile keyword first, it's the only place in the ** configuration that is correct. */ pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "WARNING: No tokens to '%s' configuration.", SERVER); return 1; } if(!strcmp(PROFILE_STRING, pcToken)) { iRet = ProcessProfile(GlobalConf, ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "No port list to the profile token."); return -1; } do { if(!strcmp(PORTS, pcToken)) { iRet = ProcessPorts(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } iPorts = 1; } else if(!strcmp(IIS_UNICODE_MAP, pcToken)) { iRet = ProcessIISUnicodeMap(&ServerConf->iis_unicode_map, &ServerConf->iis_unicode_map_filename, &ServerConf->iis_unicode_codepage, ErrorString,ErrStrLen, saveptr); if (iRet) { return -1; } } else if(!strcmp(ALLOW_PROXY, pcToken)) { ServerConf->allow_proxy = 1; } else if(!strcmp(FLOW_DEPTH, pcToken) || !strcmp(SERVER_FLOW_DEPTH, pcToken)) { iRet = ProcessFlowDepth(ServerConf, HI_SI_SERVER_MODE, ErrorString, ErrStrLen, saveptr, pcToken, MAX_SERVER_DEPTH); if (iRet) { return iRet; } } else if(!strcmp(CLIENT_FLOW_DEPTH, pcToken)) { iRet = ProcessFlowDepth(ServerConf, HI_SI_CLIENT_MODE, ErrorString, ErrStrLen, saveptr, pcToken, MAX_CLIENT_DEPTH); if (iRet) { return iRet; } } else if(!strcmp(POST_DEPTH, pcToken)) { iRet = ProcessPostDepth(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(GLOBAL_ALERT, pcToken)) { ServerConf->no_alerts = 1; } else if(!strcmp(OVERSIZE_DIR, pcToken)) { iRet = ProcessOversizeDir(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(INSPECT_URI_ONLY, pcToken)) { ServerConf->uri_only = 1; } else if (!strcmp(NORMALIZE_HEADERS, pcToken)) { ServerConf->normalize_headers = 1; } else if (!strcmp(NORMALIZE_COOKIES, pcToken)) { ServerConf->normalize_cookies = 1; } else if (!strcmp(NORMALIZE_UTF, pcToken)) { ServerConf->normalize_utf = 1; } else if (!strcmp(NORMALIZE_JS, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",INSPECT_RESPONSE, NORMALIZE_JS); return -1; } ServerConf->normalize_javascript = 1; } else if(!strcmp(MAX_JS_WS, pcToken)) { if(!ServerConf->normalize_javascript) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'", NORMALIZE_JS, MAX_JS_WS); return -1; } iRet = ProcessMaxSpaces(ServerConf, ErrorString, ErrStrLen, saveptr, MAX_JS_WS, CONFIG_MAX_JS_WS); if (iRet) { return iRet; } } else if (!strcmp(INSPECT_COOKIES, pcToken)) { ServerConf->enable_cookie = 1; } else if (!strcmp(INSPECT_RESPONSE, pcToken)) { ServerConf->inspect_response = 1; } else if (!strcmp(HTTP_METHODS, pcToken)) { iRet = ProcessHttpMethodList(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if (!strcmp(EXTRACT_GZIP, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",INSPECT_RESPONSE, EXTRACT_GZIP); return -1; } ServerConf->extract_gzip = 1; } else if (!strcmp(UNLIMIT_DECOMPRESS, pcToken)) { if(!ServerConf->extract_gzip) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",EXTRACT_GZIP, UNLIMIT_DECOMPRESS); return -1; } if((GlobalConf->compr_depth != MAX_GZIP_DEPTH) && (GlobalConf->decompr_depth != MAX_GZIP_DEPTH)) { SnortSnprintf(ErrorString, ErrStrLen, "'%s' and '%s' should be set to max in the default policy to enable '%s'", COMPRESS_DEPTH, DECOMPRESS_DEPTH, UNLIMIT_DECOMPRESS); return -1; } GlobalConf->compr_depth = GlobalConf->decompr_depth = MAX_GZIP_DEPTH; ServerConf->unlimited_decompress = 1; } #ifdef FILE_DECOMP_SWF else if (!strcmp(INSPECT_SWF, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",INSPECT_RESPONSE, INSPECT_SWF); return -1; } ProcessDecompressionTypeList(ServerConf,pcToken,ErrorString,ErrStrLen, saveptr); } #endif #ifdef FILE_DECOMP_PDF else if (!strcmp(INSPECT_PDF, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",INSPECT_RESPONSE, INSPECT_PDF); return -1; } ProcessDecompressionTypeList(ServerConf,pcToken,ErrorString,ErrStrLen, saveptr); } #endif else if(!strcmp(MAX_HDR_LENGTH, pcToken)) { iRet = ProcessMaxHdrLen(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(MAX_HEADERS, pcToken)) { iRet = ProcessMaxHeaders(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(MAX_SPACES, pcToken)) { iRet = ProcessMaxSpaces(ServerConf, ErrorString, ErrStrLen, saveptr, MAX_SPACES, CONFIG_MAX_SPACES); if (iRet) { return iRet; } } else if(!strcmp(ENABLE_XFF, pcToken)) { ServerConf->enable_xff = 1; } else if(!strcmp(XFF_HEADERS_TOK, pcToken)) { if(!ServerConf->enable_xff) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",ENABLE_XFF, XFF_HEADERS_TOK); return -1; } if( ((iRet = ProcessXFF_HeaderList(ServerConf, ErrorString, ErrStrLen, saveptr)) != 0) ) { return iRet; } } else if(!strcmp(LOG_URI, pcToken)) { ServerConf->log_uri = 1; } else if(!strcmp(LOG_HOSTNAME, pcToken)) { ServerConf->log_hostname = 1; } else if(!strcmp(SMALL_CHUNK_LENGTH, pcToken)) { iRet = ProcessSmallChunkLength(ServerConf,ErrorString,ErrStrLen, saveptr); if (iRet) { return iRet; } } else if (!strcmp(LEGACY_MODE, pcToken)) { pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if (pcToken == NULL) break; if(!strcmp(BOOL_YES, pcToken)) { ServerConf->h2_mode = false; } else if(!strcmp(BOOL_NO, pcToken)) { ServerConf->h2_mode = true; } else { continue; } } else { SnortSnprintf(ErrorString, ErrStrLen, "Invalid token while configuring the profile token. " "The only allowed tokens when configuring profiles " "are: '%s', '%s', '%s', '%s', '%s', '%s', '%s', " "'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', " #ifdef FILE_DECOMP_SWF "'%s', " #endif #ifdef FILE_DECOMP_PDF "'%s', " #endif "'%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', and '%s'. ", PORTS,IIS_UNICODE_MAP, ALLOW_PROXY, FLOW_DEPTH, CLIENT_FLOW_DEPTH, GLOBAL_ALERT, OVERSIZE_DIR, MAX_HDR_LENGTH, INSPECT_URI_ONLY, INSPECT_COOKIES, INSPECT_RESPONSE, EXTRACT_GZIP,MAX_HEADERS, NORMALIZE_COOKIES, ENABLE_XFF, XFF_HEADERS_TOK, #ifdef FILE_DECOMP_SWF INSPECT_SWF, #endif #ifdef FILE_DECOMP_PDF INSPECT_PDF, #endif NORMALIZE_HEADERS, NORMALIZE_UTF, UNLIMIT_DECOMPRESS, HTTP_METHODS, LOG_URI, LOG_HOSTNAME, MAX_SPACES, NORMALIZE_JS, MAX_JS_WS, LEGACY_MODE); return -1; } } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL); if(!iPorts) { SnortSnprintf(ErrorString, ErrStrLen, "No port list to the profile token."); return -1; } return 0; } /* ** If there is no profile configuration then we go into the hard-core ** configuration. */ hi_ui_config_reset_http_methods(ServerConf); do { if(!strcmp(PORTS, pcToken)) { iRet = ProcessPorts(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(FLOW_DEPTH, pcToken) || !strcmp(SERVER_FLOW_DEPTH, pcToken)) { iRet = ProcessFlowDepth(ServerConf, HI_SI_SERVER_MODE, ErrorString, ErrStrLen, saveptr, pcToken, MAX_SERVER_DEPTH); if (iRet) { return iRet; } } else if(!strcmp(CLIENT_FLOW_DEPTH, pcToken)) { iRet = ProcessFlowDepth(ServerConf, HI_SI_CLIENT_MODE, ErrorString, ErrStrLen, saveptr, pcToken, MAX_CLIENT_DEPTH); if (iRet) { return iRet; } } else if(!strcmp(POST_DEPTH, pcToken)) { iRet = ProcessPostDepth(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(IIS_UNICODE_MAP, pcToken)) { iRet = ProcessIISUnicodeMap(&ServerConf->iis_unicode_map, &ServerConf->iis_unicode_map_filename, &ServerConf->iis_unicode_codepage, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(CHUNK_LENGTH, pcToken)) { iRet = ProcessChunkLength(ServerConf,ErrorString,ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(SMALL_CHUNK_LENGTH, pcToken)) { iRet = ProcessSmallChunkLength(ServerConf,ErrorString,ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(PIPELINE, pcToken)) { ServerConf->no_pipeline = 1; } else if(!strcmp(NON_STRICT, pcToken)) { ServerConf->non_strict = 1; } else if(!strcmp(ALLOW_PROXY, pcToken)) { ServerConf->allow_proxy = 1; } else if(!strcmp(GLOBAL_ALERT, pcToken)) { ServerConf->no_alerts = 1; } else if(!strcmp(TAB_URI_DELIMITER, pcToken)) { ServerConf->tab_uri_delimiter = 1; } else if(!strcmp(EXTENDED_ASCII, pcToken)) { ServerConf->extended_ascii_uri = 1; } else if (!strcmp(NORMALIZE_HEADERS, pcToken)) { ServerConf->normalize_headers = 1; } else if (!strcmp(NORMALIZE_COOKIES, pcToken)) { ServerConf->normalize_cookies = 1; } else if (!strcmp(NORMALIZE_UTF, pcToken)) { ServerConf->normalize_utf = 1; } else if (!strcmp(NORMALIZE_JS, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'", INSPECT_RESPONSE, NORMALIZE_JS); return -1; } ServerConf->normalize_javascript = 1; } else if(!strcmp(MAX_JS_WS, pcToken)) { if(!ServerConf->normalize_javascript) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'", NORMALIZE_JS, MAX_JS_WS); return -1; } iRet = ProcessMaxSpaces(ServerConf, ErrorString, ErrStrLen, saveptr, MAX_JS_WS, CONFIG_MAX_JS_WS); if (iRet) { return iRet; } } else if(!strcmp(OVERSIZE_DIR, pcToken)) { iRet = ProcessOversizeDir(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(INSPECT_URI_ONLY, pcToken)) { ServerConf->uri_only = 1; } else if(!strcmp(INSPECT_COOKIES, pcToken)) { ServerConf->enable_cookie = 1; } else if(!strcmp(INSPECT_RESPONSE, pcToken)) { ServerConf->inspect_response = 1; } else if (!strcmp(HTTP_METHODS, pcToken)) { iRet = ProcessHttpMethodList(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(EXTRACT_GZIP, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' inspection before setting '%s'",INSPECT_RESPONSE, EXTRACT_GZIP); return -1; } ServerConf->extract_gzip = 1; } else if(!strcmp(UNLIMIT_DECOMPRESS, pcToken)) { if(!ServerConf->extract_gzip) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' inspection before setting '%s'",EXTRACT_GZIP, UNLIMIT_DECOMPRESS); return -1; } if((GlobalConf->compr_depth != MAX_GZIP_DEPTH) && (GlobalConf->decompr_depth != MAX_GZIP_DEPTH)) { SnortSnprintf(ErrorString, ErrStrLen, "'%s' and '%s' should be set to max in the default policy to enable '%s'", COMPRESS_DEPTH, DECOMPRESS_DEPTH, UNLIMIT_DECOMPRESS); return -1; } GlobalConf->compr_depth = GlobalConf->decompr_depth = MAX_GZIP_DEPTH; ServerConf->unlimited_decompress = 1; } #ifdef FILE_DECOMP_SWF else if (!strcmp(INSPECT_SWF, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",INSPECT_RESPONSE, INSPECT_SWF); return -1; } ProcessDecompressionTypeList(ServerConf,pcToken,ErrorString,ErrStrLen, saveptr); } #endif #ifdef FILE_DECOMP_PDF else if (!strcmp(INSPECT_PDF, pcToken)) { if(!ServerConf->inspect_response) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",INSPECT_RESPONSE, INSPECT_PDF); return -1; } ProcessDecompressionTypeList(ServerConf,pcToken,ErrorString,ErrStrLen, saveptr); } #endif /* ** Start the CONF_OPT configurations. */ else if(!strcmp(ASCII, pcToken)) { ConfOpt = &ServerConf->ascii; iRet = ProcessConfOpt(ConfOpt, ASCII, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(UTF_8, pcToken)) { /* ** In order for this to work we also need to set ASCII */ ServerConf->ascii.on = 1; ConfOpt = &ServerConf->utf_8; iRet = ProcessConfOpt(ConfOpt, UTF_8, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(IIS_UNICODE, pcToken)) { if(ServerConf->iis_unicode_map == NULL) { ServerConf->iis_unicode_map = GlobalConf->iis_unicode_map; } /* ** We need to set up: ** - ASCII ** - DOUBLE_DECODE ** - U_ENCODE ** - BARE_BYTE ** - IIS_UNICODE ** ** Base 36 is deprecated and essentially a noop ** - BASE36 */ ServerConf->ascii.on = 1; ConfOpt = &ServerConf->iis_unicode; iRet = ProcessConfOpt(ConfOpt, IIS_UNICODE, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(DOUBLE_DECODE, pcToken)) { ServerConf->ascii.on = 1; ConfOpt = &ServerConf->double_decoding; iRet = ProcessConfOpt(ConfOpt, DOUBLE_DECODE, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(U_ENCODE, pcToken)) { /* ** We set the unicode map to default if it's not already ** set. */ if(ServerConf->iis_unicode_map == NULL) { ServerConf->iis_unicode_map = GlobalConf->iis_unicode_map; } ConfOpt = &ServerConf->u_encoding; iRet = ProcessConfOpt(ConfOpt, U_ENCODE, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(BARE_BYTE, pcToken)) { ConfOpt = &ServerConf->bare_byte; iRet = ProcessConfOpt(ConfOpt, BARE_BYTE, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(BASE36, pcToken)) { /* Base 36 is deprecated and essentially a noop */ ErrorMessage("WARNING: %s (%d): The \"base36\" option to the " "\"http_inspect\" preprocessor configuration is " "deprecated and void of functionality.\n", file_name, file_line); /* Need to get and chuck yes/no argument to option since * we're not doing anything with this anymore. */ pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); } else if(!strcmp(NON_RFC_CHAR, pcToken)) { iRet = ProcessNonRfcChar(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(MULTI_SLASH, pcToken)) { ConfOpt = &ServerConf->multiple_slash; iRet = ProcessConfOpt(ConfOpt, MULTI_SLASH, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(IIS_BACKSLASH, pcToken)) { ConfOpt = &ServerConf->iis_backslash; iRet = ProcessConfOpt(ConfOpt, IIS_BACKSLASH, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(DIRECTORY, pcToken)) { ConfOpt = &ServerConf->directory; iRet = ProcessConfOpt(ConfOpt, DIRECTORY, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(APACHE_WS, pcToken)) { ConfOpt = &ServerConf->apache_whitespace; iRet = ProcessConfOpt(ConfOpt, APACHE_WS, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(WHITESPACE, pcToken)) { iRet = ProcessWhitespaceChars(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(IIS_DELIMITER, pcToken)) { ConfOpt = &ServerConf->iis_delimiter; iRet = ProcessConfOpt(ConfOpt, IIS_DELIMITER, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(WEBROOT, pcToken)) { ConfOpt = &ServerConf->webroot; iRet = ProcessConfOpt(ConfOpt, WEBROOT, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(MAX_HDR_LENGTH, pcToken)) { iRet = ProcessMaxHdrLen(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(MAX_HEADERS, pcToken)) { iRet = ProcessMaxHeaders(ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { return iRet; } } else if(!strcmp(MAX_SPACES, pcToken)) { iRet = ProcessMaxSpaces(ServerConf, ErrorString, ErrStrLen, saveptr, MAX_SPACES, CONFIG_MAX_SPACES); if (iRet) { return iRet; } } else if(!strcmp(ENABLE_XFF, pcToken)) { ServerConf->enable_xff = 1; } else if(!strcmp(XFF_HEADERS_TOK, pcToken)) { if(!ServerConf->enable_xff) { SnortSnprintf(ErrorString, ErrStrLen, "Enable '%s' before setting '%s'",ENABLE_XFF, XFF_HEADERS_TOK); return -1; } if( ((iRet = ProcessXFF_HeaderList(ServerConf, ErrorString, ErrStrLen, saveptr)) != 0) ) { return iRet; } } else if(!strcmp(LOG_URI, pcToken)) { ServerConf->log_uri = 1; } else if(!strcmp(LOG_HOSTNAME, pcToken)) { ServerConf->log_hostname = 1; } else if (!strcmp(LEGACY_MODE, pcToken)) { pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(pcToken == NULL) break; if(!strcmp(BOOL_YES, pcToken)) { ServerConf->h2_mode = false; } else if(!strcmp(BOOL_NO, pcToken)) { ServerConf->h2_mode = true; } else { continue; } } else { SnortSnprintf(ErrorString, ErrStrLen, "Invalid keyword '%s' for server configuration.", pcToken); return -1; } } while ((pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr)) != NULL); return 0; } static void PrintFileDecompOpt(HTTPINSPECT_CONF *ServerConf) { LogMessage(" Decompress response files: %s %s %s\n", ((ServerConf->file_decomp_modes & FILE_SWF_ZLIB_BIT) != 0) ? "SWF-ZLIB" : "", ((ServerConf->file_decomp_modes & FILE_SWF_LZMA_BIT) != 0) ? "SWF-LZMA" : "", ((ServerConf->file_decomp_modes & FILE_PDF_DEFL_BIT) != 0) ? "PDF-DEFL" : ""); } static int PrintConfOpt(HTTPINSPECT_CONF_OPT *ConfOpt, char *Option) { if(!ConfOpt || !Option) { return HI_INVALID_ARG; } if(ConfOpt->on) { LogMessage(" %s: YES alert: %s\n", Option, ConfOpt->alert ? "YES" : "NO"); } else { LogMessage(" %s: OFF\n", Option); } return 0; } static int PrintServerConf(HTTPINSPECT_CONF *ServerConf) { char buf[STD_BUF+1]; int iCtr; int iChar = 0; char* paf = ""; PROFILES prof; if(!ServerConf) { return HI_INVALID_ARG; } prof = ServerConf->profile; LogMessage(" Server profile: %s\n", prof==HI_ALL?"All": prof==HI_APACHE?"Apache": prof==HI_IIS?"IIS": prof==HI_IIS4?"IIS4":"IIS5"); memset(buf, 0, STD_BUF+1); if ( ScPafEnabled() && stream_api ) paf = " (PAF)"; SnortSnprintf(buf, STD_BUF + 1, " Ports%s: ", paf); /* ** Print out all the applicable ports. */ for(iCtr = 0; iCtr < MAXPORTS; iCtr++) { if( isPortEnabled( ServerConf->ports, iCtr ) ) { sfsnprintfappend(buf, STD_BUF, "%d ", iCtr); } } LogMessage("%s\n", buf); LogMessage(" Server Flow Depth: %d\n", ServerConf->server_flow_depth); LogMessage(" Client Flow Depth: %d\n", ServerConf->client_flow_depth); LogMessage(" Max Chunk Length: %d\n", ServerConf->chunk_length); if (ServerConf->small_chunk_length.size > 0) LogMessage(" Small Chunk Length Evasion: chunk size <= %u, threshold >= %u times\n", ServerConf->small_chunk_length.size, ServerConf->small_chunk_length.num); LogMessage(" Max Header Field Length: %d\n", ServerConf->max_hdr_len); LogMessage(" Max Number Header Fields: %d\n", ServerConf->max_headers); LogMessage(" Max Number of WhiteSpaces allowed with header folding: %d\n", ServerConf->max_spaces); LogMessage(" Inspect Pipeline Requests: %s\n", ServerConf->no_pipeline ? "NO" : "YES"); LogMessage(" URI Discovery Strict Mode: %s\n", ServerConf->non_strict ? "NO" : "YES"); LogMessage(" Allow Proxy Usage: %s\n", ServerConf->allow_proxy ? "YES" : "NO"); LogMessage(" Disable Alerting: %s\n", ServerConf->no_alerts ? "YES":"NO"); LogMessage(" Oversize Dir Length: %d\n", ServerConf->long_dir); LogMessage(" Only inspect URI: %s\n", ServerConf->uri_only ? "YES" : "NO"); LogMessage(" Normalize HTTP Headers: %s\n", ServerConf->normalize_headers ? "YES" : "NO"); LogMessage(" Inspect HTTP Cookies: %s\n", ServerConf->enable_cookie ? "YES" : "NO"); LogMessage(" Inspect HTTP Responses: %s\n", ServerConf->inspect_response ? "YES" : "NO"); LogMessage(" Extract Gzip from responses: %s\n", ServerConf->extract_gzip ? "YES" : "NO"); PrintFileDecompOpt(ServerConf); LogMessage(" Unlimited decompression of gzip data from responses: %s\n", ServerConf->unlimited_decompress ? "YES" : "NO"); LogMessage(" Normalize Javascripts in HTTP Responses: %s\n", ServerConf->normalize_javascript ? "YES" : "NO"); if(ServerConf->normalize_javascript) { if(ServerConf->max_js_ws) LogMessage(" Max Number of WhiteSpaces allowed with Javascript Obfuscation in HTTP responses: %d\n", ServerConf->max_js_ws); } LogMessage(" Normalize HTTP Cookies: %s\n", ServerConf->normalize_cookies ? "YES" : "NO"); LogMessage(" Enable XFF and True Client IP: %s\n", ServerConf->enable_xff ? "YES" : "NO"); LogMessage(" Log HTTP URI data: %s\n", ServerConf->log_uri ? "YES" : "NO"); LogMessage(" Log HTTP Hostname data: %s\n", ServerConf->log_hostname ? "YES" : "NO"); LogMessage(" Extended ASCII code support in URI: %s\n", ServerConf->extended_ascii_uri ? "YES" : "NO"); PrintConfOpt(&ServerConf->ascii, "Ascii"); PrintConfOpt(&ServerConf->double_decoding, "Double Decoding"); PrintConfOpt(&ServerConf->u_encoding, "%U Encoding"); PrintConfOpt(&ServerConf->bare_byte, "Bare Byte"); PrintConfOpt(&ServerConf->utf_8, "UTF 8"); PrintConfOpt(&ServerConf->iis_unicode, "IIS Unicode"); PrintConfOpt(&ServerConf->multiple_slash, "Multiple Slash"); PrintConfOpt(&ServerConf->iis_backslash, "IIS Backslash"); PrintConfOpt(&ServerConf->directory, "Directory Traversal"); PrintConfOpt(&ServerConf->webroot, "Web Root Traversal"); PrintConfOpt(&ServerConf->apache_whitespace, "Apache WhiteSpace"); PrintConfOpt(&ServerConf->iis_delimiter, "IIS Delimiter"); if(ServerConf->iis_unicode_map_filename) { LogMessage(" IIS Unicode Map Filename: %s\n", ServerConf->iis_unicode_map_filename); LogMessage(" IIS Unicode Map Codepage: %d\n", ServerConf->iis_unicode_codepage); } else if(ServerConf->iis_unicode_map) { LogMessage(" IIS Unicode Map: " "GLOBAL IIS UNICODE MAP CONFIG\n"); } else { LogMessage(" IIS Unicode Map: NOT CONFIGURED\n"); } /* ** Print out the non-rfc chars */ memset(buf, 0, STD_BUF+1); SnortSnprintf(buf, STD_BUF + 1, " Non-RFC Compliant Characters: "); for(iCtr = 0; iCtr < 256; iCtr++) { if(ServerConf->non_rfc_chars[iCtr]) { sfsnprintfappend(buf, STD_BUF, "0x%.2x ", (u_char)iCtr); iChar = 1; } } if(!iChar) { sfsnprintfappend(buf, STD_BUF, "NONE"); } LogMessage("%s\n", buf); /* ** Print out the whitespace chars */ iChar = 0; memset(buf, 0, STD_BUF+1); SnortSnprintf(buf, STD_BUF + 1, " Whitespace Characters: "); for(iCtr = 0; iCtr < 256; iCtr++) { if(ServerConf->whitespace[iCtr]) { sfsnprintfappend(buf, STD_BUF, "0x%.2x ", (u_char)iCtr); iChar = 1; } } if(!iChar) { sfsnprintfappend(buf, STD_BUF, "NONE"); } LogMessage("%s\n", buf); LogMessage(" Legacy mode: %s\n", ServerConf->h2_mode ? "YES" : "NO"); return 0; } static void registerPortsWithStream( HTTPINSPECT_CONF *policy, char *network ) { uint32_t port; uint32_t dir = 0; if( policy->client_flow_depth > -1 ) dir |= SSN_DIR_FROM_CLIENT; if( ( policy->server_extract_size > -1 ) ) dir |= SSN_DIR_FROM_SERVER; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) stream_api->register_reassembly_port( network, port, dir ); } } static void enableHiForConfiguredPorts( struct _SnortConfig *sc, HTTPINSPECT_CONF *policy ) { int port; for ( port = 0; port < MAXPORTS; port++ ) { if( isPortEnabled( policy->ports, port ) ) session_api->enable_preproc_for_port( sc, PP_HTTPINSPECT, PROTO_BIT__TCP, port ); } } int ProcessUniqueServerConf(struct _SnortConfig *sc, HTTPINSPECT_GLOBAL_CONF *GlobalConf, char *ErrorString, int ErrStrLen, char **saveptr) { char *pcToken; char *pIpAddressList = NULL; char *pIpAddressList2 = NULL; char *brkt = NULL; char firstIpAddress = 1; sfcidr_t Ip; HTTPINSPECT_CONF *ServerConf = NULL; int iRet; int retVal = -1; pcToken = strtok_r(NULL, CONF_SEPARATORS, saveptr); if(!pcToken) { SnortSnprintf(ErrorString, ErrStrLen, "No arguments to '%s' token.", SERVER); retVal = -1; goto _return; } /* ** Check for the default configuration first */ if (strcasecmp(SERVER_DEFAULT, pcToken) == 0) { if (GlobalConf->global_server != NULL) { SnortSnprintf(ErrorString, ErrStrLen, "Cannot configure '%s' settings more than once.", GLOBAL_SERVER); goto _return; } GlobalConf->global_server = (HTTPINSPECT_CONF *)SnortPreprocAlloc(1, sizeof(HTTPINSPECT_CONF), PP_HTTPINSPECT, PP_MEM_CATEGORY_CONFIG); ServerConf = GlobalConf->global_server; iRet = hi_ui_config_default(ServerConf); if (iRet) { snprintf(ErrorString, ErrStrLen, "Error configuring default global configuration."); return -1; } iRet = ProcessServerConf(GlobalConf, ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { retVal = iRet; goto _return; } // register enabled ports for reassembly with Stream for the default registerPortsWithStream( ServerConf, NULL ); enableHiForConfiguredPorts( sc, ServerConf ); /* ** Start writing out the Default Server Config */ LogMessage(" DEFAULT SERVER CONFIG:\n"); } else { /* ** Convert string to IP address */ /* get the first delimiter*/ if(strcmp(START_IPADDR_LIST, pcToken) == 0) { /*list begin token matched*/ if ((pIpAddressList = strtok_r(NULL, END_IPADDR_LIST, saveptr)) == NULL) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid IP Address list in '%s' token.", SERVER); goto _return; } } else { /*list begin didn't match so this must be an IP address*/ pIpAddressList = pcToken; } pIpAddressList2 = strdup(pIpAddressList); if (!pIpAddressList2) { SnortSnprintf(ErrorString, ErrStrLen, "Could not allocate memory for server configuration."); goto _return; } for (pcToken = strtok_r(pIpAddressList, CONF_SEPARATORS, &brkt); pcToken; pcToken = strtok_r(NULL, CONF_SEPARATORS, &brkt)) { if (sfip_pton(pcToken, &Ip) != SFIP_SUCCESS) { SnortSnprintf(ErrorString, ErrStrLen, "Invalid IP to '%s' token.", SERVER); goto _return; } /* ** allocate the memory for the server configuration */ if (firstIpAddress) { ServerConf = (HTTPINSPECT_CONF *)SnortPreprocAlloc(1, sizeof(HTTPINSPECT_CONF), PP_HTTPINSPECT, PP_MEM_CATEGORY_CONFIG); if(!ServerConf) { SnortSnprintf(ErrorString, ErrStrLen, "Could not allocate memory for server configuration."); goto _return; } iRet = ProcessServerConf(GlobalConf, ServerConf, ErrorString, ErrStrLen, saveptr); if (iRet) { retVal = iRet; goto _return; } } iRet = hi_ui_config_add_server(GlobalConf, &Ip, ServerConf); if (iRet) { /* ** Check for already added servers */ if(iRet == HI_NONFATAL_ERR) { SnortSnprintf(ErrorString, ErrStrLen, "Duplicate server configuration."); goto _return; } else { SnortSnprintf(ErrorString, ErrStrLen, "Error when adding server configuration."); goto _return; } } // register enabled ports for reassembly with Stream for the current netowrk registerPortsWithStream( ServerConf, pcToken ); enableHiForConfiguredPorts( sc, ServerConf ); if (firstIpAddress) { //process the first IP address as usual firstIpAddress = 0; } //create a reference ServerConf->referenceCount++; } if (firstIpAddress) { //no IP address was found SnortSnprintf(ErrorString, ErrStrLen, "Invalid IP Address list in '%s' token.", SERVER); goto _return; } /* ** Print out the configuration header */ LogMessage(" SERVER: %s\n", pIpAddressList2); } /* ** Finish printing out the server configuration */ PrintServerConf(ServerConf); retVal = 0; _return: if (pIpAddressList2) { free(pIpAddressList2); } return retVal; } int PrintGlobalConf(HTTPINSPECT_GLOBAL_CONF *GlobalConf) { LogMessage("HttpInspect Config:\n"); LogMessage(" GLOBAL CONFIG\n"); if(GlobalConf->disabled) { LogMessage(" Http Inspect: INACTIVE\n"); LogMessage(" Max Gzip Memory: %d\n", GlobalConf->max_gzip_mem); LogMessage(" Memcap used for logging URI and Hostname: %u\n", GlobalConf->memcap); return 0; } LogMessage(" Detect Proxy Usage: %s\n", GlobalConf->proxy_alert ? "YES" : "NO"); LogMessage(" IIS Unicode Map Filename: %s\n", GlobalConf->iis_unicode_map_filename); LogMessage(" IIS Unicode Map Codepage: %d\n", GlobalConf->iis_unicode_codepage); LogMessage(" Memcap used for logging URI and Hostname: %u\n", GlobalConf->memcap); LogMessage(" Max Gzip Memory: %d\n", GlobalConf->max_gzip_mem); LogMessage(" Max Gzip Sessions: %d\n", GlobalConf->max_gzip_sessions); LogMessage(" Gzip Compress Depth: %d\n", GlobalConf->compr_depth); LogMessage(" Gzip Decompress Depth: %d\n", GlobalConf->decompr_depth); LogMessage(" Normalize Random Nulls in Text: %s\n", GlobalConf->normalize_nulls ? "YES" : "NO"); return 0; } /* ** NAME ** LogEvents:: */ /** ** This is the routine that logs HttpInspect alerts through Snort. ** ** Every Session gets looked at for any logged events, and if there are ** events to be logged then we select the one with the highest priority. ** ** We use a generic event structure that we set for each different event ** structure. This way we can use the same code for event logging regardless ** of what type of event strucure we are dealing with. ** ** The important things to know about this function is how to work with ** the event queue. The number of unique events is contained in the ** stack_count variable. So we loop through all the unique events and ** find which one has the highest priority. During this loop, we also ** re-initialize the individual event counts for the next iteration, saving ** us time in a separate initialization phase. ** ** After we've iterated through all the events and found the one with the ** highest priority, we then log that event through snort. ** ** We've mapped the HttpInspect and the Snort alert IDs together, so we ** can access them directly instead of having a more complex mapping ** function. It's the only good way to do this. ** ** @param Session pointer to Session construct ** @param p pointer to the Snort packet construct ** @param iInspectMode inspection mode to take event queue from ** ** @return integer ** ** @retval 0 this function only return success */ static inline int LogEvents(HI_SESSION *hi_ssn, Packet *p, int iInspectMode, HttpSessionData *hsd) { HI_GEN_EVENTS GenEvents; HI_EVENT *OrigEvent; HI_EVENT *HiEvent = NULL; uint64_t uiMask = 0; int iGenerator; int iStackCnt; uint64_t iEvent; int iCtr; /* ** Set the session ptr, if applicable */ if(iInspectMode == HI_SI_CLIENT_MODE) { GenEvents.stack = hi_ssn->client.event_list.stack; GenEvents.stack_count = &(hi_ssn->client.event_list.stack_count); GenEvents.events = hi_ssn->client.event_list.events; iGenerator = GENERATOR_SPP_HTTP_INSPECT_CLIENT; } else if(iInspectMode == HI_SI_SERVER_MODE) { GenEvents.stack = hi_ssn->server.event_list.stack; GenEvents.stack_count = &(hi_ssn->server.event_list.stack_count); GenEvents.events = hi_ssn->server.event_list.events; iGenerator = GENERATOR_SPP_HTTP_INSPECT; } else { GenEvents.stack = hi_ssn->anom_server.event_list.stack; GenEvents.stack_count = &(hi_ssn->anom_server.event_list.stack_count); GenEvents.events = hi_ssn->anom_server.event_list.events; iGenerator = GENERATOR_SPP_HTTP_INSPECT; } /* ** Now starts the generic event processing */ iStackCnt = *(GenEvents.stack_count); /* ** IMPORTANT:: ** We have to check the stack count of the event queue before we process ** an log. */ if(iStackCnt == 0) { return 0; } /* ** Cycle through the events and select the event with the highest ** priority. */ for(iCtr = 0; iCtr < iStackCnt; iCtr++) { iEvent = (uint64_t)GenEvents.stack[iCtr]; OrigEvent = &(GenEvents.events[iEvent]); /* ** Set the event to start off the comparison */ if(!HiEvent) { HiEvent = OrigEvent; } /* ** This is our "comparison function". Log the event with the highest ** priority. */ if(OrigEvent->event_info->priority < HiEvent->event_info->priority) { HiEvent = OrigEvent; } /* ** IMPORTANT: ** This is how we reset the events in the event queue. ** If you miss this step, you can be really screwed. */ OrigEvent->count = 0; } /* ** We use the iEvent+1 because the event IDs between snort and ** HttpInspect are mapped off-by-one. Don't ask why, drink Bud ** Dry . . . They're mapped off-by one because in the internal ** HttpInspect queue, events are mapped starting at 0. For some ** reason, it appears that the first event can't be zero, so we ** use the internal value and add one for snort. */ iEvent = (uint64_t)HiEvent->event_info->alert_id + 1; uiMask = (uint64_t)1 << (iEvent & 63); if (hsd != NULL) { /* We've already logged this event for this session, * don't log it again */ if (hsd->event_flags & uiMask) return 0; hsd->event_flags |= uiMask; } SnortEventqAdd(iGenerator, iEvent, 1, 0, 3, HiEvent->event_info->alert_str,0); /* ** Reset the event queue stack counter, in the case of pipelined ** requests. */ *(GenEvents.stack_count) = 0; return 0; } static inline int SetSiInput(HI_SI_INPUT *SiInput, Packet *p) { IP_COPY_VALUE(SiInput->sip, GET_SRC_IP(p)); IP_COPY_VALUE(SiInput->dip, GET_DST_IP(p)); SiInput->sport = p->sp; SiInput->dport = p->dp; /* ** We now set the packet direction */ if(p->ssnptr && session_api->get_session_flags(p->ssnptr) & SSNFLAG_MIDSTREAM) { SiInput->pdir = HI_SI_NO_MODE; } else if(p->packet_flags & PKT_FROM_SERVER) { SiInput->pdir = HI_SI_SERVER_MODE; } else if(p->packet_flags & PKT_FROM_CLIENT) { SiInput->pdir = HI_SI_CLIENT_MODE; } else { SiInput->pdir = HI_SI_NO_MODE; } return HI_SUCCESS; } static inline void ApplyClientFlowDepth (Packet* p, int flow_depth) { switch (flow_depth) { case -1: // Inspect none of the client if there is normalized/extracted // URI/Method/Header/Body data */ SetDetectLimit(p, 0); break; case 0: // Inspect all of the client, even if there is normalized/extracted // URI/Method/Header/Body data */ /* XXX: HUGE performance hit here */ SetDetectLimit(p, p->dsize); break; default: // Limit inspection of the client, even if there is normalized/extracted // URI/Method/Header/Body data */ /* XXX: Potential performance hit here */ if (flow_depth < p->dsize) { SetDetectLimit(p, flow_depth); } else { SetDetectLimit(p, p->dsize); } break; } } // FIXTHIS extra data masks should only be updated as extra data changes state // eg just once when captured; this function is called on every packet and // repeatedly sets the flags on session static inline void HttpLogFuncs(HTTPINSPECT_GLOBAL_CONF *GlobalConf, HttpSessionData *hsd, Packet *p, int iCallDetect ) { if(!hsd) return; /* for pipelined HTTP requests */ if ( !iCallDetect ) stream_api->clear_extra_data(p->ssnptr, p, 0); if ( hsd->tList_start != NULL ) { if( hsd->tList_start->tID == hsd->http_resp_id || hsd->tList_end->tID == hsd->http_req_id ) { if(!(p->packet_flags & PKT_STREAM_INSERT) && !(p->packet_flags & PKT_REBUILT_STREAM)) SetExtraData(p, GlobalConf->xtra_trueip_id); else stream_api->set_extra_data(p->ssnptr, p, GlobalConf->xtra_trueip_id); } } if(hsd->log_flags & HTTP_LOG_URI) { stream_api->set_extra_data(p->ssnptr, p, GlobalConf->xtra_uri_id); } if(hsd->log_flags & HTTP_LOG_HOSTNAME) { stream_api->set_extra_data(p->ssnptr, p, GlobalConf->xtra_hname_id); } #ifndef SOURCEFIRE if(hsd->log_flags & HTTP_LOG_JSNORM_DATA) { SetExtraData(p, GlobalConf->xtra_jsnorm_id); } if(hsd->log_flags & HTTP_LOG_GZIP_DATA) { SetExtraData(p, GlobalConf->xtra_gzip_id); } #endif } static inline void setFileName(Packet *p) { uint8_t *buf = NULL; uint32_t len = 0; uint32_t type = 0; GetHttpUriData(p->ssnptr, &buf, &len, &type); file_api->set_file_name (p->ssnptr, buf, len, false); } static inline bool rfc_2616_token(u_char c) { return isalpha(c) || isdigit(c) || c == '!' || c == '#' || c == '$' || c == '%' || c == '&' || c == '\'' || c == '*' || c == '+' || c == '-' || c == '.' || c == '^' || c == '_' || c == '`' || c == '|' || c == '~'; } static inline bool rfc5987_attr_char(u_char c) { return rfc_2616_token(c) && (( c != '*')|| (c !='\'') || (c !='%')); } /* Extract the filename from the content-dispostion header- RFC6266 */ static inline int extract_file_name(u_char *start, int length, u_char **fname_ptr, uint8_t *charset) { u_char *cur = start, *tmp = NULL; u_char *end = start+length; u_char *fname_begin = NULL; u_char *fname_end = NULL; bool char_set = false; enum { CD_STATE_START, CD_STATE_BEFORE_VAL, CD_STATE_VAL, CD_STATE_QUOTED_VAL, CD_STATE_BEFORE_EXT_VAL, CD_STATE_CHARSET, CD_STATE_LANGUAGE, CD_STATE_EXT_VAL, CD_STATE_FINAL }; const char *cd_file1 = "filename"; const char *cd_file2 = "filename*"; if (length <= 0) return -1; uint8_t state = CD_STATE_START; while(cur < end) { switch(state) { case CD_STATE_START: { if( (tmp = (u_char *)SnortStrcasestr((const char *)cur, end-cur, cd_file2))) { state = CD_STATE_BEFORE_EXT_VAL; cur = tmp + strlen(cd_file2)-1; } else if( (tmp = (u_char *)SnortStrcasestr((const char *)cur, end-cur, cd_file1))) { state = CD_STATE_BEFORE_VAL; cur = tmp + strlen(cd_file1)-1; } else return -1; } break; case CD_STATE_BEFORE_VAL: { if(*cur == '=') state = CD_STATE_VAL; else if( *cur != ' ') state = CD_STATE_START; } break; case CD_STATE_VAL: { if( !fname_begin && *cur == '"') state = CD_STATE_QUOTED_VAL; else if(rfc_2616_token(*cur)) { if(!fname_begin) fname_begin = cur; } else if(*cur == ';' || *cur == '\r' || *cur == '\n' || *cur == ' ' || *cur == '\t') { if(fname_begin) { fname_end = cur - 1; state = CD_STATE_FINAL; } } else return -1; } break; case CD_STATE_QUOTED_VAL: { if(!fname_begin) fname_begin = cur; if(*cur == '"' ) { fname_end = cur; state = CD_STATE_FINAL; } } break; case CD_STATE_BEFORE_EXT_VAL: { if(*cur == '=') state = CD_STATE_CHARSET; else if( *cur != ' ') state = CD_STATE_START; } break; case CD_STATE_CHARSET: { if( *cur == '\'') { if(!char_set) return -1; else state = CD_STATE_LANGUAGE; } else if(!char_set) { /* Ignore space before the ext-value */ while(cur < end && *cur == ' ' ) cur++; if(cur < end) { if(!strncasecmp((const char*)cur,"UTF-8",5)) { *charset = CD_CHARSET_UTF8; cur += 5; } else if(!strncasecmp((const char *)cur,"ISO-8859-1",10)) { *charset = CD_CHARSET_ISO_9959_1; cur += 10; } else if(!strncasecmp((const char *)cur,"mime-charset",12)) { *charset = CD_CHARSET_MIME; cur+=12; } else return -1; char_set = true; continue; } } else return -1; } break; case CD_STATE_LANGUAGE: { if(*cur == '\'') state = CD_STATE_EXT_VAL; } break; case CD_STATE_EXT_VAL: { if(!rfc5987_attr_char(*cur)) { if(*cur == '%') { //Percent encoded, check if the next two digits are hex if(!fname_begin) fname_begin = cur; if( !(cur+2 < end && isxdigit(*++cur) && isxdigit(*++cur))) return -1; } else if(*cur == ';' || *cur == '\r' || *cur == '\n' || *cur == ' ' || *cur == '\t') { fname_end = cur; state = CD_STATE_FINAL; } } else { if(!fname_begin) fname_begin = cur; } } break; case CD_STATE_FINAL: { if(fname_begin && fname_end) { *fname_ptr = fname_begin; return fname_end-fname_begin; } } default: return -1; } cur++; } switch(state) { case CD_STATE_FINAL: case CD_STATE_VAL: case CD_STATE_EXT_VAL: { if(fname_begin) { *fname_ptr = fname_begin; if(!fname_end) fname_end = end; return fname_end-fname_begin; } } } return -1; } static bool set_file_name_cd_header(u_char *start, u_char *end, void *ssn) { uint8_t unfold_buf[DECODE_BLEN] = {0}; uint32_t unfold_size =0; int num_spaces = 0; u_char *p = start; u_char *fname = NULL; int len = 0; uint8_t charset = 0; sf_unfold_header(p, end-p, unfold_buf, sizeof(unfold_buf), &unfold_size, 0 , &num_spaces); if(!unfold_size) return false; if((len = extract_file_name(unfold_buf, unfold_size, &fname, &charset)) > 0 ) { //Strip the size to 255 if bigger file_api->set_file_name(ssn, fname, len > 255 ? 255:len, true); return true; } return false; } static inline bool is_boundary_present(const u_char *start, const u_char *end) { uint8_t unfold_buf[DECODE_BLEN] = {0}; uint32_t unfold_size =0; int num_spaces = 0; const u_char *p = start; const char *BOUNDARY_STR = "boundary="; sf_unfold_header(p, end-p, unfold_buf, sizeof(unfold_buf), &unfold_size, 0 , &num_spaces); if(!unfold_size) return false; return SnortStrcasestr((const char *)unfold_buf, unfold_size, BOUNDARY_STR); } static inline int processPostFileData(HTTPINSPECT_GLOBAL_CONF *GlobalConf, Packet *p, HI_SESSION *Session, HttpSessionData *hsd) { const u_char *start = Session->client.request.content_type; const u_char *end = (Session->client.request.post_raw + Session->client.request.post_raw_size); if ( !PacketHasPAFPayload(p) || (p->packet_flags & PKT_PSEUDO_FLUSH)) return 0; if ( hsd && start && is_boundary_present(start, end)) { /* mime parsing * mime boundary should be processed before this */ if (!hsd->mime_ssn) { hsd->mime_ssn = (MimeState *)SnortPreprocAlloc(1, sizeof(MimeState), PP_HTTPINSPECT, PP_MEM_CATEGORY_CONFIG); if (!hsd->mime_ssn) return -1; hsd->mime_ssn->log_config = &(GlobalConf->mime_conf); hsd->mime_ssn->decode_conf = &(GlobalConf->decode_conf); hsd->mime_ssn->mime_mempool = mime_decode_mempool; hsd->mime_ssn->log_mempool = mime_log_mempool; /*Set log buffers per session*/ if (file_api->set_log_buffers(&(hsd->mime_ssn->log_state), hsd->mime_ssn->log_config, hsd->mime_ssn->log_mempool, p->ssnptr, PP_HTTPINSPECT) < 0) { return -1; } } else { file_api->reset_mime_paf_state(&(hsd->mime_ssn->mime_boundary)); } file_api->process_mime_data(p, start, end, hsd->mime_ssn, 1, false,"HTTP", PP_HTTPINSPECT); } else { if (file_api->file_process(p,(uint8_t *)Session->client.request.post_raw, (uint16_t)Session->client.request.post_raw_size, file_api->get_file_position(p), true, false, false)) { if( Session->client.request.content_disp ) { if(!set_file_name_cd_header((u_char *)Session->client.request.content_disp,(u_char *)end, p->ssnptr)) { setFileName(p); } } else { setFileName(p); } } } return 0; } static inline void processFileData(Packet *p, HttpSessionData *hsd, bool *fileProcessed) { if (*fileProcessed || !PacketHasPAFPayload(p)) return; if (hsd->mime_ssn) { uint8_t *end = ( uint8_t *)(p->data) + p->dsize; file_api->process_mime_data(p, p->data, end, hsd->mime_ssn, 1, false, "HTTP", PP_HTTPINSPECT); *fileProcessed = true; } else if (file_api->get_file_processed_size(p->ssnptr) >0) { file_api->file_process(p, (uint8_t *)p->data, p->dsize, file_api->get_file_position(p), true, false, false); *fileProcessed = true; } } static inline int get_file_current_position(Packet *p,bool decomp_more,bool is_first) { int file_data_position = SNORT_FILE_POSITION_UNKNOWN; uint64_t processed_size = file_api->get_file_processed_size(p->ssnptr); if(decomp_more) { if(is_first) { if(PacketHasStartOfPDU(p)) file_data_position = SNORT_FILE_START; else if(processed_size) file_data_position = SNORT_FILE_MIDDLE; } else { if(processed_size) file_data_position = SNORT_FILE_MIDDLE; } } else { if(is_first) { file_data_position = file_api->get_file_position(p); } else { if(p->packet_flags & PKT_PDU_TAIL) file_data_position = SNORT_FILE_END; else if(processed_size) file_data_position = SNORT_FILE_MIDDLE; } } return file_data_position; } char *convert_range_flag_to_str(uint16_t range_flag) { switch (range_flag) { case HTTP_RESP_RANGE_NONE: return "Range None"; case RANGE_WITH_RESP_FULL_CONTENT: return "Full Content"; case RANGE_WITH_RESP_PARTIAL_CONTENT: return "Partial Content"; case RANGE_WITH_RESP_ERROR: return "Error in Range Field"; case RANGE_WITH_RESP_NON_BYTE: return "Non-Byte unit"; case RANGE_WITH_UNKNOWN_CONTENT_RANGE: return "Unknown Range Content"; case RANGE_WITH_RESP_UNKNOWN_CONTENT_SIZE: return "Unknown Range Content Length"; default: return "Skip Range"; } } /* ** NAME ** SnortHttpInspect:: */ /** ** This function calls the HttpInspect function that processes an HTTP ** session. ** ** We need to instantiate a pointer for the HI_SESSION that HttpInspect ** fills in. Right now stateless processing fills in this session, which ** we then normalize, and eventually detect. We'll have to handle ** separately the normalization events, etc. ** ** This function is where we can see from the highest level what the ** HttpInspect flow looks like. ** ** @param GlobalConf pointer to the global configuration ** @param p pointer to the Packet structure ** ** @return integer ** ** @retval 0 function successful ** @retval <0 fatal error ** @retval >0 non-fatal error */ #define HTTP_BUF_URI_FLAG 0x01 #define HTTP_BUF_HEADER_FLAG 0x02 #define HTTP_BUF_CLIENT_BODY_FLAG 0x04 #define HTTP_BUF_METHOD_FLAG 0x08 #define HTTP_BUF_COOKIE_FLAG 0x10 #define HTTP_BUF_STAT_CODE 0x20 #define HTTP_BUF_STAT_MSG 0x40 int SnortHttpInspect(HTTPINSPECT_GLOBAL_CONF *GlobalConf, Packet *p) { HI_SESSION *Session; HI_SI_INPUT SiInput; int iInspectMode = 0; int iRet; int iCallDetect = 1; HttpSessionData *hsd = NULL; bool fileProcessed = false; bool is_first = true; if (stream_api && stream_api->is_session_http2(p->ssnptr) && !(p->packet_flags & PKT_REBUILT_STREAM) && !(p->packet_flags & PKT_PDU_TAIL)) { return 0; } PROFILE_VARS; if(!(stream_api->get_preproc_flags(p->ssnptr) & PP_HTTPINSPECT_PAF_FLUSH_POST_HDR)) hi_stats.total++; /* ** Set up the HI_SI_INPUT pointer. This is what the session_inspection() ** routines use to determine client and server traffic. Plus, this makes ** the HttpInspect library very independent from snort. */ SetSiInput(&SiInput, p); /* ** HTTPINSPECT PACKET FLOW:: ** ** Session Inspection Module:: ** The Session Inspection Module retrieves the appropriate server ** configuration for sessions, and takes care of the stateless ** vs. stateful processing in order to do this. Once this module ** does it's magic, we're ready for the primetime. ** ** HTTP Inspection Module:: ** This isn't really a module in HttpInspect, but more of a helper ** function that sends the data to the appropriate inspection ** routine (client, server, anomalous server detection). ** ** HTTP Normalization Module:: ** This is where we normalize the data from the HTTP Inspection ** Module. The Normalization module handles what type of normalization ** to do (client, server). ** ** HTTP Detection Module:: ** This isn't being used in the first iteration of HttpInspect, but ** all the HTTP detection components of signatures will be. ** ** HTTP Event Output Module:: ** The Event Ouput Module handles any events that have been logged ** in the inspection, normalization, or detection phases. */ /* ** Session Inspection Module:: */ iRet = hi_si_session_inspection(GlobalConf, &Session, &SiInput, &iInspectMode, p); if (iRet) return iRet; /* If no mode then we just look for anomalous servers if configured * to do so and get out of here */ if (iInspectMode == HI_SI_NO_MODE) { /* Let's look for rogue HTTP servers and stuff */ if (GlobalConf->anomalous_servers && (p->dsize > 5)) { iRet = hi_server_anomaly_detection(Session, p->data, p->dsize); if (iRet) return iRet; /* ** We log events before doing detection because every non-HTTP ** packet is possible an anomalous server. So we still want to ** go through the regular detection engine, and just log any ** alerts here before returning. ** ** Return normally if this isn't either HTTP client or server ** traffic. */ if (Session->anom_server.event_list.stack_count) LogEvents(Session, p, iInspectMode, NULL); } return 0; } hsd = GetHttpSessionData(p); /* ** HI_EO_SERVER_PROTOCOL_OTHER alert added to detect 'SSH tunneling over HTTP', ** In SSH over HTTP evasion, first data message will always be 'HTTP response with SSH server ** version/banner' (without any client request). If the HTTP server response is the ** first message in http_session, this alert will be generated */ if(!hsd && ( SiInput.pdir == HI_SI_SERVER_MODE ) && (p->packet_flags & PKT_STREAM_ORDER_OK)) { if(p->ssnptr && ((session_api->get_session_flags(p->ssnptr) &(SSNFLAG_SEEN_BOTH|SSNFLAG_MIDSTREAM)) == SSNFLAG_SEEN_BOTH)) { if(hi_eo_generate_event(Session, HI_EO_SERVER_PROTOCOL_OTHER)) { hi_eo_server_event_log(Session, HI_EO_SERVER_PROTOCOL_OTHER, NULL, NULL); } LogEvents(Session, p, iInspectMode, hsd); } } if ( ScPafEnabled() && (p->packet_flags & PKT_STREAM_INSERT) && (!(p->packet_flags & PKT_PDU_TAIL)) ) { int flow_depth; if ( iInspectMode == HI_SI_CLIENT_MODE ) { flow_depth = Session->server_conf->client_flow_depth; ApplyClientFlowDepth(p, flow_depth); } else { ApplyFlowDepth(Session->server_conf, p, hsd, 0, 1, GET_PKT_SEQ(p)); } p->packet_flags |= PKT_HTTP_DECODE; HttpLogFuncs(GlobalConf, hsd, p, iCallDetect); if ( p->alt_dsize == 0 ) { DisableDetect( p ); EnablePreprocessor(p, PP_SDF); return 0; } // see comments on call to Detect() below PREPROC_PROFILE_START(hiDetectPerfStats); Detect(p); #ifdef PERF_PROFILING hiDetectCalled = 1; #endif PREPROC_PROFILE_END(hiDetectPerfStats); return 0; } if (hsd == NULL) { hsd = SetNewHttpSessionData(p, (void *)Session); if (hsd == NULL) return 0; } else { /* Gzip data should not be logged with all the packets of the session.*/ hsd->log_flags &= ~HTTP_LOG_GZIP_DATA; hsd->log_flags &= ~HTTP_LOG_JSNORM_DATA; } /* ** HTTP Inspection Module:: ** ** This is where we do the client/server inspection and find the ** various HTTP protocol fields. We then normalize these fields and ** call the detection engine. ** ** The reason for the loop is for pipelined requests. Doing pipelined ** requests in this way doesn't require any memory or tracking overhead. ** Instead, we just process each request linearly. */ uint16_t vlanId = p->vh ? VTH_VLAN( p->vh ) : 0; if (hsd->decomp_state) hsd->decomp_state->stage = HTTP_DECOMP_START; do { /* ** INIT: ** We set this equal to zero (again) because of the pipelining ** requests. We don't want to bail before we get to setting the ** URI, so we make sure here that this can't happen. */ SetHttpDecode(0); ClearHttpBuffers(); iRet = hi_mi_mode_inspection(Session, iInspectMode, p, hsd); if (iRet) { if (hsd) { processFileData(p, hsd, &fileProcessed); } LogEvents(Session, p, iInspectMode, hsd); return iRet; } iRet = hi_normalization(Session, iInspectMode, hsd); if (iRet) { LogEvents(Session, p, iInspectMode, hsd); return iRet; } HttpLogFuncs(GlobalConf, hsd, p, iCallDetect); /* ** Let's setup the pointers for the detection engine, and ** then go for it. */ if ( iInspectMode == HI_SI_CLIENT_MODE ) { const HttpBuffer* hb; ClearHttpBuffers(); // FIXTHIS needed here and right above?? #ifdef DUMP_BUFFER clearReqBuffers(); clearRespBuffers(); #endif if ( Session->client.request.uri_norm ) { SetHttpBufferEncoding( HTTP_BUFFER_URI, Session->client.request.uri_norm, Session->client.request.uri_norm_size, Session->client.request.uri_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_URI, Session->client.request.uri, Session->client.request.uri_size); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(URI_DUMP, Session->client.request.uri_norm, Session->client.request.uri_norm_size); #endif } else if ( Session->client.request.uri ) { SetHttpBufferEncoding( HTTP_BUFFER_URI, Session->client.request.uri, Session->client.request.uri_size, Session->client.request.uri_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_URI, Session->client.request.uri, Session->client.request.uri_size); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(RAW_URI_DUMP, Session->client.request.uri, Session->client.request.uri_size); #endif } if ( Session->client.request.header_norm || Session->client.request.header_raw ) { if ( Session->client.request.header_norm ) { SetHttpBufferEncoding( HTTP_BUFFER_HEADER, Session->client.request.header_norm, Session->client.request.header_norm_size, Session->client.request.header_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_HEADER, Session->client.request.header_raw, Session->client.request.header_raw_size); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(REQ_HEADER_DUMP, Session->client.request.header_norm, Session->client.request.header_norm_size); #endif #ifdef DEBUG hi_stats.req_header_len += Session->client.request.header_norm_size; #endif } else { SetHttpBufferEncoding( HTTP_BUFFER_HEADER, Session->client.request.header_raw, Session->client.request.header_raw_size, Session->client.request.header_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_HEADER, Session->client.request.header_raw, Session->client.request.header_raw_size); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(RAW_REQ_HEADER_DUMP, Session->client.request.header_raw, Session->client.request.header_raw_size); #endif } } if(Session->client.request.method & (HI_POST_METHOD | HI_GET_METHOD)) { if(Session->client.request.post_raw) { if(processPostFileData(GlobalConf, p, Session, hsd) != 0) return 0; if(Session->server_conf->post_depth > -1) { if(Session->server_conf->post_depth && ((int)Session->client.request.post_raw_size > Session->server_conf->post_depth)) { Session->client.request.post_raw_size = Session->server_conf->post_depth; } SetHttpBufferEncoding( HTTP_BUFFER_CLIENT_BODY, Session->client.request.post_raw, Session->client.request.post_raw_size, Session->client.request.post_encode_type); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(CLIENT_BODY_DUMP, Session->client.request.post_raw, Session->client.request.post_raw_size); #endif } } } else if (hsd) { processFileData(p, hsd, &fileProcessed); } if ( Session->client.request.method_raw ) { SetHttpBuffer( HTTP_BUFFER_METHOD, Session->client.request.method_raw, Session->client.request.method_size); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(METHOD_DUMP, Session->client.request.method_raw, Session->client.request.method_size); #endif } if ( Session->client.request.cookie_norm || Session->client.request.cookie.cookie ) { if ( Session->client.request.cookie_norm ) { SetHttpBufferEncoding( HTTP_BUFFER_COOKIE, Session->client.request.cookie_norm, Session->client.request.cookie_norm_size, Session->client.request.cookie_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_COOKIE, Session->client.request.cookie.cookie, Session->client.request.cookie.cookie_end - Session->client.request.cookie.cookie); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(COOKIE_DUMP, Session->client.request.cookie_norm, Session->client.request.cookie_norm_size); #endif } else { SetHttpBufferEncoding( HTTP_BUFFER_COOKIE, Session->client.request.cookie.cookie, Session->client.request.cookie.cookie_end - Session->client.request.cookie.cookie, Session->client.request.cookie_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_COOKIE, Session->client.request.cookie.cookie, Session->client.request.cookie.cookie_end - Session->client.request.cookie.cookie); p->packet_flags |= PKT_HTTP_DECODE; #ifdef DUMP_BUFFER dumpBuffer(RAW_COOKIE_DUMP, Session->client.request.cookie.cookie, Session->client.request.cookie.cookie_end - Session->client.request.cookie.cookie); #endif } } else if ( !Session->server_conf->enable_cookie && (hb = GetHttpBuffer(HTTP_BUFFER_HEADER)) ) { SetHttpBufferEncoding( HTTP_BUFFER_COOKIE, hb->buf, hb->length, hb->encode_type); hb = GetHttpBuffer(HTTP_BUFFER_RAW_HEADER); assert(hb); SetHttpBuffer(HTTP_BUFFER_RAW_COOKIE, hb->buf, hb->length); #ifdef DUMP_BUFFER dumpBuffer(RAW_COOKIE_DUMP, hb->buf, hb->length); #endif p->packet_flags |= PKT_HTTP_DECODE; } if ( IsLimitedDetect(p) ) { ApplyClientFlowDepth(p, Session->server_conf->client_flow_depth); if( !GetHttpBufferMask() && (p->alt_dsize == 0) ) { DisableDetect( p ); EnablePreprocessor(p, PP_SDF); return 0; } } if (Session->client.request.range_flag != HTTP_RANGE_NONE) { if (Session->client.request.method != HI_GET_METHOD) { if (hi_eo_generate_event(Session, HI_EO_CLIENT_RANGE_NON_GET_METHOD)) { hi_eo_client_event_log(Session, HI_EO_CLIENT_RANGE_NON_GET_METHOD, NULL, NULL); } } else { if (Session->client.request.range_flag == RANGE_WITH_REQ_ERROR) { if (hi_eo_generate_event(Session, HI_EO_CLIENT_RANGE_FIELD_ERROR)) { hi_eo_client_event_log(Session, HI_EO_CLIENT_RANGE_FIELD_ERROR, NULL, NULL); } } else { if (vlanId == 0) hsd->resp_state.look_for_partial_content |= GET_REQ_WITH_RANGE; } } } } else /* Server mode */ { const HttpBuffer* hb; /* ** We check here to see whether this was a server response ** header or not. If the header size is 0 then, we know that this ** is not the header and don't do any detection. */ #if defined(FEAT_OPEN_APPID) if( !(Session->server_conf->inspect_response || Session->server_conf->appid_enabled) && #else if( !(Session->server_conf->inspect_response) && #endif /* defined(FEAT_OPEN_APPID) */ IsLimitedDetect(p) && !p->alt_dsize ) { DisableDetect( p ); EnablePreprocessor(p, PP_SDF); if(Session->server_conf->server_flow_depth == -1) { EnablePreprocessor(p, PP_DCE2); } return 0; } ClearHttpBuffers(); if ( Session->server.response.header_norm || Session->server.response.header_raw ) { if ( Session->server.response.header_norm ) { SetHttpBufferEncoding( HTTP_BUFFER_HEADER, Session->server.response.header_norm, Session->server.response.header_norm_size, Session->server.response.header_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_HEADER, Session->server.response.header_raw, Session->server.response.header_raw_size); #ifdef DUMP_BUFFER dumpBuffer(RESP_HEADER_DUMP, Session->server.response.header_norm, Session->server.response.header_norm_size); #endif } else { SetHttpBuffer( HTTP_BUFFER_HEADER, Session->server.response.header_raw, Session->server.response.header_raw_size); SetHttpBuffer( HTTP_BUFFER_RAW_HEADER, Session->server.response.header_raw, Session->server.response.header_raw_size); #ifdef DUMP_BUFFER dumpBuffer(RAW_RESP_HEADER_DUMP, Session->server.response.header_raw, Session->server.response.header_raw_size); #endif } } if ( Session->server.response.cookie_norm || Session->server.response.cookie.cookie ) { if(Session->server.response.cookie_norm ) { SetHttpBufferEncoding( HTTP_BUFFER_COOKIE, Session->server.response.cookie_norm, Session->server.response.cookie_norm_size, Session->server.response.cookie_encode_type); SetHttpBuffer( HTTP_BUFFER_RAW_COOKIE, Session->server.response.cookie.cookie, Session->server.response.cookie.cookie_end - Session->server.response.cookie.cookie); #ifdef DUMP_BUFFER dumpBuffer(RESP_COOKIE_DUMP, Session->server.response.cookie_norm, Session->server.response.cookie_norm_size); #endif } else { SetHttpBuffer( HTTP_BUFFER_COOKIE, Session->server.response.cookie.cookie, Session->server.response.cookie.cookie_end - Session->server.response.cookie.cookie); SetHttpBuffer( HTTP_BUFFER_RAW_COOKIE, Session->server.response.cookie.cookie, Session->server.response.cookie.cookie_end - Session->server.response.cookie.cookie); #ifdef DUMP_BUFFER dumpBuffer(RAW_RESP_COOKIE_DUMP, Session->server.response.cookie.cookie, Session->server.response.cookie.cookie_end - Session->server.response.cookie.cookie); #endif } } else if ( !Session->server_conf->enable_cookie && (hb = GetHttpBuffer(HTTP_BUFFER_HEADER)) ) { SetHttpBufferEncoding( HTTP_BUFFER_COOKIE, hb->buf, hb->length, hb->encode_type); hb = GetHttpBuffer(HTTP_BUFFER_RAW_HEADER); assert(hb); SetHttpBuffer(HTTP_BUFFER_RAW_COOKIE, hb->buf, hb->length); #ifdef DUMP_BUFFER dumpBuffer(RAW_RESP_COOKIE_DUMP, hb->buf, hb->length); #endif } if(Session->server.response.status_code) { SetHttpBuffer( HTTP_BUFFER_STAT_CODE, Session->server.response.status_code, Session->server.response.status_code_size); if (!strncmp((const char*)Session->server.response.status_code, "206", 3)) { if ((Session->server.response.range_flag == RANGE_WITH_RESP_ERROR) && hi_eo_generate_event(Session, HI_EO_SERVER_RANGE_FIELD_ERROR)) { hi_eo_server_event_log(Session, HI_EO_SERVER_RANGE_FIELD_ERROR, NULL, NULL); } if ((vlanId == 0) && !(hsd->resp_state.look_for_partial_content &= GET_REQ_WITH_RANGE) && hi_eo_generate_event(Session, HI_EO_SERVER_NON_RANGE_GET_PARTIAL_METHOD)) { hi_eo_server_event_log(Session, HI_EO_SERVER_NON_RANGE_GET_PARTIAL_METHOD, NULL, NULL); } if (Session->server.response.range_flag == HTTP_RESP_RANGE_NONE) { hsd->resp_state.look_for_partial_content |= CONTENT_NONE; } else if (Session->server.response.range_flag == RANGE_WITH_RESP_FULL_CONTENT) { hsd->resp_state.look_for_partial_content |= FULL_CONTENT; } else { hsd->resp_state.look_for_partial_content |= PARTIAL_CONTENT; } if ((Session->client.request.range_flag == HTTP_RANGE_WITH_FULL_CONTENT_REQ) && ((Session->server.response.range_flag == RANGE_WITH_RESP_UNKNOWN_CONTENT_SIZE) || (Session->server.response.range_flag == RANGE_WITH_UNKNOWN_CONTENT_RANGE) || (Session->server.response.range_flag == RANGE_WITH_RESP_ERROR))) { hsd->resp_state.look_for_partial_content |= FULL_CONTENT; } } #ifdef DUMP_BUFFER dumpBuffer(STAT_CODE_DUMP, Session->server.response.status_code, Session->server.response.status_code_size); #endif } if(Session->server.response.status_msg) { SetHttpBuffer( HTTP_BUFFER_STAT_MSG, Session->server.response.status_msg, Session->server.response.status_msg_size); #ifdef DUMP_BUFFER dumpBuffer(STAT_MSG_DUMP, Session->server.response.status_msg, Session->server.response.status_msg_size); #endif } if(Session->server.response.body_raw_size > 0) { int detect_data_size = (int)Session->server.response.body_size; /*body_size is included in the data_extracted*/ if((Session->server_conf->server_flow_depth > 0) && (hsd->resp_state.data_extracted < (Session->server_conf->server_flow_depth + (int)Session->server.response.body_raw_size))) { /*flow_depth is smaller than data_extracted, need to subtract*/ if(Session->server_conf->server_flow_depth < hsd->resp_state.data_extracted) detect_data_size -= hsd->resp_state.data_extracted - Session->server_conf->server_flow_depth; } else if (Session->server_conf->server_flow_depth) { detect_data_size = 0; } /* Do we have a file decompression object? */ if( hsd->fd_state != 0 ) { fd_status_t Ret_Code; uint16_t Data_Len; const uint8_t *Data; hsd->fd_state->Next_In = (uint8_t *) (Data = Session->server.response.body_raw); hsd->fd_state->Avail_In = (Data_Len = (uint16_t) detect_data_size); (void)File_Decomp_SetBuf( hsd->fd_state ); Ret_Code = File_Decomp( hsd->fd_state ); if( Ret_Code == File_Decomp_DecompError ) { Session->server.response.body = Data; Session->server.response.body_raw = Data; Session->server.response.body_size = Data_Len; Session->server.response.body_raw_size = Data_Len; if(hi_eo_generate_event(Session, hsd->fd_state->Error_Event)) { hi_eo_server_event_log(Session, hsd->fd_state->Error_Event, NULL, NULL); } File_Decomp_StopFree( hsd->fd_state ); hsd->fd_state = NULL; } /* If we didn't find a Sig, then clear the File_Decomp state and don't keep looking. */ else if( Ret_Code == File_Decomp_NoSig ) { File_Decomp_StopFree( hsd->fd_state ); hsd->fd_state = NULL; } else { Session->server.response.body = hsd->fd_state->Buffer; Session->server.response.body_size = (DECODE_BLEN - hsd->fd_state->Avail_Out); Session->server.response.body_raw = Data; Session->server.response.body_raw_size = Data_Len; } setFileDataPtr(Session->server.response.body, (uint16_t)Session->server.response.body_size); #ifdef DUMP_BUFFER dumpBuffer(FILE_DATA_DUMP, Session->server.response.body, (uint16_t)Session->server.response.body_size); #endif } else { setFileDataPtr(Session->server.response.body, (uint16_t)detect_data_size); #ifdef DUMP_BUFFER dumpBuffer(FILE_DATA_DUMP, Session->server.response.body, (uint16_t)detect_data_size); #endif } if (ScPafEnabled() && PacketHasPAFPayload(p) && !(p->packet_flags & PKT_PSEUDO_FLUSH)) { bool decomp_more = (hsd->decomp_state && hsd->decomp_state->stage == HTTP_DECOMP_MID)?true:false; char *pfile_type = NULL; int file_data_position = get_file_current_position(p,decomp_more,is_first); if (file_data_position == SNORT_FILE_POSITION_UNKNOWN && hsd->resp_state.eoh_found) { file_data_position = SNORT_FILE_START; } if (file_api->file_process(p, (uint8_t *)Session->server.response.body_raw, (uint16_t)Session->server.response.body_raw_size, file_data_position, false, false, false)) { setFileName(p); } if (GlobalConf->normalize_nulls) { /* Call File API to get the file type */ pfile_type = file_api->file_get_filetype (p->ssnptr); if (pfile_type) { if (SnortStrcasestr(pfile_type,strlen(pfile_type), "Unknown" ) || SnortStrcasestr(pfile_type,strlen(pfile_type), "RTF" )) { Session->server.response.body_size = NormalizeRandomNulls( (uint8_t*) Session->server.response.body, Session->server.response.body_size, (uint8_t*) Session->server.response.body); } } } } is_first = false; #ifdef DUMP_BUFFER dumpBuffer(RESP_BODY_DUMP, Session->server.response.body_raw, Session->server.response.body_raw_size); #endif } if( IsLimitedDetect(p) && !GetHttpBufferMask() && (p->alt_dsize == 0) ) { DisableDetect( p ); EnablePreprocessor(p, PP_SDF); if(Session->server_conf->server_flow_depth == -1) { EnablePreprocessor(p, PP_DCE2); } return 0; } } /* ** If we get here we either had a client or server request/response. ** We do the detection here, because we're starting a new paradigm ** about protocol decoders. ** ** Protocol decoders are now their own detection engine, since we are ** going to be moving protocol field detection from the generic ** detection engine into the protocol module. This idea scales much ** better than having all these Packet struct field checks in the ** main detection engine for each protocol field. */ PREPROC_PROFILE_START(hiDetectPerfStats); Detect(p); #ifdef PERF_PROFILING hiDetectCalled = 1; #endif PREPROC_PROFILE_END(hiDetectPerfStats); /* ** Handle event stuff after we do detection. ** ** Here's the reason why: ** - since snort can only handle one logged event per packet, ** we only log HttpInspect events if there wasn't one in the ** detection engine. I say that events generated in the ** "advanced generic content matching" engine is more ** important than generic events that I can log here. */ LogEvents(Session, p, iInspectMode, hsd); /* ** We set the global detection flag here so that if request pipelines ** fail, we don't do any detection. */ iCallDetect = 0; #ifdef PPM_MGR /* ** Check PPM here to ensure decompression loop doesn't spin indefinitely */ if( PPM_PKTS_ENABLED() ) { PPM_GET_TIME(); PPM_PACKET_TEST(); if( PPM_PACKET_ABORT_FLAG() ) return 0; } #endif } while(Session->client.request.pipeline_req || (hsd->decomp_state && hsd->decomp_state->stage == HTTP_DECOMP_MID)); if ( iCallDetect == 0 ) { /* Detect called at least once from above pkt processing loop. */ DisableAllDetect( p ); /* dcerpc2 preprocessor may need to look at this for * RPC over HTTP setup */ EnablePreprocessor(p, PP_DCE2); /* sensitive_data preprocessor may look for PII over HTTP */ EnablePreprocessor(p, PP_SDF); } return 0; } int HttpInspectInitializeGlobalConfig(HTTPINSPECT_GLOBAL_CONF *config, char *ErrorString, int iErrStrLen) { int iRet; if (config == NULL) { snprintf(ErrorString, iErrStrLen, "Global configuration is NULL."); return -1; } iRet = hi_ui_config_init_global_conf(config); if (iRet) { snprintf(ErrorString, iErrStrLen, "Error initializing Global Configuration."); return -1; } iRet = hi_client_init(config); if (iRet) { snprintf(ErrorString, iErrStrLen, "Error initializing client module."); return -1; } file_api->set_mime_decode_config_defauts(&(config->decode_conf)); file_api->set_mime_log_config_defauts(&(config->mime_conf)); RegisterGetHttpXffFields(getHttpXffFields); session_api->register_get_http_xff_precedence(getHttpXffPrecedence); return 0; } HttpSessionData * SetNewHttpSessionData(Packet *p, void *data) { HttpSessionData *hsd; if (p->ssnptr == NULL) return NULL; hi_stats.session_count++; hsd = (HttpSessionData *)SnortPreprocAlloc(1, sizeof(HttpSessionData), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION); hi_stats.mem_used += (sizeof(HttpSessionData) + sizeof(DECOMPRESS_STATE) + sizeof(HTTP_LOG_STATE)); init_decode_utf_state(&hsd->utf_state); session_api->set_application_data(p->ssnptr, PP_HTTPINSPECT, hsd, FreeHttpSessionData); hsd->fd_state = (fd_session_p_t)NULL; hsd->resp_state.eoh_found = false; hsd->resp_state.look_for_partial_content = CONTENT_NONE; hsd->resp_state.chunk_len_state = CHUNK_LEN_DEFAULT; return hsd; } void FreeHttpSessionData(void *data) { HttpSessionData *hsd = (HttpSessionData *)data; if (hsd == NULL) return; hi_stats.session_count--; if (hsd->decomp_state != NULL) { inflateEnd(&(hsd->decomp_state->d_stream)); mempool_free(hi_gzip_mempool, hsd->decomp_state->bkt); } if (hsd->log_state != NULL) { mempool_free(http_mempool, hsd->log_state->log_bucket); SnortPreprocFree(hsd->log_state, sizeof(HTTP_LOG_STATE), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION); } while(hsd->tList_start != NULL ) deleteNode_tList(hsd); file_api->free_mime_session(hsd->mime_ssn); if( hsd->fd_state != 0 ) { File_Decomp_StopFree(hsd->fd_state); // Stop & Stop & Free fd session object hsd->fd_state = NULL; // ...just for good measure } hi_stats.mem_used -= (sizeof(HttpSessionData) + sizeof(DECOMPRESS_STATE) + sizeof(HTTP_LOG_STATE)); SnortPreprocFree(hsd, sizeof(HttpSessionData), PP_HTTPINSPECT, PP_MEM_CATEGORY_SESSION); } int GetHttpTrueIP(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { sfaddr_t *true_ip; true_ip = GetTrueIPForSession(data); if(!true_ip) return 0; if(sfaddr_family(true_ip) == AF_INET6) { *type = EVENT_INFO_XFF_IPV6; *len = sizeof(struct in6_addr); /*ipv6 address size in bytes*/ *buf = (uint8_t*)sfaddr_get_ip6_ptr(true_ip); } else { *type = EVENT_INFO_XFF_IPV4; *len = sizeof(struct in_addr); /*ipv4 address size in bytes*/ *buf = (uint8_t*)sfaddr_get_ip4_ptr(true_ip); } return 1; } int IsGzipData(void *data) { HttpSessionData *hsd = NULL; if (data == NULL) return -1; hsd = (HttpSessionData *)session_api->get_application_data(data, PP_HTTPINSPECT); if(hsd == NULL) return -1; if((hsd->log_flags & HTTP_LOG_GZIP_DATA) && (file_data_ptr.len > 0 )) return 0; else return -1; } int GetHttpGzipData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { if(!IsGzipData(data)) { *buf = (uint8_t*)file_data_ptr.data; *len = file_data_ptr.len; *type = EVENT_INFO_GZIP_DATA; return 1; } return 0; } int IsJSNormData(void *data) { HttpSessionData *hsd = NULL; if (data == NULL) return -1; hsd = (HttpSessionData *)session_api->get_application_data(data, PP_HTTPINSPECT); if(hsd == NULL) return -1; if((hsd->log_flags & HTTP_LOG_JSNORM_DATA) && (file_data_ptr.len > 0 )) return 0; else return -1; } int GetHttpJSNormData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { if(!IsJSNormData(data)) { *buf = (uint8_t*) file_data_ptr.data; *len = file_data_ptr.len; *type = EVENT_INFO_JSNORM_DATA; return 1; } return 0; } int GetHttpUriData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { HttpSessionData *hsd = NULL; if (data == NULL) return 0; hsd = (HttpSessionData *)session_api->get_application_data(data, PP_HTTPINSPECT); if(hsd == NULL) return 0; if(hsd->log_state && hsd->log_state->uri_bytes > 0) { *buf = hsd->log_state->uri_extracted; *len = hsd->log_state->uri_bytes; *type = EVENT_INFO_HTTP_URI; return 1; } return 0; } int GetHttpHostnameData(void *data, uint8_t **buf, uint32_t *len, uint32_t *type) { HttpSessionData *hsd = NULL; if (data == NULL) return 0; hsd = (HttpSessionData *)session_api->get_application_data(data, PP_HTTPINSPECT); if(hsd == NULL) return 0; if(hsd->log_state && hsd->log_state->hostname_bytes > 0) { *buf = hsd->log_state->hostname_extracted; *len = hsd->log_state->hostname_bytes; *type = EVENT_INFO_HTTP_HOSTNAME; return 1; } return 0; } void HI_SearchInit(void) { const HiSearchToken *tmp; hi_javascript_search_mpse = search_api->search_instance_new(); if (hi_javascript_search_mpse == NULL) { FatalError("%s(%d) Could not allocate memory for HTTP \end{verbatim} The above javascript will generate the preprocessor alert with SID 9 and GIDF 120 when \texttt{normalize\_javascript} is turned on. Http Inspect will also generate a preprocessor alert with GID 120 and SID 11 when there are more than one type of encodings within the escaped/encoded data. \begin{verbatim} For example: unescape("%48\x65%6C%6C%6F%2C%20%73%6E%6F%72%74%20%74%65%61%6D%21"); String.fromCharCode(0x48, 0x65, 0x6c, 0x6c, 111, 44, 32, 115, 110, 111, 114, 116, 32, 116, 101, 97, 109, 33) \\end{verbatim} The above obfuscation will generate the preprocessor alert with GID 120 and SID 11. This option is turned off by default in HTTP Inspect. \item \texttt{max\_javascript\_whitespaces $<$positive integer up to 65535$>$} This option takes an integer as an argument. The integer determines the maximum number of consecutive whitespaces allowed within the Javascript obfuscated data in a HTTP response body. The config option \texttt{normalize\_javascript} should be turned on before configuring this config option. When the whitespaces in the javascript obfuscated data is equal to or more than this value a preprocessor alert with GID 120 and SID 10 is generated. The default value for this option is 200. To enable, specify an integer argument to \texttt{max\_javascript\_spaces} of 1 to 65535. Specifying a value of 0 is treated as disabling the alert. \item \texttt{enable\_xff} This option enables Snort to parse and log the original client IP present in the X-Forwarded-For or True-Client-IP HTTP request headers along with the generated events. The XFF/True-Client-IP Original client IP address is logged only with unified2 output and is not logged with console (-A cmg) output. \item \texttt{xff\_headers} If/When the \texttt{enable\_xff} option is present, the \texttt{xff\_headers} option specifies a set of custom 'xff' headers. This option allows the definition of up to six custom headers in addition to the two default (and always present) X-Forwarded-For and True-Client-IP headers. The option permits both the custom and default headers to be prioritized. The headers/priority pairs are specified as a list. Lower numerical values imply a higher priority. The headers do not need to be specified in priority order. Nor do the priorities need to be contiguous. Priority values can range from 1 to 255. The priority values and header names must be unique. The header names must not collide with known http headers such as 'host', 'cookie', 'content-length', etc. A example of the \texttt{xff\_header} syntax is: \begin{verbatim} xff_headers { [ x-forwarded-highest-priority 1 ] [ x-forwarded-second-highest-priority 2 ] \ [ x-forwarded-lowest-priority-custom 3 ] } \end{verbatim} The default X-Forwarded-For and True-Client-IP headers are always present. They may be explicitly specified in the \texttt{xff\_headers} config in order to determine their priority. If not specified, they will be automatically added to the xff list as the lowest priority headers. For example, let us say that we have the following (abbreviated) HTTP request header: \begin{verbatim} ... Host: www.snort.org X-Forwarded-For: 192.168.1.1 X-Was-Originally-Forwarded-From: 10.1.1.1 ... \end{verbatim} With the default xff behavior (no \texttt{xff\_headers}), the 'X-Forwarded-For' header would be used to provide a 192.168.1.1 Original Client IP address in the unified2 log. Custom headers are not parsed. With: \begin{verbatim} xff_headers { [ x-was-originally-forwarded-from 1 ] [ x-another-forwarding-header 2 ] \ [ x-forwarded-for 3 ] } \end{verbatim} The X-Was-Originally-Forwarded-From header is the highest priority present and its value of 10.1.1.1 will be logged as the Original Client IP in the unified2 log. But with: \begin{verbatim} xff_headers { [ x-was-originally-forwarded-from 3 ] [ x-another-forwarding-header 2 ] \ [ x-forwarded-for 1 ] } \end{verbatim} Now the X-Forwarded-For header is the highest priority and its value of 192.168.1.1 is logged. \begin{note} The original client IP from XFF/True-Client-IP in unified2 logs can be viewed using the tool u2spewfoo. This tool is present in the tools/u2spewfoo directory of snort source tree. \end{note} \item \texttt{server\_flow\_depth $<$integer$>$} This specifies the amount of server response payload to inspect. When \texttt{extended\_response\_inspection} is turned on, it is applied to the HTTP response body (decompressed data when \texttt{inspect\_gzip} is turned on) and not the HTTP headers. When \texttt{extended\_response\_inspection} is turned off the \texttt{server\_flow\_depth} is applied to the entire HTTP response (including headers). Unlike \texttt{client\_flow\_depth} this option is applied per TCP session. This option can be used to balance the needs of IDS performance and level of inspection of HTTP server response data. Snort rules are targeted at HTTP server response traffic and when used with a small flow\_depth value may cause false negatives. Most of these rules target either the HTTP header, or the content that is likely to be in the first hundred or so bytes of non-header data. Headers are usually under 300 bytes long, but your mileage may vary. It is suggested to set the \texttt{server\_flow\_depth} to its maximum value. This value can be set from -1 to 65535. A value of -1 causes Snort to ignore all server side traffic for ports defined in \texttt{ports} when \texttt{extended\_response\_inspection} is turned off. When the \texttt{extended\_response\_inspection} is turned on, value of -1 causes Snort to ignore the HTTP response body data and not the HTTP headers. Inversely, a value of 0 causes Snort to inspect all HTTP server payloads defined in "ports" (note that this will likely slow down IDS performance). Values above 0 tell Snort the number of bytes to inspect of the server response (excluding the HTTP headers when \texttt{extended\_response\_inspection} is turned on) in a given HTTP session. Only packets payloads starting with 'HTTP' will be considered as the first packet of a server response. If less than flow\_depth bytes are in the payload of the HTTP response packets in a given session, the entire payload will be inspected. If more than flow\_depth bytes are in the payload of the HTTP response packet in a session only flow\_depth bytes of the payload will be inspected for that session. Rules that are meant to inspect data in the payload of the HTTP response packets in a session beyond 65535 bytes will be ineffective unless flow\_depth is set to 0. The default value for \texttt{server\_flow\_depth} is 300. Note that the 65535 byte maximum flow\_depth applies to stream reassembled packets as well. It is suggested to set the \texttt{server\_flow\_depth} to its maximum value. \begin{note} \texttt{server\_flow\_depth} is the same as the old \texttt{flow\_depth} option, which will be deprecated in a future release. \end{note} \item \texttt{client\_flow\_depth $<$integer$>$} This specifies the amount of raw client request payload to inspect. This value can be set from -1 to 1460. Unlike \texttt{server\_flow\_depth} this value is applied to the first packet of the HTTP request. It is not a session based flow depth. It has a default value of 300. It primarily eliminates Snort from inspecting larger HTTP Cookies that appear at the end of many client request Headers. A value of -1 causes Snort to ignore all client side traffic for ports defined in "ports." Inversely, a value of 0 causes Snort to inspect all HTTP client side traffic defined in "ports" (note that this will likely slow down IDS performance). Values above 0 tell Snort the number of bytes to inspect in the first packet of the client request. If less than flow\_depth bytes are in the TCP payload (HTTP request) of the first packet, the entire payload will be inspected. If more than flow\_depth bytes are in the payload of the first packet only flow\_depth bytes of the payload will be inspected. Rules that are meant to inspect data in the payload of the first packet of a client request beyond 1460 bytes will be ineffective unless flow\_depth is set to 0. Note that the 1460 byte maximum flow\_depth applies to stream reassembled packets as well. It is suggested to set the \texttt{client\_flow\_depth} to its maximum value. \item \texttt{post\_depth $<$integer$>$} This specifies the amount of data to inspect in a client post message. The value can be set from -1 to 65495. The default value is -1. A value of -1 causes Snort to ignore all the data in the post message. Inversely, a value of 0 causes Snort to inspect all the client post message. This increases the performance by inspecting only specified bytes in the post message. \item \texttt{ascii $<$yes$|$no$>$} The \texttt{ascii} decode option tells us whether to decode encoded ASCII chars, a.k.a \%2f = /, \%2e = ., etc. It is normal to see ASCII encoding usage in URLs, so it is recommended that you disable HTTP Inspect alerting for this option. \item \texttt{extended\_ascii\_uri} This option enables the support for extended ASCII codes in the HTTP request URI. This option is turned off by default and is not supported with any of the profiles. \item \texttt{utf\_8 $<$yes$|$no$>$} The \texttt{utf-8} decode option tells HTTP Inspect to decode standard UTF-8 Unicode sequences that are in the URI. This abides by the Unicode standard and only uses \% encoding. Apache uses this standard, so for any Apache servers, make sure you have this option turned on. As for alerting, you may be interested in knowing when you have a UTF-8 encoded URI, but this will be prone to false positives as legitimate web clients use this type of encoding. When \texttt{utf\_8} is enabled, ASCII decoding is also enabled to enforce correct functioning. \item \texttt{u\_encode $<$yes$|$no$>$} This option emulates the IIS \%u encoding scheme. How the \%u encoding scheme works is as follows: the encoding scheme is started by a \%u followed by 4 characters, like \%uxxxx. The xxxx is a hex-encoded value that correlates to an IIS Unicode codepoint. This value can most definitely be ASCII. An ASCII character is encoded like \%u002f = /, \%u002e = ., etc. If no iis\_unicode\_map is specified before or after this option, the default codemap is used. You should alert on \%u encodings, because we are not aware of any legitimate clients that use this encoding. So it is most likely someone trying to be covert. \item \texttt{bare\_byte $<$yes$|$no$>$} Bare byte encoding is an IIS trick that uses non-ASCII characters as valid values when decoding UTF-8 values. This is not in the HTTP standard, as all non-ASCII values have to be encoded with a \%. Bare byte encoding allows the user to emulate an IIS server and interpret non-standard encodings correctly. The alert on this decoding should be enabled, because there are no legitimate clients that encode UTF-8 this way since it is non-standard. \item \texttt{iis\_unicode $<$yes$|$no$>$} The \texttt{iis\_unicode} option turns on the Unicode codepoint mapping. If there is no iis\_unicode\_map option specified with the server config, \texttt{iis\_unicode} uses the default codemap. The \texttt{iis\_unicode} option handles the mapping of non-ASCII codepoints that the IIS server accepts and decodes normal UTF-8 requests. You should alert on the \texttt{iis\_unicode option}, because it is seen mainly in attacks and evasion attempts. When \texttt{iis\_unicode} is enabled, ASCII and UTF-8 decoding are also enabled to enforce correct decoding. To alert on UTF-8 decoding, you must enable also enable \texttt{utf\_8 yes}. \item \texttt{double\_decode $<$yes$|$no$>$} The \texttt{double\_decode} option is once again IIS-specific and emulates IIS functionality. How this works is that IIS does two passes through the request URI, doing decodes in each one. In the first pass, it seems that all types of iis encoding is done: utf-8 unicode, ASCII, bare byte, and \%u. In the second pass, the following encodings are done: ASCII, bare byte, and \%u. We leave out utf-8 because I think how this works is that the \% encoded utf-8 is decoded to the Unicode byte in the first pass, and then UTF-8 is decoded in the second stage. Anyway, this is really complex and adds tons of different encodings for one character. When \texttt{double\_decode} is enabled, so ASCII is also enabled to enforce correct decoding. \item \texttt{non\_rfc\_char $\{ <$byte$> [<$byte ...$>] \}$} This option lets users receive an alert if certain non-RFC chars are used in a request URI. For instance, a user may not want to see null bytes in the request URI and we can alert on that. Please use this option with care, because you could configure it to say, alert on all `/' or something like that. It's flexible, so be careful. \item \texttt{multi\_slash $<$yes$|$no$>$} This option normalizes multiple slashes in a row, so something like: ``foo/////////bar'' get normalized to ``foo/bar.'' If you want an alert when multiple slashes are seen, then configure with a \texttt{yes}; otherwise, use \texttt{no}. \item \texttt{iis\_backslash $<$yes$|$no$>$} Normalizes backslashes to slashes. This is again an IIS emulation. So a request URI of ``/foo$\backslash$bar'' gets normalized to ``/foo/bar.'' \item \texttt{directory $<$yes$|$no$>$} This option normalizes directory traversals and self-referential directories. The directory: \begin{verbatim} /foo/fake\_dir/../bar \end{verbatim} gets normalized to: \begin{verbatim} /foo/bar \end{verbatim} The directory: \begin{verbatim} /foo/./bar \end{verbatim} gets normalized to: \begin{verbatim} /foo/bar \end{verbatim} If you want to configure an alert, specify \texttt{yes}, otherwise, specify \texttt{no}. This alert may give false positives, since some web sites refer to files using directory traversals. \item \texttt{apache\_whitespace $<$yes$|$no$>$} This option deals with the non-RFC standard of using tab for a space delimiter. Apache uses this, so if the emulated web server is Apache, enable this option. Alerts on this option may be interesting, but may also be false positive prone. \item \texttt{iis\_delimiter $<$yes$|$no$>$} This started out being IIS-specific, but Apache takes this non-standard delimiter was well. Since this is common, we always take this as standard since the most popular web servers accept it. But you can still get an alert on this option. \item \texttt{chunk\_length $<$non-zero positive integer$>$} This option is an anomaly detector for abnormally large chunk sizes. This picks up the Apache chunk encoding exploits, and may also alert on HTTP tunneling that uses chunk encoding. \item \texttt{small\_chunk\_length \{ $<$chunk size$>$ $<$consecutive chunks$>$ \} } This option is an evasion detector for consecutive small chunk sizes when either the client or server use \texttt{Transfer-Encoding: chunked}. $<$chunk size$>$ specifies the maximum chunk size for which a chunk will be considered small. $<$consecutive chunks$>$ specifies the number of consecutive small chunks $<$= $<$chunk size$>$ before an event will be generated. This option is turned off by default. Maximum values for each are 255 and a $<$chunk size$>$ of 0 disables. Events generated are gid:119, sid:26 for client small chunks and gid:120, sid:7 for server small chunks. Example: \begin{verbatim} small_chunk_length { 10 5 } \end{verbatim} Meaning alert if we see 5 consecutive chunk sizes of 10 or less. \item \texttt{no\_pipeline\_req} This option turns HTTP pipeline decoding off, and is a performance enhancement if needed. By default, pipeline requests are inspected for attacks, but when this option is enabled, pipeline requests are not decoded and analyzed per HTTP protocol field. It is only inspected with the generic pattern matching. \item \texttt{non\_strict} This option turns on non-strict URI parsing for the broken way in which Apache servers will decode a URI. Only use this option on servers that will accept URIs like this: "get /index.html alsjdfk alsj lj aj la jsj s$\backslash$n". The non\_strict option assumes the URI is between the first and second space even if there is no valid HTTP identifier after the second space. \item \texttt{allow\_proxy\_use} By specifying this keyword, the user is allowing proxy use on this server. This means that no alert will be generated if the \texttt{proxy\_alert} global keyword has been used. If the proxy\_alert keyword is not enabled, then this option does nothing. The \texttt{allow\_proxy\_use} keyword is just a way to suppress unauthorized proxy use for an authorized server. \item \texttt{no\_alerts} This option turns off all alerts that are generated by the HTTP Inspect preprocessor module. This has no effect on HTTP rules in the rule set. No argument is specified. \item \texttt{oversize\_dir\_length $<$non-zero positive integer$>$} This option takes a non-zero positive integer as an argument. The argument specifies the max char directory length for URL directory. If a url directory is larger than this argument size, an alert is generated. A good argument value is 300 characters. This should limit the alerts to IDS evasion type attacks, like whisker -i 4. \item \texttt{inspect\_uri\_only} This is a performance optimization. When enabled, only the URI portion of HTTP requests will be inspected for attacks. As this field usually contains 90-95\% of the web attacks, you'll catch most of the attacks. So if you need extra performance, enable this optimization. It's important to note that if this option is used without any \texttt{uricontent} rules, then no inspection will take place. This is obvious since the URI is only inspected with \texttt{uricontent} rules, and if there are none available, then there is nothing to inspect. For example, if we have the following rule set: \begin{verbatim} alert tcp any any -> any 80 ( msg:"content"; content: "foo"; ) \end{verbatim} and the we inspect the following URI: \begin{verbatim} get /foo.htm http/1.0\r\n\r\n \end{verbatim} No alert will be generated when \texttt{inspect\_uri\_only} is enabled. The \texttt{inspect\_uri\_only} configuration turns off all forms of detection except \texttt{uricontent} inspection. \item \texttt{max\_header\_length $<$positive integer up to 65535$>$} This option takes an integer as an argument. The integer is the maximum length allowed for an HTTP client request header field. Requests that exceed this length will cause a "Long Header" alert. This alert is off by default. To enable, specify an integer argument to max\_header\_length of 1 to 65535. Specifying a value of 0 is treated as disabling the alert. \item \texttt{max\_spaces $<$positive integer up to 65535$>$} This option takes an integer as an argument. The integer determines the maximum number of whitespaces allowed with HTTP client request line folding. Requests headers folded with whitespaces equal to or more than this value will cause a "Space Saturation" alert with SID 26 and GID 119. The default value for this option is 200. To enable, specify an integer argument to \texttt{max\_spaces} of 1 to 65535. Specifying a value of 0 is treated as disabling the alert. \item \texttt{webroot $<$yes$|$no$>$} This option generates an alert when a directory traversal traverses past the web server root directory. This generates much fewer false positives than the directory option, because it doesn't alert on directory traversals that stay within the web server directory structure. It only alerts when the directory traversals go past the web server root directory, which is associated with certain web attacks. \item \texttt{tab\_uri\_delimiter} This option turns on the use of the tab character (0x09) as a delimiter for a URI. Apache accepts tab as a delimiter; IIS does not. For IIS, a tab in the URI should be treated as any other character. Whether this option is on or not, a tab is treated as whitespace if a space character (0x20) precedes it. No argument is specified. \item \texttt{normalize\_headers} This option turns on normalization for HTTP Header Fields, not including Cookies (using the same configuration parameters as the URI normalization (i.e., multi-slash, directory, etc.). It is useful for normalizing Referrer URIs that may appear in the HTTP Header. \item \texttt{normalize\_cookies} This option turns on normalization for HTTP Cookie Fields (using the same configuration parameters as the URI normalization (i.e., multi-slash, directory, etc.). It is useful for normalizing data in HTTP Cookies that may be encoded. \item \texttt{normalize\_utf} This option turns on normalization of HTTP response bodies where the Content-Type header lists the character set as "utf-16le", "utf-16be", "utf-32le", or "utf-32be". HTTP Inspect will attempt to normalize these back into 8-bit encoding, generating an alert if the extra bytes are non-zero. \item \texttt{max\_headers $<$positive integer up to 1024$>$} This option takes an integer as an argument. The integer is the maximum number of HTTP client request header fields. Requests that contain more HTTP Headers than this value will cause a "Max Header" alert. The alert is off by default. To enable, specify an integer argument to max\_headers of 1 to 1024. Specifying a value of 0 is treated as disabling the alert. \item \texttt{http\_methods $\{ cmd [cmd] \}$ } This specifies additional HTTP Request Methods outside of those checked by default within the preprocessor (GET and POST). The list should be enclosed within braces and delimited by spaces, tabs, line feed or carriage return. The config option, braces and methods also needs to be separated by braces. \begin{verbatim} http_methods { PUT CONNECT } \end{verbatim} \begin{note} Please note the maximum length for a method name is 256. \end{note} \item \texttt{log\_uri} This option enables HTTP Inspect preprocessor to parse the URI data from the HTTP request and log it along with all the generated events for that session. Stream reassembly needs to be turned on HTTP ports to enable the logging. If there are multiple HTTP requests in the session, the URI data of the most recent HTTP request during the alert will be logged. The maximum URI logged is 2048. \begin{note} Please note, this is logged only with the unified2 output and is not logged with console output (-A cmg). \texttt{u2spewfoo} can be used to read this data from the unified2. \end{note} \item \texttt{log\_hostname} This option enables HTTP Inspect preprocessor to parse the hostname data from the "Host" header of the HTTP request and log it along with all the generated events for that session. Stream reassembly needs to be turned on HTTP ports to enable the logging. If there are multiple HTTP requests in the session, the Hostname data of the most recent HTTP request during the alert will be logged. In case of multiple "Host" headers within one HTTP request, a preprocessor alert with sid 24 is generated. The maximum hostname length logged is 256. \begin{note} Please note, this is logged only with the unified2 output and is not logged with console output (-A cmg). \texttt{u2spewfoo} can be used to read this data from the unified2. \end{note} \begin{verbatim} ########################################## # HTTP2 SUPPORT IS STILL EXPERIMENTAL! # DO NOT USE IN PRODUCTION ENVIRONMENTS. # Please send any issues to the Snort team ########################################## \end{verbatim} \item \texttt{legacy\_mode} By default, HTTP2 traffic is not supported. You can use "legacy\_mode no" to enable HTTP2 support. If http legacy mode is configured, HTTP2 inspection is disabled. \end{slist} \subsubsection{Examples} \begin{verbatim} preprocessor http_inspect_server: \ server 10.1.1.1 \ ports { 80 3128 8080 } \ server_flow_depth 0 \ ascii no \ double_decode yes \ non_rfc_char { 0x00 } \ chunk_length 500000 \ non_strict \ no_alerts preprocessor http_inspect_server: \ server default \ ports { 80 3128 } \ non_strict \ non_rfc_char { 0x00 } \ server_flow_depth 300 \ apache_whitespace yes \ directory no \ iis_backslash no \ u_encode yes \ ascii no \ chunk_length 500000 \ bare_byte yes \ double_decode yes \ iis_unicode yes \ iis_delimiter yes \ multi_slash no preprocessor http_inspect_server: \ server default \ profile all \ ports { 80 8080 } \end{verbatim} \subsection{SMTP Preprocessor} \label{SMTP} The SMTP preprocessor is an SMTP decoder for user applications. Given a data buffer, SMTP will decode the buffer and find SMTP commands and responses. It will also mark the command, data header data body sections, and TLS data. SMTP handles stateless and stateful processing. It saves state between individual packets. However maintaining correct state is dependent on the reassembly of the client side of the stream (i.e., a loss of coherent stream data results in a loss of state). \subsubsection{Configuration} SMTP has the usual configuration items, such as \texttt{port} and \texttt{inspection\_type}. Also, SMTP command lines can be normalized to remove extraneous spaces. TLS-encrypted traffic can be ignored, which improves performance. In addition, regular mail data can be ignored for an additional performance boost. Since so few (none in the current snort rule set) exploits are against mail data, this is relatively safe to do and can improve the performance of data inspection. The configuration options are described below: \begin{slist} \item \texttt{ports \{ [] ... \}} This specifies on what ports to check for SMTP data. Typically, this will include 25 and possibly 465, for encrypted SMTP. \item \texttt{inspection\_type } Indicate whether to operate in stateful or stateless mode. \item \texttt{normalize } This turns on normalization. Normalization checks for more than one space character after a command. Space characters are defined as space (ASCII 0x20) or tab (ASCII 0x09). \texttt{all} checks all commands \texttt{none} turns off normalization for all commands. \texttt{cmds} just checks commands listed with the \texttt{normalize\_cmds} parameter. \item \texttt{ignore\_data} Ignore data section of mail (except for mail headers) when processing rules. \item \texttt{ignore\_tls\_data} Ignore TLS-encrypted data when processing rules. \item \texttt{max\_command\_line\_len } Alert if an SMTP command line is longer than this value. Absence of this option or a "0" means never alert on command line length. RFC 2821 recommends 512 as a maximum command line length. \item \texttt{max\_header\_line\_len } Alert if an SMTP DATA header line is longer than this value. Absence of this option or a "0" means never alert on data header line length. RFC 2821 recommends 1024 as a maximum data header line length. \item \texttt{max\_response\_line\_len } Alert if an SMTP response line is longer than this value. Absence of this option or a "0" means never alert on response line length. RFC 2821 recommends 512 as a maximum response line length. \item \texttt{alt\_max\_command\_line\_len \{ [] \}} Overrides \texttt{max\_command\_line\_len} for specific commands. \item \texttt{no\_alerts} Turn off all alerts for this preprocessor. \item \texttt{invalid\_cmds \{ \}} Alert if this command is sent from client side. Default is an empty list. \item \texttt{valid\_cmds \{ \}} List of valid commands. We do not alert on commands in this list. Default is an empty list, but preprocessor has this list hard-coded: \begin{itemize} \item[] \{ ATRN AUTH BDAT DATA DEBUG EHLO EMAL ESAM ESND ESOM ETRN EVFY EXPN HELO HELP IDENT MAIL NOOP QUIT RCPT RSET SAML SOML SEND ONEX QUEU STARTTLS TICK TIME TURN TURNME VERB VRFY X-EXPS X-LINK2STATE XADR XAUTH XCIR XEXCH50 XGEN XLICENSE XQUE XSTA XTRN XUSR \} \end{itemize} \item \texttt{data\_cmds \{ \}} List of commands that initiate sending of data with an end of data delimiter the same as that of the DATA command per RFC 5321 - \texttt{"."}. Default is \{ DATA \}. \item \texttt{binary\_data\_cmds \{ \}} List of commands that initiate sending of data and use a length value after the command to indicate the amount of data to be sent, similar to that of the BDAT command per RFC 3030. Default is \{ BDAT XEXCH50 \}. \item \texttt{auth\_cmds \{ \}} List of commands that initiate an authentication exchange between client and server. Default is \{ AUTH XAUTH X-EXPS \}. \item \texttt{alert\_unknown\_cmds} Alert if we don't recognize command. Default is off. \item \texttt{normalize\_cmds \{ \}} Normalize this list of commands Default is \{ RCPT VRFY EXPN \}. \item \texttt{xlink2state \{ enable | disable [drop] \}} Enable/disable xlink2state alert. Drop if alerted. Default is \texttt{enable}. \item \texttt{print\_cmds} List all commands understood by the preprocessor. This not normally printed out with the configuration because it can print so much data. \item \texttt{disabled} Disables the SMTP preprocessor in a config. This is useful when specifying the decoding depths such as \texttt{b64\_decode\_depth}, \texttt{qp\_decode\_depth}, \texttt{uu\_decode\_depth}, \texttt{bitenc\_decode\_depth} or the memcap used for decoding \texttt{max\_mime\_mem} in default config without turning on the SMTP preprocessor. \item \texttt{b64\_decode\_depth} This config option is used to turn off/on or set the base64 decoding depth used to decode the base64 encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the base64 decoding of MIME attachments. The value of 0 sets the decoding of base64 encoded MIME attachments to unlimited. A value other than 0 or -1 restricts the decoding of base64 MIME attachments, and applies per attachment. A SMTP preprocessor alert with sid 10 is generated (if enabled) when the decoding fails. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the base64 encoded MIME attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. This option replaces the deprecated options, \texttt{enable\_mime\_decoding} and \texttt{max\_mime\_depth}. It is recommended that user inputs a value that is a multiple of 4. When the value specified is not a multiple of 4, the SMTP preprocessor will round it up to the next multiple of 4. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{qp\_decode\_depth} This config option is used to turn off/on or set the Quoted-Printable decoding depth used to decode the Quoted-Printable(QP) encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the QP decoding of MIME attachments. The value of 0 sets the decoding of QP encoded MIME attachments to unlimited. A value other than 0 or -1 restricts the decoding of QP MIME attachments, and applies per attachment. A SMTP preprocessor alert with sid 11 is generated (if enabled) when the decoding fails. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the QP encoded MIME attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{bitenc\_decode\_depth} This config option is used to turn off/on or set the non-encoded MIME extraction depth used to extract the non-encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the extraction of these MIME attachments. The value of 0 sets the extraction of these MIME attachments to unlimited. A value other than 0 or -1 restricts the extraction of these MIME attachments, and applies per attachment. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the non-encoded MIME attachments/data across multiple packets are extracted too. The extracted data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{uu\_decode\_depth} This config option is used to turn off/on or set the Unix-to-Unix decoding depth used to decode the Unix-to-Unix(UU) encoded attachments. The value ranges from -1 to 65535. A value of -1 turns off the UU decoding of SMTP attachments. The value of 0 sets the decoding of UU encoded SMTP attachments to unlimited. A value other than 0 or -1 restricts the decoding of UU SMTP attachments, and applies per attachment. A SMTP preprocessor alert with sid 13 is generated (if enabled) when the decoding fails. Multiple UU attachments/data in one packet are pipelined. When stateful inspection is turned on the UU encoded SMTP attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{enable\_mime\_decoding} Enables Base64 decoding of Mime attachments/data. Multiple base64 encoded MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the base64 encoded MIME attachments/data across multiple packets are decoded too. The decoding of base64 encoded attachments/data ends when either the \texttt{max\_mime\_depth} or maximum MIME sessions (calculated using \texttt{max\_mime\_depth} and \texttt{max\_mime\_mem}) is reached or when the encoded data ends. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. This option is deprecated. Use the option \texttt{b64\_decode\_depth} to turn off or on the base64 decoding instead. \item \texttt{max\_mime\_depth } Specifies the maximum number of base64 encoded data to decode per SMTP attachment. The option take values ranging from 4 to 20480 bytes. The default value for this in snort in 1460 bytes. It is recommended that user inputs a value that is a multiple of 4. When the value specified is not a multiple of 4, the SMTP preprocessor will round it up to the next multiple of 4. This option is deprecated. Use the option \texttt{b64\_decode\_depth} to turn off or on the base64 decoding instead. \item \texttt{max\_mime\_mem } This option determines (in bytes) the maximum amount of memory the SMTP preprocessor will use for decoding base64 encoded/quoted-printable/non-encoded MIME attachments/data or Unix-to-Unix encoded attachments. This value can be set from 3276 bytes to 100MB. This option along with the maximum of the decoding depths will determine the SMTP sessions that will be decoded at any given instant. The default value for this option is 838860. Note: It is suggested to set this value such that the max smtp session calculated as follows is at least 1. max smtp session = \texttt{max\_mime\_mem} /(2 * max of (\texttt{b64\_decode\_depth}, \texttt{uu\_decode\_depth}, \texttt{qp\_decode\_depth} or \texttt{bitenc\_decode\_depth})) For example, if \texttt{b64\_decode\_depth} is 0 (indicates unlimited decoding) and \texttt{qp\_decode\_depth} is 100, then max smtp session = \texttt{max\_mime\_mem}/2*65535 (max value for \texttt{b64\_decode\_depth}) In case of multiple configs, the \texttt{max\_mime\_mem} of the non-default configs will be overwritten by the default config's value. Hence user needs to define it in the default config with the new keyword disabled (used to disable SMTP preprocessor in a config). \item \texttt{log\_mailfrom} This option enables SMTP preprocessor to parse and log the sender's email address extracted from the "MAIL FROM" command along with all the generated events for that session. The maximum number of bytes logged for this option is 1024. Please note, this is logged only with the unified2 output and is not logged with console output (-A cmg). u2spewfoo can be used to read this data from the unified2. \item \texttt{log\_rcptto} This option enables SMTP preprocessor to parse and log the recipient's email addresses extracted from the "RCPT TO" command along with all the generated events for that session. Multiple recipients are appended with commas. The maximum number of bytes logged for this option is 1024. Please note, this is logged only with the unified2 output and is not logged with console output (-A cmg). u2spewfoo can be used to read this data from the unified2. \item \texttt{log\_filename} This option enables SMTP preprocessor to parse and log the MIME attachment filenames extracted from the Content-Disposition header within the MIME body along with all the generated events for that session. Multiple filenames are appended with commas. The maximum number of bytes logged for this option is 1024. Please note, this is logged only with the unified2 output and is not logged with the console output (-A cmg). u2spewfoo can be used to read this data from the unified2. \item \texttt{log\_email\_hdrs} This option enables SMTP preprocessor to parse and log the SMTP email headers extracted from SMTP data along with all generated events for that session. The number of bytes extracted and logged depends upon the \texttt{email\_hdrs\_log\_depth}. Please note, this is logged only with the unified2 output and is not logged with the console output (-A cmg). u2spewfoo can be used to read this data from the unified2. \item \texttt{email\_hdrs\_log\_depth } This option specifies the depth for logging email headers. The allowed range for this option is 0 - 20480. A value of 0 will disable email headers logging. The default value for this option is 1464. Please note, in case of multiple policies, the value specified in the default policy is used and the values specified in the targeted policies are overwritten by the default value. This option must be configured in the default policy even if the SMTP configuration is disabled. \item \texttt{memcap } This option determines in bytes the maximum amount of memory the SMTP preprocessor will use for logging of filename, MAIL FROM addresses, RCPT TO addresses and email headers. This value along with the buffer size used to log MAIL FROM, RCPT TO, filenames and \texttt{email\_hdrs\_log\_depth} will determine the maximum SMTP sessions that will log the email headers at any given time. When this memcap is reached SMTP will stop logging the filename, MAIL FROM address, RCPT TO addresses and email headers until memory becomes available. Max SMTP sessions logging email headers at any given time = memcap/(1024 + 1024 + 1024 + \texttt{email\_hdrs\_log\_depth}) The size 1024 is the maximum buffer size used for logging filename, RCPTTO and MAIL FROM addresses. Default value for this option is 838860. The allowed range for this option is 3276 to 104857600. The value specified in the default config is used when this option is specified in multiple configs. This option must be configured in the default config even if the SMTP configuration is disabled. Please note, in case of multiple policies, the value specified in the default policy is used and the values specified in the targeted policies are overwritten by the default value. This option must be configured in the default policy even if the SMTP configuration is disabled. \end{slist} \subsubsection{Example} \begin{verbatim} preprocessor SMTP: \ ports { 25 } \ inspection_type stateful \ normalize cmds \ normalize_cmds { EXPN VRFY RCPT } \ ignore_data \ ignore_tls_data \ max_command_line_len 512 \ max_header_line_len 1024 \ max_response_line_len 512 \ no_alerts \ alt_max_command_line_len 300 { RCPT } \ invalid_cmds { } \ valid_cmds { } \ xlink2state { disable } \ print_cmds \ log_filename \ log_email_hdrs \ log_mailfrom \ log_rcptto \ email_hdrs_log_depth 2920 \ memcap 6000 preprocessor SMTP: \ b64_decode_depth 0\ max_mime_mem 4000 \ memcap 6000 \ email_hdrs_log_depth 2920 \ disabled \end{verbatim} \subsubsection{Default} \begin{verbatim} preprocessor SMTP: \ ports { 25 } \ inspection_type stateful \ normalize cmds \ normalize_cmds { EXPN VRFY RCPT } \ alt_max_command_line_len 260 { MAIL } \ alt_max_command_line_len 300 { RCPT } \ alt_max_command_line_len 500 { HELP HELO ETRN } \ alt_max_command_line_len 255 { EXPN VRFY } \end{verbatim} \subsubsection{Note} \texttt{RCPT TO:} and \texttt{MAIL FROM:} are SMTP commands. For the preprocessor configuration, they are referred to as RCPT and MAIL, respectively. Within the code, the preprocessor actually maps RCPT and MAIL to the correct command name. \subsection{POP Preprocessor} \label{POP} POP is an POP3 decoder for user applications. Given a data buffer, POP will decode the buffer and find POP3 commands and responses. It will also mark the command, data header data body sections and extract the POP3 attachments and decode it appropriately. POP will handle stateful processing. It saves state between individual packets. However maintaining correct state is dependent on the reassembly of the server side of the stream (i.e., a loss of coherent stream data results in a loss of state). Stream should be turned on for POP. Please ensure that the POP ports are added to the stream5 ports for proper reassembly. The POP preprocessor uses GID 142 to register events. \subsubsection{Configuration} The configuration options are described below: \begin{slist} \item \texttt{ports \{ [] ... \}} This specifies on what ports to check for POP data. Typically, this will include 110. Default ports if none are specified are 110 . \item \texttt{disabled} Disables the POP preprocessor in a config. This is useful when specifying the decoding depths such as \texttt{b64\_decode\_depth}, \texttt{qp\_decode\_depth}, \texttt{uu\_decode\_depth}, \texttt{bitenc\_decode\_depth} or the memcap used for decoding \texttt{memcap} in default config without turning on the POP preprocessor. \item \texttt{b64\_decode\_depth} This config option is used to turn off/on or set the base64 decoding depth used to decode the base64 encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the base64 decoding of MIME attachments. The value of 0 sets the decoding of base64 encoded MIME attachments to unlimited. A value other than 0 or -1 restricts the decoding of base64 MIME attachments, and applies per attachment. A POP preprocessor alert with sid 4 is generated (if enabled) when the decoding fails. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the base64 encoded MIME attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. It is recommended that user inputs a value that is a multiple of 4. When the value specified is not a multiple of 4, the POP preprocessor will round it up to the next multiple of 4. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{qp\_decode\_depth} This config option is used to turn off/on or set the Quoted-Printable decoding depth used to decode the Quoted-Printable(QP) encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the QP decoding of MIME attachments. The value of 0 sets the decoding of QP encoded MIME attachments to unlimited. A value other than 0 or -1 restricts the decoding of QP MIME attachments, and applies per attachment. A POP preprocessor alert with sid 5 is generated (if enabled) when the decoding fails. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the QP encoded MIME attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{bitenc\_decode\_depth} This config option is used to turn off/on or set the non-encoded MIME extraction depth used to extract the non-encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the extraction of these MIME attachments. The value of 0 sets the extraction of these MIME attachments to unlimited. A value other than 0 or -1 restricts the extraction of these MIME attachments, and applies per attachment. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the non-encoded MIME attachments/data across multiple packets are extracted too. The extracted data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{uu\_decode\_depth} This config option is used to turn off/on or set the Unix-to-Unix decoding depth used to decode the Unix-to-Unix(UU) encoded attachments. The value ranges from -1 to 65535. A value of -1 turns off the UU decoding of POP attachments. The value of 0 sets the decoding of UU encoded POP attachments to unlimited. A value other than 0 or -1 restricts the decoding of UU POP attachments, and applies per attachment. A POP preprocessor alert with sid 7 is generated (if enabled) when the decoding fails. Multiple UU attachments/data in one packet are pipelined. When stateful inspection is turned on the UU encoded POP attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{memcap } This option determines (in bytes) the maximum amount of memory the POP preprocessor will use for decoding base64 encoded/quoted-printable/non-encoded MIME attachments/data or Unix-to-Unix encoded attachments. This value can be set from 3276 bytes to 100MB. This option along with the maximum of the decoding depths will determine the POP sessions that will be decoded at any given instant. The default value for this option is 838860. Note: It is suggested to set this value such that the max pop session calculated as follows is at least 1. max pop session = \texttt{memcap} /(2 * max of (\texttt{b64\_decode\_depth}, \texttt{uu\_decode\_depth}, \texttt{qp\_decode\_depth} or \texttt{bitenc\_decode\_depth})) For example, if \texttt{b64\_decode\_depth} is 0 (indicates unlimited decoding) and \texttt{qp\_decode\_depth} is 100, then max pop session = \texttt{memcap}/2*65535 (max value for \texttt{b64\_decode\_depth}) In case of multiple configs, the \texttt{memcap} of the non-default configs will be overwritten by the default config's value. Hence user needs to define it in the default config with the new keyword disabled (used to disable POP preprocessor in a config). When the memcap for decoding (\texttt{memcap}) is exceeded the POP preprocessor alert with sid 3 is generated (when enabled). \end{slist} \subsubsection{Example} \begin{verbatim} preprocessor pop: \ ports { 110 } \ memcap 1310700 \ qp_decode_depth -1 \ b64_decode_depth 0 \ bitenc_decode_depth 100 preprocessor pop: \ memcap 1310700 \ qp_decode_depth 0 \ disabled \end{verbatim} \subsubsection{Default} \begin{verbatim} preprocessor pop: \ ports { 110 } \ b64_decode_depth 1460 \ qp_decode_depth 1460 \ bitenc_decode_depth 1460 \ uu_decode_depth 1460 \end{verbatim} \subsection{IMAP Preprocessor} \label{IMAP} IMAP is an IMAP4 decoder for user applications. Given a data buffer, IMAP will decode the buffer and find IMAP4 commands and responses. It will also mark the command, data header data body sections and extract the IMAP4 attachments and decode it appropriately. IMAP will handle stateful processing. It saves state between individual packets. However maintaining correct state is dependent on the reassembly of the server side of the stream (i.e., a loss of coherent stream data results in a loss of state). Stream should be turned on for IMAP. Please ensure that the IMAP ports are added to the stream5 ports for proper reassembly. The IMAP preprocessor uses GID 141 to register events. \subsubsection{Configuration} The configuration options are described below: \begin{slist} \item \texttt{ports \{ [] ... \}} This specifies on what ports to check for IMAP data. Typically, this will include 143. Default ports if none are specified are 143 . \item \texttt{disabled} Disables the IMAP preprocessor in a config. This is useful when specifying the decoding depths such as \texttt{b64\_decode\_depth}, \texttt{qp\_decode\_depth}, \texttt{uu\_decode\_depth}, \texttt{bitenc\_decode\_depth} or the memcap used for decoding \texttt{memcap} in default config without turning on the IMAP preprocessor. \item \texttt{b64\_decode\_depth} This config option is used to turn off/on or set the base64 decoding depth used to decode the base64 encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the base64 decoding of MIME attachments. The value of 0 sets the decoding of base64 encoded MIME attachments to unlimited. A value other than 0 or -1 restricts the decoding of base64 MIME attachments, and applies per attachment. A IMAP preprocessor alert with sid 4 is generated (if enabled) when the decoding fails. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the base64 encoded MIME attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. It is recommended that user inputs a value that is a multiple of 4. When the value specified is not a multiple of 4, the IMAP preprocessor will round it up to the next multiple of 4. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{qp\_decode\_depth} This config option is used to turn off/on or set the Quoted-Printable decoding depth used to decode the Quoted-Printable(QP) encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the QP decoding of MIME attachments. The value of 0 sets the decoding of QP encoded MIME attachments to unlimited. A value other than 0 or -1 restricts the decoding of QP MIME attachments, and applies per attachment. A IMAP preprocessor alert with sid 5 is generated (if enabled) when the decoding fails. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the QP encoded MIME attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{bitenc\_decode\_depth} This config option is used to turn off/on or set the non-encoded MIME extraction depth used to extract the non-encoded MIME attachments. The value ranges from -1 to 65535. A value of -1 turns off the extraction of these MIME attachments. The value of 0 sets the extraction of these MIME attachments to unlimited. A value other than 0 or -1 restricts the extraction of these MIME attachments, and applies per attachment. Multiple MIME attachments/data in one packet are pipelined. When stateful inspection is turned on the non-encoded MIME attachments/data across multiple packets are extracted too. The extracted data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{uu\_decode\_depth} This config option is used to turn off/on or set the Unix-to-Unix decoding depth used to decode the Unix-to-Unix(UU) encoded attachments. The value ranges from -1 to 65535. A value of -1 turns off the UU decoding of IMAP attachments. The value of 0 sets the decoding of UU encoded IMAP attachments to unlimited. A value other than 0 or -1 restricts the decoding of UU IMAP attachments, and applies per attachment. A IMAP preprocessor alert with sid 7 is generated (if enabled) when the decoding fails. Multiple UU attachments/data in one packet are pipelined. When stateful inspection is turned on the UU encoded IMAP attachments/data across multiple packets are decoded too. The decoded data is available for detection using the rule option \texttt{file\_data}. See \ref{sub:file_data} rule option for more details. In case of multiple configs, the value specified in the non-default config cannot exceed the value specified in the default config. \item \texttt{memcap } This option determines (in bytes) the maximum amount of memory the IMAP preprocessor will use for decoding base64 encoded/quoted-printable/non-encoded MIME attachments/data or Unix-to-Unix encoded attachments. This value can be set from 3276 bytes to 100MB. This option along with the maximum of the decoding depths will determine the IMAP sessions that will be decoded at any given instant. The default value for this option is 838860. Note: It is suggested to set this value such that the max imap session calculated as follows is at least 1. max imap session = \texttt{memcap} /(2 * max of (\texttt{b64\_decode\_depth}, \texttt{uu\_decode\_depth}, \texttt{qp\_decode\_depth} or \texttt{bitenc\_decode\_depth})) For example, if \texttt{b64\_decode\_depth} is 0 (indicates unlimited decoding) and \texttt{qp\_decode\_depth} is 100, then max imap session = \texttt{memcap}/2*65535 (max value for \texttt{b64\_decode\_depth}) In case of multiple configs, the \texttt{memcap} of the non-default configs will be overwritten by the default config's value. Hence user needs to define it in the default config with the new keyword disabled (used to disable IMAP preprocessor in a config). When the memcap for decoding (\texttt{memcap}) is exceeded the IMAP preprocessor alert with sid 3 is generated (when enabled). \end{slist} \subsubsection{Example} \begin{verbatim} preprocessor imap: \ ports { 110 } \ memcap 1310700 \ qp_decode_depth -1 \ b64_decode_depth 0 \ bitenc_decode_depth 100 preprocessor imap: \ memcap 1310700 \ qp_decode_depth 0 \ disabled \end{verbatim} \subsubsection{Default} \begin{verbatim} preprocessor imap: \ ports { 110 } \ b64_decode_depth 1460 \ qp_decode_depth 1460 \ bitenc_decode_depth 1460 \ uu_decode_depth 1460 \end{verbatim} \subsection{FTP/Telnet Preprocessor} \label{sub:ftptelnet} FTP/Telnet is an improvement to the Telnet decoder and provides stateful inspection capability for both FTP and Telnet data streams. FTP/Telnet will decode the stream, identifying FTP commands and responses and Telnet escape sequences and normalize the fields. FTP/Telnet works on both client requests and server responses. FTP/Telnet has the capability to handle stateless processing, meaning it only looks for information on a packet-by-packet basis. The default is to run FTP/Telnet in stateful inspection mode, meaning it looks for information and handles reassembled data correctly. FTP/Telnet has a very ``rich'' user configuration, similar to that of HTTP Inspect (See \ref{sub:http-inspect}). Users can configure individual FTP servers and clients with a variety of options, which should allow the user to emulate any type of FTP server or FTP Client. Within FTP/Telnet, there are four areas of configuration: Global, Telnet, FTP Client, and FTP Server. \begin{note} Some configuration options have an argument of \texttt{yes} or \texttt{no}. This argument specifies whether the user wants the configuration option to generate a ftptelnet alert or not. The presence of the option indicates the option itself is on, while the \texttt{yes/no} argument applies to the alerting functionality associated with that option. \end{note} \subsubsection{Global Configuration} The global configuration deals with configuration options that determine the global functioning of FTP/Telnet. The following example gives the generic global configuration format: \subsubsection{Format} \begin{verbatim} preprocessor ftp_telnet: \ global \ inspection_type stateful \ encrypted_traffic yes \ check_encrypted \end{verbatim} You can only have a single global configuration, you'll get an error if you try otherwise. The FTP/Telnet global configuration must appear before the other three areas of configuration. \paragraph{Configuration} \begin{slist} \item \texttt{inspection\_type} This indicates whether to operate in stateful or stateless mode. \item \texttt{encrypted\_traffic $<$yes|no$>$} This option enables detection and alerting on encrypted Telnet and FTP command channels. \begin{note} When \texttt{inspection\_type} is in stateless mode, checks for encrypted traffic will occur on every packet, whereas in stateful mode, a particular session will be noted as encrypted and not inspected any further. \end{note} \item \texttt{check\_encrypted} Instructs the preprocessor to continue to check an encrypted session for a subsequent command to cease encryption. \end{slist} \subsubsection{Example Global Configuration} \begin{verbatim} preprocessor ftp_telnet: \ global inspection_type stateful encrypted_traffic no \end{verbatim} \subsubsection{Telnet Configuration} The telnet configuration deals with configuration options that determine the functioning of the Telnet portion of the preprocessor. The following example gives the generic telnet configuration format: \subsubsection{Format} \begin{verbatim} preprocessor ftp_telnet_protocol: \ telnet \ ports { 23 } \ normalize \ ayt_attack_thresh 6 \ detect_anomalies \end{verbatim} There should only be a single telnet configuration, and subsequent instances will override previously set values. \paragraph{Configuration} \begin{slist} \item \texttt{ports $\{ <$port$> [<$port$> <...>] \}$} This is how the user configures which ports to decode as telnet traffic. SSH tunnels cannot be decoded, so adding port 22 will only yield false positives. Typically port 23 will be included. \item \texttt{normalize} This option tells the preprocessor to normalize the telnet traffic by eliminating the telnet escape sequences. It functions similarly to its predecessor, the telnet\_decode preprocessor. Rules written with 'raw' content options will ignore the normalized buffer that is created when this option is in use. \item \texttt{ayt\_attack\_thresh $<$ number $>$} This option causes the preprocessor to alert when the number of consecutive telnet Are You There (AYT) commands reaches the number specified. It is only applicable when the mode is stateful. \item \texttt{detect\_anomalies} In order to support certain options, Telnet supports subnegotiation. Per the Telnet RFC, subnegotiation begins with SB (subnegotiation begin) and must end with an SE (subnegotiation end). However, certain implementations of Telnet servers will ignore the SB without a corresponding SE. This is anomalous behavior which could be an evasion case. Being that FTP uses the Telnet protocol on the control connection, it is also susceptible to this behavior. The \texttt{detect\_anomalies} option enables alerting on Telnet SB without the corresponding SE. \end{slist} \subsubsection{Example Telnet Configuration} \begin{verbatim} preprocessor ftp_telnet_protocol: \ telnet ports { 23 } normalize ayt_attack_thresh 6 \end{verbatim} \subsubsection{FTP Server Configuration} There are two types of FTP server configurations: default and by IP address. \paragraph{Default} This configuration supplies the default server configuration for any FTP server that is not individually configured. Most of your FTP servers will most likely end up using the default configuration. \subsubsection{Example Default FTP Server Configuration} \begin{verbatim} preprocessor ftp_telnet_protocol: \ ftp server default ports { 21 } \end{verbatim} Refer to \pageref{sub:default ftp server config} for the list of options set in default ftp server configuration. \paragraph{Configuration by IP Address} This format is very similar to ``default'', the only difference being that specific IPs can be configured. \subsubsection{Example IP specific FTP Server Configuration} \begin{verbatim} preprocessor _telnet_protocol: \ ftp server 10.1.1.1 ports { 21 } ftp_cmds { XPWD XCWD } \end{verbatim} \subsubsection{FTP Server Configuration Options} \begin{slist} \item \texttt{ports $\{ <$port$> [<$port$> <...>] \}$} This is how the user configures which ports to decode as FTP command channel traffic. Typically port 21 will be included. \item \texttt{print\_cmds} During initialization, this option causes the preprocessor to print the configuration for each of the FTP commands for this server. \item \texttt{ftp\_cmds $\{ cmd [cmd] \}$ } The preprocessor is configured to alert when it sees an FTP command that is not allowed by the server. This option specifies a list of additional commands allowed by this server, outside of the default FTP command set as specified in RFC 959. This may be used to allow the use of the 'X' commands identified in RFC 775, as well as any additional commands as needed. For example: \begin{verbatim} ftp_cmds { XPWD XCWD XCUP XMKD XRMD } \end{verbatim} \item \texttt{def\_max\_param\_len $<$number$>$} This specifies the default maximum allowed parameter length for an FTP command. It can be used as a basic buffer overflow detection. \item \texttt{alt\_max\_param\_len $<$number$>$ $\{ cmd [cmd] \}$} This specifies the maximum allowed parameter length for the specified FTP command(s). It can be used as a more specific buffer overflow detection. For example the USER command -- usernames may be no longer than 16 bytes, so the appropriate configuration would be: \begin{verbatim} alt_max_param_len 16 { USER } \end{verbatim} \item \texttt{chk\_str\_fmt $\{ cmd [cmd] \}$} This option causes a check for string format attacks in the specified commands. \item \texttt{cmd\_validity cmd $<$ fmt $>$} This option specifies the valid format for parameters of a given command. fmt must be enclosed in $<>$'s and may contain the following: \begin{center} \begin{tabular}{| l | p{3in} |} \hline \textbf{Value} & \textbf{Description} \\ \hline \hline int & Parameter must be an integer \\ \hline number & Parameter must be an integer between 1 and 255 \\ \hline char $<$chars$>$ & Parameter must be a single character, one of $<$chars$>$ \\ \hline date $<$datefmt$>$ & Parameter follows format specified, where: \begin{tabular}{ l l } n & Number \\ C & Character \\ $[]$ & optional format enclosed \\ $|$ & OR \\ $\{\}$ & choice of options \\ . + - & literal \\ \end{tabular} \\ \hline string & Parameter is a string (effectively unrestricted) \\ \hline host\_port & Parameter must be a host/port specified, per RFC 959 \\ \hline long\_host\_port & Parameter must be a long host port specified, per RFC 1639 \\ \hline extended\_host\_port & Parameter must be an extended host port specified, per RFC 2428 \\ \hline $\{\}$, $|$ & One of choices enclosed within, separated by $|$ \\ \hline $\{\}$, $[]$ & One of the choices enclosed within $\{\}$, optional value enclosed within $[]$ \\ \hline \end{tabular} \end{center} Examples of the cmd\_validity option are shown below. These examples are the default checks, per RFC 959 and others performed by the preprocessor. \begin{verbatim} cmd_validity MODE cmd_validity STRU cmd_validity ALLO < int [ char R int ] > cmd_validity TYPE < { char AE [ char NTC ] | char I | char L [ number ] } > cmd_validity PORT < host_port > \end{verbatim} A cmd\_validity line can be used to override these defaults and/or add a check for other commands. \begin{verbatim} # This allows additional modes, including mode Z which allows for # zip-style compression. cmd_validity MODE < char ASBCZ > # Allow for a date in the MDTM command. cmd_validity MDTM < [ date nnnnnnnnnnnnnn[.n[n[n]]] ] string > \end{verbatim} MDTM is an off case that is worth discussing. While not part of an established standard, certain FTP servers accept MDTM commands that set the modification time on a file. The most common among servers that do, accept a format using YYYYMMDDHHmmss[.uuu]. Some others accept a format using YYYYMMDDHHmmss[+|-]TZ format. The example above is for the first case (time format as specified in http://www.ietf.org/internet-drafts/draft-ietf-ftpext-mlst-16.txt) To check validity for a server that uses the TZ format, use the following: \begin{verbatim} cmd_validity MDTM < [ date nnnnnnnnnnnnnn[{+|-}n[n]] ] string > \end{verbatim} \item \texttt{telnet\_cmds $<$yes$|$no$>$} This option turns on detection and alerting when telnet escape sequences are seen on the FTP command channel. Injection of telnet escape sequences could be used as an evasion attempt on an FTP command channel. \item \texttt{ignore\_telnet\_erase\_cmds $<$yes|no$>$} This option allows Snort to ignore telnet escape sequences for erase character (TNC EAC) and erase line (TNC EAL) when normalizing FTP command channel. Some FTP servers do not process those telnet escape sequences. \item \texttt{data\_chan} This option causes the rest of snort (rules, other preprocessors) to ignore FTP data channel connections. Using this option means that \textbf{NO INSPECTION} other than TCP state will be performed on FTP data transfers. It can be used to improve performance, especially with large file transfers from a trusted source. If your rule set includes virus-type rules, it is recommended that this option not be used. Use of the "data\_chan" option is deprecated in favor of the "ignore\_data\_chan" option. "data\_chan" will be removed in a future release. \item \texttt{ignore\_data\_chan $<$yes$|$no$>$} This option causes the rest of Snort (rules, other preprocessors) to ignore FTP data channel connections. Setting this option to "yes" means that \textbf{NO INSPECTION} other than TCP state will be performed on FTP data transfers. It can be used to improve performance, especially with large file transfers from a trusted source. If your rule set includes virus-type rules, it is recommended that this option not be used. \end{slist} \subsubsection{FTP Server Base Configuration Options} \label{sub:default ftp server config} The base FTP server configuration is as follows. Options specified in the configuration file will modify this set of options. FTP commands are added to the set of allowed commands. The other options will override those in the base configuration. \begin{verbatim} def_max_param_len 100 ftp_cmds { USER PASS ACCT CWD CDUP SMNT QUIT REIN TYPE STRU MODE RETR STOR STOU APPE ALLO REST RNFR RNTO ABOR DELE RMD MKD PWD LIST NLST SITE SYST STAT HELP NOOP } ftp_cmds { AUTH ADAT PROT PBSZ CONF ENC } ftp_cmds { PORT PASV LPRT LPSV EPRT EPSV } ftp_cmds { FEAT OPTS } ftp_cmds { MDTM REST SIZE MLST MLSD } alt_max_param_len 0 { CDUP QUIT REIN PASV STOU ABOR PWD SYST NOOP } cmd_validity MODE < char SBC > cmd_validity STRU < char FRPO [ string ] > cmd_validity ALLO < int [ char R int ] > cmd_validity TYPE < { char AE [ char NTC ] | char I | char L [ number ] } > cmd_validity PORT < host_port > cmd_validity LPRT < long_host_port > cmd_validity EPRT < extd_host_port > cmd_validity EPSV < [ { '1' | '2' | 'ALL' } ] > \end{verbatim} \subsubsection{FTP Client Configuration} Similar to the FTP Server configuration, the FTP client configurations has two types: default, and by IP address. \paragraph{Default} This configuration supplies the default client configuration for any FTP client that is not individually configured. Most of your FTP clients will most likely end up using the default configuration. \subsubsection{Example Default FTP Client Configuration} \begin{verbatim} preprocessor ftp_telnet_protocol: \ ftp client default bounce no max_resp_len 200 \end{verbatim} \paragraph{Configuration by IP Address} This format is very similar to ``default'', the only difference being that specific IPs can be configured. \subsubsection{Example IP specific FTP Client Configuration} \begin{verbatim} preprocessor ftp_telnet_protocol: \ ftp client 10.1.1.1 bounce yes max_resp_len 500 \end{verbatim} \subsubsection{FTP Client Configuration Options} \begin{slist} \item \texttt{max\_resp\_len $<$number$>$} This specifies the maximum allowed response length to an FTP command accepted by the client. It can be used as a basic buffer overflow detection. \item \texttt{bounce $<$yes|no$>$} This option turns on detection and alerting of FTP bounce attacks. An FTP bounce attack occurs when the FTP PORT command is issued and the specified host does not match the host of the client. \item \texttt{bounce\_to $<$ CIDR,[port$|$portlow,porthi] $>$} When the bounce option is turned on, this allows the PORT command to use the IP address (in CIDR format) and port (or inclusive port range) without generating an alert. It can be used to deal with proxied FTP connections where the FTP data channel is different from the client. A few examples: \begin{itemize} \item Allow bounces to 192.162.1.1 port 20020 -- i.e., the use of \texttt{PORT 192,168,1,1,78,52}. \begin{verbatim} bounce_to { 192.168.1.1,20020 } \end{verbatim} \item Allow bounces to 192.162.1.1 ports 20020 through 20040 -- i.e., the use of \texttt{PORT 192,168,1,1,78,xx}, where xx is 52 through 72 inclusive. \begin{verbatim} bounce_to { 192.168.1.1,20020,20040 } \end{verbatim} \item Allow bounces to 192.162.1.1 port 20020 and 192.168.1.2 port 20030. \begin{verbatim} bounce_to { 192.168.1.1,20020 192.168.1.2,20030 } \end{verbatim} \item Allows bounces to IPv6 address fe8::5 port 59340. \begin{verbatim} bounce_to { fe8::5,59340 } \end{verbatim} \end{itemize} \item \texttt{telnet\_cmds $<$yes|no$>$} This option turns on detection and alerting when telnet escape sequences are seen on the FTP command channel. Injection of telnet escape sequences could be used as an evasion attempt on an FTP command channel. \item \texttt{ignore\_telnet\_erase\_cmds $<$yes|no$>$} This option allows Snort to ignore telnet escape sequences for erase character (TNC EAC) and erase line (TNC EAL) when normalizing FTP command channel. Some FTP clients do not process those telnet escape sequences. \end{slist} \subsubsection{Examples/Default Configuration from snort.conf} \begin{verbatim} preprocessor ftp_telnet: \ global \ encrypted_traffic yes \ inspection_type stateful preprocessor ftp_telnet_protocol:\ telnet \ normalize \ ayt_attack_thresh 200 # This is consistent with the FTP rules as of 18 Sept 2004. # Set CWD to allow parameter length of 200 # MODE has an additional mode of Z (compressed) # Check for string formats in USER & PASS commands # Check MDTM commands that set modification time on the file. preprocessor ftp_telnet_protocol: \ ftp server default \ def_max_param_len 100 \ alt_max_param_len 200 { CWD } \ cmd_validity MODE < char ASBCZ > \ cmd_validity MDTM < [ date nnnnnnnnnnnnnn[.n[n[n]]] ] string > \ chk_str_fmt { USER PASS RNFR RNTO SITE MKD } \ telnet_cmds yes \ ignore_data_chan yes preprocessor ftp_telnet_protocol: \ ftp client default \ max_resp_len 256 \ bounce yes \ telnet_cmds yes \end{verbatim} \subsection{SSH} \label{sub:ssh} The SSH preprocessor detects the following exploits: Challenge-Response Buffer Overflow, CRC 32, Secure CRT, and the Protocol Mismatch exploit. Both Challenge-Response Overflow and CRC 32 attacks occur after the key exchange, and are therefore encrypted. Both attacks involve sending a large payload (20kb+) to the server immediately after the authentication challenge. To detect the attacks, the SSH preprocessor counts the number of bytes transmitted to the server. If those bytes exceed a predefined limit within a predefined number of packets, an alert is generated. Since the Challenge-Response Overflow only effects SSHv2 and CRC 32 only effects SSHv1, the SSH version string exchange is used to distinguish the attacks. The Secure CRT and protocol mismatch exploits are observable before the key exchange. \subsubsection{Configuration} By default, all alerts are disabled and the preprocessor checks traffic on port 22. The available configuration options are described below. \begin{slist} \item \texttt{server\_ports $\{ <$port$> [<$port$> <...>] \}$} This option specifies which ports the SSH preprocessor should inspect traffic to. \item \texttt{max\_encrypted\_packets $<$ number $>$} The number of stream reassembled encrypted packets that Snort will inspect before ignoring a given SSH session. The SSH vulnerabilities that Snort can detect all happen at the very beginning of an SSH session. Once max\_encrypted\_packets packets have been seen, Snort ignores the session to increase performance. The default is set to 25. This value can be set from 0 to 65535. \item \texttt{max\_client\_bytes $<$ number $>$} The number of unanswered bytes allowed to be transferred before alerting on Challenge-Response Overflow or CRC 32. This number must be hit before max\_encrypted\_packets packets are sent, or else Snort will ignore the traffic. The default is set to 19600. This value can be set from 0 to 65535. \item \texttt{max\_server\_version\_len $<$ number $>$} The maximum number of bytes allowed in the SSH server version string before alerting on the Secure CRT server version string overflow. The default is set to 80. This value can be set from 0 to 255. \item \texttt{autodetect} Attempt to automatically detect SSH. \item \texttt{enable\_respoverflow} Enables checking for the Challenge-Response Overflow exploit. \item \texttt{enable\_ssh1crc32} Enables checking for the CRC 32 exploit. \item \texttt{enable\_srvoverflow} Enables checking for the Secure CRT exploit. \item \texttt{enable\_protomismatch} Enables checking for the Protocol Mismatch exploit. \item \texttt{enable\_badmsgdir} Enable alerts for traffic flowing the wrong direction. For instance, if the presumed server generates client traffic, or if a client generates server traffic. \item \texttt{enable\_paysize} Enables alerts for invalid payload sizes. \item \texttt{enable\_recognition} Enable alerts for non-SSH traffic on SSH ports. \end{slist} The SSH preprocessor should work by default. After max\_encrypted\_packets is reached, the preprocessor will stop processing traffic for a given session. If Challenge-Response Overflow or CRC 32 false positive, try increasing the number of required client bytes with max\_client\_bytes. \subsubsection{Example Configuration from snort.conf} Looks for attacks on SSH server port 22. Alerts at 19600 unacknowledged bytes within 20 encrypted packets for the Challenge-Response Overflow/CRC32 exploits. \begin{verbatim} preprocessor ssh: \ server_ports { 22 } \ max_client_bytes 19600 \ max_encrypted_packets 20 \ enable_respoverflow \ enable_ssh1crc32 \end{verbatim} \subsection{DNS} \label{sub:dns} The DNS preprocessor decodes DNS Responses and can detect the following exploits: DNS Client RData Overflow, Obsolete Record Types, and Experimental Record Types. DNS looks at DNS Response traffic over UDP and TCP and it requires Stream preprocessor to be enabled for TCP decoding. \subsubsection{Configuration} By default, all alerts are disabled and the preprocessor checks traffic on port 53. The available configuration options are described below. \begin{slist} \item \texttt{ports $\{ <$port$> [<$port$> <...>] \}$} This option specifies the source ports that the DNS preprocessor should inspect traffic. \item \texttt{enable\_obsolete\_types} Alert on Obsolete (per RFC 1035) Record Types \item \texttt{enable\_experimental\_types} Alert on Experimental (per RFC 1035) Record Types \item \texttt{enable\_rdata\_overflow} Check for DNS Client RData TXT Overflow \end{slist} The DNS preprocessor does nothing if none of the 3 vulnerabilities it checks for are enabled. It will not operate on TCP sessions picked up midstream, and it will cease operation on a session if it loses state because of missing data (dropped packets). \subsubsection{Examples/Default Configuration from snort.conf} Looks for traffic on DNS server port 53. Check for the DNS Client RData overflow vulnerability. Do not alert on obsolete or experimental RData record types. \begin{verbatim} preprocessor dns: \ ports { 53 } \ enable_rdata_overflow \end{verbatim} \subsection{SSL/TLS} \label{sub:SSL/TLS} Encrypted traffic should be ignored by Snort for both performance reasons and to reduce false positives. The SSL Dynamic Preprocessor (SSLPP) decodes SSL and TLS traffic and optionally determines if and when Snort should stop inspection of it. Typically, SSL is used over port 443 as HTTPS. By enabling the SSLPP to inspect port 443 and enabling the noinspect\_encrypted option, only the SSL handshake of each connection will be inspected. Once the traffic is determined to be encrypted, no further inspection of the data on the connection is made. By default, SSLPP looks for a handshake followed by encrypted traffic traveling to both sides. If one side responds with an indication that something has failed, such as the handshake, the session is not marked as encrypted. Verifying that faultless encrypted traffic is sent from both endpoints ensures two things: the last client-side handshake packet was not crafted to evade Snort, and that the traffic is legitimately encrypted. In some cases, especially when packets may be missed, the only observed response from one endpoint will be TCP ACKs. Therefore, if a user knows that server-side encrypted data can be trusted to mark the session as encrypted, the user should use the 'trustservers' option, documented below. \subsubsection{Configuration} \begin{slist} \item \texttt{ports $\{ <$port$> [<$port$> <...>] \}$} This option specifies which ports SSLPP will inspect traffic on. By default, SSLPP watches the following ports: \begin{itemize} \item \texttt{443} HTTPS \item \texttt{465} SMTPS \item \texttt{563} NNTPS \item \texttt{636} LDAPS \item \texttt{989} FTPS \item \texttt{992} TelnetS \item \texttt{993} IMAPS \item \texttt{994} IRCS \item \texttt{995} POPS \end{itemize} \item \texttt{noinspect\_encrypted} Disable inspection on traffic that is encrypted. Default is off. \item \texttt{max\_heartbeat\_length} Maximum length of heartbeat record allowed. This config option is used to detect the heartbleed attacks. The allowed range is 0 to 65535. Setting the value to 0 turns off the heartbeat length checks. For heartbeat requests, if the payload size of the request record is greater than the max\_heartbeat\_length an alert with sid 3 and gid 137 is generated. For heartbeat responses, if the record size itself is greater than the max\_heartbeat\_length an alert with sid 4 and gid 137 is generated. Default is off. \item \texttt{trustservers} Disables the requirement that application (encrypted) data must be observed on both sides of the session before a session is marked encrypted. Use this option for slightly better performance if you trust that your servers are not compromised. This requires the \texttt{noinspect\_encrypted} option to be useful. Default is off. \end{slist} \subsubsection{Examples/Default Configuration from snort.conf} Enables the SSL preprocessor and tells it to disable inspection on encrypted traffic. \begin{verbatim} preprocessor ssl: noinspect_encrypted \end{verbatim} \subsubsection{Rule Options} The following rule options are supported by enabling the \texttt{ssl} preprocessor: \begin{itemize} \item[] \begin{verbatim} ssl_version ssl_state \end{verbatim} \end{itemize} \texttt{ssl\_version} \label{ssl:ssl_version} \begin{itemize} \item[] The \texttt{ssl\_version} rule option tracks the version negotiated between the endpoints of the SSL encryption. The list of version identifiers are below, and more than one identifier can be specified, via a comma separated list. Lists of identifiers are OR'ed together. The option will match if any one of the OR'ed versions are used in the SSL connection. To check for two or more SSL versions in use simultaneously, multiple \texttt{ssl\_version} rule options should be used. \textit{Syntax} \footnotesize \begin{verbatim} ssl_version: version-list = version | version , version-list version = ["!"] "sslv2" | "sslv3" | "tls1.0" | "tls1.1" | "tls1.2" \end{verbatim} \textit{Examples} \begin{verbatim} ssl_version:sslv3; ssl_version:tls1.0,tls1.1,tls1.2; ssl_version:!sslv2; \end{verbatim} \end{itemize} \texttt{ssl\_state} \label{ssl:ssl_state} \begin{itemize} \item[] The \texttt{ssl\_state} rule option tracks the state of the SSL encryption during the process of hello and key exchange. The list of states are below. More than one state can be specified, via a comma separated list, and are OR'ed together. The option will match if the connection is currently in any one of the OR'ed states. To ensure the connection has reached each of a set of states, multiple rules using the \texttt{ssl\_state} rule option should be used. \textit{Syntax} \footnotesize \begin{verbatim} ssl_state: state-list = state | state , state-list state = ["!"] "client_hello" | "server_hello" | "client_keyx" | "server_keyx" | "unknown" \end{verbatim} \textit{Examples} \begin{verbatim} ssl_state:client_hello; ssl_state:client_keyx,server_keyx; ssl_state:!server_hello; \end{verbatim} \end{itemize} \subsection{ARP Spoof Preprocessor} \label{sub:arpspoof} The ARP spoof preprocessor decodes ARP packets and detects ARP attacks, unicast ARP requests, and inconsistent Ethernet to IP mapping. When no arguments are specified to arpspoof, the preprocessor inspects Ethernet addresses and the addresses in the ARP packets. When inconsistency occurs, an alert with GID 112 and SID 2 or 3 is generated. When "\texttt{-unicast}" is specified as the argument of arpspoof, the preprocessor checks for unicast ARP requests. An alert with GID 112 and SID 1 will be generated if a unicast ARP request is detected. Specify a pair of IP and hardware address as the argument to \texttt{arpspoof\_detect\_host}. The host with the IP address should be on the same layer 2 segment as Snort is. Specify one host IP MAC combo per line. The preprocessor will use this list when detecting ARP cache overwrite attacks. Alert SID 4 is used in this case. \subsubsection{Format} \begin{verbatim} preprocessor arpspoof[: -unicast] preprocessor arpspoof_detect_host: ip mac \end{verbatim} \begin{table}[h] \begin{center} \begin{tabular}{| l | l |} \hline \textbf{Option} & \textbf{Description}\\ \hline \hline \texttt{ip} & IP address.\\ \hline \texttt{mac} & The Ethernet address corresponding to the preceding IP. \\ \hline \end{tabular} \end{center} \end{table} \subsubsection{Example Configuration} The first example configuration does neither unicast detection nor ARP mapping monitoring. The preprocessor merely looks for Ethernet address inconsistencies. \begin{verbatim} preprocessor arpspoof \end{verbatim} The next example configuration does not do unicast detection but monitors ARP mapping for hosts 192.168.40.1 and 192.168.40.2. \begin{verbatim} preprocessor arpspoof preprocessor arpspoof_detect_host: 192.168.40.1 f0:0f:00:f0:0f:00 preprocessor arpspoof_detect_host: 192.168.40.2 f0:0f:00:f0:0f:01 \end{verbatim} The third example configuration has unicast detection enabled. \begin{verbatim} preprocessor arpspoof: -unicast preprocessor arpspoof_detect_host: 192.168.40.1 f0:0f:00:f0:0f:00 preprocessor arpspoof_detect_host: 192.168.40.2 f0:0f:00:f0:0f:01 \end{verbatim} \subsection{DCE/RPC 2 Preprocessor} \label{sub:dcerpc2} The main purpose of the preprocessor is to perform SMB desegmentation and DCE/RPC defragmentation to avoid rule evasion using these techniques. SMB desegmentation is performed for the following commands that can be used to transport DCE/RPC requests and responses: \texttt{Write}, \texttt{Write Block Raw}, \texttt{Write and Close}, \texttt{Write AndX}, \texttt{Transaction}, \texttt{Transaction Secondary}, \texttt{Read}, \texttt{Read Block Raw} and \texttt{Read AndX}. The following transports are supported for DCE/RPC: SMB, TCP, UDP and RPC over HTTP v.1 proxy and server. New rule options have been implemented to improve performance, reduce false positives and reduce the count and complexity of DCE/RPC based rules. \subsubsection{Dependency Requirements} For proper functioning of the preprocessor: \begin{itemize} \item Stream session tracking must be enabled, i.e. \texttt{stream5}. The preprocessor requires a session tracker to keep its data. \item Stream reassembly must be performed for TCP sessions. If it is decided that a session is SMB or DCE/RPC, either through configured ports, servers or autodetecting, the \texttt{dcerpc2} preprocessor will enable stream reassembly for that session if necessary. \item IP defragmentation should be enabled, i.e. the \texttt{frag3} preprocessor should be enabled and configured. \end{itemize} \subsubsection{Target Based} There are enough important differences between Windows and Samba versions that a target based approach has been implemented. Some important differences:\\ \textit{Named pipe instance tracking} \begin{itemize} \item[] A combination of valid login handle or UID, share handle or TID and file/named pipe handle or FID must be used to write data to a named pipe. The binding between these is dependent on OS/software version. \begin{itemize} \item[] Samba 3.0.22 and earlier \begin{itemize} \item[] Any valid UID and TID, along with a valid FID can be used to make a request, however, if the TID used in creating the FID is deleted (via a tree disconnect), the FID that was created using this TID becomes invalid, i.e. no more requests can be written to that named pipe instance. \end{itemize} \item[] Samba greater than 3.0.22 \begin{itemize} \item[] Any valid TID, along with a valid FID can be used to make a request. However, only the UID used in opening the named pipe can be used to make a request using the FID handle to the named pipe instance. If the TID used to create the FID is deleted (via a tree disconnect), the FID that was created using this TID becomes invalid, i.e. no more requests can be written to that named pipe instance. If the UID used to create the named pipe instance is deleted (via a \texttt{Logoff AndX}), since it is necessary in making a request to the named pipe, the FID becomes invalid. \end{itemize} \item[] Windows 2003 \item[] Windows XP \item[] Windows Vista \begin{itemize} \item[] These Windows versions require strict binding between the UID, TID and FID used to make a request to a named pipe instance. Both the UID and TID used to open the named pipe instance must be used when writing data to the same named pipe instance. Therefore, deleting either the UID or TID invalidates the FID. \end{itemize} \item[] Windows 2000 \begin{itemize} \item[] Windows 2000 is interesting in that the first request to a named pipe must use the same binding as that of the other Windows versions. However, requests after that follow the same binding as Samba 3.0.22 and earlier, i.e. no binding. It also follows Samba greater than 3.0.22 in that deleting the UID or TID used to create the named pipe instance also invalidates it. \end{itemize} \end{itemize} \end{itemize} \textit{Accepted SMB commands} \begin{itemize} \item[] Samba in particular does not recognize certain commands under an \texttt{IPC\$} tree. \begin{itemize} \item[] Samba (all versions) \begin{itemize} \item[] Under an \texttt{IPC\$} tree, does not accept: \begin{itemize} \item[] \texttt{Open} \item[] \texttt{Write And Close} \item[] \texttt{Read} \item[] \texttt{Read Block Raw} \item[] \texttt{Write Block Raw} \end{itemize} \end{itemize} \item[] Windows (all versions) \begin{itemize} \item[] Accepts all of the above commands under an \texttt{IPC\$} tree. \end{itemize} \end{itemize} \end{itemize} \textit{AndX command chaining} \begin{itemize} \item[] Windows is very strict in what command combinations it allows to be chained. Samba, on the other hand, is very lax and allows some nonsensical combinations, e.g. multiple logins and tree connects (only one place to return handles for these), login/logoff and tree connect/tree disconnect. Ultimately, we don't want to keep track of data that the server won't accept. An evasion possibility would be accepting a fragment in a request that the server won't accept that gets sandwiched between an exploit. \end{itemize} \textit{Transaction tracking} \begin{itemize} \item[] The differences between a \texttt{Transaction} request and using one of the \texttt{Write*} commands to write data to a named pipe are that (1) a \texttt{Transaction} performs the operations of a write and a read from the named pipe, whereas in using the \texttt{Write*} commands, the client has to explicitly send one of the \texttt{Read*} requests to tell the server to send the response and (2) a \texttt{Transaction} request is not written to the named pipe until all of the data is received (via potential \texttt{Transaction Secondary} requests) whereas with the \texttt{Write*} commands, data is written to the named pipe as it is received by the server. Multiple Transaction requests can be made simultaneously to the same named pipe. These requests can also be segmented with \texttt{Transaction Secondary} commands. What distinguishes them (when the same named pipe is being written to, i.e. having the same FID) are fields in the SMB header representing a process id (PID) and multiplex id (MID). The PID represents the process this request is a part of. An MID represents different sub-processes within a process (or under a PID). Segments for each "thread" are stored separately and written to the named pipe when all segments are received. It is necessary to track this so as not to munge these requests together (which would be a potential evasion opportunity). \begin{itemize} \item[] Windows (all versions) \begin{itemize} \item[] Uses a combination of PID and MID to define a "thread". \end{itemize} \item[] Samba (all versions) \begin{itemize} \item[] Uses just the MID to define a "thread". \end{itemize} \end{itemize} \end{itemize} \textit{Multiple Bind Requests} \begin{itemize} \item[] A \texttt{Bind} request is the first request that must be made in a connection-oriented DCE/RPC session in order to specify the interface/interfaces that one wants to communicate with. \begin{itemize} \item[] Windows (all versions) \begin{itemize} \item[] For all of the Windows versions, only one \texttt{Bind} can ever be made on a session whether or not it succeeds or fails. Any binding after that must use the \texttt{Alter Context} request. If another \texttt{Bind} is made, all previous interface bindings are invalidated. \end{itemize} \item[] Samba 3.0.20 and earlier \begin{itemize} \item[] Any amount of \texttt{Bind} requests can be made. \end{itemize} \item[] Samba later than 3.0.20 \begin{itemize} \item[] Another \texttt{Bind} request can be made if the first failed and no interfaces were successfully bound to. If a \texttt{Bind} after a successful \texttt{Bind} is made, all previous interface bindings are invalidated. \end{itemize} \end{itemize} \end{itemize} \textit{DCE/RPC Fragmented requests - Context ID} \begin{itemize} \item[] Each fragment in a fragmented request carries the context id of the bound interface it wants to make the request to. \begin{itemize} \item[] Windows (all versions) \begin{itemize} \item[] The context id that is ultimately used for the request is contained in the first fragment. The context id field in any other fragment can contain any value. \end{itemize} \item[] Samba (all versions) \begin{itemize} \item[] The context id that is ultimately used for the request is contained in the last fragment. The context id field in any other fragment can contain any value. \end{itemize} \end{itemize} \end{itemize} \textit{DCE/RPC Fragmented requests - Operation number} \begin{itemize} \item[] Each fragment in a fragmented request carries an operation number (opnum) which is more or less a handle to a function offered by the interface. \begin{itemize} \item[] Samba (all versions) \item[] Windows 2000 \item[] Windows 2003 \item[] Windows XP \begin{itemize} \item[] The opnum that is ultimately used for the request is contained in the last fragment. The opnum field in any other fragment can contain any value. \end{itemize} \item[] Windows Vista \begin{itemize} \item[] The opnum that is ultimately used for the request is contained in the first fragment. The opnum field in any other fragment can contain any value. \end{itemize} \end{itemize} \end{itemize} \textit{DCE/RPC Stub data byte order} \begin{itemize} \item[] The byte order of the stub data is determined differently for Windows and Samba. \begin{itemize} \item[] Windows (all versions) \begin{itemize} \item[] The byte order of the stub data is that which was used in the \texttt{Bind} request. \end{itemize} \item[] Samba (all versions) \begin{itemize} \item[] The byte order of the stub data is that which is used in the request carrying the stub data. \end{itemize} \end{itemize} \end{itemize} \subsubsection{Configuration} The \texttt{dcerpc2} preprocessor has a global configuration and one or more server configurations. The global preprocessor configuration name is \texttt{dcerpc2} and the server preprocessor configuration name is \texttt{dcerpc2\_server}.\\ \underline{Global Configuration} \begin{verbatim} preprocessor dcerpc2 \end{verbatim} The global \texttt{dcerpc2} configuration is required. Only one global \texttt{dcerpc2} configuration can be specified.\\ \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{memcap} & \texttt{} & NO & \texttt{memcap 102400}\\ \hline \texttt{disable\_defrag} & NONE & NO & OFF\\ \hline \texttt{max\_frag\_len} & \texttt{} & NO & OFF\\ \hline \texttt{events} & \texttt{} & NO & OFF\\ \hline \texttt{reassemble\_threshold} & \texttt{} & NO & OFF\\ \hline \texttt{disabled} & NONE & NO & OFF\\ \hline \texttt{smb\_fingerprint\_policy} & \texttt{} & NO & OFF\\ \hline \texttt{smb\_legacy\_mode} & NONE & NO & OFF\\ \hline \end{tabular} \end{itemize} \footnotesize \begin{verbatim} memcap = 1024-4194303 (kilobytes) max-frag-len = 1514-65535 events = pseudo-event | event | '[' event-list ']' pseudo-event = "none" | "all" event-list = event | event ',' event-list event = "memcap" | "smb" | "co" | "cl" re-thresh = 0-65535 fp-policy = "server" | "client" | "both" \end{verbatim} \normalsize \textit{Option explanations} \begin{itemize} \item[] \texttt{memcap} \begin{itemize} \item[] Specifies the maximum amount of run-time memory that can be allocated. Run-time memory includes any memory allocated after configuration. Default is 100 MB. \end{itemize} \item[] \texttt{disabled} \begin{itemize} \item[] Disables the preprocessor. By default this value is turned off. When the preprocessor is disabled only the memcap option is applied when specified with the configuration. \end{itemize} \item[] \texttt{disable\_defrag} \begin{itemize} \item[] Tells the preprocessor not to do DCE/RPC defragmentation. Default is to do defragmentation. \end{itemize} \item[] \texttt{max\_frag\_len} \begin{itemize} \item[] Specifies the maximum fragment size that will be added to the defragmentation module. If a fragment is greater than this size, it is truncated before being added to the defragmentation module. The allowed range for this option is 1514 - 65535. \end{itemize} \item[] \texttt{events} \begin{itemize} \item[] Specifies the classes of events to enable. (See Events section for an enumeration and explanation of events.) \begin{itemize} \item[] \texttt{memcap} \begin{itemize} \item[] Only one event. If the memcap is reached or exceeded, alert. \end{itemize} \item[] \texttt{smb} \begin{itemize} \item[] Alert on events related to SMB processing. \end{itemize} \item[] \texttt{co} \begin{itemize} \item[] Stands for connection-oriented DCE/RPC. Alert on events related to connection-oriented DCE/RPC processing. \end{itemize} \item[] \texttt{cl} \begin{itemize} \item[] Stands for connectionless DCE/RPC. Alert on events related to connectionless DCE/RPC processing. \end{itemize} \end{itemize} \end{itemize} \item[] \texttt{reassemble\_threshold} \begin{itemize} \item[] Specifies a minimum number of bytes in the DCE/RPC desegmentation and defragmentation buffers before creating a reassembly packet to send to the detection engine. This option is useful in inline mode so as to potentially catch an exploit early before full defragmentation is done. A value of 0 supplied as an argument to this option will, in effect, disable this option. Default is disabled. \end{itemize} \item[] \texttt{smb\_fingerprint\_policy} \begin{itemize} \item[] By default, SMBv1, SMBv2, and SMBv3 files are inspected. If legacy mode is configured, only SMBv1 file inspection is enabled. \end{itemize} \item[] \texttt{legacy\_mode} \begin{itemize} \item[] In the initial phase of an SMB session, the client needs to authenticate with a SessionSetupAndX. Both the request and response to this command contain OS and version information that can allow the preprocessor to dynamically set the policy for a session which allows for better protection against Windows and Samba specific evasions. \end{itemize} \end{itemize} \textit{Option examples} \footnotesize \begin{verbatim} memcap 30000 max_frag_len 16840 events none events all events smb events co events [co] events [smb, co] events [memcap, smb, co, cl] reassemble_threshold 500 smb_fingerprint_policy both smb_fingerprint_policy client smb_legacy_mode \end{verbatim} \normalsize \textit{Configuration examples} \footnotesize \begin{verbatim} preprocessor dcerpc2 preprocessor dcerpc2: memcap 500000 preprocessor dcerpc2: max_frag_len 16840, memcap 300000, events smb preprocessor dcerpc2: memcap 50000, events [memcap, smb, co, cl], max_frag_len 14440 preprocessor dcerpc2: disable_defrag, events [memcap, smb] preprocessor dcerpc2: reassemble_threshold 500 preprocessor dcerpc2: memcap 50000, events [memcap, smb, co, cl], max_frag_len 14440, smb_fingerprint_policy both \end{verbatim} \normalsize \textit{Default global configuration} \footnotesize \begin{verbatim} preprocessor dcerpc2: memcap 102400 \end{verbatim} \normalsize \underline{Server Configuration} \begin{verbatim} preprocessor dcerpc2_server \end{verbatim} The \texttt{dcerpc2\_server} configuration is optional. A \texttt{dcerpc2\_server} configuration must start with \texttt{default} or \texttt{net} options. The \texttt{default} and \texttt{net} options are mutually exclusive. At most one default configuration can be specified. If no \texttt{default} configuration is specified, default values will be used for the \texttt{default} configuration. Zero or more \texttt{net} configurations can be specified. For any \texttt{dcerpc2\_server} configuration, if non-required options are not specified, the defaults will be used. When processing DCE/RPC traffic, the \texttt{default} configuration is used if no net configurations match. If a \texttt{net} configuration matches, it will override the \texttt{default} configuration. A \texttt{net} configuration matches if the packet's server IP address matches an IP address or net specified in the \texttt{net} configuration. The \texttt{net} option supports IPv6 addresses. Note that port and ip variables defined in \texttt{snort.conf} \textsc{cannot} be used. \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{default} & NONE & YES & NONE\\ \hline \texttt{net} & \texttt{} & YES & NONE\\ \hline \texttt{policy} & \texttt{} & NO & \texttt{policy WinXP}\\ \hline \texttt{detect} & \texttt{} & NO & \texttt{detect [smb [139,445], tcp 135, udp 135, rpc-over-http-server 593]}\\ \hline \texttt{autodetect} & \texttt{} & NO & \texttt{autodetect [tcp 1025:, udp 1025:, rpc-over-http-server 1025:]}\\ \hline \texttt{no\_autodetect\_http\_proxy\_ports} & NONE & NO & DISABLED (The preprocessor autodetects on all proxy ports by default)\\ \hline \texttt{smb\_invalid\_shares} & \texttt{} & NO & NONE\\ \hline \texttt{smb\_max\_chain} & \texttt{} & NO & \texttt{smb\_max\_chain 3}\\ \hline \texttt{smb\_file\_inspection} & \texttt{} & NO & \texttt{smb\_file\_inspection off}\\ \hline \end{tabular} \end{itemize} \footnotesize \begin{verbatim} net = ip | '[' ip-list ']' ip-list = ip | ip ',' ip-list ip = ip-addr | ip-addr '/' prefix | ip4-addr '/' netmask ip-addr = ip4-addr | ip6-addr ip4-addr = a valid IPv4 address ip6-addr = a valid IPv6 address (can be compressed) prefix = a valid CIDR netmask = a valid netmask policy = "Win2000" | "Win2003" | "WinXP" | "WinVista" | "Samba" | "Samba-3.0.22" | "Samba-3.0.20" detect = "none" | detect-opt | '[' detect-list ']' detect-list = detect-opt | detect-opt ',' detect-list detect-opt = transport | transport port-item | transport '[' port-list ']' transport = "smb" | "tcp" | "udp" | "rpc-over-http-proxy" | "rpc-over-http-server" port-list = port-item | port-item ',' port-list port-item = port | port-range port-range = ':' port | port ':' | port ':' port port = 0-65535 shares = share | '[' share-list ']' share-list = share | share ',' share-list share = word | '"' word '"' | '"' var-word '"' word = graphical ASCII characters except ',' '"' ']' '[' '$' var-word = graphical ASCII characters except ',' '"' ']' '[' max-chain = 0-255 file-inspect = file-arg | '[' file-list ']' file-arg = "off" | "on" | "only" file-list = file-arg [ ',' "file-depth" ] \end{verbatim} \normalsize \begin{itemize} \item[] Because the Snort main parser treats '\$' as the start of a variable and tries to expand it, shares with '\$' must be enclosed quotes. \end{itemize} \textit{Option explanations} \begin{itemize} \item[] \texttt{default} \begin{itemize} \item[] Specifies that this configuration is for the default server configuration. \end{itemize} \item[] \texttt{net} \begin{itemize} \item[] Specifies that this configuration is an IP or net specific configuration. The configuration will only apply to the IP addresses and nets supplied as an argument. \end{itemize} \item[] \texttt{policy} \begin{itemize} \item[] Specifies the target-based policy to use when processing. Default is "WinXP". \end{itemize} \item[] \texttt{detect} \begin{itemize} \item[] Specifies the DCE/RPC transport and server ports that should be detected on for the transport. Defaults are ports 139 and 445 for SMB, 135 for TCP and UDP, 593 for RPC over HTTP server and 80 for RPC over HTTP proxy. \end{itemize} \item[] \texttt{autodetect} \begin{itemize} \item[] Specifies the DCE/RPC transport and server ports that the preprocessor should attempt to autodetect on for the transport. The autodetect ports are only queried if no detect transport/ports match the packet. The order in which the preprocessor will attempt to autodetect will be - TCP/UDP, RPC over HTTP server, RPC over HTTP proxy and lastly SMB. Note that most dynamic DCE/RPC ports are above 1024 and ride directly over TCP or UDP. It would be very uncommon to see SMB on anything other than ports 139 and 445. Defaults are 1025-65535 for TCP, UDP and RPC over HTTP server. \end{itemize} \item[] \texttt{no\_autodetect\_http\_proxy\_ports} \begin{itemize} \item[] By default, the preprocessor will always attempt to autodetect for ports specified in the detect configuration for rpc-over-http-proxy. This is because the proxy is likely a web server and the preprocessor should not look at all web traffic. This option is useful if the RPC over HTTP proxy configured with the detect option is only used to proxy DCE/RPC traffic. Default is to autodetect on RPC over HTTP proxy detect ports. \end{itemize} \item[] \texttt{smb\_invalid\_shares} \begin{itemize} \item[] Specifies SMB shares that the preprocessor should alert on if an attempt is made to connect to them via a \texttt{Tree Connect} or \texttt{Tree Connect AndX}. Default is empty. \end{itemize} \item[] \texttt{smb\_max\_chain} \begin{itemize} \item[] Specifies the maximum amount of AndX command chaining that is allowed before an alert is generated. Default maximum is 3 chained commands. A value of 0 disables this option. This value can be set from 0 to 255. \end{itemize} \item[] \texttt{smb\_file\_inspection} \begin{itemize} \item[] Instructs the preprocessor to do inspection of normal SMB file transfers. This includes doing file type and signature through the file API as well as setting a pointer for the \texttt{file\_data} rule option. Note that the \texttt{file-depth} option only applies to the maximum amount of file data for which it will set the pointer for the \texttt{file\_data} rule option. For file type and signature it will use the value configured for the file API. If \texttt{only} is specified, the preprocessor will only do SMB file inspection, i.e. it will not do any DCE/RPC tracking or inspection. If \texttt{on} is specified with no arguments, the default file depth is 16384 bytes. An argument of -1 to \texttt{file-depth} disables setting the pointer for \texttt{file\_data}, effectively disabling SMB file inspection in rules. An argument of 0 to \texttt{file-depth} means unlimited. Default is \texttt{off}, i.e. no SMB file inspection is done in the preprocessor. SMBv1, SMBv2, and SMBv3 are supported. \end{itemize} \end{itemize} \textit{Option examples} \footnotesize \begin{verbatim} net 192.168.0.10 net 192.168.0.0/24 net [192.168.0.0/24] net 192.168.0.0/255.255.255.0 net feab:45b3:ab92:8ac4:d322:007f:e5aa:7845 net feab:45b3:ab92:8ac4:d322:007f:e5aa:7845/128 net feab:45b3::/32 net [192.168.0.10, feab:45b3::/32] net [192.168.0.0/24, feab:45b3:ab92:8ac4:d322:007f:e5aa:7845] policy Win2000 policy Samba-3.0.22 detect none detect smb detect [smb] detect smb 445 detect [smb 445] detect smb [139,445] detect [smb [139,445]] detect [smb, tcp] detect [smb 139, tcp [135,2103]] detect [smb [139,445], tcp 135, udp 135, rpc-over-http-server [593,6002:6004]] autodetect none autodetect tcp autodetect [tcp] autodetect tcp 2025: autodetect [tcp 2025:] autodetect tcp [2025:3001,3003:] autodetect [tcp [2025:3001,3003:]] autodetect [tcp, udp] autodetect [tcp 2025:, udp 2025:] autodetect [tcp 2025:, udp, rpc-over-http-server [1025:6001,6005:]] smb_invalid_shares private smb_invalid_shares "private" smb_invalid_shares "C$" smb_invalid_shares [private, "C$"] smb_invalid_shares ["private", "C$"] smb_max_chain 1 smb_file_inspection on smb_file_inspection off smb_file_inspection [ on, file-depth -1 ] smb_file_inspection [ on, file-depth 0 ] smb_file_inspection [ on, file-depth 4294967296 ] smb_file_inspection [ only, file-depth -1 ] \end{verbatim} \normalsize \textit{Configuration examples} \footnotesize \begin{verbatim} preprocessor dcerpc2_server: \ default preprocessor dcerpc2_server: \ default, policy Win2000 preprocessor dcerpc2_server: \ default, policy Win2000, detect [smb, tcp], autodetect tcp 1025:, \ smb_invalid_shares ["C$", "D$", "ADMIN$"] preprocessor dcerpc2_server: net 10.4.10.0/24, policy Win2000 preprocessor dcerpc2_server: \ net [10.4.10.0/24,feab:45b3::/126], policy WinVista, smb_max_chain 1 preprocessor dcerpc2_server: \ net [10.4.10.0/24,feab:45b3::/126], policy WinVista, \ detect [smb, tcp, rpc-over-http-proxy 8081], autodetect [tcp, rpc-over-http-proxy [1025:6001,6005:]], \ smb_invalid_shares ["C$", "ADMIN$"], no_autodetect_http_proxy_ports preprocessor dcerpc2_server: \ net [10.4.11.56,10.4.11.57], policy Samba, detect smb, autodetect none preprocessor dcerpc2_server: default, policy WinXP, \ smb_file_inspection [ on, file-depth 0 ] \end{verbatim} \normalsize \textit{Default server configuration} \footnotesize \begin{verbatim} preprocessor dcerpc2_server: default, policy WinXP, \ detect [smb [139,445], tcp 135, udp 135, rpc-over-http-server 593], \ autodetect [tcp 1025:, udp 1025:, rpc-over-http-server 1025:], \ smb_max_chain 3, smb_file_inspection off \end{verbatim} \normalsize \underline{Complete \texttt{dcerpc2} default configuration} \footnotesize \begin{verbatim} preprocessor dcerpc2: memcap 102400 preprocessor dcerpc2_server: \ default, policy WinXP, \ detect [smb [139,445], tcp 135, udp 135, rpc-over-http-server 593], \ autodetect [tcp 1025:, udp 1025:, rpc-over-http-server 1025:], \ smb_max_chain 3, smb_file_inspection off \end{verbatim} \normalsize \subsubsection{Events} The preprocessor uses GID 133 to register events.\\ \textit{Memcap events} \begin{itemize} \item[] \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline \hline 1 & If the memory cap is reached and the preprocessor is configured to alert.\\ \hline \end{longtable} \end{itemize} \textit{SMB events} \begin{itemize} \item[] \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline \hline 2 & An invalid NetBIOS Session Service type was specified in the header. Valid types are: \texttt{Message}, \texttt{Request} (only from client), \texttt{Positive Response} (only from server), \texttt{Negative Response} (only from server), \texttt{Retarget Response} (only from server) and \texttt{Keep Alive}.\\ \hline 3 & An SMB message type was specified in the header. Either a request was made by the server or a response was given by the client.\\ \hline 4 & The SMB id does not equal \texttt{\textbackslash xffSMB} or \texttt{\textbackslash xfeSMB}.\\ \hline 5 & The word count of the command header is invalid. SMB commands have pretty specific word counts and if the preprocessor sees a command with a word count that doesn't jive with that command, the preprocessor will alert.\\ \hline 6 & Some commands require a minimum number of bytes after the command header. If a command requires this and the byte count is less than the minimum required byte count for that command, the preprocessor will alert.\\ \hline 7 & Some commands, especially the commands from the SMB Core implementation require a data format field that specifies the kind of data that will be coming next. Some commands require a specific format for the data. The preprocessor will alert if the format is not that which is expected for that command.\\ \hline 8 & Many SMB commands have a field containing an offset from the beginning of the SMB header to where the data the command is carrying starts. If this offset puts us before data that has already been processed or after the end of payload, the preprocessor will alert.\\ \hline 9 & Some SMB commands, such as \texttt{Transaction}, have a field containing the total amount of data to be transmitted. If this field is zero, the preprocessor will alert.\\ \hline 10 & The preprocessor will alert if the NetBIOS Session Service length field contains a value less than the size of an SMB header.\\ \hline 11 & The preprocessor will alert if the remaining NetBIOS packet length is less than the size of the SMB command header to be decoded.\\ \hline 12 & The preprocessor will alert if the remaining NetBIOS packet length is less than the size of the SMB command byte count specified in the command header.\\ \hline 13 & The preprocessor will alert if the remaining NetBIOS packet length is less than the size of the SMB command data size specified in the command header.\\ \hline 14 & The preprocessor will alert if the total data count specified in the SMB command header is less than the data size specified in the SMB command header. (Total data count must always be greater than or equal to current data size.)\\ \hline 15 & The preprocessor will alert if the total amount of data sent in a transaction is greater than the total data count specified in the SMB command header.\\ \hline 16 & The preprocessor will alert if the byte count specified in the SMB command header is less than the data size specified in the SMB command. (The byte count must always be greater than or equal to the data size.)\\ \hline 17 & Some of the Core Protocol commands (from the initial SMB implementation) require that the byte count be some value greater than the data size exactly. The preprocessor will alert if the byte count minus a predetermined amount based on the SMB command is not equal to the data size.\\ \hline 18 & For the \texttt{Tree Connect} command (and not the \texttt{Tree Connect AndX} command), the preprocessor has to queue the requests up and wait for a server response to determine whether or not an IPC share was successfully connected to (which is what the preprocessor is interested in). Unlike the \texttt{Tree Connect AndX} response, there is no indication in the \texttt{Tree Connect} response as to whether the share is IPC or not. There should be under normal circumstances no more than a few pending tree connects at a time and the preprocessor will alert if this number is excessive.\\ \hline 19 & After a client is done writing data using the \texttt{Write*} commands, it issues a \texttt{Read*} command to the server to tell it to send a response to the data it has written. In this case the preprocessor is concerned with the server response. The \texttt{Read*} request contains the file id associated with a named pipe instance that the preprocessor will ultimately send the data to. The server response, however, does not contain this file id, so it need to be queued with the request and dequeued with the response. If multiple \texttt{Read*} requests are sent to the server, they are responded to in the order they were sent. There should be under normal circumstances no more than a few pending \texttt{Read*} requests at a time and the preprocessor will alert if this number is excessive.\\ \hline 20 & The preprocessor will alert if the number of chained commands in a single request is greater than or equal to the configured amount (default is 3).\\ \hline 21 & With \texttt{AndX} command chaining it is possible to chain multiple \texttt{Session Setup AndX} commands within the same request. There is, however, only one place in the SMB header to return a login handle (or Uid). Windows does not allow this behavior, however Samba does. This is anomalous behavior and the preprocessor will alert if it happens.\\ \hline 22 & With \texttt{AndX} command chaining it is possible to chain multiple \texttt{Tree Connect AndX} commands within the same request. There is, however, only one place in the SMB header to return a tree handle (or Tid). Windows does not allow this behavior, however Samba does. This is anomalous behavior and the preprocessor will alert if it happens.\\ \hline 23 & When a \texttt{Session Setup AndX} request is sent to the server, the server responds (if the client successfully authenticates) which a user id or login handle. This is used by the client in subsequent requests to indicate that it has authenticated. A \texttt{Logoff AndX} request is sent by the client to indicate it wants to end the session and invalidate the login handle. With commands that are chained after a \texttt{Session Setup AndX} request, the login handle returned by the server is used for the subsequent chained commands. The combination of a \texttt{Session Setup AndX} command with a chained \texttt{Logoff AndX} command, essentially logins in and logs off in the same request and is anomalous behavior. The preprocessor will alert if it sees this.\\ \hline 24 & A \texttt{Tree Connect AndX} command is used to connect to a share. The \texttt{Tree Disconnect} command is used to disconnect from that share. The combination of a \texttt{Tree Connect AndX} command with a chained \texttt{Tree Disconnect} command, essentially connects to a share and disconnects from the same share in the same request and is anomalous behavior. The preprocessor will alert if it sees this.\\ \hline 25 & An \texttt{Open AndX} or \texttt{Nt Create AndX} command is used to open/create a file or named pipe. (The preprocessor is only interested in named pipes as this is where DCE/RPC requests are written to.) The \texttt{Close} command is used to close that file or named pipe. The combination of a \texttt{Open AndX} or \texttt{Nt Create AndX} command with a chained \texttt{Close} command, essentially opens and closes the named pipe in the same request and is anomalous behavior. The preprocessor will alert if it sees this.\\ \hline 26 & The preprocessor will alert if it sees any of the invalid SMB shares configured. It looks for a \texttt{Tree Connect} or \texttt{Tree Connect AndX} to the share.\\ \hline 48 & The preprocessor will alert if a data count for a Core dialect write command is zero.\\ \hline 49 & For some of the Core dialect commands such as \texttt{Write} and \texttt{Read}, there are two data count fields, one in the main command header and one in the data format section. If these aren't the same, the preprocessor will alert.\\ \hline 50 & In the initial negotiation phase of an SMB session, the server in a \texttt{Negotiate} response and the client in a \texttt{SessionSetupAndX} request will advertise the maximum number of outstanding requests supported. The preprocessor will alert if the lesser of the two is exceeded.\\ \hline 51 & When a client sends a request it uses a value called the MID (multiplex id) to match a response, which the server is supposed to echo, to a request. If there are multiple outstanding requests with the same MID, the preprocessor will alert.\\ \hline 52 & In the \texttt{Negotiate} request a client gives a list of SMB dialects it supports, normally in order from least desirable to most desirable and the server responds with the index of the dialect to be used on the SMB session. Anything less than "NT LM 0.12" would be very odd these days (even Windows 98 supports it) and the preprocessor will alert if the client doesn't offer it as a supported dialect or the server chooses a lesser dialect.\\ \hline 53 & There are a number of commands that are considered deprecated and/or obsolete by Microsoft (see MS-CIFS and MS-SMB). If the preprocessor detects the use of a deprecated/obsolete command used it will alert.\\ \hline 54 & There are some commands that can be used that can be considered unusual in the context they are used. These include some of the transaction commands such as: \texttt{SMB\_COM\_TRANSACTION / TRANS\_READ\_NMPIPE} \texttt{SMB\_COM\_TRANSACTION / TRANS\_WRITE\_NMPIPE} \texttt{SMB\_COM\_TRANSACTION2 / TRANS2\_OPEN2} \texttt{SMB\_COM\_NT\_TRANSACT / NT\_TRANSACT\_CREATE} The preprocessor will alert if it detects unusual use of a command.\\ \hline 55 & Transaction commands have a setup count field that indicates the number of 16bit words in the transaction setup. The preprocessor will alert if the setup count is invalid for the transaction command / sub command.\\ \hline 56 & There can be only one Negotiate transaction per session and it is the first thing a client and server do to determine the SMB dialect each supports. The preprocessor will alert if the client attempts multiple dialect negotiations.\\ \hline 57 & Malware will often set a file's attributes to ReadOnly/Hidden/System if it is successful in installing itself as a Windows service or is able to write an autorun.inf file since it doesn't want the user to see the file and the default folder options in Windows is not to display Hidden files. The preprocessor will alert if it detects a client attempt to set a file's attributes to ReadOnly/Hidden/System.\\ \hline 58 & File offset provided is greater than file size specified. This is applied to read / write requests for file.\\ \hline 59 & Nextcommand specified in SMBv2 or SMBv3 header is beyond payload boundary.\\ \hline \end{longtable} \end{itemize} \textit{Connection-oriented DCE/RPC events} \begin{itemize} \item[] \begin{longtable}[h]{|r|p{13.5cm}|} \hline SID & Description\\ \hline \hline 27 & The preprocessor will alert if the connection-oriented DCE/RPC major version contained in the header is not equal to 5.\\ \hline 28 & The preprocessor will alert if the connection-oriented DCE/RPC minor version contained in the header is not equal to 0.\\ \hline 29 & The preprocessor will alert if the connection-oriented DCE/RPC PDU type contained in the header is not a valid PDU type.\\ \hline 30 & The preprocessor will alert if the fragment length defined in the header is less than the size of the header.\\ \hline 31 & The preprocessor will alert if the remaining fragment length is less than the remaining packet size.\\ \hline 32 & The preprocessor will alert if in a \texttt{Bind} or \texttt{Alter Context} request, there are no context items specified.\\ \hline 33 & The preprocessor will alert if in a \texttt{Bind} or \texttt{Alter Context} request, there are no transfer syntaxes to go with the requested interface.\\ \hline 34 & The preprocessor will alert if a non-last fragment is less than the size of the negotiated maximum fragment length. Most evasion techniques try to fragment the data as much as possible and usually each fragment comes well below the negotiated transmit size.\\ \hline 35 & The preprocessor will alert if a fragment is larger than the maximum negotiated fragment length.\\ \hline 36 & The byte order of the request data is determined by the Bind in connection-oriented DCE/RPC for Windows. It is anomalous behavior to attempt to change the byte order mid-session.\\ \hline 37 & The call id for a set of fragments in a fragmented request should stay the same (it is incremented for each complete request). The preprocessor will alert if it changes in a fragment mid-request.\\ \hline 38 & The operation number specifies which function the request is calling on the bound interface. If a request is fragmented, this number should stay the same for all fragments. The preprocessor will alert if the opnum changes in a fragment mid-request.\\ \hline 39 & The context id is a handle to a interface that was bound to. If a request if fragmented, this number should stay the same for all fragments. The preprocessor will alert if the context id changes in a fragment mid-request.\\ \hline \end{longtable} \end{itemize} \textit{Connectionless DCE/RPC events} \begin{itemize} \item[] \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline \hline 40 & The preprocessor will alert if the connectionless DCE/RPC major version is not equal to 4.\\ \hline 41 & The preprocessor will alert if the connectionless DCE/RPC PDU type is not a valid PDU type.\\ \hline 42 & The preprocessor will alert if the packet data length is less than the size of the connectionless header.\\ \hline 43 & The preprocessor will alert if the sequence number uses in a request is the same or less than a previously used sequence number on the session. In testing, wrapping the sequence number space produces strange behavior from the server, so this should be considered anomalous behavior.\\ \hline \end{longtable} \end{itemize} \subsubsection{Rule Options} New rule options are supported by enabling the \texttt{dcerpc2} preprocessor: \begin{itemize} \item[] \begin{verbatim} dce_iface dce_opnum dce_stub_data \end{verbatim} \end{itemize} New modifiers to existing \texttt{byte\_test} and \texttt{byte\_jump} rule options: \begin{itemize} \item[] \begin{verbatim} byte_test:dce byte_jump:dce \end{verbatim} \end{itemize} \texttt{dce\_iface} \label{dcerpc2:dce_iface} \begin{itemize} \item[] For DCE/RPC based rules it has been necessary to set flow-bits based on a client bind to a service to avoid false positives. It is necessary for a client to bind to a service before being able to make a call to it. When a client sends a bind request to the server, it can, however, specify one or more service interfaces to bind to. Each interface is represented by a UUID. Each interface UUID is paired with a unique index (or context id) that future requests can use to reference the service that the client is making a call to. The server will respond with the interface UUIDs it accepts as valid and will allow the client to make requests to those services. When a client makes a request, it will specify the context id so the server knows what service the client is making a request to. Instead of using flow-bits, a rule can simply ask the preprocessor, using this rule option, whether or not the client has bound to a specific interface UUID and whether or not this client request is making a request to it. This can eliminate false positives where more than one service is bound to successfully since the preprocessor can correlate the bind UUID to the context id used in the request. A DCE/RPC request can specify whether numbers are represented as big endian or little endian. The representation of the interface UUID is different depending on the endianness specified in the DCE/RPC previously requiring two rules - one for big endian and one for little endian. The preprocessor eliminates the need for two rules by normalizing the UUID. An interface contains a version. Some versions of an interface may not be vulnerable to a certain exploit. Also, a DCE/RPC request can be broken up into 1 or more fragments. Flags (and a field in the connectionless header) are set in the DCE/RPC header to indicate whether the fragment is the first, a middle or the last fragment. Many checks for data in the DCE/RPC request are only relevant if the DCE/RPC request is a first fragment (or full request), since subsequent fragments will contain data deeper into the DCE/RPC request. A rule which is looking for data, say 5 bytes into the request (maybe it's a length field), will be looking at the wrong data on a fragment other than the first, since the beginning of subsequent fragments are already offset some length from the beginning of the request. This can be a source of false positives in fragmented DCE/RPC traffic. By default it is reasonable to only evaluate if the request is a first fragment (or full request). However, if the \texttt{any\_frag} option is used to specify evaluating on all fragments.\\ \textit{Syntax} \footnotesize \begin{verbatim} dce_iface:[, ][, any_frag]; uuid = hexlong '-' hexshort '-' hexshort '-' 2hexbyte '-' 6hexbyte hexlong = 4hexbyte hexshort = 2hexbyte hexbyte = 2HEXDIGIT operator = '<' | '>' | '=' | '!' version = 0-65535 \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} dce_iface:4b324fc8-1670-01d3-1278-5a47bf6ee188; dce_iface:4b324fc8-1670-01d3-1278-5a47bf6ee188, <2; dce_iface:4b324fc8-1670-01d3-1278-5a47bf6ee188, any_frag; dce_iface:4b324fc8-1670-01d3-1278-5a47bf6ee188, =1, any_frag; \end{verbatim} \normalsize This option is used to specify an interface UUID. Optional arguments are an interface version and operator to specify that the version be less than ('\textless'), greater than ('\textgreater'), equal to ('=') or not equal to ('!') the version specified. Also, by default the rule will only be evaluated for a first fragment (or full request, i.e. not a fragment) since most rules are written to start at the beginning of a request. The \texttt{any\_frag} argument says to evaluate for middle and last fragments as well. This option requires tracking client \texttt{Bind} and \texttt{Alter Context} requests as well as server \texttt{Bind Ack} and \texttt{Alter Context} responses for connection-oriented DCE/RPC in the preprocessor. For each \texttt{Bind} and \texttt{Alter Context} request, the client specifies a list of interface UUIDs along with a handle (or context id) for each interface UUID that will be used during the DCE/RPC session to reference the interface. The server response indicates which interfaces it will allow the client to make requests to - it either accepts or rejects the client's wish to bind to a certain interface. This tracking is required so that when a request is processed, the context id used in the request can be correlated with the interface UUID it is a handle for. \texttt{hexlong} and \texttt{hexshort} will be specified and interpreted to be in big endian order (this is usually the default way an interface UUID will be seen and represented). As an example, the following Messenger interface UUID as taken off the wire from a little endian \texttt{Bind} request: \begin{verbatim} |f8 91 7b 5a 00 ff d0 11 a9 b2 00 c0 4f b6 e6 fc| \end{verbatim} must be written as: \begin{verbatim} 5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc \end{verbatim} The same UUID taken off the wire from a big endian \texttt{Bind} request: \begin{verbatim} |5a 7b 91 f8 ff 00 11 d0 a9 b2 00 c0 4f b6 e6 fc| \end{verbatim} must be written the same way: \begin{verbatim} 5a7b91f8-ff00-11d0-a9b2-00c04fb6e6fc \end{verbatim} This option matches if the specified interface UUID matches the interface UUID (as referred to by the context id) of the DCE/RPC request and if supplied, the version operation is true. This option will not match if the fragment is not a first fragment (or full request) unless the \texttt{any\_frag} option is supplied in which case only the interface UUID and version need match. Note that a defragmented DCE/RPC request will be considered a full request. \begin{note} Using this rule option will automatically insert fast pattern contents into the fast pattern matcher. For UDP rules, the interface UUID, in both big and little endian format will be inserted into the fast pattern matcher. For TCP rules, (1) if the rule option \texttt{flow:to\_server|from\_client} is used, $|$05 00 00$|$ will be inserted into the fast pattern matcher, (2) if the rule option \texttt{flow:from\_server|to\_client} is used, $|$05 00 02$|$ will be inserted into the fast pattern matcher and (3) if the flow isn't known, $|$05 00$|$ will be inserted into the fast pattern matcher. Note that if the rule already has content rule options in it, the best (meaning longest) pattern will be used. If a content in the rule uses the \texttt{fast\_pattern} rule option, it will unequivocally be used over the above mentioned patterns. \end{note} \end{itemize} \texttt{dce\_opnum} \label{dcerpc2:dce_opnum} \begin{itemize} \item[] The opnum represents a specific function call to an interface. After is has been determined that a client has bound to a specific interface and is making a request to it (see above - \texttt{dce\_iface}) usually we want to know what function call it is making to that service. It is likely that an exploit lies in the particular DCE/RPC function call.\\ \textit{Syntax} \footnotesize \begin{verbatim} dce_opnum:; opnum-list = opnum-item | opnum-item ',' opnum-list opnum-item = opnum | opnum-range opnum-range = opnum '-' opnum opnum = 0-65535 \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} dce_opnum:15; dce_opnum:15-18; dce_opnum:15, 18-20; dce_opnum:15, 17, 20-22; \end{verbatim} \normalsize This option is used to specify an opnum (or operation number), opnum range or list containing either or both opnum and/or opnum-range. The opnum of a DCE/RPC request will be matched against the opnums specified with this option. This option matches if any one of the opnums specified match the opnum of the DCE/RPC request. \end{itemize} \texttt{dce\_stub\_data} \label{dcerpc2:dce_stub_data} \begin{itemize} \item[] Since most netbios rules were doing protocol decoding only to get to the DCE/RPC stub data, i.e. the remote procedure call or function call data, this option will alleviate this need and place the cursor at the beginning of the DCE/RPC stub data. This reduces the number of rule option checks and the complexity of the rule. This option takes no arguments.\\ \textit{Example} \footnotesize \begin{verbatim} dce_stub_data; \end{verbatim} \normalsize This option is used to place the cursor (used to walk the packet payload in rules processing) at the beginning of the DCE/RPC stub data, regardless of preceding rule options. There are no arguments to this option. This option matches if there is DCE/RPC stub data. The cursor is moved to the beginning of the stub data. All ensuing rule options will be considered "sticky" to this buffer. The first rule option following \texttt{dce\_stub\_data} should use absolute location modifiers if it is position-dependent. Subsequent rule options should use a relative modifier if they are meant to be relative to a previous rule option match in the stub data buffer. Any rule option that does not specify a relative modifier will be evaluated from the start of the stub data buffer. To leave the stub data buffer and return to the main payload buffer, use the \texttt{pkt\_data} rule option - see section \ref{sub:pkt_data} for details). \end{itemize} \texttt{byte\_test} and \texttt{byte\_jump} with \texttt{dce}\label{dcerpc2:byte_test_jump} \begin{itemize} \item[] A DCE/RPC request can specify whether numbers are represented in big or little endian. These rule options will take as a new argument \texttt{dce} and will work basically the same as the normal \texttt{byte\_test}/\texttt{byte\_jump}, but since the DCE/RPC preprocessor will know the endianness of the request, it will be able to do the correct conversion. \texttt{byte\_test} \begin{itemize} \item[] \textit{Syntax} \footnotesize \begin{verbatim} byte_test:, [!], , [, relative], dce; convert = 1 | 2 | 4 (only with option "dce") operator = '<' | '=' | '>' | '<=' | '>=' | '&' | '^' value = 0 - 4294967295 offset = -65535 to 65535 \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} byte_test:4, >, 35000, 0, relative, dce; byte_test:2, !=, 2280, -10, relative, dce; \end{verbatim} \normalsize When using the \texttt{dce} argument to a \texttt{byte\_test}, the following normal \texttt{byte\_test} arguments will not be allowed: \texttt{big}, \texttt{little}, \texttt{string}, \texttt{hex}, \texttt{dec} and \texttt{oct}. \end{itemize} \texttt{byte\_jump} \begin{itemize} \item[] \textit{Syntax} \footnotesize \begin{verbatim} byte_jump:, [, relative][, multiplier ] \ [, align][, post_offset ], dce; convert = 1 | 2 | 4 (only with option "dce") offset = -65535 to 65535 mult_value = 0 - 65535 adjustment_value = -65535 to 65535 \end{verbatim} \normalsize \textit{Example} \footnotesize \begin{verbatim} byte_jump:4,-4,relative,align,multiplier 2,post_offset -4,dce; \end{verbatim} \normalsize When using the \texttt{dce} argument to a \texttt{byte\_jump}, the following normal \texttt{byte\_jump} arguments will not be allowed: \texttt{big}, \texttt{little}, \texttt{string}, \texttt{hex}, \texttt{dec}, \texttt{oct} and \texttt{from\_beginning}. \end{itemize} \end{itemize} \textit{Example of rule complexity reduction} \begin{itemize} \item[] The following two rules using the new rule options replace 64 (set and isset flowbit) rules that are necessary if the new rule options are not used: \footnotesize \begin{verbatim} alert tcp $EXTERNAL_NET any -> $HOME_NET [135,139,445,593,1024:] \ (msg:"dns R_Dnssrv funcs2 overflow attempt"; flow:established,to_server; \ dce_iface:50abc2a4-574d-40b3-9d66-ee4fd5fba076; dce_opnum:0-11; dce_stub_data; \ pcre:"/^.{12}(\x00\x00\x00\x00|.{12})/s"; byte_jump:4,-4,relative,align,dce; \ byte_test:4,>,256,4,relative,dce; reference:bugtraq,23470; reference:cve,2007-1748; \ classtype:attempted-admin; sid:1000068;) alert udp $EXTERNAL_NET any -> $HOME_NET [135,1024:] \ (msg:"dns R_Dnssrv funcs2 overflow attempt"; flow:established,to_server; \ dce_iface:50abc2a4-574d-40b3-9d66-ee4fd5fba076; dce_opnum:0-11; dce_stub_data; \ pcre:"/^.{12}(\x00\x00\x00\x00|.{12})/s"; byte_jump:4,-4,relative,align,dce; \ byte_test:4,>,256,4,relative,dce; reference:bugtraq,23470; reference:cve,2007-1748; \ classtype:attempted-admin; sid:1000069;) \end{verbatim} \normalsize \end{itemize} \subsection{Sensitive Data Preprocessor} \label{sub:sensitive_data} The Sensitive Data preprocessor is a Snort module that performs detection and filtering of Personally Identifiable Information (PII). This information includes credit card numbers, U.S. Social Security numbers, and email addresses. A limited regular expression syntax is also included for defining your own PII. \subsubsection{Dependencies} The Stream preprocessor must be enabled for the Sensitive Data preprocessor to work. \subsubsection{Preprocessor Configuration} Sensitive Data configuration is split into two parts: the preprocessor config, and the rule options. The preprocessor config starts with: \begin{verbatim} preprocessor sensitive_data: \end{verbatim} \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{alert\_threshold} & \texttt{} & NO & \texttt{alert\_threshold 25}\\ \hline \texttt{mask\_output} & NONE & NO & OFF\\ \hline \texttt{ssn\_file} & \texttt{} & NO & OFF\\ \hline \end{tabular} \end{itemize} \footnotesize \begin{verbatim} alert_threshold = 1 - 65535 \end{verbatim} \normalsize \textit{Option explanations} \begin{itemize} \item[] \texttt{alert\_threshold} \begin{itemize} \item[] The preprocessor will alert when any combination of PII are detected in a session. This option specifies how many need to be detected before alerting. This should be set higher than the highest individual count in your "sd\_pattern" rules. \end{itemize} \item[] \texttt{mask\_output} \begin{itemize} \item[] This option replaces all but the last 4 digits of a detected PII with "X"s. This is only done on credit card \& Social Security numbers, where an organization's regulations may prevent them from seeing unencrypted numbers. \end{itemize} \item[] \texttt{ssn\_file} \begin{itemize} \item[] A Social Security number is broken up into 3 sections: Area (3 digits), Group (2 digits), and Serial (4 digits). On a monthly basis, the Social Security Administration publishes a list of which Group numbers are in use for each Area. These numbers can be updated in Snort by supplying a CSV file with the new maximum Group numbers to use. By default, Snort recognizes Social Security numbers issued up through November 2009. \end{itemize} \end{itemize} \textit{Example preprocessor config} \begin{verbatim} preprocessor sensitive_data: alert_threshold 25 \ mask_output \ ssn_file ssn_groups_Jan10.csv \end{verbatim} \subsubsection{Rule Options} Snort rules are used to specify which PII the preprocessor should look for. A new rule option is provided by the preprocessor: \begin{verbatim} sd_pattern \end{verbatim} This rule option specifies what type of PII a rule should detect. \textit{Syntax} \begin{verbatim} sd_pattern:, ; \end{verbatim} \footnotesize \begin{verbatim} count = 1 - 255 pattern = any string \end{verbatim} \normalsize \textit{Option Explanations} \begin{itemize} \item[] \texttt{count} \begin{itemize} \item[] This dictates how many times a PII pattern must be matched for an alert to be generated. The count is tracked across all packets in a session. \end{itemize} \item[] \texttt{pattern} \begin{itemize} \item[] This is where the pattern of the PII gets specified. There are a few built-in patterns to choose from: \begin{itemize} \item[] \texttt{credit\_card} \begin{itemize} \item[] The "credit\_card" pattern matches 15- and 16-digit credit card numbers. These numbers may have spaces, dashes, or nothing in between groups. This covers Visa, Mastercard, Discover, and American Express. Credit card numbers matched this way have their check digits verified using the Luhn algorithm. \end{itemize} \item[] \texttt{us\_social} \begin{itemize} \item[] This pattern matches against 9-digit U.S. Social Security numbers. The SSNs are expected to have dashes between the Area, Group, and Serial sections. SSNs have no check digits, but the preprocessor will check matches against the list of currently allocated group numbers. \end{itemize} \item[] \texttt{us\_social\_nodashes} \begin{itemize} \item[] This pattern matches U.S. Social Security numbers without dashes separating the Area, Group, and Serial sections. \end{itemize} \item[] \texttt{email} \begin{itemize} \item[] This pattern matches against email addresses. \end{itemize} \end{itemize} \item[] If the pattern specified is not one of the above built-in patterns, then it is the definition of a custom PII pattern. Custom PII types are defined using a limited regex-style syntax. The following special characters and escape sequences are supported: \item[] \begin{tabular}{|c|p{10cm}|} \hline \texttt{\textbackslash d} & matches any digit\\ \hline \texttt{\textbackslash D} & matches any non-digit\\ \hline \texttt{\textbackslash l} & matches any letter\\ \hline \texttt{\textbackslash L} & matches any non-letter\\ \hline \texttt{\textbackslash w} & matches any alphanumeric character\\ \hline \texttt{\textbackslash W} & matches any non-alphanumeric character\\ \hline \texttt{\{num\}} & used to repeat a character or escape sequence "num" times. example: "\d\{3\}" matches 3 digits.\\ \hline \texttt{?} & makes the previous character or escape sequence optional. example: " ?" matches an optional space. This behaves in a greedy manner.\\ \hline \texttt{\textbackslash\textbackslash} & matches a backslash\\ \hline \textbackslash \{, \textbackslash \} & matches \{ and \}\\ \hline \textbackslash ? & matches a question mark.\\ \hline \end{tabular} \item[] Other characters in the pattern will be matched literally. \begin{note} Unlike PCRE, \texttt{\textbackslash w} in this rule option does NOT match underscores. \end{note} \end{itemize} \item[] \textit{Examples} \begin{verbatim} sd_pattern: 2,us_social; \end{verbatim} Alerts when 2 social security numbers (with dashes) appear in a session. \begin{verbatim} sd_pattern: 5,(\d{3})\d{3}-\d{4}; \end{verbatim} Alerts on 5 U.S. phone numbers, following the format (123)456-7890 Whole rule example: \begin{verbatim} alert tcp $HOME_NET $HIGH_PORTS -> $EXTERNAL_NET $SMTP_PORTS \ (msg:"Credit Card numbers sent over email"; gid:138; sid:1000; rev:1; \ sd_pattern:4,credit_card; metadata:service smtp;) \end{verbatim} \item[] \textit{Caveats} \begin{itemize} \item[] \texttt{sd\_pattern} is not compatible with other rule options. Trying to use other rule options with \texttt{sd\_pattern} will result in an error message. Rules using \texttt{sd\_pattern} must use GID 138. \end{itemize} \end{itemize} \subsection{Normalizer} When operating Snort in inline mode, it is helpful to normalize packets to help minimize the chances of evasion. To enable the normalizer, use the following when configuring Snort: \begin{verbatim} ./configure --enable-normalizer \end{verbatim} The normalize preprocessor is activated via the conf as outlined below. There are also many new preprocessor and decoder rules to alert on or drop packets with "abnormal" encodings. Note that in the following, fields are cleared only if they are non-zero. Also, normalizations will only be enabled if the selected DAQ supports packet replacement and is operating in inline mode. If a policy is configured for \texttt{inline\_test} or passive mode, any normalization statements in the policy config are ignored. \subsubsection{IP4 Normalizations} IP4 normalizations are enabled with: \begin{verbatim} preprocessor normalize_ip4: [df], [rf], [tos], [trim] \end{verbatim} Base normalizations enabled with "preprocessor \texttt{normalize\_ip4}" include: \begin{itemize} \item TTL normalization if enabled (explained below). \item Clear the differentiated services field (formerly TOS). \item NOP all options octets. \end{itemize} Optional normalizations include: \begin{itemize} \item \texttt{df} don't fragment: clear this bit on incoming packets. \item \texttt{rf} reserved flag: clear this bit on incoming packets. \item \texttt{tos} type of service (differentiated services): clear this byte. \item \texttt{trim} truncate packets with excess payload to the datagram length specified in the IP header + the layer 2 header (e.g. ethernet), but don't truncate below minimum frame length. This is automatically disabled if the DAQ can't inject packets. \end{itemize} \subsubsection{IP6 Normalizations} IP6 normalizations are enabled with: \begin{verbatim} preprocessor normalize_ip6 \end{verbatim} Base normalizations enabled with "preprocessor \texttt{normalize\_ip6}" include: \begin{itemize} \item Hop limit normalization if enabled (explained below). \item NOP all options octets in hop-by-hop and destination options extension headers. \end{itemize} \subsubsection{ICMP4/6 Normalizations} ICMP4 and ICMP6 normalizations are enabled with: \begin{verbatim} preprocessor normalize_icmp4 preprocessor normalize_icmp6 \end{verbatim} Base normalizations enabled with the above include: \begin{itemize} \item Clear the code field in echo requests and replies. \end{itemize} \subsubsection{TCP Normalizations} TCP normalizations are enabled with: \begin{verbatim} preprocessor normalize_tcp: \ [block], [rsv], [pad], \ [req_urg], [req_pay], [req_urp], \ [ips], [urp], [trim], \ [trim_syn], [trim_rst], \ [trim_win], [trim_mss], \ [ecn ], \ [opts [allow +]] ::= stream | packet ::= \ sack | echo | partial_order | conn_count | alt_checksum | md5 | ::= { 4, 5 } ::= { 6, 7 } ::= { 9, 10 } ::= { 11, 12, 13 } ::= { 14, 15 } ::= { 19 } ::= (3..255) \end{verbatim} Normalizations include: \begin{itemize} \item \texttt{block} allow packet drops during TCP normalization. \item \texttt{rsv} clear the reserved bits in the TCP header. \item \texttt{pad} clear any option padding bytes. \item \texttt{req\_urg} clear the urgent pointer if the urgent flag is not set. \item \texttt{req\_pay} clear the urgent pointer and the urgent flag if there is no payload. \item \texttt{req\_urp} clear the urgent flag if the urgent pointer is not set. \item \texttt{ips} ensure consistency in retransmitted data (also forces reassembly policy to "first"). Any segments that can't be properly reassembled will be dropped. \item \texttt{trim\_syn} remove data on SYN. \item \texttt{trim\_rst} remove any data from RST packet. \item \texttt{trim\_win} trim data to window. \item \texttt{trim\_mss} trim data to MSS. \item \texttt{trim} enable all of the above trim options. \item \texttt{ecn packet} clear ECN flags on a per packet basis (regardless of negotiation). \item \texttt{ecn stream} clear ECN flags if usage wasn't negotiated. Should also enable \texttt{require\_3whs}. \item \texttt{opts} NOP all option bytes other than maximum segment size, window scaling, timestamp, and any explicitly allowed with the allow keyword. You can allow options to pass by name or number. \item \texttt{opts} if timestamp is present but invalid, or valid but not negotiated, NOP the timestamp octets. \item \texttt{opts} if timestamp was negotiated but not present, block the packet. \item \texttt{opts} clear TS ECR if ACK flag is not set. \item \texttt{opts} MSS and window scale options are NOP'd if SYN flag is not set. \end{itemize} \subsubsection{TTL Normalization} TTL normalization pertains to both IP4 TTL (time-to-live) and IP6 (hop limit) and is only performed if both the relevant base normalization is enabled (as described above) and the minimum and new TTL values are configured, as follows: \begin{verbatim} config min_ttl: config new_ttl: ::= (1..255) ::= (+1..255) \end{verbatim} If \texttt{new\_ttl }$>$ \texttt{min\_ttl}, then if a packet is received with a TTL $<$ \texttt{min\_ttl}, the TTL will be set to \texttt{new\_ttl}. Note that this configuration item was deprecated in 2.8.6: \begin{verbatim} preprocessor stream5_tcp: min_ttl <#> \end{verbatim} By default \texttt{min\_ttl} = 1 (TTL normalization is disabled). When TTL normalization is turned on the \texttt{new\_ttl} is set to 5 by default. \subsection{SIP Preprocessor} \label{sub:sip} Session Initiation Protocol (SIP) is an application-layer control (signaling) protocol for creating, modifying, and terminating sessions with one or more participants. These sessions include Internet telephone calls, multimedia distribution, and multimedia conferences. SIP Preprocessor provides ways to tackle Common Vulnerabilities and Exposures (CVEs) related with SIP found over the past few years. It also makes detecting new attacks easier. \subsubsection{Dependency Requirements} For proper functioning of the preprocessor: \begin{itemize} \item Stream session tracking must be enabled, i.e. stream5. Both TCP and UDP must be enabled in stream5. The preprocessor requires a session tracker to keep its data. In addition, Stream API is able to provide correct support for ignoring audio/video data channel. \item IP defragmentation should be enabled, i.e. the frag3 preprocessor should be enabled and configured. \end{itemize} \subsubsection{Configuration} The preprocessor configuration name is \texttt{sip}.\\ \begin{verbatim} preprocessor sip \end{verbatim} \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{disabled} & NONE & NO & OFF\\ \hline \texttt{max\_sessions} & \texttt{} & NO & \texttt{max\_sessions 10000}\\ \hline \texttt{max\_dialogs} & \texttt{} & NO & \texttt{max\_dialogs 4}\\ \hline \texttt{ports} & \texttt{} & NO & \texttt{ports \{ 5060 5061 \} }\\ \hline \texttt{methods} & \texttt{} & NO & \texttt{methods \{ invite cancel ack bye register options \} }\\ \hline \texttt{max\_uri\_len} & \texttt{} & NO & \texttt{max\_uri\_len 256 }\\ \hline \texttt{max\_call\_id\_len} & \texttt{} & NO & \texttt{max\_call\_id\_len 256 }\\ \hline \texttt{max\_requestName\_len} & \texttt{} & NO & \texttt{max\_requestName\_len 20 }\\ \hline \texttt{max\_from\_len} & \texttt{} & NO & \texttt{max\_from\_len 256 }\\ \hline \texttt{max\_to\_len} & \texttt{} & NO & \texttt{max\_to\_len 256 }\\ \hline \texttt{max\_via\_len} & \texttt{} & NO & \texttt{max\_via\_len 1024 }\\ \hline \texttt{max\_contact\_len} & \texttt{} & NO & \texttt{max\_contact\_len 256 }\\ \hline \texttt{max\_content\_len} & \texttt{} & NO & \texttt{max\_content\_len 1024 }\\ \hline \texttt{ignore\_call\_channel} & NONE & NO & OFF\\ \hline \end{tabular} \end{itemize} \footnotesize \begin{verbatim} max_sessions = 1024-4194303 max_dialogs = 1-4194303 methods = "invite"|"cancel"|"ack"|"bye"|"register"| "options"\ |"refer" |"subscribe"|"update"|"join"|"info"|"message"\ |"notify"|"prack" max_uri_len = 0-65535 max_call_id_len = 0-65535 max_requestName_len = 0-65535 max_from_len = 0-65535 max_to_len = 0-65535 max_via_len = 0-65535 max_contact_len = 0-65535 max_content_len = 0-65535 \end{verbatim} \normalsize \textit{Option explanations} \begin{itemize} \item[] \texttt{disabled} \begin{itemize} \item[] SIP dynamic preprocessor can be enabled/disabled through configuration. By default this value is turned off. When the preprocessor is disabled, only the max\_sessions option is applied when specified with the configuration. \end{itemize} \item[] \texttt{max\_sessions} \begin{itemize} \item[] This specifies the maximum number of sessions that can be allocated. Those sessions are stream sessions, so they are bounded by maximum number of stream sessions. Default is 10000. \end{itemize} \item[] \texttt{max\_dialogs} \begin{itemize} \item[] This specifies the maximum number of dialogs within one stream session. If exceeded, the oldest dialog will be dropped. Default is 4. \end{itemize} \item[] \texttt{ports} \begin{itemize} \item[] This specifies on what ports to check for SIP messages. Typically, this will include 5060, 5061. \item[] \textit{Syntax} \begin{verbatim} ports { [< ... >] } \end{verbatim} \item[] \textit{Examples} \begin{verbatim} ports { 5060 5061 } \end{verbatim} \item[] Note: there are spaces before and after `\{' and `\}'. \end{itemize} \item[] \texttt{methods} \begin{itemize} \item[] This specifies on what methods to check for SIP messages: (1) invite, (2) cancel, (3) ack, (4) bye, (5) register, (6) options, (7) refer, (8) subscribe, (9) update (10) join (11) info (12) message (13) notify (14) prack. Note: those 14 methods are up to date list (Feb. 2011). New methods can be added to the list. Up to 32 methods supported. \item[] \textit{Syntax} \begin{verbatim} methods { } method-list = method|method method-list methods = "invite"|"cancel"|"ack"|"bye"|"register"| "options"\ |"refer"|"subscribe"|"update"|"join"|"info"|"message"\ |"notify"|"prack" \end{verbatim} \item[] \textit{Examples} \begin{verbatim} methods { invite cancel ack bye register options } methods { invite cancel ack bye register options information } \end{verbatim} \item[] Note: there are spaces before and after `\{' and `\}'. \end{itemize} \item[] \texttt{max\_uri\_len} \begin{itemize} \item[] This specifies the maximum Request URI field size. If the Request URI field is greater than this size, an alert is generated. Default is set to 256. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{max\_call\_id\_len} \begin{itemize} \item[] This specifies the maximum Call-ID field size. If the Call-ID field is greater than this size, an alert is generated. Default is set to 256. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{max\_requestName\_len} \begin{itemize} \item[] This specifies the maximum request name size that is part of the CSeq ID. If the request name is greater than this size, an alert is generated. Default is set to 20. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{max\_from\_len} \begin{itemize} \item[] This specifies the maximum From field size. If the From field is greater than this size, an alert is generated. Default is set to 256. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{max\_to\_len} \begin{itemize} \item[] This specifies the maximum To field size. If the To field is greater than this size, an alert is generated. Default is set to 256. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{max\_via\_len} \begin{itemize} \item[] This specifies the maximum Via field size. If the Via field is greater than this size, an alert is generated. Default is set to 1024. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{max\_contact\_len} \begin{itemize} \item[] This specifies the maximum Contact field size. If the Contact field is greater than this size, an alert is generated. Default is set to 256. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{max\_content\_len} \begin{itemize} \item[] This specifies the maximum content length of the message body. If the content length is greater than this number, an alert is generated. Default is set to 1024. The allowed range for this option is 0 - 65535. ``0'' means never alert. \end{itemize} \item[] \texttt{ignore\_call\_channel} \begin{itemize} \item[] This enables the support for ignoring audio/video data channel (through Stream API). By default, this is disabled. \end{itemize} \end{itemize} \textit{Option examples} \footnotesize \begin{verbatim} max_sessions 30000 disabled ports { 5060 5061 } methods { invite cancel ack bye register options } methods { invite cancel ack bye register options information } max_uri_len 1024 max_call_id_len 1024 max_requestName_len 10 max_from_len 1024 max_to_len 1024 max_via_len 1024 max_contact_len 1024 max_content_len 1024 max_content_len ignore_call_channel \end{verbatim} \normalsize \textit{Configuration examples} \footnotesize \begin{verbatim} preprocessor sip preprocessor sip: max_sessions 500000 preprocessor sip: max_contact_len 512, max_sessions 300000, methods { invite \ cancel ack bye register options } , ignore_call_channel preprocessor sip: ports { 5060 49848 36780 10270 }, max_call_id_len 200, \ max_from_len 100, max_to_len 200, max_via_len 1000, \ max_requestName_len 50, max_uri_len 100, ignore_call_channel,\ max_content_len 1000 preprocessor sip: disabled preprocessor sip: ignore_call_channel \end{verbatim} \normalsize \textit{Default configuration} \footnotesize \begin{verbatim} preprocessor sip \end{verbatim} \normalsize \subsubsection{Events} The preprocessor uses GID 140 to register events. \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline 1 & If the memory cap is reached and the preprocessor is configured to alert, this alert will be created. \\ \hline 2 & Request URI is required. When Request URI is empty, this alert will be created. \\ \hline 3 & The Request URI is larger than the defined length in configuration.\\ \hline 4 & When Call-ID is empty, this alert will be created.\\ \hline 5 & The Call-ID is larger than the defined length in configuration.\\ \hline 6 & The sequence e number value MUST be expressible as a 32-bit unsigned integer and MUST be less than $2^{31}$.\\ \hline 7 & The request name in the CSeq is larger than the defined length in configuration.\\ \hline 8 & From field is empty.\\ \hline 9 & From field is larger than the defined length in configuration.\\ \hline 10 & To field is empty.\\ \hline 11 & To field is larger than the defined length in configuration.\\ \hline 12 & Via filed is empty.\\ \hline 13 & Via filed is larger than the defined length in configuration.\\ \hline 14 & Contact is empty, but it is required non-empty for the message.\\ \hline 15 & The Contact is larger than the defined length in configuration. \\ \hline 16 & The content length is larger than the defined length in configuration or is negative. \\ \hline 17 & There are multiple requests in a single packet. Old SIP protocol supports multiple sip messages within one packet.\\ \hline 18 & There are inconsistencies between Content-Length in SIP header and actual body data.\\ \hline 19 & Request name is invalid in response.\\ \hline 20 & Authenticated invite message received, but no challenge from server received. This is the case of InviteReplay billing attack.\\ \hline 21 & Authenticated invite message received, but session information has been changed. This is different from re-INVITE, where the dialog has been established. and authenticated. This is can prevent FakeBusy billing attack.\\ \hline 22 & Response status code is not a 3 digit number.\\ \hline 23 & Content type header field is required if the message body is not empty.\\ \hline 24 & SIP version other than 2.0, 1.0, and 1.1 is invalid \\ \hline 25 & Mismatch in Method of request and the CSEQ header\\ \hline 26 & The method is unknown \\ \hline 27 & The number of dialogs in the stream session exceeds the maximal value. \\ \hline \end{longtable} \subsubsection{Rule Options} New rule options are supported by enabling the \texttt{sip} preprocessor: \begin{itemize} \item[] \begin{verbatim} sip_method sip_stat_code sip_header sip_body \end{verbatim} \end{itemize} Overload modifiers to existing \texttt{pcre} rule options: \begin{itemize} \item[] H: Match SIP request or SIP response header, Similar to \texttt{sip\_header}. \item[] P: Match SIP request or SIP response body, Similar to \texttt{sip\_body}. \end{itemize} \texttt{sip\_method} \label{sip:sip_method} \begin{itemize} \item[] The \texttt{sip\_method} keyword is used to check for specific SIP request methods. The list of methods is: invite, cancel, ack, bye, register, options, refer, subscribe, update, join, info, message, notify, prack. More than one method can be specified, via a comma separated list, and are OR'ed together. It will be applied in fast pattern match if available. If the method used in this rule is not listed in the preprocessor configuration, it will be added to the preprocessor configuration for the associated policy.\\ \textit{Syntax} \footnotesize \begin{verbatim} sip_method:; method-list = method|method, method-list method = ["!"] "invite"|"cancel"|"ack"|"bye"|"register"| "options"\ |"refer"|"subscribe"|"update"|"join"|"info"|"message"\ |"notify"|"prack" Note: if "!" is used, only one method is allowed in sip_method. \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} sip_method:invite, cancel sip_method:!invite Note: If a user wants to use "and", they can use something like this: sip_method:!invite; sip_method:!bye \end{verbatim} \normalsize \end{itemize} \texttt{sip\_stat\_code} \label{sip:sip_stat_code} \begin{itemize} \item[] The \texttt{sip\_stat\_code} is used to check the SIP response status code. This option matches if any one of the state codes specified matches the status codes of the SIP response.\\ \textit{Syntax} \footnotesize \begin{verbatim} sip_stat_code: ; code_list = state_code|state_code, code_list code = "100-999"|"1-9" \end{verbatim} \item[] Note: 1,2,3,4,5,6... mean to check for "1xx", "2xx", '3xx', '4xx', '5xx', '6xx'... responses. \\ \normalsize \textit{Examples} \footnotesize \begin{verbatim} sip_stat_code:200 sip_stat_code: 2 sip_stat_code: 200, 180 \end{verbatim} \normalsize \end{itemize} \texttt{sip\_header} \label{sip:sip_header} \begin{itemize} \item[] The \texttt{sip\_header} keyword restricts the search to the extracted Header fields of a SIP message request or a response. This works similar to \texttt{file\_data}. \\ \textit{Syntax} \footnotesize \begin{verbatim} sip_header; \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} alert udp any any -> any 5060 (sip_header; content:"CSeq"; ) \end{verbatim} \normalsize \end{itemize} \texttt{sip\_body} \label{sip:sip_body} \begin{itemize} \item[] The \texttt{sip\_body} keyword places the cursor at the beginning of the Body fields of a SIP message. This works similar to \texttt{file\_data} and \texttt{dce\_stub\_data}. The message body includes channel information using SDP protocol (Session Description Protocol).\\ \textit{Syntax} \footnotesize \begin{verbatim} sip_body; \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} alert udp any any -> any 5060 (sip_body; content:"C=IN 0.0.0.0"; within 100;) \end{verbatim} \normalsize \end{itemize} \texttt{pcre} \label{sip:pcre} \begin{itemize} \item[] SIP overloads two options for \texttt{pcre}:\\ \begin{itemize} \item H: Match SIP header for request or response , Similar to \texttt{sip\_header}.\\ \item P: Match SIP body for request or response , Similar to \texttt{sip\_body}.\\ \end{itemize} \textit{Examples} \footnotesize \begin{verbatim} alert udp any any -> any 5060 (pcre:"/INVITE/H"; sid:1000000;) alert udp any any -> any 5060 (pcre:"/m=/P"; sid:2000000;) \end{verbatim} \normalsize \end{itemize} \subsection{Reputation Preprocessor} \label{sub:reputation} Reputation preprocessor provides basic IP block-list/do-not-block-list capabilities, to block/drop/pass traffic from IP addresses listed. In the past, we use standard Snort rules to implement Reputation-based IP blocking. This preprocessor will address the performance issue and make the IP reputation management easier. This preprocessor runs before other preprocessors. \subsubsection{Configuration} The preprocessor configuration name is \texttt{reputation}.\\ \begin{verbatim} preprocessor reputation \end{verbatim} \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{memcap} & \texttt{} & NO & \texttt{memcap 500}\\ \hline \texttt{scan\_local} & NONE & NO & OFF\\ \hline \texttt{BlockFlow} & \texttt{} & NO & NONE\\ \hline \texttt{AllowFlow} & \texttt{} & NO & NONE\\ \hline \texttt{priority} & [BlockFlow AllowFlow] & NO & \texttt{priority AllowFlow}\\ \hline \texttt{nested\_ip} & [inner outer both] & NO & \texttt{nested\_ip inner}\\ \hline \texttt{white} & [unblack trust] & NO & \texttt{white unblack}\\ \hline \end{tabular} \end{itemize} \footnotesize \begin{verbatim} memcap = 1-4095 Mbytes \end{verbatim} \normalsize \textit{Option explanations} \begin{itemize} \item[] \texttt{memcap} \begin{itemize} \item[] Maximum total memory supported. It can be set up to 4095 Mbytes. \end{itemize} \item[] \texttt{scan\_local} \begin{itemize} \item[] Enable to inspect local address defined in RFC 1918: \begin{itemize} \item[] 10.0.0.0 - 10.255.255.255 (10/8 prefix) \item[] 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) \item[] 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) \end{itemize} \end{itemize} \item[] \texttt{BlockFlow/AllowFlow} \begin{itemize} \item[] The IP lists are loaded from external files. It supports relative paths for inclusion and \$variables for path. Multiple BlockFlows or AllowFlows are supported. \item[] Note: if the same IP is redefined later, it will overwrite the previous one. In other words, IP lists always favors the last file or entry processed. \end{itemize} \item[] \texttt{priority} \begin{itemize} \item[] Specify either BlockFlow or AllowFlow has higher priority when source/destination is on BlockFlow while destination/source is on AllowFlow. By default, AllowFlow has higher priority. In other words, the packet will be passed when either source or destination is AllowFlowed. \item[] Note: this only defines priority when there is a decision conflict, during run-time. During initialization time, if the same IP address is defined in AllowFlow and BlockFlow, whoever the last one defined will be the final one. Priority does not work on this case. \end{itemize} \item[] \texttt{nested\_ip} \begin{itemize} \item[] Specify which IP address to be used when there is IP encapsulation. \end{itemize} \item[] \texttt{white} \begin{itemize} \item[] Specify the meaning of AllowFlow. When white means unblack, it unblacks IPs that are in BlockFlows; when white means trust, the packet gets bypassed, without further detection by snort. You can only specify either unblack or trust. \item[] Note: when white means unblack, AllowFlow always has higher priority than BlockFlow. \end{itemize} \end{itemize} \textit{Configuration examples} \footnotesize \begin{verbatim} preprocessor reputation:\ block-list /etc/snort/default.blacklist, \ do-not-block-list /etc/snort/default.whitelist preprocessor reputation: \ nested_ip both, \ block-list /etc/snort/default.blacklist, \ do-not-block-list /etc/snort/default.whitelist preprocessor reputation: \ memcap 4095, scan_local, nested_ip both, \ priority do-not-block-list, \ block-list /etc/snort/default.blacklist, \ do-not-block-list /etc/snort/default.whitelist, white trust $REP_BLACK_FILE1 = ../dshield.list $REP_BLACK_FILE2 = ../snort.org.list preprocessor reputation: \ block-list $REP_BLACK_FILE1,\ block-list $REP_BLACK_FILE2 \end{verbatim} \normalsize \textit{IP List File Format} \begin{itemize} \item[] \texttt{Syntax} \begin{itemize} \item[] The IP list file has 1 entry per line. The entry can be either IP entry or comment. \end{itemize} \begin{itemize} \item[] \texttt{IP Entry} \begin{itemize} \item[] CIDR notation $<$comments$>$ line break. \item[] Example: \footnotesize \begin{verbatim} 172.16.42.32/32 172.33.42.32/16 \end{verbatim} \normalsize \end{itemize} \item[] \texttt{Comment} \begin{itemize} \item[] The comment start with \# \item[] \# $<$comments$>$ \item[] Example \footnotesize \begin{verbatim} # This is a full line comment 172.33.42.32/16 # This is a in-line comment \end{verbatim} \normalsize \end{itemize} \end{itemize} \item[] \texttt{IP List File Example} \begin{itemize} \item[] \footnotesize \begin{verbatim} # This is a full line comment 172.16.42.32/32 # This is an inline comment, line with single CIDR block 172.33.42.32/16 \end{verbatim} \normalsize \end{itemize} \end{itemize} \textit{Use case} \begin{itemize} \item[] A user wants to protect his/her network from unwanted/unknown IPs, only allowing some trusted IPs. Here is the configuration: \item[] \footnotesize \begin{verbatim} preprocessor reputation: \ block-list /etc/snort/default.blacklist do-not-block-list /etc/snort/default.whitelist In file "default.blacklist" # These two entries will match all ipv4 addresses 1.0.0.0/1 128.0.0.0/1 In file "default.whitelist" 68.177.102.22 # sourcefire.com 74.125.93.104 # google.com \end{verbatim} \end{itemize} \normalsize \subsubsection{Events} Reputation preprocessor uses GID 136 to register events. \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline 1 & Packet is BlockFlowed. \\ \hline 2 & Packet is AllowFlowed. \\ \hline 3 & Packet is inspected. \\ \hline \end{longtable} \subsubsection{Shared memory support} \begin{itemize} \item[] In order to minimize memory consumption when multiple Snort instances are running concurrently, we introduce the support of shared memory. After configured, all the snort instances share the same IP tables in shared memory. \item[]\textit{System requirement} \begin{itemize} \item[]This feature is supported only in Linux. \end{itemize} \item[]\textit{Build configuration} \begin{itemize} \item[]A new option, \texttt{--enable-shared-rep} is introduced to \texttt{./configure} command. This option enables the support for shared memory. \end{itemize} \item[]\textit{Configuration} \begin{itemize} \item[]\texttt{shared\_mem} \begin{itemize} \item[] If the build supports shared memory, this configuration will enable shared memory. If this option isn't set, standard memory is used. This option must specify a path or directory where IP lists will be loaded in shared memory. One snort instance will create and maintain the shared IP lists. We use instance ID 1, specified in the snort \texttt{-G} option to be the primary snort. All the other snort instances are clients (readers). \item[] \textit{Syntax} \begin{verbatim} shared_mem: path \end{verbatim} \item[] \textit{Examples} \begin{verbatim} shared_mem /user/reputation/iplists \end{verbatim} \end{itemize} \item[]\texttt{shared\_refresh} \begin{itemize} \item[]This option changes the period of checking new shared memory segment, in the unit of second. By default, the refresh rate is $60$ seconds. \item[]\textit{Syntax} \begin{verbatim} shared_refresh period = "1 - 4294967295" \end{verbatim} \item[]\textit{Examples} \begin{verbatim} shared_refresh 60 \end{verbatim} \end{itemize} \end{itemize} \item[]\textit{Steps to configure shared memory} \begin{itemize} \item When building Snort, add option \texttt{--enable-shared-rep} to \texttt{./configure}\\ For example: \begin{verbatim} ./configure --enable-gre --enable-sourcefire --enable-flexresp3 --enable-pthread --enable-linux-smp-stats --enable-targetbased --enable-shared-rep --enable-control-socket \end{verbatim} \item Put your IP list file into a directory, where snort has full access. \\ For example: \begin{verbatim} /user/reputation/iplists \end{verbatim} In order to separate AllowFlow with BlockFlow, you need to specify AllowFlow with \texttt{.wlf} extension and BlockFlow with \texttt{.blf} extension. \item In snort config file, specify shared memory support with the path to IP files.\\ For example: \begin{verbatim} shared_mem /user/reputation/iplists \end{verbatim} If you want to change the period of checking new IP lists, add refresh period.\\ For example: \begin{verbatim} shared_refresh 300 \end{verbatim} \item Start shared memory primary(writer) with \texttt{-G} 0 option. Note: only one primary should be enabled. \item Start shared memory clients (readers) with \texttt{-G} 1 or other IDs. Note: for one ID, only one snort instance should be enabled. \item You will see the IP lists got loaded and shared across snort instances! \end{itemize} \item[]\textit{Reload IP lists using control socket} \begin{itemize} \item Run snort using command line with option \texttt{--cs-dir } or configure snort with: \begin{verbatim} config cs_dir: \end{verbatim} \item (Optional) you can create a version file named ``IPRVersion.dat'' in the IP list directory. This file helps managing reloading IP lists, by specifying a version. When the version isn't changed, IP lists will not be reloaded if they are already in shared memory. The version number should be a 32 bit number.\\ For example: \begin{verbatim} VERSION=1 \end{verbatim} \item In the \texttt{/src/tools/control} directory, you will find \texttt{snort\_control} command if built with \texttt{--enable-control-socket} option. \item Type the following command to reload IP lists. Before typing this command, make sure to update version file if you are using version file. The \texttt{} is the same path in first step.\\ \begin{verbatim} /src/tools/control/snort_control 1361 \end{verbatim} \end{itemize} \item[]\textit{Using manifest file to manage loading (optional)} \begin{itemize} \item[] Using manifest file, you can control the file loading sequence, action taken, and support zone based detection. You can create a manifest file named ``zone.info'' in the IP list directory.\\ \item[] When Snort is signaled to load new lists, a manifest file is read first to determine which zones the IPs in each list are applicable to and what action to take per list (Block, White, Monitor). \\ \item[] Files listed in manifest are loaded from top to bottom. You should put files that have higher priority first. In manifest file, you can put up to 255 files. Without manifest file, files will be loaded in alphabet order.\\ \item[] Here's the format of the manifest file. Each line of the file has the following format:\\ \begin{verbatim} , ,[, ]+ ::= 32 bit integer ::= "monitor"|"block"|"white" ::= [0-1051] \end{verbatim} \item[] Using manifest file, you can specify a new action called ``monitor'', which indicates a packet needs to be inspected, but does not disable detection. This is different from ``block'' action, which disables further detection. This new action helps users evaluate their IP lists before applying it. \item[] An example manifest file: \begin{verbatim} #ipreputation manifest file white.wlf, 111 ,white, black1.blf, 1112, black, 3, 12 black2.blf, 1113, black, 3, 12 monitor.blf,2222, monitor, 0, 2, 8 \end{verbatim} \end{itemize} \end{itemize} \subsection{GTP Decoder and Preprocessor} \label{sub:gtp} GTP (GPRS Tunneling Protocol) is used in core communication networks to establish a channel between GSNs (GPRS Serving Node). GTP decoding preprocessor provides ways to tackle intrusion attempts to those networks through GTP. It also makes detecting new attacks easier. Two components are developed: GTP decoder and GTP preprocessor. \begin{itemize} \item GTP decoder extracts payload inside GTP PDU; \item GTP preprocessor inspects all the signaling messages and provide keywords for further inspection \end{itemize} When the decoder is enabled and configured, the decoder strips the GTP headers and parses the underlying IP/TCP/UDP encapsulated packets. Therefore all rules and detection work as if there was no GTP header. Example: \begin{itemize} \item[] Most GTP packets look like this \begin{verbatim} IP -> UDP -> GTP -> IP -> TCP -> HTTP \end{verbatim} If you had a standard HTTP rule: \begin{verbatim} alert tcp any any -> any $HTTP_PORTS (msg:"Test HTTP"; flow:to_server,established; content:"SOMETHINGEVIL"; http_uri; .... sid:X; rev:Y;) \end{verbatim} it would alert on the inner HTTP data that is encapsulated in GTP without any changes to the rule other than enabling and configuring the GTP decoder. \end{itemize} \subsubsection{Dependency Requirements} For proper functioning of the preprocessor: \begin{itemize} \item Stream session tracking must be enabled, i.e. stream5. UDP must be enabled in stream5. The preprocessor requires a session tracker to keep its data. \item IP defragmentation should be enabled, i.e. the frag3 preprocessor should be enabled and configured. \end{itemize} \subsubsection{GTP Data Channel Decoder Configuration} GTP decoder extracts payload from GTP PDU. The following configuration sets GTP decoding: \begin{verbatim} config enable_gtp \end{verbatim} By default, GTP decoder uses port number $2152$ (GTPv1) and $3386$ (GTPv0). If users want to change those values, they can use \texttt{portvar GTP\_PORTS}: \begin{verbatim} portvar GTP_PORTS [2152,3386] \end{verbatim} \subsubsection{GTP Control Channel Preprocessor Configuration} Different from GTP decoder, GTP preprocessor examines all signaling messages. The preprocessor configuration name is \texttt{gtp}. \begin{verbatim} preprocessor gtp \end{verbatim} \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{ports} & \texttt{} & NO & \texttt{ports \{ 2123 3386 \} }\\ \hline \end{tabular} \end{itemize} \normalsize \textit{Option explanations} \begin{itemize} \item[] \texttt{ports} \begin{itemize} \item[] This specifies on what ports to check for GTP messages. Typically, this will include 2123, 3386. \item[] \textit{Syntax} \begin{verbatim} ports { [< ... >] } \end{verbatim} \item[] \textit{Examples} \begin{verbatim} ports { 2123 3386 2152 } \end{verbatim} \item[] Note: there are spaces before and after `\{' and `\}'. \end{itemize} \end{itemize} \normalsize \textit{Default configuration} \footnotesize \begin{verbatim} preprocessor gtp \end{verbatim} \normalsize \subsubsection{GTP Decoder Events} \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline 297 & Two or more GTP encapsulation layers present \\ \hline 298 & GTP header length is invalid \\ \hline \end{longtable} \subsubsection{GTP Preprocessor Events} \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline 1 & Message length is invalid. \\ \hline 2 & Information element length is invalid. \\ \hline 3 & Information elements are out of order. \\ \hline \end{longtable} \subsubsection{Rule Options} New rule options are supported by enabling the \texttt{gtp} preprocessor: \begin{itemize} \item[] \begin{verbatim} gtp_type gtp_info gtp_version \end{verbatim} \end{itemize} \texttt{gtp\_type} \label{gtp:gtp_method} \begin{itemize} \item[] The \texttt{gtp\_type} keyword is used to check for specific GTP types. User can input message type value, an integer in [0, 255], or a string defined in the Table below. More than one type can be specified, via a comma separated list, and are OR'ed together. If the type used in a rule is not listed in the preprocessor configuration, an error will be thrown. \item[] A message type can have different type value in different GTP versions. For example, \texttt{sgsn\_\-context\_\-request} has message type value $50$ in GTPv0 and GTPv1, but $130$ in GTPv2. \texttt{gtp\_type} will match to a different value depending on the version number in the packet. In this example, evaluating a GTPv0 or GTPv1 packet will check whether the message type value is $50$; evaluating a GTPv2 packet will check whether the message type value is $130$. When a message type is not defined in a version, any packet in that version will always return ``No match''. \item[] If an integer is used to specify message type, every GTP packet is evaluated, no matter what version the packet is. If the message type matches the value in packet, it will return ``Match''. \\ \textit{Syntax} \footnotesize \begin{verbatim} gtp_type:; type-list = type|type, type-list type = "0-255"| | "echo_request" | "echo_response" ... \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} gtp_type:10, 11, echo_request; \end{verbatim} \normalsize \textit{GTP message types} \small \begin{longtable}{|r|c|c|c|p{13.5cm}|} \hline Type & GTPv0 & GTPv1 & GTPv2\\ \hline 0 & N/A & N/A & N/A\\ \hline 1 & echo\_request & echo\_request & echo\_request\\ \hline 2 & echo\_response & echo\_response & echo\_response\\ \hline 3 & version\_not\_supported & version\_not\_supported & version\_not\_supported\\ \hline 4 & node\_alive\_request & node\_alive\_request & N/A\\ \hline 5 & node\_alive\_response & node\_alive\_response & N/A\\ \hline 6 & redirection\_request & redirection\_request & N/A\\ \hline 7 & redirection\_response & redirection\_response & N/A \\ \hline 16 & create\_pdp\_context\_request & create\_pdp\_context\_request & N/A\\ \hline 17 & create\_pdp\_context\_response & create\_pdp\_context\_response & N/A \\ \hline 18 & update\_pdp\_context\_request & update\_pdp\_context\_request & N/A\\ \hline 19 & update\_pdp\_context\_response & update\_pdp\_context\_response & N/A\\ \hline 20 & delete\_pdp\_context\_request & delete\_pdp\_context\_request & N/A\\ \hline 21 & delete\_pdp\_context\_response & delete\_pdp\_context\_response & N/A\\ \hline 22 & create\_aa\_pdp\_context\_request & init\_pdp\_context\_activation\_request & N/A\\ \hline 23 & create\_aa\_pdp\_context\_response & init\_pdp\_context\_activation\_response & N/A\\ \hline 24 & delete\_aa\_pdp\_context\_request & N/A & N/A\\ \hline 25 & delete\_aa\_pdp\_context\_response & N/A & N/A\\ \hline 26 & error\_indication & error\_indication & N/A\\ \hline 27 & pdu\_notification\_request & pdu\_notification\_request & N/A\\ \hline 28 & pdu\_notification\_response & pdu\_notification\_response & N/A\\ \hline 29 & pdu\_notification\_reject\_request & pdu\_notification\_reject\_request & N/A\\ \hline 30 & pdu\_notification\_reject\_response & pdu\_notification\_reject\_response & N/A\\ \hline 31 & N/A & supported\_ext\_header\_notification & N/A \\ \hline 32 & send\_routing\_info\_request & send\_routing\_info\_request & create\_session\_request \\ \hline 33 & send\_routing\_info\_response & send\_routing\_info\_response & create\_session\_response \\ \hline 34 & failure\_report\_request & failure\_report\_request & modify\_bearer\_request \\ \hline 35 & failure\_report\_response & failure\_report\_response & modify\_bearer\_response \\ \hline 36 & note\_ms\_present\_request & note\_ms\_present\_request & delete\_session\_request \\ \hline 37 & note\_ms\_present\_response & note\_ms\_present\_response & delete\_session\_response \\ \hline 38 & N/A & N/A & change\_notification\_request \\ \hline 39 & N/A & N/A & change\_notification\_response \\ \hline 48 & identification\_request & identification\_request & N/A \\ \hline 49 & identification\_response & identification\_response & N/A \\ \hline 50 & sgsn\_context\_request & sgsn\_context\_request & N/A \\ \hline 51 & sgsn\_context\_response & sgsn\_context\_response & N/A \\ \hline 52 & sgsn\_context\_ack & sgsn\_context\_ack & N/A \\ \hline 53 & N/A & forward\_relocation\_request & N/A \\ \hline 54 & N/A & forward\_relocation\_response & N/A \\ \hline 55 & N/A & forward\_relocation\_complete & N/A \\ \hline 56 & N/A & relocation\_cancel\_request & N/A \\ \hline 57 & N/A & relocation\_cancel\_response & N/A \\ \hline 58 & N/A & forward\_srns\_contex & N/A \\ \hline 59 & N/A & forward\_relocation\_complete\_ack & N/A \\ \hline 60 & N/A & forward\_srns\_contex\_ack & N/A \\ \hline 64 & N/A & N/A & modify\_bearer\_command \\ \hline 65 & N/A & N/A & modify\_bearer\_failure\_indication \\ \hline 66 & N/A & N/A & delete\_bearer\_command \\ \hline 67 & N/A & N/A & delete\_bearer\_failure\_indication \\ \hline 68 & N/A & N/A & bearer\_resource\_command \\ \hline 69 & N/A & N/A & bearer\_resource\_failure\_indication \\ \hline 70 & N/A & ran\_info\_relay & downlink\_failure\_indication \\ \hline 71 & N/A & N/A & trace\_session\_activation \\ \hline 72 & N/A & N/A & trace\_session\_deactivation \\ \hline 73 & N/A & N/A & stop\_paging\_indication \\ \hline 95 & N/A & N/A & create\_bearer\_request \\ \hline 96 & N/A & mbms\_notification\_request & create\_bearer\_response \\ \hline 97 & N/A & mbms\_notification\_response & update\_bearer\_request \\ \hline 98 & N/A & mbms\_notification\_reject\_request & update\_bearer\_response \\ \hline 99 & N/A & mbms\_notification\_reject\_response & delete\_bearer\_request \\ \hline 100 & N/A & create\_mbms\_context\_request & delete\_bearer\_response \\ \hline 101 & N/A & create\_mbms\_context\_response & delete\_pdn\_request \\ \hline 102 & N/A & update\_mbms\_context\_request & delete\_pdn\_response \\ \hline 103 & N/A & update\_mbms\_context\_response & N/A \\ \hline 104 & N/A & delete\_mbms\_context\_request & N/A \\ \hline 105 & N/A & delete\_mbms\_context\_response & N/A \\ \hline 112 & N/A & mbms\_register\_request & N/A \\ \hline 113 & N/A & mbms\_register\_response & N/A \\ \hline 114 & N/A & mbms\_deregister\_request & N/A \\ \hline 115 & N/A & mbms\_deregister\_response & N/A \\ \hline 116 & N/A & mbms\_session\_start\_request & N/A \\ \hline 117 & N/A & mbms\_session\_start\_response & N/A \\ \hline 118 & N/A & mbms\_session\_stop\_request & N/A \\ \hline 119 & N/A & mbms\_session\_stop\_response & N/A \\ \hline 120 & N/A & mbms\_session\_update\_request & N/A \\ \hline 121 & N/A & mbms\_session\_update\_response & N/A \\ \hline 128 & N/A & ms\_info\_change\_request & identification\_request \\ \hline 129 & N/A & ms\_info\_change\_response & identification\_response \\ \hline 130 & N/A & N/A & sgsn\_context\_request \\ \hline 131 & N/A & N/A & sgsn\_context\_response \\ \hline 132 & N/A & N/A & sgsn\_context\_ack \\ \hline 133 & N/A & N/A & forward\_relocation\_request \\ \hline 134 & N/A & N/A & forward\_relocation\_response \\ \hline 135 & N/A & N/A & forward\_relocation\_complete \\ \hline 136 & N/A & N/A & forward\_relocation\_complete\_ack \\ \hline 137 & N/A & N/A & forward\_access \\ \hline 138 & N/A & N/A & forward\_access\_ack \\ \hline 139 & N/A & N/A & relocation\_cancel\_request \\ \hline 140 & N/A & N/A & relocation\_cancel\_response \\ \hline 141 & N/A & N/A & configuration\_transfer\_tunnel \\ \hline 149 & N/A & N/A & detach \\ \hline 150 & N/A & N/A & detach\_ack \\ \hline 151 & N/A & N/A & cs\_paging \\ \hline 152 & N/A & N/A & ran\_info\_relay \\ \hline 153 & N/A & N/A & alert\_mme \\ \hline 154 & N/A & N/A & alert\_mme\_ack \\ \hline 155 & N/A & N/A & ue\_activity \\ \hline 156 & N/A & N/A & ue\_activity\_ack \\ \hline 160 & N/A & N/A & create\_forward\_tunnel\_request \\ \hline 161 & N/A & N/A & create\_forward\_tunnel\_response \\ \hline 162 & N/A & N/A & suspend \\ \hline 163 & N/A & N/A & suspend\_ack \\ \hline 164 & N/A & N/A & resume \\ \hline 165 & N/A & N/A & resume\_ack \\ \hline 166 & N/A & N/A & create\_indirect\_forward\_tunnel\_request \\ \hline 167 & N/A & N/A & create\_indirect\_forward\_tunnel\_response \\ \hline 168 & N/A & N/A & delete\_indirect\_forward\_tunnel\_request \\ \hline 169 & N/A & N/A & delete\_indirect\_forward\_tunnel\_response \\ \hline 170 & N/A & N/A & release\_access\_bearer\_request \\ \hline 171 & N/A & N/A & release\_access\_bearer\_response \\ \hline 176 & N/A & N/A & downlink\_data \\ \hline 177 & N/A & N/A & downlink\_data\_ack \\ \hline 178 & N/A & N/A & N/A \\ \hline 179 & N/A & N/A & pgw\_restart \\ \hline 199 & N/A & N/A & pgw\_restart\_ack \\ \hline 200 & N/A & N/A & update\_pdn\_request \\ \hline 201 & N/A & N/A & update\_pdn\_response \\ \hline 211 & N/A & N/A & modify\_access\_bearer\_request \\ \hline 212 & N/A & N/A & modify\_access\_bearer\_response \\ \hline 231 & N/A & N/A & mbms\_session\_start\_request \\ \hline 232 & N/A & N/A & mbms\_session\_start\_response \\ \hline 233 & N/A & N/A & mbms\_session\_update\_request \\ \hline 234 & N/A & N/A & mbms\_session\_update\_response \\ \hline 235 & N/A & N/A & mbms\_session\_stop\_request \\ \hline 236 & N/A & N/A & mbms\_session\_stop\_response \\ \hline 240 & data\_record\_transfer\_request & data\_record\_transfer\_request & N/A \\ \hline 241 & data\_record\_transfer\_response & data\_record\_transfer\_response & N/A \\ \hline 254 & N/A & end\_marker & N/A \\ \hline 255 & pdu & pdu & N/A \\ \hline \end{longtable} \end{itemize} \texttt{gtp\_info} \label{gtp:gtp_info} \begin{itemize} \item[] The \texttt{gtp\_info} keyword is used to check for specific GTP information element. This keyword restricts the search to the information element field. User can input information element value, an integer in $[0, 255]$, or a string defined in the Table below. If the information element used in this rule is not listed in the preprocessor configuration, an error will be thrown. \item[] When there are several information elements with the same type in the message, this keyword restricts the search to the total consecutive buffer. Because the standard requires same types group together, this feature will be available for all valid messages. In the case of ``out of order information elements'', this keyword restricts the search to the last buffer. \item[] Similar to message type, same information element might have different information element value in different GTP versions. For example, \texttt{cause} has value $1$ in GTPv0 and GTPv1, but $2$ in GTPv2. \texttt{gtp\_info} will match to a different value depending on the version number in the packet. When an information element is not defined in a version, any packet in that version will always return ``No match''. If an integer is used to specify information element type, every GTP packet is evaluated, no matter what version the packet is. If the message type matches the value in packet, it will return ``Match''.\\ \textit{Syntax} \footnotesize \begin{verbatim} gtp_info:; ie = "0-255"| "rai" | "tmsi"... \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} gtp_info: 16; gtp_info: tmsi \end{verbatim} \normalsize \textit{GTP information elements} \small \begin{longtable}{|r|c|c|c|p{13.5cm}|} \hline Type & GTPv0 & GTPv1 & GTPv2\\ \hline 0 & N/A & N/A & N/A \\ \hline 1 & cause & cause & imsi\\ \hline 2 & imsi & imsi & cause \\ \hline 3 & rai & rai & recovery\\ \hline 4 & tlli & tlli & N/A\\ \hline 5 & p\_tmsi & p\_tmsi & N/A\\ \hline 6 & qos & N/A & N/A\\ \hline 7 & N/A & N/A & N/A \\ \hline 8 & recording\_required & recording\_required & N/A\\ \hline 9 & authentication & authentication & N/A\\ \hline 10 & N/A & N/A & N/A\\ \hline 11 & map\_cause & map\_cause & N/A\\ \hline 12 & p\_tmsi\_sig & p\_tmsi\_sig & N/A\\ \hline 13 & ms\_validated & ms\_validated & N/A\\ \hline 14 & recovery & recovery & N/A\\ \hline 15 & selection\_mode & selection\_mode & N/A\\ \hline 16 & flow\_label\_data\_1 & teid\_1 & N/A\\ \hline 17 & flow\_label\_signalling & teid\_control & N/A\\ \hline 18 & flow\_label\_data\_2 & teid\_2 & N/A\\ \hline 19 & ms\_unreachable & teardown\_ind & N/A\\ \hline 20 & N/A & nsapi & N/A\\ \hline 21 & N/A & ranap & N/A\\ \hline 22 & N/A & rab\_context & N/A\\ \hline 23 & N/A & radio\_priority\_sms & N/A\\ \hline 24 & N/A & radio\_priority & N/A\\ \hline 25 & N/A & packet\_flow\_id & N/A\\ \hline 26 & N/A & charging\_char & N/A\\ \hline 27 & N/A & trace\_ref & N/A\\ \hline 28 & N/A & trace\_type & N/A\\ \hline 29 & N/A & ms\_unreachable & N/A\\ \hline 71 & N/A & N/A & apn\\ \hline 72 & N/A & N/A & ambr\\ \hline 73 & N/A & N/A & ebi\\ \hline 74 & N/A & N/A & ip\_addr\\ \hline 75 & N/A & N/A & mei\\ \hline 76 & N/A & N/A & msisdn\\ \hline 77 & N/A & N/A & indication\\ \hline 78 & N/A & N/A & pco\\ \hline 79 & N/A & N/A & paa\\ \hline 80 & N/A & N/A & bearer\_qos\\ \hline 81 & N/A & N/A & flow\_qos\\ \hline 82 & N/A & N/A & rat\_type\\ \hline 83 & N/A & N/A & serving\_network\\ \hline 84 & N/A & N/A & bearer\_tft\\ \hline 85 & N/A & N/A & tad\\ \hline 86 & N/A & N/A & uli\\ \hline 87 & N/A & N/A & f\_teid\\ \hline 88 & N/A & N/A & tmsi\\ \hline 89 & N/A & N/A & cn\_id\\ \hline 90 & N/A & N/A & s103pdf\\ \hline 91 & N/A & N/A & s1udf\\ \hline 92 & N/A & N/A & delay\_value\\ \hline 93 & N/A & N/A & bearer\_context\\ \hline 94 & N/A & N/A & charging\_id\\ \hline 95 & N/A & N/A & charging\_char\\ \hline 96 & N/A & N/A & trace\_info\\ \hline 97 & N/A & N/A & bearer\_flag\\ \hline 98 & N/A & N/A & N/A\\ \hline 99 & N/A & N/A & pdn\_type\\ \hline 100 & N/A & N/A & pti\\ \hline 101 & N/A & N/A & drx\_parameter\\ \hline 102 & N/A & N/A & N/A\\ \hline 103 & N/A & N/A & gsm\_key\_tri\\ \hline 104 & N/A & N/A & umts\_key\_cipher\_quin\\ \hline 105 & N/A & N/A & gsm\_key\_cipher\_quin\\ \hline 106 & N/A & N/A & umts\_key\_quin\\ \hline 107 & N/A & N/A & eps\_quad\\ \hline 108 & N/A & N/A & umts\_key\_quad\_quin\\ \hline 109 & N/A & N/A & pdn\_connection\\ \hline 110 & N/A & N/A & pdn\_number\\ \hline 111 & N/A & N/A & p\_tmsi\\ \hline 112 & N/A & N/A & p\_tmsi\_sig\\ \hline 113 & N/A & N/A & hop\_counter\\ \hline 114 & N/A & N/A & ue\_time\_zone\\ \hline 115 & N/A & N/A & trace\_ref\\ \hline 116 & N/A & N/A & complete\_request\_msg\\ \hline 117 & N/A & N/A & guti\\ \hline 118 & N/A & N/A & f\_container\\ \hline 119 & N/A & N/A & f\_cause\\ \hline 120 & N/A & N/A & plmn\_id\\ \hline 121 & N/A & N/A & target\_id\\ \hline 122 & N/A & N/A & N/A\\ \hline 123 & N/A & N/A & packet\_flow\_id\\ \hline 124 & N/A & N/A & rab\_contex\\ \hline 125 & N/A & N/A & src\_rnc\_pdcp\\ \hline 126 & N/A & N/A & udp\_src\_port\\ \hline 127 & charge\_id & charge\_id & apn\_restriction\\ \hline 128 & end\_user\_address & end\_user\_address & selection\_mode\\ \hline 129 & mm\_context & mm\_context & src\_id\\ \hline 130 & pdp\_context & pdp\_context & N/A\\ \hline 131 & apn & apn & change\_report\_action\\ \hline 132 & protocol\_config & protocol\_config & fq\_csid\\ \hline 133 & gsn & gsn & channel\\ \hline 134 & msisdn & msisdn & emlpp\_pri\\ \hline 135 & N/A & qos & node\_type\\ \hline 136 & N/A & authentication\_qu & fqdn\\ \hline 137 & N/A & tft & ti\\ \hline 138 & N/A & target\_id & mbms\_session\_duration\\ \hline 139 & N/A & utran\_trans & mbms\_service\_area\\ \hline 140 & N/A & rab\_setup & mbms\_session\_id\\ \hline 141 & N/A & ext\_header & mbms\_flow\_id\\ \hline 142 & N/A & trigger\_id & mbms\_ip\_multicast\\ \hline 143 & N/A & omc\_id & mbms\_distribution\_ack\\ \hline 144 & N/A & ran\_trans & rfsp\_index\\ \hline 145 & N/A & pdp\_context\_pri & uci\\ \hline 146 & N/A & addi\_rab\_setup & csg\_info\\ \hline 147 & N/A & sgsn\_number & csg\_id\\ \hline 148 & N/A & common\_flag & cmi\\ \hline 149 & N/A & apn\_restriction & service\_indicator\\ \hline 150 & N/A & radio\_priority\_lcs & detach\_type\\ \hline 151 & N/A & rat\_type & ldn\\ \hline 152 & N/A & user\_loc\_info & node\_feature\\ \hline 153 & N/A & ms\_time\_zone & mbms\_time\_to\_transfer\\ \hline 154 & N/A & imei\_sv & throttling\\ \hline 155 & N/A & camel & arp\\ \hline 156 & N/A & mbms\_ue\_context & epc\_timer\\ \hline 157 & N/A & tmp\_mobile\_group\_id & signalling\_priority\_indication\\ \hline 158 & N/A & rim\_routing\_addr & tmgi\\ \hline 159 & N/A & mbms\_config & mm\_srvcc\\ \hline 160 & N/A & mbms\_service\_area & flags\_srvcc\\ \hline 161 & N/A & src\_rnc\_pdcp & mmbr\\ \hline 162 & N/A & addi\_trace\_info & N/A\\ \hline 163 & N/A & hop\_counter & N/A\\ \hline 164 & N/A & plmn\_id & N/A\\ \hline 165 & N/A & mbms\_session\_id & N/A\\ \hline 166 & N/A & mbms\_2g3g\_indicator & N/A\\ \hline 167 & N/A & enhanced\_nsapi & N/A\\ \hline 168 & N/A & mbms\_session\_duration & N/A\\ \hline 169 & N/A & addi\_mbms\_trace\_info & N/A\\ \hline 170 & N/A & mbms\_session\_repetition\_num & N/A\\ \hline 171 & N/A & mbms\_time\_to\_data & N/A\\ \hline 173 & N/A & bss & N/A\\ \hline 174 & N/A & cell\_id & N/A\\ \hline 175 & N/A & pdu\_num & N/A\\ \hline 176 & N/A & N/A & N/A\\ \hline 177 & N/A & mbms\_bearer\_capab & N/A\\ \hline 178 & N/A & rim\_routing\_disc & N/A\\ \hline 179 & N/A & list\_pfc & N/A\\ \hline 180 & N/A & ps\_xid & N/A\\ \hline 181 & N/A & ms\_info\_change\_report & N/A\\ \hline 182 & N/A & direct\_tunnel\_flags & N/A\\ \hline 183 & N/A & correlation\_id & N/A\\ \hline 184 & N/A & bearer\_control\_mode & N/A\\ \hline 185 & N/A & mbms\_flow\_id & N/A\\ \hline 186 & N/A & mbms\_ip\_multicast & N/A\\ \hline 187 & N/A & mbms\_distribution\_ack & N/A\\ \hline 188 & N/A & reliable\_inter\_rat\_handover & N/A\\ \hline 189 & N/A & rfsp\_index & N/A\\ \hline 190 & N/A & fqdn & N/A\\ \hline 191 & N/A & evolved\_allocation1 & N/A\\ \hline 192 & N/A & evolved\_allocation2 & N/A\\ \hline 193 & N/A & extended\_flags & N/A\\ \hline 194 & N/A & uci & N/A\\ \hline 195 & N/A & csg\_info & N/A\\ \hline 196 & N/A & csg\_id & N/A\\ \hline 197 & N/A & cmi & N/A\\ \hline 198 & N/A & apn\_ambr & N/A\\ \hline 199 & N/A & ue\_network & N/A\\ \hline 200 & N/A & ue\_ambr & N/A\\ \hline 201 & N/A & apn\_ambr\_nsapi & N/A\\ \hline 202 & N/A & ggsn\_backoff\_timer & N/A\\ \hline 203 & N/A & signalling\_priority\_indication & N/A\\ \hline 204 & N/A & signalling\_priority\_indication\_nsapi & N/A\\ \hline 205 & N/A & high\_bitrate & N/A\\ \hline 206 & N/A & max\_mbr & N/A\\ \hline 250 & N/A & N/A & N/A\\ \hline & N/A & N/A & N/A\\ \hline 251 & charging\_gateway\_addr & charging\_gateway\_addr & N/A\\ \hline 255 & private\_extension & private\_extension & private\_extension\\ \hline \end{longtable} \end{itemize} \texttt{gtp\_version} \label{gtp:gtp_version} \begin{itemize} \item[] The \texttt{gtp\_version} keyword is used to check for specific GTP version. \item[] Because different GTP version defines different message types and information elements, this keyword should combine with \texttt{gtp\_type} and \texttt{gtp\_info.}\\ \textit{Syntax} \footnotesize \begin{verbatim} gtp_version:; version = "0, 1, 2' \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} gtp_version: 1; \end{verbatim} \normalsize \end{itemize} \subsection{Modbus Preprocessor} \label{sub:modbus} The Modbus preprocessor is a Snort module that decodes the Modbus protocol. It also provides rule options to access certain protocol fields. This allows a user to write rules for Modbus packets without decoding the protocol with a series of "content" and "byte\_test" options. Modbus is a protocol used in SCADA networks. If your network does not contain any Modbus-enabled devices, we recommend leaving this preprocessor turned off. \subsubsection{Dependency Requirements} For proper functioning of the preprocessor: \begin{itemize} \item Stream session tracking must be enabled, i.e. stream5. TCP must be enabled in stream5. The preprocessor requires a session tracker to keep its data. \item Protocol Aware Flushing (PAF) must be enabled. \item IP defragmentation should be enabled, i.e. the frag3 preprocessor should be enabled and configured. \end{itemize} \subsubsection{Preprocessor Configuration} To get started, the Modbus preprocessor must be enabled. The preprocessor name is \texttt{modbus}. \begin{verbatim} preprocessor modbus \end{verbatim} \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{ports} & \texttt{} & NO & \texttt{ports \{ 502 \} }\\ \hline \end{tabular} \end{itemize} \normalsize \textit{Option explanations} \begin{itemize} \item[] \texttt{ports} \begin{itemize} \item[] This specifies on what ports to check for Modbus messages. Typically, this will include 502. \item[] \textit{Syntax} \begin{verbatim} ports { [< ... >] } \end{verbatim} \item[] \textit{Examples} \begin{verbatim} ports { 1237 3945 5067 } \end{verbatim} \item[] Note: there are spaces before and after `\{' and `\}'. \end{itemize} \end{itemize} \normalsize \textit{Default configuration} \footnotesize \begin{verbatim} preprocessor modbus \end{verbatim} \normalsize \subsubsection{Rule Options} The Modbus preprocessor adds 3 new rule options. These rule options match on various pieces of the Modbus headers: \begin{itemize} \item[] \begin{verbatim} modbus_func modbus_unit modbus_data \end{verbatim} \end{itemize} The preprocessor must be enabled for these rule option to work. \texttt{modbus\_func} \label{modbus:modbus_func} \begin{itemize} \item[] This option matches against the Function Code inside of a Modbus header. The code may be a number (in decimal format), or a string from the list provided below. \end{itemize} \textit{Syntax} \footnotesize \begin{verbatim} modbus_func: code = 0-255 | "read_coils" | "read_discrete_inputs" | "read_holding_registers" | "read_input_registers" | "write_single_coil" | "write_single_register" | "read_exception_status" | "diagnostics" | "get_comm_event_counter" | "get_comm_event_log" | "write_multiple_coils" | "write_multiple_registers" | "report_slave_id" | "read_file_record" | "write_file_record" | "mask_write_register" | "read_write_multiple_registers" | "read_fifo_queue" | "encapsulated_interface_transport" \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} modbus_func:1; modbus_func:write_multiple_coils; \end{verbatim} \normalsize \texttt{modbus\_unit} \label{modbus:modbus_unit} \begin{itemize} \item[] This option matches against the Unit ID field in a Modbus header. \end{itemize} \textit{Syntax} \footnotesize \begin{verbatim} modbus_unit: unit = 0-255 \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} modbus_unit:1; \end{verbatim} \normalsize \texttt{modbus\_data} \label{modbus:modbus_data} \begin{itemize} \item[] This rule option sets the cursor at the beginning of the Data field in a Modbus request/response. \end{itemize} \textit{Syntax} \footnotesize \begin{verbatim} modbus_data; \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} modbus_data; content:"badstuff"; \end{verbatim} \normalsize \subsubsection{Preprocessor Events} The Modbus preprocessor uses GID 144 for its preprocessor events. \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline 1 & The length in the Modbus header does not match the length needed \\ & by the Modbus function code. \\ &\\ & Each Modbus function has an expected format for requests and responses. \\ & If the length of the message does not match the expected format, this \\ & alert is generated. \\ \hline 2 & Modbus protocol ID is non-zero. \\ &\\ & The protocol ID field is used for multiplexing other protocols with \\ & Modbus. Since the preprocessor cannot handle these other protocols, \\ & this alert is generated instead. \\ \hline 3 & Reserved Modbus function code in use. \\ \hline \end{longtable} \subsection{DNP3 Preprocessor} \label{sub:dnp3} The DNP3 preprocessor is a Snort module that decodes the DNP3 protocol. It also provides rule options to access certain protocol fields. This allows a user to write rules for DNP3 packets without decoding the protocol with a series of "content" and "byte\_test" options. DNP3 is a protocol used in SCADA networks. If your network does not contain any DNP3-enabled devices, we recommend leaving this preprocessor turned off. \subsubsection{Dependency Requirements} For proper functioning of the preprocessor: \begin{itemize} \item Stream session tracking must be enabled, i.e. stream5. TCP or UDP must be enabled in stream5. The preprocessor requires a session tracker to keep its data. \item Protocol Aware Flushing (PAF) must be enabled. \item IP defragmentation should be enabled, i.e. the frag3 preprocessor should be enabled and configured. \end{itemize} \subsubsection{Preprocessor Configuration} To get started, the DNP3 preprocessor must be enabled. The preprocessor name is \texttt{dnp3}. \begin{verbatim} preprocessor dnp3 \end{verbatim} \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{ports} & \texttt{} & NO & \texttt{ports \{ 20000 \} }\\ \texttt{memcap} & \texttt{ [< ... >] } \end{verbatim} \item[] \textit{Examples} \begin{verbatim} ports { 1237 3945 5067 } \end{verbatim} \item[] Note: there are spaces before and after `\{' and `\}'. \end{itemize} \item[] \texttt{memcap} \begin{itemize} \item[] This sets a maximum to the amount of memory allocated to the DNP3 preprocessor for session-tracking purposes. The argument is given in bytes. Each session requires about 4 KB to track, and the default is 256 kB. This gives the preprocessor the ability to track 63 DNP3 sessions simultaneously. Setting the memcap below 4144 bytes will cause a fatal error. When multiple configs are used, the memcap in the non-default configs will be overwritten by the memcap in the default config. If the default config isn't intended to inspect DNP3 traffic, use the "disabled" keyword. \end{itemize} \item[] \texttt{check\_crc} \begin{itemize} \item[] This option makes the preprocessor validate the checksums contained in DNP3 Link-Layer Frames. Frames with invalid checksums will be ignored. If the corresponding preprocessor rule is enabled, invalid checksums will generate alerts. The corresponding rule is GID 145, SID 1. \end{itemize} \item[] \texttt{disabled} \begin{itemize} \item[] This option is used for loading the preprocessor without inspecting any DNP3 traffic. The \texttt{disabled} keyword is only useful when the DNP3 preprocessor is turned on in a separate policy. \end{itemize} \end{itemize} \normalsize \textit{Default configuration} \footnotesize \begin{verbatim} preprocessor dnp3 \end{verbatim} \normalsize \subsubsection{Rule Options} The DNP3 preprocessor adds 4 new rule options. These rule options match on various pieces of the DNP3 headers: \begin{itemize} \item[] \begin{verbatim} dnp3_func dnp3_obj dnp3_ind dnp3_data \end{verbatim} \end{itemize} The preprocessor must be enabled for these rule option to work. \texttt{dnp3\_func} \label{dnp3:dnp3_func} \begin{itemize} \item[] This option matches against the Function Code inside of a DNP3 Application-Layer request/response header. The code may be a number (in decimal format), or a string from the list provided below. \end{itemize} \textit{Syntax} \footnotesize \begin{verbatim} dnp3_func: code = 0-255 | "confirm" | "read" | "write" | "select" | "operate" | "direct_operate" | "direct_operate_nr" | "immed_freeze" | "immed_freeze_nr" | "freeze_clear" | "freeze_clear_nr" | "freeze_at_time" | "freeze_at_time_nr" | "cold_restart" | "warm_restart" | "initialize_data" | "initialize_appl" | "start_appl" | "stop_appl" | "save_config" | "enable_unsolicited" | "disable_unsolicited" | "assign_class" | "delay_measure" | "record_current_time" | "open_file" | "close_file" | "delete_file" | "get_file_info" | "authenticate_file" | "abort_file" | "activate_config" | "authenticate_req" | "authenticate_err" | "response" | "unsolicited_response" | "authenticate_resp" \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} dnp3_func:1; dnp3_func:delete_file; \end{verbatim} \normalsize \texttt{dnp3\_ind} \label{dnp3:dnp3_ind} \begin{itemize} \item[] This option matches on the Internal Indicators flags present in a DNP3 Application Response Header. Much like the TCP flags rule option, providing multiple flags in one option will cause the rule to fire if \emph{ANY} one of the flags is set. To alert on a combination of flags, use multiple rule options. \end{itemize} \textit{Syntax} \footnotesize \begin{verbatim} dnp3_ind:{,...] flag = "all_stations" "class_1_events" "class_2_events" "class_3_events" "need_time" "local_control" "defice_trouble" "device_restart" "no_func_code_support" "object_unknown" "parameter_error" "event_buffer_overflow" "already_executing" "config_corrupt" "reserved_2" "reserved_1" \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} # Alert on reserved_1 OR reserved_2 dnp3_ind:reserved_1,reserved_2; # Alert on class_1 AND class_2 AND class_3 events dnp3_ind:class_1_events; dnp3_ind:class_2_events; dnp3_ind:class_3_events; \end{verbatim} \normalsize \texttt{dnp3\_obj} \label{dnp3:dnp3_obj} \begin{itemize} \item[] This option matches on DNP3 object headers present in a request or response. \end{itemize} \textit{Syntax} \footnotesize \begin{verbatim} dnp3_obj:, group = 0 - 255 var = 0 - 255 \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} # Alert on DNP3 "Date and Time" object dnp3_obj:50,1; \end{verbatim} \normalsize \texttt{dnp3\_data} \label{dnp3:dnp3_data} \begin{itemize} \item[] As Snort processes DNP3 packets, the DNP3 preprocessor collects Link-Layer Frames and reassembles them back into Application-Layer Fragments. This rule option sets the cursor to the beginning of an Application-Layer Fragment, so that other rule options can work on the reassembled data. With the dnp3\_data rule option, you can write rules based on the data within Fragments without splitting up the data and adding CRCs every 16 bytes. \end{itemize} \textit{Syntax} \footnotesize \begin{verbatim} dnp3_data; \end{verbatim} \normalsize \textit{Examples} \footnotesize \begin{verbatim} dnp3_data; content:"badstuff_longer_than_16chars"; \end{verbatim} \normalsize \subsubsection{Preprocessor Events} The DNP3 preprocessor uses GID 145 for its preprocessor events. \begin{longtable}{|r|p{13.5cm}|} \hline SID & Description\\ \hline 1 & A Link-Layer Frame contained an invalid CRC. \\ & (Enable \texttt{check\_crc} in the preprocessor config to get this alert.) \\ \hline 2 & A DNP3 Link-Layer Frame was dropped, due to an invalid length. \\ \hline 3 & A Transport-Layer Segment was dropped during reassembly. \\ & This happens when segments have invalid sequence numbers. \\ \hline 4 & The DNP3 Reassembly buffer was cleared before a complete fragment could \\ & be reassembled. \\ & This happens when a segment carrying the "FIR" flag appears after some \\ & other segments have been queued. \\ \hline 5 & A DNP3 Link-Layer Frame is larger than 260 bytes. \\ \hline 6 & A DNP3 Link-Layer Frame uses an address that is reserved. \\ \hline 7 & A DNP3 request or response uses a reserved function code. \\ \hline \end{longtable} \subsection{AppId Preprocessor} \label{sub:appid} With increasingly complex networks and growing network traffic, network administrators require application awareness in managing networks. An administrator may allow only applications that are business relevant, low bandwidth and/or deal with certain subject matter. AppId preprocessor adds application level view to manage networks. It does this by adding the following features \begin{itemize} \item Network control: The preprocessor provides simplified single point application awareness by making a set of application identifiers (AppId) available to Snort Rule writers. \item Network usage awareness: the preprocessor outputs statistics to show network bandwidth used by each application seen on network. Administrators can monitor bandwidth usage and may decide to block applications that are wasteful. \item Custom applications: The preprocessor enables administrators to create their own application detectors to detect new applications. The detectors are written in Lua and interface with Snort using a well-defined C-Lua API. \end{itemize} \subsubsection{Dependency Requirements} For proper functioning of the preprocessor: \begin{itemize} \item Stream session tracking must be enabled, i.e. stream5. TCP or UDP must be enabled in stream5. The preprocessor requires a session tracker to keep its data. \item Protocol Aware Flushing (PAF) must be enabled. \item IP defragmentation should be enabled, i.e. the frag3 preprocessor should be enabled and configured. \item HTTP preprocessor must be enabled and configured. The processor does not require any AppId specific configuration. The preprocessor provides parsed HTTP headers for application determination. Without HTTP preprocessor, AppId preprocessor will identify only non-HTTP applications. \item LuaJIT version 2.0.2 must be installed on host where snort is being compiled and run. Newer versions of LuaJIT are not tested for compatibility. \end{itemize} \subsubsection{Preprocessor Configuration} AppId dynamic preprocessor is enabled by default(from snort-2.9.12). The preprocessor can be disabled during build time by including the following option in ./configure: --disable-open-appid The configuration name is "appid": The preprocessor name is \texttt{appid}. \begin{verbatim} preprocessor appid \end{verbatim} \textit{Option syntax} \begin{itemize} \item[] \begin{tabular}{|l|c|c|p{6cm}|} \hline Option & Argument & Required & Default\\ \hline \hline \texttt{app\_detector\_dir} & \texttt{} & NO & \texttt{app\_detector\_dir \{ /usr/local/etc/appid \} }\\ \texttt{app\_stats\_filename} & \texttt{} & NO & \texttt{NULL}\\ \texttt{app\_stats\_period} & \texttt{